Module: jam-internals Author: Peter S. Housel Copyright: Original Code is Copyright 2004 Gwydion Dylan Maintainers All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND define constant $any-char-set = begin let set = make(, upper-bound-hint: 256); for (symbol :: from 0 below 256) set-add!(set, symbol); end for; set end; define function parse-glob-pattern (string :: ) => (node :: ); iterate loop (node :: = make(), i :: = 0) if (i < string.size) let char = string[i]; select (char) // ? - match any single character '?' => loop(make(, head: node, tail: make(, symbol-set: $any-char-set)), i + 1); // * - match zero or more characters '*' => loop(make(, head: node, tail: make(, of: make(, symbol-set: $any-char-set))), i + 1); // [chars] / [^chars] - match any single character in a character // set or character set complement '[' => let cclass-set = make(, upper-bound-hint: 256); let pos = i + 1; let complement? = if (pos < string.size & string[pos] = '^') i := i + 1; pos := pos + 1; #t; else #f end if; while (pos < string.size & (string[pos] ~== ']' | pos = i + 1)) if (pos + 2 < string.size & string[pos + 1] == '-') for (symbol :: from as(, string[pos]) to as(, string[pos + 2])) set-add!(cclass-set, symbol); end for; pos := pos + 3; else set-add!(cclass-set, as(, string[pos])); pos := pos + 1; end; end while; if (pos = string.size) error("closing ']' missing in regexp '%s'", string); end if; if (complement?) let complement-set = make(, upper-bound-hint: 256); for (symbol :: from 0 below 256) unless (member?(symbol, cclass-set)) add!(complement-set, symbol) end; end for; loop(make(, head: node, tail: make(, symbol-set: complement-set)), pos + 1); else loop(make(, head: node, tail: make(, symbol-set: cclass-set)), pos + 1); end; '\\' => if (i + 1 < string.size) loop(make(, head: node, tail: make(, symbol: as(, string[i + 1]))), i + 2); else error("glob pattern '%s' ends in '\\'", string); end if; otherwise => loop(make(, head: node, tail: make(, symbol: as(, char))), i + 1); end select; else node end end iterate end function; define function glob-match-function (pattern :: , #rest more-patterns) => (matcher :: ); let regex = parse-glob-pattern(pattern); for (pattern in more-patterns) regex := make(, union1: regex, union2: parse-glob-pattern(pattern)); end for; let dfa = regular-expression-dfa(make(, head: regex, tail: make()), transition-collection-class: , transition-collection-size: 256, state-class: ); method (match-string :: ) => (match? :: ); block (return) for (char in match-string, state = dfa then state.regular-expression-dfa-state-transitions[as(, char)] | return(#f)) finally state.match-dfa-state-accepting? end for; end block end method; end function; define function jam-builtin-glob (jam :: , directories :: , patterns :: ) => (result :: ); let match? = apply(glob-match-function, patterns); let result = make(); for (directory-name in directories) do-directory(method (directory :: , name, type) if (match?(name)) add!(result, as(, merge-locators(as(, name), directory))); end if; end method, as(, directory-name)); end for; result end function;