func ParseFSFile(fset *token.FileSet, fs FileSystem, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
	ext := filepath.Ext(filename)
	ft, isOk := extGopFiles[ext]
	if !isOk {
		ft = ast.FileTypeGop
	}
	return parseFSFileEx(fset, fs, filename, src, mode, ft)
}
ParseFSFile parses the source code of a single Go+ source file and returns the corresponding ast.File node.
package parser
import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"github.com/goplus/gop/ast"
	"github.com/goplus/gop/scanner"
	"github.com/goplus/gop/token"
)
const (
	DbgFlagParseOutput = 1 << iota
	DbgFlagParseError
	DbgFlagAll = DbgFlagParseOutput | DbgFlagParseError
)
var (
	debugParseOutput bool
	debugParseError  bool
)
func SetDebug(dbgFlags int) {
	debugParseOutput = (dbgFlags & DbgFlagParseOutput) != 0
	debugParseError = (dbgFlags & DbgFlagParseError) != 0
}
type FileSystem interface {
	ReadDir(dirname string) ([]os.FileInfo, error)
	ReadFile(filename string) ([]byte, error)
	Join(elem ...string) string
}
type localFS struct{}
func (p localFS) ReadDir(dirname string) ([]os.FileInfo, error) {
	return ioutil.ReadDir(dirname)
}
func (p localFS) ReadFile(filename string) ([]byte, error) {
	return ioutil.ReadFile(filename)
}
func (p localFS) Join(elem ...string) string {
	return filepath.Join(elem...)
}
var local FileSystem = localFS{}
func Parse(fset *token.FileSet, target string, src interface{}, mode Mode) (pkgs map[string]*ast.Package, err error) {
	file, err := ParseFile(fset, target, src, mode)
	if err != nil {
		return
	}
	pkgs = make(map[string]*ast.Package)
	pkgs[file.Name.Name] = astFileToPkg(file, target)
	return
}
func astFileToPkg(file *ast.File, fileName string) (pkg *ast.Package) {
	pkg = &ast.Package{
		Name:  file.Name.Name,
		Files: make(map[string]*ast.File),
	}
	pkg.Files[fileName] = file
	return
}
func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {
	return ParseFSDir(fset, local, path, filter, mode)
}
func ParseFSDir(fset *token.FileSet, fs FileSystem, path string, filter func(os.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {
	list, err := fs.ReadDir(path)
	if err != nil {
		return nil, err
	}
	pkgs = make(map[string]*ast.Package)
	for _, d := range list {
		if d.IsDir() {
			continue
		}
		fname := d.Name()
		ext := filepath.Ext(fname)
		ft, isOk := extGopFiles[ext]
		if ft == ast.FileTypeGo && (mode&ParseGoFiles) == 0 {
			isOk = false
		}
		if isOk && !strings.HasPrefix(fname, "_") && (filter == nil || filter(d)) {
			filename := fs.Join(path, fname)
			if filedata, err := fs.ReadFile(filename); err == nil {
				if src, err := ParseFSFile(fset, fs, filename, filedata, mode); err == nil {
					name := src.Name.Name
					pkg, found := pkgs[name]
					if !found {
						pkg = &ast.Package{
							Name:  name,
							Files: make(map[string]*ast.File),
						}
						pkgs[name] = pkg
					}
					pkg.Files[filename] = src
				} else if first == nil {
					first = err
				}
			} else if first == nil {
				first = err
			}
		}
	}
	return
}
var (
	extGopFiles = map[string]ast.FileType{
		".go":  ast.FileTypeGo,
		".gop": ast.FileTypeGop,
		".spx": ast.FileTypeSpx,
		".gmx": ast.FileTypeGmx,
		".spc": ast.FileTypeGmx,  
	}
)
func RegisterFileType(ext string, format ast.FileType) {
	if format != ast.FileTypeSpx && format != ast.FileTypeGmx {
		panic("RegisterFileType: format should be FileTypeSpx or FileTypeGmx")
	}
	if _, ok := extGopFiles[ext]; ok {
		panic("RegisterFileType: file type exists")
	}
	extGopFiles[ext] = format
}
func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
	return ParseFSFile(fset, local, filename, src, mode)
}