                 }
             }
             
-            // TODO: verify that we're not projecting a variable more than once
+            // TODO: verify that we're not projecting a variable more than once; projection is on a Set of variables, though we preserve the ordering for API convenience
             // TODO: add projection for aggregate variables
             algebra = .project(algebra, projection)
         }

            }

            let us = UnicodeScalar("\(c)")!
            if SPARQLLexer.pnCharSet.contains(us) {
                if let t = try getPName() {
                    return packageToken(t)
                }

                return self
            }
        case .prefixedLocalName:
            var v = ""
            /**
             
             prefixed name first character:
             - leave untouched characters in pnCharsU, ':', or [0-9]
             - backslash escape these characters: ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%' )
             - percent encode anything else
             **/
            
            let okFirst = String.pnCharsU.union(CharacterSet(charactersIn: "0123456789:"))
            guard let first = self.first else { return "" }
            let fcs = CharacterSet(first.unicodeScalars)
            if fcs.isStrictSubset(of: okFirst) {
                v += String(first)
            } else if let escaped = String(first).sparqlBackslashEscape {
                v += escaped
            } else {
                v += String(first).sparqlPercentEncoded
            }
            
            /**
             prefixed name local part (non-first character):
             - cannot end in a '.'
             - backslash escape these characters: ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%' )
             - leave untouched characters in pnChars
             - percent encode anything else
             
             **/
            for c in self.dropFirst(1) {
                let cs = CharacterSet(c.unicodeScalars)
                if let escaped = String(c).sparqlBackslashEscape {
                    v += String(escaped)
                } else if cs.isStrictSubset(of: String.pnChars) {
                    v += String(c)
                } else {
                    v += String(c).sparqlPercentEncoded
                }
            }
            
            guard let last = v.last else { return v }
            if last == "." {
                v = v.substring(to: v.index(before: v.endIndex))
                v += ".".sparqlPercentEncoded
            }
            
            return v
        }
    }

    private var sparqlPercentEncoded : String {
        var v = ""
        for u in self.utf8 {
            v += String(format: "%%%02d", u)
        }
        return v
    }
    
    private var sparqlBackslashEscape : String? {
        let needsEscaping = CharacterSet(charactersIn: "_~.-!$('()*+,;=/?#@%")
        var v = ""
        for c in self {
            let cs = CharacterSet(c.unicodeScalars)
            if cs.isStrictSubset(of: needsEscaping) {
                v += "\\"
                v += String(c)
            } else {
                v += String(c)
            }
        }
        return v
    }
    
    private static let pnChars: CharacterSet = {
        var pn = pnCharsU
        pn.insert(charactersIn: "0123456789-")
