mirror of
https://codeberg.org/scip/golsky.git
synced 2025-12-16 12:10:58 +01:00
Added RLE parser by N.Hoffmann and incorporated it onto my gol.
This commit is contained in:
159
rle/pattern_parser.go
Normal file
159
rle/pattern_parser.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package rle
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type TokenType string
|
||||
|
||||
type Token struct {
|
||||
Type TokenType
|
||||
Literal string
|
||||
}
|
||||
|
||||
const (
|
||||
RUN_COUNT = "RUN_COUNT"
|
||||
DEAD_CELL = "DEAD_CELL"
|
||||
ALIVE_CELL = "ALIVE_CELL"
|
||||
EOL = "EOL"
|
||||
EOP = "EOP"
|
||||
)
|
||||
|
||||
type Lexer struct {
|
||||
input string
|
||||
position int
|
||||
readPosition int
|
||||
char byte
|
||||
}
|
||||
|
||||
func NewLexer(input string) *Lexer {
|
||||
l := &Lexer{input: input}
|
||||
l.readChar()
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Lexer) NextToken() Token {
|
||||
var tok Token
|
||||
|
||||
l.skipWhitespace()
|
||||
|
||||
switch l.char {
|
||||
case '$':
|
||||
tok = newToken(EOL, l.char)
|
||||
case '!':
|
||||
tok = newToken(EOP, l.char)
|
||||
case 'b':
|
||||
tok = newToken(DEAD_CELL, l.char)
|
||||
case 'o':
|
||||
tok = newToken(ALIVE_CELL, l.char)
|
||||
default:
|
||||
if isDigit(l.char) {
|
||||
tok.Type = RUN_COUNT
|
||||
tok.Literal = l.readNumber()
|
||||
return tok
|
||||
}
|
||||
}
|
||||
|
||||
l.readChar()
|
||||
return tok
|
||||
}
|
||||
|
||||
func newToken(tokenType TokenType, char byte) Token {
|
||||
return Token{Type: tokenType, Literal: string(char)}
|
||||
}
|
||||
|
||||
type PatternParser struct {
|
||||
lexer *Lexer
|
||||
currentToken Token
|
||||
peekToken Token
|
||||
}
|
||||
|
||||
func NewParser(lexer *Lexer) *PatternParser {
|
||||
p := &PatternParser{
|
||||
lexer: lexer,
|
||||
}
|
||||
p.nextToken()
|
||||
p.nextToken()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (pp *PatternParser) ParsePattern(width, height int) [][]int {
|
||||
result := make([][]int, height)
|
||||
|
||||
row := make([]int, width)
|
||||
var rowIndex int
|
||||
var colIndex int
|
||||
for {
|
||||
switch pp.currentToken.Type {
|
||||
case RUN_COUNT:
|
||||
count, _ := strconv.Atoi(pp.currentToken.Literal)
|
||||
for i := 0; i < count; i++ {
|
||||
switch pp.peekToken.Type {
|
||||
case ALIVE_CELL:
|
||||
row[rowIndex+i] = 1
|
||||
case DEAD_CELL:
|
||||
row[rowIndex+i] = 0
|
||||
case EOL:
|
||||
result[colIndex] = row
|
||||
row = make([]int, width)
|
||||
rowIndex = -1
|
||||
colIndex++
|
||||
}
|
||||
}
|
||||
|
||||
if pp.peekToken.Type != EOL {
|
||||
rowIndex += count - 1
|
||||
}
|
||||
pp.nextToken()
|
||||
case ALIVE_CELL:
|
||||
row[rowIndex] = 1
|
||||
case DEAD_CELL:
|
||||
row[rowIndex] = 0
|
||||
case EOL:
|
||||
result[colIndex] = row
|
||||
row = make([]int, width)
|
||||
rowIndex = -1
|
||||
colIndex++
|
||||
case EOP:
|
||||
result[colIndex] = row
|
||||
return result
|
||||
}
|
||||
rowIndex++
|
||||
pp.nextToken()
|
||||
}
|
||||
}
|
||||
|
||||
func (pp *PatternParser) nextToken() {
|
||||
pp.currentToken = pp.peekToken
|
||||
pp.peekToken = pp.lexer.NextToken()
|
||||
}
|
||||
|
||||
func isDigit(char byte) bool {
|
||||
return '0' <= char && char <= '9'
|
||||
}
|
||||
|
||||
func (l *Lexer) readChar() {
|
||||
if l.readPosition >= len(l.input) {
|
||||
l.char = 0
|
||||
} else {
|
||||
l.char = l.input[l.readPosition]
|
||||
}
|
||||
l.position = l.readPosition
|
||||
l.readPosition++
|
||||
}
|
||||
|
||||
func (l *Lexer) readNumber() string {
|
||||
position := l.position
|
||||
for isDigit(l.char) {
|
||||
l.readChar()
|
||||
}
|
||||
|
||||
return l.input[position:l.position]
|
||||
}
|
||||
|
||||
func (l *Lexer) skipWhitespace() {
|
||||
for l.char == ' ' || l.char == '\t' || l.char == '\n' || l.char == '\r' {
|
||||
l.readChar()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user