Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/main/parse.go
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2025-10-29 18:14:52 +0000
committerCharlie Stanton <charlie@shtanton.xyz>2025-10-29 18:14:52 +0000
commite4e454665bf7e21db21763c65bf35d23665b17cf (patch)
tree121d749fe4487739ebfb239dc40d5cc88e0097dd /main/parse.go
parentb2ce005d227a10a9b8a6f5362c87a0e34ee07acc (diff)
downloadstred-go-e4e454665bf7e21db21763c65bf35d23665b17cf.tar
Improve internals of command control flowHEADmain
Diffstat (limited to 'main/parse.go')
-rw-r--r--main/parse.go243
1 files changed, 195 insertions, 48 deletions
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