func magics(m []string) ([]frames.Signature, error) { hx, ascii, hxx, asciix, err := characterise(m) if err != nil { return nil, err } if len(hx) > 0 { sigs := make([]frames.Signature, len(hx)) for i, v := range hx { byts, offs, masks, err := dehex(v, hxx[i]) if err != nil { return nil, err } sigs[i] = make(frames.Signature, len(byts)) for ii, vv := range byts { rel := frames.BOF if ii > 0 { rel = frames.PREV } var pat patterns.Pattern if masks[ii] { pat = patterns.Mask(vv[0]) } else { pat = patterns.Sequence(vv) } sigs[i][ii] = frames.NewFrame(rel, pat, offs[ii], offs[ii]) } } return sigs, nil } else if len(ascii) > 0 { sigs := make([]frames.Signature, len(ascii)) for i, v := range ascii { pat := patterns.Sequence(v) sigs[i] = frames.Signature{frames.NewFrame(frames.BOF, pat, asciix[i], asciix[i])} } return sigs, nil } return nil, nil }
// groups are chunks of PRONOM/Droid patterns delimited by parentheses or brackets // these chunks represent any non-sequence pattern (choices, ranges, bitmasks, not-patterns etc.) func processGroup(l *lexer) (patterns.Pattern, error) { var ( list patterns.List // bucket to stuff patterns into choice patterns.Choice // bucket to stuff choices into val []byte // bucket to stuff text values not, mask, anyMask, rng bool // retains state from previous tokens ) // when commit a pattern (to the list), go back to zero state reset := func() { val = []byte{} not, mask, anyMask, rng = false, false, false, false } // make a pattern based on the current state makePat := func() patterns.Pattern { if len(val) == 0 { return nil } var pat patterns.Pattern switch { case mask: pat = patterns.Mask(val[0]) case anyMask: pat = patterns.AnyMask(val[0]) default: pat = patterns.Sequence(val) } if not { pat = patterns.Not{pat} } reset() return pat } // add patterns to the choice addChoice := func() (patterns.Choice, error) { switch len(list) { case 0: return nil, errors.New(l.name + " has choice marker without preceding pattern") case 1: choice = append(choice, list[0]) default: choice = append(choice, list) } list = patterns.List{} return choice, nil } for { i := <-l.items switch i.typ { default: return nil, errors.New(l.name + " encountered unexpected token " + i.val) case itemEnterGroup: // recurse e.g. for a range nested within a choice if pat := makePat(); pat != nil { list = append(list, pat) } pat, err := processGroup(l) if err != nil { return nil, err } list = append(list, pat) case itemExitGroup: if pat := makePat(); pat != nil { list = append(list, pat) } if len(choice) > 0 { return addChoice() } else { switch len(list) { case 0: return nil, errors.New(l.name + " has group with no legal pattern") case 1: return list[0], nil default: return list, nil } } case itemRangeMarker: rng = true case itemChoiceMarker: if pat := makePat(); pat != nil { list = append(list, pat) } _, err := addChoice() if err != nil { return nil, err } case itemNotMarker: not = true case itemMaskMarker: mask = true case itemAnyMaskMarker: anyMask = true case itemUnprocessedText: v := processText(i.val) // if it is a range, we need values before and after the range marker, so add it here if rng { r := Range{val, v} if not { list = append(list, patterns.Not{r}) } else { list = append(list, r) } reset() } else { val = v } } } }