<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2024-04-07 15:27:36 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2024-04-07 15:27:36 +0100
commit658900fcae610caace83a112ac0ee865108ebc92 (patch)
tree4d7b6bebe0d192abf62970ca4324ef1ff274e3c8 /main
parent81925b6ad5212512d27365b8224b76095191431f (diff)
downloadstred-go-658900fcae610caace83a112ac0ee865108ebc92.tar
Change output subex internals to allow structures
Also add substitute register syntactic sugar
Diffstat (limited to 'main')
-rw-r--r--main/command.go144
-rw-r--r--main/lex.go35
-rw-r--r--main/main.go5
-rw-r--r--main/main_test.go46
-rw-r--r--main/parse.go56
5 files changed, 265 insertions, 21 deletions
diff --git a/main/command.go b/main/command.go
index 1d089ee..3f22821 100644
--- a/main/command.go
+++ b/main/command.go
@@ -84,6 +84,54 @@ func (cmd SubstituteValueCommand) String() string {
return "s/.../"
}
+type IsStartCommand struct {}
+func (cmd IsStartCommand) exec(state *ProgramState) {
+ if state.start {
+ state.pc += 2
+ } else {
+ state.pc += 1
+ }
+}
+func (cmd IsStartCommand) String() string {
+ return "a"
+}
+
+type IsPrevStartCommand struct {}
+func (cmd IsPrevStartCommand) exec(state *ProgramState) {
+ if state.prevStart {
+ state.pc += 2
+ } else {
+ state.pc += 1
+ }
+}
+func (cmd IsPrevStartCommand) String() string {
+ return "A"
+}
+
+type IsEndCommand struct {}
+func (cmd IsEndCommand) exec(state *ProgramState) {
+ if state.end {
+ state.pc += 2
+ } else {
+ state.pc += 1
+ }
+}
+func (cmd IsEndCommand) String() string {
+ return "e"
+}
+
+type IsNextEndCommand struct {}
+func (cmd IsNextEndCommand) exec(state *ProgramState) {
+ if state.nextEnd {
+ state.pc += 2
+ } else {
+ state.pc += 1
+ }
+}
+func (cmd IsNextEndCommand) String() string {
+ return "E"
+}
+
type NoopCommand struct {}
func (cmd NoopCommand) exec(state *ProgramState) {
state.pc++
@@ -112,6 +160,38 @@ func (cmd AppendXRegCommand) String() string {
return "X"
}
+type SubstituteToXRegCommand struct {
+ subex subex.Transducer
+}
+func (cmd SubstituteToXRegCommand) exec(state *ProgramState) {
+ newValue, err := runSubex(cmd.subex, state.value)
+ if err {
+ state.pc++
+ } else {
+ state.pc += 2
+ state.xreg = newValue
+ }
+}
+func (cmd SubstituteToXRegCommand) String() string {
+ return "x/.../"
+}
+
+type SubstituteAppendXRegCommand struct {
+ subex subex.Transducer
+}
+func (cmd SubstituteAppendXRegCommand) exec(state *ProgramState) {
+ newValue, err := runSubex(cmd.subex, state.value)
+ if err {
+ state.pc++
+ } else {
+ state.pc += 2
+ state.xreg = append(state.xreg, newValue...)
+ }
+}
+func (cmd SubstituteAppendXRegCommand) String() string {
+ return "X/.../"
+}
+
type SwapYRegCommand struct {}
func (cmd SwapYRegCommand) exec(state *ProgramState) {
v := state.value
@@ -132,6 +212,38 @@ func (cmd AppendYRegCommand) String() string {
return "Y"
}
+type SubstituteToYRegCommand struct {
+ subex subex.Transducer
+}
+func (cmd SubstituteToYRegCommand) exec(state *ProgramState) {
+ newValue, err := runSubex(cmd.subex, state.value)
+ if err {
+ state.pc++
+ } else {
+ state.pc += 2
+ state.yreg = newValue
+ }
+}
+func (cmd SubstituteToYRegCommand) String() string {
+ return "y/.../"
+}
+
+type SubstituteAppendYRegCommand struct {
+ subex subex.Transducer
+}
+func (cmd SubstituteAppendYRegCommand) exec(state *ProgramState) {
+ newValue, err := runSubex(cmd.subex, state.value)
+ if err {
+ state.pc++
+ } else {
+ state.pc += 2
+ state.yreg = append(state.xreg, newValue...)
+ }
+}
+func (cmd SubstituteAppendYRegCommand) String() string {
+ return "Y/.../"
+}
+
type SwapZRegCommand struct {}
func (cmd SwapZRegCommand) exec(state *ProgramState) {
v := state.value
@@ -152,6 +264,38 @@ func (cmd AppendZRegCommand) String() string {
return "Z"
}
+type SubstituteToZRegCommand struct {
+ subex subex.Transducer
+}
+func (cmd SubstituteToZRegCommand) exec(state *ProgramState) {
+ newValue, err := runSubex(cmd.subex, state.value)
+ if err {
+ state.pc++
+ } else {
+ state.pc += 2
+ state.zreg = newValue
+ }
+}
+func (cmd SubstituteToZRegCommand) String() string {
+ return "z/.../"
+}
+
+type SubstituteAppendZRegCommand struct {
+ subex subex.Transducer
+}
+func (cmd SubstituteAppendZRegCommand) exec(state *ProgramState) {
+ newValue, err := runSubex(cmd.subex, state.value)
+ if err {
+ state.pc++
+ } else {
+ state.pc += 2
+ state.zreg = append(state.xreg, newValue...)
+ }
+}
+func (cmd SubstituteAppendZRegCommand) String() string {
+ return "Z/.../"
+}
+
type JumpCommand struct {
destination int
}
diff --git a/main/lex.go b/main/lex.go
index 496abd0..a28975f 100644
--- a/main/lex.go
+++ b/main/lex.go
@@ -171,21 +171,28 @@ func lexCommand(l *lexer) stateFunc {
l.ignore()
r := l.next()
switch r {
- case eof:
- l.emit(TokenEOF)
- return nil
- case '{':
- l.emit(TokenLBrace)
- return lexCommand
- case '}':
- l.emit(TokenRBrace)
- return lexCommand
- case 's', 'S':
- l.emit(TokenCommand)
+ case eof:
+ l.emit(TokenEOF)
+ return nil
+ case '{':
+ l.emit(TokenLBrace)
+ return lexCommand
+ case '}':
+ l.emit(TokenRBrace)
+ return lexCommand
+ case 's', 'S':
+ l.emit(TokenCommand)
+ return lexSubstitution
+ case 'x', 'X', 'y', 'Y', 'z', 'Z':
+ l.emit(TokenCommand)
+ if l.peek() == '/' {
return lexSubstitution
- case ':', 'b':
- l.emit(TokenCommand)
- return lexLabel
+ } else {
+ return lexCommand
+ }
+ case ':', 'b':
+ l.emit(TokenCommand)
+ return lexLabel
}
if isAlpha(r) {
l.emit(TokenCommand)
diff --git a/main/main.go b/main/main.go
index 3a864ad..3fb1cbf 100644
--- a/main/main.go
+++ b/main/main.go
@@ -10,6 +10,7 @@ import (
type ProgramState struct {
value, xreg, yreg, zreg []walk.Value
+ start, prevStart, end, nextEnd bool
in walk.StredReader
out walk.StredWriter
program []Command
@@ -42,6 +43,10 @@ func run(config config) {
break
}
state.value = []walk.Value{walkItem.Value}
+ state.start = walkItem.Start
+ state.prevStart = walkItem.PrevStart
+ state.end = walkItem.End
+ state.nextEnd = walkItem.NextEnd
state.pc = 0
for state.pc < len(state.program) {
state.program[state.pc].exec(&state)
diff --git a/main/main_test.go b/main/main_test.go
index a7a7795..7aa90aa 100644
--- a/main/main_test.go
+++ b/main/main_test.go
@@ -5,6 +5,8 @@ import (
"testing"
)
+var miscInput string = `{"something":{"nested":"Here is my test value"},"array":["Hello","world","these","are","values"],"people":[{"first_name":"Charlie","last_name":"Johnson","age":22},{"first_name":"Tom","last_name":"Johnson","age":18},{"first_name":"Charlie","last_name":"Chaplin","age":122},{"first_name":"John","last_name":"Johnson","age":48}]}`
+
func TestMain(t *testing.T) {
type test struct {
program string
@@ -17,9 +19,51 @@ func TestMain(t *testing.T) {
{
program: `s/#(~(people)~$_@(1$_#(~(first_name)~$_.|(..$_){-0})-|(..$_){-0})-|(..$_){-0})-/p`,
quiet: true,
- input: `{"something":{"nested":"Here is my test value"},"array":["Hello","world","these","are","values"],"people":[{"first_name":"Charlie","last_name":"Johnson","age":22},{"first_name":"Tom","last_name":"Johnson","age":18},{"first_name":"Charlie","last_name":"Chaplin","age":122},{"first_name":"John","last_name":"Johnson","age":48}]}`,
+ input: miscInput,
+ expected: `"Tom"`,
+ },
+ {
+ program: `s/#("people"$_ @(1 $_#("first_name"$_ .)-)-)-/p`,
+ quiet: true,
+ input: miscInput,
+ expected: `"Tom"`,
+ },
+ {
+ program: "s/#(\"people\" @(1 #(\"first_name\" (.$a))-)-)-$_ `$a`/p",
+ quiet: true,
+ input: miscInput,
expected: `"Tom"`,
},
+ {
+ program: "s/#(\"people\" @(2 (.$a))-)-$_ `$a`/p",
+ quiet: true,
+ input: miscInput,
+ expected: `{"first_name":"Charlie","last_name":"Chaplin","age":122}`,
+ },
+ {
+ program: "s/#(\"people\"$_ :(#(\"age\"$_ .)-):)-/p",
+ quiet: true,
+ input: miscInput,
+ expected: `[22,18,122,48]`,
+ },
+ {
+ program: "aX/#(\"people\" :(#()#):)#$_ `1`/o es/#()#/{ xs/.{-0}+/p }",
+ quiet: true,
+ input: miscInput,
+ expected: "4",
+ },
+ {
+ program: "s/#(\"people\"$_ .)-/{ s/:():/p as/:(#()#):/{ xdx } s/:(#((\"first_name\" | \"last_name\") .)#)-/X es/@(.#()-)-/{ xs/(#(\"first_name\" \".{-0}$a\")# | #(\"last_name\" \".{-0}$b\")# | .){-0}$_ `\"$a $b\"`/Xxs/-(..)@/p } }",
+ quiet: true,
+ input: miscInput,
+ expected: `["Charlie Johnson","Tom Johnson","Charlie Chaplin","John Johnson"]`,
+ },
+ {
+ program: "s/#(\"people\"$_ .)-/{ s/:():/p as/:(#()#):/{ xdx } X/:(#((\"first_name\" | \"last_name\") .)#)-/o es/@(.#()-)-/{ xX/(#(\"first_name\" \".{-0}$a\")# | #(\"last_name\" \".{-0}$b\")# | .){-0}$_ `\"$a $b\"`/xs/-(..)@/p } }",
+ quiet: true,
+ input: miscInput,
+ expected: `["Charlie Johnson","Tom Johnson","Charlie Chaplin","John Johnson"]`,
+ },
}
for i, test := range tests {
diff --git a/main/parse.go b/main/parse.go
index 9c7a437..3e0e80b 100644
--- a/main/parse.go
+++ b/main/parse.go
@@ -76,17 +76,61 @@ func (p *parser) parseBasicCommand(commands []Command, commandChar rune) []Comma
case 'o':
return append(commands, NoopCommand{})
case 'x':
- return append(commands, SwapXRegCommand{})
+ delim := p.peek()
+ if delim.typ != TokenSubstituteDelimiter {
+ return append(commands, SwapXRegCommand{})
+ }
+ ast := p.parseSubex()
+ subex := subex.CompileTransducer(ast)
+ return append(commands, SubstituteToXRegCommand {subex}, JumpCommand {len(commands) + 3})
case 'X':
- return append(commands, AppendXRegCommand{})
+ delim := p.peek()
+ if delim.typ != TokenSubstituteDelimiter {
+ return append(commands, AppendXRegCommand{})
+ }
+ ast := p.parseSubex()
+ subex := subex.CompileTransducer(ast)
+ return append(commands, SubstituteAppendXRegCommand {subex}, JumpCommand {len(commands) + 3})
case 'y':
- return append(commands, SwapYRegCommand{})
+ delim := p.peek()
+ if delim.typ != TokenSubstituteDelimiter {
+ return append(commands, SwapYRegCommand{})
+ }
+ ast := p.parseSubex()
+ subex := subex.CompileTransducer(ast)
+ return append(commands, SubstituteToYRegCommand {subex}, JumpCommand {len(commands) + 3})
case 'Y':
- return append(commands, AppendYRegCommand{})
+ delim := p.peek()
+ if delim.typ != TokenSubstituteDelimiter {
+ return append(commands, AppendYRegCommand{})
+ }
+ ast := p.parseSubex()
+ subex := subex.CompileTransducer(ast)
+ return append(commands, SubstituteAppendYRegCommand {subex}, JumpCommand {len(commands) + 3})
case 'z':
- return append(commands, SwapZRegCommand{})
+ delim := p.peek()
+ if delim.typ != TokenSubstituteDelimiter {
+ return append(commands, SwapZRegCommand{})
+ }
+ ast := p.parseSubex()
+ subex := subex.CompileTransducer(ast)
+ return append(commands, SubstituteToZRegCommand {subex}, JumpCommand {len(commands) + 3})
case 'Z':
- return append(commands, AppendZRegCommand{})
+ delim := p.peek()
+ if delim.typ != TokenSubstituteDelimiter {
+ return append(commands, AppendZRegCommand{})
+ }
+ ast := p.parseSubex()
+ subex := subex.CompileTransducer(ast)
+ return append(commands, SubstituteAppendZRegCommand {subex}, JumpCommand {len(commands) + 3})
+ case 'a':
+ return append(commands, IsStartCommand{}, JumpCommand {len(commands) + 3})
+ case 'A':
+ return append(commands, IsPrevStartCommand{}, JumpCommand {len(commands) + 3})
+ case 'e':
+ return append(commands, IsEndCommand{}, JumpCommand {len(commands) + 3})
+ case 'E':
+ return append(commands, IsNextEndCommand{}, JumpCommand {len(commands) + 3})
case ':':
labelToken := p.next()
if labelToken.typ != TokenLabel {