blob: d85ba310a811b31bd50805e54e67350816ab350d [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
"fmt"
"path/filepath"
"strings"
"android.googlesource.com/platform/tools/gpu/framework/stringtable"
"android.googlesource.com/platform/tools/gpu/framework/stringtable/minidown"
"android.googlesource.com/platform/tools/gpu/framework/stringtable/minidown/node"
)
// Parse parses the given string table file.
func Parse(filename, data string) (*stringtable.StringTable, []error) {
var errs []error
// Start by parsing the minidown
minidown, parseErrs := minidown.Parse(filename, data)
if len(parseErrs) > 0 {
errs = append(errs, parseErrs)
}
// Process the minidown into stringtable entries.
entries, processErrs := process(minidown)
if len(processErrs) > 0 {
errs = append(errs, processErrs...)
}
table := &stringtable.StringTable{
Info: stringtable.Info{
CultureCode: strings.Split(filepath.Base(filename), ".")[0],
},
Entries: entries,
}
return table, errs
}
func process(in node.Node) (map[string]stringtable.Node, []error) {
var errs []error
table := map[string]stringtable.Node{}
var current string // Name of the current entry.
newlines := 0 // Number of pending newlines.
add := func(n node.Node) {
if len(current) == 0 {
return // No entry assigned yet. Perhaps a file header message?
}
// Convert the minidown node to a stringtable node.
converted, e := convert(n)
errs = append(errs, e...)
// Check to see if this is a new entry, or appending to an existing.
existing := table[current]
if existing == nil {
// New entry. Simply assign to the table.
table[current] = converted
newlines = 0
return
}
block, ok := existing.(*stringtable.Block)
if !ok {
// Multiple nodes for entry. Box first entry into a block.
block = &stringtable.Block{Children: []stringtable.Node{existing}}
}
// Add any pending line breaks
if newlines > 0 {
block.Children = append(block.Children, &stringtable.LineBreak{
Lines: uint32(newlines),
})
newlines = 0
}
// Add new node to the block.
block.Children = append(block.Children, converted)
// Update table.
table[current] = block
}
root, ok := in.(*node.Block)
if !ok {
return nil, []error{fmt.Errorf("Expected root to be a block, instead got %T", in)}
}
for _, n := range root.Children {
switch n := n.(type) {
case *node.NewLine:
newlines++
case *node.Heading:
// Check for a new table entry. These are declared as H1 headings.
if n.Scale == 1 {
if text, ok := n.Body.(*node.Text); ok {
// Change current.
current = text.Text
// Check for duplicates.
if _, dup := table[current]; dup {
errs = append(errs, fmt.Errorf("Duplicate stringtable entry '%s'", current))
}
} else {
errs = append(errs, fmt.Errorf("Entry name must be simple text, instead got %T", n.Body))
}
} else {
add(n) // A heading, but not H1. Add to existing entry.
}
default:
add(n)
}
}
return table, errs
}
func convert(in node.Node) (stringtable.Node, []error) {
switch in := in.(type) {
case *node.Text:
return &stringtable.Text{Text: in.Text}, nil
case *node.Whitespace:
return &stringtable.Whitespace{}, nil
case *node.Link:
body, errsA := convert(in.Body)
target, errsB := convert(in.Target)
return &stringtable.Link{Body: body, Target: target}, append(errsA, errsB...)
case *node.Tag:
return &stringtable.Parameter{Key: in.Identifier}, nil
default:
return nil, []error{fmt.Errorf("Stringtable does not currently support %T", in)}
}
}