diff options
Diffstat (limited to 'main')
| -rw-r--r-- | main/command.go | 135 | ||||
| -rw-r--r-- | main/lex.go | 2 | ||||
| -rw-r--r-- | main/main_test.go | 89 | ||||
| -rw-r--r-- | main/parse.go | 245 |
4 files changed, 379 insertions, 92 deletions
diff --git a/main/command.go b/main/command.go index 38e1c95..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...) } } @@ -138,9 +140,43 @@ func (cmd MergeCommand) String() string { return "m" } -type FullMergeCommand struct {} +type FullMergeCommand struct { + subex subex.Transducer + elseJump int +} func (cmd FullMergeCommand) exec(state *ProgramState) { - panic("Unimplemented") + _, notOk := runSubex(cmd.subex, state.value) + if notOk { + state.pc += cmd.elseJump + return + } + if !state.start { + state.pc++ + return + } + + for { + item, err := state.Read() + if err != nil { + panic("Missing next value") + } + + _, nonTerminal := runSubex(cmd.subex, []walk.Value{item.Value}) + + state.value = append( + state.value[:len(state.value) - 1], + walk.Merge(state.value[len(state.value) - 1], item.Value)... + ) + + if !nonTerminal && item.End { + state.prevStart = item.PrevStart + state.start = item.Start + state.end = item.End + state.nextEnd = item.NextEnd + state.pc++ + return + } + } } func (cmd FullMergeCommand) String() string { return "M" @@ -165,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 } } @@ -179,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++ @@ -257,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 } } @@ -273,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...) } } @@ -309,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 } } @@ -325,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...) } } @@ -361,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 } } @@ -377,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...) } } @@ -391,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 8e66890..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': + 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 1510497..802d248 100644 --- a/main/main_test.go +++ b/main/main_test.go @@ -5,9 +5,11 @@ 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}]}` +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",null,3,{"name":"second"},"third"]}` +const mixedArray2 string = `{"array":["first",null,3,"second",{"name":"third"}]}` -func TestMain(t *testing.T) { +func TestSpecificCases(t *testing.T) { type test struct { name string program string @@ -19,73 +21,136 @@ func TestMain(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\"\".*>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/.*:():/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\"_:(.*)-)-/ s/-(~(.*` `)-*~(.*)-)~/p }", + quiet: true, + input: miscInput, + expected: `"Hello world these are values"`, + }, + { + name: "Drop first element of array", + 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 }`, + 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 }`, + 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 }`, + input: mixedArray, + 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\"`):)#/", + 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\"`):)#/", + 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}]}`, + }, } for _, test := range tests { diff --git a/main/parse.go b/main/parse.go index 36917ac..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,142 +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) + 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()...) } } @@ -196,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 |
