diff options
Diffstat (limited to 'main')
| -rw-r--r-- | main/command.go | 104 | ||||
| -rw-r--r-- | main/lex.go | 2 | ||||
| -rw-r--r-- | main/main_test.go | 49 | ||||
| -rw-r--r-- | main/parse.go | 243 |
4 files changed, 295 insertions, 103 deletions
diff --git a/main/command.go b/main/command.go index bbbb036..832a236 100644 --- a/main/command.go +++ b/main/command.go @@ -67,6 +67,7 @@ func (cmd AppendNextCommand) String() string { type SubstituteNextCommand struct { subex subex.Transducer + elseJump int } func (cmd SubstituteNextCommand) exec(state *ProgramState) { item, err := state.Peek() @@ -77,14 +78,14 @@ func (cmd SubstituteNextCommand) exec(state *ProgramState) { newValue, notOk := runSubex(cmd.subex, []walk.Value{item.Value}) if notOk { - state.pc++ + state.pc += cmd.elseJump } else { + state.pc++ state.Read() state.prevStart = item.PrevStart state.start = item.Start state.end = item.End state.nextEnd = item.NextEnd - state.pc += 2 state.value = newValue } } @@ -94,6 +95,7 @@ func (cmd SubstituteNextCommand) String() string { type SubstituteAppendNextCommand struct { subex subex.Transducer + elseJump int } func (cmd SubstituteAppendNextCommand) exec(state *ProgramState) { item, err := state.Peek() @@ -104,14 +106,14 @@ func (cmd SubstituteAppendNextCommand) exec(state *ProgramState) { newValue, notOk := runSubex(cmd.subex, []walk.Value{item.Value}) if notOk { - state.pc++ + state.pc += cmd.elseJump } else { state.Read() state.prevStart = item.PrevStart state.start = item.Start state.end = item.End state.nextEnd = item.NextEnd - state.pc += 2 + state.pc++ state.value = append(state.value, newValue...) } } @@ -140,15 +142,16 @@ func (cmd MergeCommand) String() string { type FullMergeCommand struct { subex subex.Transducer + elseJump int } func (cmd FullMergeCommand) exec(state *ProgramState) { _, notOk := runSubex(cmd.subex, state.value) if notOk { - state.pc++ + state.pc += cmd.elseJump return } if !state.start { - state.pc += 2 + state.pc++ return } @@ -170,7 +173,7 @@ func (cmd FullMergeCommand) exec(state *ProgramState) { state.start = item.Start state.end = item.End state.nextEnd = item.NextEnd - state.pc += 2 + state.pc++ return } } @@ -198,13 +201,14 @@ func runSubex(state subex.Transducer, in []walk.Value) ([]walk.Value, bool) { type SubstituteValueCommand struct { subex subex.Transducer + elseJump int } func (cmd SubstituteValueCommand) exec(state *ProgramState) { newValue, err := runSubex(cmd.subex, state.value) if err { - state.pc++ + state.pc += cmd.elseJump } else { - state.pc += 2 + state.pc++ state.value = newValue } } @@ -212,54 +216,72 @@ func (cmd SubstituteValueCommand) String() string { return "s/.../" } -type IsStartCommand struct {} +type IsStartCommand struct { + elseJump int +} func (cmd IsStartCommand) exec(state *ProgramState) { if state.start { - state.pc += 2 + state.pc++ } else { - state.pc += 1 + state.pc += cmd.elseJump } } func (cmd IsStartCommand) String() string { return "a" } -type IsPrevStartCommand struct {} +type IsPrevStartCommand struct { + elseJump int +} func (cmd IsPrevStartCommand) exec(state *ProgramState) { if state.prevStart { - state.pc += 2 + state.pc++ } else { - state.pc += 1 + state.pc += cmd.elseJump } } func (cmd IsPrevStartCommand) String() string { return "A" } -type IsEndCommand struct {} +type IsEndCommand struct { + elseJump int +} func (cmd IsEndCommand) exec(state *ProgramState) { if state.end { - state.pc += 2 + state.pc++ } else { - state.pc += 1 + state.pc += cmd.elseJump } } func (cmd IsEndCommand) String() string { return "e" } -type IsNextEndCommand struct {} +type IsNextEndCommand struct { + elseJump int +} func (cmd IsNextEndCommand) exec(state *ProgramState) { if state.nextEnd { - state.pc += 2 + state.pc++ } else { - state.pc += 1 + state.pc += cmd.elseJump } } func (cmd IsNextEndCommand) String() string { return "E" } +type LabelCommand struct { + label rune +} +func (cmd LabelCommand) exec(state *ProgramState) { + state.pc++ +} +func (cmd LabelCommand) String() string { + return fmt.Sprintf(":%c", cmd.label) +} + type NoopCommand struct {} func (cmd NoopCommand) exec(state *ProgramState) { state.pc++ @@ -290,13 +312,14 @@ func (cmd AppendXRegCommand) String() string { type SubstituteToXRegCommand struct { subex subex.Transducer + elseJump int } func (cmd SubstituteToXRegCommand) exec(state *ProgramState) { newValue, err := runSubex(cmd.subex, state.value) if err { - state.pc++ + state.pc += cmd.elseJump } else { - state.pc += 2 + state.pc++ state.xreg = newValue } } @@ -306,13 +329,14 @@ func (cmd SubstituteToXRegCommand) String() string { type SubstituteAppendXRegCommand struct { subex subex.Transducer + elseJump int } func (cmd SubstituteAppendXRegCommand) exec(state *ProgramState) { newValue, err := runSubex(cmd.subex, state.value) if err { - state.pc++ + state.pc += cmd.elseJump } else { - state.pc += 2 + state.pc++ state.xreg = append(state.xreg, newValue...) } } @@ -342,13 +366,14 @@ func (cmd AppendYRegCommand) String() string { type SubstituteToYRegCommand struct { subex subex.Transducer + elseJump int } func (cmd SubstituteToYRegCommand) exec(state *ProgramState) { newValue, err := runSubex(cmd.subex, state.value) if err { - state.pc++ + state.pc += cmd.elseJump } else { - state.pc += 2 + state.pc++ state.yreg = newValue } } @@ -358,13 +383,14 @@ func (cmd SubstituteToYRegCommand) String() string { type SubstituteAppendYRegCommand struct { subex subex.Transducer + elseJump int } func (cmd SubstituteAppendYRegCommand) exec(state *ProgramState) { newValue, err := runSubex(cmd.subex, state.value) if err { - state.pc++ + state.pc += cmd.elseJump } else { - state.pc += 2 + state.pc++ state.yreg = append(state.xreg, newValue...) } } @@ -394,13 +420,14 @@ func (cmd AppendZRegCommand) String() string { type SubstituteToZRegCommand struct { subex subex.Transducer + elseJump int } func (cmd SubstituteToZRegCommand) exec(state *ProgramState) { newValue, err := runSubex(cmd.subex, state.value) if err { - state.pc++ + state.pc += cmd.elseJump } else { - state.pc += 2 + state.pc++ state.zreg = newValue } } @@ -410,13 +437,14 @@ func (cmd SubstituteToZRegCommand) String() string { type SubstituteAppendZRegCommand struct { subex subex.Transducer + elseJump int } func (cmd SubstituteAppendZRegCommand) exec(state *ProgramState) { newValue, err := runSubex(cmd.subex, state.value) if err { - state.pc++ + state.pc += cmd.elseJump } else { - state.pc += 2 + state.pc++ state.zreg = append(state.xreg, newValue...) } } @@ -424,6 +452,16 @@ func (cmd SubstituteAppendZRegCommand) String() string { return "Z/.../" } +type RelativeJumpCommand struct { + destination int +} +func (cmd RelativeJumpCommand) exec(state *ProgramState) { + state.pc += cmd.destination +} +func (cmd RelativeJumpCommand) String() string { + return fmt.Sprintf("b+%v", cmd.destination) +} + type JumpCommand struct { destination int } diff --git a/main/lex.go b/main/lex.go index da517cc..0bcdaec 100644 --- a/main/lex.go +++ b/main/lex.go @@ -180,7 +180,7 @@ func lexCommand(l *lexer) stateFunc { case '}': l.emit(TokenRBrace) return lexCommand - case 's', 'S', 'M': + case 's', 'S', 'M', 'r': l.emit(TokenCommand) return lexSubstitution case 'x', 'X', 'y', 'Y', 'z', 'Z', 'n', 'N': diff --git a/main/main_test.go b/main/main_test.go index 3d10c48..802d248 100644 --- a/main/main_test.go +++ b/main/main_test.go @@ -6,7 +6,8 @@ import ( ) const 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}]}` -const mixedArray string = `{"array":["first",{"name":"second"},"third"]}` +const mixedArray string = `{"array":["first",null,3,{"name":"second"},"third"]}` +const mixedArray2 string = `{"array":["first",null,3,"second",{"name":"third"}]}` func TestSpecificCases(t *testing.T) { type test struct { @@ -20,127 +21,133 @@ func TestSpecificCases(t *testing.T) { tests := []test { { name: "Verbose Extract", - program: `s/#(~(people)~$_@(1$_#(~(first_name)~$_.|(..$_){-0})-|(..$_){-0})-|(..$_){-0})-/p`, + program: `s/#(~(people)~>_@(1>_#(~(first_name)~>_.|(..)>_*)-|(..)>_*)-|(..)>_*)-/p`, quiet: true, input: miscInput, expected: `"Tom"`, }, { name: "Extract", - program: `s/#("people"$_ @(1 $_#("first_name"$_ .)-)-)-/p`, + program: `s/#("people">_ @(1>_#("first_name">_ .)-)-)-/p`, quiet: true, input: miscInput, expected: `"Tom"`, }, { name: "Simple Extract", - program: "s/#(\"people\" @(1 #(\"first_name\" (.$a))-)-)-$_ `$a`/p", + program: "s/#(\"people\" @(1 #(\"first_name\" (.>a))-)-)->_ <a/p", quiet: true, input: miscInput, expected: `"Tom"`, }, { name: "Larger Extract", - program: "s/#(\"people\" @(2 (.$a))-)-$_ `$a`/p", + program: "s/#(\"people\" @(2 (.>a))-)->_ `<a`/p", quiet: true, input: miscInput, expected: `{"first_name":"Charlie","last_name":"Chaplin","age":122}`, }, { name: "Extract ages", - program: "s/#(\"people\"$_ :(#(\"age\"$_ .)-):)-/p", + program: "s/#(\"people\">_ :(#(\"age\">_ .)-):)-/p", quiet: true, input: miscInput, expected: `[22,18,122,48]`, }, { name: "Low memory count people", - program: "aX/#(\"people\" :(#()#):)#$_ `1`/o es/#()#/{ xs/.{-0}+/p }", + program: "aX/#(\"people\" :(#()#):)#>_ `1`/o es/#()#/{ xs/.*%+/p }", quiet: true, input: miscInput, expected: "4", }, { name: "Get full names", - 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 } }", + program: "s/#(\"people\">_ .)-/{ s/:():/p as/:(#()#):/{ xdx } s/:(#((\"first_name\"|\"last_name\") .)#)-/X es/@(.#()-)-/{ xs/(#(\"first_name\"\".*>a\")#|#(\"last_name\"\".*>b\")#|.)*>_`\"<a <b\"`/Xxs/-(..)@/p } }", quiet: true, input: miscInput, expected: `["Charlie Johnson","Tom Johnson","Charlie Chaplin","John Johnson"]`, }, { name: "Get full names 2", - 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 } }", + program: "s/#(\"people\">_.)-/{ s/:():/p as/:(#()#):/{ xdx } X/:(#((\"first_name\"|\"last_name\") .)#)-/o es/@(.#()-)-/{ xX/(#(\"first_name\"\".*>a\")#|#(\"last_name\"\".*>b\")#|.)*_`\"<a <b\"`/xs/-(..)@/p } }", quiet: true, input: miscInput, expected: `["Charlie Johnson","Tom Johnson","Charlie Chaplin","John Johnson"]`, }, { name: "Change full names in place", - program: "s/#(\"people\" @(. #(\"first_name\" .)#)@)#/{ Nms/#(\"people\" @(. (#(\"first_name\" \".{-0}$a\" \"last_name\" \".{-0}$b\")#$_) `#(\"name\" \"$a $b\")#`)@)#/ }", + program: "s/#(\"people\"@(.#(\"first_name\".)#)@)#/{ Nms/#(\"people\"@(.(#(\"first_name\"\".*>a\"\"last_name\"\".*>b\")#_)`#(\"name\"\"<a <b\")#`)@)#/ }", input: miscInput, expected: `{"something":{"nested":"Here is my test value"},"array":["Hello","world","these","are","values"],"people":[{"name":"Charlie Johnson","age":22},{"name":"Tom Johnson","age":18},{"name":"Charlie Chaplin","age":122},{"name":"John Johnson","age":48}]}`, }, { name: "Get full names with substitute next command", - program: "s/#( \"people\"$_ :( #( \"first_name\"$_ . )- )- )-/{ N/#( \"people\"$_ :( #( \"last_name\"$_ . )- )- )-/{ s/-( -( ~(.{-0}` `)- ~(.{-0})- )~ ):/p }}", + program: "s/#(\"people\"_:(#(\"first_name\"_.)-)-)-/{ N/#(\"people\"_:(#(\"last_name\"_.)-)-)-/{ s/-(-(~(.*` `)-~(.*)-)~):/p }}", quiet: true, 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 }", + program: "s/#(\"people\"_:():)-/p M/#(\"people\"@(.#()#)@)#/{ s/#(\"people\"_@(.#[(\"first_name\"\".*>a\"|\"last_name\"\".*>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 }", + program: "as/#(\"array\"_:():)-/{ :s N/#(._.)-/{ es/.*:():/be mbs } :em s/:(-(~(.*` `)-*~(.*)-)~)-/p }", quiet: true, input: miscInput, expected: `"Hello world these are values"`, }, { name: "Short concat array values", - program: "M/#( \"array\" :(): )#/{ s/#( \"array\"$_ :( .{-0} )- )-/ s/-( ~(.{-0}` `)-{-0} ~(.{-0})- )~/p }", + program: "M/#(\"array\":():)#/{ s/#(\"array\"_:(.*)-)-/ s/-(~(.*` `)-*~(.*)-)~/p }", quiet: true, input: miscInput, expected: `"Hello world these are values"`, }, { name: "Drop first element of array", - program: `s/#( "people" @( 0 . )@ )#/d`, + program: `s/#("people"@(0 .)@)#/d`, input: miscInput, expected: `{"something":{"nested":"Here is my test value"},"array":["Hello","world","these","are","values"],"people":[{"first_name":"Tom","last_name":"Johnson","age":18},{"first_name":"Charlie","last_name":"Chaplin","age":122},{"first_name":"John","last_name":"Johnson","age":48}]}`, }, { name: "Drop last element of array", - program: `M/#( "people" @( . #()# )@ )#/{ Ed }`, + program: `M/#("people"@(.,)@)#/{ Ed }`, input: miscInput, expected: `{"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}]}`, }, { name: "Drop last element of simple array", - program: `s/#( "array" @( . . )@ )#/{ Ed }`, + program: `s/#("array"@(..)@)#/{ Ed }`, input: miscInput, expected: `{"something":{"nested":"Here is my test value"},"array":["Hello","world","these","are"],"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}]}`, }, { name: "Drop last element of mixed array", - program: `M/#( "array" @( . (~[.]~|@()@|#()#) )@ )#/{ Ed }`, + program: `M/#("array"@(.,)@)#/{ Ed }`, input: mixedArray, - expected: `{"array":["first",{"name":"second"}]}`, + expected: `{"array":["first",null,3,{"name":"second"}]}`, + }, + { + name: "Drop last element of mixed array 2", + program: `M/#("array"@(.,)@)#/{ Ed }`, + input: mixedArray2, + expected: `{"array":["first",null,3,"second"]}`, }, { name: "Prepend to array", - program: "as/#( \"array\" :( `\"First\"` ): )#/", + program: "as/#(\"array\":(`\"First\"`):)#/", input: miscInput, expected: `{"something":{"nested":"Here is my test value"},"array":["First","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}]}`, }, { name: "Append to array", - program: "es/#( \"array\" :( `\"Last\"` ): )#/", + program: "es/#(\"array\":(`\"Last\"`):)#/", input: miscInput, expected: `{"something":{"nested":"Here is my test value"},"array":["Hello","world","these","are","values","Last"],"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}]}`, }, diff --git a/main/parse.go b/main/parse.go index 36bd3ee..d0a0255 100644 --- a/main/parse.go +++ b/main/parse.go @@ -10,7 +10,6 @@ import ( type parser struct { tokenStream chan Token rewinds []Token - labels map[rune]int } func (p *parser) next() Token { var token Token @@ -53,146 +52,288 @@ func (p *parser) parseSubex() subex.SubexAST { return subexAST } -func (p *parser) parseBasicCommand(commands []Command, commandChar rune) []Command { +func (p *parser) parseBasicCommand(commandChar rune) []Command { switch commandChar { case 'p': - return append(commands, PrintValueCommand{}) + return []Command {PrintValueCommand{}} case 'd': - return append(commands, DeleteValueCommand{}) + return []Command {DeleteValueCommand{}} case 'n': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { - return append(commands, NextCommand{}) + return []Command {NextCommand{}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, SubstituteNextCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + SubstituteNextCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'N': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { - return append(commands, AppendNextCommand{}) + return []Command {AppendNextCommand{}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, SubstituteAppendNextCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + SubstituteAppendNextCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'm': - return append(commands, MergeCommand{}) + return []Command {MergeCommand {}} case 'M': ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, FullMergeCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + FullMergeCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 's': ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, SubstituteValueCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + SubstituteValueCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) + case 'r': + ast := p.parseSubex() + subex := subex.CompileTransducer(ast) + return []Command { + SubstituteValueCommand { + subex: subex, + elseJump: 2, + }, + RelativeJumpCommand { + destination: -1, + }, + } case 'o': - return append(commands, NoopCommand{}) + return []Command {NoopCommand {}} case 'x': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { - return append(commands, SwapXRegCommand{}) + return []Command {SwapXRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, SubstituteToXRegCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + SubstituteToXRegCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'X': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { - return append(commands, AppendXRegCommand{}) + return []Command {AppendXRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, SubstituteAppendXRegCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + SubstituteAppendXRegCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'y': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { - return append(commands, SwapYRegCommand{}) + return []Command {SwapYRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, SubstituteToYRegCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + SubstituteToYRegCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'Y': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { - return append(commands, AppendYRegCommand{}) + return []Command {AppendYRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, SubstituteAppendYRegCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + SubstituteAppendYRegCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'z': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { - return append(commands, SwapZRegCommand{}) + return []Command {SwapZRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, SubstituteToZRegCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + SubstituteToZRegCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'Z': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { - return append(commands, AppendZRegCommand{}) + return []Command {AppendZRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) - return append(commands, SubstituteAppendZRegCommand {subex}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + SubstituteAppendZRegCommand { + subex: subex, + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'a': - return append(commands, IsStartCommand{}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + IsStartCommand { + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'A': - return append(commands, IsPrevStartCommand{}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + IsPrevStartCommand { + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'e': - return append(commands, IsEndCommand{}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + IsEndCommand { + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case 'E': - return append(commands, IsNextEndCommand{}, JumpCommand {len(commands) + 3}) + elseBranch := p.parseCommand() + return append( + []Command { + IsNextEndCommand { + elseJump: len(elseBranch) + 1, + }, + }, + elseBranch..., + ) case ':': labelToken := p.next() if labelToken.typ != TokenLabel { panic("Missing branch label") } label, _ := utf8.DecodeRuneInString(labelToken.val) - p.labels[label] = len(commands) - return commands + return []Command { + LabelCommand { + label: label, + }, + } case 'b': labelToken := p.next() if labelToken.typ != TokenLabel { panic("Missing branch label") } label, _ := utf8.DecodeRuneInString(labelToken.val) - return append(commands, BranchPlaceholderCommand {label}) + return []Command { + BranchPlaceholderCommand { + label: label, + }, + } default: panic("Invalid command") } } -func (p *parser) parseCommand(commands []Command) []Command { +func (p *parser) parseCommand() []Command { token := p.next() switch token.typ { case TokenLBrace: - jumpToBlockCommand := &JumpCommand{0} - commands = append(commands, JumpCommand {len(commands) + 2}, jumpToBlockCommand) - commands = p.parseCommands(commands) + children := p.parseCommandSequence() if p.next().typ != TokenRBrace { panic("Missing matching }") } - jumpToBlockCommand.destination = len(commands) - return commands + return children + case TokenRBrace, TokenEOF: + p.rewind(token) + return nil case TokenCommand: commandChar, _, err := strings.NewReader(token.val).ReadRune() if err != nil { panic("Error reading a command character!?") } - return p.parseBasicCommand(commands, commandChar) + return p.parseBasicCommand(commandChar) default: panic("Invalid token, expected command") } } -func (p *parser) parseCommands(commands []Command) []Command { +func (p *parser) parseCommandSequence() []Command { + var commands []Command for { nextToken := p.peek() if nextToken.typ == TokenEOF || nextToken.typ == TokenRBrace { return commands } - commands = p.parseCommand(commands) + commands = append(commands, p.parseCommand()...) } } @@ -200,17 +341,23 @@ func Parse(tokens chan Token) []Command { p := parser { tokenStream: tokens, rewinds: nil, - labels: make(map[rune]int), } - program := p.parseCommands(nil) + program := p.parseCommandSequence() + labels := make(map[rune]int) + for i, command := range program { + switch label := command.(type) { + case LabelCommand: + labels[label.label] = i + } + } for i, command := range program { switch branch := command.(type) { - case BranchPlaceholderCommand: - destination, exists := p.labels[branch.label] - if !exists { - panic("Tried to branch to a label that doesn't exist") - } - program[i] = JumpCommand {destination} + case BranchPlaceholderCommand: + destination, exists := labels[branch.label] + if !exists { + panic("Tried to branch to a label that doesn't exist") + } + program[i] = JumpCommand {destination} } } return program |
