Parse dependencies

andybalholm
Mar 25, 2023, 6:47 PM
FBQT4QEBOCEVB3DZKVRUDWW4TUHG27JEQIDHN67D2PD6WQGRTSXQC

Dependencies

  • [2] EKAB33DH Start using some parser combinators
  • [3] PHRWK7NK Parse change headers

Change contents

  • edit in combinators.go at line 5
    [2.73]
    [2.73]
    "errors"
  • edit in combinators.go at line 7
    [2.80]
    [2.80]
    "strconv"
  • edit in combinators.go at line 53
    [2.1174]
    [2.1174]
    }
    }
    func tag(t string) parser[string] {
    b := []byte(t)
    return func(input []byte) (rest []byte, value string, err error) {
    if bytes.HasPrefix(input, b) {
    return input[len(b):], t, nil
    } else {
    return input, "", fmt.Errorf("not found: %q", t)
    }
    }
    }
    func value[T, U any](val T, p parser[U]) parser[T] {
    return func(input []byte) (rest []byte, value T, err error) {
    rest, _, err = p(input)
    if err != nil {
    return input, value, err
    }
    return rest, val, nil
    }
    }
    // takeAny returns the longest input slice (if any) that contains only
    // characters found in set.
    func takeAny(set string) parser[[]byte] {
    return func(input []byte) (rest []byte, value []byte, err error) {
    rest = bytes.TrimLeft(input, set)
    return rest, input[:len(input)-len(rest)], nil
    }
    }
    // takeAny1 returns the longest input slice that contains only
    // characters found in set. If it doesn't find at least one byte that matches,
    // it returns an error.
    func takeAny1(set string) parser[[]byte] {
    return func(input []byte) (rest []byte, value []byte, err error) {
    rest = bytes.TrimLeft(input, set)
    if len(rest) == len(input) {
    return input, nil, fmt.Errorf("nothing matching %q was found", set)
    }
    return rest, input[:len(input)-len(rest)], nil
    }
    }
    func positiveInt(input []byte) (rest []byte, value int, err error) {
    return mapWithError(
    takeAny1("0123456789"),
    func(b []byte) (int, error) {
    return strconv.Atoi(string(b))
    },
    )(input)
    }
    func delimited[T, U, V any](left parser[T], inner parser[U], right parser[V]) parser[U] {
    return func(input []byte) (rest []byte, value U, err error) {
    rest, _, err = left(input)
    if err != nil {
    return
    }
    rest, value, err = inner(rest)
    if err != nil {
    return
    }
    rest, _, err = right(rest)
    return
    }
    }
    func terminated[T, U any](first parser[T], second parser[U]) parser[T] {
    return func(input []byte) (rest []byte, value T, err error) {
    rest, value, err = first(input)
    if err != nil {
    return
    }
    rest, _, err = second(rest)
    return
    }
    }
    func preceded[T, U any](first parser[T], second parser[U]) parser[U] {
    return func(input []byte) (rest []byte, value U, err error) {
    rest, _, err = first(input)
    if err != nil {
    return
    }
    rest, value, err = second(rest)
    return
    }
    }
    func space0(input []byte) (rest []byte, value []byte, err error) {
    return takeAny(" \t")(input)
    }
    func multispace0(input []byte) (rest []byte, value []byte, err error) {
    return takeAny(" \t\r\n")(input)
    }
    func lineEnding(input []byte) (rest []byte, value string, err error) {
    return alt(tag("\n"), tag("\r\n"))(input)
    }
    func takeWhile(f func(byte) bool) parser[[]byte] {
    return func(input []byte) ([]byte, []byte, error) {
    i := 0
    for i < len(input) && f(input[i]) {
    i++
    }
    return input[i:], input[:i], nil
    }
    }
    func recognize[T any](p parser[T]) parser[[]byte] {
    return func(input []byte) (rest []byte, value []byte, err error) {
    rest, _, err = p(input)
    if err != nil {
    return
    }
    return rest, input[:len(input)-len(rest)], nil
    }
    }
    func recognize2[T, U any](p1 parser[T], p2 parser[U]) parser[[]byte] {
    return func(input []byte) (rest []byte, value []byte, err error) {
    rest, _, err = p1(input)
    if err != nil {
    return
    }
    rest, _, err = p2(rest)
    if err != nil {
    return
    }
    return rest, input[:len(input)-len(rest)], nil
    }
    }
    func recognize3[T, U, V any](p1 parser[T], p2 parser[U], p3 parser[V]) parser[[]byte] {
    return func(input []byte) (rest []byte, value []byte, err error) {
    rest, _, err = p1(input)
    if err != nil {
    return
    }
    rest, _, err = p2(rest)
    if err != nil {
    return
    }
    rest, _, err = p3(rest)
    if err != nil {
    return
    }
    return rest, input[:len(input)-len(rest)], nil
    }
    }
    func recognize4[T, U, V, W any](p1 parser[T], p2 parser[U], p3 parser[V], p4 parser[W]) parser[[]byte] {
    return func(input []byte) (rest []byte, value []byte, err error) {
    rest, _, err = p1(input)
    if err != nil {
    return
    }
    rest, _, err = p2(rest)
    if err != nil {
    return
    }
    rest, _, err = p3(rest)
    if err != nil {
    return
    }
    rest, _, err = p4(rest)
    if err != nil {
    return
    }
    return rest, input[:len(input)-len(rest)], nil
  • edit in combinators.go at line 230
    [2.1179]
    func many0[T any](p parser[T]) parser[[]T] {
    return func(input []byte) (rest []byte, value []T, err error) {
    for {
    var v T
    rest, v, err = p(input)
    if err != nil {
    return input, value, nil
    }
    if len(rest) == len(input) {
    return rest, value, errors.New("infinite loop in many0")
    }
    value = append(value, v)
    input = rest
    }
    }
    }
    func opt[T any](p parser[T]) parser[*T] {
    return func(input []byte) ([]byte, *T, error) {
    rest, value, err := p(input)
    if err != nil {
    return input, nil, nil
    }
    return rest, &value, nil
    }
    }
  • replacement in change_test.go at line 9
    [3.414][3.414:449]()
    var changeHeaderTests = []struct {
    [3.414]
    [2.1180]
    var changeParsingTests = []struct {
  • replacement in change_test.go at line 11
    [2.1195][3.464:485](),[3.464][3.464:485]()
    parsed ChangeHeader
    [2.1195]
    [3.485]
    parsed Change
  • replacement in change_test.go at line 23
    [3.569][3.569:692]()
    ChangeHeader{
    Timestamp: time.Date(2023, 3, 24, 17, 52, 8, 476452868, time.UTC),
    Authors: []map[string]string{},
    [3.569]
    [3.692]
    Change{
    Header: ChangeHeader{
    Timestamp: time.Date(2023, 3, 24, 17, 52, 8, 476452868, time.UTC),
    Authors: []map[string]string{},
    },
  • replacement in change_test.go at line 46
    [2.3607][3.851:1072](),[3.851][3.851:1072]()
    ChangeHeader{
    Message: "Add some gibberish",
    Timestamp: time.Date(2023, 3, 24, 17, 52, 8, 476298107, time.UTC),
    Authors: []map[string]string{
    {
    "key": "BCEXYuKWaQ96btsk8UyBZWHLjn1Brhykv8tuZGPRjzFn",
    [2.3607]
    [3.1072]
    Change{
    Header: ChangeHeader{
    Message: "Add some gibberish",
    Timestamp: time.Date(2023, 3, 24, 17, 52, 8, 476298107, time.UTC),
    Authors: []map[string]string{
    {
    "key": "BCEXYuKWaQ96btsk8UyBZWHLjn1Brhykv8tuZGPRjzFn",
    },
  • edit in change_test.go at line 56
    [3.1085]
    [3.1085]
    Dependencies: []string{"AYY5CBLPBVTCHWSC7HDSZDL7KCUFJNX3NPNN6Q7ITTWF232IS4PQC"},
  • replacement in change_test.go at line 62
    [3.1142][3.1142:1181](),[3.1181][2.3608:3661]()
    for i, c := range changeHeaderTests {
    _, parsed, err := parseChangeHeader([]byte(c.raw))
    [3.1142]
    [3.1232]
    for i, c := range changeParsingTests {
    parsed, err := ParseChange([]byte(c.raw))
  • edit in change.go at line 8
    [3.1508]
    [3.1508]
    type Change struct {
    Hash string
    Header ChangeHeader
    Dependencies []string
    ExtraKnown []string
    }
  • edit in change.go at line 34
    [2.3918]
    [2.3918]
    }
    func ParseChange(input []byte) (Change, error) {
    var c Change
    rest, header, err := parseChangeHeader(input)
    if err != nil {
    return Change{}, err
    }
    c.Header = header
    rest, dependencies, err := parseDependencies(rest)
    if err != nil {
    return Change{}, err
    }
    deps := map[int]string{}
    for _, dep := range dependencies {
    switch dep.typ {
    case numbered:
    c.Dependencies = append(c.Dependencies, dep.hash)
    deps[dep.number] = dep.hash
    case numberedPlus:
    deps[dep.number] = dep.hash
    case extraKnown:
    c.ExtraKnown = append(c.ExtraKnown, dep.hash)
    }
    }
    // TODO: hunks
    return c, nil
    }
    type depType int
    const (
    numbered depType = iota
    numberedPlus
    extraKnown
    extraUnknown
    )
    type printableDependency struct {
    typ depType
    number int
    hash string
  • edit in change.go at line 81
    [2.3920]
    func parseDependency(input []byte) ([]byte, printableDependency, error) {
    rest, n, err := delimited(
    tag("["),
    alt(
    positiveInt,
    value(-1, tag("*")),
    // I don't think the syntax for ExtraUnknown will ever match, so I'll skip it.
    ),
    tag("]"),
    )(input)
    if err != nil {
    return rest, printableDependency{}, err
    }
    rest, plus, err := terminated(
    alt(tag("+"), tag(" ")),
    space0,
    )(rest)
    if err != nil {
    return rest, printableDependency{}, err
    }
    var dep printableDependency
    if n == -1 {
    dep.typ = extraKnown
    } else {
    dep.number = n
    if plus == "+" {
    dep.typ = numberedPlus
    } else {
    dep.typ = numbered
    }
    }
    rest, hash, err := delimited(
    space0,
    takeWhile(func(c byte) bool {
    return '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
    }),
    recognize3(space0, opt(parseComment), lineEnding),
    )(rest)
    if err != nil {
    return rest, printableDependency{}, err
    }
    dep.hash = string(hash)
    return rest, dep, nil
    }
    func parseComment(input []byte) ([]byte, []byte, error) {
    return preceded(
    tag("#"),
    takeUntil("\n"),
    )(input)
    }
    func parseDependencies(input []byte) ([]byte, []printableDependency, error) {
    return alt(
    preceded(
    recognize4(tag("# Dependencies"), space0, lineEnding, multispace0),
    many0(terminated(parseDependency, multispace0)),
    ),
    value([]printableDependency{}, multispace0),
    )(input)
    }