go-dasm-mlb/parse.go

250 lines
4.5 KiB
Go

package dasmlbl
import (
"strings"
"strconv"
"fmt"
)
type Parser struct {
config *Config
items chan LexItem
}
func NewParser(items chan LexItem) *Parser {
return &Parser{
config: &Config{},
items: items,
}
}
func (p *Parser) next() LexItem {
select {
case itm := <- p.items:
//fmt.Println(itm)
return itm
}
return LexItem{lex_EOF, "EOF"}
}
func (p *Parser) Run() (*Config, error) {
for {
itm := p.next()
//fmt.Println("<", itm)
if itm.typ == lex_EOF {
break
}
if itm.typ == lex_Pound {
itm = p.next()
continue
}
if itm.typ != lex_Ident {
continue
//return nil, fmt.Errorf("expected ident, got %s", itm)
}
//if strings.ToLower(itm.val) != "label" {
// continue
//}
var err error
switch strings.ToLower(itm.val) {
case "label":
err = p.parseLabel()
case "range":
err = p.parseRange()
}
if err != nil {
return p.config, err
}
}
return p.config, nil
}
func (p *Parser) parseRange() error {
itm := p.next()
if itm.typ != lex_OpenBracket {
return fmt.Errorf("missing open bracket")
}
rng := Range{}
for {
itm = p.next()
if itm.typ == lex_CloseBracket {
break
}
if itm.typ != lex_Ident {
return fmt.Errorf("missing ident")
}
switch strings.ToLower(itm.val) {
case "name", "comment":
typ := itm.val
itm = p.next()
if itm.typ != lex_String {
return fmt.Errorf("%s requires string: %s", typ, itm)
//return fmt.Errorf("name requires string")
}
switch typ {
case "name":
rng.Name = itm.val
case "comment":
rng.Comment = itm.val
case "type":
rng.Type = itm.val
case "addrmode":
rng.AddrMode = itm.val
}
case "type", "addrmode":
itm = p.next()
if itm.typ != lex_Ident {
return fmt.Errorf("%s requires ident: %s", itm.val, itm)
}
rng.Type = itm.val
case "start", "end", "unit":
typ := itm.val
itm = p.next()
if itm.typ != lex_Number {
return fmt.Errorf("%s: %s", strings.ToLower(typ+" requires number"), itm)
}
str := itm.val
if strings.HasPrefix(str, "$") {
str = "0x"+strings.TrimPrefix(str, "$")
} else if strings.HasPrefix(str, "%") {
str = "0b"+strings.TrimPrefix(str, "%")
}
val, err := strconv.ParseInt(str, 0, 32)
if err != nil {
return err
}
switch typ {
case "start":
rng.Start = int(val)
case "end":
rng.End = int(val)
case "unit":
rng.Unit = int(val)
}
}
itm = p.next()
if itm.typ != lex_Semicolon {
return fmt.Errorf("[parseRange] missing semicolon")
}
}
p.config.Ranges = append(p.config.Ranges, rng)
return nil
}
func (p *Parser) parseLabel() error {
itm := p.next()
if itm.typ != lex_OpenBracket {
return fmt.Errorf("missing open bracket")
}
lbl := Label{}
for {
itm = p.next()
if itm.typ == lex_CloseBracket {
break
}
if itm.typ != lex_Ident {
return fmt.Errorf("missing ident")
}
//fmt.Println("]", itm)
switch strings.ToLower(itm.val) {
case "name":
itm = p.next()
if itm.typ != lex_String {
return fmt.Errorf("name requires string: %s", itm)
}
lbl.Name = itm.val
case "addr", "size", "paramsize":
typ := strings.ToLower(itm.val)
itm = p.next()
if itm.typ != lex_Number {
return fmt.Errorf("%s: %s", strings.ToLower(typ+" requires number"), itm)
}
str := itm.val
if strings.HasPrefix(str, "$") {
str = "0x"+strings.TrimPrefix(str, "$")
} else if strings.HasPrefix(str, "%") {
str = "0b"+strings.TrimPrefix(str, "%")
}
val, err := strconv.ParseInt(str, 0, 32)
if err != nil {
return err
}
switch typ {
case "addr":
lbl.Address = int(val)
case "size":
lbl.Size = int(val)
case "paramsize":
lbl.ParamSize = int(val)
}
case "comment":
itm = p.next()
if itm.typ != lex_String {
return fmt.Errorf("comment requires string")
}
lbl.Comment = itm.val
}
itm = p.next()
if itm.typ != lex_Semicolon {
return fmt.Errorf("[parseLabel] missing semicolon; %#v", lbl)
}
}
p.config.Labels = append(p.config.Labels, lbl)
return nil
}
//func (p *Parser) parseIdent(itm LexItem) error {
// switch strings.ToLower(itm.val) {
// case "global":
// select {
// case itm = <- p.items:
// if itm.typ != lex_OpenBracket {
// return fmt.Errorf("expected { after global, got %q", itm)
// }
// return p.parseGlobal()
// default:
// return fmt.Errorf("early EOF")
// }
// }
//
// return nil
//}
//func (p *Parser) parseGlobal() error {
// for {
// select {
// case itm := <- p.items:
// default:
// return fmt.Errorf("early EOF")
// }
// }
//}