package rdf

import (
	
	
	
)

// XSD datatypes used by Turtle's literal shorthands.
const (
	xsdBoolean = "http://www.w3.org/2001/XMLSchema#boolean"
	xsdInteger = "http://www.w3.org/2001/XMLSchema#integer"
	xsdDecimal = "http://www.w3.org/2001/XMLSchema#decimal"
	xsdDouble  = "http://www.w3.org/2001/XMLSchema#double"
)

// maxParseDepth bounds nesting in the recursive-descent parsers — Turtle
// blank-node property lists (`[ … ]`) and collections (`( … )`), and RDF/XML
// node elements — so adversarial deeply nested input fails with an error
// instead of overflowing the goroutine stack, which is a fatal, unrecoverable
// runtime crash (recover cannot catch it). No real document nests this deep.
const maxParseDepth = 1 << 13

// ParseTurtle parses a Turtle document into a Graph. It supports the constructs
// real RDF uses: @prefix/@base and SPARQL-style PREFIX/BASE, prefixed names and
// IRIs, the `a` keyword, predicate-object lists (`;`) and object lists (`,`),
// blank-node labels, `[ … ]` blank-node property lists, `( … )` collections, and
// string (including triple-quoted), language-tagged, datatyped, numeric and
// boolean literals. Local names are read as the common identifier subset.
func ( []byte) (*Graph, error) {
	 := &Graph{Triples: make([]Triple, 0, len()/32)}
	 := &turtleParser{
		s:        string(),
		emit:     .Add,
		prefixes: map[string]string{},
		iriCache: map[string]map[string]string{},
		strs:     &arena{},
	}
	for {
		,  := .stmt()
		if  {
			return , nil
		}
		if ! {
			return , errTurtle()
		}
	}
}

// stmt parses one directive or triple statement from the current buffer, emitting
// its triples. done is true at end of input; ok is false on a malformed statement.
// It is shared by the whole-document parser and the streaming decoder (which feeds
// one statement at a time).
func ( *turtleParser) () (,  bool) {
	.ws()
	if .pos >= len(.s) {
		return true, true
	}
	if .s[.pos] == '@' || .peekKeyword("prefix") || .peekKeyword("base") {
		return .directive(), false
	}
	return .triples(), false
}

type turtleError struct{ pos, line, col int }

// Error reports the failure with its position. For a whole-document parse the
// line and column locate the byte in the source; when streaming, p.s is a single
// statement, so they are relative to the start of that statement.
func ( *turtleError) () string {
	return fmt.Sprintf("rdf: malformed Turtle at line %d, column %d (byte offset %d)", .line, .col, .pos)
}

func errTurtle( *turtleParser) error {
	,  := lineCol(.s, .pos)
	return &turtleError{pos: .pos, line: , col: }
}

// lineCol returns the 1-based line and column of byte offset pos in s.
func lineCol( string,  int) (,  int) {
	,  = 1, 1
	if  > len() {
		 = len()
	}
	for  := range  {
		if [] == '\n' {
			++
			 = 1
		} else {
			++
		}
	}
	return , 
}

type turtleParser struct {
	s        string
	pos      int
	emit     func(s, pr, o Term) // sink: Graph.Add (whole document) or channel (streaming)
	prefixes map[string]string
	iriCache map[string]map[string]string // prefix -> local -> interned full IRI
	strs     *arena                       // backs expanded IRIs when whole-document; nil when streaming
	base     string
	blanks   int
	depth    int // current `[`/`(` nesting, bounded by maxParseDepth
}

func ( *turtleParser) () Term {
	.blanks++
	return NewBlank("t" + strconv.Itoa(.blanks))
}

// ws skips whitespace and # comments.
func ( *turtleParser) () {
	for .pos < len(.s) {
		switch .s[.pos] {
		case ' ', '\t', '\r', '\n':
			.pos++
		case '#':
			for .pos < len(.s) && .s[.pos] != '\n' {
				.pos++
			}
		default:
			return
		}
	}
}

func ( *turtleParser) () byte {
	if .pos < len(.s) {
		return .s[.pos]
	}
	return 0
}

