blob: 5bfad94a07dcb5aee0a5c926ad201f23b1e27ec6 [file] [log] [blame]
package parse
import (
"errors"
"regexp"
"strconv"
"strings"
"go.skia.org/infra/fiddlek/go/types"
"go.skia.org/infra/go/skerr"
)
var (
ErrorInactiveExample = errors.New("Inactive example (ifdef'd out)")
// registerFiddleRegex is used to parse the REG_FIDDLE macro found in the sample code.
registerFiddleRegex = regexp.MustCompile(`REG_FIDDLE\((?P<name>\w+),\s+(?P<width>\w+),\s+(?P<height>\w+),\s+(?P<textonly>\w+),\s+(?P<source>\w+)\)`)
registerAnimatedFiddleRegex = regexp.MustCompile(`REG_FIDDLE_ANIMATED\((?P<name>\w+),\s+(?P<width>\w+),\s+(?P<height>\w+),\s+(?P<textonly>\w+),\s+(?P<source>\w+),\s+(?P<duration>\S+)\)`)
registerSRGBFiddleRegex = regexp.MustCompile(`REG_FIDDLE_SRGB\((?P<name>\w+),\s+(?P<width>\w+),\s+(?P<height>\w+),\s+(?P<textonly>\w+),\s+(?P<source>\w+),\s+(?P<duration>\S+),\s+(?P<usefloat16>\w+)\)`)
)
// ParseCpp parses a Skia example and returns a FiddleContext that's ready to run.
//
// Returns ErrorInactiveExample is the example is ifdef'd out. Other errors
// indicate a failure to parse the code or options.
func ParseCpp(body string) (*types.FiddleContext, error) {
if body[:3] == "#if" {
return nil, ErrorInactiveExample
}
fCtx, err := tryParsingAsNormalFiddle(body)
if err == failedToMatchErr {
fCtx, err = tryParsingAsAnimatedFiddle(body)
if err == failedToMatchErr {
fCtx, err = tryParsingAsSRGBFiddle(body)
if err == failedToMatchErr {
return nil, skerr.Fmt("failed to find any REG_FIDDLE* macro")
}
}
}
if err != nil {
return nil, skerr.Wrap(err)
}
// Extract the code.
lines := strings.Split(body, "\n")
var code []string
foundREG := false
foundEnd := false
for _, line := range lines {
if !foundREG {
if strings.HasPrefix(line, "REG_FIDDLE") {
foundREG = true
}
continue
}
if strings.Contains(line, "END FIDDLE") {
foundEnd = true
break
}
code = append(code, line)
}
if !foundEnd {
return nil, skerr.Fmt("failed to find END FIDDLE")
}
fCtx.Code = strings.Join(code, "\n")
return fCtx, nil
}
var failedToMatchErr = errors.New("failed to match")
func tryParsingAsNormalFiddle(body string) (*types.FiddleContext, error) {
// Parse up the REG_FIDDLE macro values.
match := registerFiddleRegex.FindStringSubmatch(body)
if len(match) == 0 {
return nil, failedToMatchErr
}
namedGroup := func(name string) string {
for i, groupName := range registerFiddleRegex.SubexpNames() {
if i != 0 && name == groupName {
return match[i]
}
}
panic("Could not find group with name " + name)
}
width, err := strconv.Atoi(namedGroup("width"))
if err != nil {
return nil, skerr.Wrapf(err, "parsing width")
}
height, err := strconv.Atoi(namedGroup("height"))
if err != nil {
return nil, skerr.Wrapf(err, "parsing height")
}
source, err := strconv.Atoi(namedGroup("source"))
if err != nil {
return nil, skerr.Wrapf(err, "parsing source")
}
textonly := namedGroup("textonly") == "true"
return &types.FiddleContext{
Name: namedGroup("name"),
Options: types.Options{
Width: width,
Height: height,
Source: source,
TextOnly: textonly,
},
}, nil
}
func tryParsingAsAnimatedFiddle(body string) (*types.FiddleContext, error) {
// Parse up the REG_FIDDLE_ANIMATED macro values.
match := registerAnimatedFiddleRegex.FindStringSubmatch(body)
if len(match) == 0 {
return nil, failedToMatchErr
}
namedGroup := func(name string) string {
for i, groupName := range registerAnimatedFiddleRegex.SubexpNames() {
if i != 0 && name == groupName {
return match[i]
}
}
panic("Could not find group with name " + name)
}
width, err := strconv.Atoi(namedGroup("width"))
if err != nil {
return nil, skerr.Wrapf(err, "parsing width")
}
height, err := strconv.Atoi(namedGroup("height"))
if err != nil {
return nil, skerr.Wrapf(err, "parsing height")
}
source, err := strconv.Atoi(namedGroup("source"))
if err != nil {
return nil, skerr.Wrapf(err, "parsing source")
}
duration, err := strconv.ParseFloat(namedGroup("duration"), 64)
if err != nil {
return nil, skerr.Wrapf(err, "parsing duration")
}
textonly := namedGroup("textonly") == "true"
return &types.FiddleContext{
Name: namedGroup("name"),
Options: types.Options{
Width: width,
Height: height,
Source: source,
TextOnly: textonly,
Duration: duration,
Animated: true,
},
}, nil
}
func tryParsingAsSRGBFiddle(body string) (*types.FiddleContext, error) {
// Parse up the REG_FIDDLE_SRGB macro values.
match := registerSRGBFiddleRegex.FindStringSubmatch(body)
if len(match) == 0 {
return nil, failedToMatchErr
}
namedGroup := func(name string) string {
for i, groupName := range registerSRGBFiddleRegex.SubexpNames() {
if i != 0 && name == groupName {
return match[i]
}
}
panic("Could not find group with name " + name)
}
width, err := strconv.Atoi(namedGroup("width"))
if err != nil {
return nil, skerr.Wrapf(err, "parsing width")
}
height, err := strconv.Atoi(namedGroup("height"))
if err != nil {
return nil, skerr.Wrapf(err, "parsing height")
}
source, err := strconv.Atoi(namedGroup("source"))
if err != nil {
return nil, skerr.Wrapf(err, "parsing source")
}
duration, err := strconv.ParseFloat(namedGroup("duration"), 64)
if err != nil {
return nil, skerr.Wrapf(err, "parsing duration")
}
textonly := namedGroup("textonly") == "true"
useFloat16 := namedGroup("usefloat16") == "true"
return &types.FiddleContext{
Name: namedGroup("name"),
Options: types.Options{
Width: width,
Height: height,
Source: source,
TextOnly: textonly,
Animated: duration > 0,
Duration: duration,
SRGB: true,
F16: useFloat16,
},
}, nil
}