package rdfimportfunc ( *turtleParser) () bool {return .pos+1 < len(.s) && .s[.pos+1] >= '0' && .s[.pos+1] <= '9'}// matchWord matches a bare keyword followed by a non-name character, consuming it.func ( *turtleParser) ( string) bool {if .pos+len() > len(.s) || .s[.pos:.pos+len()] != {returnfalse } := .pos + len()if < len(.s) && isNameChar(.s[]) {returnfalse } .pos += len()returntrue}func ( *turtleParser) () (Term, bool) {if .peek() == '<' { , := .iriRef()returnNewIRI(), } := .readPrefixLabelNoColon()if .peek() != ':' {returnTerm{}, false } .pos++ // ':' := .readLocalName() , := .prefixes[]if ! {returnTerm{}, false }returnNewIRI(.expandPName(, , )), true}// expandPName returns base+local, interning the result per (prefix, local) so a// repeated prefixed name — predicates and types recur heavily — allocates the// full IRI only on its first occurrence.func ( *turtleParser) (, , string) string { := .iriCache[]if == nil { = make(map[string]string) .iriCache[] = }if , := []; {return }varstringif .strs != nil { = .strs.concat(, ) // arena-backed (whole document) } else { = + // streaming: a plain string the triple owns }// Cache to dedup repeated names (predicates, types, common subjects), but cap // the size: a file with millions of distinct subjects would otherwise grow an // unbounded map of entries that never see a second lookup. The vocabulary that // actually repeats is small and appears early, so a bound costs nothing real.iflen() < maxIRICacheEntries { [] = }return}// maxIRICacheEntries bounds each prefix's expansion cache.const maxIRICacheEntries = 1 << 16// iriRef reads `<IRI>` and resolves it against @base when relative.func ( *turtleParser) () (string, bool) {if .peek() != '<' {return"", false } := strings.IndexByte(.s[.pos:], '>')if < 0 {return"", false } := unescapeRDF(.s[.pos+1 : .pos+]) .pos += + 1if .base != "" && !hasScheme() {// Simple base joining for relative references. An absolute IRI -- any // scheme, including non-hierarchical ones like urn:/mailto:/info: that // carry no "://" -- is left untouched. = .base + }return , true}// hasScheme reports whether ref begins with an IRI scheme ("scheme:"): an ASCII// letter followed by letters, digits, "+", "-" or "." up to a ":", before any// "/", "?" or "#". Such a reference is absolute and must not be base-joined.func hasScheme( string) bool {for := 0; < len(); ++ { := []if == ':' {return > 0 } := ( >= 'A' && <= 'Z') || ( >= 'a' && <= 'z')if == 0 {if ! {returnfalse }continue }if ! && !( >= '0' && <= '9') && != '+' && != '-' && != '.' {returnfalse } }returnfalse}func ( *turtleParser) () (Term, bool) {if !strings.HasPrefix(.s[.pos:], "_:") {returnTerm{}, false } .pos += 2 := .posfor .pos < len(.s) && isNameChar(.s[.pos]) { .pos++ }for .pos > && .s[.pos-1] == '.' { // a label cannot end with '.' .pos-- }if .pos == {returnTerm{}, false }returnNewBlank("u" + .s[:.pos]), true}// blankPropertyList parses `[ predicateObjectList ]` into a fresh blank node.func ( *turtleParser) () (Term, bool) {if .peek() != '[' {returnTerm{}, false }if .depth >= maxParseDepth {returnTerm{}, false } .depth++deferfunc() { .depth-- }() .pos++ := .fresh() .ws()if .peek() == ']' { .pos++return , true }if !.predicateObjectList() {returnTerm{}, false } .ws()if .peek() != ']' {returnTerm{}, false } .pos++return , true}// collection parses `( object* )` into an RDF list, returning its head.func ( *turtleParser) () (Term, bool) {if .peek() != '(' {returnTerm{}, false }if .depth >= maxParseDepth {returnTerm{}, false } .depth++deferfunc() { .depth-- }() .pos++var []Termfor { .ws()if .peek() == ')' { .pos++break } , := .object()if ! {returnTerm{}, false } = append(, ) }iflen() == 0 {returnNewIRI(NilIRI), true } := .fresh() := for , := range { .emit(, NewIRI(FirstIRI), )if == len()-1 { .emit(, NewIRI(RestIRI), NewIRI(NilIRI)) } else { := .fresh() .emit(, NewIRI(RestIRI), ) = } }return , true}// literal parses a quoted string and any @lang or ^^datatype suffix.func ( *turtleParser) () (Term, bool) { , := .quotedString()if ! {returnTerm{}, false }switch {case .peek() == '@': .pos++ := langTagLen(.s[.pos:]) := .s[.pos : .pos+] .pos += returnNewLiteral(, , ""), truecasestrings.HasPrefix(.s[.pos:], "^^"): .pos += 2 , := .iriOrPName()if ! {returnTerm{}, false }returnNewLiteral(, "", .Value), true }returnNewLiteral(, "", ""), true}// quotedString reads a "…" or '…' string, or a """…""" / ”'…”' long string,// decoding escapes.func ( *turtleParser) () (string, bool) { := .peek()if != '"' && != '\'' {return"", false } := 1if .pos+2 < len(.s) && .s[.pos+1] == && .s[.pos+2] == { = 3// a """ or ''' long string } .pos += := .pos := falsefor .pos < len(.s) { := .s[.pos]if == '\\' { = true .pos += 2// skip the escaped charactercontinue }if == && ( == 1 || (.pos+2 < len(.s) && .s[.pos+1] == && .s[.pos+2] == )) { := .s[:.pos] // zero-copy when unescaped .pos += if {if .strs != nil {return .strs.unescape(), true }returnunescapeRDF(), true }return , true } .pos++ }return"", false}// number reads a numeric literal and assigns its xsd datatype.func ( *turtleParser) () (Term, bool) { := .posif := .peek(); == '+' || == '-' { .pos++ } , := false, false , := 0, 0for .pos < len(.s) { := .s[.pos]switch {case >= '0' && <= '9': ++if { ++ } .pos++case == '.' && ! && ! && .digitNext(): = true .pos++case ( == 'e' || == 'E') && ! && > 0: = true .pos++if := .peek(); == '+' || == '-' { .pos++ }default:goto } }:// Require at least one digit overall, and (for a number with an exponent) at // least one digit after the e/E, so a bare sign or "1e" is rejected.if == 0 || ( && == 0) { .pos = returnTerm{}, false } := xsdIntegerif { = xsdDouble } elseif { = xsdDecimal }returnNewLiteral(.s[:.pos], "", ), true}func ( *turtleParser) () string { := .posfor .pos < len(.s) && isAlpha(.s[.pos]) { .pos++ }return .s[:.pos]}// readPrefixLabel reads a prefix label up to and including its ':'.func ( *turtleParser) () string { := .readPrefixLabelNoColon()if .peek() == ':' { .pos++ }return}func ( *turtleParser) () string { := .posfor .pos < len(.s) && .s[.pos] != ':' && isNameChar(.s[.pos]) { .pos++ }return .s[:.pos]}func ( *turtleParser) () string { := .posfor .pos < len(.s) && isNameChar(.s[.pos]) { .pos++ }for .pos > && .s[.pos-1] == '.' { // PN_LOCAL cannot end with '.' (the terminator) .pos-- }return .s[:.pos]}func isAlpha( byte) bool {return >= 'A' && <= 'Z' || >= 'a' && <= 'z'}// isNameChar reports whether c is allowed in the common subset of prefixed-name// local parts and blank-node labels handled here.func isNameChar( byte) bool {returnisAlpha() || >= '0' && <= '9' || == '_' || == '-' || == '.'}
The pages are generated with Goldsv0.8.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.