// peekKeyword reports whether a case-insensitive keyword (SPARQL PREFIX/BASE)
// begins at the cursor, followed by a non-name character.
func ( *turtleParser) ( string) bool {
	if .pos+len() > len(.s) {
		return false
	}
	if !strings.EqualFold(.s[.pos:.pos+len()], ) {
		return false
	}
	 := .pos + len()
	return  >= len(.s) || isWS(.s[])
}

func isWS( byte) bool { return  == ' ' ||  == '\t' ||  == '\r' ||  == '\n' }

// directive parses @prefix/@base or SPARQL PREFIX/BASE.
func ( *turtleParser) () bool {
	 := .s[.pos] != '@'
	if ! {
		.pos++ // '@'
	}
	 := .readBareWord()
	.ws()
	switch strings.ToLower() {
	case "prefix":
		 := .readPrefixLabel()
		.ws()
		,  := .iriRef()
		if ! {
			return false
		}
		.prefixes[] = 
	case "base":
		,  := .iriRef()
		if ! {
			return false
		}
		.base = 
	default:
		return false
	}
	if ! {
		.ws()
		if .peek() != '.' {
			return false
		}
		.pos++
	}
	return true
}

// triples parses a subject and its predicate-object list, terminated by '.'.
// A blankNodePropertyList subject ("[ ... ]") carries its predicate-object pairs
// inside the brackets, so per the Turtle grammar the trailing predicate-object
// list is optional there ("[ a :Work ] ." is a complete statement); every other
// subject requires one.
func ( *turtleParser) () bool {
	.ws()
	 := .peek() == '['
	,  := .subject()
	if ! {
		return false
	}
	.ws()
	if  && .peek() == '.' {
		.pos++
		return true
	}
	if !.predicateObjectList() {
		return false
	}
	.ws()
	if .peek() != '.' {
		return false
	}
	.pos++
	return true
}

// predicateObjectList parses `verb objectList (';' verb objectList)*`.
func ( *turtleParser) ( Term) bool {
	for {
		.ws()
		,  := .verb()
		if ! {
			return false
		}
		if !.objectList(, ) {
			return false
		}
		.ws()
		if .peek() != ';' {
			return true
		}
		for .peek() == ';' { // allow repeated/empty ';'
			.pos++
			.ws()
		}
		// A trailing ';' before '.' or ']' ends the list.
		if  := .peek();  == '.' ||  == ']' ||  == 0 {
			return true
		}
	}
}

func ( *turtleParser) (,  Term) bool {
	for {
		,  := .object()
		if ! {
			return false
		}
		.emit(, , )
		.ws()
		if .peek() != ',' {
			return true
		}
		.pos++
	}
}

func ( *turtleParser) () (Term, bool) {
	if .peek() == 'a' {
		 := .pos + 1
		if  >= len(.s) || isWS(.s[]) || .s[] == '<' {
			.pos++
			return NewIRI(TypeIRI), true
		}
	}
	return .iriOrPName()
}

func ( *turtleParser) () (Term, bool) {
	.ws()
	switch .peek() {
	case '_':
		return .blankLabel()
	case '[':
		return .blankPropertyList()
	case '(':
		return .collection()
	default:
		return .iriOrPName()
	}
}

func ( *turtleParser) () (Term, bool) {
	.ws()
	switch  := .peek(); {
	case  == '<':
		return .iriOrPName()
	case  == '"',  == '\'':
		return .literal()
	case  == '_':
		return .blankLabel()
	case  == '[':
		return .blankPropertyList()
	case  == '(':
		return .collection()
	case  == 't' && .matchWord("true"):
		return NewLiteral("true", "", xsdBoolean), true
	case  == 'f' && .matchWord("false"):
		return NewLiteral("false", "", xsdBoolean), true
	case ( >= '0' &&  <= '9') ||  == '+' ||  == '-' || ( == '.' && .digitNext()):
		return .number()
	default:
		return .iriOrPName()
	}
}

// digitNext reports whether a digit follows the cursor (to tell a leading-dot
// number from a statement terminator).