281 lines
5.2 KiB
Go
281 lines
5.2 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
func DebugParser(input []rune, writer io.Writer) {
|
||
|
p := NewParser(input)
|
||
|
tdl := p.Parse()
|
||
|
fmt.Fprintln(writer, string(tdl.JSON()))
|
||
|
}
|
||
|
|
||
|
type Parser struct {
|
||
|
lexer *Lexer
|
||
|
sym Sym
|
||
|
}
|
||
|
|
||
|
func NewParser(input []rune) *Parser {
|
||
|
p := &Parser{lexer: NewLexer(input)}
|
||
|
p.get()
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
func (p *Parser) exitWith(v ...interface{}) {
|
||
|
v = append([]interface{}{"syntax error in line", p.lexer.line, ":"}, v...)
|
||
|
panic(fmt.Sprintln(v...))
|
||
|
}
|
||
|
|
||
|
func (p *Parser) invalid(what string) {
|
||
|
p.exitWith("invalid", what, ">", p.sym.Value, "<")
|
||
|
}
|
||
|
|
||
|
func (p *Parser) string() string {
|
||
|
val := strings.TrimPrefix(strings.TrimSuffix(p.sym.Value, "\""), "\"")
|
||
|
if p.sym.Type != SymString {
|
||
|
p.invalid("string")
|
||
|
}
|
||
|
p.get()
|
||
|
return val
|
||
|
}
|
||
|
|
||
|
func (p *Parser) ident() string {
|
||
|
val := p.sym.Value
|
||
|
if p.sym.Type != SymIdent {
|
||
|
p.invalid("identifier")
|
||
|
}
|
||
|
p.get()
|
||
|
return val
|
||
|
}
|
||
|
|
||
|
func (p *Parser) notice() string {
|
||
|
val := p.sym.Value
|
||
|
if p.sym.Type != SymNotice {
|
||
|
p.invalid("notice")
|
||
|
}
|
||
|
p.get()
|
||
|
return val
|
||
|
}
|
||
|
|
||
|
func (p *Parser) float() float64 {
|
||
|
val := p.sym.Value
|
||
|
f, err := strconv.ParseFloat(val, 64)
|
||
|
if err != nil || (p.sym.Type != SymFloat && p.sym.Type != SymInteger) {
|
||
|
p.invalid("float")
|
||
|
}
|
||
|
p.get()
|
||
|
return f
|
||
|
}
|
||
|
|
||
|
func (p *Parser) int() int {
|
||
|
val := p.sym.Value
|
||
|
i, err := strconv.Atoi(val)
|
||
|
if err != nil || p.sym.Type != SymInteger {
|
||
|
p.invalid("integer")
|
||
|
}
|
||
|
p.get()
|
||
|
return i
|
||
|
}
|
||
|
|
||
|
func (p *Parser) get() {
|
||
|
p.sym = p.lexer.Lex()
|
||
|
}
|
||
|
|
||
|
func (p *Parser) expect(symTypes ...SymType) {
|
||
|
expected := []string{}
|
||
|
for _, symType := range symTypes {
|
||
|
if p.sym.Type == symType {
|
||
|
p.get()
|
||
|
return
|
||
|
}
|
||
|
expected = append(expected, symType.String())
|
||
|
}
|
||
|
p.exitWith("expected >", strings.Join(expected, " or "), "< but got:", p.sym)
|
||
|
}
|
||
|
|
||
|
func (p *Parser) hasPreamble(s string) bool {
|
||
|
return p.sym.Type == SymPreamble && p.sym.Value == s
|
||
|
}
|
||
|
|
||
|
func (p *Parser) expectPreamble(s string) {
|
||
|
if p.hasPreamble(s) {
|
||
|
p.get()
|
||
|
} else {
|
||
|
p.exitWith("expected preamble >", s, "< but got:", p.sym)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*---------------------*/
|
||
|
|
||
|
func (p *Parser) Parse() (r TDL) {
|
||
|
r = TDL{}
|
||
|
r.Lifts = p.Lifts()
|
||
|
if p.hasPreamble("Plates:") {
|
||
|
r.Plates = p.Plates()
|
||
|
}
|
||
|
if p.hasPreamble("SetTemplates:") {
|
||
|
r.SetTemplates = p.SetTemplates()
|
||
|
}
|
||
|
r.TrainingDays = p.TrainingDays()
|
||
|
p.expect(SymEOI)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) Lifts() (r []Lift) {
|
||
|
p.expectPreamble("Lifts:")
|
||
|
r = append(r, p.Lift())
|
||
|
for p.sym.Type == SymComma {
|
||
|
p.get()
|
||
|
r = append(r, p.Lift())
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) Lift() (r Lift) {
|
||
|
r.Name = p.ident()
|
||
|
p.expectPreamble("Max:")
|
||
|
r.Max = p.float()
|
||
|
p.expectPreamble("Increment:")
|
||
|
r.Increment = p.float()
|
||
|
if p.sym.Type == SymPercent {
|
||
|
p.get()
|
||
|
r.IncrementPercent = true
|
||
|
}
|
||
|
if p.hasPreamble("Bar:") {
|
||
|
p.expectPreamble("Bar:")
|
||
|
r.Bar = p.float()
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) Plates() (r []Plate) {
|
||
|
p.expectPreamble("Plates:")
|
||
|
r = append(r, p.Plate())
|
||
|
for p.sym.Type == SymComma {
|
||
|
p.get()
|
||
|
r = append(r, p.Plate())
|
||
|
}
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
func (p *Parser) Plate() (r Plate) {
|
||
|
r.Weight = p.float()
|
||
|
p.expect(SymTimes)
|
||
|
r.Amount = p.int()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) SetTemplates() (r []SetTemplate) {
|
||
|
p.expectPreamble("SetTemplates:")
|
||
|
p.expect(SymDash)
|
||
|
r = append(r, p.SetTemplate())
|
||
|
for p.sym.Type == SymDash {
|
||
|
p.get()
|
||
|
r = append(r, p.SetTemplate())
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) SetTemplate() (r SetTemplate) {
|
||
|
if p.sym.Type == SymPreamble {
|
||
|
r.Name = strings.TrimSuffix(p.sym.Value, ":")
|
||
|
p.get()
|
||
|
} else {
|
||
|
p.expect(SymPreamble)
|
||
|
}
|
||
|
r.Items = append(r.Items, p.SetTemplateItem())
|
||
|
for p.sym.Type == SymComma {
|
||
|
p.get()
|
||
|
r.Items = append(r.Items, p.SetTemplateItem())
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) SetTemplateItem() (r SetTemplateItem) {
|
||
|
if p.sym.Type == SymIdent {
|
||
|
r.ReferenceName = p.ident()
|
||
|
} else {
|
||
|
r.Set = p.Set()
|
||
|
}
|
||
|
if p.sym.Type == SymTimes {
|
||
|
p.expect(SymTimes)
|
||
|
r.Amount = p.int()
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) Set() (r Set) {
|
||
|
r.Reps = p.int()
|
||
|
p.expect(SymReps)
|
||
|
p.expect(SymAt)
|
||
|
r.Percentage = p.float()
|
||
|
p.expect(SymPercent)
|
||
|
if p.sym.Type == SymPlus {
|
||
|
p.expect(SymPlus)
|
||
|
r.PlusWeight = p.float()
|
||
|
}
|
||
|
if p.sym.Type == SymTimes {
|
||
|
p.expect(SymTimes)
|
||
|
r.Amount = p.int()
|
||
|
}
|
||
|
if p.sym.Type == SymNotice {
|
||
|
r.Notice = p.notice()
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) TrainingDays() (r []TrainingDay) {
|
||
|
p.expectPreamble("TrainingDays:")
|
||
|
p.expect(SymDash)
|
||
|
r = append(r, p.TrainingDay())
|
||
|
for p.sym.Type == SymDash {
|
||
|
p.get()
|
||
|
r = append(r, p.TrainingDay())
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) TrainingDay() (r TrainingDay) {
|
||
|
for p.sym.Type != SymDash && p.sym.Type != SymEOI {
|
||
|
r.Items = append(r.Items, p.TrainingDayItem())
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) TrainingDayItem() (r TrainingDayItem) {
|
||
|
if p.sym.Type == SymString {
|
||
|
r.Raw = p.string()
|
||
|
} else {
|
||
|
r.LiftSchedule = p.LiftSchedule()
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (p *Parser) LiftSchedule() (r LiftSchedule) {
|
||
|
if p.sym.Type == SymPreamble {
|
||
|
r.LiftName = strings.TrimSuffix(p.sym.Value, ":")
|
||
|
p.get()
|
||
|
} else {
|
||
|
p.expect(SymPreamble)
|
||
|
}
|
||
|
r.Items = append(r.Items, p.SetTemplateItem())
|
||
|
for p.sym.Type == SymComma {
|
||
|
p.get()
|
||
|
r.Items = append(r.Items, p.SetTemplateItem())
|
||
|
}
|
||
|
if p.sym.Type == SymIncrease {
|
||
|
r.Increase = true
|
||
|
p.get()
|
||
|
if p.sym.Type == SymFloat || p.sym.Type == SymInteger {
|
||
|
r.IncreaseAmount = p.float()
|
||
|
if p.sym.Type == SymPercent {
|
||
|
r.IncreasePercent = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|