mirror of
https://github.com/thunderbrewhq/binana.git
synced 2025-12-12 09:52:28 +00:00
feat(go): add format-symbols command
This commit is contained in:
parent
b8b0b5076f
commit
0eec0219f9
7 changed files with 269 additions and 142 deletions
|
|
@ -5,8 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -28,147 +26,11 @@ func (l *loader) read_line() (line string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func parse_attributes(attribute_columns []string) (attributes map[string]string, err error) {
|
||||
attributes = make(map[string]string)
|
||||
var (
|
||||
scanning_quoted_string bool
|
||||
current_key string
|
||||
current_value string
|
||||
)
|
||||
for _, attribute_column := range attribute_columns {
|
||||
if scanning_quoted_string {
|
||||
current_value += " "
|
||||
current_value += attribute_column
|
||||
|
||||
if strings.HasSuffix(attribute_column, `"`) {
|
||||
scanning_quoted_string = false
|
||||
attributes[current_key], err = strconv.Unquote(current_value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
current_key = ""
|
||||
current_value = ""
|
||||
continue
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
key, value_start, found := strings.Cut(attribute_column, "=")
|
||||
if !found {
|
||||
err = fmt.Errorf("extraneous column: '%s'", attribute_column)
|
||||
return
|
||||
}
|
||||
|
||||
current_key = key
|
||||
|
||||
if strings.HasPrefix(value_start, `"`) {
|
||||
current_value = value_start
|
||||
if strings.HasSuffix(value_start, `"`) {
|
||||
attributes[current_key], err = strconv.Unquote(value_start)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
scanning_quoted_string = true
|
||||
}
|
||||
} else {
|
||||
// unquoted, we can succeed immediately
|
||||
attributes[current_key] = value_start
|
||||
current_value = ""
|
||||
current_key = ""
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if scanning_quoted_string {
|
||||
err = fmt.Errorf("line ends in the middle of a quoted attribute --> \"%s", current_value)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *loader) parse_line(line string) (err error) {
|
||||
// trim extraneous whitespace
|
||||
line = strings.Trim(line, " \t")
|
||||
|
||||
// split into columns
|
||||
columns := strings.Split(line, " ")
|
||||
|
||||
// validate
|
||||
if len(columns) < min_columns {
|
||||
// this line is discarded but not in error
|
||||
return
|
||||
}
|
||||
var (
|
||||
start_address uint64
|
||||
comment_text string
|
||||
)
|
||||
|
||||
// get name of symbol
|
||||
name_column := columns[0]
|
||||
if name_column == "" {
|
||||
return fmt.Errorf("symfile: (*loader).parse_line: line %d: entry has invalid name '%s", l.line_number, name_column)
|
||||
}
|
||||
|
||||
start_address, err = strconv.ParseUint(columns[1], 16, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
kind_column := columns[2]
|
||||
if len(kind_column) != 1 {
|
||||
return fmt.Errorf("symfile: (*loader).parse_line: line %d: entry has invalid kind", l.line_number)
|
||||
}
|
||||
|
||||
kind := EntryKind(kind_column[0])
|
||||
|
||||
if !slices.Contains(valid_kinds, kind) {
|
||||
return fmt.Errorf("symfile: (*loader).parse_line: line %d: entry has invalid kind", l.line_number)
|
||||
}
|
||||
|
||||
// find index of comment column
|
||||
index_of_comment := slices.Index(columns, ";")
|
||||
|
||||
var num_semantic_columns int
|
||||
|
||||
if index_of_comment != -1 {
|
||||
num_semantic_columns = index_of_comment
|
||||
comment_text_columns := columns[index_of_comment+1:]
|
||||
comment_text = strings.Join(comment_text_columns, " ")
|
||||
} else {
|
||||
num_semantic_columns = len(columns)
|
||||
}
|
||||
|
||||
// Start to build entry
|
||||
var entry Entry
|
||||
entry.Name = name_column
|
||||
entry.StartAddress = start_address
|
||||
entry.Kind = kind
|
||||
entry.Comment = comment_text
|
||||
|
||||
// build attributes
|
||||
if num_semantic_columns > 3 {
|
||||
extra_columns := columns[3:num_semantic_columns]
|
||||
|
||||
var attributes map[string]string
|
||||
attributes, err = parse_attributes(extra_columns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("symfile: (*loader).parse_line: line %d: error parsing attribute: '%s'", l.line_number, err)
|
||||
}
|
||||
|
||||
if data_type, found := attributes["type"]; found {
|
||||
entry.DataType = data_type
|
||||
}
|
||||
|
||||
if end_address, found := attributes["end"]; found {
|
||||
entry.EndAddress, err = strconv.ParseUint(end_address, 16, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = entry.Parse(line); err != nil {
|
||||
err = fmt.Errorf("%w: line %d", err, l.line_number)
|
||||
return
|
||||
}
|
||||
|
||||
err = l.table.Insert(&entry)
|
||||
|
|
|
|||
153
go/symfile/parse_entry.go
Normal file
153
go/symfile/parse_entry.go
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
package symfile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parse_attributes(attribute_columns []string) (attributes map[string]string, err error) {
|
||||
attributes = make(map[string]string)
|
||||
var (
|
||||
scanning_quoted_string bool
|
||||
current_key string
|
||||
current_value string
|
||||
)
|
||||
for _, attribute_column := range attribute_columns {
|
||||
if scanning_quoted_string {
|
||||
current_value += " "
|
||||
current_value += attribute_column
|
||||
|
||||
if strings.HasSuffix(attribute_column, `"`) {
|
||||
scanning_quoted_string = false
|
||||
attributes[current_key], err = strconv.Unquote(current_value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
current_key = ""
|
||||
current_value = ""
|
||||
continue
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
key, value_start, found := strings.Cut(attribute_column, "=")
|
||||
if !found {
|
||||
err = fmt.Errorf("extraneous column: '%s'", attribute_column)
|
||||
return
|
||||
}
|
||||
|
||||
current_key = key
|
||||
|
||||
if strings.HasPrefix(value_start, `"`) {
|
||||
current_value = value_start
|
||||
if strings.HasSuffix(value_start, `"`) {
|
||||
attributes[current_key], err = strconv.Unquote(value_start)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
scanning_quoted_string = true
|
||||
}
|
||||
} else {
|
||||
// unquoted, we can succeed immediately
|
||||
attributes[current_key] = value_start
|
||||
current_value = ""
|
||||
current_key = ""
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if scanning_quoted_string {
|
||||
err = fmt.Errorf("line ends in the middle of a quoted attribute --> \"%s", current_value)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (entry *Entry) Parse(line string) (err error) {
|
||||
// trim extraneous whitespace
|
||||
line = strings.Trim(line, " \t")
|
||||
|
||||
// split into columns
|
||||
columns := strings.Split(line, " ")
|
||||
|
||||
// validate
|
||||
if len(columns) < min_columns {
|
||||
// this line is discarded but not in error
|
||||
return
|
||||
}
|
||||
var (
|
||||
start_address uint64
|
||||
comment_text string
|
||||
)
|
||||
|
||||
// get name of symbol
|
||||
name_column := columns[0]
|
||||
if name_column == "" {
|
||||
return fmt.Errorf("symfile: (*entry).Parse: entry has invalid name '%s", name_column)
|
||||
}
|
||||
|
||||
start_address, err = strconv.ParseUint(columns[1], 16, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
kind_column := columns[2]
|
||||
if len(kind_column) != 1 {
|
||||
return fmt.Errorf("symfile: (*entry).Parse: entry has invalid kind")
|
||||
}
|
||||
|
||||
kind := EntryKind(kind_column[0])
|
||||
|
||||
if !slices.Contains(valid_kinds, kind) {
|
||||
return fmt.Errorf("symfile: (*entry).Parse: entry has invalid kind")
|
||||
}
|
||||
|
||||
// find index of comment column
|
||||
index_of_comment := slices.Index(columns, ";")
|
||||
|
||||
var num_semantic_columns int
|
||||
|
||||
if index_of_comment != -1 {
|
||||
num_semantic_columns = index_of_comment
|
||||
comment_text_columns := columns[index_of_comment+1:]
|
||||
comment_text = strings.Join(comment_text_columns, " ")
|
||||
} else {
|
||||
num_semantic_columns = len(columns)
|
||||
}
|
||||
|
||||
// Start to build entry
|
||||
entry.Name = name_column
|
||||
entry.StartAddress = start_address
|
||||
entry.Kind = kind
|
||||
entry.Comment = comment_text
|
||||
|
||||
// build attributes
|
||||
if num_semantic_columns > 3 {
|
||||
extra_columns := columns[3:num_semantic_columns]
|
||||
|
||||
var attributes map[string]string
|
||||
attributes, err = parse_attributes(extra_columns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("symfile: (*entry).Parse: error parsing attribute: %w", err)
|
||||
}
|
||||
|
||||
if data_type, found := attributes["type"]; found {
|
||||
entry.DataType = data_type
|
||||
}
|
||||
|
||||
if end_address, found := attributes["end"]; found {
|
||||
entry.EndAddress, err = strconv.ParseUint(end_address, 16, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package symfile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// What kind of Entry is this?
|
||||
|
|
@ -36,6 +38,21 @@ type Entry struct {
|
|||
DataType string
|
||||
}
|
||||
|
||||
func (entry *Entry) String() string {
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, "%s %08X %c", entry.Name, entry.StartAddress, entry.Kind)
|
||||
if entry.EndAddress != 0 {
|
||||
fmt.Fprintf(&b, " end=%08X", entry.EndAddress)
|
||||
}
|
||||
if entry.DataType != "" {
|
||||
fmt.Fprintf(&b, " type=\"%s\"", entry.DataType)
|
||||
}
|
||||
if entry.Comment != "" {
|
||||
fmt.Fprintf(&b, " ; %s", entry.Comment)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
type Table interface {
|
||||
Insert(entry *Entry) (err error)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue