1-- 2-- criteria.lua 3-- 4-- Stores a list of criteria terms with support for negation, conjunction, 5-- and wildcard matches. Provides functions match match these criteria 6-- against various contexts. 7-- 8-- Copyright (c) 2012-2015 Jason Perkins and the Premake project 9-- 10 11 local p = premake 12 13 p.criteria = criteria -- criteria namespace is defined in C host 14 local criteria = p.criteria 15 16 17-- 18-- These prefixes correspond to the context information built by the oven 19-- during baking. In theory, any field could be used as a filter, but right 20-- now only these are set. 21-- 22 23 criteria._validPrefixes = { 24 _action = true, 25 action = true, 26 architecture = true, 27 configurations = true, 28 files = true, 29 kind = true, 30 language = true, 31 _options = true, 32 options = true, 33 platforms = true, 34 system = true, 35 toolset = true, 36 tags = true, 37 } 38 39 40-- 41-- Flattens a hierarchy of criteria terms into a single array containing all 42-- of the values as strings in the form of "term:value1 or value2" etc. 43-- 44 function criteria.flatten(terms) 45 local result = {} 46 47 local function flatten(terms) 48 for key, value in pairs(terms) do 49 if type(key) == "number" then 50 if type(value) == "table" then 51 flatten(value) 52 elseif value then 53 table.insert(result, value) 54 end 55 elseif type(key) == "string" then 56 local word = key .. ":" 57 if type(value) == "table" then 58 local values = table.flatten(value) 59 word = word .. table.concat(values, " or ") 60 else 61 word = word .. value 62 end 63 table.insert(result, word) 64 else 65 error("Unknown key type in terms.") 66 end 67 end 68 end 69 70 flatten(terms) 71 return result 72 end 73 74--- 75-- Create a new criteria object. 76-- 77-- @param terms 78-- A list of criteria terms. 79-- @param unprefixed 80-- If true, use the old style, unprefixed filter terms. This will 81-- eventually be phased out in favor of prefixed terms only. 82-- @return 83-- A new criteria object. 84--- 85 86 function criteria.new(terms, unprefixed) 87 terms = criteria.flatten(terms) 88 89 -- Preprocess the list of terms for better performance in matches(). 90 -- Each term is replaced with a pattern, with an implied AND between 91 -- them. Each pattern contains one or more words, with an implied OR 92 -- between them. A word maybe be flagged as negated, or as a wildcard 93 -- pattern, and may have a field prefix associated with it. 94 95 local patterns = {} 96 97 for i, term in ipairs(terms) do 98 term = term:lower() 99 100 local pattern = {} 101 local prefix = iif(unprefixed, nil, "configurations") 102 103 local words = term:explode(" or ") 104 for _, word in ipairs(words) do 105 word, prefix = criteria._word(word, prefix) 106 if prefix and not criteria._validPrefixes[prefix] then 107 return nil, string.format("Invalid field prefix '%s'", prefix) 108 end 109 110 -- check for field value aliases 111 if prefix then 112 local fld = p.field.get(prefix) 113 if fld and fld.aliases then 114 word[1] = fld.aliases[word[1]] or word[1] 115 end 116 end 117 118 table.insert(pattern, word) 119 end 120 121 table.insert(patterns, pattern) 122 end 123 124 -- The matching logic is written in C now for performance; compile 125 -- this collection of patterns to C data structures to make that 126 -- code easier to read and maintain. 127 128 local crit = {} 129 crit.patterns = patterns 130 crit.data = criteria._compile(patterns) 131 crit.terms = terms 132 return crit 133 end 134 135 136 137 function criteria._word(word, prefix) 138 local wildcard 139 local assertion = true 140 141 -- Trim off all "not" and field prefixes and check for wildcards 142 while (true) do 143 if word:startswith("not ") then 144 assertion = not assertion 145 word = word:sub(5) 146 else 147 local i = word:find(":", 1, true) 148 if prefix and i then 149 prefix = word:sub(1, i - 1) 150 word = word:sub(i + 1) 151 else 152 wildcard = (word:find("*", 1, true) ~= nil) 153 if wildcard then 154 word = path.wildcards(word) 155 end 156 break 157 end 158 end 159 end 160 161 return { word, prefix, assertion, wildcard }, prefix 162 end 163 164 165--- 166-- Add a new prefix to the list of allowed values for filters. Note 167-- setting a prefix on its own has no effect on the output; a filter 168-- term must also be set on the corresponding context during baking. 169-- 170-- @param prefix 171-- The new prefix to be allowed. 172--- 173 174 function criteria.allowPrefix(prefix) 175 criteria._validPrefixes[prefix:lower()] = true 176 end 177 178