func rooted(parent, name string) (string, error) {
	if path.IsAbs(name) {
		return name[1:], nil
	}
	r := path.Join(path.Dir(parent), name)
	if strings.HasPrefix(r, "..") {
		return "", os.ErrInvalid
	}
	return r, nil
}
rooted returns the path of an Extend, Import or Render node, rooted at
the parent path of the template.

Supposing that a/b/c is the parent path

if name is /d/e, the rooted path name is d/e
if name is d/e, the rooted path name is a/b/d/e
if name is ../d/e, the rooted path name is a/d/e
if name is ../../d/e, the rooted path name is d/e
package compiler
import (
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path"
	"strings"
	"github.com/open2b/scriggo/ast"
)
type FormatFS interface {
	fs.FS
	Format(name string) (ast.Format, error)
}
func ParseTemplate(fsys fs.FS, name string, noParseShow, dollarIdentifier bool) (*ast.Tree, error) {
	if name == "." || strings.HasSuffix(name, "/") {
		return nil, os.ErrInvalid
	}
	src, format, err := readFileAndFormat(fsys, name)
	if err != nil {
		return nil, err
	}
	pp := &templateExpansion{
		fsys:             fsys,
		trees:            map[string]parsedTree{},
		paths:            []string{},
		canExtend:        true,
		noParseShow:      noParseShow,
		dollarIdentifier: dollarIdentifier,
	}
	tree, err := pp.parseSource(src, name, format, true, false)
	if err != nil {
		if err2, ok := err.(*SyntaxError); ok && err2.path == "" {
			err2.path = name
		} else if e, ok := err.(*CycleError); ok {
			e.msg = "file " + name + e.msg + ": cycle not allowed"
		}
		return nil, err
	}
	return tree, nil
}
type templateExpansion struct {
	fsys             fs.FS
	trees            map[string]parsedTree
	paths            []string
	canExtend        bool
	noParseShow      bool
	dollarIdentifier bool
}
type parsedTree struct {
	tree   *ast.Tree
	parent struct {
		path string
		node ast.Node
	}
}