package rdf

import 

func ( *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()] !=  {
		return false
	}
	 := .pos + len()
	if  < len(.s) && isNameChar(.s[]) {
		return false
	}
	.pos += len()
	return true
}

func ( *turtleParser) () (Term, bool) {
	if .peek() == '<' {
		,  := .iriRef()
		return NewIRI(), 
	}
	 := .readPrefixLabelNoColon()
	if .peek() != ':' {
		return Term{}, false
	}
	.pos++ // ':'
	 := .readLocalName()
	,  := .prefixes[]
	if ! {
		return Term{}, false
	}
	return NewIRI(.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 
	}
	var  string
	if .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.
	if len() < 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 +=  + 1
	if .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 ! {
				return false
			}
			continue
		}
		if ! && !( >= '0' &&  <= '9') &&  != '+' &&  != '-' &&  != '.' {
			return false
		}
	}
	return false
}

func ( *turtleParser) () (Term, bool) {
	if !strings.HasPrefix(.s[.pos:], "_:") {
		return Term{}, false
	}
	.pos += 2
	 := .pos
	for .pos < len(.s) && isNameChar(.s[.pos]) {
		.pos++
	}
	for .pos >  && .s[.pos-1] == '.' { // a label cannot end with '.'
		.pos--
	}
	if .pos ==  {
		return Term{}, false
	}
	return NewBlank("u" + .s[:.pos]), true
}

// blankPropertyList parses `[ predicateObjectList ]` into a fresh blank node.
func ( *turtleParser) () (Term, bool) {
	if .peek() != '[' {
		return Term{}, false
	}
	if .depth >= maxParseDepth {
		return Term{}, false
	}
	.depth++
	defer func() { .depth-- }()
	.pos++
	 := .fresh()
	.ws()
	if .peek() == ']' {
		.pos++
		return , true
	}
	if !.predicateObjectList() {
		return Term{}, false
	}
	.ws()
	if .peek() != ']' {
		return Term{}, false
	}
	.pos++
	return , true
}

// collection parses `( object* )` into an RDF list, returning its head.
func ( *turtleParser) () (Term, bool) {
	if .peek() != '(' {
		return Term{}, false
	}
	if .depth >= maxParseDepth {
		return Term{}, false
	}
	.depth++
	defer func() { .depth-- }()
	.pos++
	var  []Term
	for {
		.ws()
		if .peek() == ')' {
			.pos++
			break
		}
		,  := .object()
		if ! {
			return Term{}, false
		}
		 = append(, )
	}
	if len() == 0 {
		return NewIRI(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 ! {
		return Term{}, false
	}
	switch {
	case .peek() == '@':
		.pos++
		 := langTagLen(.s[.pos:])
		 := .s[.pos : .pos+]
		.pos += 
		return NewLiteral(, , ""), true
	case strings.HasPrefix(.s[.pos:], "^^"):
		.pos += 2
		,  := .iriOrPName()
		if ! {
			return Term{}, false
		}
		return NewLiteral(, "", .Value), true
	}
	return NewLiteral(, "", ""), true
}

// quotedString reads a "…" or '…' string, or a """…""" / ”'…”' long string,
// decoding escapes.
func ( *turtleParser) () (string, bool) {
	 := .peek()
	if  != '"' &&  != '\'' {
		return "", false
	}
	 := 1
	if .pos+2 < len(.s) && .s[.pos+1] ==  && .s[.pos+2] ==  {
		 = 3 // a """ or ''' long string
	}
	.pos += 
	 := .pos
	 := false
	for .pos < len(.s) {
		 := .s[.pos]
		if  == '\\' {
			 = true
			.pos += 2 // skip the escaped character
			continue
		}
		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
				}
				return unescapeRDF(), true
			}
			return , true
		}
		.pos++
	}
	return "", false
}

// number reads a numeric literal and assigns its xsd datatype.
func ( *turtleParser) () (Term, bool) {
	 := .pos
	if  := .peek();  == '+' ||  == '-' {
		.pos++
	}
	,  := false, false
	,  := 0, 0
	for .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 = 
		return Term{}, false
	}
	 := xsdInteger
	if  {
		 = xsdDouble
	} else if  {
		 = xsdDecimal
	}
	return NewLiteral(.s[:.pos], "", ), true
}

func ( *turtleParser) () string {
	 := .pos
	for .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 {
	 := .pos
	for .pos < len(.s) && .s[.pos] != ':' && isNameChar(.s[.pos]) {
		.pos++
	}
	return .s[:.pos]
}

func ( *turtleParser) () string {
	 := .pos
	for .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 {
	return isAlpha() ||  >= '0' &&  <= '9' ||  == '_' ||  == '-' ||  == '.'
}