[
  {
    "path": ".gitattributes",
    "content": "*.expect text eol=lf\n*.golden text eol=lf\n\n*.xgo text eol=lf\n*.gop text eol=lf\n*.go text eol=lf\n*.gox text eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "content": "name: Bug Report\ndescription: Create a report to help us improve\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        ⚠️ Make sure to browse the opened and closed issues before submit your issue.\n\n  - type: textarea\n    id: sample\n    attributes:\n      label: \"The following program `sample.gop` triggers an unexpected result\"\n      value: |\n        // add a sample\n      render: coffee\n    validations:\n      required: true\n\n  - type: textarea\n    id: expected\n    attributes:\n      label: Expected result\n      render: console\n    validations:\n      required: true\n\n  - type: textarea\n    id: got\n    attributes:\n      label: Got\n      description: |-\n        ```console\n        $ gop run ./sample.gop\n        // output\n        ```\n      placeholder: $ xgo run ./sample.gop\n      render: console\n    validations:\n      required: true\n\n  - type: input\n    id: version\n    attributes:\n      label: XGo Version\n      description: Can be a tag or a hash.\n    validations:\n      required: true\n\n  - type: textarea\n    id: additional\n    attributes:\n      label: Additional Notes\n      description: Use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) if needed.\n    validations:\n      required: false"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Visit the XGo website\n    url: https://xgo.dev\n    about: Much help can be found there\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/enhancement.yml",
    "content": "name: Feature request\ndescription: Propose a change to XGo\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        ⚠️ Make sure to browse the opened and closed issues before submit your issue.\n\n  - type: textarea\n    id: proposal\n    attributes:\n      label: Proposal\n      description: Write your feature request in the form of a proposal to be considered for implementation.\n    validations:\n      required: true\n\n  - type: textarea\n    id: background\n    attributes:\n      label: Background\n      description: Describe the background problem or need that led to this feature request.\n    validations:\n      required: true\n\n  - type: textarea\n    id: workarounds\n    attributes:\n      label: Workarounds\n      description: Are there any current workarounds that you're using that others in similar positions should know about?\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/codecov.yml",
    "content": "coverage:\n  ignore:\n    - \"builtin\"\n    - \"ast\"\n    - \"test\"\n    - \"scanner\"\n    - \"format\"\n    - \"demo\"\n    - \"demo/unit-test\"\n    - \"doc/spec\"\n    - \"cmd\"\n    - \"cl/cltest\"\n    - \"cl/outline\"\n    - \"cl/internal/dql\"\n    - \"cl/internal/huh\"\n    - \"cl/internal/llgo-hello\"\n    - \"cl/internal/mcp\"\n    - \"cl/internal/overload\"\n    - \"cl/internal/spx\"\n    - \"cl/internal/spx2\"\n    - \"cl/internal/spx3\"\n    - \"cl/internal/test\"\n    - \"cl/internal/unit\"\n    - \"parser/fsx\"\n    - \"parser/iox\"\n    - \"parser/parsertest\"\n    - \"x/jsonrpc2\"\n    - \"x/jsonrpc2/jsonrpc2test\"\n    - \"x/watcher\"\n    - \"x/langserver\"\n    - \"x/fsnotify\"\n    - \"x/fakenet\"\n    - \"x/typesutil\"\n    - \"x/gocmd\"\n    - \"x/gopenv\"\n    - \"tool\"\n    - \"encoding\"\n    - \"tpl\"\n    - \"dql\"\n  "
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    labels:\n      - dependabot\n      - actions\n    schedule:\n      interval: daily\n\n  - package-ecosystem: \"gomod\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/check_goreleaser_config.py",
    "content": "import os\nimport subprocess\nimport yaml\n\nfiles = [f for f in os.listdir('.') if not f.startswith(\n    \".\") and f not in [\"VERSION\"]]\nfiles.sort()\n# filter out the files that are ignored by git\nfiles = [f for f in files if subprocess.call(\n    [\"git\", \"ls-files\", \"--error-unmatch\", f],\n    stdout=subprocess.DEVNULL,\n    stderr=subprocess.DEVNULL) == 0]\n\ngorel = yaml.load(open(\".goreleaser.yaml\", \"r\"), Loader=yaml.FullLoader)\nscfiles = [f[\"source\"] for f in gorel[\"snapcrafts\"][0][\"extra_files\"]]\nscfiles.sort()\n\nfailed = False\n\nif files != scfiles:\n    failed = True\n    print(\"Files in snapcraft are different from the ones in the repo\")\n    print(\"Update .goreleaser.yaml in the snapcraft section:\")\n    for f in files:\n        print(f\"      - source: \\\"{f}\\\"\")\n        print(f\"        destination: \\\"{f}\\\"\")\n\nnfpms_files = [f[\"src\"]\n               for f in gorel[\"nfpms\"][0][\"contents\"] if f.get(\"type\") != \"symlink\"]\nnfpms_files.sort()\n\nif files != nfpms_files:\n    failed = True\n    print(\"Files in nfpms are different from the ones in the repo\")\n    print(\"Update .goreleaser.yaml in the nfpms section:\")\n    for f in files:\n        print(f\"      - src: \\\"{f}\\\"\")\n        print(f\"        dst: \\\"/usr/lib/{{{{ .ProjectName }}}}/{f}\\\"\")\n\n# Check archives[0].files\narchives_files = gorel[\"archives\"][0].get(\"files\", [])\narchives_files.sort()\n\nif files != archives_files:\n    failed = True\n    print(\"Files in archives are different from the ones in the repo\")\n    print(\"Update .goreleaser.yaml in the archives section:\")\n    for f in files:\n        print(f\"      - \\\"{f}\\\"\")\n\nif failed:\n    exit(1)\n\nprint(\".goreleaser checks passed\")\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: XGo CI\n\non:\n  push:\n    branches:\n    - \"*\"\n  pull_request:\n    branches:\n    - \"*\"\n\njobs:\n  Check:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n    - name: Check goreleaser files\n      run: |\n        pip install --no-input pyyaml\n        python .github/workflows/check_goreleaser_config.py\n\n  TestXGoInstaller:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n    - name: Test XGo installer\n      run: |\n        git config --global user.email \"build-robot@xgo.dev\"\n        git config --global user.name \"build robot\"\n        go test -v cmd/make_test.go\n\n  Test:\n    strategy:\n      matrix:\n        os:\n        - ubuntu-latest\n        - windows-latest\n        - macos-latest\n        go:\n        - 1.24.x\n        - 1.25.x\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v6\n\n    - name: Set up Go\n      uses: actions/setup-go@v6\n      with:\n        go-version: ${{ matrix.go }}\n\n    - name: Run testcases\n      run: go test -v -coverprofile=\"coverage.txt\" -covermode=atomic ./...\n\n    - name: Codecov\n      uses: codecov/codecov-action@v5\n      with:\n        slug: goplus/xgo\n\n  FormatCheck:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: \"1.24.2\"\n\n      - name: Check formatting\n        run: |\n          if [ -n \"$(go fmt ./... | grep -v xgo_autogen)\" ]; then\n            echo \"Some files are not properly formatted. Please run 'go fmt ./...'\"\n            exit 1\n          fi\n          ./all.bash\n          cd cmd/xgo; xgo fmt -t ./...\n"
  },
  {
    "path": ".github/workflows/release-build.yml",
    "content": "name: Release Build\n\non:\n  push:\n    tags:\n      - \"v*\"\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        fetch-depth: 0\n\n    - name: Check goreleaser files\n      run: |\n        pip install --no-input pyyaml\n        python .github/workflows/check_goreleaser_config.py\n\n    - name: Install Snapcraft\n      uses: samuelmeuli/action-snapcraft@v3\n\n    - name: Checkout tag\n      run: |\n        git fetch --depth=1 origin +refs/tags/*:refs/tags/*\n        tag_name=\"${GITHUB_REF##*/}\"\n        echo Current tag: $tag_name\n        git checkout $tag_name\n        echo \"TAG_NAME=${tag_name}\" >> $GITHUB_ENV\n\n    - name: Check pre-release\n      run: |\n        tag=\"${TAG_NAME}\"\n        # check stable version format vX.Y.Z\n        if ! [[ $tag =~ ^v[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then\n          echo \"This is a pre-release: $tag\"\n          echo \"IS_PRERELEASE=true\" >> $GITHUB_ENV\n        fi\n\n    - name: Set up Go\n      uses: actions/setup-go@v6\n      with:\n        go-version: 1.24.x\n\n    - name: Set up QEMU\n      uses: docker/setup-qemu-action@v4\n\n    - name: Set up Docker Buildx\n      uses: docker/setup-buildx-action@v4\n\n    - name: Log in to GitHub Container Registry\n      uses: docker/login-action@v4\n      with:\n        registry: ghcr.io\n        username: ${{ github.actor }}\n        password: ${{ github.token }}\n\n    - name: Release with goreleaser\n      uses: goreleaser/goreleaser-action@v7\n      with:\n        # either 'goreleaser' (default) or 'goreleaser-pro'\n        distribution: goreleaser\n        version: latest\n        args: release --clean -p 4\n      env:\n        GITHUB_TOKEN: ${{ github.token }}\n        DOCKER_IMAGE_REPO: ghcr.io/${{ github.repository }}\n        WINGET_PKGS_PRIVATE_KEY: ${{ secrets.WINGET_PKGS_PRIVATE_KEY }}\n\n    - name: Upload deb/rpm to Fury.io\n      if: env.IS_PRERELEASE != 'true'\n      run: |\n        for file in .dist/*.{deb,rpm}\n        do\n          echo \"Uploading $file to Fury.io\"\n          CODE=`curl --write-out '%{http_code}' --output /dev/null -sS -F package=@$file https://$FURY_TOKEN@push.fury.io/$GITHUB_REPOSITORY_OWNER/`\n          if [ \"$CODE\" != \"200\" ]; then\n            echo \"Upload failed with code $CODE\"\n            exit 1\n          fi\n        done\n      env:\n        FURY_TOKEN: ${{ secrets.FURY_TOKEN }}\n"
  },
  {
    "path": ".github/xgopilot.yml",
    "content": "claude:\n  model: \"claude-4.6-opus\"\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n*.txt\n*.cache\n.DS_Store\ntest.db\ngo.json\nx.mod\n\n# gop\n# gopfmt\n# goptestgo\n\n# Folders\n_obj\n_test\n_old\n_t\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\ncoverage.txt\n\n.xgo/\n.gop/\ngop_autogen*.go\nxgo_autogen*.go\n!dql/fetcher/**/xgo_autogen.go\n!cmd/xgo/gop_autogen*.go\n!cmd/xgo/xgo_autogen*.go\n!cmd/hdq/xgo_autogen*.go\n!demo/fullspec/mixgo-complex/xgo_autogen.go\n!demo/mixgo/xgo_autogen.go\ngo.json\ngo.work*\nformat.result\n*.cache\n\n*.exe\n*.test\n*.prof\n\n.vscode\n.idea\n\ncmd/qfmt/qfmt\n\nbin/\ngo-num/\n_todo*.go\n\n.dist/\n"
  },
  {
    "path": ".goreleaser.yaml",
    "content": "version: 2\n\nenv:\n  - DOCKER_IMAGE_REPO={{ envOrDefault \"DOCKER_IMAGE_REPO\" \"xgo\" }}\n\ndist: .dist\n\nbefore:\n  hooks:\n    - go mod download\n\nbuilds:\n  - id: xgo\n    main: ./cmd/xgo\n    binary: bin/xgo\n    flags:\n      - -trimpath\n    ldflags:\n      - -X github.com/goplus/xgo/env.buildVersion=v{{.Version}}\n      - -X github.com/goplus/xgo/env.buildDate={{.CommitDate}}\n    env:\n      - CGO_ENABLED=0\n    goos:\n      - linux\n      - windows\n      - darwin\n    mod_timestamp: \"{{ .CommitTimestamp }}\"\n  - id: gop\n    main: ./cmd/xgo\n    binary: bin/gop\n    flags:\n      - -trimpath\n    ldflags:\n      - -X github.com/goplus/xgo/env.buildVersion=v{{.Version}}\n      - -X github.com/goplus/xgo/env.buildDate={{.CommitDate}}\n    env:\n      - CGO_ENABLED=0\n    goos:\n      - linux\n      - windows\n      - darwin\n    mod_timestamp: \"{{ .CommitTimestamp }}\"\n\narchives:\n  - format: tar.gz\n    name_template: >-\n      {{.ProjectName}}{{.Version}}.{{.Os}}-{{.Arch}}\n      {{- if .Arm}}v{{.Arm}}{{end}}\n    format_overrides:\n      - goos: windows\n        format: zip\n    files:\n      - \"CLAUDE.md\"\n      - \"CODE_OF_CONDUCT.md\"\n      - \"Dockerfile\"\n      - \"LICENSE\"\n      - \"Makefile\"\n      - \"README.md\"\n      - \"all.bash\"\n      - \"all.bat\"\n      - \"ast\"\n      - \"tpl\"\n      - \"tool\"\n      - \"builtin\"\n      - \"cl\"\n      - \"cmd\"\n      - \"doc\"\n      - \"dql\"\n      - \"encoding\"\n      - \"env\"\n      - \"format\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"make.bash\"\n      - \"make.bat\"\n      - \"parser\"\n      - \"printer\"\n      - \"scanner\"\n      - \"test\"\n      - \"demo\"\n      - \"token\"\n      - \"x\"\n\nchangelog:\n  sort: asc\n  filters:\n    exclude:\n      - \"^docs:\"\n      - \"^test:\"\n\ndockers_v2:\n  - id: xgo\n    dockerfile: Dockerfile\n    images:\n      - \"{{ .Env.DOCKER_IMAGE_REPO }}\"\n    tags:\n      - \"{{ .Version }}\"\n      - \"{{ .Major }}.{{ .Minor }}\"\n      - \"{{ .Major }}\"\n      - latest\n    platforms:\n      - linux/386\n      - linux/amd64\n      - linux/arm64\n    build_args:\n      USE_GORELEASER_ARTIFACTS: \"1\"\n    extra_files:\n      - ./\n\nwinget:\n  - name: goplus\n    homepage: \"https://xgo.dev/\"\n    publisher: goplus\n    publisher_url: https://github.com/goplus/xgo\n    publisher_support_url: \"https://github.com/goplus/xgo/issues/new\"\n    package_identifier: goplus.xgo\n    path: \"manifests/g/goplus/xgo/{{.Version}}\"\n    tags:\n      - golang\n      - go\n      - xgo\n      - goplus\n      - programming\n      - language\n      - compiler\n      - interpreter\n      - data science\n      - engineering\n      - education\n    short_description: The XGo Programming Language\n    description: |\n      The XGo programming language is designed for engineering, STEM education, and data science.\n      - For engineering: working in the simplest language that can be mastered by children.\n      - For STEM education: studying an engineering language that can be used for work in the future.\n      - For data science: communicating with engineers in the same language.\n    license: Apache-2.0\n    skip_upload: auto\n    release_notes: \"{{.Changelog}}\"\n    release_notes_url: \"https://github.com/{{ .Env.GITHUB_REPOSITORY_OWNER }}/xgo/releases/tag/v{{.Version}}\"\n    dependencies:\n      - package_identifier: GoLang.Go\n        minimum_version: 1.18.0\n    repository:\n      owner: goplus\n      name: winget-pkgs\n      branch: \"{{.ProjectName}}-v{{.Version}}\"\n      git:\n        url: \"git@github.com:{{ .Env.GITHUB_REPOSITORY_OWNER }}/winget-pkgs.git\"\n        private_key: \"{{ .Env.WINGET_PKGS_PRIVATE_KEY }}\"\n      pull_request:\n        enabled: true\n        draft: true\n        base:\n          owner: microsoft\n          name: winget-pkgs\n          branch: master\n\nnfpms:\n  - package_name: xgo\n    vendor: goplus\n    homepage: https://xgo.dev/\n    maintainer: Li Jie <cpunion@gmail.com>\n    license: Apache-2.0\n    description: |\n      The XGo programming language is designed for engineering, STEM education, and data science.\n      - For engineering: working in the simplest language that can be mastered by children.\n      - For STEM education: studying an engineering language that can be used for work in the future.\n      - For data science: communicating with engineers in the same language.\n    formats:\n      - \"deb\"\n      - \"rpm\"\n    overrides:\n      deb:\n        dependencies:\n          - \"golang-go (>= 1.18.0)\"\n      rpm:\n        dependencies:\n          - \"golang-bin >= 1.18.0\"\n    file_name_template: >-\n      {{ .ProjectName }}{{.Version}}.{{ .Os }}-{{ .Arch }}\n      {{- if .Arm }}v{{ .Arm }}{{ end }}\n    bindir: /usr/lib/{{ .ProjectName }}\n    contents:\n      # source folder\n      - src: \"CLAUDE.md\"\n        dst: \"/usr/lib/{{ .ProjectName }}/CLAUDE.md\"\n      - src: \"CODE_OF_CONDUCT.md\"\n        dst: \"/usr/lib/{{ .ProjectName }}/CODE_OF_CONDUCT.md\"\n      - src: \"Dockerfile\"\n        dst: \"/usr/lib/{{ .ProjectName }}/Dockerfile\"\n      - src: \"LICENSE\"\n        dst: \"/usr/lib/{{ .ProjectName }}/LICENSE\"\n      - src: \"Makefile\"\n        dst: \"/usr/lib/{{ .ProjectName }}/Makefile\"\n      - src: \"README.md\"\n        dst: \"/usr/lib/{{ .ProjectName }}/README.md\"\n      - src: \"all.bash\"\n        dst: \"/usr/lib/{{ .ProjectName }}/all.bash\"\n      - src: \"all.bat\"\n        dst: \"/usr/lib/{{ .ProjectName }}/all.bat\"\n      - src: \"ast\"\n        dst: \"/usr/lib/{{ .ProjectName }}/ast\"\n      - src: \"tpl\"\n        dst: \"/usr/lib/{{ .ProjectName }}/tpl\"\n      - src: \"dql\"\n        dst: \"/usr/lib/{{ .ProjectName }}/dql\"\n      - src: \"tool\"\n        dst: \"/usr/lib/{{ .ProjectName }}/tool\"\n      - src: \"builtin\"\n        dst: \"/usr/lib/{{ .ProjectName }}/builtin\"\n      - src: \"cl\"\n        dst: \"/usr/lib/{{ .ProjectName }}/cl\"\n      - src: \"cmd\"\n        dst: \"/usr/lib/{{ .ProjectName }}/cmd\"\n      - src: \"doc\"\n        dst: \"/usr/lib/{{ .ProjectName }}/doc\"\n      - src: \"encoding\"\n        dst: \"/usr/lib/{{ .ProjectName }}/encoding\"\n      - src: \"env\"\n        dst: \"/usr/lib/{{ .ProjectName }}/env\"\n      - src: \"format\"\n        dst: \"/usr/lib/{{ .ProjectName }}/format\"\n      - src: \"go.mod\"\n        dst: \"/usr/lib/{{ .ProjectName }}/go.mod\"\n      - src: \"go.sum\"\n        dst: \"/usr/lib/{{ .ProjectName }}/go.sum\"\n      - src: \"make.bash\"\n        dst: \"/usr/lib/{{ .ProjectName }}/make.bash\"\n      - src: \"make.bat\"\n        dst: \"/usr/lib/{{ .ProjectName }}/make.bat\"\n      - src: \"parser\"\n        dst: \"/usr/lib/{{ .ProjectName }}/parser\"\n      - src: \"printer\"\n        dst: \"/usr/lib/{{ .ProjectName }}/printer\"\n      - src: \"scanner\"\n        dst: \"/usr/lib/{{ .ProjectName }}/scanner\"\n      - src: \"test\"\n        dst: \"/usr/lib/{{ .ProjectName }}/test\"\n      - src: \"demo\"\n        dst: \"/usr/lib/{{ .ProjectName }}/demo\"\n      - src: \"token\"\n        dst: \"/usr/lib/{{ .ProjectName }}/token\"\n      - src: \"x\"\n        dst: \"/usr/lib/{{ .ProjectName }}/x\"\n      # symlinks to binaries\n      - src: \"/usr/lib/{{ .ProjectName }}/bin/xgo\"\n        dst: /usr/bin/xgo\n        type: symlink\n      - src: \"/usr/lib/{{ .ProjectName }}/bin/xgo\"\n        dst: /usr/bin/gop\n        type: symlink\n\nsnapcrafts:\n  - id: xgo\n    name: xgo\n    title: The XGo Programming Language\n    summary: The XGo Programming Language\n    description: |\n      The XGo programming language is designed for engineering, STEM education, and data science.\n      - For engineering: working in the simplest language that can be mastered by children.\n      - For STEM education: studying an engineering language that can be used for work in the future.\n      - For data science: communicating with engineers in the same language.\n    confinement: classic\n    license: Apache-2.0\n    name_template: >-\n      {{ .ProjectName }}{{.Version}}.{{ .Os }}-{{ .Arch }}\n      {{- if .Arm }}v{{ .Arm }}{{ end }}\n    extra_files:\n      # source folder\n      - source: \"CLAUDE.md\"\n        destination: \"CLAUDE.md\"\n      - source: \"CODE_OF_CONDUCT.md\"\n        destination: \"CODE_OF_CONDUCT.md\"\n      - source: \"Dockerfile\"\n        destination: \"Dockerfile\"\n      - source: \"LICENSE\"\n        destination: \"LICENSE\"\n      - source: \"Makefile\"\n        destination: \"Makefile\"\n      - source: \"README.md\"\n        destination: \"README.md\"\n      - source: \"all.bash\"\n        destination: \"all.bash\"\n      - source: \"all.bat\"\n        destination: \"all.bat\"\n      - source: \"ast\"\n        destination: \"ast\"\n      - source: \"tpl\"\n        destination: \"tpl\"\n      - source: \"dql\"\n        destination: \"dql\"\n      - source: \"tool\"\n        destination: \"tool\"\n      - source: \"builtin\"\n        destination: \"builtin\"\n      - source: \"cl\"\n        destination: \"cl\"\n      - source: \"cmd\"\n        destination: \"cmd\"\n      - source: \"doc\"\n        destination: \"doc\"\n      - source: \"encoding\"\n        destination: \"encoding\"\n      - source: \"env\"\n        destination: \"env\"\n      - source: \"format\"\n        destination: \"format\"\n      - source: \"go.mod\"\n        destination: \"go.mod\"\n      - source: \"go.sum\"\n        destination: \"go.sum\"\n      - source: \"make.bash\"\n        destination: \"make.bash\"\n      - source: \"make.bat\"\n        destination: \"make.bat\"\n      - source: \"parser\"\n        destination: \"parser\"\n      - source: \"printer\"\n        destination: \"printer\"\n      - source: \"scanner\"\n        destination: \"scanner\"\n      - source: \"test\"\n        destination: \"test\"\n      - source: \"demo\"\n        destination: \"demo\"\n      - source: \"token\"\n        destination: \"token\"\n      - source: \"x\"\n        destination: \"x\"\n    apps:\n      xgo:\n        command: \"xgo\"\n        aliases: [\"xgo\", \"gop\"]\n        environment:\n          XGOROOT: \"$SNAP\"\n\nchecksum:\n  name_template: \"{{ .ProjectName }}{{ .Version }}.checksums.txt\"\n\nrelease:\n  prerelease: auto\n"
  },
  {
    "path": "CLAUDE.md",
    "content": "# XGo Project AI Assistant Guide\n\n## Project Overview\n\n**XGo** is the first AI-native programming language that integrates software engineering into a unified whole.\n\n**Key Characteristics**:\n- Easy to learn with smaller syntax set than Go and Python\n- Ready for large projects with unified ecosystem integration\n\n## My Role & Your Role\n\n- **My Role**: XGo language developer/contributor\n- **Your Role**: Senior programming language development assistant specializing in syntax design and compiler implementation\n\n## Workflow & Collaboration Style\n\n### Adding New Syntax Features\nWhen implementing new language syntax, follow this three-phase approach:\n\n**IMPORTANT**: Each phase must be implemented in a separate pull request. Do NOT mix phases in a single PR. This separation ensures:\n- Clear review focus (grammar vs semantics vs documentation)\n- Easier rollback if issues are found\n- Better git history and maintainability\n- Allows grammar to be reviewed independently from implementation details\n\n#### Phase 1: Grammar Definition (First Pull Request)\n**Scope**: AST, parser, and printer modifications ONLY\n- **AST**: Define new node types in `ast/` directory (if needed - often existing nodes can be reused)\n- **Parser**: Implement parsing rules in `parser/` directory to recognize the new syntax\n- **Printer**: Add formatting support for new syntax (inverse of parsing) in `printer/` directory\n- **Testing**: Add test cases in `parser/_testdata/` for new syntax\n  - **Note**: Printer shares test cases with parser - do NOT create separate test files in `printer/_testdata/`\n- **What NOT to include**: Do NOT add any code generation or semantic logic in `cl/` package - that belongs in Phase 2\n\n#### Phase 2: Semantic Implementation (Second Pull Request)\n**Scope**: Code generation via `cl` package ONLY\n- **Code Generation**: Implement semantics using `github.com/goplus/gogen` package\n- **Type Safety**: Leverage gogen's type information maintenance for semantic correctness\n- **Testing**: Add comprehensive test cases in `cl/_testgop/` covering various usage scenarios\n- **Prerequisite**: Phase 1 PR must be merged before starting Phase 2\n\n#### Phase 3: Documentation (Third Pull Request)\n**Scope**: User-facing documentation updates ONLY\n- **Quick Start Guide**: Add feature documentation to `doc/docs.md` with practical examples\n- **Table of Contents**: Update TOC in quick start to include new feature section\n- **Language Specification**: Update specification documents (see Language Specification Structure below)\n- **Examples**: Provide clear, runnable code examples demonstrating the feature\n- **Prerequisite**: Phase 2 PR must be merged before starting Phase 3\n\n### Language Specification Structure\n\nXGo maintains two levels of language specifications to serve different user needs:\n\n#### MiniSpec (Recommended Best Practices)\n- **Purpose**: Simplified syntax set representing recommended best practices\n- **Audience**: All XGo users - everyone should learn and apply this subset\n- **Characteristics**: Simple, Turing-complete, and sufficient for elegant implementation of any business requirements\n- **Files to update**:\n  - `doc/spec-mini.md` - MiniSpec documentation in markdown format\n  - `doc/spec/mini/mini.xgo` - MiniSpec grammar definition in XGo TPL (EBNF-like) syntax\n\n#### FullSpec (Complete Language Syntax)\n- **Purpose**: Complete syntax set including all language features\n- **Audience**: Experts and library designers who need advanced features\n- **Characteristics**: Comprehensive syntax including specialized features beyond MiniSpec\n- **Files to update**:\n  - `doc/spec.md` - FullSpec documentation in markdown format\n\n#### Determining Spec Classification for New Syntax\n\nWhen adding new syntax to XGo, you must determine whether it belongs in the MiniSpec or FullSpec:\n\n**Add to MiniSpec if the syntax**:\n- Represents a recommended best practice for general use\n- Is simple and intuitive for most users\n- Solves common programming problems elegantly\n- Should be learned by all XGo developers\n\n**Add to FullSpec only if the syntax**:\n- Is specialized for advanced use cases (e.g., library design)\n- Adds complexity that most users don't need\n- Provides alternative ways to accomplish tasks already covered in MiniSpec\n- Is primarily intended for expert developers\n\n**Update Process**:\n1. Determine the appropriate specification level (MiniSpec or FullSpec)\n2. Update the corresponding markdown documentation file(s)\n3. If adding to MiniSpec, also update the TPL grammar file (`doc/spec/mini/mini.xgo`)\n4. Ensure examples demonstrate the new syntax clearly\n\n### Communication Protocol\n- When I request syntax additions, first confirm the exact grammar specification\n- Always consider backward compatibility with existing Go code\n- For ambiguous requirements, ask clarifying questions about:\n  - Precedence and associativity rules\n  - Error handling expectations\n  - Integration with existing type system\n\n## Technical Specifications\n\n### Compiler Architecture\n- **Target**: XGo compiles to Go code, not machine code\n- **Foundation**: Built on `github.com/goplus/gogen` for robust Go AST generation\n- **Key Benefit**: gogen maintains type information, ensuring both syntactic and semantic correctness\n\n## Quality Standards\n\n### Code Requirements\n\n- Maintain full compatibility with existing Go ecosystem\n- Ensure new syntax doesn't break existing XGo/Go code\n- Follow Go idioms in generated code\n- Provide comprehensive error messages\n- **Code Formatting**: Run `go fmt` on any changed source files before committing\n\n### Documentation Expectations\n\n- Update language specification documents\n- Add examples to Quick Start guide\n- Document any limitations or special considerations\n\n### Testing Requirements\n\n- **Phase 1**: 100% test coverage for new syntax parsing in `parser/_testdata/`\n- **Phase 2**: Comprehensive test coverage for semantic implementation in `cl/_testgop/` covering:\n  - Common usage scenarios\n  - Edge cases and error conditions\n  - Integration with existing type system\n- **Phase 3**: Documentation validation\n  - Ensure all code examples in documentation are runnable and correct\n  - Verify documentation accurately reflects implemented behavior\n  - Check that TOC links work correctly\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\nconduct@xgo.dev.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "Dockerfile",
    "content": "ARG BASE_IMAGE=golang:1.24-bookworm\n\nFROM $BASE_IMAGE AS build\n\nARG TARGETPLATFORM\nARG USE_GORELEASER_ARTIFACTS=0\n\nWORKDIR /usr/local/src/xgo\nCOPY . .\n\nRUN << EOF\nset -eux\n\nXGOROOT=/usr/local/xgo\nmkdir -p \"$XGOROOT\"\n\nif [ \"$USE_GORELEASER_ARTIFACTS\" -eq 1 ]; then\n\tcp -rp \"$TARGETPLATFORM\"/* \"$XGOROOT\"/\nelse\n\tgit ls-tree --full-tree --name-only -r HEAD | grep -vE \"^\\.\" | xargs -I {} cp --parents {} \"$XGOROOT\"/\n\t./all.bash\n\tmv bin \"$XGOROOT\"/\nfi\nEOF\n\nFROM $BASE_IMAGE\nENV XGOROOT=/usr/local/xgo\nCOPY --from=build $XGOROOT/ $XGOROOT/\nENV PATH=$XGOROOT/bin:$PATH\nWORKDIR /xgo\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "Makefile",
    "content": "NAME := gop\nRELEASE_VERSION := `git describe --tags`\nBUILD_ROOT_DIR := build-dir\n\n.PHONY: clean all\nall: build\n\nclean:\n\trm -rf $(BUILD_ROOT_DIR)/*\n\trm -f bin/*\n\nbuild:\n\tgo run cmd/make.go -build\n\ndist:\n\t$(MAKE) clean\n\tmkdir -p bin/\n\tgo build -o $(BUILD_ROOT_DIR)/make cmd/make.go\n\t$(MAKE) build-all\n\nbuild-all: darwin-amd64.zip darwin-arm64.zip linux-386.zip linux-amd64.zip \\\n\tlinux-armv7.zip windows-386.zip windows-amd64.zip windows-armv7.zip windows-arm64.zip\n\nbuild-dist:\n\t@mkdir -p bin/\n\t@rm -rf bin/*\n\t$(BUILD_ROOT_DIR)/make -build\n\n%.zip: %\n\t@echo \"Building $(NAME)-$(RELEASE_VERSION)-$@\"\n\n\t@rm -f $(BUILD_ROOT_DIR)/$(NAME)-$(RELEASE_VERSION)-$@\n\tzip -r $(BUILD_ROOT_DIR)/$(NAME)-$(RELEASE_VERSION)-$@ . -x \".*\" -x \"*/.*\" -x \"$(BUILD_ROOT_DIR)/*\"\n\t@echo \"$(NAME)-$(RELEASE_VERSION)-$@ Done\"\n\ndarwin-amd64:\n\t$(MAKE) GOARCH=amd64 GOOS=darwin BUILD_DIR=$(BUILD_ROOT_DIR)/$@/bin build-dist\n\ndarwin-arm64:\n\t$(MAKE) GOARCH=arm64 GOOS=darwin BUILD_DIR=$(BUILD_ROOT_DIR)/$@/bin build-dist\n\nlinux-386:\n\t$(MAKE) GOARCH=386 GOOS=linux BUILD_DIR=$(BUILD_ROOT_DIR)/$@/bin build-dist\n\nlinux-amd64:\n\t$(MAKE) GOARCH=amd64 GOOS=linux BUILD_DIR=$(BUILD_ROOT_DIR)/$@/bin build-dist\n\nlinux-armv7:\n\t$(MAKE) GOARCH=arm GOOS=linux GOARM=7 BUILD_DIR=$(BUILD_ROOT_DIR)/$@/bin build-dist\n\nwindows-386:\n\t$(MAKE) GOARCH=386 GOOS=windows EXE_SUFFIX=.exe BUILD_DIR=$(BUILD_ROOT_DIR)/$@/bin build-dist\n\nwindows-amd64:\n\t$(MAKE) GOARCH=amd64 GOOS=windows EXE_SUFFIX=.exe BUILD_DIR=$(BUILD_ROOT_DIR)/$@/bin build-dist\n\nwindows-armv7:\n\t$(MAKE) GOARCH=arm GOOS=windows EXE_SUFFIX=.exe GOARM=7 BUILD_DIR=$(BUILD_ROOT_DIR)/$@/bin build-dist\n\nwindows-arm64:\n\t$(MAKE) GOARCH=arm64 GOOS=windows EXE_SUFFIX=.exe BUILD_DIR=$(BUILD_ROOT_DIR)/$@/bin build-dist"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n<p></p>\n<p>\n    <img width=\"80\" src=\"https://xgo.dev/favicon.svg\">\n</p>\n<h1>The XGo Programming Language</h1>\n\n\n[xgo.dev](https://xgo.dev) | [Docs](doc/docs.md) | [XGo vs. Go](doc/xgo-vs-go.md) | [Tutorials](https://tutorial.xgo.dev) | [Playground](https://play.xgo.dev) | [XGo REPL (iXGo)](https://repl.xgo.dev) | [Contributing & compiler design](doc/contributing.md)\n</div>\n\n<div align=\"center\">\n<!--\n[![VSCode](https://img.shields.io/badge/vscode-XGo-teal.svg)](https://github.com/gopcode/vscode-goplus)\n[![Discord](https://img.shields.io/discord/983646982100897802?label=Discord&logo=discord&logoColor=white)](https://discord.gg/mYjWCJDcAr)\n[![Interpreter](https://img.shields.io/badge/interpreter-iXGo-seagreen.svg)](https://github.com/goplus/ixgo)\n-->\n\n[![Build Status](https://github.com/goplus/xgo/actions/workflows/go.yml/badge.svg)](https://github.com/goplus/xgo/actions/workflows/go.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/goplus/xgo)](https://goreportcard.com/report/github.com/goplus/xgo)\n[![Coverage Status](https://codecov.io/gh/goplus/xgo/branch/main/graph/badge.svg)](https://codecov.io/gh/goplus/xgo)\n[![GitHub release](https://img.shields.io/github/v/tag/goplus/xgo.svg?label=release)](https://github.com/goplus/xgo/releases)\n[![Discord](https://img.shields.io/badge/Discord-online-success.svg?logo=discord&logoColor=white)](https://discord.com/invite/mYjWCJDcAr)\n\n</div>\n\nXGo is a programming language that reads like plain English. But it's also incredibly powerful — it lets you leverage assets from C/C++, Go, Python, and JavaScript/TypeScript, creating a unified software engineering ecosystem.\n\n```\nXGo := C * Go * Python * JavaScript + Scratch\n```\n\nOur vision is to **enable everyone to become a builder of the world**.\n\n#### Easy to learn\n\n* Simple and easy to understand\n* Smaller syntax set than Go and Python in best practices\n\n#### Ready for large projects\n\n* Integrate C/C++, Go, Python, and JavaScript/TypeScript into a unified ecosystem\n* Derived from Go and easy to build large projects from its good engineering foundation\n\nThe XGo programming language is designed for engineering, STEM education, and data science.\n\n* **For engineering**: working in the simplest language that can be mastered by children.\n* **For STEM education**: studying an engineering language that can be used for work in the future.\n* **For data science**: communicating with engineers in the same language.\n\nFor more details, see [Quick Start](doc/docs.md).\n\n\n## Key Features of XGo\n\n* Approaching natural language expression and intuitive (see [How XGo simplifies Go's expressions](#how-xgo-simplifies-gos-expressions)).\n* Smallest but Turing-complete syntax set in best practices (see [The XGo Mini Specification](doc/spec-mini.md)).\n* Fully compatible with [Go](https://github.com/golang/go) and can mix Go/XGo code in the same package (see [The XGo Full Specification](doc/spec.md) and [Go/XGo Hybrid Programming](doc/docs.md#gogo-hybrid-programming)).\n* Integrating with the C ecosystem including Python/JavaScript and providing limitless possibilities based on [LLGo](https://github.com/goplus/llgo) (see [Importing C/C++ and Python libraries](#importing-cc-and-python-libraries)).\n* Does not support DSL (Domain-Specific Languages), but supports SDF (Specific Domain Friendliness) (see [XGo Classfiles](#xgo-classfiles) and [Domain Text Literals](doc/domian-text-lit.md)).\n\n\n## How XGo simplifies Go's expressions\n\nDifferent from the function call style of most languages, XGo recommends command style code:\n\n```coffee\nprintln \"Hello world\"\n```\n\nTo emphasize our preference for command style, we introduce `echo` as an alias for `println`:\n\n```coffee\necho \"Hello world\"\n```\n\nFor more discussion on coding style, see https://tutorial.xgo.dev/hello-world.\n\nCode style is just the first step. We have made many efforts to make the code more intuitive and closer to natural language expression. These include:\n\n| Go code | XGo code | Note |\n| ---- | ---- | ---- |\n| package main<br><br>import \"fmt\"<br><br>func main() {<br>&nbsp;&nbsp;&nbsp;&nbsp;fmt.Println(\"Hi\")<br>} | import \"fmt\"<br><br>fmt.Println(\"Hi\")<br> | Program structure: XGo allows omitting `package main` and `func main` |\n| fmt.Println(\"Hi\") | echo(\"Hi\") | [More builtin functions](doc/builtin.md): It simplifies the expression of the most common tasks |\n| fmt.Println(\"Hi\") | echo \"Hi\" | [Command-line](doc/fncall.md) style code: It reduces the number of parentheses in the code as much as possible, making it closer to natural language |\n| name := \"Ken\"<br>fmt.Printf(<br>&nbsp;&nbsp;\"Hi %s\\n\", name) | name := \"Ken\"<br>echo \"Hi ${name}\" | [Goodbye printf](doc/goodbye-printf.md), use `${expr}` in [string](doc/string.md) literals |\n| a := []int{1, 2, 3} | a := [1, 2, 3] | [List/Slice](doc/slice.md) literals |\n| a = append(a, 4)<br>a = append(a, 5, 6, 7) | a <- 4<br>a <- 5, 6, 7 | Append values to a list |\n| a := map[string]int{<br>&nbsp;&nbsp;&nbsp;&nbsp;\"Monday\": 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;\"Tuesday\": 2,<br>} | a := {<br>&nbsp;&nbsp;&nbsp;&nbsp;\"Monday\": 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;\"Tuesday\": 2,<br>} | [Map](doc/map.md) literals |\n| OnStart(func() {<br>&nbsp;&nbsp;&nbsp;&nbsp;...<br>}) | onStart => {<br>&nbsp;&nbsp;&nbsp;&nbsp;...<br>} | [Lambda](doc/func-closure.md) expressions |\n| Play(\"1.mp3\", &Options{Loop: true}) | play \"1.mp3\", loop = true | Python-like [keyword arguments](doc/func-closure.md#keyword-arguments) (kwargs) |\n| type Rect struct {<br>&nbsp;&nbsp;&nbsp;&nbsp;Width&nbsp; float64<br>&nbsp;&nbsp;&nbsp;&nbsp;Height float64<br>}<br> | type Rect (width, height float64) | [Tuples vs. Structs](doc/struct-vs-tuple.md): We encourage using tuples to implement UDTs instead of structs. |\n| type Rect struct {<br>&nbsp;&nbsp;&nbsp;&nbsp;Width&nbsp; float64<br>&nbsp;&nbsp;&nbsp;&nbsp;Height float64<br>}<br><br>func (this *Rect) Area() float64 { <br>&nbsp;&nbsp;&nbsp;&nbsp;return this.Width * this.Height<br>} | var (<br>&nbsp;&nbsp;&nbsp;&nbsp;Width&nbsp; float64<br>&nbsp;&nbsp;&nbsp;&nbsp;Height float64<br>)<br><br>func Area() float64 { <br>&nbsp;&nbsp;&nbsp;&nbsp;return Width * Height<br>} | [XGo Classfiles](doc/classfile.md): We can express OOP with global variables and functions. |\n\nFor more details, see [The XGo Mini Specification](doc/spec-mini.md).\n\n\n## Importing C/C++ and Python libraries\n\nXGo can choose different Go compilers as its underlying support. Currently known supported Go compilers include:\n\n* [go](https://go.dev/) (The official Go compiler supported by Google)\n* [llgo](https://github.com/goplus/llgo) (The Go compiler supported by the XGo team)\n* [tinygo](https://tinygo.org/) (A Go compiler for small places)\n\nCurrently, XGo defaults to using [go](https://go.dev/) as its underlying support, but in the future, it will be [llgo](https://github.com/goplus/llgo).\n\nLLGo is a Go compiler based on [LLVM](https://llvm.org/) in order to better integrate Go with the C ecosystem including Python and JavaScript. It aims to expand the boundaries of Go/XGo, providing limitless possibilities such as:\n\n* Game development\n* AI and data science\n* WebAssembly\n* Embedded development\n* ...\n\nIf you wish to use [llgo](https://github.com/goplus/llgo), specify the `-llgo` flag when initializing an XGo module:\n\n```sh\nxgo mod init -llgo YourModulePath\n```\n\nThis will generate a `go.mod` file with the following contents (It may vary slightly depending on the versions of local XGo and LLGo):\n\n```go\nmodule YourModulePath\n\ngo 1.21 // llgo 1.0\n\nrequire github.com/goplus/lib v0.2.0\n```\n\nBased on LLGo, XGo can import libraries written in C/C++ and Python.\n\nHere is an example (see [chello](demo/_llgo/chello/hello.xgo)) of printing `Hello world` using C's `printf`:\n\n```go\nimport \"c\"\n\nc.printf c\"Hello world\\n\"\n```\n\nHere, `c\"Hello world\\n\"` is a syntax supported by XGo, representing a null-terminated C-style string.\n\nTo run this example, you can:\n\n```sh\ncd YourModulePath  # set work directory to your module\nxgo mod tidy       # for generating go.sum file\nxgo run .\n```\n\nAnd here is an example (see [pyhello](demo/_llgo/pyhello/hello.xgo)) of printing `Hello world` using Python's `print`:\n\n```go\nimport \"py/std\"\n\nstd.print py\"Hello world\"\n```\n\nHere, `py\"Hello world\"` is a syntax supported by XGo, representing a Python string.\n\nHere are more examples of XGo calling C/C++ and Python libraries:\n\n* [pytensor](demo/_llgo/pytensor/tensor.xgo): a simple demo using [py/torch](https://pkg.go.dev/github.com/goplus/lib/py/torch)\n* [tetris](demo/_llgo/tetris/tetris.xgo): a tetris game based on [c/raylib](https://pkg.go.dev/github.com/goplus/lib/c/raylib)\n* [sqlitedemo](demo/_llgo/sqlitedemo/sqlitedemo.xgo): a demo using [c/sqlite](https://pkg.go.dev/github.com/goplus/lib/c/sqlite)\n\nTo find out more about LLGo/XGo's support for C/C++ and Python in detail, please refer to homepage of [llgo](https://github.com/goplus/llgo).\n\n\n## XGo Classfiles\n\n```\nOne language can change the whole world.\nXGo is a \"DSL\" for all domains.\n```\n\nRob Pike once said that if he could only introduce one feature to Go, he would choose `interface` instead of `goroutine`. `classfile` (and `class framework`) is as important to XGo as `interface` is to Go.\n\nIn the design philosophy of XGo, we do not recommend `DSL` (Domain Specific Language). But `SDF` (Specific Domain Friendliness) is very important. The XGo philosophy about `SDF` is:\n\n```\nDon't define a language for specific domain.\nAbstract domain knowledge for it.\n```\n\nXGo introduces `classfile` and `class framework` to abstract domain knowledge.\n\n* [What's Classfile?](doc/classfile.md#whats-classfile)\n* [Dive into XGo Classfiles](doc/classfile.md)\n\nSound a bit abstract? Let's see some XGo class frameworks.\n\n* STEM Education: [spx: A Scratch Compatible 2D Game Engine](https://github.com/goplus/spx)\n* AI Programming: [mcp: An XGo implementation of the Model Context Protocol (MCP)](https://github.com/goplus/mcp)\n* AI Programming: [mcptest: An XGo MCP Test Framework](https://github.com/goplus/mcp/tree/main/mtest)\n* Web Programming: [yap: Yet Another HTTP Web Framework](https://github.com/goplus/yap)\n* Web Programming: [yaptest: An XGo HTTP Test Framework](https://github.com/goplus/yap/tree/main/ytest)\n* Web Programming: [ydb: An XGo Database Framework](https://github.com/goplus/yap/tree/main/ydb)\n* CLI Programming: [cobra: A Commander for modern XGo CLI interactions](https://github.com/goplus/cobra)\n* CLI Programming: [gsh: An alternative to write shell scripts](https://github.com/qiniu/x/tree/main/gsh)\n* Unit Test: [test: Unit Test](doc/classfile.md#class-framework-unit-test)\n\n\n### yap: Yet Another HTTP Web Framework\n\nThis classfile has the file suffix `.yap`.\n\nCreate a file named [get.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_hello/get.yap) with the following content:\n\n```go\nhtml `<html><body>Hello, YAP!</body></html>`\n```\n\nExecute the following commands:\n\n```sh\nxgo mod init hello\nxgo get github.com/goplus/yap@latest\nxgo mod tidy\nxgo run .\n```\n\nA simplest web program is running now. At this time, if you visit http://localhost:8080, you will get:\n\n```\nHello, YAP!\n```\n\nYAP uses filenames to define routes. `get.yap`'s route is `get \"/\"` (GET homepage), and `get_p_#id.yap`'s route is `get \"/p/:id\"` (In fact, the filename can also be `get_p_:id.yap`, but it is not recommended because `:` is not allowed to exist in filenames under Windows).\n\nLet's create a file named [get_p_#id.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_hello/get_p_%23id.yap) with the following content:\n\n```coffee\njson {\n\t\"id\": ${id},\n}\n```\n\nExecute `xgo run .` and visit http://localhost:8080/p/123, you will get:\n\n```\n{\"id\": \"123\"}\n```\n\nSee [yap: Yet Another HTTP Web Framework](https://github.com/goplus/yap) for more details.\n\n\n### spx: A Scratch Compatible 2D Game Engine\n\n![Screen Shot1](https://github.com/goplus/spx/blob/v1/tutorial/01-Weather/1.jpg) ![Screen Shot2](https://github.com/goplus/spx/blob/v1/tutorial/01-Weather/2.jpg)\n\nThrough this example you can learn how to implement dialogues between multiple actors.\n\nHere are some codes in [Kai.spx](https://github.com/goplus/spx/blob/v1/tutorial/01-Weather/Kai.spx):\n\n```coffee\nonStart => {\n\tsay \"Where do you come from?\", 2\n\tbroadcast \"1\"\n}\n\nonMsg \"2\", => {\n\tsay \"What's the climate like in your country?\", 3\n\tbroadcast \"3\"\n}\n```\n\nWe call `onStart` and `onMsg` to listen events. `onStart` is called when the program is started. And `onMsg` is called when someone calls `broadcast` to broadcast a message.\n\nWhen the program starts, Kai says `Where do you come from?`, and then broadcasts the message `1`. Who will recieve this message? Let's see codes in [Jaime.spx](https://github.com/goplus/spx/blob/v1/tutorial/01-Weather/Jaime.spx):\n\n```coffee\nonMsg \"1\", => {\n\tsay \"I come from England.\", 2\n\tbroadcast \"2\"\n}\n```\n\nYes, Jaime recieves the message `1` and says `I come from England.`. Then he broadcasts the message `2`. Kai recieves it and says `What's the climate like in your country?`.\n\nThe following procedures are very similar. In this way you can implement dialogues between multiple actors.\n\nSee [spx: A Scratch Compatible 2D Game Engine](https://github.com/goplus/spx) for more details.\n\n\n### gsh: XGo DevOps Tools\n\nYes, now you can write `shell script` in XGo. It supports all shell commands.\n\nLet's create a file named [example.gsh](https://github.com/qiniu/x/blob/main/gsh/demo/hello/example.gsh) and write the following code:\n\n```coffee\nmkdir \"testgsh\"\n```\n\nDon't need a `go.mod` file, just enter `xgo run ./example.gsh` directly to run.\n\nSee [gsh: XGo DevOps Tools](https://github.com/qiniu/x/tree/main/gsh) for more details.\n\n\n## How to install\n\nNote: Requires go1.19 or later\n\n### on Windows\n\n```sh\nwinget install goplus.xgo\n```\n\n### on Debian/Ubuntu\n\n```sh\nsudo bash -c ' echo \"deb [trusted=yes] https://pkgs.xgo.dev/apt/ /\" > /etc/apt/sources.list.d/goplus.list'\nsudo apt update\nsudo apt install xgo\n```\n\n### on RedHat/CentOS/Fedora\n\n```sh\nsudo bash -c 'echo -e \"[goplus]\\nname=XGo Repo\\nbaseurl=https://pkgs.xgo.dev/yum/\\nenabled=1\\ngpgcheck=0\" > /etc/yum.repos.d/goplus.repo'\nsudo yum install xgo\n```\n\n### on macOS/Linux (Homebrew)\n\nInstall via [brew](https://brew.sh/)\n\n```sh\n$ brew install xgo\n```\n\n### from source code\n\n```bash\ngit clone https://github.com/goplus/xgo.git\ncd xgo\n\n# On mac/linux run:\n./all.bash\n# On Windows run:\nall.bat\n```\n\n## XGo Applications\n\n### Game Programming\n\n* [A Scratch Compatible 2D Game Engine](https://github.com/goplus/spx)\n* [Aircraft War](https://github.com/goplus/AircraftWar)\n* [Flappy Bird](https://github.com/goplus/FlappyCalf)\n* [Maze Play](https://github.com/goplus/MazePlay)\n* [BetaGo](https://github.com/xushiwei/BetaGo)\n* [Gobang](https://github.com/xushiwei/Gobang)\n* [Dinosaur](https://github.com/xushiwei/Dinosaur)\n\n### Web Programming\n\n* [yap: Yet Another HTTP Web Framework](https://github.com/goplus/yap)\n* [yaptest: HTTP Test Framework](https://github.com/goplus/yap/tree/main/ytest)\n* [ydb: Database Framework](https://github.com/goplus/yap#ydb-database-framework)\n\n### DevOps Tools\n\n* [gsh: XGo DevOps Tools](https://github.com/qiniu/x/tree/main/gsh)\n\n### Data Processing\n\n* [hdq: HTML DOM Query Language for XGo](https://github.com/goplus/hdq)\n\n\n## IDE Plugins\n\n* vscode: [Go/XGo for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=goplus.gop)\n\n\n## Contributing\n\nThe XGo project welcomes all contributors. We appreciate your help!\n\nFor more details, see [Contributing & compiler design](doc/contributing.md).\n\n\n## Give a Star! ⭐\n\nIf you like or are using XGo to learn or start your projects, please give it a star. Thanks!\n"
  },
  {
    "path": "all.bash",
    "content": "#! /usr/bin/env bash\n\n#\n# Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nset -ex\n\ngo run cmd/make.go --install --regtest --autoproxy\n"
  },
  {
    "path": "all.bat",
    "content": "go run cmd/make.go --install --regtest --autoproxy\n"
  },
  {
    "path": "ast/ast.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package ast declares the types used to represent syntax trees for XGo\n// packages.\npackage ast\n\nimport (\n\t\"go/ast\"\n\n\t\"github.com/goplus/xgo/token\"\n)\n\n// ----------------------------------------------------------------------------\n// Interfaces\n//\n// There are 3 main classes of nodes: Expressions and type nodes,\n// statement nodes, and declaration nodes. The node names usually\n// match the corresponding Go spec production names to which they\n// correspond. The node fields correspond to the individual parts\n// of the respective productions.\n//\n// All nodes contain position information marking the beginning of\n// the corresponding source text segment; it is accessible via the\n// Pos accessor method. Nodes may contain additional position info\n// for language constructs where comments may be found between parts\n// of the construct (typically any larger, parenthesized subpart).\n// That position information is needed to properly position comments\n// when printing the construct.\n\n// Node interface: all node types implement the Node interface.\ntype Node = ast.Node\n\n// Expr interface: all expression nodes implement the Expr interface.\ntype Expr interface {\n\tNode\n\texprNode()\n}\n\n// Stmt interface: all statement nodes implement the Stmt interface.\ntype Stmt interface {\n\tNode\n\tstmtNode()\n}\n\n// Decl interface: all declaration nodes implement the Decl interface.\ntype Decl interface {\n\tNode\n\tdeclNode()\n}\n\n// ----------------------------------------------------------------------------\n// Comments\n\n// A Comment node represents a single //-style 、#-style or /*-style comment.\ntype Comment = ast.Comment\n\n// A CommentGroup represents a sequence of comments\n// with no other tokens and no empty lines between.\ntype CommentGroup = ast.CommentGroup\n\n// ----------------------------------------------------------------------------\n// Expressions and types\n\n// A Field represents a Field declaration list in a struct type,\n// a method list in an interface type, or a parameter/result declaration\n// in a signature.\n// Field.Names is nil for unnamed parameters (parameter lists which only contain types)\n// and embedded struct fields. In the latter case, the field name is the type name.\ntype Field struct {\n\tDoc      *CommentGroup // associated documentation; or nil\n\tNames    []*Ident      // field/method/parameter names; or nil\n\tType     Expr          // field/method/parameter type\n\tTag      *BasicLit     // field tag; or nil\n\tComment  *CommentGroup // line comments; or nil\n\tOptional token.Pos     // position of \"?\", or NoPos if not optional\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (f *Field) Pos() token.Pos {\n\tif len(f.Names) > 0 {\n\t\treturn f.Names[0].Pos()\n\t}\n\treturn f.Type.Pos()\n}\n\n// End returns position of first character immediately after the node.\nfunc (f *Field) End() token.Pos {\n\tif f.Tag != nil {\n\t\treturn f.Tag.End()\n\t}\n\tif f.Optional.IsValid() {\n\t\treturn f.Optional + 1\n\t}\n\treturn f.Type.End()\n}\n\n// A FieldList represents a list of Fields, enclosed by parentheses or braces.\ntype FieldList struct {\n\tOpening token.Pos // position of opening parenthesis/brace, if any\n\tList    []*Field  // field list; or nil\n\tClosing token.Pos // position of closing parenthesis/brace, if any\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (f *FieldList) Pos() token.Pos {\n\tif f.Opening.IsValid() {\n\t\treturn f.Opening\n\t}\n\t// the list should not be empty in this case;\n\t// be conservative and guard against bad ASTs\n\tif len(f.List) > 0 {\n\t\treturn f.List[0].Pos()\n\t}\n\treturn token.NoPos\n}\n\n// End returns position of first character immediately after the node.\nfunc (f *FieldList) End() token.Pos {\n\tif f.Closing.IsValid() {\n\t\treturn f.Closing + 1\n\t}\n\t// the list should not be empty in this case;\n\t// be conservative and guard against bad ASTs\n\tif n := len(f.List); n > 0 {\n\t\treturn f.List[n-1].End()\n\t}\n\treturn token.NoPos\n}\n\n// NumFields returns the number of parameters or struct fields represented by a FieldList.\nfunc (f *FieldList) NumFields() int {\n\tn := 0\n\tif f != nil {\n\t\tfor _, g := range f.List {\n\t\t\tm := len(g.Names)\n\t\t\tif m == 0 {\n\t\t\t\tm = 1\n\t\t\t}\n\t\t\tn += m\n\t\t}\n\t}\n\treturn n\n}\n\n// An expression is represented by a tree consisting of one\n// or more of the following concrete expression nodes.\ntype (\n\t// A BadExpr node is a placeholder for expressions containing\n\t// syntax errors for which no correct expression nodes can be\n\t// created.\n\tBadExpr struct {\n\t\tFrom, To token.Pos // position range of bad expression\n\t}\n\n\t// An Ident node represents an identifier.\n\tIdent struct {\n\t\tNamePos token.Pos // identifier position\n\t\tName    string    // identifier name\n\t\tObj     *Object   // denoted object; or nil\n\t}\n\n\t// An Ellipsis node stands for the \"...\" type in a\n\t// parameter list or the \"...\" length in an array type.\n\tEllipsis struct {\n\t\tEllipsis token.Pos // position of \"...\"\n\t\tElt      Expr      // ellipsis element type (parameter lists only); or nil\n\t}\n\n\t// A FuncLit node represents a function literal.\n\tFuncLit struct {\n\t\tType *FuncType  // function type\n\t\tBody *BlockStmt // function body\n\t}\n\n\t// A CompositeLit node represents a composite literal.\n\tCompositeLit struct {\n\t\tType       Expr      // literal type; or nil\n\t\tLbrace     token.Pos // position of \"{\"\n\t\tElts       []Expr    // list of composite elements; or nil\n\t\tRbrace     token.Pos // position of \"}\"\n\t\tIncomplete bool      // true if (source) expressions are missing in the Elts list\n\t}\n\n\t// A ParenExpr node represents a parenthesized expression.\n\tParenExpr struct {\n\t\tLparen token.Pos // position of \"(\"\n\t\tX      Expr      // parenthesized expression\n\t\tRparen token.Pos // position of \")\"\n\t}\n\n\t// A SelectorExpr node represents an expression followed by a selector.\n\t// Sel may not be a simple identifier. For example:\n\t//   - x.field\n\t//   - x.\"field-name\"\n\t//   - x.$attr\n\t//   - x.$\"attr-name\"\n\t//   - x.0, x.1, ...\n\t//   - x.*\n\tSelectorExpr struct {\n\t\tX   Expr   // expression\n\t\tSel *Ident // field selector (it may not be a simple identifier)\n\t}\n\n\t// An IndexExpr node represents an expression followed by an index.\n\tIndexExpr struct {\n\t\tX      Expr      // expression\n\t\tLbrack token.Pos // position of \"[\"\n\t\tIndex  Expr      // index expression\n\t\tRbrack token.Pos // position of \"]\"\n\t}\n\n\t// An IndexListExpr node represents an expression followed by multiple\n\t// indices.\n\tIndexListExpr struct {\n\t\tX       Expr      // expression\n\t\tLbrack  token.Pos // position of \"[\"\n\t\tIndices []Expr    // index expressions\n\t\tRbrack  token.Pos // position of \"]\"\n\t}\n\n\t// A SliceExpr node represents an expression followed by slice indices.\n\tSliceExpr struct {\n\t\tX      Expr      // expression\n\t\tLbrack token.Pos // position of \"[\"\n\t\tLow    Expr      // begin of slice range; or nil\n\t\tHigh   Expr      // end of slice range; or nil\n\t\tMax    Expr      // maximum capacity of slice; or nil\n\t\tSlice3 bool      // true if 3-index slice (2 colons present)\n\t\tRbrack token.Pos // position of \"]\"\n\t}\n\n\t// A TypeAssertExpr node represents an expression followed by a\n\t// type assertion.\n\tTypeAssertExpr struct {\n\t\tX      Expr      // expression\n\t\tLparen token.Pos // position of \"(\"\n\t\tType   Expr      // asserted type; nil means type switch X.(type)\n\t\tRparen token.Pos // position of \")\"\n\t}\n\n\t// A StarExpr node represents an expression of the form \"*\" Expression.\n\t// Semantically it could be a unary \"*\" expression, or a pointer type.\n\tStarExpr struct {\n\t\tStar token.Pos // position of \"*\"\n\t\tX    Expr      // operand\n\t}\n\n\t// A UnaryExpr node represents a unary expression.\n\t// Unary \"*\" expressions are represented via StarExpr nodes.\n\tUnaryExpr struct {\n\t\tOpPos token.Pos   // position of Op\n\t\tOp    token.Token // operator\n\t\tX     Expr        // operand\n\t}\n\n\t// A BinaryExpr node represents a binary expression.\n\tBinaryExpr struct {\n\t\tX     Expr        // left operand\n\t\tOpPos token.Pos   // position of Op\n\t\tOp    token.Token // operator\n\t\tY     Expr        // right operand\n\t}\n\n\t// A KeyValueExpr node represents (key : value) pairs\n\t// in composite literals.\n\tKeyValueExpr struct {\n\t\tKey   Expr\n\t\tColon token.Pos // position of \":\"\n\t\tValue Expr\n\t}\n)\n\n// ChanDir is the direction of a channel type is indicated by a bit\n// mask including one or both of the following constants.\ntype ChanDir int\n\nconst (\n\t// SEND - ChanDir\n\tSEND ChanDir = 1 << iota\n\t// RECV - ChanDir\n\tRECV\n)\n\n// A type is represented by a tree consisting of one\n// or more of the following type-specific expression\n// nodes.\ntype (\n\t// An ArrayType node represents an array or slice type.\n\tArrayType struct {\n\t\tLbrack token.Pos // position of \"[\"\n\t\tLen    Expr      // Ellipsis node for [...]T array types, nil for slice types\n\t\tElt    Expr      // element type\n\t}\n\n\t// A StructType node represents a struct type.\n\tStructType struct {\n\t\tStruct     token.Pos  // position of \"struct\" keyword\n\t\tFields     *FieldList // list of field declarations\n\t\tIncomplete bool       // true if (source) fields are missing in the Fields list\n\t}\n\n\t// Pointer types are represented via StarExpr nodes.\n\n\t// A FuncType node represents a function type.\n\tFuncType struct {\n\t\tFunc       token.Pos  // position of \"func\" keyword (token.NoPos if there is no \"func\")\n\t\tTypeParams *FieldList // type parameters; or nil\n\t\tParams     *FieldList // (incoming) parameters; non-nil\n\t\tResults    *FieldList // (outgoing) results; or nil\n\t}\n\n\t// An InterfaceType node represents an interface type.\n\tInterfaceType struct {\n\t\tInterface  token.Pos  // position of \"interface\" keyword\n\t\tMethods    *FieldList // list of methods\n\t\tIncomplete bool       // true if (source) methods are missing in the Methods list\n\t}\n\n\t// A MapType node represents a map type.\n\tMapType struct {\n\t\tMap   token.Pos // position of \"map\" keyword\n\t\tKey   Expr\n\t\tValue Expr\n\t}\n\n\t// A ChanType node represents a channel type.\n\tChanType struct {\n\t\tBegin token.Pos // position of \"chan\" keyword or \"<-\" (whichever comes first)\n\t\tArrow token.Pos // position of \"<-\" (token.NoPos if there is no \"<-\")\n\t\tDir   ChanDir   // channel direction\n\t\tValue Expr      // value type\n\t}\n\n\t// A TupleType node represents a tuple type.\n\t// Tuple types are syntactic sugar for anonymous structs with ordinal field names.\n\t// Examples:\n\t//   ()                                     ≡ struct{}\n\t//   (T)                                    ≡ T (degenerates to the type itself)\n\t//   (T0, T1, ..., TN)                      ≡ struct{ _0 T0; _1 T1; ...; _N TN }\n\t//   (name0 T0, name1 T1, ..., nameN TN)    ≡ struct{ _0 T0; _1 T1; ...; _N TN }\n\t// Named fields are compile-time aliases only; runtime uses ordinal fields.\n\tTupleType struct {\n\t\tLparen token.Pos  // position of \"(\"\n\t\tFields *FieldList // tuple element types (and optional names)\n\t\tRparen token.Pos  // position of \")\"\n\t}\n)\n\n// Pos and End implementations for expression/type nodes.\n\n// Pos returns position of first character belonging to the node.\nfunc (x *BadExpr) Pos() token.Pos { return x.From }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *Ident) Pos() token.Pos { return x.NamePos }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *Ellipsis) Pos() token.Pos { return x.Ellipsis }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *FuncLit) Pos() token.Pos { return x.Type.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *CompositeLit) Pos() token.Pos {\n\tif x.Type != nil {\n\t\treturn x.Type.Pos()\n\t}\n\treturn x.Lbrace\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (x *ParenExpr) Pos() token.Pos { return x.Lparen }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *SelectorExpr) Pos() token.Pos { return x.X.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *IndexExpr) Pos() token.Pos { return x.X.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *IndexListExpr) Pos() token.Pos { return x.X.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *SliceExpr) Pos() token.Pos { return x.X.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *StarExpr) Pos() token.Pos { return x.Star }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *UnaryExpr) Pos() token.Pos { return x.OpPos }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *ArrayType) Pos() token.Pos { return x.Lbrack }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *StructType) Pos() token.Pos { return x.Struct }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *FuncType) Pos() token.Pos {\n\tif x.Func.IsValid() || x.Params == nil { // see issue 3870\n\t\treturn x.Func\n\t}\n\treturn x.Params.Pos() // interface method declarations have no \"func\" keyword\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (x *InterfaceType) Pos() token.Pos { return x.Interface }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *MapType) Pos() token.Pos { return x.Map }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *ChanType) Pos() token.Pos { return x.Begin }\n\n// Pos returns position of first character belonging to the node.\nfunc (x *TupleType) Pos() token.Pos { return x.Lparen }\n\n// End returns position of first character immediately after the node.\nfunc (x *BadExpr) End() token.Pos { return x.To }\n\n// End returns position of first character immediately after the node.\nfunc (x *Ident) End() token.Pos {\n\tif x.Implicit() { // implicitly declared\n\t\treturn x.NamePos\n\t}\n\treturn x.NamePos + token.Pos(len(x.Name))\n}\n\n// Implicit reports whether the identifier was implicitly declared\nfunc (x *Ident) Implicit() bool {\n\to := x.Obj\n\treturn o != nil && o.Kind >= implicitBase\n}\n\n// End returns position of first character immediately after the node.\nfunc (x *Ellipsis) End() token.Pos {\n\tif x.Elt != nil {\n\t\treturn x.Elt.End()\n\t}\n\treturn x.Ellipsis + 3 // len(\"...\")\n}\n\n// End returns position of first character immediately after the node.\nfunc (x *FuncLit) End() token.Pos { return x.Body.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 }\n\n// End returns position of first character immediately after the node.\nfunc (x *ParenExpr) End() token.Pos { return x.Rparen + 1 }\n\n// End returns position of first character immediately after the node.\nfunc (x *SelectorExpr) End() token.Pos { return x.Sel.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }\n\n// End returns position of first character immediately after the node.\nfunc (x *IndexListExpr) End() token.Pos { return x.Rbrack + 1 }\n\n// End returns position of first character immediately after the node.\nfunc (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }\n\n// End returns position of first character immediately after the node.\nfunc (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 }\n\n// End returns position of first character immediately after the node.\nfunc (x *StarExpr) End() token.Pos { return x.X.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *UnaryExpr) End() token.Pos { return x.X.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *BinaryExpr) End() token.Pos { return x.Y.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *KeyValueExpr) End() token.Pos { return x.Value.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *ArrayType) End() token.Pos { return x.Elt.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *StructType) End() token.Pos { return x.Fields.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *FuncType) End() token.Pos {\n\tif x.Results != nil {\n\t\treturn x.Results.End()\n\t}\n\treturn x.Params.End()\n}\n\n// End returns position of first character immediately after the node.\nfunc (x *InterfaceType) End() token.Pos { return x.Methods.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *MapType) End() token.Pos { return x.Value.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *ChanType) End() token.Pos { return x.Value.End() }\n\n// End returns position of first character immediately after the node.\nfunc (x *TupleType) End() token.Pos { return x.Rparen + 1 }\n\n// exprNode() ensures that only expression/type nodes can be\n// assigned to an Expr.\nfunc (*BadExpr) exprNode()        {}\nfunc (*Ident) exprNode()          {}\nfunc (*Ellipsis) exprNode()       {}\nfunc (*FuncLit) exprNode()        {}\nfunc (*CompositeLit) exprNode()   {}\nfunc (*ParenExpr) exprNode()      {}\nfunc (*SelectorExpr) exprNode()   {}\nfunc (*IndexExpr) exprNode()      {}\nfunc (*IndexListExpr) exprNode()  {}\nfunc (*SliceExpr) exprNode()      {}\nfunc (*TypeAssertExpr) exprNode() {}\nfunc (*StarExpr) exprNode()       {}\nfunc (*UnaryExpr) exprNode()      {}\nfunc (*BinaryExpr) exprNode()     {}\nfunc (*KeyValueExpr) exprNode()   {}\n\nfunc (*ArrayType) exprNode()     {}\nfunc (*StructType) exprNode()    {}\nfunc (*FuncType) exprNode()      {}\nfunc (*InterfaceType) exprNode() {}\nfunc (*MapType) exprNode()       {}\nfunc (*ChanType) exprNode()      {}\nfunc (*TupleType) exprNode()     {}\n\n// ----------------------------------------------------------------------------\n// Convenience functions for Idents\n\n// NewIdent creates a new Ident without position.\n// Useful for ASTs generated by code other than the XGo parser.\nfunc NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }\n\n// NewIdentEx creates a new Ident with position and with the given kind.\nfunc NewIdentEx(pos token.Pos, name string, kind ObjKind) *Ident {\n\treturn &Ident{pos, name, NewObj(kind, name)}\n}\n\n// IsExported reports whether name starts with an upper-case letter.\nfunc IsExported(name string) bool { return token.IsExported(name) }\n\n// IsExported reports whether id starts with an upper-case letter.\nfunc (x *Ident) IsExported() bool { return token.IsExported(x.Name) }\n\nfunc (x *Ident) String() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"<nil>\"\n}\n\n// ----------------------------------------------------------------------------\n// Statements\n\n// A statement is represented by a tree consisting of one\n// or more of the following concrete statement nodes.\ntype (\n\t// A BadStmt node is a placeholder for statements containing\n\t// syntax errors for which no correct statement nodes can be\n\t// created.\n\t//\n\tBadStmt struct {\n\t\tFrom, To token.Pos // position range of bad statement\n\t}\n\n\t// A DeclStmt node represents a declaration in a statement list.\n\tDeclStmt struct {\n\t\tDecl Decl // *GenDecl with CONST, TYPE, or VAR token\n\t}\n\n\t// An EmptyStmt node represents an empty statement.\n\t// The \"position\" of the empty statement is the position\n\t// of the immediately following (explicit or implicit) semicolon.\n\t//\n\tEmptyStmt struct {\n\t\tSemicolon token.Pos // position of following \";\"\n\t\tImplicit  bool      // if set, \";\" was omitted in the source\n\t}\n\n\t// A LabeledStmt node represents a labeled statement.\n\tLabeledStmt struct {\n\t\tLabel *Ident\n\t\tColon token.Pos // position of \":\"\n\t\tStmt  Stmt\n\t}\n\n\t// An ExprStmt node represents a (stand-alone) expression\n\t// in a statement list.\n\t//\n\tExprStmt struct {\n\t\tX Expr // expression\n\t}\n\n\t// An IncDecStmt node represents an increment or decrement statement.\n\tIncDecStmt struct {\n\t\tX      Expr\n\t\tTokPos token.Pos   // position of Tok\n\t\tTok    token.Token // INC or DEC\n\t}\n\n\t// An AssignStmt node represents an assignment or\n\t// a short variable declaration.\n\t//\n\tAssignStmt struct {\n\t\tLhs    []Expr      // left hand side expressions\n\t\tTokPos token.Pos   // position of Tok\n\t\tTok    token.Token // assignment token, DEFINE\n\t\tRhs    []Expr      // right hand side expressions\n\t}\n\n\t// A GoStmt node represents a go statement.\n\tGoStmt struct {\n\t\tGo   token.Pos // position of \"go\" keyword\n\t\tCall *CallExpr\n\t}\n\n\t// A DeferStmt node represents a defer statement.\n\tDeferStmt struct {\n\t\tDefer token.Pos // position of \"defer\" keyword\n\t\tCall  *CallExpr\n\t}\n\n\t// A ReturnStmt node represents a return statement.\n\tReturnStmt struct {\n\t\tReturn  token.Pos // position of \"return\" keyword\n\t\tResults []Expr    // result expressions; or nil\n\t}\n\n\t// A BranchStmt node represents a break, continue, goto,\n\t// or fallthrough statement.\n\t//\n\tBranchStmt struct {\n\t\tTokPos token.Pos   // position of Tok\n\t\tTok    token.Token // keyword token (BREAK, CONTINUE, GOTO, FALLTHROUGH)\n\t\tLabel  *Ident      // label name; or nil\n\t}\n\n\t// A BlockStmt node represents a braced statement list.\n\tBlockStmt struct {\n\t\tLbrace token.Pos // position of \"{\"\n\t\tList   []Stmt\n\t\tRbrace token.Pos // position of \"}\", if any (may be absent due to syntax error)\n\t}\n\n\t// An IfStmt node represents an if statement.\n\tIfStmt struct {\n\t\tIf   token.Pos // position of \"if\" keyword\n\t\tInit Stmt      // initialization statement; or nil\n\t\tCond Expr      // condition\n\t\tBody *BlockStmt\n\t\tElse Stmt // else branch; or nil\n\t}\n\n\t// A CaseClause represents a case of an expression or type switch statement.\n\tCaseClause struct {\n\t\tCase  token.Pos // position of \"case\" or \"default\" keyword\n\t\tList  []Expr    // list of expressions or types; nil means default case\n\t\tColon token.Pos // position of \":\"\n\t\tBody  []Stmt    // statement list; or nil\n\t}\n\n\t// A SwitchStmt node represents an expression switch statement.\n\tSwitchStmt struct {\n\t\tSwitch token.Pos  // position of \"switch\" keyword\n\t\tInit   Stmt       // initialization statement; or nil\n\t\tTag    Expr       // tag expression; or nil\n\t\tBody   *BlockStmt // CaseClauses only\n\t}\n\n\t// A TypeSwitchStmt node represents a type switch statement.\n\tTypeSwitchStmt struct {\n\t\tSwitch token.Pos  // position of \"switch\" keyword\n\t\tInit   Stmt       // initialization statement; or nil\n\t\tAssign Stmt       // x := y.(type) or y.(type)\n\t\tBody   *BlockStmt // CaseClauses only\n\t}\n\n\t// A CommClause node represents a case of a select statement.\n\tCommClause struct {\n\t\tCase  token.Pos // position of \"case\" or \"default\" keyword\n\t\tComm  Stmt      // send or receive statement; nil means default case\n\t\tColon token.Pos // position of \":\"\n\t\tBody  []Stmt    // statement list; or nil\n\t}\n\n\t// A SelectStmt node represents a select statement.\n\tSelectStmt struct {\n\t\tSelect token.Pos  // position of \"select\" keyword\n\t\tBody   *BlockStmt // CommClauses only\n\t}\n\n\t// A ForStmt represents a `for init; cond; post { ... }` statement.\n\tForStmt struct {\n\t\tFor  token.Pos // position of \"for\" keyword\n\t\tInit Stmt      // initialization statement; or nil\n\t\tCond Expr      // condition; or nil\n\t\tPost Stmt      // post iteration statement; or nil\n\t\tBody *BlockStmt\n\t}\n\n\t// A RangeStmt represents a for statement with a range clause.\n\tRangeStmt struct {\n\t\tFor        token.Pos   // position of \"for\" keyword\n\t\tKey, Value Expr        // Key, Value may be nil\n\t\tTokPos     token.Pos   // position of Tok; invalid if Key == nil\n\t\tTok        token.Token // ILLEGAL if Key == nil, ASSIGN, DEFINE\n\t\tX          Expr        // value to range over\n\t\tBody       *BlockStmt\n\t\tNoRangeOp  bool\n\t}\n)\n\n// Pos and End implementations for statement nodes.\n\n// Pos returns position of first character belonging to the node.\nfunc (s *BadStmt) Pos() token.Pos { return s.From }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *DeclStmt) Pos() token.Pos { return s.Decl.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *EmptyStmt) Pos() token.Pos { return s.Semicolon }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *LabeledStmt) Pos() token.Pos { return s.Label.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *ExprStmt) Pos() token.Pos { return s.X.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *SendStmt) Pos() token.Pos { return s.Chan.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *IncDecStmt) Pos() token.Pos { return s.X.Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *AssignStmt) Pos() token.Pos { return s.Lhs[0].Pos() }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *GoStmt) Pos() token.Pos { return s.Go }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *DeferStmt) Pos() token.Pos { return s.Defer }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *ReturnStmt) Pos() token.Pos { return s.Return }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *BranchStmt) Pos() token.Pos { return s.TokPos }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *BlockStmt) Pos() token.Pos { return s.Lbrace }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *IfStmt) Pos() token.Pos { return s.If }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *CaseClause) Pos() token.Pos { return s.Case }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *SwitchStmt) Pos() token.Pos { return s.Switch }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *TypeSwitchStmt) Pos() token.Pos { return s.Switch }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *CommClause) Pos() token.Pos { return s.Case }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *SelectStmt) Pos() token.Pos { return s.Select }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *ForStmt) Pos() token.Pos { return s.For }\n\n// Pos returns position of first character belonging to the node.\nfunc (s *RangeStmt) Pos() token.Pos { return s.For }\n\n// End returns position of first character immediately after the node.\nfunc (s *BadStmt) End() token.Pos { return s.To }\n\n// End returns position of first character immediately after the node.\nfunc (s *DeclStmt) End() token.Pos { return s.Decl.End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *EmptyStmt) End() token.Pos {\n\tif s.Implicit {\n\t\treturn s.Semicolon\n\t}\n\treturn s.Semicolon + 1 /* len(\";\") */\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *LabeledStmt) End() token.Pos { return s.Stmt.End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *ExprStmt) End() token.Pos { return s.X.End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *IncDecStmt) End() token.Pos {\n\treturn s.TokPos + 2 /* len(\"++\") */\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *AssignStmt) End() token.Pos { return s.Rhs[len(s.Rhs)-1].End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *GoStmt) End() token.Pos { return s.Call.End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *DeferStmt) End() token.Pos { return s.Call.End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *ReturnStmt) End() token.Pos {\n\tif n := len(s.Results); n > 0 {\n\t\treturn s.Results[n-1].End()\n\t}\n\treturn s.Return + 6 // len(\"return\")\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *BranchStmt) End() token.Pos {\n\tif s.Label != nil {\n\t\treturn s.Label.End()\n\t}\n\treturn token.Pos(int(s.TokPos) + len(s.Tok.String()))\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *BlockStmt) End() token.Pos {\n\tif s.Rbrace.IsValid() {\n\t\treturn s.Rbrace + 1\n\t}\n\tif n := len(s.List); n > 0 {\n\t\treturn s.List[n-1].End()\n\t}\n\treturn s.Lbrace + 1\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *IfStmt) End() token.Pos {\n\tif s.Else != nil {\n\t\treturn s.Else.End()\n\t}\n\treturn s.Body.End()\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *CaseClause) End() token.Pos {\n\tif n := len(s.Body); n > 0 {\n\t\treturn s.Body[n-1].End()\n\t}\n\treturn s.Colon + 1\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *SwitchStmt) End() token.Pos { return s.Body.End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *TypeSwitchStmt) End() token.Pos { return s.Body.End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *CommClause) End() token.Pos {\n\tif n := len(s.Body); n > 0 {\n\t\treturn s.Body[n-1].End()\n\t}\n\treturn s.Colon + 1\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *SelectStmt) End() token.Pos { return s.Body.End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *ForStmt) End() token.Pos { return s.Body.End() }\n\n// End returns position of first character immediately after the node.\nfunc (s *RangeStmt) End() token.Pos { return s.Body.End() }\n\n// stmtNode() ensures that only statement nodes can be\n// assigned to a Stmt.\nfunc (*BadStmt) stmtNode()        {}\nfunc (*DeclStmt) stmtNode()       {}\nfunc (*EmptyStmt) stmtNode()      {}\nfunc (*LabeledStmt) stmtNode()    {}\nfunc (*ExprStmt) stmtNode()       {}\nfunc (*SendStmt) stmtNode()       {}\nfunc (*IncDecStmt) stmtNode()     {}\nfunc (*AssignStmt) stmtNode()     {}\nfunc (*GoStmt) stmtNode()         {}\nfunc (*DeferStmt) stmtNode()      {}\nfunc (*ReturnStmt) stmtNode()     {}\nfunc (*BranchStmt) stmtNode()     {}\nfunc (*BlockStmt) stmtNode()      {}\nfunc (*IfStmt) stmtNode()         {}\nfunc (*CaseClause) stmtNode()     {}\nfunc (*SwitchStmt) stmtNode()     {}\nfunc (*TypeSwitchStmt) stmtNode() {}\nfunc (*CommClause) stmtNode()     {}\nfunc (*SelectStmt) stmtNode()     {}\nfunc (*ForStmt) stmtNode()        {}\nfunc (*RangeStmt) stmtNode()      {}\n\n// ----------------------------------------------------------------------------\n// Declarations\n\n// A Spec node represents a single (non-parenthesized) import,\n// constant, type, or variable declaration.\ntype (\n\t// The Spec type stands for any of *ImportSpec, *ValueSpec, and *TypeSpec.\n\tSpec interface {\n\t\tNode\n\t\tspecNode()\n\t}\n\n\t// An ImportSpec node represents a single package import.\n\tImportSpec struct {\n\t\tDoc     *CommentGroup // associated documentation; or nil\n\t\tName    *Ident        // local package name (including \".\"); or nil\n\t\tPath    *BasicLit     // import path\n\t\tComment *CommentGroup // line comments; or nil\n\t\tEndPos  token.Pos     // end of spec (overrides Path.Pos if nonzero)\n\t}\n\n\t// A ValueSpec node represents a constant or variable declaration\n\t// (ConstSpec or VarSpec production).\n\t//\n\tValueSpec struct {\n\t\tDoc     *CommentGroup // associated documentation; or nil\n\t\tNames   []*Ident      // value names (len(Names) > 0)\n\t\tType    Expr          // value type; or nil\n\t\tTag     *BasicLit     // classfile field tag; or nil\n\t\tValues  []Expr        // initial values; or nil\n\t\tComment *CommentGroup // line comments; or nil\n\t}\n\n\t// A TypeSpec node represents a type declaration (TypeSpec production).\n\tTypeSpec struct {\n\t\tDoc        *CommentGroup // associated documentation; or nil\n\t\tName       *Ident        // type name\n\t\tTypeParams *FieldList    // type parameters; or nil\n\t\tAssign     token.Pos     // position of '=', if any\n\t\tType       Expr          // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes\n\t\tComment    *CommentGroup // line comments; or nil\n\t}\n)\n\n// Pos and End implementations for spec nodes.\n\n// Pos returns position of first character belonging to the node.\nfunc (s *ImportSpec) Pos() token.Pos {\n\tif s.Name != nil {\n\t\treturn s.Name.Pos()\n\t}\n\treturn s.Path.Pos()\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (s *ValueSpec) Pos() token.Pos {\n\tif len(s.Names) == 0 {\n\t\treturn s.Type.Pos()\n\t}\n\treturn s.Names[0].Pos()\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (s *TypeSpec) Pos() token.Pos { return s.Name.Pos() }\n\n// End returns position of first character immediately after the node.\nfunc (s *ImportSpec) End() token.Pos {\n\tif s.EndPos != 0 {\n\t\treturn s.EndPos\n\t}\n\treturn s.Path.End()\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *ValueSpec) End() token.Pos {\n\tif n := len(s.Values); n > 0 {\n\t\treturn s.Values[n-1].End()\n\t}\n\tif s.Type != nil {\n\t\treturn s.Type.End()\n\t}\n\treturn s.Names[len(s.Names)-1].End()\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *TypeSpec) End() token.Pos { return s.Type.End() }\n\n// specNode() ensures that only spec nodes can be\n// assigned to a Spec.\nfunc (*ImportSpec) specNode() {}\nfunc (*ValueSpec) specNode()  {}\nfunc (*TypeSpec) specNode()   {}\n\n// A declaration is represented by one of the following declaration nodes.\ntype (\n\t// A BadDecl node is a placeholder for declarations containing\n\t// syntax errors for which no correct declaration nodes can be\n\t// created.\n\t//\n\tBadDecl struct {\n\t\tFrom, To token.Pos // position range of bad declaration\n\t}\n\n\t// A GenDecl node (generic declaration node) represents an import,\n\t// constant, type or variable declaration. A valid Lparen position\n\t// (Lparen.IsValid()) indicates a parenthesized declaration.\n\t//\n\t// Relationship between Tok value and Specs element type:\n\t//\n\t//\ttoken.IMPORT  *ImportSpec\n\t//\ttoken.CONST   *ValueSpec\n\t//\ttoken.TYPE    *TypeSpec\n\t//\ttoken.VAR     *ValueSpec\n\t//\n\tGenDecl struct {\n\t\tDoc    *CommentGroup // associated documentation; or nil\n\t\tTokPos token.Pos     // position of Tok\n\t\tTok    token.Token   // IMPORT, CONST, TYPE, VAR\n\t\tLparen token.Pos     // position of '(', if any\n\t\tSpecs  []Spec\n\t\tRparen token.Pos // position of ')', if any\n\t}\n\n\t// A FuncDecl node represents a function declaration.\n\tFuncDecl struct {\n\t\tDoc      *CommentGroup // associated documentation; or nil\n\t\tRecv     *FieldList    // receiver (methods); or nil (functions)\n\t\tName     *Ident        // function/method name\n\t\tType     *FuncType     // function signature: parameters, results, and position of \"func\" keyword\n\t\tBody     *BlockStmt    // function body; or nil for external (non-Go) function\n\t\tOperator bool          // is operator or not\n\t\tShadow   bool          // is a shadow entry\n\t\tIsClass  bool          // recv set by class\n\t\tStatic   bool          // recv is static (class method)\n\t}\n)\n\n// Pos and End implementations for declaration nodes.\n\n// Pos returns position of first character belonging to the node.\nfunc (d *BadDecl) Pos() token.Pos { return d.From }\n\n// Pos returns position of first character belonging to the node.\nfunc (d *GenDecl) Pos() token.Pos { return d.TokPos }\n\n// Pos returns position of first character belonging to the node.\nfunc (d *FuncDecl) Pos() token.Pos { return d.Type.Pos() }\n\n// End returns position of first character immediately after the node.\nfunc (d *BadDecl) End() token.Pos { return d.To }\n\n// End returns position of first character immediately after the node.\nfunc (d *GenDecl) End() token.Pos {\n\tif d.Rparen.IsValid() {\n\t\treturn d.Rparen + 1\n\t}\n\treturn d.Specs[0].End()\n}\n\n// End returns position of first character immediately after the node.\nfunc (d *FuncDecl) End() token.Pos {\n\tif d.Body != nil {\n\t\treturn d.Body.End()\n\t}\n\treturn d.Type.End()\n}\n\n// declNode() ensures that only declaration nodes can be\n// assigned to a Decl.\nfunc (*BadDecl) declNode()  {}\nfunc (*GenDecl) declNode()  {}\nfunc (*FuncDecl) declNode() {}\n\n// ----------------------------------------------------------------------------\n\n// A Package node represents a set of source files\n// collectively building an XGo package.\ntype Package struct {\n\tName    string               // package name\n\tImports map[string]*Object   // map of package id -> package object\n\tFiles   map[string]*File     // XGo source files by filename\n\tGoFiles map[string]*ast.File // Go source files by filename\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (p *Package) Pos() token.Pos { return token.NoPos }\n\n// End returns position of first character immediately after the node.\nfunc (p *Package) End() token.Pos { return token.NoPos }\n\n// ----------------------------------------------------------------------------\n"
  },
  {
    "path": "ast/ast_xgo.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ast\n\nimport (\n\t\"github.com/goplus/xgo/token\"\n)\n\n// -----------------------------------------------------------------------------\n\n// OverloadFuncDecl node represents an overload function declaration:\n//\n// `func name = (overloadFuncs)`\n// `func (T).nameOrOp = (overloadFuncs)`\n//\n// here overloadFunc represents\n//\n// `func(params) results {...}`\n// `funcName`\n// `(*T).methodName`\ntype OverloadFuncDecl struct {\n\tDoc      *CommentGroup // associated documentation; or nil\n\tFunc     token.Pos     // position of \"func\" keyword\n\tRecv     *FieldList    // receiver (methods); or nil (functions)\n\tName     *Ident        // function/method name\n\tAssign   token.Pos     // position of token \"=\"\n\tLparen   token.Pos     // position of \"(\"\n\tFuncs    []Expr        // overload functions. here `Expr` can be *FuncLit, *Ident or *SelectorExpr\n\tRparen   token.Pos     // position of \")\"\n\tOperator bool          // is operator or not\n\tIsClass  bool          // recv set by class\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *OverloadFuncDecl) Pos() token.Pos {\n\treturn p.Func\n}\n\n// End - position of first character immediately after the node.\nfunc (p *OverloadFuncDecl) End() token.Pos {\n\treturn p.Rparen + 1\n}\n\nfunc (*OverloadFuncDecl) declNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A CallExpr node represents an expression followed by an argument list.\n// The argument list may include positional arguments (Args) and/or\n// keyword arguments (Kwargs).\ntype CallExpr struct {\n\tFun        Expr         // function expression\n\tLparen     token.Pos    // position of \"(\"\n\tArgs       []Expr       // positional arguments; or nil\n\tEllipsis   token.Pos    // position of \"...\" (token.NoPos if there is no \"...\")\n\tKwargs     []*KwargExpr // keyword arguments; or nil\n\tRparen     token.Pos    // position of \")\"\n\tNoParenEnd token.Pos\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() }\n\n// End returns position of first character immediately after the node.\nfunc (x *CallExpr) End() token.Pos {\n\tif x.NoParenEnd != token.NoPos {\n\t\treturn x.NoParenEnd\n\t}\n\treturn x.Rparen + 1\n}\n\nfunc (*CallExpr) exprNode() {}\n\n// IsCommand returns if a CallExpr is a command style CallExpr or not.\nfunc (x *CallExpr) IsCommand() bool {\n\treturn x.NoParenEnd != token.NoPos\n}\n\n// A KwargExpr node represents a keyword argument expression.\ntype KwargExpr struct {\n\tName  *Ident // argument name\n\tValue Expr   // argument value\n}\n\nfunc (p *KwargExpr) Pos() token.Pos {\n\treturn p.Name.Pos()\n}\n\nfunc (p *KwargExpr) End() token.Pos {\n\treturn p.Value.End()\n}\n\nfunc (p *KwargExpr) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A DomainTextLit node represents a domain-specific text literal.\n// https://github.com/goplus/xgo/issues/2143\n//\n//\tdomainTag`...`\n//\tdomainTag`> arg1, arg2, ...\n//\t  ...\n//\t`\ntype DomainTextLit struct {\n\tDomain   *Ident    // domain name\n\tValuePos token.Pos // literal position\n\tValue    string    // literal string; e.g. `\\m\\n\\o`\n\tExtra    any       // *DomainTextLitEx or *xgo/tpl/ast.File, optional\n}\n\n// DomainTextLitEx represents extra information for domain text literal.\ntype DomainTextLitEx struct {\n\tArgs   []Expr    // domain text arguments; or nil\n\tRawPos token.Pos // position of the first character of the raw string\n\tRaw    string    // raw string without backquote\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (x *DomainTextLit) Pos() token.Pos { return x.Domain.NamePos }\n\n// End returns position of first character immediately after the node.\nfunc (x *DomainTextLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) }\n\nfunc (*DomainTextLit) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A BasicLit node represents a literal of basic type.\ntype BasicLit struct {\n\tValuePos token.Pos    // literal position\n\tKind     token.Token  // token.INT, token.FLOAT, token.IMAG, token.RAT, token.CHAR, token.STRING, token.CSTRING\n\tValue    string       // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 3r, 'a', '\\x7f', \"foo\" or `\\m\\n\\o`\n\tExtra    *StringLitEx // optional (only available when Kind == token.STRING)\n}\n\ntype StringLitEx struct {\n\tParts []any // can be (val string) or (xval Expr)\n}\n\n// NextPartPos - position of first character of next part.\n// pos - position of this part (not including quote character).\nfunc NextPartPos(pos token.Pos, part any) (nextPos token.Pos) {\n\tswitch v := part.(type) {\n\tcase string: // normal string literal or end with \"$$\"\n\t\treturn pos + token.Pos(len(v))\n\tcase Expr:\n\t\treturn v.End()\n\t}\n\tpanic(\"NextPartPos: unexpected parameters\")\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (x *BasicLit) Pos() token.Pos { return x.ValuePos }\n\n// End returns position of first character immediately after the node.\nfunc (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) }\n\nfunc (*BasicLit) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A NumberUnitLit node represents a number with unit.\ntype NumberUnitLit struct {\n\tValuePos token.Pos   // literal position\n\tKind     token.Token // token.INT or token.FLOAT\n\tValue    string      // literal string of the number; e.g. 42, 0x7f, 3.14, 1e-9\n\tUnit     string      // unit string of the number; e.g. \"px\", \"em\", \"rem\"\n}\n\nfunc (*NumberUnitLit) exprNode() {}\n\nfunc (x *NumberUnitLit) Pos() token.Pos {\n\treturn x.ValuePos\n}\n\nfunc (x *NumberUnitLit) End() token.Pos {\n\treturn token.Pos(int(x.ValuePos) + len(x.Value) + len(x.Unit))\n}\n\n// -----------------------------------------------------------------------------\n\n// A EnvExpr node represents a ${name} expression.\ntype EnvExpr struct {\n\tTokPos token.Pos // position of \"$\"\n\tLbrace token.Pos // position of \"{\"\n\tName   *Ident    // name\n\tRbrace token.Pos // position of \"}\"\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *EnvExpr) Pos() token.Pos {\n\treturn p.TokPos\n}\n\n// End - position of first character immediately after the node.\nfunc (p *EnvExpr) End() token.Pos {\n\tif p.Rbrace != token.NoPos {\n\t\treturn p.Rbrace\n\t}\n\treturn p.Name.End()\n}\n\n// HasBrace checks is this EnvExpr ${name} or $name.\nfunc (p *EnvExpr) HasBrace() bool {\n\treturn p.Rbrace != token.NoPos\n}\n\nfunc (*EnvExpr) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// AnySelectorExpr represents `X.**.Sel` expression, which selects any field\n// named Sel in the nested object X.\n// Sel may not be a simple identifier. For example:\n//   - x.**.field\n//   - x.**.\"field-name\"\n//   - x.**.*\ntype AnySelectorExpr struct {\n\tX      Expr      // expression\n\tTokPos token.Pos // position of \"**\"\n\tSel    *Ident    // field selector (it may not be a simple identifier)\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *AnySelectorExpr) Pos() token.Pos {\n\treturn p.X.Pos()\n}\n\n// End - position of first character immediately after the node.\nfunc (p *AnySelectorExpr) End() token.Pos {\n\treturn p.Sel.End()\n}\n\nfunc (*AnySelectorExpr) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A CondExpr node represents a conditional expression: `expr @ cond`.\n//   - ns@(condExpr)\n//   - ns@fn(args)\n//   - ns@\"elem-name\" (Cond will be a *Ident with name \"elem-name\", not a *BasicLit)\n//   - ns@name\ntype CondExpr struct {\n\tX     Expr      // expression\n\tOpPos token.Pos // position of \"@\"\n\tCond  Expr      // condition expression (can be *CallExpr, *ParenExpr or *Ident)\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *CondExpr) Pos() token.Pos {\n\treturn p.X.Pos()\n}\n\n// End - position of first character immediately after the node.\nfunc (p *CondExpr) End() token.Pos {\n\treturn p.Cond.End()\n}\n\nfunc (*CondExpr) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A SliceLit node represents a slice literal.\ntype SliceLit struct {\n\tLbrack     token.Pos // position of \"[\"\n\tElts       []Expr    // list of slice elements; or nil\n\tRbrack     token.Pos // position of \"]\"\n\tIncomplete bool      // true if (source) expressions are missing in the Elts list\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *SliceLit) Pos() token.Pos {\n\treturn p.Lbrack\n}\n\n// End - position of first character immediately after the node.\nfunc (p *SliceLit) End() token.Pos {\n\treturn p.Rbrack + 1\n}\n\nfunc (*SliceLit) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A TupleLit node represents a tuple literal.\ntype TupleLit struct {\n\tLparen   token.Pos // position of \"(\"\n\tElts     []Expr    // list of tuple elements; or nil\n\tEllipsis token.Pos // position of \"...\" (token.NoPos if there is no \"...\")\n\tRparen   token.Pos // position of \")\"\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *TupleLit) Pos() token.Pos {\n\treturn p.Lparen\n}\n\n// End - position of first character immediately after the node.\nfunc (p *TupleLit) End() token.Pos {\n\treturn p.Rparen + 1\n}\n\nfunc (*TupleLit) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A MatrixLit node represents a matrix literal.\ntype MatrixLit struct {\n\tLbrack     token.Pos // position of \"[\"\n\tElts       [][]Expr  // list of matrix elements\n\tRbrack     token.Pos // position of \"]\"\n\tIncomplete bool      // true if (source) expressions are missing in the Elts list\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *MatrixLit) Pos() token.Pos {\n\treturn p.Lbrack\n}\n\n// End - position of first character immediately after the node.\nfunc (p *MatrixLit) End() token.Pos {\n\treturn p.Rbrack + 1\n}\n\nfunc (*MatrixLit) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A ElemEllipsis node represents a matrix row elements.\ntype ElemEllipsis struct {\n\tElt      Expr      // ellipsis element\n\tEllipsis token.Pos // position of \"...\"\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *ElemEllipsis) Pos() token.Pos {\n\treturn p.Elt.Pos()\n}\n\n// End - position of first character immediately after the node.\nfunc (p *ElemEllipsis) End() token.Pos {\n\treturn p.Ellipsis + 3\n}\n\nfunc (*ElemEllipsis) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// ErrWrapExpr represents `expr!`, `expr?` or `expr?:defaultValue`.\ntype ErrWrapExpr struct {\n\tX       Expr\n\tTok     token.Token // ! or ?\n\tTokPos  token.Pos\n\tDefault Expr // can be nil\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *ErrWrapExpr) Pos() token.Pos {\n\treturn p.X.Pos()\n}\n\n// End - position of first character immediately after the node.\nfunc (p *ErrWrapExpr) End() token.Pos {\n\tif p.Default != nil {\n\t\treturn p.Default.End()\n\t}\n\treturn p.TokPos + 1\n}\n\nfunc (*ErrWrapExpr) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// LambdaExpr represents one of the following expressions:\n//\n//\t`(x, y, ...) => exprOrExprTuple`\n//\t`x => exprOrExprTuple`\n//\t`=> exprOrExprTuple`\n//\n// here exprOrExprTuple represents\n//\n//\t`expr`\n//\t`(expr1, expr2, ...)`\ntype LambdaExpr struct {\n\tFirst       token.Pos\n\tLhs         []*Ident\n\tRarrow      token.Pos\n\tRhs         []Expr\n\tLast        token.Pos\n\tLhsHasParen bool\n\tRhsHasParen bool\n}\n\n// LambdaExpr2 represents one of the following expressions:\n//\n//\t`(x, y, ...) => { ... }`\n//\t`x => { ... }`\n//\t`=> { ... }`\ntype LambdaExpr2 struct {\n\tFirst       token.Pos\n\tLhs         []*Ident\n\tRarrow      token.Pos\n\tBody        *BlockStmt\n\tLhsHasParen bool\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *LambdaExpr) Pos() token.Pos {\n\treturn p.First\n}\n\n// End - position of first character immediately after the node.\nfunc (p *LambdaExpr) End() token.Pos {\n\treturn p.Last\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *LambdaExpr2) Pos() token.Pos {\n\treturn p.First\n}\n\n// End - position of first character immediately after the node.\nfunc (p *LambdaExpr2) End() token.Pos {\n\tif p.Body == nil {\n\t\treturn p.Rarrow + 2\n\t}\n\treturn p.Body.End()\n}\n\nfunc (*LambdaExpr) exprNode()  {}\nfunc (*LambdaExpr2) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A RangeExpr node represents a range expression.\ntype RangeExpr struct {\n\tFirst  Expr      // start of composite elements; or nil\n\tTo     token.Pos // position of \":\"\n\tLast   Expr      // end of composite elements\n\tColon2 token.Pos // position of \":\" or token.NoPos\n\tExpr3  Expr      // step (or max) of composite elements; or nil\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *RangeExpr) Pos() token.Pos {\n\tif p.First != nil {\n\t\treturn p.First.Pos()\n\t}\n\treturn p.To\n}\n\n// End - position of first character immediately after the node.\nfunc (p *RangeExpr) End() token.Pos {\n\tif p.Expr3 != nil {\n\t\treturn p.Expr3.End()\n\t}\n\tif p.Colon2 != token.NoPos {\n\t\treturn p.Colon2 + 1\n\t}\n\tif p.Last != nil {\n\t\treturn p.Last.End()\n\t}\n\treturn p.To + 1\n}\n\nfunc (*RangeExpr) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// ForPhrase represents `for k, v in container if init; cond` phrase.\ntype ForPhrase struct {\n\tFor        token.Pos // position of \"for\" keyword\n\tKey, Value *Ident    // Key may be nil\n\tTokPos     token.Pos // position of \"in\" operator\n\tX          Expr      // value to range over\n\tIfPos      token.Pos // position of if or comma; or NoPos\n\tInit       Stmt      // initialization statement; or nil\n\tCond       Expr      // value filter, can be nil\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (p *ForPhrase) Pos() token.Pos { return p.For }\n\n// End returns position of first character immediately after the node.\nfunc (p *ForPhrase) End() token.Pos {\n\tif p.Cond != nil {\n\t\treturn p.Cond.End()\n\t}\n\treturn p.X.End()\n}\n\nfunc (p *ForPhrase) exprNode() {}\n\n// ComprehensionExpr represents one of the following expressions:\n//\n//\t`[vexpr for k1, v1 in container1, cond1 ...]` or\n//\t`{vexpr for k1, v1 in container1, cond1 ...}` or\n//\t`{kexpr: vexpr for k1, v1 in container1, cond1 ...}` or\n//\t`{for k1, v1 in container1, cond1 ...}`\ntype ComprehensionExpr struct {\n\tLpos token.Pos   // position of \"[\" or \"{\"\n\tTok  token.Token // token.LBRACK '[' or token.LBRACE '{'\n\tElt  Expr        // *KeyValueExpr or Expr or nil\n\tFors []*ForPhrase\n\tRpos token.Pos // position of \"]\" or \"}\"\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *ComprehensionExpr) Pos() token.Pos {\n\treturn p.Lpos\n}\n\n// End - position of first character immediately after the node.\nfunc (p *ComprehensionExpr) End() token.Pos {\n\treturn p.Rpos + 1\n}\n\nfunc (*ComprehensionExpr) exprNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A ForPhraseStmt represents a for statement with a for..in clause.\ntype ForPhraseStmt struct {\n\t*ForPhrase\n\tBody *BlockStmt\n}\n\n// Pos - position of first character belonging to the node.\nfunc (p *ForPhraseStmt) Pos() token.Pos {\n\treturn p.For\n}\n\n// End - position of first character immediately after the node.\nfunc (p *ForPhraseStmt) End() token.Pos {\n\treturn p.Body.End()\n}\n\nfunc (*ForPhraseStmt) stmtNode() {}\n\n// -----------------------------------------------------------------------------\n\n// A SendStmt node represents a send statement.\ntype SendStmt struct {\n\tChan     Expr\n\tArrow    token.Pos // position of \"<-\"\n\tValues   []Expr    // len(Values) must > 0\n\tEllipsis token.Pos // position of \"...\"\n}\n\n// End returns position of first character immediately after the node.\nfunc (s *SendStmt) End() token.Pos {\n\tif s.Ellipsis != token.NoPos {\n\t\treturn s.Ellipsis + 3\n\t}\n\tvals := s.Values\n\treturn vals[len(vals)-1].End()\n}\n\n// -----------------------------------------------------------------------------\n\n// A File node represents an XGo source file.\n//\n// The Comments list contains all comments in the source file in order of\n// appearance, including the comments that are pointed to from other nodes\n// via Doc and Comment fields.\n//\n// For correct printing of source code containing comments (using packages\n// go/format and go/printer), special care must be taken to update comments\n// when a File's syntax tree is modified: For printing, comments are interspersed\n// between tokens based on their position. If syntax tree nodes are\n// removed or moved, relevant comments in their vicinity must also be removed\n// (from the File.Comments list) or moved accordingly (by updating their\n// positions). A CommentMap may be used to facilitate some of these operations.\n//\n// Whether and how a comment is associated with a node depends on the\n// interpretation of the syntax tree by the manipulating program: Except for Doc\n// and Comment comments directly associated with nodes, the remaining comments\n// are \"free-floating\" (see also issues #18593, #20744).\ntype File struct {\n\tDoc     *CommentGroup // associated documentation; or nil\n\tPackage token.Pos     // position of \"package\" keyword; or NoPos\n\tName    *Ident        // package name\n\tDecls   []Decl        // top-level declarations; or nil\n\n\tImports     []*ImportSpec   // imports in this file\n\tComments    []*CommentGroup // list of all comments in the source file\n\tCode        []byte\n\tShadowEntry *FuncDecl // indicate the module entry point.\n\tNoPkgDecl   bool      // no `package xxx` declaration\n\tIsClass     bool      // is a classfile (including normal .gox file)\n\tIsProj      bool      // is a project classfile\n\tIsNormalGox bool      // is a normal .gox file\n}\n\n// There is no entrypoint func to indicate the module entry point.\nfunc (f *File) HasShadowEntry() bool {\n\treturn f.ShadowEntry != nil\n}\n\n// HasPkgDecl checks if `package xxx` exists or not.\nfunc (f *File) HasPkgDecl() bool {\n\treturn f.Package != token.NoPos\n}\n\n// ClassFieldsDecl returns the class fields declaration.\nfunc (f *File) ClassFieldsDecl() *GenDecl {\n\tif f.IsClass {\n\t\tfor _, decl := range f.Decls {\n\t\t\tif g, ok := decl.(*GenDecl); ok {\n\t\t\t\tif g.Tok == token.VAR {\n\t\t\t\t\treturn g\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nil\n}\n\n// Pos returns position of first character belonging to the node.\nfunc (f *File) Pos() token.Pos {\n\tif f.Package != token.NoPos {\n\t\treturn f.Package\n\t}\n\t// if no package clause, name records the position of the first token in the file\n\treturn f.Name.NamePos\n}\n\n// End returns position of first character immediately after the node.\nfunc (f *File) End() token.Pos {\n\tif f.ShadowEntry != nil { // has shadow entry\n\t\treturn f.ShadowEntry.End()\n\t}\n\tfor n := len(f.Decls) - 1; n >= 0; n-- {\n\t\td := f.Decls[n]\n\t\tif fn, ok := d.(*FuncDecl); ok && fn.Shadow {\n\t\t\t// skip shadow functions like Classfname (see cl.astFnClassfname)\n\t\t\tcontinue\n\t\t}\n\t\treturn d.End()\n\t}\n\tif f.Package != token.NoPos { // has package clause\n\t\treturn f.Name.End()\n\t}\n\treturn f.Name.Pos()\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "ast/commentmap.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ast\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"sort\"\n\n\t\"github.com/goplus/xgo/token\"\n)\n\ntype byPos []*CommentGroup\n\nfunc (a byPos) Len() int           { return len(a) }\nfunc (a byPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }\nfunc (a byPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }\n\n// sortComments sorts the list of comment groups in source order.\nfunc sortComments(list []*CommentGroup) {\n\t// TODO(gri): Does it make sense to check for sorted-ness\n\t//            first (because we know that sorted-ness is\n\t//            very likely)?\n\tif orderedList := byPos(list); !sort.IsSorted(orderedList) {\n\t\tsort.Sort(orderedList)\n\t}\n}\n\n// A CommentMap maps an AST node to a list of comment groups\n// associated with it. See NewCommentMap for a description of\n// the association.\ntype CommentMap map[Node][]*CommentGroup\n\nfunc (cmap CommentMap) addComment(n Node, c *CommentGroup) {\n\tlist := cmap[n]\n\tif len(list) == 0 {\n\t\tlist = []*CommentGroup{c}\n\t} else {\n\t\tlist = append(list, c)\n\t}\n\tcmap[n] = list\n}\n\n// nodeList returns the list of nodes of the AST n in source order.\nfunc nodeList(n Node) []Node {\n\tvar list []Node\n\tInspect(n, func(n Node) bool {\n\t\t// don't collect comments\n\t\tswitch n.(type) {\n\t\tcase nil, *CommentGroup, *Comment:\n\t\t\treturn false\n\t\t}\n\t\tlist = append(list, n)\n\t\treturn true\n\t})\n\t// Note: The current implementation assumes that Inspect traverses the\n\t//       AST in depth-first and thus _source_ order. If AST traversal\n\t//       does not follow source order, the sorting call below will be\n\t//       required.\n\t// sort.Sort(byInterval(list))\n\treturn list\n}\n\n// A commentListReader helps iterating through a list of comment groups.\ntype commentListReader struct {\n\tfset     *token.FileSet\n\tlist     []*CommentGroup\n\tindex    int\n\tcomment  *CommentGroup  // comment group at current index\n\tpos, end token.Position // source interval of comment group at current index\n}\n\nfunc (r *commentListReader) eol() bool {\n\treturn r.index >= len(r.list)\n}\n\nfunc (r *commentListReader) next() {\n\tif !r.eol() {\n\t\tr.comment = r.list[r.index]\n\t\tr.pos = r.fset.Position(r.comment.Pos())\n\t\tr.end = r.fset.Position(r.comment.End())\n\t\tr.index++\n\t}\n}\n\n// A nodeStack keeps track of nested nodes.\n// A node lower on the stack lexically contains the nodes higher on the stack.\ntype nodeStack []Node\n\n// push pops all nodes that appear lexically before n\n// and then pushes n on the stack.\nfunc (s *nodeStack) push(n Node) {\n\ts.pop(n.Pos())\n\t*s = append((*s), n)\n}\n\n// pop pops all nodes that appear lexically before pos\n// (i.e., whose lexical extent has ended before or at pos).\n// It returns the last node popped.\nfunc (s *nodeStack) pop(pos token.Pos) (top Node) {\n\ti := len(*s)\n\tfor i > 0 && (*s)[i-1].End() <= pos {\n\t\ttop = (*s)[i-1]\n\t\ti--\n\t}\n\t*s = (*s)[0:i]\n\treturn top\n}\n\n// NewCommentMap creates a new comment map by associating comment groups\n// of the comments list with the nodes of the AST specified by node.\n//\n// A comment group g is associated with a node n if:\n//\n//   - g starts on the same line as n ends\n//   - g starts on the line immediately following n, and there is\n//     at least one empty line after g and before the next node\n//   - g starts before n and is not associated to the node before n\n//     via the previous rules\n//\n// NewCommentMap tries to associate a comment group to the \"largest\"\n// node possible: For instance, if the comment is a line comment\n// trailing an assignment, the comment is associated with the entire\n// assignment rather than just the last operand in the assignment.\nfunc NewCommentMap(fset *token.FileSet, node Node, comments []*CommentGroup) CommentMap {\n\tif len(comments) == 0 {\n\t\treturn nil // no comments to map\n\t}\n\n\tcmap := make(CommentMap)\n\n\t// set up comment reader r\n\ttmp := make([]*CommentGroup, len(comments))\n\tcopy(tmp, comments) // don't change incoming comments\n\tsortComments(tmp)\n\tr := commentListReader{fset: fset, list: tmp} // !r.eol() because len(comments) > 0\n\tr.next()\n\n\t// create node list in lexical order\n\tnodes := nodeList(node)\n\tnodes = append(nodes, nil) // append sentinel\n\n\t// set up iteration variables\n\tvar (\n\t\tp     Node           // previous node\n\t\tpend  token.Position // end of p\n\t\tpg    Node           // previous node group (enclosing nodes of \"importance\")\n\t\tpgend token.Position // end of pg\n\t\tstack nodeStack      // stack of node groups\n\t)\n\n\tfor _, q := range nodes {\n\t\tvar qpos token.Position\n\t\tif q != nil {\n\t\t\tqpos = fset.Position(q.Pos()) // current node position\n\t\t} else {\n\t\t\t// set fake sentinel position to infinity so that\n\t\t\t// all comments get processed before the sentinel\n\t\t\tconst infinity = 1 << 30\n\t\t\tqpos.Offset = infinity\n\t\t\tqpos.Line = infinity\n\t\t}\n\n\t\t// process comments before current node\n\t\tfor r.end.Offset <= qpos.Offset {\n\t\t\t// determine recent node group\n\t\t\tif top := stack.pop(r.comment.Pos()); top != nil {\n\t\t\t\tpg = top\n\t\t\t\tpgend = fset.Position(pg.End())\n\t\t\t}\n\t\t\t// Try to associate a comment first with a node group\n\t\t\t// (i.e., a node of \"importance\" such as a declaration);\n\t\t\t// if that fails, try to associate it with the most recent\n\t\t\t// node.\n\t\t\t// TODO(gri) try to simplify the logic below\n\t\t\tvar assoc Node\n\t\t\tswitch {\n\t\t\tcase pg != nil &&\n\t\t\t\t(pgend.Line == r.pos.Line ||\n\t\t\t\t\tpgend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line):\n\t\t\t\t// 1) comment starts on same line as previous node group ends, or\n\t\t\t\t// 2) comment starts on the line immediately after the\n\t\t\t\t//    previous node group and there is an empty line before\n\t\t\t\t//    the current node\n\t\t\t\t// => associate comment with previous node group\n\t\t\t\tassoc = pg\n\t\t\tcase p != nil &&\n\t\t\t\t(pend.Line == r.pos.Line ||\n\t\t\t\t\tpend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line ||\n\t\t\t\t\tq == nil):\n\t\t\t\t// same rules apply as above for p rather than pg,\n\t\t\t\t// but also associate with p if we are at the end (q == nil)\n\t\t\t\tassoc = p\n\t\t\tdefault:\n\t\t\t\t// otherwise, associate comment with current node\n\t\t\t\tif q == nil {\n\t\t\t\t\t// we can only reach here if there was no p\n\t\t\t\t\t// which would imply that there were no nodes\n\t\t\t\t\tpanic(\"internal error: no comments should be associated with sentinel\")\n\t\t\t\t}\n\t\t\t\tassoc = q\n\t\t\t}\n\t\t\tcmap.addComment(assoc, r.comment)\n\t\t\tif r.eol() {\n\t\t\t\treturn cmap\n\t\t\t}\n\t\t\tr.next()\n\t\t}\n\n\t\t// update previous node\n\t\tp = q\n\t\tpend = fset.Position(p.End())\n\n\t\t// update previous node group if we see an \"important\" node\n\t\tswitch q.(type) {\n\t\tcase *File, *Field, Decl, Spec, Stmt:\n\t\t\tstack.push(q)\n\t\t}\n\t}\n\n\treturn cmap\n}\n\n// Update replaces an old node in the comment map with the new node\n// and returns the new node. Comments that were associated with the\n// old node are associated with the new node.\nfunc (cmap CommentMap) Update(old, new Node) Node {\n\tif list := cmap[old]; len(list) > 0 {\n\t\tdelete(cmap, old)\n\t\tcmap[new] = append(cmap[new], list...)\n\t}\n\treturn new\n}\n\n// Filter returns a new comment map consisting of only those\n// entries of cmap for which a corresponding node exists in\n// the AST specified by node.\nfunc (cmap CommentMap) Filter(node Node) CommentMap {\n\tumap := make(CommentMap)\n\tInspect(node, func(n Node) bool {\n\t\tif g := cmap[n]; len(g) > 0 {\n\t\t\tumap[n] = g\n\t\t}\n\t\treturn true\n\t})\n\treturn umap\n}\n\n// Comments returns the list of comment groups in the comment map.\n// The result is sorted in source order.\nfunc (cmap CommentMap) Comments() []*CommentGroup {\n\tlist := make([]*CommentGroup, 0, len(cmap))\n\tfor _, e := range cmap {\n\t\tlist = append(list, e...)\n\t}\n\tsortComments(list)\n\treturn list\n}\n\nfunc summary(list []*CommentGroup) string {\n\tconst maxLen = 40\n\tvar buf bytes.Buffer\n\n\t// collect comments text\nloop:\n\tfor _, group := range list {\n\t\t// Note: CommentGroup.Text() does too much work for what we\n\t\t//       need and would only replace this innermost loop.\n\t\t//       Just do it explicitly.\n\t\tfor _, comment := range group.List {\n\t\t\tif buf.Len() >= maxLen {\n\t\t\t\tbreak loop\n\t\t\t}\n\t\t\tbuf.WriteString(comment.Text)\n\t\t}\n\t}\n\n\t// truncate if too long\n\tif buf.Len() > maxLen {\n\t\tbuf.Truncate(maxLen - 3)\n\t\tbuf.WriteString(\"...\")\n\t}\n\n\t// replace any invisibles with blanks\n\tbytes := buf.Bytes()\n\tfor i, b := range bytes {\n\t\tswitch b {\n\t\tcase '\\t', '\\n', '\\r':\n\t\t\tbytes[i] = ' '\n\t\t}\n\t}\n\n\treturn string(bytes)\n}\n\nfunc (cmap CommentMap) String() string {\n\tvar buf bytes.Buffer\n\tfmt.Fprintln(&buf, \"CommentMap {\")\n\tfor node, comment := range cmap {\n\t\t// print name of identifiers; print node type for other nodes\n\t\tvar s string\n\t\tif ident, ok := node.(*Ident); ok {\n\t\t\ts = ident.Name\n\t\t} else {\n\t\t\ts = fmt.Sprintf(\"%T\", node)\n\t\t}\n\t\tfmt.Fprintf(&buf, \"\\t%p  %20s:  %s\\n\", node, s, summary(comment))\n\t}\n\tfmt.Fprintln(&buf, \"}\")\n\treturn buf.String()\n}\n"
  },
  {
    "path": "ast/filter.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ast\n\nimport (\n\t\"sort\"\n\n\t\"github.com/goplus/xgo/token\"\n)\n\n// ----------------------------------------------------------------------------\n// Export filtering\n\n// exportFilter is a special filter function to extract exported nodes.\nfunc exportFilter(name string) bool {\n\treturn IsExported(name)\n}\n\n// FileExports trims the AST for a Go source file in place such that\n// only exported nodes remain: all top-level identifiers which are not exported\n// and their associated information (such as type, initial value, or function\n// body) are removed. Non-exported fields and methods of exported types are\n// stripped. The File.Comments list is not changed.\n//\n// FileExports reports whether there are exported declarations.\nfunc FileExports(src *File) bool {\n\treturn filterFile(src, exportFilter, true)\n}\n\n// PackageExports trims the AST for a Go package in place such that\n// only exported nodes remain. The pkg.Files list is not changed, so that\n// file names and top-level package comments don't get lost.\n//\n// PackageExports reports whether there are exported declarations;\n// it returns false otherwise.\nfunc PackageExports(pkg *Package) bool {\n\treturn filterPackage(pkg, exportFilter, true)\n}\n\n// ----------------------------------------------------------------------------\n// General filtering\n\n// Filter type\ntype Filter func(string) bool\n\nfunc filterIdentList(list []*Ident, f Filter) []*Ident {\n\tj := 0\n\tfor _, x := range list {\n\t\tif f(x.Name) {\n\t\t\tlist[j] = x\n\t\t\tj++\n\t\t}\n\t}\n\treturn list[0:j]\n}\n\n// fieldName assumes that x is the type of an anonymous field and\n// returns the corresponding field name. If x is not an acceptable\n// anonymous field, the result is nil.\nfunc fieldName(x Expr) *Ident {\n\tswitch t := x.(type) {\n\tcase *Ident:\n\t\treturn t\n\tcase *SelectorExpr:\n\t\tif _, ok := t.X.(*Ident); ok {\n\t\t\treturn t.Sel\n\t\t}\n\tcase *StarExpr:\n\t\treturn fieldName(t.X)\n\t}\n\treturn nil\n}\n\nfunc filterFieldList(fields *FieldList, filter Filter, export bool) (removedFields bool) {\n\tif fields == nil {\n\t\treturn false\n\t}\n\tlist := fields.List\n\tj := 0\n\tfor _, f := range list {\n\t\tvar keepField bool\n\t\tif len(f.Names) == 0 {\n\t\t\t// anonymous field\n\t\t\tname := fieldName(f.Type)\n\t\t\tkeepField = name != nil && filter(name.Name)\n\t\t} else {\n\t\t\tn := len(f.Names)\n\t\t\tf.Names = filterIdentList(f.Names, filter)\n\t\t\tif len(f.Names) < n {\n\t\t\t\tremovedFields = true\n\t\t\t}\n\t\t\tkeepField = len(f.Names) > 0\n\t\t}\n\t\tif keepField {\n\t\t\tif export {\n\t\t\t\tfilterType(f.Type, filter, export)\n\t\t\t}\n\t\t\tlist[j] = f\n\t\t\tj++\n\t\t}\n\t}\n\tif j < len(list) {\n\t\tremovedFields = true\n\t}\n\tfields.List = list[0:j]\n\treturn\n}\n\nfunc filterCompositeLit(lit *CompositeLit, filter Filter, export bool) {\n\tn := len(lit.Elts)\n\tlit.Elts = filterExprList(lit.Elts, filter, export)\n\tif len(lit.Elts) < n {\n\t\tlit.Incomplete = true\n\t}\n}\n\nfunc filterExprList(list []Expr, filter Filter, export bool) []Expr {\n\tj := 0\n\tfor _, exp := range list {\n\t\tswitch x := exp.(type) {\n\t\tcase *CompositeLit:\n\t\t\tfilterCompositeLit(x, filter, export)\n\t\tcase *KeyValueExpr:\n\t\t\tif x, ok := x.Key.(*Ident); ok && !filter(x.Name) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif x, ok := x.Value.(*CompositeLit); ok {\n\t\t\t\tfilterCompositeLit(x, filter, export)\n\t\t\t}\n\t\t}\n\t\tlist[j] = exp\n\t\tj++\n\t}\n\treturn list[0:j]\n}\n\nfunc filterParamList(fields *FieldList, filter Filter, export bool) bool {\n\tif fields == nil {\n\t\treturn false\n\t}\n\tvar b bool\n\tfor _, f := range fields.List {\n\t\tif filterType(f.Type, filter, export) {\n\t\t\tb = true\n\t\t}\n\t}\n\treturn b\n}\n\nfunc filterType(typ Expr, f Filter, export bool) bool {\n\tswitch t := typ.(type) {\n\tcase *Ident:\n\t\treturn f(t.Name)\n\tcase *ParenExpr:\n\t\treturn filterType(t.X, f, export)\n\tcase *ArrayType:\n\t\treturn filterType(t.Elt, f, export)\n\tcase *StructType:\n\t\tif filterFieldList(t.Fields, f, export) {\n\t\t\tt.Incomplete = true\n\t\t}\n\t\treturn len(t.Fields.List) > 0\n\tcase *FuncType:\n\t\tb1 := filterParamList(t.Params, f, export)\n\t\tb2 := filterParamList(t.Results, f, export)\n\t\treturn b1 || b2\n\tcase *InterfaceType:\n\t\tif filterFieldList(t.Methods, f, export) {\n\t\t\tt.Incomplete = true\n\t\t}\n\t\treturn len(t.Methods.List) > 0\n\tcase *MapType:\n\t\tb1 := filterType(t.Key, f, export)\n\t\tb2 := filterType(t.Value, f, export)\n\t\treturn b1 || b2\n\tcase *ChanType:\n\t\treturn filterType(t.Value, f, export)\n\tcase *TupleType:\n\t\tif filterFieldList(t.Fields, f, export) {\n\t\t\t// Note: TupleType doesn't have an Incomplete field like StructType\n\t\t}\n\t\treturn t.Fields != nil && len(t.Fields.List) > 0\n\t}\n\treturn false\n}\n\nfunc filterSpec(spec Spec, f Filter, export bool) bool {\n\tswitch s := spec.(type) {\n\tcase *ValueSpec:\n\t\ts.Names = filterIdentList(s.Names, f)\n\t\ts.Values = filterExprList(s.Values, f, export)\n\t\tif len(s.Names) > 0 {\n\t\t\tif export {\n\t\t\t\tfilterType(s.Type, f, export)\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\tcase *TypeSpec:\n\t\tif f(s.Name.Name) {\n\t\t\tif export {\n\t\t\t\tfilterType(s.Type, f, export)\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t\tif !export {\n\t\t\t// For general filtering (not just exports),\n\t\t\t// filter type even if name is not filtered\n\t\t\t// out.\n\t\t\t// If the type contains filtered elements,\n\t\t\t// keep the declaration.\n\t\t\treturn filterType(s.Type, f, export)\n\t\t}\n\t}\n\treturn false\n}\n\nfunc filterSpecList(list []Spec, f Filter, export bool) []Spec {\n\tj := 0\n\tfor _, s := range list {\n\t\tif filterSpec(s, f, export) {\n\t\t\tlist[j] = s\n\t\t\tj++\n\t\t}\n\t}\n\treturn list[0:j]\n}\n\n// FilterDecl trims the AST for a Go declaration in place by removing\n// all names (including struct field and interface method names, but\n// not from parameter lists) that don't pass through the filter f.\n//\n// FilterDecl reports whether there are any declared names left after\n// filtering.\nfunc FilterDecl(decl Decl, f Filter) bool {\n\treturn filterDecl(decl, f, false)\n}\n\nfunc filterDecl(decl Decl, f Filter, export bool) bool {\n\tswitch d := decl.(type) {\n\tcase *GenDecl:\n\t\td.Specs = filterSpecList(d.Specs, f, export)\n\t\treturn len(d.Specs) > 0\n\tcase *FuncDecl:\n\t\treturn f(d.Name.Name)\n\t}\n\treturn false\n}\n\n// FilterFile trims the AST for a Go file in place by removing all\n// names from top-level declarations (including struct field and\n// interface method names, but not from parameter lists) that don't\n// pass through the filter f. If the declaration is empty afterwards,\n// the declaration is removed from the AST. Import declarations are\n// always removed. The File.Comments list is not changed.\n//\n// FilterFile reports whether there are any top-level declarations\n// left after filtering.\nfunc FilterFile(src *File, f Filter) bool {\n\treturn filterFile(src, f, false)\n}\n\nfunc filterFile(src *File, f Filter, export bool) bool {\n\tj := 0\n\tfor _, d := range src.Decls {\n\t\tif filterDecl(d, f, export) {\n\t\t\tsrc.Decls[j] = d\n\t\t\tj++\n\t\t}\n\t}\n\tsrc.Decls = src.Decls[0:j]\n\treturn j > 0\n}\n\n// FilterPackage trims the AST for a Go package in place by removing\n// all names from top-level declarations (including struct field and\n// interface method names, but not from parameter lists) that don't\n// pass through the filter f. If the declaration is empty afterwards,\n// the declaration is removed from the AST. The pkg.Files list is not\n// changed, so that file names and top-level package comments don't get\n// lost.\n//\n// FilterPackage reports whether there are any top-level declarations\n// left after filtering.\nfunc FilterPackage(pkg *Package, f Filter) bool {\n\treturn filterPackage(pkg, f, false)\n}\n\nfunc filterPackage(pkg *Package, f Filter, export bool) bool {\n\thasDecls := false\n\tfor _, src := range pkg.Files {\n\t\tif filterFile(src, f, export) {\n\t\t\thasDecls = true\n\t\t}\n\t}\n\treturn hasDecls\n}\n\n// ----------------------------------------------------------------------------\n// Merging of package files\n\n// The MergeMode flags control the behavior of MergePackageFiles.\ntype MergeMode uint\n\nconst (\n\t// FilterFuncDuplicates - If set, duplicate function declarations are excluded.\n\tFilterFuncDuplicates MergeMode = 1 << iota\n\t// FilterUnassociatedComments - If set, comments that are not associated with a specific\n\t// AST node (as Doc or Comment) are excluded.\n\tFilterUnassociatedComments\n\t// FilterImportDuplicates - If set, duplicate import declarations are excluded.\n\tFilterImportDuplicates\n)\n\n// nameOf returns the function (foo) or method name (foo.bar) for\n// the given function declaration. If the AST is incorrect for the\n// receiver, it assumes a function instead.\nfunc nameOf(f *FuncDecl) string {\n\tif r := f.Recv; r != nil && len(r.List) == 1 {\n\t\t// looks like a correct receiver declaration\n\t\tt := r.List[0].Type\n\t\t// dereference pointer receiver types\n\t\tif p, _ := t.(*StarExpr); p != nil {\n\t\t\tt = p.X\n\t\t}\n\t\t// the receiver type must be a type name\n\t\tif p, _ := t.(*Ident); p != nil {\n\t\t\treturn p.Name + \".\" + f.Name.Name\n\t\t}\n\t\t// otherwise assume a function instead\n\t}\n\treturn f.Name.Name\n}\n\n// separator is an empty //-style comment that is interspersed between\n// different comment groups when they are concatenated into a single group\nvar separator = &Comment{Slash: token.NoPos, Text: \"//\"}\n\n// MergePackageFiles creates a file AST by merging the ASTs of the\n// files belonging to a package. The mode flags control merging behavior.\nfunc MergePackageFiles(pkg *Package, mode MergeMode) *File {\n\t// Count the number of package docs, comments and declarations across\n\t// all package files. Also, compute sorted list of filenames, so that\n\t// subsequent iterations can always iterate in the same order.\n\tndocs := 0\n\tncomments := 0\n\tndecls := 0\n\tfilenames := make([]string, len(pkg.Files))\n\ti := 0\n\tfor filename, f := range pkg.Files {\n\t\tfilenames[i] = filename\n\t\ti++\n\t\tif f.Doc != nil {\n\t\t\tndocs += len(f.Doc.List) + 1 // +1 for separator\n\t\t}\n\t\tncomments += len(f.Comments)\n\t\tndecls += len(f.Decls)\n\t}\n\tsort.Strings(filenames)\n\n\t// Collect package comments from all package files into a single\n\t// CommentGroup - the collected package documentation. In general\n\t// there should be only one file with a package comment; but it's\n\t// better to collect extra comments than drop them on the floor.\n\tvar doc *CommentGroup\n\tvar pos token.Pos\n\tif ndocs > 0 {\n\t\tlist := make([]*Comment, ndocs-1) // -1: no separator before first group\n\t\ti := 0\n\t\tfor _, filename := range filenames {\n\t\t\tf := pkg.Files[filename]\n\t\t\tif f.Doc != nil {\n\t\t\t\tif i > 0 {\n\t\t\t\t\t// not the first group - add separator\n\t\t\t\t\tlist[i] = separator\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\t\tfor _, c := range f.Doc.List {\n\t\t\t\t\tlist[i] = c\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\t\tif f.Package > pos {\n\t\t\t\t\t// Keep the maximum package clause position as\n\t\t\t\t\t// position for the package clause of the merged\n\t\t\t\t\t// files.\n\t\t\t\t\tpos = f.Package\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdoc = &CommentGroup{List: list}\n\t}\n\n\t// Collect declarations from all package files.\n\tvar decls []Decl\n\tif ndecls > 0 {\n\t\tdecls = make([]Decl, ndecls)\n\t\tfuncs := make(map[string]int) // map of func name -> decls index\n\t\ti := 0                        // current index\n\t\tn := 0                        // number of filtered entries\n\t\tfor _, filename := range filenames {\n\t\t\tf := pkg.Files[filename]\n\t\t\tfor _, d := range f.Decls {\n\t\t\t\tif mode&FilterFuncDuplicates != 0 {\n\t\t\t\t\t// A language entity may be declared multiple\n\t\t\t\t\t// times in different package files; only at\n\t\t\t\t\t// build time declarations must be unique.\n\t\t\t\t\t// For now, exclude multiple declarations of\n\t\t\t\t\t// functions - keep the one with documentation.\n\t\t\t\t\t//\n\t\t\t\t\t// TODO(gri): Expand this filtering to other\n\t\t\t\t\t//            entities (const, type, vars) if\n\t\t\t\t\t//            multiple declarations are common.\n\t\t\t\t\tif f, isFun := d.(*FuncDecl); isFun {\n\t\t\t\t\t\tname := nameOf(f)\n\t\t\t\t\t\tif j, exists := funcs[name]; exists {\n\t\t\t\t\t\t\t// function declared already\n\t\t\t\t\t\t\tif decls[j] != nil && decls[j].(*FuncDecl).Doc == nil {\n\t\t\t\t\t\t\t\t// existing declaration has no documentation;\n\t\t\t\t\t\t\t\t// ignore the existing declaration\n\t\t\t\t\t\t\t\tdecls[j] = nil\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// ignore the new declaration\n\t\t\t\t\t\t\t\td = nil\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tn++ // filtered an entry\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfuncs[name] = i\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdecls[i] = d\n\t\t\t\ti++\n\t\t\t}\n\t\t}\n\n\t\t// Eliminate nil entries from the decls list if entries were\n\t\t// filtered. We do this using a 2nd pass in order to not disturb\n\t\t// the original declaration order in the source (otherwise, this\n\t\t// would also invalidate the monotonically increasing position\n\t\t// info within a single file).\n\t\tif n > 0 {\n\t\t\ti = 0\n\t\t\tfor _, d := range decls {\n\t\t\t\tif d != nil {\n\t\t\t\t\tdecls[i] = d\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\t}\n\t\t\tdecls = decls[0:i]\n\t\t}\n\t}\n\n\t// Collect import specs from all package files.\n\tvar imports []*ImportSpec\n\tif mode&FilterImportDuplicates != 0 {\n\t\tseen := make(map[string]bool)\n\t\tfor _, filename := range filenames {\n\t\t\tf := pkg.Files[filename]\n\t\t\tfor _, imp := range f.Imports {\n\t\t\t\tif path := imp.Path.Value; !seen[path] {\n\t\t\t\t\t// TODO: consider handling cases where:\n\t\t\t\t\t// - 2 imports exist with the same import path but\n\t\t\t\t\t//   have different local names (one should probably\n\t\t\t\t\t//   keep both of them)\n\t\t\t\t\t// - 2 imports exist but only one has a comment\n\t\t\t\t\t// - 2 imports exist and they both have (possibly\n\t\t\t\t\t//   different) comments\n\t\t\t\t\timports = append(imports, imp)\n\t\t\t\t\tseen[path] = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Iterate over filenames for deterministic order.\n\t\tfor _, filename := range filenames {\n\t\t\tf := pkg.Files[filename]\n\t\t\timports = append(imports, f.Imports...)\n\t\t}\n\t}\n\n\t// Collect comments from all package files.\n\tvar comments []*CommentGroup\n\tif mode&FilterUnassociatedComments == 0 {\n\t\tcomments = make([]*CommentGroup, ncomments)\n\t\ti := 0\n\t\tfor _, filename := range filenames {\n\t\t\tf := pkg.Files[filename]\n\t\t\ti += copy(comments[i:], f.Comments)\n\t\t}\n\t}\n\n\t// TODO(gri) need to compute unresolved identifiers!\n\treturn &File{\n\t\tdoc, pos, NewIdent(pkg.Name), decls,\n\t\timports, comments, nil, nil, false, false, false, false,\n\t}\n}\n"
  },
  {
    "path": "ast/fromgo/gopast.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage fromgo\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"log\"\n\t\"reflect\"\n\n\tgopast \"github.com/goplus/xgo/ast\"\n\tgoptoken \"github.com/goplus/xgo/token\"\n\n\t\"github.com/goplus/xgo/ast/fromgo/typeparams\"\n)\n\n// ----------------------------------------------------------------------------\n\nfunc gopExpr(val ast.Expr) gopast.Expr {\n\tif val == nil {\n\t\treturn nil\n\t}\n\tswitch v := val.(type) {\n\tcase *ast.Ident:\n\t\treturn gopIdent(v)\n\tcase *ast.SelectorExpr:\n\t\treturn &gopast.SelectorExpr{\n\t\t\tX:   gopExpr(v.X),\n\t\t\tSel: gopIdent(v.Sel),\n\t\t}\n\tcase *ast.SliceExpr:\n\t\treturn &gopast.SliceExpr{\n\t\t\tX:      gopExpr(v.X),\n\t\t\tLbrack: v.Lbrack,\n\t\t\tLow:    gopExpr(v.Low),\n\t\t\tHigh:   gopExpr(v.High),\n\t\t\tMax:    gopExpr(v.Max),\n\t\t\tSlice3: v.Slice3,\n\t\t\tRbrack: v.Rbrack,\n\t\t}\n\tcase *ast.StarExpr:\n\t\treturn &gopast.StarExpr{\n\t\t\tStar: v.Star,\n\t\t\tX:    gopExpr(v.X),\n\t\t}\n\tcase *ast.MapType:\n\t\treturn &gopast.MapType{\n\t\t\tMap:   v.Map,\n\t\t\tKey:   gopType(v.Key),\n\t\t\tValue: gopType(v.Value),\n\t\t}\n\tcase *ast.StructType:\n\t\treturn &gopast.StructType{\n\t\t\tStruct: v.Struct,\n\t\t\tFields: gopFieldList(v.Fields),\n\t\t}\n\tcase *ast.FuncType:\n\t\treturn gopFuncType(v)\n\tcase *ast.InterfaceType:\n\t\treturn &gopast.InterfaceType{\n\t\t\tInterface: v.Interface,\n\t\t\tMethods:   gopFieldList(v.Methods),\n\t\t}\n\tcase *ast.ArrayType:\n\t\treturn &gopast.ArrayType{\n\t\t\tLbrack: v.Lbrack,\n\t\t\tLen:    gopExpr(v.Len),\n\t\t\tElt:    gopType(v.Elt),\n\t\t}\n\tcase *ast.ChanType:\n\t\treturn &gopast.ChanType{\n\t\t\tBegin: v.Begin,\n\t\t\tArrow: v.Arrow,\n\t\t\tDir:   gopast.ChanDir(v.Dir),\n\t\t\tValue: gopType(v.Value),\n\t\t}\n\tcase *ast.BasicLit:\n\t\treturn gopBasicLit(v)\n\tcase *ast.BinaryExpr:\n\t\treturn &gopast.BinaryExpr{\n\t\t\tX:     gopExpr(v.X),\n\t\t\tOpPos: v.OpPos,\n\t\t\tOp:    goptoken.Token(v.Op),\n\t\t\tY:     gopExpr(v.Y),\n\t\t}\n\tcase *ast.UnaryExpr:\n\t\treturn &gopast.UnaryExpr{\n\t\t\tOpPos: v.OpPos,\n\t\t\tOp:    goptoken.Token(v.Op),\n\t\t\tX:     gopExpr(v.X),\n\t\t}\n\tcase *ast.CallExpr:\n\t\treturn &gopast.CallExpr{\n\t\t\tFun:      gopExpr(v.Fun),\n\t\t\tLparen:   v.Lparen,\n\t\t\tArgs:     gopExprs(v.Args),\n\t\t\tEllipsis: v.Ellipsis,\n\t\t\tRparen:   v.Rparen,\n\t\t}\n\tcase *ast.IndexExpr:\n\t\treturn &gopast.IndexExpr{\n\t\t\tX:      gopExpr(v.X),\n\t\t\tLbrack: v.Lbrack,\n\t\t\tIndex:  gopExpr(v.Index),\n\t\t\tRbrack: v.Rbrack,\n\t\t}\n\tcase *typeparams.IndexListExpr:\n\t\treturn &gopast.IndexListExpr{\n\t\t\tX:       gopExpr(v.X),\n\t\t\tLbrack:  v.Lbrack,\n\t\t\tIndices: gopExprs(v.Indices),\n\t\t\tRbrack:  v.Rbrack,\n\t\t}\n\tcase *ast.ParenExpr:\n\t\treturn &gopast.ParenExpr{\n\t\t\tLparen: v.Lparen,\n\t\t\tX:      gopExpr(v.X),\n\t\t\tRparen: v.Rparen,\n\t\t}\n\tcase *ast.CompositeLit:\n\t\treturn &gopast.CompositeLit{\n\t\t\tType:   gopType(v.Type),\n\t\t\tLbrace: v.Lbrace,\n\t\t\tElts:   gopExprs(v.Elts),\n\t\t\tRbrace: v.Rbrace,\n\t\t}\n\tcase *ast.FuncLit:\n\t\treturn &gopast.FuncLit{\n\t\t\tType: gopFuncType(v.Type),\n\t\t\tBody: &gopast.BlockStmt{}, // skip closure body\n\t\t}\n\tcase *ast.TypeAssertExpr:\n\t\treturn &gopast.TypeAssertExpr{\n\t\t\tX:      gopExpr(v.X),\n\t\t\tLparen: v.Lparen,\n\t\t\tType:   gopType(v.Type),\n\t\t\tRparen: v.Rparen,\n\t\t}\n\tcase *ast.KeyValueExpr:\n\t\treturn &gopast.KeyValueExpr{\n\t\t\tKey:   gopExpr(v.Key),\n\t\t\tColon: v.Colon,\n\t\t\tValue: gopExpr(v.Value),\n\t\t}\n\tcase *ast.Ellipsis:\n\t\treturn &gopast.Ellipsis{\n\t\t\tEllipsis: v.Ellipsis,\n\t\t\tElt:      gopExpr(v.Elt),\n\t\t}\n\t}\n\tlog.Panicln(\"gopExpr: unknown expr -\", reflect.TypeOf(val))\n\treturn nil\n}\n\nfunc gopExprs(vals []ast.Expr) []gopast.Expr {\n\tn := len(vals)\n\tif n == 0 {\n\t\treturn nil\n\t}\n\tret := make([]gopast.Expr, n)\n\tfor i, v := range vals {\n\t\tret[i] = gopExpr(v)\n\t}\n\treturn ret\n}\n\n// ----------------------------------------------------------------------------\n\nfunc gopFuncType(v *ast.FuncType) *gopast.FuncType {\n\treturn &gopast.FuncType{\n\t\tFunc:       v.Func,\n\t\tTypeParams: gopFieldList(typeparams.ForFuncType(v)),\n\t\tParams:     gopFieldList(v.Params),\n\t\tResults:    gopFieldList(v.Results),\n\t}\n}\n\nfunc gopType(v ast.Expr) gopast.Expr {\n\treturn gopExpr(v)\n}\n\nfunc gopBasicLit(v *ast.BasicLit) *gopast.BasicLit {\n\tif v == nil {\n\t\treturn nil\n\t}\n\treturn &gopast.BasicLit{\n\t\tValuePos: v.ValuePos,\n\t\tKind:     goptoken.Token(v.Kind),\n\t\tValue:    v.Value,\n\t}\n}\n\nfunc gopIdent(v *ast.Ident) *gopast.Ident {\n\tif v == nil {\n\t\treturn nil\n\t}\n\treturn &gopast.Ident{\n\t\tNamePos: v.NamePos,\n\t\tName:    v.Name,\n\t\tObj:     &gopast.Object{Data: v},\n\t}\n}\n\n// CheckIdent checks if an XGo ast.Ident is converted from a Go ast.Ident or not.\n// If it is, CheckIdent returns the original Go ast.Ident object.\nfunc CheckIdent(v *gopast.Ident) (id *ast.Ident, ok bool) {\n\tif o := v.Obj; o != nil && o.Kind == 0 && o.Data != nil {\n\t\tid, ok = o.Data.(*ast.Ident)\n\t}\n\treturn\n}\n\nfunc gopIdents(names []*ast.Ident) []*gopast.Ident {\n\tret := make([]*gopast.Ident, len(names))\n\tfor i, v := range names {\n\t\tret[i] = gopIdent(v)\n\t}\n\treturn ret\n}\n\n// ----------------------------------------------------------------------------\n\nfunc gopField(v *ast.Field) *gopast.Field {\n\treturn &gopast.Field{\n\t\tNames: gopIdents(v.Names),\n\t\tType:  gopType(v.Type),\n\t\tTag:   gopBasicLit(v.Tag),\n\t}\n}\n\nfunc gopFieldList(v *ast.FieldList) *gopast.FieldList {\n\tif v == nil {\n\t\treturn nil\n\t}\n\tlist := make([]*gopast.Field, len(v.List))\n\tfor i, item := range v.List {\n\t\tlist[i] = gopField(item)\n\t}\n\treturn &gopast.FieldList{Opening: v.Opening, List: list, Closing: v.Closing}\n}\n\nfunc gopFuncDecl(v *ast.FuncDecl) *gopast.FuncDecl {\n\treturn &gopast.FuncDecl{\n\t\tDoc:  v.Doc,\n\t\tRecv: gopFieldList(v.Recv),\n\t\tName: gopIdent(v.Name),\n\t\tType: gopFuncType(v.Type),\n\t\tBody: &gopast.BlockStmt{}, // ignore function body\n\t}\n}\n\n// ----------------------------------------------------------------------------\n\nfunc gopImportSpec(spec *ast.ImportSpec) *gopast.ImportSpec {\n\treturn &gopast.ImportSpec{\n\t\tName:   gopIdent(spec.Name),\n\t\tPath:   gopBasicLit(spec.Path),\n\t\tEndPos: spec.EndPos,\n\t}\n}\n\nfunc gopTypeSpec(spec *ast.TypeSpec) *gopast.TypeSpec {\n\treturn &gopast.TypeSpec{\n\t\tName:       gopIdent(spec.Name),\n\t\tTypeParams: gopFieldList(typeparams.ForTypeSpec(spec)),\n\t\tAssign:     spec.Assign,\n\t\tType:       gopType(spec.Type),\n\t}\n}\n\nfunc gopValueSpec(spec *ast.ValueSpec) *gopast.ValueSpec {\n\treturn &gopast.ValueSpec{\n\t\tNames:  gopIdents(spec.Names),\n\t\tType:   gopType(spec.Type),\n\t\tValues: gopExprs(spec.Values),\n\t}\n}\n\nfunc gopGenDecl(v *ast.GenDecl) *gopast.GenDecl {\n\tspecs := make([]gopast.Spec, len(v.Specs))\n\tfor i, spec := range v.Specs {\n\t\tswitch v.Tok {\n\t\tcase token.IMPORT:\n\t\t\tspecs[i] = gopImportSpec(spec.(*ast.ImportSpec))\n\t\tcase token.TYPE:\n\t\t\tspecs[i] = gopTypeSpec(spec.(*ast.TypeSpec))\n\t\tcase token.VAR, token.CONST:\n\t\t\tspecs[i] = gopValueSpec(spec.(*ast.ValueSpec))\n\t\tdefault:\n\t\t\tlog.Panicln(\"gopGenDecl: unknown spec -\", v.Tok)\n\t\t}\n\t}\n\treturn &gopast.GenDecl{\n\t\tDoc:    v.Doc,\n\t\tTokPos: v.TokPos,\n\t\tTok:    goptoken.Token(v.Tok),\n\t\tLparen: v.Lparen,\n\t\tSpecs:  specs,\n\t\tRparen: v.Rparen,\n\t}\n}\n\n// ----------------------------------------------------------------------------\n\nfunc gopDecl(decl ast.Decl) gopast.Decl {\n\tswitch v := decl.(type) {\n\tcase *ast.GenDecl:\n\t\treturn gopGenDecl(v)\n\tcase *ast.FuncDecl:\n\t\treturn gopFuncDecl(v)\n\t}\n\tlog.Panicln(\"gopDecl: unknown decl -\", reflect.TypeOf(decl))\n\treturn nil\n}\n\nfunc gopDecls(decls []ast.Decl) []gopast.Decl {\n\tret := make([]gopast.Decl, len(decls))\n\tfor i, decl := range decls {\n\t\tret[i] = gopDecl(decl)\n\t}\n\treturn ret\n}\n\n// ----------------------------------------------------------------------------\n\nconst (\n\tKeepFuncBody = 1 << iota\n\tKeepCgo\n)\n\n// ASTFile converts a Go ast.File into an XGo ast.File object.\nfunc ASTFile(f *ast.File, mode int) *gopast.File {\n\tif (mode & KeepFuncBody) != 0 {\n\t\tlog.Panicln(\"ASTFile: doesn't support keeping func body now\")\n\t}\n\tif (mode & KeepCgo) != 0 {\n\t\tlog.Panicln(\"ASTFile: doesn't support keeping cgo now\")\n\t}\n\treturn &gopast.File{\n\t\tDoc:     f.Doc,\n\t\tPackage: f.Package,\n\t\tName:    gopIdent(f.Name),\n\t\tDecls:   gopDecls(f.Decls),\n\t}\n}\n\n// ----------------------------------------------------------------------------\n"
  },
  {
    "path": "ast/fromgo/gopast_test.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage fromgo\n\nimport (\n\t\"bytes\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"testing\"\n\n\tgopast \"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/format\"\n)\n\nfunc testAST(t *testing.T, from, to string) {\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"foo.go\", from, 0)\n\tif err != nil {\n\t\tt.Fatal(\"parser.ParseFile:\", err)\n\t}\n\tgopf := ASTFile(f, 0)\n\tvar b bytes.Buffer\n\terr = format.Node(&b, fset, gopf)\n\tif err != nil {\n\t\tt.Fatal(\"format.Node:\", err)\n\t}\n\tresult := b.String()\n\tif to != result {\n\t\tt.Fatalf(\"\\nResult:\\n%s\\nExpected:\\n%s\\n\", result, to)\n\t}\n}\n\nfunc test(t *testing.T, src string) {\n\ttestAST(t, src, src)\n}\n\nfunc testPanic(t *testing.T, panicMsg string, doPanic func()) {\n\tt.Run(panicMsg, func(t *testing.T) {\n\t\tdefer func() {\n\t\t\tif e := recover(); e == nil {\n\t\t\t\tt.Fatal(\"testPanic: no error?\")\n\t\t\t} else if msg := e.(string); msg != panicMsg {\n\t\t\t\tt.Fatalf(\"\\nResult:\\n%s\\nExpected Panic:\\n%s\\n\", msg, panicMsg)\n\t\t\t}\n\t\t}()\n\t\tdoPanic()\n\t})\n}\n\nfunc TestErrASTFile(t *testing.T) {\n\ttestPanic(t, \"ASTFile: doesn't support keeping cgo now\\n\", func() {\n\t\tASTFile(nil, KeepCgo)\n\t})\n\ttestPanic(t, \"ASTFile: doesn't support keeping func body now\\n\", func() {\n\t\tASTFile(nil, KeepFuncBody)\n\t})\n}\n\nfunc TestErrDecl(t *testing.T) {\n\ttestPanic(t, \"gopDecl: unknown decl - <nil>\\n\", func() {\n\t\tgopDecl(nil)\n\t})\n\ttestPanic(t, \"gopGenDecl: unknown spec - ILLEGAL\\n\", func() {\n\t\tgopGenDecl(&ast.GenDecl{\n\t\t\tSpecs: []ast.Spec{nil},\n\t\t})\n\t})\n}\n\nfunc TestErrExpr(t *testing.T) {\n\ttestPanic(t, \"gopExpr: unknown expr - *ast.BadExpr\\n\", func() {\n\t\tgopExpr(&ast.BadExpr{})\n\t})\n}\n\nfunc TestBasic(t *testing.T) {\n\ttest(t, `package main\n\nimport \"fmt\"\n\ntype a struct {\n\tv   map[int]chan int\n\tarr *[2]func()\n\ti   interface{}\n}\n\nvar b = &a{\n\tarr: &[2]func(){\n\t\tnil,\n\t\tfunc() {},\n\t},\n}\n\nconst c = (10 + 20) * 2\n\nvar d = b.arr[1]\n\nvar e = b.arr[:1]\n\nvar f = a.i.(func() (int))()\n\nfunc foo(v ...interface{}) {}\n`)\n}\n\nfunc TestMethod(t *testing.T) {\n\ttest(t, `package main\n\ntype foo int\n\nfunc (a foo) Str() (string) {}\n`)\n}\n\nfunc TestCheckIdent(t *testing.T) {\n\tif _, ok := CheckIdent(&gopast.Ident{}); ok {\n\t\tt.Fatal(\"CheckIdent: found?\")\n\t}\n\tif _, ok := CheckIdent(&gopast.Ident{Obj: &gopast.Object{\n\t\tData: &ast.Ident{},\n\t}}); !ok {\n\t\tt.Fatal(\"CheckIdent: not found?\")\n\t}\n}\n"
  },
  {
    "path": "ast/fromgo/typeparams/typeparams_go117.go",
    "content": "//go:build !go1.18\n// +build !go1.18\n\npackage typeparams\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n)\n\nfunc unsupported() {\n\tpanic(\"type parameters are unsupported at this go version\")\n}\n\n// IndexListExpr is a placeholder type, as type parameters are not supported at\n// this Go version. Its methods panic on use.\ntype IndexListExpr struct {\n\tast.Expr\n\tX       ast.Expr   // expression\n\tLbrack  token.Pos  // position of \"[\"\n\tIndices []ast.Expr // index expressions\n\tRbrack  token.Pos  // position of \"]\"\n}\n\nfunc (*IndexListExpr) Pos() token.Pos { unsupported(); return token.NoPos }\nfunc (*IndexListExpr) End() token.Pos { unsupported(); return token.NoPos }\n\n// ForFuncType returns an empty field list, as type parameters are not\n// supported at this Go version.\nfunc ForFuncType(*ast.FuncType) *ast.FieldList {\n\treturn nil\n}\n\n// ForTypeSpec returns an empty field list, as type parameters are not\n// supported at this Go version.\nfunc ForTypeSpec(*ast.TypeSpec) *ast.FieldList {\n\treturn nil\n}\n"
  },
  {
    "path": "ast/fromgo/typeparams/typeparams_go118.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage typeparams\n\nimport \"go/ast\"\n\n// IndexListExpr is an alias for ast.IndexListExpr.\ntype IndexListExpr = ast.IndexListExpr\n\n// ForFuncType returns n.TypeParams.\nfunc ForFuncType(n *ast.FuncType) *ast.FieldList {\n\tif n == nil {\n\t\treturn nil\n\t}\n\treturn n.TypeParams\n}\n\n// ForTypeSpec returns n.TypeParams.\nfunc ForTypeSpec(n *ast.TypeSpec) *ast.FieldList {\n\tif n == nil {\n\t\treturn nil\n\t}\n\treturn n.TypeParams\n}\n"
  },
  {
    "path": "ast/fromgo/typeparams_test.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage fromgo\n\nimport \"testing\"\n\nfunc TestIndexListExpr(t *testing.T) {\n\ttest(t, `package main\n\ntype N1 = T1[int]\n\ntype N2 = T2[string, int]\n\nvar f = test[string, int](1, 2)\n`)\n}\n"
  },
  {
    "path": "ast/gopq/dom.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage gopq\n\nimport (\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype astPackages map[string]*ast.Package\n\nfunc (p astPackages) Pos() token.Pos { return token.NoPos }\nfunc (p astPackages) End() token.Pos { return token.NoPos }\n\nfunc (p astPackages) ForEach(filter func(node Node) error) error {\n\tfor _, pkg := range p {\n\t\tnode := astPackage{pkg}\n\t\tif err := filter(node); err == ErrBreak {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p astPackages) Obj() any {\n\treturn p\n}\n\n// -----------------------------------------------------------------------------\n\ntype astPackage struct {\n\t*ast.Package\n}\n\nfunc (p astPackage) ForEach(filter func(node Node) error) error {\n\tfor _, file := range p.Files {\n\t\tnode := astFile{file}\n\t\tif err := filter(node); err == ErrBreak {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p astPackage) Obj() any {\n\treturn p.Package\n}\n\n// -----------------------------------------------------------------------------\n\ntype astFile struct {\n\t*ast.File\n}\n\nfunc (p astFile) ForEach(filter func(node Node) error) error {\n\tfor _, decl := range p.Decls {\n\t\tnode := &astDecl{decl}\n\t\tif err := filter(node); err == ErrBreak {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p astFile) Obj() any {\n\treturn p.File\n}\n\n// -----------------------------------------------------------------------------\n\ntype astDecl struct {\n\tast.Decl\n}\n\nfunc (p *astDecl) ForEach(filter func(node Node) error) error {\n\tif decl, ok := p.Decl.(*ast.GenDecl); ok {\n\t\tfor _, spec := range decl.Specs {\n\t\t\tnode := &astSpec{spec}\n\t\t\tif err := filter(node); err == ErrBreak {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *astDecl) Obj() any {\n\treturn p.Decl\n}\n\n// -----------------------------------------------------------------------------\n\ntype astSpec struct {\n\tast.Spec\n}\n\nfunc (p *astSpec) ForEach(filter func(node Node) error) error {\n\treturn nil\n}\n\nfunc (p *astSpec) Obj() any {\n\treturn p.Spec\n}\n\n// -----------------------------------------------------------------------------\n\nfunc visitStmt(stmt ast.Stmt, filter func(node Node) error) error {\n\tif stmt != nil {\n\t\treturn filter(&astStmt{stmt})\n\t}\n\treturn nil\n}\n\ntype astStmt struct {\n\tast.Stmt\n}\n\nfunc (p *astStmt) ForEach(filter func(node Node) error) error {\n\tswitch stmt := p.Stmt.(type) {\n\tcase *ast.IfStmt:\n\t\tif err := visitStmt(stmt.Init, filter); err == ErrBreak {\n\t\t\treturn err\n\t\t}\n\t\tif err := filter(&astStmt{stmt.Body}); err == ErrBreak {\n\t\t\treturn err\n\t\t}\n\t\tif err := visitStmt(stmt.Else, filter); err == ErrBreak {\n\t\t\treturn err\n\t\t}\n\tcase *ast.BlockStmt:\n\t\tfor _, stmt := range stmt.List {\n\t\t\tnode := &astStmt{stmt}\n\t\t\tif err := filter(node); err == ErrBreak {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\t// TODO(xsw): visit other stmts\n\treturn nil\n}\n\nfunc (p *astStmt) Obj() any {\n\treturn p.Stmt\n}\n\n// -----------------------------------------------------------------------------\n\ntype astExpr struct {\n\tast.Expr\n}\n\nfunc (p *astExpr) ForEach(filter func(node Node) error) error {\n\treturn nil\n}\n\nfunc (p *astExpr) Obj() any {\n\treturn p.Expr\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "ast/gopq/gopq.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage gopq\n\nimport (\n\t\"errors\"\n\t\"io/fs\"\n\t\"syscall\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/parser/fsx\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// -----------------------------------------------------------------------------\n\nconst (\n\tGopPackage = true\n)\n\nvar (\n\t// ErrBreak - break\n\tErrBreak = syscall.ELOOP\n\n\t// ErrNotFound - not found\n\tErrNotFound = errors.New(\"not found\")\n\n\t// ErrTooManyNodes - too may nodes\n\tErrTooManyNodes = errors.New(\"too many nodes\")\n\n\t// ErrUnexpectedNode - unexpected node\n\tErrUnexpectedNode = errors.New(\"unexpected node\")\n)\n\n// Node - node interface\ntype Node interface {\n\tast.Node\n\tForEach(filter func(node Node) error) error\n\tObj() any\n}\n\n// NodeEnum - node enumerator\ntype NodeEnum interface {\n\tForEach(filter func(node Node) error) error\n}\n\n// NodeSet - node set\ntype NodeSet struct {\n\tData NodeEnum\n\tErr  error\n}\n\n// FromFile calls ParseFile for a single file and returns *ast.File node set.\nfunc FromFile(fset *token.FileSet, filename string, src any, mode parser.Mode) (doc NodeSet, err error) {\n\tfile, err := parser.ParseFile(fset, filename, src, mode)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn NodeSet{Data: &oneNode{astFile{file}}}, nil\n}\n\n// FromFSFile calls ParseFSFile for a single file and returns *ast.File node set.\nfunc FromFSFile(\n\tfset *token.FileSet, fs fsx.FileSystem,\n\tfilename string, src any, mode parser.Mode) (doc NodeSet, err error) {\n\tfile, err := parser.ParseFSFile(fset, fs, filename, src, mode)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn NodeSet{Data: &oneNode{astFile{file}}}, nil\n}\n\n// FromDir calls ParseFile for all XGo files in the directory specified by path\n// and returns a map of package name -> package AST with all the packages found.\n//\n// If filter != nil, only the XGo files with fs.FileInfo entries passing through\n// the filter are considered. The mode bits are passed to ParseFile unchanged.\n// Position information is recorded in fset, which must not be nil.\n//\n// If the directory couldn't be read, a nil map and the respective error are\n// returned. If a parse error occurred, a non-nil but incomplete map and the\n// first error encountered are returned.\nfunc FromDir(\n\tfset *token.FileSet, path string,\n\tfilter func(fs.FileInfo) bool, mode parser.Mode) (doc NodeSet, err error) {\n\n\tpkgs, err := parser.ParseDir(fset, path, filter, mode)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn NodeSet{Data: &oneNode{astPackages(pkgs)}}, nil\n}\n\n// FromFSDir calls ParseFile for all XGo files in the directory specified by path\n// and returns a map of package name -> package AST with all the packages found.\n//\n// If filter != nil, only the XGo files with fs.FileInfo entries passing through\n// the filter are considered. The mode bits are passed to ParseFile unchanged.\n// Position information is recorded in fset, which must not be nil.\n//\n// If the directory couldn't be read, a nil map and the respective error are\n// returned. If a parse error occurred, a non-nil but incomplete map and the\n// first error encountered are returned.\nfunc FromFSDir(\n\tfset *token.FileSet, fs parser.FileSystem, path string,\n\tfilter func(fs.FileInfo) bool, mode parser.Mode) (doc NodeSet, err error) {\n\n\tpkgs, err := parser.ParseFSDir(fset, fs, path, parser.Config{Filter: filter, Mode: mode})\n\tif err != nil {\n\t\treturn\n\t}\n\treturn NodeSet{Data: &oneNode{astPackages(pkgs)}}, nil\n}\n\n// Ok returns if node set is valid or not.\nfunc (p NodeSet) Ok() bool {\n\treturn p.Err == nil\n}\n\n// -----------------------------------------------------------------------------\n\n// FuncDecl returns *ast.FuncDecl node set.\nfunc (p NodeSet) FuncDecl__0() NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astDecl); ok {\n\t\t\t_, ok = node.Decl.(*ast.FuncDecl)\n\t\t\treturn ok\n\t\t}\n\t\treturn false\n\t})\n}\n\n// FuncDecl returns *ast.FuncDecl node set.\nfunc (p NodeSet) FuncDecl__1(name string) NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astDecl); ok {\n\t\t\tif fn, ok := node.Decl.(*ast.FuncDecl); ok {\n\t\t\t\treturn fn.Name.Name == name\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n}\n\n// GenDecl returns *ast.GenDecl node set.\nfunc (p NodeSet) GenDecl__0(tok token.Token) NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astDecl); ok {\n\t\t\tif decl, ok := node.Decl.(*ast.GenDecl); ok {\n\t\t\t\treturn decl.Tok == tok\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n}\n\n// TypeSpec returns *ast.TypeSpec node set.\nfunc (p NodeSet) TypeSpec() NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astSpec); ok {\n\t\t\t_, ok = node.Spec.(*ast.TypeSpec)\n\t\t\treturn ok\n\t\t}\n\t\treturn false\n\t})\n}\n\n// ValueSpec returns *ast.ValueSpec node set.\nfunc (p NodeSet) ValueSpec() NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astSpec); ok {\n\t\t\t_, ok = node.Spec.(*ast.ValueSpec)\n\t\t\treturn ok\n\t\t}\n\t\treturn false\n\t})\n}\n\n// ImportSpec returns *ast.ImportSpec node set.\nfunc (p NodeSet) ImportSpec() NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astSpec); ok {\n\t\t\t_, ok = node.Spec.(*ast.ImportSpec)\n\t\t\treturn ok\n\t\t}\n\t\treturn false\n\t})\n}\n\n// ExprStmt returns *ast.ExprStmt node set.\nfunc (p NodeSet) ExprStmt() NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astStmt); ok {\n\t\t\t_, ok = node.Stmt.(*ast.ExprStmt)\n\t\t\treturn ok\n\t\t}\n\t\treturn false\n\t})\n}\n\n// AssignStmt returns *ast.AssignStmt node set.\nfunc (p NodeSet) AssignStmt() NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astStmt); ok {\n\t\t\t_, ok = node.Stmt.(*ast.AssignStmt)\n\t\t\treturn ok\n\t\t}\n\t\treturn false\n\t})\n}\n\n// CallExpr returns *ast.CallExpr node set.\nfunc (p NodeSet) CallExpr__0() NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astExpr); ok {\n\t\t\t_, ok = node.Expr.(*ast.CallExpr)\n\t\t\treturn ok\n\t\t}\n\t\treturn false\n\t})\n}\n\n// CallExpr returns *ast.CallExpr node set.\nfunc (p NodeSet) CallExpr__1(name string) NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astExpr); ok {\n\t\t\tif expr, ok := node.Expr.(*ast.CallExpr); ok {\n\t\t\t\treturn getName(expr.Fun, true) == name\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n}\n\n// CompositeLit returns *ast.CompositeLit node set.\nfunc (p NodeSet) CompositeLit__0() NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astExpr); ok {\n\t\t\t_, ok = node.Expr.(*ast.CompositeLit)\n\t\t\treturn ok\n\t\t}\n\t\treturn false\n\t})\n}\n\n// CompositeLit returns *ast.CompositeLit node set.\nfunc (p NodeSet) CompositeLit__1(name string) NodeSet {\n\treturn p.Match(func(node Node) bool {\n\t\tif node, ok := node.(*astExpr); ok {\n\t\t\tif lit, ok := node.Expr.(*ast.CompositeLit); ok && lit.Type != nil {\n\t\t\t\treturn getName(lit.Type, true) == name\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n}\n\n// -----------------------------------------------------------------------------\n\nfunc (p NodeSet) Gop_Enum(callback func(node NodeSet)) {\n\tif p.Err == nil {\n\t\tp.Data.ForEach(func(node Node) error {\n\t\t\tt := NodeSet{Data: &oneNode{node}}\n\t\t\tcallback(t)\n\t\t\treturn nil\n\t\t})\n\t}\n}\n\nfunc (p NodeSet) ForEach(callback func(node NodeSet)) {\n\tp.Gop_Enum(callback)\n}\n\n// -----------------------------------------------------------------------------\n\ntype oneNode struct {\n\tNode\n}\n\nfunc (p *oneNode) ForEach(filter func(node Node) error) error {\n\treturn filter(p.Node)\n}\n\nfunc (p *oneNode) Cached() int {\n\treturn 1\n}\n\n// One returns the first node as a node set.\nfunc (p NodeSet) One() NodeSet {\n\tif _, ok := p.Data.(*oneNode); ok {\n\t\treturn p\n\t}\n\tnode, err := p.CollectOne__0()\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn NodeSet{Data: &oneNode{node}}\n}\n\n// One creates a node set that only contains a signle node.\nfunc One__0(node Node) NodeSet {\n\treturn NodeSet{Data: &oneNode{node}}\n}\n\n// One creates a node set that only contains a signle node.\nfunc One__1(f *ast.File) NodeSet {\n\treturn NodeSet{Data: &oneNode{astFile{f}}}\n}\n\n// One creates a node set that only contains a signle node.\nfunc One__2(pkg *ast.Package) NodeSet {\n\treturn NodeSet{Data: &oneNode{astPackage{pkg}}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype fixNodes struct {\n\tnodes []Node\n}\n\nfunc (p *fixNodes) ForEach(filter func(node Node) error) error {\n\tfor _, node := range p.nodes {\n\t\tif filter(node) == ErrBreak {\n\t\t\treturn ErrBreak\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *fixNodes) Cached() int {\n\treturn len(p.nodes)\n}\n\n// Nodes creates a fixed node set.\nfunc Nodes(nodes ...Node) NodeSet {\n\treturn NodeSet{Data: &fixNodes{nodes}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype cached interface {\n\tCached() int\n}\n\n// Cache caches node set.\nfunc (p NodeSet) Cache() NodeSet {\n\tif _, ok := p.Data.(cached); ok {\n\t\treturn p\n\t}\n\tnodes, err := p.Collect()\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn NodeSet{Data: &fixNodes{nodes}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype anyNodes struct {\n\tdata NodeEnum\n}\n\nfunc (p *anyNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\treturn anyForEach(node, filter)\n\t})\n}\n\nfunc anyForEach(p Node, filter func(node Node) error) error {\n\tif err := filter(p); err == ErrBreak {\n\t\treturn err\n\t}\n\treturn p.ForEach(func(node Node) error {\n\t\treturn anyForEach(node, filter)\n\t})\n}\n\n// Any returns deeply visiting node set.\nfunc (p NodeSet) Any() (ret NodeSet) {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &anyNodes{p.Data}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype childNodes struct {\n\tdata NodeEnum\n}\n\nfunc (p *childNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\treturn node.ForEach(filter)\n\t})\n}\n\n// Child returns child node set.\nfunc (p NodeSet) Child() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &childNodes{p.Data}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype bodyNodes struct {\n\tdata NodeEnum\n}\n\nfunc (p *bodyNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\tswitch node := node.(type) {\n\t\tcase *astDecl:\n\t\t\tif fn, ok := node.Decl.(*ast.FuncDecl); ok && fn.Body != nil {\n\t\t\t\treturn filter(&astStmt{fn.Body})\n\t\t\t}\n\t\t}\n\t\treturn ErrNotFound\n\t})\n}\n\n// Body returns body node set.\nfunc (p NodeSet) Body() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &bodyNodes{p.Data}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype xNodes struct {\n\tdata NodeEnum\n}\n\nfunc (p *xNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\tswitch node := node.(type) {\n\t\tcase *astStmt:\n\t\t\tswitch stmt := node.Stmt.(type) {\n\t\t\tcase *ast.ExprStmt:\n\t\t\t\treturn filter(&astExpr{stmt.X})\n\t\t\t}\n\t\tcase *astExpr:\n\t\t\tswitch expr := node.Expr.(type) {\n\t\t\tcase *ast.SelectorExpr:\n\t\t\t\treturn filter(&astExpr{expr.X})\n\t\t\tcase *ast.UnaryExpr:\n\t\t\t\treturn filter(&astExpr{expr.X})\n\t\t\t}\n\t\t}\n\t\treturn ErrNotFound\n\t})\n}\n\n// X returns x node set.\nfunc (p NodeSet) X() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &xNodes{p.Data}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype funNodes struct {\n\tdata NodeEnum\n}\n\nfunc (p *funNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\tswitch node := node.(type) {\n\t\tcase *astExpr:\n\t\t\tswitch expr := node.Expr.(type) {\n\t\t\tcase *ast.CallExpr:\n\t\t\t\treturn filter(&astExpr{expr.Fun})\n\t\t\t}\n\t\t}\n\t\treturn ErrNotFound\n\t})\n}\n\n// Fun returns fun node set.\nfunc (p NodeSet) Fun() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &funNodes{p.Data}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype argNodes struct {\n\tdata NodeEnum\n\ti    int\n\tvarg bool\n}\n\nfunc (p *argNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\tswitch node := node.(type) {\n\t\tcase *astExpr:\n\t\t\tswitch expr := node.Expr.(type) {\n\t\t\tcase *ast.CallExpr:\n\t\t\t\targs := expr.Args\n\t\t\t\tif p.varg {\n\t\t\t\t\tfor i, n := p.i, len(args); i < n; i++ {\n\t\t\t\t\t\tif err := filter(&astExpr{args[i]}); err == ErrBreak {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn nil\n\t\t\t\t} else {\n\t\t\t\t\treturn filter(&astExpr{args[p.i]})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn ErrNotFound\n\t})\n}\n\n// Arg returns args[i] node set.\nfunc (p NodeSet) Arg(i int) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &argNodes{p.Data, i, false}}\n}\n\n// Varg returns args[i:] node set.\nfunc (p NodeSet) Varg(i int) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &argNodes{p.Data, i, true}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype ieltNodes struct {\n\tdata NodeEnum\n\ti    int\n}\n\nfunc (p *ieltNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\tswitch node := node.(type) {\n\t\tcase *astExpr:\n\t\t\tswitch expr := node.Expr.(type) {\n\t\t\tcase *ast.CompositeLit:\n\t\t\t\tif i := p.i; i == -1 {\n\t\t\t\t\tfor _, elt := range expr.Elts {\n\t\t\t\t\t\tif err := filter(&astExpr{elt}); err == ErrBreak {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn filter(&astExpr{expr.Elts[i]})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn ErrNotFound\n\t})\n}\n\n// Elt returns elts[i] node set.\nfunc (p NodeSet) Elt__0(i int) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &ieltNodes{p.Data, i}}\n}\n\n// Elt returns elts[:] node set.\nfunc (p NodeSet) Elt__1() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &ieltNodes{p.Data, -1}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype eltNodes struct {\n\tdata NodeEnum\n\tname string\n}\n\nfunc (p *eltNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\tswitch node := node.(type) {\n\t\tcase *astExpr:\n\t\t\tswitch expr := node.Expr.(type) {\n\t\t\tcase *ast.CompositeLit:\n\t\t\t\tif elt, ok := getElt(expr.Elts, p.name); ok {\n\t\t\t\t\treturn filter(&astExpr{elt})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn ErrNotFound\n\t})\n}\n\n// Elt returns elts[name] node set.\nfunc (p NodeSet) Elt__2(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &eltNodes{p.Data, name}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype rhsNodes struct {\n\tdata NodeEnum\n\ti    int\n}\n\nfunc (p *rhsNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\tswitch node := node.(type) {\n\t\tcase *astStmt:\n\t\t\tswitch stmt := node.Stmt.(type) {\n\t\t\tcase *ast.AssignStmt:\n\t\t\t\treturn filter(&astExpr{stmt.Rhs[p.i]})\n\t\t\t}\n\t\t}\n\t\treturn ErrNotFound\n\t})\n}\n\n// Rhs returns rhs[i] node set.\nfunc (p NodeSet) Rhs(i int) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &rhsNodes{p.Data, i}}\n}\n\n// -----------------------------------------------------------------------------\n\ntype matchedNodes struct {\n\tdata  NodeEnum\n\tmatch func(node Node) bool\n}\n\nfunc (p *matchedNodes) ForEach(filter func(node Node) error) error {\n\treturn p.data.ForEach(func(node Node) error {\n\t\tif p.match(node) {\n\t\t\treturn filter(node)\n\t\t}\n\t\treturn ErrNotFound\n\t})\n}\n\n// Match filters the node set.\nfunc (p NodeSet) Match(match func(node Node) bool) (ret NodeSet) {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{Data: &matchedNodes{p.Data, match}}\n}\n\n// -----------------------------------------------------------------------------\n\n// Name returns names of the node set.\nfunc (p NodeSet) Name() []string {\n\treturn p.ToString(NameOf)\n}\n\n// ToString returns string values of the node set.\nfunc (p NodeSet) ToString(str func(node Node) string) (items []string) {\n\tif p.Err != nil {\n\t\treturn nil\n\t}\n\tp.Data.ForEach(func(node Node) error {\n\t\titems = append(items, str(node))\n\t\treturn nil\n\t})\n\treturn\n}\n\n// Collect collects all nodes of the node set.\nfunc (p NodeSet) Collect() (items []Node, err error) {\n\tif p.Err != nil {\n\t\treturn nil, p.Err\n\t}\n\tp.Data.ForEach(func(node Node) error {\n\t\titems = append(items, node)\n\t\treturn nil\n\t})\n\treturn\n}\n\n// CollectOne collects one node of a node set.\n// If exactly is true, it returns ErrTooManyNodes when node set is more than one.\nfunc (p NodeSet) CollectOne__1(exactly bool) (item Node, err error) {\n\tif p.Err != nil {\n\t\treturn nil, p.Err\n\t}\n\terr = ErrNotFound\n\tif exactly {\n\t\tp.Data.ForEach(func(node Node) error {\n\t\t\tif err == ErrNotFound {\n\t\t\t\titem, err = node, nil\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\terr = ErrTooManyNodes\n\t\t\treturn ErrBreak\n\t\t})\n\t} else {\n\t\tp.Data.ForEach(func(node Node) error {\n\t\t\titem, err = node, nil\n\t\t\treturn ErrBreak\n\t\t})\n\t}\n\treturn\n}\n\n// CollectOne returns the first node.\nfunc (p NodeSet) CollectOne__0() (item Node, err error) {\n\treturn p.CollectOne__1(false)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "ast/gopq/helper.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage gopq\n\nimport (\n\t\"strconv\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Funcs returns all `*ast.FuncDecl` nodes.\nfunc (p NodeSet) Funcs() NodeSet {\n\treturn p.Any().FuncDecl__0().Cache()\n}\n\n// -----------------------------------------------------------------------------\n\nfunc (p NodeSet) UnquotedString__1(exactly bool) (ret string, err error) {\n\titem, err := p.CollectOne__1(exactly)\n\tif err != nil {\n\t\treturn\n\t}\n\tif lit, ok := item.Obj().(*ast.BasicLit); ok && lit.Kind == token.STRING {\n\t\treturn strconv.Unquote(lit.Value)\n\t}\n\treturn \"\", ErrUnexpectedNode\n}\n\nfunc (p NodeSet) UnquotedString__0() (ret string, err error) {\n\treturn p.UnquotedString__1(false)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc (p NodeSet) UnquotedStringElts__1(exactly bool) (ret []string, err error) {\n\titem, err := p.CollectOne__1(exactly)\n\tif err != nil {\n\t\treturn\n\t}\n\tif lit, ok := item.Obj().(*ast.CompositeLit); ok {\n\t\tret = make([]string, len(lit.Elts))\n\t\tfor i, elt := range lit.Elts {\n\t\t\tif lit, ok := elt.(*ast.BasicLit); ok && lit.Kind == token.STRING {\n\t\t\t\tif ret[i], err = strconv.Unquote(lit.Value); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn nil, ErrUnexpectedNode\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\treturn nil, ErrUnexpectedNode\n}\n\nfunc (p NodeSet) UnquotedStringElts__0() (ret []string, err error) {\n\treturn p.UnquotedStringElts__1(false)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc (p NodeSet) Positions__1(exactly bool) (ret []token.Pos, err error) {\n\titem, err := p.CollectOne__1(exactly)\n\tif err != nil {\n\t\treturn\n\t}\n\tswitch o := item.Obj().(type) {\n\tcase *ast.CompositeLit:\n\t\treturn []token.Pos{o.Pos(), o.End(), o.Lbrace, o.Rbrace}, nil\n\tcase ast.Node:\n\t\treturn []token.Pos{o.Pos(), o.End()}, nil\n\t}\n\treturn nil, ErrUnexpectedNode\n}\n\nfunc (p NodeSet) Positions__0() (ret []token.Pos, err error) {\n\treturn p.Positions__1(false)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc (p NodeSet) EltLen__1(exactly bool) (ret int, err error) {\n\titem, err := p.CollectOne__1(exactly)\n\tif err != nil {\n\t\treturn\n\t}\n\tif lit, ok := item.Obj().(*ast.CompositeLit); ok {\n\t\treturn len(lit.Elts), nil\n\t}\n\treturn 0, ErrUnexpectedNode\n}\n\nfunc (p NodeSet) EltLen__0() (ret int, err error) {\n\treturn p.EltLen__1(false)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc (p NodeSet) Ident__1(exactly bool) (ret string, err error) {\n\titem, err := p.CollectOne__1(exactly)\n\tif err != nil {\n\t\treturn\n\t}\n\tif ident, ok := item.Obj().(*ast.Ident); ok {\n\t\treturn ident.Name, nil\n\t}\n\treturn \"\", ErrUnexpectedNode\n}\n\nfunc (p NodeSet) Ident__0() (ret string, err error) {\n\treturn p.Ident__1(false)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc getElt(elts []ast.Expr, name string) (ast.Expr, bool) {\n\tfor _, elt := range elts {\n\t\tif kv, ok := elt.(*ast.KeyValueExpr); ok {\n\t\t\tif ident, ok := kv.Key.(*ast.Ident); ok && ident.Name == name {\n\t\t\t\treturn kv.Value, true\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, false\n}\n\n// -----------------------------------------------------------------------------\n\n// NameOf returns name of an ast node.\nfunc NameOf(node Node) string {\n\treturn getName(node.Obj(), false)\n}\n\nfunc getName(v any, useEmpty bool) string {\n\tswitch v := v.(type) {\n\tcase *ast.FuncDecl:\n\t\treturn v.Name.Name\n\tcase *ast.ImportSpec:\n\t\tn := v.Name\n\t\tif n == nil {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn n.Name\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.SelectorExpr:\n\t\treturn getName(v.X, useEmpty) + \".\" + v.Sel.Name\n\t}\n\tif useEmpty {\n\t\treturn \"\"\n\t}\n\tpanic(\"node doesn't contain the `name` property\")\n}\n\n// -----------------------------------------------------------------------------\n\nfunc CodeOf(fset *token.FileSet, f *ast.File, start, end token.Pos) string {\n\tpos := fset.Position(start)\n\tn := int(end - start)\n\treturn string(f.Code[pos.Offset : pos.Offset+n])\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "ast/goptest/gopq.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage goptest\n\nimport (\n\t\"go/token\"\n\n\t\"github.com/goplus/xgo/ast/gopq\"\n\t\"github.com/goplus/xgo/parser/fsx/memfs\"\n)\n\nconst (\n\tGopPackage = \"github.com/goplus/xgo/ast/gopq\"\n)\n\n// -----------------------------------------------------------------------------\n\n// New creates a nodeset object that represents an XGo dom tree.\nfunc New(script string) (gopq.NodeSet, error) {\n\tfset := token.NewFileSet()\n\tfs := memfs.SingleFile(\"/foo\", \"bar.xgo\", script)\n\treturn gopq.FromFSDir(fset, fs, \"/foo\", nil, 0)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "ast/import.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ast\n\nimport (\n\t\"sort\"\n\t\"strconv\"\n\n\t\"github.com/goplus/xgo/token\"\n)\n\n// SortImports sorts runs of consecutive import lines in import blocks in f.\n// It also removes duplicate imports when it is possible to do so without data loss.\nfunc SortImports(fset *token.FileSet, f *File) {\n\tfor _, d := range f.Decls {\n\t\td, ok := d.(*GenDecl)\n\t\tif !ok || d.Tok != token.IMPORT {\n\t\t\t// Not an import declaration, so we're done.\n\t\t\t// Imports are always first.\n\t\t\tbreak\n\t\t}\n\n\t\tif !d.Lparen.IsValid() {\n\t\t\t// Not a block: sorted by default.\n\t\t\tcontinue\n\t\t}\n\n\t\t// Identify and sort runs of specs on successive lines.\n\t\ti := 0\n\t\tspecs := d.Specs[:0]\n\t\tfor j, s := range d.Specs {\n\t\t\tif j > i && lineAt(fset, s.Pos()) > 1+lineAt(fset, d.Specs[j-1].End()) {\n\t\t\t\t// j begins a new run. End this one.\n\t\t\t\tspecs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)\n\t\t\t\ti = j\n\t\t\t}\n\t\t}\n\t\tspecs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)\n\t\td.Specs = specs\n\n\t\t// Deduping can leave a blank line before the rparen; clean that up.\n\t\tif len(d.Specs) > 0 {\n\t\t\tlastSpec := d.Specs[len(d.Specs)-1]\n\t\t\tlastLine := lineAt(fset, lastSpec.Pos())\n\t\t\trParenLine := lineAt(fset, d.Rparen)\n\t\t\tfor rParenLine > lastLine+1 {\n\t\t\t\trParenLine--\n\t\t\t\tfset.File(d.Rparen).MergeLine(rParenLine)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc lineAt(fset *token.FileSet, pos token.Pos) int {\n\treturn fset.PositionFor(pos, false).Line\n}\n\nfunc importPath(s Spec) string {\n\tt, err := strconv.Unquote(s.(*ImportSpec).Path.Value)\n\tif err == nil {\n\t\treturn t\n\t}\n\treturn \"\"\n}\n\nfunc importName(s Spec) string {\n\tn := s.(*ImportSpec).Name\n\tif n == nil {\n\t\treturn \"\"\n\t}\n\treturn n.Name\n}\n\nfunc importComment(s Spec) string {\n\tc := s.(*ImportSpec).Comment\n\tif c == nil {\n\t\treturn \"\"\n\t}\n\treturn c.Text()\n}\n\n// collapse indicates whether prev may be removed, leaving only next.\nfunc collapse(prev, next Spec) bool {\n\tif importPath(next) != importPath(prev) || importName(next) != importName(prev) {\n\t\treturn false\n\t}\n\treturn prev.(*ImportSpec).Comment == nil\n}\n\ntype posSpan struct {\n\tStart token.Pos\n\tEnd   token.Pos\n}\n\ntype cgPos struct {\n\tleft bool // true if comment is to the left of the spec, false otherwise.\n\tcg   *CommentGroup\n}\n\nfunc sortSpecs(fset *token.FileSet, f *File, specs []Spec) []Spec {\n\t// Can't short-circuit here even if specs are already sorted,\n\t// since they might yet need deduplication.\n\t// A lone import, however, may be safely ignored.\n\tif len(specs) <= 1 {\n\t\treturn specs\n\t}\n\n\t// Record positions for specs.\n\tpos := make([]posSpan, len(specs))\n\tfor i, s := range specs {\n\t\tpos[i] = posSpan{s.Pos(), s.End()}\n\t}\n\n\t// Identify comments in this range.\n\tbegSpecs := pos[0].Start\n\tendSpecs := pos[len(pos)-1].End\n\tbeg := fset.File(begSpecs).LineStart(lineAt(fset, begSpecs))\n\tendLine := lineAt(fset, endSpecs)\n\tendFile := fset.File(endSpecs)\n\tvar end token.Pos\n\tif endLine == endFile.LineCount() {\n\t\tend = endSpecs\n\t} else {\n\t\tend = endFile.LineStart(endLine + 1) // beginning of next line\n\t}\n\tfirst := len(f.Comments)\n\tlast := -1\n\tfor i, g := range f.Comments {\n\t\tif g.End() >= end {\n\t\t\tbreak\n\t\t}\n\t\t// g.End() < end\n\t\tif beg <= g.Pos() {\n\t\t\t// comment is within the range [beg, end[ of import declarations\n\t\t\tif i < first {\n\t\t\t\tfirst = i\n\t\t\t}\n\t\t\tif i > last {\n\t\t\t\tlast = i\n\t\t\t}\n\t\t}\n\t}\n\n\tvar comments []*CommentGroup\n\tif last >= 0 {\n\t\tcomments = f.Comments[first : last+1]\n\t}\n\n\t// Assign each comment to the import spec on the same line.\n\timportComments := map[*ImportSpec][]cgPos{}\n\tspecIndex := 0\n\tfor _, g := range comments {\n\t\tfor specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {\n\t\t\tspecIndex++\n\t\t}\n\t\tvar left bool\n\t\t// A block comment can appear before the first import spec.\n\t\tif specIndex == 0 && pos[specIndex].Start > g.Pos() {\n\t\t\tleft = true\n\t\t} else if specIndex+1 < len(specs) && // Or it can appear on the left of an import spec.\n\t\t\tlineAt(fset, pos[specIndex].Start)+1 == lineAt(fset, g.Pos()) {\n\t\t\tspecIndex++\n\t\t\tleft = true\n\t\t}\n\t\ts := specs[specIndex].(*ImportSpec)\n\t\timportComments[s] = append(importComments[s], cgPos{left: left, cg: g})\n\t}\n\n\t// Sort the import specs by import path.\n\t// Remove duplicates, when possible without data loss.\n\t// Reassign the import paths to have the same position sequence.\n\t// Reassign each comment to the spec on the same line.\n\t// Sort the comments by new position.\n\tsort.Slice(specs, func(i, j int) bool {\n\t\tipath := importPath(specs[i])\n\t\tjpath := importPath(specs[j])\n\t\tif ipath != jpath {\n\t\t\treturn ipath < jpath\n\t\t}\n\t\tiname := importName(specs[i])\n\t\tjname := importName(specs[j])\n\t\tif iname != jname {\n\t\t\treturn iname < jname\n\t\t}\n\t\treturn importComment(specs[i]) < importComment(specs[j])\n\t})\n\n\t// Dedup. Thanks to our sorting, we can just consider\n\t// adjacent pairs of imports.\n\tdeduped := specs[:0]\n\tfor i, s := range specs {\n\t\tif i == len(specs)-1 || !collapse(s, specs[i+1]) {\n\t\t\tdeduped = append(deduped, s)\n\t\t} else {\n\t\t\tp := s.Pos()\n\t\t\tfset.File(p).MergeLine(lineAt(fset, p))\n\t\t}\n\t}\n\tspecs = deduped\n\n\t// Fix up comment positions\n\tfor i, s := range specs {\n\t\ts := s.(*ImportSpec)\n\t\tif s.Name != nil {\n\t\t\ts.Name.NamePos = pos[i].Start\n\t\t}\n\t\ts.Path.ValuePos = pos[i].Start\n\t\ts.EndPos = pos[i].End\n\t\tfor _, g := range importComments[s] {\n\t\t\tfor _, c := range g.cg.List {\n\t\t\t\tif g.left {\n\t\t\t\t\tc.Slash = pos[i].Start - 1\n\t\t\t\t} else {\n\t\t\t\t\t// An import spec can have both block comment and a line comment\n\t\t\t\t\t// to its right. In that case, both of them will have the same pos.\n\t\t\t\t\t// But while formatting the AST, the line comment gets moved to\n\t\t\t\t\t// after the block comment.\n\t\t\t\t\tc.Slash = pos[i].End\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tsort.Slice(comments, func(i, j int) bool {\n\t\treturn comments[i].Pos() < comments[j].Pos()\n\t})\n\n\treturn specs\n}\n"
  },
  {
    "path": "ast/mod/deps.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage mod\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\tgoast \"go/ast\"\n\tgotoken \"go/token\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// ----------------------------------------------------------------------------\n\ntype Deps struct {\n\tHandlePkg func(pkgPath string)\n}\n\nfunc (p Deps) Load(pkg *ast.Package, withXgoStd bool) {\n\tfor _, f := range pkg.Files {\n\t\tp.LoadFile(f, withXgoStd)\n\t}\n\tfor _, f := range pkg.GoFiles {\n\t\tp.LoadGoFile(f)\n\t}\n}\n\nfunc (p Deps) LoadGoFile(f *goast.File) {\n\tfor _, imp := range f.Imports {\n\t\tpath := imp.Path\n\t\tif path.Kind == gotoken.STRING {\n\t\t\tif s, err := strconv.Unquote(path.Value); err == nil {\n\t\t\t\tif s == \"C\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tp.HandlePkg(s)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (p Deps) LoadFile(f *ast.File, withXgoStd bool) {\n\tfor _, imp := range f.Imports {\n\t\tpath := imp.Path\n\t\tif path.Kind == token.STRING {\n\t\t\tif s, err := strconv.Unquote(path.Value); err == nil {\n\t\t\t\tp.xgoPkgPath(s, withXgoStd)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (p Deps) xgoPkgPath(s string, withXgoStd bool) {\n\tif strings.HasPrefix(s, \"xgo/\") || strings.HasPrefix(s, \"gop/\") {\n\t\tif !withXgoStd {\n\t\t\treturn\n\t\t}\n\t\ts = \"github.com/goplus/xgo/\" + s[4:]\n\t} else if strings.HasPrefix(s, \"C\") {\n\t\tif len(s) == 1 {\n\t\t\ts = \"github.com/goplus/libc\"\n\t\t} else if s[1] == '/' {\n\t\t\ts = s[2:]\n\t\t\tif strings.IndexByte(s, '/') < 0 {\n\t\t\t\ts = \"github.com/goplus/\" + s\n\t\t\t}\n\t\t}\n\t}\n\tp.HandlePkg(s)\n}\n\n// ----------------------------------------------------------------------------\n"
  },
  {
    "path": "ast/print.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This file contains printing support for ASTs.\n\npackage ast\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"reflect\"\n\n\t\"github.com/goplus/xgo/token\"\n)\n\n// A FieldFilter may be provided to Fprint to control the output.\ntype FieldFilter func(name string, value reflect.Value) bool\n\n// NotNilFilter returns true for field values that are not nil;\n// it returns false otherwise.\nfunc NotNilFilter(_ string, v reflect.Value) bool {\n\tswitch v.Kind() {\n\tcase reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:\n\t\treturn !v.IsNil()\n\t}\n\treturn true\n}\n\n// Fprint prints the (sub-)tree starting at AST node x to w.\n// If fset != nil, position information is interpreted relative\n// to that file set. Otherwise positions are printed as integer\n// values (file set specific offsets).\n//\n// A non-nil FieldFilter f may be provided to control the output:\n// struct fields for which f(fieldname, fieldvalue) is true are\n// printed; all others are filtered from the output. Unexported\n// struct fields are never printed.\nfunc Fprint(w io.Writer, fset *token.FileSet, x any, f FieldFilter) error {\n\treturn fprint(w, fset, x, f)\n}\n\nfunc fprint(w io.Writer, fset *token.FileSet, x any, f FieldFilter) (err error) {\n\t// setup printer\n\tp := printer{\n\t\toutput: w,\n\t\tfset:   fset,\n\t\tfilter: f,\n\t\tptrmap: make(map[any]int),\n\t\tlast:   '\\n', // force printing of line number on first line\n\t}\n\n\t// install error handler\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\terr = e.(localError).err // re-panics if it's not a localError\n\t\t}\n\t}()\n\n\t// print x\n\tif x == nil {\n\t\tp.printf(\"nil\\n\")\n\t\treturn\n\t}\n\tp.print(reflect.ValueOf(x))\n\tp.printf(\"\\n\")\n\n\treturn\n}\n\n// Print prints x to standard output, skipping nil fields.\n// Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter).\nfunc Print(fset *token.FileSet, x any) error {\n\treturn Fprint(os.Stdout, fset, x, NotNilFilter)\n}\n\ntype printer struct {\n\toutput io.Writer\n\tfset   *token.FileSet\n\tfilter FieldFilter\n\tptrmap map[any]int // *T -> line number\n\tindent int         // current indentation level\n\tlast   byte        // the last byte processed by Write\n\tline   int         // current line number\n}\n\nvar indent = []byte(\".  \")\n\nfunc (p *printer) Write(data []byte) (n int, err error) {\n\tvar m int\n\tfor i, b := range data {\n\t\t// invariant: data[0:n] has been written\n\t\tif b == '\\n' {\n\t\t\tm, err = p.output.Write(data[n : i+1])\n\t\t\tn += m\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tp.line++\n\t\t} else if p.last == '\\n' {\n\t\t\t_, err = fmt.Fprintf(p.output, \"%6d  \", p.line)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor j := p.indent; j > 0; j-- {\n\t\t\t\t_, err = p.output.Write(indent)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tp.last = b\n\t}\n\tif len(data) > n {\n\t\tm, err = p.output.Write(data[n:])\n\t\tn += m\n\t}\n\treturn\n}\n\n// localError wraps locally caught errors so we can distinguish\n// them from genuine panics which we don't want to return as errors.\ntype localError struct {\n\terr error\n}\n\n// printf is a convenience wrapper that takes care of print errors.\nfunc (p *printer) printf(format string, args ...any) {\n\tif _, err := fmt.Fprintf(p, format, args...); err != nil {\n\t\tpanic(localError{err})\n\t}\n}\n\n// Implementation note: Print is written for AST nodes but could be\n// used to print arbitrary data structures; such a version should\n// probably be in a different package.\n//\n// Note: This code detects (some) cycles created via pointers but\n// not cycles that are created via slices or maps containing the\n// same slice or map. Code for general data structures probably\n// should catch those as well.\n\nfunc (p *printer) print(x reflect.Value) {\n\tif !NotNilFilter(\"\", x) {\n\t\tp.printf(\"nil\")\n\t\treturn\n\t}\n\n\tswitch x.Kind() {\n\tcase reflect.Interface:\n\t\tp.print(x.Elem())\n\n\tcase reflect.Map:\n\t\tp.printf(\"%s (len = %d) {\", x.Type(), x.Len())\n\t\tif x.Len() > 0 {\n\t\t\tp.indent++\n\t\t\tp.printf(\"\\n\")\n\t\t\tfor _, key := range x.MapKeys() {\n\t\t\t\tp.print(key)\n\t\t\t\tp.printf(\": \")\n\t\t\t\tp.print(x.MapIndex(key))\n\t\t\t\tp.printf(\"\\n\")\n\t\t\t}\n\t\t\tp.indent--\n\t\t}\n\t\tp.printf(\"}\")\n\n\tcase reflect.Ptr:\n\t\tp.printf(\"*\")\n\t\t// type-checked ASTs may contain cycles - use ptrmap\n\t\t// to keep track of objects that have been printed\n\t\t// already and print the respective line number instead\n\t\tptr := x.Interface()\n\t\tif line, exists := p.ptrmap[ptr]; exists {\n\t\t\tp.printf(\"(obj @ %d)\", line)\n\t\t} else {\n\t\t\tp.ptrmap[ptr] = p.line\n\t\t\tp.print(x.Elem())\n\t\t}\n\n\tcase reflect.Array:\n\t\tp.printf(\"%s {\", x.Type())\n\t\tif x.Len() > 0 {\n\t\t\tp.indent++\n\t\t\tp.printf(\"\\n\")\n\t\t\tfor i, n := 0, x.Len(); i < n; i++ {\n\t\t\t\tp.printf(\"%d: \", i)\n\t\t\t\tp.print(x.Index(i))\n\t\t\t\tp.printf(\"\\n\")\n\t\t\t}\n\t\t\tp.indent--\n\t\t}\n\t\tp.printf(\"}\")\n\n\tcase reflect.Slice:\n\t\tif s, ok := x.Interface().([]byte); ok {\n\t\t\tp.printf(\"%#q\", s)\n\t\t\treturn\n\t\t}\n\t\tp.printf(\"%s (len = %d) {\", x.Type(), x.Len())\n\t\tif x.Len() > 0 {\n\t\t\tp.indent++\n\t\t\tp.printf(\"\\n\")\n\t\t\tfor i, n := 0, x.Len(); i < n; i++ {\n\t\t\t\tp.printf(\"%d: \", i)\n\t\t\t\tp.print(x.Index(i))\n\t\t\t\tp.printf(\"\\n\")\n\t\t\t}\n\t\t\tp.indent--\n\t\t}\n\t\tp.printf(\"}\")\n\n\tcase reflect.Struct:\n\t\tt := x.Type()\n\t\tp.printf(\"%s {\", t)\n\t\tp.indent++\n\t\tfirst := true\n\t\tfor i, n := 0, t.NumField(); i < n; i++ {\n\t\t\t// exclude non-exported fields because their\n\t\t\t// values cannot be accessed via reflection\n\t\t\tif name := t.Field(i).Name; IsExported(name) {\n\t\t\t\tvalue := x.Field(i)\n\t\t\t\tif p.filter == nil || p.filter(name, value) {\n\t\t\t\t\tif first {\n\t\t\t\t\t\tp.printf(\"\\n\")\n\t\t\t\t\t\tfirst = false\n\t\t\t\t\t}\n\t\t\t\t\tp.printf(\"%s: \", name)\n\t\t\t\t\tp.print(value)\n\t\t\t\t\tp.printf(\"\\n\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tp.indent--\n\t\tp.printf(\"}\")\n\n\tdefault:\n\t\tv := x.Interface()\n\t\tswitch v := v.(type) {\n\t\tcase string:\n\t\t\t// print strings in quotes\n\t\t\tp.printf(\"%q\", v)\n\t\t\treturn\n\t\tcase token.Pos:\n\t\t\t// position values can be printed nicely if we have a file set\n\t\t\tif p.fset != nil {\n\t\t\t\tp.printf(\"%s\", p.fset.Position(v))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// default\n\t\tp.printf(\"%v\", v)\n\t}\n}\n"
  },
  {
    "path": "ast/resolve.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This file implements NewPackage.\n\npackage ast\n\n/* TODO(xsw): remove\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/goplus/xgo/scanner\"\n\t\"github.com/goplus/xgo/token\"\n)\n\ntype pkgBuilder struct {\n\tfset   *token.FileSet\n\terrors scanner.ErrorList\n}\n\nfunc (p *pkgBuilder) error(pos token.Pos, msg string) {\n\tp.errors.Add(p.fset.Position(pos), msg)\n}\n\nfunc (p *pkgBuilder) errorf(pos token.Pos, format string, args ...any) {\n\tp.error(pos, fmt.Sprintf(format, args...))\n}\n\nfunc (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) {\n\talt := scope.Insert(obj)\n\tif alt == nil && altScope != nil {\n\t\t// see if there is a conflicting declaration in altScope\n\t\talt = altScope.Lookup(obj.Name)\n\t}\n\tif alt != nil {\n\t\tprevDecl := \"\"\n\t\tif pos := alt.Pos(); pos.IsValid() {\n\t\t\tprevDecl = fmt.Sprintf(\"\\n\\tprevious declaration at %s\", p.fset.Position(pos))\n\t\t}\n\t\tp.error(obj.Pos(), fmt.Sprintf(\"%s redeclared in this block%s\", obj.Name, prevDecl))\n\t}\n}\n\nfunc resolve(scope *Scope, ident *Ident) bool {\n\tfor ; scope != nil; scope = scope.Outer {\n\t\tif obj := scope.Lookup(ident.Name); obj != nil {\n\t\t\tident.Obj = obj\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// An Importer resolves import paths to package Objects.\n// The imports map records the packages already imported,\n// indexed by package id (canonical import path).\n// An Importer must determine the canonical import path and\n// check the map to see if it is already present in the imports map.\n// If so, the Importer can return the map entry. Otherwise, the\n// Importer should load the package data for the given path into\n// a new *Object (pkg), record pkg in the imports map, and then\n// return pkg.\ntype Importer func(imports map[string]*Object, path string) (pkg *Object, err error)\n\n// NewPackage creates a new Package node from a set of File nodes. It resolves\n// unresolved identifiers across files and updates each file's Unresolved list\n// accordingly. If a non-nil importer and universe scope are provided, they are\n// used to resolve identifiers not declared in any of the package files. Any\n// remaining unresolved identifiers are reported as undeclared. If the files\n// belong to different packages, one package name is selected and files with\n// different package names are reported and then ignored.\n// The result is a package node and a scanner.ErrorList if there were errors.\nfunc NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, error) {\n\tvar p pkgBuilder\n\tp.fset = fset\n\n\t// complete package scope\n\tpkgName := \"\"\n\tpkgScope := NewScope(universe)\n\tfor _, file := range files {\n\t\t// package names must match\n\t\tswitch name := file.Name.Name; {\n\t\tcase pkgName == \"\":\n\t\t\tpkgName = name\n\t\tcase name != pkgName:\n\t\t\tp.errorf(file.Package, \"package %s; expected %s\", name, pkgName)\n\t\t\tcontinue // ignore this file\n\t\t}\n\n\t\t// collect top-level file objects in package scope\n\t\tfor _, obj := range file.Scope.Objects {\n\t\t\tp.declare(pkgScope, nil, obj)\n\t\t}\n\t}\n\n\t// package global mapping of imported package ids to package objects\n\timports := make(map[string]*Object)\n\n\t// complete file scopes with imports and resolve identifiers\n\tfor _, file := range files {\n\t\t// ignore file if it belongs to a different package\n\t\t// (error has already been reported)\n\t\tif file.Name.Name != pkgName {\n\t\t\tcontinue\n\t\t}\n\n\t\t// build file scope by processing all imports\n\t\timportErrors := false\n\t\tfileScope := NewScope(pkgScope)\n\t\tfor _, spec := range file.Imports {\n\t\t\tif importer == nil {\n\t\t\t\timportErrors = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tpath, _ := strconv.Unquote(spec.Path.Value)\n\t\t\tpkg, err := importer(imports, path)\n\t\t\tif err != nil {\n\t\t\t\tp.errorf(spec.Path.Pos(), \"could not import %s (%s)\", path, err)\n\t\t\t\timportErrors = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// TODO(gri) If a local package name != \".\" is provided,\n\t\t\t// global identifier resolution could proceed even if the\n\t\t\t// import failed. Consider adjusting the logic here a bit.\n\n\t\t\t// local name overrides imported package name\n\t\t\tname := pkg.Name\n\t\t\tif spec.Name != nil {\n\t\t\t\tname = spec.Name.Name\n\t\t\t}\n\n\t\t\t// add import to file scope\n\t\t\tif name == \".\" {\n\t\t\t\t// merge imported scope with file scope\n\t\t\t\tfor _, obj := range pkg.Data.(*Scope).Objects {\n\t\t\t\t\tp.declare(fileScope, pkgScope, obj)\n\t\t\t\t}\n\t\t\t} else if name != \"_\" {\n\t\t\t\t// declare imported package object in file scope\n\t\t\t\t// (do not re-use pkg in the file scope but create\n\t\t\t\t// a new object instead; the Decl field is different\n\t\t\t\t// for different files)\n\t\t\t\tobj := NewObj(Pkg, name)\n\t\t\t\tobj.Decl = spec\n\t\t\t\tobj.Data = pkg.Data\n\t\t\t\tp.declare(fileScope, pkgScope, obj)\n\t\t\t}\n\t\t}\n\n\t\t// resolve identifiers\n\t\tif importErrors {\n\t\t\t// don't use the universe scope without correct imports\n\t\t\t// (objects in the universe may be shadowed by imports;\n\t\t\t// with missing imports, identifiers might get resolved\n\t\t\t// incorrectly to universe objects)\n\t\t\tpkgScope.Outer = nil\n\t\t}\n\t\ti := 0\n\t\tfor _, ident := range file.Unresolved {\n\t\t\tif !resolve(fileScope, ident) {\n\t\t\t\tp.errorf(ident.Pos(), \"undeclared name: %s\", ident.Name)\n\t\t\t\tfile.Unresolved[i] = ident\n\t\t\t\ti++\n\t\t\t}\n\n\t\t}\n\t\tfile.Unresolved = file.Unresolved[0:i]\n\t\tpkgScope.Outer = universe // reset universe scope\n\t}\n\n\tp.errors.Sort()\n\treturn &Package{pkgName, pkgScope, imports, files, nil}, p.errors.Err()\n}\n*/\n"
  },
  {
    "path": "ast/scope.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This file implements scopes and the objects they contain.\n\npackage ast\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\n\t\"github.com/goplus/xgo/token\"\n)\n\n// A Scope maintains the set of named language entities declared\n// in the scope and a link to the immediately surrounding (outer)\n// scope.\ntype Scope struct {\n\tOuter   *Scope\n\tObjects map[string]*Object\n}\n\n// NewScope creates a new scope nested in the outer scope.\nfunc NewScope(outer *Scope) *Scope {\n\tconst n = 4 // initial scope capacity\n\treturn &Scope{outer, make(map[string]*Object, n)}\n}\n\n// Lookup returns the object with the given name if it is\n// found in scope s, otherwise it returns nil. Outer scopes\n// are ignored.\nfunc (s *Scope) Lookup(name string) *Object {\n\treturn s.Objects[name]\n}\n\n// Insert attempts to insert a named object obj into the scope s.\n// If the scope already contains an object alt with the same name,\n// Insert leaves the scope unchanged and returns alt. Otherwise\n// it inserts obj and returns nil.\nfunc (s *Scope) Insert(obj *Object) (alt *Object) {\n\tif alt = s.Objects[obj.Name]; alt == nil {\n\t\ts.Objects[obj.Name] = obj\n\t}\n\treturn\n}\n\n// Debugging support\nfunc (s *Scope) String() string {\n\tvar buf bytes.Buffer\n\tfmt.Fprintf(&buf, \"scope %p {\", s)\n\tif s != nil && len(s.Objects) > 0 {\n\t\tfmt.Fprintln(&buf)\n\t\tfor _, obj := range s.Objects {\n\t\t\tfmt.Fprintf(&buf, \"\\t%s %s\\n\", obj.Kind, obj.Name)\n\t\t}\n\t}\n\tfmt.Fprintf(&buf, \"}\\n\")\n\treturn buf.String()\n}\n\n// ----------------------------------------------------------------------------\n// Objects\n\n// An Object describes a named language entity such as a package,\n// constant, type, variable, function (incl. methods), or label.\n//\n// The Data fields contains object-specific data:\n//\n//\tKind    Data type         Data value\n//\tPkg     *Scope            package scope\n//\tCon     int               iota for the respective declaration\ntype Object struct {\n\tKind ObjKind\n\tName string // declared name\n\tDecl any    // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil\n\tData any    // object-specific data; or nil\n\tType any    // placeholder for type information; may be nil\n}\n\n// NewObj creates a new object of a given kind and name.\nfunc NewObj(kind ObjKind, name string) *Object {\n\treturn &Object{Kind: kind, Name: name}\n}\n\n// Pos computes the source position of the declaration of an object name.\n// The result may be an invalid position if it cannot be computed\n// (obj.Decl may be nil or not correct).\nfunc (obj *Object) Pos() token.Pos {\n\tname := obj.Name\n\tswitch d := obj.Decl.(type) {\n\tcase *Field:\n\t\tfor _, n := range d.Names {\n\t\t\tif n.Name == name {\n\t\t\t\treturn n.Pos()\n\t\t\t}\n\t\t}\n\tcase *ImportSpec:\n\t\tif d.Name != nil && d.Name.Name == name {\n\t\t\treturn d.Name.Pos()\n\t\t}\n\t\treturn d.Path.Pos()\n\tcase *ValueSpec:\n\t\tfor _, n := range d.Names {\n\t\t\tif n.Name == name {\n\t\t\t\treturn n.Pos()\n\t\t\t}\n\t\t}\n\tcase *TypeSpec:\n\t\tif d.Name.Name == name {\n\t\t\treturn d.Name.Pos()\n\t\t}\n\tcase *FuncDecl:\n\t\tif d.Name.Name == name {\n\t\t\treturn d.Name.Pos()\n\t\t}\n\tcase *LabeledStmt:\n\t\tif d.Label.Name == name {\n\t\t\treturn d.Label.Pos()\n\t\t}\n\tcase *AssignStmt:\n\t\tfor _, x := range d.Lhs {\n\t\t\tif ident, isIdent := x.(*Ident); isIdent && ident.Name == name {\n\t\t\t\treturn ident.Pos()\n\t\t\t}\n\t\t}\n\tcase *Scope:\n\t\t// predeclared object - nothing to do for now\n\t}\n\treturn token.NoPos\n}\n\n// ObjKind describes what an object represents.\ntype ObjKind int\n\n// The list of possible Object kinds.\nconst (\n\tBad ObjKind = iota // for error handling\n\tPkg                // package\n\tCon                // constant\n\tTyp                // type\n\tVar                // variable\n\tFun                // function or method\n\tLbl                // label\n\timplicitBase\n\n\tImplicitPkg = ObjKind(implicitBase)\n\tImplicitFun = ObjKind(implicitBase + 1)\n)\n\nvar objKindStrings = [...]string{\n\tBad: \"bad\",\n\tPkg: \"package\",\n\tCon: \"const\",\n\tTyp: \"type\",\n\tVar: \"var\",\n\tFun: \"func\",\n\tLbl: \"label\",\n\n\tImplicitPkg: \"package\",\n\tImplicitFun: \"func\",\n}\n\nfunc (kind ObjKind) String() string { return objKindStrings[kind] }\n"
  },
  {
    "path": "ast/togo/goast.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage togo\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"log\"\n\t\"reflect\"\n\n\tgopast \"github.com/goplus/xgo/ast\"\n\tgoptoken \"github.com/goplus/xgo/token\"\n)\n\n// ----------------------------------------------------------------------------\n\nfunc goExpr(val gopast.Expr) ast.Expr {\n\tif val == nil {\n\t\treturn nil\n\t}\n\tswitch v := val.(type) {\n\tcase *gopast.Ident:\n\t\treturn goIdent(v)\n\tcase *gopast.SelectorExpr:\n\t\treturn &ast.SelectorExpr{\n\t\t\tX:   goExpr(v.X),\n\t\t\tSel: goIdent(v.Sel),\n\t\t}\n\tcase *gopast.SliceExpr:\n\t\treturn &ast.SliceExpr{\n\t\t\tX:      goExpr(v.X),\n\t\t\tLbrack: v.Lbrack,\n\t\t\tLow:    goExpr(v.Low),\n\t\t\tHigh:   goExpr(v.High),\n\t\t\tMax:    goExpr(v.Max),\n\t\t\tSlice3: v.Slice3,\n\t\t\tRbrack: v.Rbrack,\n\t\t}\n\tcase *gopast.StarExpr:\n\t\treturn &ast.StarExpr{\n\t\t\tStar: v.Star,\n\t\t\tX:    goExpr(v.X),\n\t\t}\n\tcase *gopast.MapType:\n\t\treturn &ast.MapType{\n\t\t\tMap:   v.Map,\n\t\t\tKey:   goType(v.Key),\n\t\t\tValue: goType(v.Value),\n\t\t}\n\tcase *gopast.StructType:\n\t\treturn &ast.StructType{\n\t\t\tStruct: v.Struct,\n\t\t\tFields: goFieldList(v.Fields),\n\t\t}\n\tcase *gopast.FuncType:\n\t\treturn goFuncType(v)\n\tcase *gopast.InterfaceType:\n\t\treturn &ast.InterfaceType{\n\t\t\tInterface: v.Interface,\n\t\t\tMethods:   goFieldList(v.Methods),\n\t\t}\n\tcase *gopast.ArrayType:\n\t\treturn &ast.ArrayType{\n\t\t\tLbrack: v.Lbrack,\n\t\t\tLen:    goExpr(v.Len),\n\t\t\tElt:    goType(v.Elt),\n\t\t}\n\tcase *gopast.ChanType:\n\t\treturn &ast.ChanType{\n\t\t\tBegin: v.Begin,\n\t\t\tArrow: v.Arrow,\n\t\t\tDir:   ast.ChanDir(v.Dir),\n\t\t\tValue: goType(v.Value),\n\t\t}\n\tcase *gopast.BasicLit:\n\t\treturn goBasicLit(v)\n\tcase *gopast.BinaryExpr:\n\t\treturn &ast.BinaryExpr{\n\t\t\tX:     goExpr(v.X),\n\t\t\tOpPos: v.OpPos,\n\t\t\tOp:    token.Token(v.Op),\n\t\t\tY:     goExpr(v.Y),\n\t\t}\n\tcase *gopast.UnaryExpr:\n\t\treturn &ast.UnaryExpr{\n\t\t\tOpPos: v.OpPos,\n\t\t\tOp:    token.Token(v.Op),\n\t\t\tX:     goExpr(v.X),\n\t\t}\n\tcase *gopast.CallExpr:\n\t\treturn &ast.CallExpr{\n\t\t\tFun:      goExpr(v.Fun),\n\t\t\tLparen:   v.Lparen,\n\t\t\tArgs:     goExprs(v.Args),\n\t\t\tEllipsis: v.Ellipsis,\n\t\t\tRparen:   v.Rparen,\n\t\t}\n\tcase *gopast.IndexExpr:\n\t\treturn &ast.IndexExpr{\n\t\t\tX:      goExpr(v.X),\n\t\t\tLbrack: v.Lbrack,\n\t\t\tIndex:  goExpr(v.Index),\n\t\t\tRbrack: v.Rbrack,\n\t\t}\n\tcase *gopast.ParenExpr:\n\t\treturn &ast.ParenExpr{\n\t\t\tLparen: v.Lparen,\n\t\t\tX:      goExpr(v.X),\n\t\t\tRparen: v.Rparen,\n\t\t}\n\tcase *gopast.CompositeLit:\n\t\treturn &ast.CompositeLit{\n\t\t\tType:   goType(v.Type),\n\t\t\tLbrace: v.Lbrace,\n\t\t\tElts:   goExprs(v.Elts),\n\t\t\tRbrace: v.Rbrace,\n\t\t}\n\tcase *gopast.FuncLit:\n\t\treturn &ast.FuncLit{\n\t\t\tType: goFuncType(v.Type),\n\t\t\tBody: &ast.BlockStmt{}, // skip closure body\n\t\t}\n\tcase *gopast.TypeAssertExpr:\n\t\treturn &ast.TypeAssertExpr{\n\t\t\tX:      goExpr(v.X),\n\t\t\tLparen: v.Lparen,\n\t\t\tType:   goType(v.Type),\n\t\t\tRparen: v.Rparen,\n\t\t}\n\tcase *gopast.KeyValueExpr:\n\t\treturn &ast.KeyValueExpr{\n\t\t\tKey:   goExpr(v.Key),\n\t\t\tColon: v.Colon,\n\t\t\tValue: goExpr(v.Value),\n\t\t}\n\tcase *gopast.Ellipsis:\n\t\treturn &ast.Ellipsis{\n\t\t\tEllipsis: v.Ellipsis,\n\t\t\tElt:      goExpr(v.Elt),\n\t\t}\n\t}\n\tlog.Panicln(\"goExpr: unknown expr -\", reflect.TypeOf(val))\n\treturn nil\n}\n\nfunc goExprs(vals []gopast.Expr) []ast.Expr {\n\tn := len(vals)\n\tif n == 0 {\n\t\treturn nil\n\t}\n\tret := make([]ast.Expr, n)\n\tfor i, v := range vals {\n\t\tret[i] = goExpr(v)\n\t}\n\treturn ret\n}\n\n// ----------------------------------------------------------------------------\n\nfunc goFuncType(v *gopast.FuncType) *ast.FuncType {\n\treturn &ast.FuncType{\n\t\tFunc:    v.Func,\n\t\tParams:  goFieldList(v.Params),\n\t\tResults: goFieldList(v.Results),\n\t}\n}\n\nfunc goType(v gopast.Expr) ast.Expr {\n\treturn goExpr(v)\n}\n\nfunc goBasicLit(v *gopast.BasicLit) *ast.BasicLit {\n\tif v == nil {\n\t\treturn nil\n\t}\n\treturn &ast.BasicLit{\n\t\tValuePos: v.ValuePos,\n\t\tKind:     token.Token(v.Kind),\n\t\tValue:    v.Value,\n\t}\n}\n\nfunc goIdent(v *gopast.Ident) *ast.Ident {\n\tif v == nil {\n\t\treturn nil\n\t}\n\treturn &ast.Ident{\n\t\tNamePos: v.NamePos,\n\t\tName:    v.Name,\n\t}\n}\n\nfunc goIdents(names []*gopast.Ident) []*ast.Ident {\n\tret := make([]*ast.Ident, len(names))\n\tfor i, v := range names {\n\t\tret[i] = goIdent(v)\n\t}\n\treturn ret\n}\n\n// ----------------------------------------------------------------------------\n\nfunc goField(v *gopast.Field) *ast.Field {\n\treturn &ast.Field{\n\t\tNames: goIdents(v.Names),\n\t\tType:  goType(v.Type),\n\t\tTag:   goBasicLit(v.Tag),\n\t}\n}\n\nfunc goFieldList(v *gopast.FieldList) *ast.FieldList {\n\tif v == nil {\n\t\treturn nil\n\t}\n\tlist := make([]*ast.Field, len(v.List))\n\tfor i, item := range v.List {\n\t\tlist[i] = goField(item)\n\t}\n\treturn &ast.FieldList{Opening: v.Opening, List: list, Closing: v.Closing}\n}\n\nfunc goFuncDecl(v *gopast.FuncDecl) *ast.FuncDecl {\n\treturn &ast.FuncDecl{\n\t\tRecv: goFieldList(v.Recv),\n\t\tName: goIdent(v.Name),\n\t\tType: goFuncType(v.Type),\n\t\tBody: &ast.BlockStmt{}, // ignore function body\n\t}\n}\n\n// ----------------------------------------------------------------------------\n\nfunc goImportSpec(spec *gopast.ImportSpec) *ast.ImportSpec {\n\treturn &ast.ImportSpec{\n\t\tName:   goIdent(spec.Name),\n\t\tPath:   goBasicLit(spec.Path),\n\t\tEndPos: spec.EndPos,\n\t}\n}\n\nfunc goTypeSpec(spec *gopast.TypeSpec) *ast.TypeSpec {\n\treturn &ast.TypeSpec{\n\t\tName:   goIdent(spec.Name),\n\t\tAssign: spec.Assign,\n\t\tType:   goType(spec.Type),\n\t}\n}\n\nfunc goValueSpec(spec *gopast.ValueSpec) *ast.ValueSpec {\n\treturn &ast.ValueSpec{\n\t\tNames:  goIdents(spec.Names),\n\t\tType:   goType(spec.Type),\n\t\tValues: goExprs(spec.Values),\n\t}\n}\n\nfunc goGenDecl(v *gopast.GenDecl) *ast.GenDecl {\n\tspecs := make([]ast.Spec, len(v.Specs))\n\tfor i, spec := range v.Specs {\n\t\tswitch v.Tok {\n\t\tcase goptoken.IMPORT:\n\t\t\tspecs[i] = goImportSpec(spec.(*gopast.ImportSpec))\n\t\tcase goptoken.TYPE:\n\t\t\tspecs[i] = goTypeSpec(spec.(*gopast.TypeSpec))\n\t\tcase goptoken.VAR, goptoken.CONST:\n\t\t\tspecs[i] = goValueSpec(spec.(*gopast.ValueSpec))\n\t\tdefault:\n\t\t\tlog.Panicln(\"goGenDecl: unknown spec -\", v.Tok)\n\t\t}\n\t}\n\treturn &ast.GenDecl{\n\t\tTokPos: v.TokPos,\n\t\tTok:    token.Token(v.Tok),\n\t\tLparen: v.Lparen,\n\t\tSpecs:  specs,\n\t\tRparen: v.Rparen,\n\t}\n}\n\n// ----------------------------------------------------------------------------\n\nfunc goDecl(decl gopast.Decl) ast.Decl {\n\tswitch v := decl.(type) {\n\tcase *gopast.GenDecl:\n\t\treturn goGenDecl(v)\n\tcase *gopast.FuncDecl:\n\t\treturn goFuncDecl(v)\n\t}\n\tlog.Panicln(\"goDecl: unknown decl -\", reflect.TypeOf(decl))\n\treturn nil\n}\n\nfunc goDecls(decls []gopast.Decl) []ast.Decl {\n\tret := make([]ast.Decl, len(decls))\n\tfor i, decl := range decls {\n\t\tret[i] = goDecl(decl)\n\t}\n\treturn ret\n}\n\n// ----------------------------------------------------------------------------\n\nconst (\n\tKeepFuncBody = 1 << iota\n)\n\nfunc ASTFile(f *gopast.File, mode int) *ast.File {\n\tif (mode & KeepFuncBody) != 0 {\n\t\tlog.Panicln(\"ASTFile: doesn't support keeping func body now\")\n\t}\n\treturn &ast.File{\n\t\tPackage: f.Package,\n\t\tName:    goIdent(f.Name),\n\t\tDecls:   goDecls(f.Decls),\n\t}\n}\n\n// ----------------------------------------------------------------------------\n"
  },
  {
    "path": "ast/togo/goast_test.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage togo\n\nimport (\n\t\"bytes\"\n\t\"go/format\"\n\t\"go/token\"\n\t\"testing\"\n\n\tgopast \"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/parser\"\n)\n\nfunc testAST(t *testing.T, from, to string) {\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"foo.go\", from, 0)\n\tif err != nil {\n\t\tt.Fatal(\"parser.ParseFile:\", err)\n\t}\n\tgopf := ASTFile(f, 0)\n\tvar b bytes.Buffer\n\terr = format.Node(&b, fset, gopf)\n\tif err != nil {\n\t\tt.Fatal(\"format.Node:\", err)\n\t}\n\tresult := b.String()\n\tif to != result {\n\t\tt.Fatalf(\"\\nResult:\\n%s\\nExpected:\\n%s\\n\", result, to)\n\t}\n}\n\nfunc test(t *testing.T, src string) {\n\ttestAST(t, src, src)\n}\n\nfunc testPanic(t *testing.T, panicMsg string, doPanic func()) {\n\tt.Run(panicMsg, func(t *testing.T) {\n\t\tdefer func() {\n\t\t\tif e := recover(); e == nil {\n\t\t\t\tt.Fatal(\"testPanic: no error?\")\n\t\t\t} else if msg := e.(string); msg != panicMsg {\n\t\t\t\tt.Fatalf(\"\\nResult:\\n%s\\nExpected Panic:\\n%s\\n\", msg, panicMsg)\n\t\t\t}\n\t\t}()\n\t\tdoPanic()\n\t})\n}\n\nfunc TestErrASTFile(t *testing.T) {\n\ttestPanic(t, \"ASTFile: doesn't support keeping func body now\\n\", func() {\n\t\tASTFile(nil, KeepFuncBody)\n\t})\n}\n\nfunc TestErrDecl(t *testing.T) {\n\ttestPanic(t, \"goDecl: unknown decl - <nil>\\n\", func() {\n\t\tgoDecl(nil)\n\t})\n\ttestPanic(t, \"goGenDecl: unknown spec - ILLEGAL\\n\", func() {\n\t\tgoGenDecl(&gopast.GenDecl{\n\t\t\tSpecs: []gopast.Spec{nil},\n\t\t})\n\t})\n}\n\nfunc TestErrExpr(t *testing.T) {\n\ttestPanic(t, \"goExpr: unknown expr - *ast.BadExpr\\n\", func() {\n\t\tgoExpr(&gopast.BadExpr{})\n\t})\n}\n\nfunc TestBasic(t *testing.T) {\n\ttest(t, `package main\n\nimport \"fmt\"\n\ntype a struct {\n\tv   map[int]chan int\n\tarr *[2]func()\n\ti   interface{}\n}\n\nvar b = &a{\n\tarr: &[2]func(){\n\t\tnil,\n\t\tfunc() {},\n\t},\n}\n\nconst c = (10 + 20) * 2\n\nvar d = b.arr[1]\n\nvar e = b.arr[:1]\n\nvar f = a.i.(func() (int))()\n\nfunc foo(v ...interface{}) {}\n`)\n}\n"
  },
  {
    "path": "ast/walk.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ast\n\nimport (\n\t\"fmt\"\n)\n\n// Visitor - A Visitor's Visit method is invoked for each node encountered by Walk.\n// If the result visitor w is not nil, Walk visits each of the children\n// of node with the visitor w, followed by a call of w.Visit(nil).\ntype Visitor interface {\n\tVisit(node Node) (w Visitor)\n}\n\nfunc walkList[N Node](v Visitor, list []N) {\n\tfor _, node := range list {\n\t\tWalk(v, node)\n\t}\n}\n\n// TODO(gri): Investigate if providing a closure to Walk leads to\n//            simpler use (and may help eliminate Inspect in turn).\n\n// Walk traverses an AST in depth-first order: It starts by calling\n// v.Visit(node); node must not be nil. If the visitor w returned by\n// v.Visit(node) is not nil, Walk is invoked recursively with visitor\n// w for each of the non-nil children of node, followed by a call of\n// w.Visit(nil).\nfunc Walk(v Visitor, node Node) {\n\tif v = v.Visit(node); v == nil {\n\t\treturn\n\t}\n\n\t// walk children\n\t// (the order of the cases matches the order\n\t// of the corresponding node types in ast.go)\n\tswitch n := node.(type) {\n\t// Comments and fields\n\tcase *Comment:\n\t\t// nothing to do\n\n\tcase *CommentGroup:\n\t\twalkList(v, n.List)\n\n\tcase *Field:\n\t\tif n.Doc != nil {\n\t\t\tWalk(v, n.Doc)\n\t\t}\n\t\twalkList(v, n.Names)\n\t\tif n.Type != nil {\n\t\t\tWalk(v, n.Type)\n\t\t}\n\t\tif n.Tag != nil {\n\t\t\tWalk(v, n.Tag)\n\t\t}\n\t\tif n.Comment != nil {\n\t\t\tWalk(v, n.Comment)\n\t\t}\n\n\tcase *FieldList:\n\t\twalkList(v, n.List)\n\n\t// Expressions\n\tcase *BadExpr, *Ident, *NumberUnitLit:\n\t\t// nothing to do\n\n\tcase *BasicLit:\n\t\tif n.Extra != nil { // XGo extended\n\t\t\tfor _, part := range n.Extra.Parts {\n\t\t\t\tif e, ok := part.(Expr); ok {\n\t\t\t\t\tWalk(v, e)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tcase *DomainTextLit:\n\t\tWalk(v, n.Domain)\n\t\tif e := n.Extra; e != nil {\n\t\t\tswitch e := e.(type) {\n\t\t\tcase *StringLitEx:\n\t\t\t\tfor _, part := range e.Parts {\n\t\t\t\t\tif e, ok := part.(Expr); ok {\n\t\t\t\t\t\tWalk(v, e)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tcase *Ellipsis:\n\t\tif n.Elt != nil {\n\t\t\tWalk(v, n.Elt)\n\t\t}\n\n\tcase *FuncLit:\n\t\tWalk(v, n.Type)\n\t\tWalk(v, n.Body)\n\n\tcase *CompositeLit:\n\t\tif n.Type != nil {\n\t\t\tWalk(v, n.Type)\n\t\t}\n\t\twalkList(v, n.Elts)\n\n\tcase *ParenExpr:\n\t\tWalk(v, n.X)\n\n\tcase *SelectorExpr:\n\t\tWalk(v, n.X)\n\t\tWalk(v, n.Sel)\n\n\tcase *IndexExpr:\n\t\tWalk(v, n.X)\n\t\tWalk(v, n.Index)\n\n\tcase *IndexListExpr:\n\t\tWalk(v, n.X)\n\t\twalkList(v, n.Indices)\n\n\tcase *SliceExpr:\n\t\tWalk(v, n.X)\n\t\tif n.Low != nil {\n\t\t\tWalk(v, n.Low)\n\t\t}\n\t\tif n.High != nil {\n\t\t\tWalk(v, n.High)\n\t\t}\n\t\tif n.Max != nil {\n\t\t\tWalk(v, n.Max)\n\t\t}\n\n\tcase *TypeAssertExpr:\n\t\tWalk(v, n.X)\n\t\tif n.Type != nil {\n\t\t\tWalk(v, n.Type)\n\t\t}\n\n\tcase *CallExpr:\n\t\tWalk(v, n.Fun)\n\t\twalkList(v, n.Args)\n\t\twalkList(v, n.Kwargs)\n\n\tcase *StarExpr:\n\t\tWalk(v, n.X)\n\n\tcase *UnaryExpr:\n\t\tWalk(v, n.X)\n\n\tcase *BinaryExpr:\n\t\tWalk(v, n.X)\n\t\tWalk(v, n.Y)\n\n\tcase *KwargExpr:\n\t\tWalk(v, n.Name)\n\t\tWalk(v, n.Value)\n\n\tcase *KeyValueExpr:\n\t\tWalk(v, n.Key)\n\t\tWalk(v, n.Value)\n\n\t// Types\n\tcase *ArrayType:\n\t\tif n.Len != nil {\n\t\t\tWalk(v, n.Len)\n\t\t}\n\t\tWalk(v, n.Elt)\n\n\tcase *StructType:\n\t\tWalk(v, n.Fields)\n\n\tcase *FuncType:\n\t\tif n.TypeParams != nil {\n\t\t\tWalk(v, n.TypeParams)\n\t\t}\n\t\tif n.Params != nil {\n\t\t\tWalk(v, n.Params)\n\t\t}\n\t\tif n.Results != nil {\n\t\t\tWalk(v, n.Results)\n\t\t}\n\n\tcase *InterfaceType:\n\t\tWalk(v, n.Methods)\n\n\tcase *MapType:\n\t\tWalk(v, n.Key)\n\t\tWalk(v, n.Value)\n\n\tcase *ChanType:\n\t\tWalk(v, n.Value)\n\n\tcase *TupleType:\n\t\tif n.Fields != nil {\n\t\t\tWalk(v, n.Fields)\n\t\t}\n\n\t// Statements\n\tcase *BadStmt:\n\t\t// nothing to do\n\n\tcase *DeclStmt:\n\t\tWalk(v, n.Decl)\n\n\tcase *EmptyStmt:\n\t\t// nothing to do\n\n\tcase *LabeledStmt:\n\t\tWalk(v, n.Label)\n\t\tWalk(v, n.Stmt)\n\n\tcase *ExprStmt:\n\t\tWalk(v, n.X)\n\n\tcase *SendStmt:\n\t\tWalk(v, n.Chan)\n\t\twalkList(v, n.Values)\n\n\tcase *IncDecStmt:\n\t\tWalk(v, n.X)\n\n\tcase *AssignStmt:\n\t\twalkList(v, n.Lhs)\n\t\twalkList(v, n.Rhs)\n\n\tcase *GoStmt:\n\t\tWalk(v, n.Call)\n\n\tcase *DeferStmt:\n\t\tWalk(v, n.Call)\n\n\tcase *ReturnStmt:\n\t\twalkList(v, n.Results)\n\n\tcase *BranchStmt:\n\t\tif n.Label != nil {\n\t\t\tWalk(v, n.Label)\n\t\t}\n\n\tcase *BlockStmt:\n\t\twalkList(v, n.List)\n\n\tcase *IfStmt:\n\t\tif n.Init != nil {\n\t\t\tWalk(v, n.Init)\n\t\t}\n\t\tWalk(v, n.Cond)\n\t\tWalk(v, n.Body)\n\t\tif n.Else != nil {\n\t\t\tWalk(v, n.Else)\n\t\t}\n\n\tcase *CaseClause:\n\t\twalkList(v, n.List)\n\t\twalkList(v, n.Body)\n\n\tcase *SwitchStmt:\n\t\tif n.Init != nil {\n\t\t\tWalk(v, n.Init)\n\t\t}\n\t\tif n.Tag != nil {\n\t\t\tWalk(v, n.Tag)\n\t\t}\n\t\tWalk(v, n.Body)\n\n\tcase *TypeSwitchStmt:\n\t\tif n.Init != nil {\n\t\t\tWalk(v, n.Init)\n\t\t}\n\t\tWalk(v, n.Assign)\n\t\tWalk(v, n.Body)\n\n\tcase *CommClause:\n\t\tif n.Comm != nil {\n\t\t\tWalk(v, n.Comm)\n\t\t}\n\t\twalkList(v, n.Body)\n\n\tcase *SelectStmt:\n\t\tWalk(v, n.Body)\n\n\tcase *ForStmt:\n\t\tif n.Init != nil {\n\t\t\tWalk(v, n.Init)\n\t\t}\n\t\tif n.Cond != nil {\n\t\t\tWalk(v, n.Cond)\n\t\t}\n\t\tif n.Post != nil {\n\t\t\tWalk(v, n.Post)\n\t\t}\n\t\tWalk(v, n.Body)\n\n\tcase *RangeStmt:\n\t\tif n.Key != nil {\n\t\t\tWalk(v, n.Key)\n\t\t}\n\t\tif n.Value != nil {\n\t\t\tWalk(v, n.Value)\n\t\t}\n\t\tWalk(v, n.X)\n\t\tWalk(v, n.Body)\n\n\t// Declarations\n\tcase *ImportSpec:\n\t\tif n.Doc != nil {\n\t\t\tWalk(v, n.Doc)\n\t\t}\n\t\tif n.Name != nil {\n\t\t\tWalk(v, n.Name)\n\t\t}\n\t\tWalk(v, n.Path)\n\t\tif n.Comment != nil {\n\t\t\tWalk(v, n.Comment)\n\t\t}\n\n\tcase *ValueSpec:\n\t\tif n.Doc != nil {\n\t\t\tWalk(v, n.Doc)\n\t\t}\n\t\twalkList(v, n.Names)\n\t\tif n.Type != nil {\n\t\t\tWalk(v, n.Type)\n\t\t}\n\t\twalkList(v, n.Values)\n\t\tif n.Comment != nil {\n\t\t\tWalk(v, n.Comment)\n\t\t}\n\n\tcase *TypeSpec:\n\t\tif n.Doc != nil {\n\t\t\tWalk(v, n.Doc)\n\t\t}\n\t\tWalk(v, n.Name)\n\t\tif n.TypeParams != nil {\n\t\t\tWalk(v, n.TypeParams)\n\t\t}\n\t\tWalk(v, n.Type)\n\t\tif n.Comment != nil {\n\t\t\tWalk(v, n.Comment)\n\t\t}\n\n\tcase *BadDecl:\n\t\t// nothing to do\n\n\tcase *GenDecl:\n\t\tif n.Doc != nil {\n\t\t\tWalk(v, n.Doc)\n\t\t}\n\t\twalkList(v, n.Specs)\n\n\tcase *FuncDecl:\n\t\tif !n.Shadow { // not a shadow entry\n\t\t\tif n.Doc != nil {\n\t\t\t\tWalk(v, n.Doc)\n\t\t\t}\n\t\t\tif n.Recv != nil {\n\t\t\t\tWalk(v, n.Recv)\n\t\t\t}\n\t\t\tWalk(v, n.Name)\n\t\t\tWalk(v, n.Type)\n\t\t}\n\t\tif n.Body != nil {\n\t\t\tWalk(v, n.Body)\n\t\t}\n\n\t// Files and packages\n\tcase *File:\n\t\tif n.Doc != nil {\n\t\t\tWalk(v, n.Doc)\n\t\t}\n\t\tif !n.NoPkgDecl {\n\t\t\tWalk(v, n.Name)\n\t\t}\n\t\twalkList(v, n.Decls)\n\t\t// don't walk n.Comments - they have been\n\t\t// visited already through the individual\n\t\t// nodes\n\n\tcase *Package:\n\t\tfor _, f := range n.Files {\n\t\t\tWalk(v, f)\n\t\t}\n\n\t// XGo extended expr and stmt\n\tcase *SliceLit:\n\t\twalkList(v, n.Elts)\n\n\tcase *TupleLit:\n\t\twalkList(v, n.Elts)\n\n\tcase *LambdaExpr:\n\t\twalkList(v, n.Lhs)\n\t\twalkList(v, n.Rhs)\n\n\tcase *LambdaExpr2:\n\t\twalkList(v, n.Lhs)\n\t\tWalk(v, n.Body)\n\n\tcase *ForPhrase:\n\t\tif n.Key != nil {\n\t\t\tWalk(v, n.Key)\n\t\t}\n\t\tif n.Value != nil {\n\t\t\tWalk(v, n.Value)\n\t\t}\n\t\tif n.Init != nil {\n\t\t\tWalk(v, n.Init)\n\t\t}\n\t\tif n.Cond != nil {\n\t\t\tWalk(v, n.Cond)\n\t\t}\n\t\tWalk(v, n.X)\n\n\tcase *ComprehensionExpr:\n\t\tif n.Elt != nil {\n\t\t\tWalk(v, n.Elt)\n\t\t}\n\t\twalkList(v, n.Fors)\n\n\tcase *ForPhraseStmt:\n\t\tWalk(v, n.ForPhrase)\n\t\tWalk(v, n.Body)\n\n\tcase *RangeExpr:\n\t\tif n.First != nil {\n\t\t\tWalk(v, n.First)\n\t\t}\n\t\tif n.Last != nil {\n\t\t\tWalk(v, n.Last)\n\t\t}\n\t\tif n.Expr3 != nil {\n\t\t\tWalk(v, n.Expr3)\n\t\t}\n\n\tcase *ErrWrapExpr:\n\t\tWalk(v, n.X)\n\t\tif n.Default != nil {\n\t\t\tWalk(v, n.Default)\n\t\t}\n\n\tcase *OverloadFuncDecl:\n\t\tif n.Doc != nil {\n\t\t\tWalk(v, n.Doc)\n\t\t}\n\t\tif n.Recv != nil {\n\t\t\tWalk(v, n.Recv)\n\t\t}\n\t\tWalk(v, n.Name)\n\t\twalkList(v, n.Funcs)\n\n\tcase *EnvExpr:\n\t\tWalk(v, n.Name)\n\n\tcase *AnySelectorExpr:\n\t\tWalk(v, n.X)\n\t\tWalk(v, n.Sel)\n\n\tcase *CondExpr:\n\t\tWalk(v, n.X)\n\t\tWalk(v, n.Cond)\n\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"ast.Walk: unexpected node type %T\", n))\n\t}\n\n\tv.Visit(nil)\n}\n\ntype inspector func(Node) bool\n\nfunc (f inspector) Visit(node Node) Visitor {\n\tif f(node) {\n\t\treturn f\n\t}\n\treturn nil\n}\n\n// Inspect traverses an AST in depth-first order: It starts by calling\n// f(node); node must not be nil. If f returns true, Inspect invokes f\n// recursively for each of the non-nil children of node, followed by a\n// call of f(nil).\nfunc Inspect(node Node, f func(Node) bool) {\n\tWalk(inspector(f), node)\n}\n"
  },
  {
    "path": "builtin/doc.xgo",
    "content": "package builtin\n\nimport (\n\t\"github.com/qiniu/x/osx\"\n\t\"io\"\n\t\"os\"\n\t\"reflect\"\n)\n\n// Echo formats using the default formats for its operands and writes to standard output.\n// Spaces are always added between operands and a newline is appended.\n// It returns the number of bytes written and any write error encountered.\nfunc Echo(a ...any) (n int, err error)\n\n// Blines returns a BLineReader that reads lines from the given reader.\nfunc Blines(r io.Reader) osx.BLineReader\n\n// Type returns the reflection [Type] that represents the dynamic type of i.\n// If i is a nil interface value, Type returns nil.\nfunc Type(i any) reflect.Type\n\n// Print formats using the default formats for its operands and writes to standard output.\n// Spaces are added between operands when neither is a string.\n// It returns the number of bytes written and any write error encountered.\nfunc Print(a ...any) (n int, err error)\n\n// Println formats using the default formats for its operands and writes to standard output.\n// Spaces are always added between operands and a newline is appended.\n// It returns the number of bytes written and any write error encountered.\nfunc Println(a ...any) (n int, err error)\n\n// Printf formats according to a format specifier and writes to standard output.\n// It returns the number of bytes written and any write error encountered.\nfunc Printf(format string, a ...any) (n int, err error)\n\n// Errorf formats according to a format specifier and returns the string as a\n// value that satisfies error.\n//\n// If the format specifier includes a %w verb with an error operand,\n// the returned error will implement an Unwrap method returning the operand.\n// If there is more than one %w verb, the returned error will implement an\n// Unwrap method returning a []error containing all the %w operands in the\n// order they appear in the arguments.\n// It is invalid to supply the %w verb with an operand that does not implement\n// the error interface. The %w verb is otherwise a synonym for %v.\nfunc Errorf(format string, a ...any) error\n\n// Fprint formats using the default formats for its operands and writes to w.\n// Spaces are added between operands when neither is a string.\n// It returns the number of bytes written and any write error encountered.\nfunc Fprint(w io.Writer, a ...any) (n int, err error)\n\n// Fprintln formats using the default formats for its operands and writes to w.\n// Spaces are always added between operands and a newline is appended.\n// It returns the number of bytes written and any write error encountered.\nfunc Fprintln(w io.Writer, a ...any) (n int, err error)\n\n// Fprintf formats according to a format specifier and writes to w.\n// It returns the number of bytes written and any write error encountered.\nfunc Fprintf(w io.Writer, format string, a ...any) (n int, err error)\n\n// Sprint formats using the default formats for its operands and returns the resulting string.\n// Spaces are added between operands when neither is a string.\nfunc Sprint(a ...any) string\n\n// Sprintln formats using the default formats for its operands and returns the resulting string.\n// Spaces are always added between operands and a newline is appended.\nfunc Sprintln(a ...any) string\n\n// Sprintf formats according to a format specifier and returns the resulting string.\nfunc Sprintf(format string, a ...any) string\n\n// Open opens the named file in the root for reading.\n// See [Open] for more details.\nfunc Open(name string) (*os.File, error)\n\n// Create creates or truncates the named file in the root.\n// See [Create] for more details.\nfunc Create(name string) (*os.File, error)\n\n// Lines returns a LineReader that reads lines from the given reader.\nfunc Lines(r io.Reader) osx.LineReader\n\n// Errorln formats and prints to standard error.\nfunc Errorln(args ...any)\n\n// Fatal is equivalent to Errorln followed by os.Exit(1).\nfunc Fatal(args ...any)\n\n// XGo_Enum returns a LineIter that reads lines from the given reader.\nfunc (r io.Reader) XGo_Enum() osx.LineIter\n\n// String is equivalent to strconv.FormatFloat(f, 'g', -1, 64)\n//\n// FormatFloat converts the floating-point number f to a string,\n// according to the format fmt and precision prec. It rounds the\n// result assuming that the original was obtained from a floating-point\n// value of bitSize bits (32 for float32, 64 for float64).\n//\n// The format fmt is one of\n//   - 'b' (-ddddp±ddd, a binary exponent),\n//   - 'e' (-d.dddde±dd, a decimal exponent),\n//   - 'E' (-d.ddddE±dd, a decimal exponent),\n//   - 'f' (-ddd.dddd, no exponent),\n//   - 'g' ('e' for large exponents, 'f' otherwise),\n//   - 'G' ('E' for large exponents, 'f' otherwise),\n//   - 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or\n//   - 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent).\n//\n// The precision prec controls the number of digits (excluding the exponent)\n// printed by the 'e', 'E', 'f', 'g', 'G', 'x', and 'X' formats.\n// For 'e', 'E', 'f', 'x', and 'X', it is the number of digits after the decimal point.\n// For 'g' and 'G' it is the maximum number of significant digits (trailing\n// zeros are removed).\n// The special precision -1 uses the smallest number of digits\n// necessary such that ParseFloat will return f exactly.\n// The exponent is written as a decimal integer;\n// for all formats other than 'b', it will be at least two digits.\nfunc (f float64) String() string\n\n// String is equivalent to [FormatInt](int64(i), 10).\nfunc (i int) String() string\n\n// String is equivalent to strconv.FormatInt(i, 10)\n//\n// FormatInt returns the string representation of i in the given base,\n// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'\n// for digit values >= 10.\nfunc (i int64) String() string\n\n// String is equivalent to strconv.FormatUint(u, 10)\n//\n// FormatUint returns the string representation of i in the given base,\n// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'\n// for digit values >= 10.\nfunc (u uint64) String() string\n\n// The len built-in function returns the length of v, according to its type:\n//\n//   - Array: the number of elements in v.\n//   - Pointer to array: the number of elements in *v (even if v is nil).\n//   - Slice, or map: the number of elements in v; if v is nil, len(v) is zero.\n//   - String: the number of bytes in v.\n//   - Channel: the number of elements queued (unread) in the channel buffer;\n//     if v is nil, len(v) is zero.\n//\n// For some arguments, such as a string literal or a simple array expression, the\n// result can be a constant. See the Go language specification's \"Length and\n// capacity\" section for details.\nfunc (s string) Len() int\n\n// Count counts the number of non-overlapping instances of substr in s.\n// If substr is an empty string, Count returns 1 + the number of Unicode code points in s.\nfunc (s string) Count(substr string) int\n\n// Int is equivalent to ParseInt(s, 10, 0), converted to type int.\nfunc (s string) Int() (int, error)\n\n// Int64 is equivalent to strconv.ParseInt(s, 10, 64)\n//\n// ParseInt interprets a string s in the given base (0, 2 to 36) and\n// bit size (0 to 64) and returns the corresponding value i.\n//\n// The string may begin with a leading sign: \"+\" or \"-\".\n//\n// If the base argument is 0, the true base is implied by the string's\n// prefix following the sign (if present): 2 for \"0b\", 8 for \"0\" or \"0o\",\n// 16 for \"0x\", and 10 otherwise. Also, for argument base 0 only,\n// underscore characters are permitted as defined by the Go syntax for\n// [integer literals].\n//\n// The bitSize argument specifies the integer type\n// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64\n// correspond to int, int8, int16, int32, and int64.\n// If bitSize is below 0 or above 64, an error is returned.\n//\n// The errors that ParseInt returns have concrete type [*NumError]\n// and include err.Num = s. If s is empty or contains invalid\n// digits, err.Err = [ErrSyntax] and the returned value is 0;\n// if the value corresponding to s cannot be represented by a\n// signed integer of the given size, err.Err = [ErrRange] and the\n// returned value is the maximum magnitude integer of the\n// appropriate bitSize and sign.\n//\n// [integer literals]: https://go.dev/ref/spec#Integer_literals\nfunc (s string) Int64() (i int64, err error)\n\n// Uint64 is equivalent to strconv.ParseUint(s, 10, 64)\n//\n// ParseUint is like [ParseInt] but for unsigned numbers.\n//\n// A sign prefix is not permitted.\nfunc (s string) Uint64() (uint64, error)\n\n// Float is equivalent to strconv.ParseFloat(s, 64)\n//\n// ParseFloat converts the string s to a floating-point number\n// with the precision specified by bitSize: 32 for float32, or 64 for float64.\n// When bitSize=32, the result still has type float64, but it will be\n// convertible to float32 without changing its value.\n//\n// ParseFloat accepts decimal and hexadecimal floating-point numbers\n// as defined by the Go syntax for [floating-point literals].\n// If s is well-formed and near a valid floating-point number,\n// ParseFloat returns the nearest floating-point number rounded\n// using IEEE754 unbiased rounding.\n// (Parsing a hexadecimal floating-point value only rounds when\n// there are more bits in the hexadecimal representation than\n// will fit in the mantissa.)\n//\n// The errors that ParseFloat returns have concrete type *NumError\n// and include err.Num = s.\n//\n// If s is not syntactically well-formed, ParseFloat returns err.Err = ErrSyntax.\n//\n// If s is syntactically well-formed but is more than 1/2 ULP\n// away from the largest floating point number of the given size,\n// ParseFloat returns f = ±Inf, err.Err = ErrRange.\n//\n// ParseFloat recognizes the string \"NaN\", and the (possibly signed) strings \"Inf\" and \"Infinity\"\n// as their respective special floating point values. It ignores case when matching.\n//\n// [floating-point literals]: https://go.dev/ref/spec#Floating-point_literals\nfunc (s string) Float() (float64, error)\n\n// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s.\nfunc (s string) Index(substr string) int\n\n// IndexAny returns the index of the first instance of any Unicode code point\n// from chars in s, or -1 if no Unicode code point from chars is present in s.\nfunc (s string) IndexAny(chars string) int\n\n// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.\nfunc (s string) IndexByte(c byte) int\n\n// IndexRune returns the index of the first instance of the Unicode code point\n// r, or -1 if rune is not present in s.\n// If r is [utf8.RuneError], it returns the first instance of any\n// invalid UTF-8 byte sequence.\nfunc (s string) IndexRune(r rune) int\n\n// LastIndex returns the index of the last instance of substr in s, or -1 if substr is not present in s.\nfunc (s string) LastIndex(substr string) int\n\n// LastIndexAny returns the index of the last instance of any Unicode code\n// point from chars in s, or -1 if no Unicode code point from chars is\n// present in s.\nfunc (s string) LastIndexAny(chars string) int\n\n// LastIndexByte returns the index of the last instance of c in s, or -1 if c is not present in s.\nfunc (s string) LastIndexByte(c byte) int\n\n// Contains reports whether substr is within s.\nfunc (s string) Contains(substr string) bool\n\n// ContainsAny reports whether any Unicode code points in chars are within s.\nfunc (s string) ContainsAny(chars string) bool\n\n// ContainsRune reports whether the Unicode code point r is within s.\nfunc (s string) ContainsRune(r rune) bool\n\n// Compare returns an integer comparing two strings lexicographically.\n// The result will be 0 if a == b, -1 if a < b, and +1 if a > b.\n//\n// Use Compare when you need to perform a three-way comparison (with\n// [slices.SortFunc], for example). It is usually clearer and always faster\n// to use the built-in string comparison operators ==, <, >, and so on.\nfunc (s string) Compare(b string) int\n\n// EqualFold reports whether s and t, interpreted as UTF-8 strings,\n// are equal under simple Unicode case-folding, which is a more general\n// form of case-insensitivity.\nfunc (s string) EqualFold(t string) bool\n\n// HasPrefix reports whether the string s begins with prefix.\nfunc (s string) HasPrefix(prefix string) bool\n\n// HasSuffix reports whether the string s ends with suffix.\nfunc (s string) HasSuffix(suffix string) bool\n\n// Quote returns a double-quoted Go string literal representing s. The\n// returned string uses Go escape sequences (\\t, \\n, \\xFF, \\u0100) for\n// control characters and non-printable characters as defined by\n// [IsPrint].\nfunc (s string) Quote() string\n\n// Unquote interprets s as a single-quoted, double-quoted,\n// or backquoted Go string literal, returning the string value\n// that s quotes.  (If s is single-quoted, it would be a Go\n// character literal; Unquote returns the corresponding\n// one-character string. For an empty character literal\n// Unquote returns the empty string.)\nfunc (s string) Unquote() (string, error)\n\n// ToTitle returns a copy of the string s with all Unicode letters mapped to\n// their Unicode title case.\nfunc (s string) ToTitle() string\n\n// ToUpper returns s with all Unicode letters mapped to their upper case.\nfunc (s string) ToUpper() string\n\n// ToLower returns s with all Unicode letters mapped to their lower case.\nfunc (s string) ToLower() string\n\n// Fields splits the string s around each instance of one or more consecutive white space\n// characters, as defined by [unicode.IsSpace], returning a slice of substrings of s or an\n// empty slice if s contains only white space.\nfunc (s string) Fields() []string\n\n// Repeat returns a new string consisting of count copies of the string s.\n//\n// It panics if count is negative or if the result of (len(s) * count)\n// overflows.\nfunc (s string) Repeat(count int) string\n\n// Split slices s into all substrings separated by sep and returns a slice of\n// the substrings between those separators.\n//\n// If s does not contain sep and sep is not empty, Split returns a\n// slice of length 1 whose only element is s.\n//\n// If sep is empty, Split splits after each UTF-8 sequence. If both s\n// and sep are empty, Split returns an empty slice.\n//\n// It is equivalent to [SplitN] with a count of -1.\n//\n// To split around the first instance of a separator, see [Cut].\nfunc (s string) Split(sep string) []string\n\n// SplitAfter slices s into all substrings after each instance of sep and\n// returns a slice of those substrings.\n//\n// If s does not contain sep and sep is not empty, SplitAfter returns\n// a slice of length 1 whose only element is s.\n//\n// If sep is empty, SplitAfter splits after each UTF-8 sequence. If\n// both s and sep are empty, SplitAfter returns an empty slice.\n//\n// It is equivalent to [SplitAfterN] with a count of -1.\nfunc (s string) SplitAfter(sep string) []string\n\n// SplitN slices s into substrings separated by sep and returns a slice of\n// the substrings between those separators.\n//\n// The count determines the number of substrings to return:\n//   - n > 0: at most n substrings; the last substring will be the unsplit remainder;\n//   - n == 0: the result is nil (zero substrings);\n//   - n < 0: all substrings.\n//\n// Edge cases for s and sep (for example, empty strings) are handled\n// as described in the documentation for [Split].\n//\n// To split around the first instance of a separator, see [Cut].\nfunc (s string) SplitN(sep string, n int) []string\n\n// SplitAfterN slices s into substrings after each instance of sep and\n// returns a slice of those substrings.\n//\n// The count determines the number of substrings to return:\n//   - n > 0: at most n substrings; the last substring will be the unsplit remainder;\n//   - n == 0: the result is nil (zero substrings);\n//   - n < 0: all substrings.\n//\n// Edge cases for s and sep (for example, empty strings) are handled\n// as described in the documentation for [SplitAfter].\nfunc (s string) SplitAfterN(sep string, n int) []string\n\n// Replace returns a copy of the string s with the first n\n// non-overlapping instances of old replaced by new.\n// If old is empty, it matches at the beginning of the string\n// and after each UTF-8 sequence, yielding up to k+1 replacements\n// for a k-rune string.\n// If n < 0, there is no limit on the number of replacements.\nfunc (s string) Replace(old, new string, n int) string\n\n// ReplaceAll returns a copy of the string s with all\n// non-overlapping instances of old replaced by new.\n// If old is empty, it matches at the beginning of the string\n// and after each UTF-8 sequence, yielding up to k+1 replacements\n// for a k-rune string.\nfunc (s string) ReplaceAll(old, new string) string\n\n// Trim returns a slice of the string s with all leading and\n// trailing Unicode code points contained in cutset removed.\nfunc (s string) Trim(cutset string) string\n\n// TrimSpace returns a slice of the string s, with all leading\n// and trailing white space removed, as defined by Unicode.\nfunc (s string) TrimSpace() string\n\n// TrimLeft returns a slice of the string s with all leading\n// Unicode code points contained in cutset removed.\n//\n// To remove a prefix, use [TrimPrefix] instead.\nfunc (s string) TrimLeft(cutset string) string\n\n// TrimRight returns a slice of the string s, with all trailing\n// Unicode code points contained in cutset removed.\n//\n// To remove a suffix, use [TrimSuffix] instead.\nfunc (s string) TrimRight(cutset string) string\n\n// TrimPrefix returns s without the provided leading prefix string.\n// If s doesn't start with prefix, s is returned unchanged.\nfunc (s string) TrimPrefix(prefix string) string\n\n// TrimSuffix returns s without the provided trailing suffix string.\n// If s doesn't end with suffix, s is returned unchanged.\nfunc (s string) TrimSuffix(suffix string) string\n\n// Capitalize returns a copy of the string str with the first letter mapped to\n// its upper case.\nfunc (s string) Capitalize() string\n\n// The len built-in function returns the length of v, according to its type:\n//\n//   - Array: the number of elements in v.\n//   - Pointer to array: the number of elements in *v (even if v is nil).\n//   - Slice, or map: the number of elements in v; if v is nil, len(v) is zero.\n//   - String: the number of bytes in v.\n//   - Channel: the number of elements queued (unread) in the channel buffer;\n//     if v is nil, len(v) is zero.\n//\n// For some arguments, such as a string literal or a simple array expression, the\n// result can be a constant. See the Go language specification's \"Length and\n// capacity\" section for details.\nfunc (v []string) Len() int\n\n// The cap built-in function returns the capacity of v, according to its type:\n//\n//   - Array: the number of elements in v (same as len(v)).\n//   - Pointer to array: the number of elements in *v (same as len(v)).\n//   - Slice: the maximum length the slice can reach when resliced;\n//     if v is nil, cap(v) is zero.\n//   - Channel: the channel buffer capacity, in units of elements;\n//     if v is nil, cap(v) is zero.\n//\n// For some arguments, such as a simple array expression, the result can be a\n// constant. See the Go language specification's \"Length and capacity\" section for\n// details.\nfunc (v []string) Cap() int\n\n// Join concatenates the elements of its first argument to create a single string. The separator\n// string sep is placed between elements in the resulting string.\nfunc (v []string) Join(sep string) string\n\n// Capitalize capitalizes the first letter of each string in the slice.\nfunc (v []string) Capitalize() []string\n\n// ToTitle title-cases all strings in the slice.\nfunc (v []string) ToTitle() []string\n\n// ToUpper upper-cases all strings in the slice.\nfunc (v []string) ToUpper() []string\n\n// ToLower lower-cases all strings in the slice.\nfunc (v []string) ToLower() []string\n\n// Repeat repeats each string in the slice count times.\nfunc (v []string) Repeat(count int) []string\n\n// Replace replaces all occurrences of old in each string in the slice with new.\nfunc (v []string) Replace(old, new string, n int) []string\n\n// ReplaceAll replaces all occurrences of old in each string in the slice with new.\nfunc (v []string) ReplaceAll(old, new string) []string\n\n// Trim removes leading and trailing white space from each string in the slice.\nfunc (v []string) Trim(cutset string) []string\n\n// TrimSpace removes leading and trailing white space from each string in the slice.\nfunc (v []string) TrimSpace() []string\n\n// TrimLeft removes leading white space from each string in the slice.\nfunc (v []string) TrimLeft(cutset string) []string\n\n// TrimRight removes trailing white space from each string in the slice.\nfunc (v []string) TrimRight(cutset string) []string\n\n// TrimPrefix removes leading prefix from each string in the slice.\nfunc (v []string) TrimPrefix(prefix string) []string\n\n// TrimSuffix removes trailing suffix from each string in the slice.\nfunc (v []string) TrimSuffix(suffix string) []string\n"
  },
  {
    "path": "cl/_testc/hello/in.xgo",
    "content": "import \"c\"\n\nc.Printf c\"Hello, world!\\n\"\n"
  },
  {
    "path": "cl/_testc/hello/out.go",
    "content": "package main\n\nimport \"github.com/goplus/lib/c\"\n\nfunc main() {\n\tc.Printf(c.Str(\"Hello, world!\\n\"))\n}\n"
  },
  {
    "path": "cl/_testgop/_matrix/in.xgo",
    "content": "echo [\n\t1, 2, 3\n\t4, 5, 6\n]\n"
  },
  {
    "path": "cl/_testgop/append1/in.xgo",
    "content": "type foo struct {\n\ta []int\n}\n\na := [1, 2, 3]\na <- 4\necho a\n\nf := foo{a: [1, 2, 3]}\nf.a <- 4\necho f\n\nf2 := [2]chan int{}\nf2[0] <- 4\n"
  },
  {
    "path": "cl/_testgop/append1/out.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype foo struct {\n\ta []int\n}\n\nfunc main() {\n\ta := []int{1, 2, 3}\n\ta = append(a, 4)\n\tfmt.Println(a)\n\tf := foo{a: []int{1, 2, 3}}\n\tf.a = append(f.a, 4)\n\tfmt.Println(f)\n\tf2 := [2]chan int{}\n\tf2[0] <- 4\n}\n"
  },
  {
    "path": "cl/_testgop/append2/in.xgo",
    "content": "a := [1, 2, 3]\na <- 4, 5\necho a\n\nb := [7, 8]\na <- b...\necho a\n"
  },
  {
    "path": "cl/_testgop/append2/out.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := []int{1, 2, 3}\n\ta = append(a, 4, 5)\n\tfmt.Println(a)\n\tb := []int{7, 8}\n\ta = append(a, b...)\n\tfmt.Println(a)\n}\n"
  },
  {
    "path": "cl/_testgop/autoref-2484/in.xgo",
    "content": "type Person struct {\n\tName string\n\tAge  int\n}\n\ntype Point struct {\n\tX, Y int\n}\n\nfunc processPerson(p *Person) {\n\tprintln p.Name, p.Age\n}\n\nfunc processPoint(p *Point) {\n\tprintln p.X, p.Y\n}\n\nfunc processSlice(s *[]int) {\n\tprintln len(*s)\n}\n\nfunc processMap(m *map[string]int) {\n\tprintln len(*m)\n}\n\nfunc processArray(a *[3]int) {\n\tprintln len(*a)\n}\n\n// Test auto-reference for typed composite literals\nprocessPerson Person{Name: \"Alice\", Age: 30}\nprocessPoint Point{X: 10, Y: 20}\nprocessSlice []int{1, 2, 3}\nprocessMap map[string]int{\"a\": 1, \"b\": 2}\nprocessArray [3]int{1, 2, 3}\n\n// Test that normal pointer usage still works\nprocessPerson &Person{Name: \"Bob\", Age: 25}\nprocessPoint &Point{X: 5, Y: 15}\n"
  },
  {
    "path": "cl/_testgop/autoref-2484/out.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype Person struct {\n\tName string\n\tAge  int\n}\ntype Point struct {\n\tX int\n\tY int\n}\n\nfunc processPerson(p *Person) {\n\tfmt.Println(p.Name, p.Age)\n}\nfunc processPoint(p *Point) {\n\tfmt.Println(p.X, p.Y)\n}\nfunc processSlice(s *[]int) {\n\tfmt.Println(len(*s))\n}\nfunc processMap(m *map[string]int) {\n\tfmt.Println(len(*m))\n}\nfunc processArray(a *[3]int) {\n\tfmt.Println(len(*a))\n}\n// Test auto-reference for typed composite literals\nfunc main() {\n\tprocessPerson(&Person{Name: \"Alice\", Age: 30})\n\tprocessPoint(&Point{X: 10, Y: 20})\n\tprocessSlice(&[]int{1, 2, 3})\n\tprocessMap(&map[string]int{\"a\": 1, \"b\": 2})\n\tprocessArray(&[3]int{1, 2, 3})\n\tprocessPerson(&Person{Name: \"Bob\", Age: 25})\n\tprocessPoint(&Point{X: 5, Y: 15})\n}\n"
  },
  {
    "path": "cl/_testgop/builtin/in.xgo",
    "content": "a := [\"hello\", \"world\", \"123\"]\necho a.capitalize\necho contains(\"param-required required\", \"required\")\necho contains(\"param-required required\", \"param\")\n"
  },
  {
    "path": "cl/_testgop/builtin/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/stringslice\"\n\t\"github.com/qiniu/x/stringutil\"\n)\n\nfunc main() {\n\ta := []string{\"hello\", \"world\", \"123\"}\n\tfmt.Println(stringslice.Capitalize(a))\n\tfmt.Println(stringutil.Contains(\"param-required required\", \"required\"))\n\tfmt.Println(stringutil.Contains(\"param-required required\", \"param\"))\n}\n"
  },
  {
    "path": "cl/_testgop/domaintext-html/in.xgo",
    "content": "echo html`<html><body><h1>hello</h1></body></html>`\n"
  },
  {
    "path": "cl/_testgop/domaintext-html/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/encoding/html\"\n)\n\nfunc main() {\n\tfmt.Println(html.New(`<html><body><h1>hello</h1></body></html>`))\n}\n"
  },
  {
    "path": "cl/_testgop/domaintext-huh/in.xgo",
    "content": "import \"github.com/goplus/xgo/cl/internal/huh\"\n\nform := huh`> \"1\", 2\n<form id=\"test\">\n</form>\n`\n\nform.run\n"
  },
  {
    "path": "cl/_testgop/domaintext-huh/out.go",
    "content": "package main\n\nimport \"github.com/goplus/xgo/cl/internal/huh\"\n\nfunc main() {\n\tform := huh.New(\"<form id=\\\"test\\\">\\n</form>\\n\", \"1\", 2)\n\tform.Run()\n}\n"
  },
  {
    "path": "cl/_testgop/domaintext-json/in.xgo",
    "content": "echo json`{\"a\":1, \"b\":2}`\necho yaml`\na: 1\nb: c\n`\n"
  },
  {
    "path": "cl/_testgop/domaintext-json/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/encoding/json\"\n\t\"github.com/goplus/xgo/encoding/yaml\"\n)\n\nfunc main() {\n\tfmt.Println(json.New(`{\"a\":1, \"b\":2}`))\n\tfmt.Println(yaml.New(`\na: 1\nb: c\n`))\n}\n"
  },
  {
    "path": "cl/_testgop/domaintext-md/go.mod",
    "content": "module example\n\ngo 1.22\n\nrequire github.com/xushiwei/markdown v0.1.0\n\nrequire github.com/yuin/goldmark v1.7.8 // indirect\n"
  },
  {
    "path": "cl/_testgop/domaintext-md/go.sum",
    "content": "github.com/xushiwei/markdown v0.1.0 h1:Vjw4MVcwSEJge9ObA/3C1lrmNYX60nJwuVKdLYrf0+o=\ngithub.com/xushiwei/markdown v0.1.0/go.mod h1:6hyvHMBrprwcCYXaw71W9WwBX1st9r+Rfsj+cKXW3EE=\ngithub.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=\ngithub.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=\n"
  },
  {
    "path": "cl/_testgop/domaintext-md/in.xgo",
    "content": "import (\n    \"os\"\n\n    \"github.com/xushiwei/markdown\"\n)\n\nmd := markdown`\n# Title\n\nHello world\n`\n\nmd.convert os.Stdout\n"
  },
  {
    "path": "cl/_testgop/domaintext-md/out.go",
    "content": "package main\n\nimport (\n\t\"github.com/xushiwei/markdown\"\n\t\"os\"\n)\n\nfunc main() {\n\tmd := markdown.New(`\n# Title\n\nHello world\n`)\n\tmd.Convert(os.Stdout)\n}\n"
  },
  {
    "path": "cl/_testgop/domaintext-regexp/in.xgo",
    "content": "re, err := regexp`^[a-z]+\\[[0-9]+\\]$`\necho re.matchString(\"adam[23]\")\n_ = err\n"
  },
  {
    "path": "cl/_testgop/domaintext-regexp/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/encoding/regexp\"\n)\n\nfunc main() {\n\tre, err := regexp.New(`^[a-z]+\\[[0-9]+\\]$`)\n\tfmt.Println(re.MatchString(\"adam[23]\"))\n\t_ = err\n}\n"
  },
  {
    "path": "cl/_testgop/domaintext-tpl/in.xgo",
    "content": "cl, err := tpl`expr = INT % (\"+\" | \"-\")`\ncl.parseExpr \"1+2\", nil\n_ = err\n"
  },
  {
    "path": "cl/_testgop/domaintext-tpl/out.go",
    "content": "package main\n\nimport \"github.com/goplus/xgo/tpl\"\n\nfunc main() {\n\tcl, err := tpl.NewEx(`expr = INT % (\"+\" | \"-\")`, \"cl/_testgop/domaintext-tpl/in.xgo\", 1, 15)\n\tcl.ParseExpr(\"1+2\", nil)\n\t_ = err\n}\n"
  },
  {
    "path": "cl/_testgop/domaintpl/in.xgo",
    "content": "tpl`\nfile = stmts => {\n\treturn self\n}\n\nstmts = *(stmt \";\") => {\n\treturn [n.([]any)[0] for n in self]\n}\n\nstmt = varStmt | constStmt | outputStmt | inputStmt | ifStmt | whileStmt | untilStmt | assignStmt\n\nvarStmt = \"DECLARE\" namelist \":\" typeExpr\n\nconstStmt = \"CONSTANT\" IDENT \"<-\" expr\n\nassignStmt = IDENT \"<-\" expr\n\noutputStmt = \"OUTPUT\" exprlist\n\ninputStmt = \"INPUT\" namelist\n\nifStmt = \"IF\" expr \"THEN\" \";\" stmts ?(\"ELSE\" \";\" stmts) \"ENDIF\"\n\nwhileStmt = \"WHILE\" expr \"DO\" \";\" stmts \"ENDWHILE\"\n\nuntilStmt = \"REPEAT\" \";\" stmts \"UNTIL\" expr\n\ntypeExpr = \"INTEGER\" | \"REAL\" | \"STRING\" | \"BOOLEAN\"\n\nexpr = binaryExpr2 % (\"<\" | \"<=\" | \">\" | \">=\" | \"=\" | \"<>\")\n\nbinaryExpr2 = binaryExpr1 % (\"+\" | \"-\")\n\nbinaryExpr1 = operand % (\"*\" | \"/\")\n\noperand = basicLit | ident | parenExpr | unaryExpr\n\nunaryExpr = \"-\" operand\n\nbasicLit = INT | FLOAT | STRING\n\nident = IDENT\n\nparenExpr = \"(\" expr \")\"\n\nexprlist = expr % \",\"\n\nnamelist = IDENT % \",\"\n`\n"
  },
  {
    "path": "cl/_testgop/domaintpl/out.go",
    "content": "package main\n\nimport \"github.com/goplus/xgo/tpl\"\n\nfunc main() {\n\ttpl.NewEx(`\nfile = stmts => {\n\treturn self\n}\n\nstmts = *(stmt \";\") => {\n\treturn [n.([]any)[0] for n in self]\n}\n\nstmt = varStmt | constStmt | outputStmt | inputStmt | ifStmt | whileStmt | untilStmt | assignStmt\n\nvarStmt = \"DECLARE\" namelist \":\" typeExpr\n\nconstStmt = \"CONSTANT\" IDENT \"<-\" expr\n\nassignStmt = IDENT \"<-\" expr\n\noutputStmt = \"OUTPUT\" exprlist\n\ninputStmt = \"INPUT\" namelist\n\nifStmt = \"IF\" expr \"THEN\" \";\" stmts ?(\"ELSE\" \";\" stmts) \"ENDIF\"\n\nwhileStmt = \"WHILE\" expr \"DO\" \";\" stmts \"ENDWHILE\"\n\nuntilStmt = \"REPEAT\" \";\" stmts \"UNTIL\" expr\n\ntypeExpr = \"INTEGER\" | \"REAL\" | \"STRING\" | \"BOOLEAN\"\n\nexpr = binaryExpr2 % (\"<\" | \"<=\" | \">\" | \">=\" | \"=\" | \"<>\")\n\nbinaryExpr2 = binaryExpr1 % (\"+\" | \"-\")\n\nbinaryExpr1 = operand % (\"*\" | \"/\")\n\noperand = basicLit | ident | parenExpr | unaryExpr\n\nunaryExpr = \"-\" operand\n\nbasicLit = INT | FLOAT | STRING\n\nident = IDENT\n\nparenExpr = \"(\" expr \")\"\n\nexprlist = expr % \",\"\n\nnamelist = IDENT % \",\"\n`, \"cl/_testgop/domaintpl/in.xgo\", 1, 4, \"file\", func(self interface{}) interface{} {\n\t\treturn self\n\t}, \"stmts\", func(self []interface{}) interface{} {\n\t\treturn func() (_xgo_ret []interface{}) {\n\t\t\tfor _, n := range self {\n\t\t\t\t_xgo_ret = append(_xgo_ret, n.([]interface{})[0])\n\t\t\t}\n\t\t\treturn\n\t\t}()\n\t})\n}\n"
  },
  {
    "path": "cl/_testgop/dql1/in.xgo",
    "content": "import \"github.com/goplus/xgo/cl/internal/dql\"\n\ndoc := dql.new\necho doc.foo.**.users.*.$age\necho doc.\"foo-name\".**.\"elem-name\".*.$\"attr-name\"\necho doc.**.*.$name\n"
  },
  {
    "path": "cl/_testgop/dql1/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/dql\"\n)\n\nfunc main() {\n\tdoc := dql.New()\n\tfmt.Println(doc.XGo_Elem(\"foo\").XGo_Any(\"users\").XGo_Child().XGo_Attr__0(\"age\"))\n\tfmt.Println(doc.XGo_Elem(\"foo-name\").XGo_Any(\"elem-name\").XGo_Child().XGo_Attr__0(\"attr-name\"))\n\tfmt.Println(doc.XGo_Any(\"\").XGo_Attr__0(\"name\"))\n}\n"
  },
  {
    "path": "cl/_testgop/dql2/in.xgo",
    "content": "import \"github.com/goplus/xgo/cl/internal/dql\"\n\ndoc := dql.new\nname := doc.users@($age?:100 < 18).$name!\necho name\n"
  },
  {
    "path": "cl/_testgop/dql2/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/dql\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc main() {\n\tdoc := dql.New()\n\tname := func() (_xgo_ret int) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = dql.NodeSet_Cast(func(_xgo_yield func(*dql.Node) bool) {\n\t\t\tdoc.XGo_Elem(\"users\").XGo_Enum()(func(self dql.NodeSet) bool {\n\t\t\t\tif func() (_xgo_ret int) {\n\t\t\t\t\tvar _xgo_err error\n\t\t\t\t\t_xgo_ret, _xgo_err = self.XGo_Attr__1(\"age\")\n\t\t\t\t\tif _xgo_err != nil {\n\t\t\t\t\t\treturn 100\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}() < 18 {\n\t\t\t\t\tif _xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n\t\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t}).XGo_Attr__1(\"name\")\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"doc.users@($age?:100 < 18).$name\", \"cl/_testgop/dql2/in.xgo\", 4, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n\tfmt.Println(name)\n}\n"
  },
  {
    "path": "cl/_testgop/dql3/in.xgo",
    "content": "import \"github.com/goplus/xgo/cl/internal/dql\"\n\ndoc := dql.new\necho doc.*@users@`users`.$name\n"
  },
  {
    "path": "cl/_testgop/dql3/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/dql\"\n)\n\nfunc main() {\n\tdoc := dql.New()\n\tfmt.Println(doc.XGo_Child().XGo_Select(\"users\").XGo_Select(\"users\").XGo_Attr__0(\"name\"))\n}\n"
  },
  {
    "path": "cl/_testgop/dql4/in.xgo",
    "content": "doc := xml`<doc><animals>\n\t<animal class=\"gopher\">Line 1</animal>\n\t<animal class=\"armadillo\">Line 2</animal>\n\t<animal class=\"zebra\">Line 3</animal>\n\t<animal class=\"unknown\">Line 4</animal>\n\t<animal class=\"gopher\">Line 5</animal>\n\t<animal class=\"bee\">Line 6</animal>\n\t<animal class=\"gopher\">Line 7</animal>\n\t<animal class=\"zebra\">Line 8</animal>\n</animals></doc>\n`!\n\necho doc.animals.*@($class == \"zebra\")._dump._text\n"
  },
  {
    "path": "cl/_testgop/dql4/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\txml1 \"github.com/goplus/xgo/dql/xml\"\n\t\"github.com/goplus/xgo/encoding/xml\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc main() {\n\tdoc := func() (_xgo_ret xml.Object) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = xml.New(`<doc><animals>\n\t<animal class=\"gopher\">Line 1</animal>\n\t<animal class=\"armadillo\">Line 2</animal>\n\t<animal class=\"zebra\">Line 3</animal>\n\t<animal class=\"unknown\">Line 4</animal>\n\t<animal class=\"gopher\">Line 5</animal>\n\t<animal class=\"bee\">Line 6</animal>\n\t<animal class=\"gopher\">Line 7</animal>\n\t<animal class=\"zebra\">Line 8</animal>\n</animals></doc>\n`)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"xml`<doc><animals>\\n\\t<animal class=\\\"gopher\\\">Line 1</animal>\\n\\t<animal class=\\\"armadillo\\\">Line 2</animal>\\n\\t<animal class=\\\"zebra\\\">Line 3</animal>\\n\\t<animal class=\\\"unknown\\\">Line 4</animal>\\n\\t<animal class=\\\"gopher\\\">Line 5</animal>\\n\\t<animal class=\\\"bee\\\">Line 6</animal>\\n\\t<animal class=\\\"gopher\\\">Line 7</animal>\\n\\t<animal class=\\\"zebra\\\">Line 8</animal>\\n</animals></doc>\\n`\", \"cl/_testgop/dql4/in.xgo\", 1, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n\tfmt.Println(xml1.NodeSet_Cast(func(_xgo_yield func(*xml1.Node) bool) {\n\t\tdoc.XGo_Elem(\"animals\").XGo_Child().XGo_Enum()(func(self xml1.NodeSet) bool {\n\t\t\tif self.XGo_Attr__0(\"class\") == \"zebra\" {\n\t\t\t\tif _xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}).XGo_dump().XGo_text__0())\n}\n"
  },
  {
    "path": "cl/_testgop/dql5/in.xgo",
    "content": "doc := html`<html><body>\n<p>Links:</p>\n<ul>\n\t<li><a href=\"foo\">Foo</a>\n\t<li><a href=\"/bar/baz\">BarBaz</a>\n</ul>\n</body></html>\n`!\n\nfor a in doc.body.**.a.dump {\n\techo a.$href, a.text\n}\n"
  },
  {
    "path": "cl/_testgop/dql5/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/encoding/html\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc main() {\n\tdoc := func() (_xgo_ret html.Object) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = html.New(`<html><body>\n<p>Links:</p>\n<ul>\n\t<li><a href=\"foo\">Foo</a>\n\t<li><a href=\"/bar/baz\">BarBaz</a>\n</ul>\n</body></html>\n`)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"html`<html><body>\\n<p>Links:</p>\\n<ul>\\n\\t<li><a href=\\\"foo\\\">Foo</a>\\n\\t<li><a href=\\\"/bar/baz\\\">BarBaz</a>\\n</ul>\\n</body></html>\\n`\", \"cl/_testgop/dql5/in.xgo\", 1, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n\tfor a := range doc.XGo_Elem(\"body\").XGo_Any(\"a\").Dump().XGo_Enum() {\n\t\tfmt.Println(a.XGo_Attr__0(\"href\"), a.Text__0())\n\t}\n}\n"
  },
  {
    "path": "cl/_testgop/dql6/in.xgo",
    "content": "doc := golang`package main\n\nvar (\n\ta, b string\n\tc    int\n)\n\nfunc add(a, b int) int {\n\treturn a + b\n}\n\nfunc mul(a, b float64) float64 {\n\treturn a * b\n}\n`!\n\nfor fn in doc.decls.*@(self.class == \"FuncDecl\") {\n\techo fn.$name\n}\n"
  },
  {
    "path": "cl/_testgop/dql6/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\tgolang1 \"github.com/goplus/xgo/dql/golang\"\n\t\"github.com/goplus/xgo/dql/reflects\"\n\t\"github.com/goplus/xgo/encoding/golang\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc main() {\n\tdoc := func() (_xgo_ret golang.Object) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = golang.New(`package main\n\nvar (\n\ta, b string\n\tc    int\n)\n\nfunc add(a, b int) int {\n\treturn a + b\n}\n\nfunc mul(a, b float64) float64 {\n\treturn a * b\n}\n`)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"golang`package main\\n\\nvar (\\n\\ta, b string\\n\\tc    int\\n)\\n\\nfunc add(a, b int) int {\\n\\treturn a + b\\n}\\n\\nfunc mul(a, b float64) float64 {\\n\\treturn a * b\\n}\\n`\", \"cl/_testgop/dql6/in.xgo\", 1, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n\tfor fn := range golang1.NodeSet_Cast(func(_xgo_yield func(reflects.Node) bool) {\n\t\tdoc.XGo_Elem(\"decls\").XGo_Child().XGo_Enum()(func(self golang1.NodeSet) bool {\n\t\t\tif self.Class() == \"FuncDecl\" {\n\t\t\t\tif _xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}).XGo_Enum() {\n\t\tfmt.Println(fn.XGo_Attr__0(\"name\"))\n\t}\n}\n"
  },
  {
    "path": "cl/_testgop/dql7/in.xgo",
    "content": "doc := json`{\n\t\"animals\": [\n\t\t{\"class\": \"gopher\", \"at\": \"Line 1\"},\n\t\t{\"class\": \"armadillo\", \"at\": \"Line 2\"},\n\t\t{\"class\": \"zebra\", \"at\": \"Line 3\"},\n\t\t{\"class\": \"unknown\", \"at\": \"Line 4\"},\n\t\t{\"class\": \"gopher\", \"at\": \"Line 5\"},\n\t\t{\"class\": \"bee\", \"at\": \"Line 6\"},\n\t\t{\"class\": \"gopher\", \"at\": \"Line 7\"},\n\t\t{\"class\": \"zebra\", \"at\": \"Line 8\"}\n\t]\n}\n`!\n\nfor animal in doc.$animals.*@($class == \"zebra\") {\n\tanimal.$at\n}\n"
  },
  {
    "path": "cl/_testgop/dql7/out.go",
    "content": "package main\n\nimport (\n\t\"github.com/goplus/xgo/dql/maps\"\n\t\"github.com/goplus/xgo/encoding/json\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc main() {\n\tdoc := func() (_xgo_ret json.Object) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = json.New(`{\n\t\"animals\": [\n\t\t{\"class\": \"gopher\", \"at\": \"Line 1\"},\n\t\t{\"class\": \"armadillo\", \"at\": \"Line 2\"},\n\t\t{\"class\": \"zebra\", \"at\": \"Line 3\"},\n\t\t{\"class\": \"unknown\", \"at\": \"Line 4\"},\n\t\t{\"class\": \"gopher\", \"at\": \"Line 5\"},\n\t\t{\"class\": \"bee\", \"at\": \"Line 6\"},\n\t\t{\"class\": \"gopher\", \"at\": \"Line 7\"},\n\t\t{\"class\": \"zebra\", \"at\": \"Line 8\"}\n\t]\n}\n`)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"json`{\\n\\t\\\"animals\\\": [\\n\\t\\t{\\\"class\\\": \\\"gopher\\\", \\\"at\\\": \\\"Line 1\\\"},\\n\\t\\t{\\\"class\\\": \\\"armadillo\\\", \\\"at\\\": \\\"Line 2\\\"},\\n\\t\\t{\\\"class\\\": \\\"zebra\\\", \\\"at\\\": \\\"Line 3\\\"},\\n\\t\\t{\\\"class\\\": \\\"unknown\\\", \\\"at\\\": \\\"Line 4\\\"},\\n\\t\\t{\\\"class\\\": \\\"gopher\\\", \\\"at\\\": \\\"Line 5\\\"},\\n\\t\\t{\\\"class\\\": \\\"bee\\\", \\\"at\\\": \\\"Line 6\\\"},\\n\\t\\t{\\\"class\\\": \\\"gopher\\\", \\\"at\\\": \\\"Line 7\\\"},\\n\\t\\t{\\\"class\\\": \\\"zebra\\\", \\\"at\\\": \\\"Line 8\\\"}\\n\\t]\\n}\\n`\", \"cl/_testgop/dql7/in.xgo\", 1, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n\t_autoGo_1, _ := doc.(map[string]any)\n\tfor animal := range maps.NodeSet_Cast(func(_xgo_yield func(maps.Node) bool) {\n\t\tmaps.New(_autoGo_1[\"animals\"]).XGo_Child().XGo_Enum()(func(self maps.NodeSet) bool {\n\t\t\tif self.XGo_Attr__0(\"class\") == \"zebra\" {\n\t\t\t\tif _xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}).XGo_Enum() {\n\t\tanimal.XGo_Attr__0(\"at\")\n\t}\n}\n"
  },
  {
    "path": "cl/_testgop/enumlines-rdr/in.xgo",
    "content": "import \"io\"\n\nvar r io.Reader\n\nfor line in lines(r) {\n\tprintln line\n}\n"
  },
  {
    "path": "cl/_testgop/enumlines-rdr/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/osx\"\n\t\"io\"\n)\n\nvar r io.Reader\n\nfunc main() {\n\tfor _xgo_it := osx.Lines(r).XGo_Enum(); ; {\n\t\tvar _xgo_ok bool\n\t\tline, _xgo_ok := _xgo_it.Next()\n\t\tif !_xgo_ok {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Println(line)\n\t}\n}\n"
  },
  {
    "path": "cl/_testgop/enumlines-stdin/in.xgo",
    "content": "import \"os\"\n\nfor line <- os.Stdin {\n\tprintln line\n}\n"
  },
  {
    "path": "cl/_testgop/enumlines-stdin/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/osx\"\n\t\"os\"\n)\n\nfunc main() {\n\tfor _xgo_it := osx.EnumLines(os.Stdin); ; {\n\t\tvar _xgo_ok bool\n\t\tline, _xgo_ok := _xgo_it.Next()\n\t\tif !_xgo_ok {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Println(line)\n\t}\n}\n"
  },
  {
    "path": "cl/_testgop/errwrap1/in.xgo",
    "content": "func F() (a int8, b int16, err error) {\n\ta = 1\n\treturn\n}\n\nfunc F2() (err error) {\n\tc, d := F()!\n\t_ = c\n\t_ = d\n\treturn\n}\n\nF2()\n"
  },
  {
    "path": "cl/_testgop/errwrap1/out.go",
    "content": "package main\n\nimport \"github.com/qiniu/x/errors\"\n\nfunc F() (a int8, b int16, err error) {\n\ta = 1\n\treturn\n}\nfunc F2() (err error) {\n\tc, d := func() (_xgo_ret int8, _xgo_ret2 int16) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_ret2, _xgo_err = F()\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"F()\", \"cl/_testgop/errwrap1/in.xgo\", 7, \"main.F2\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n\t_ = c\n\t_ = d\n\treturn\n}\nfunc main() {\n\tF2()\n}\n"
  },
  {
    "path": "cl/_testgop/errwrap2/in.xgo",
    "content": "func F() (a int8, b int16, err error) {\n\ta = 1\n\treturn\n}\n\nfunc F2() (err error) {\n\tc, d := F()?\n\t_ = c\n\t_ = d\n\treturn\n}\n\nF2()\n"
  },
  {
    "path": "cl/_testgop/errwrap2/out.go",
    "content": "package main\n\nimport \"github.com/qiniu/x/errors\"\n\nfunc F() (a int8, b int16, err error) {\n\ta = 1\n\treturn\n}\nfunc F2() (err error) {\n\tvar _autoGo_1 int8\n\tvar _autoGo_2 int16\n\t{\n\t\tvar _xgo_err error\n\t\t_autoGo_1, _autoGo_2, _xgo_err = F()\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"F()\", \"cl/_testgop/errwrap2/in.xgo\", 7, \"main.F2\")\n\t\t\treturn _xgo_err\n\t\t}\n\t\tgoto _autoGo_3\n\t_autoGo_3:\n\t}\n\tc, d := _autoGo_1, _autoGo_2\n\t_ = c\n\t_ = d\n\treturn\n}\nfunc main() {\n\tF2()\n}\n"
  },
  {
    "path": "cl/_testgop/errwrap3/in.xgo",
    "content": "func BarOne() int {\n\treturn 0\n}\n\nfunc BarTwo() (int, error) {\n\treturn 0, nil\n}\n\nfunc Bar = (\n\tBarOne\n\tBarTwo\n)\n\necho bar!\n"
  },
  {
    "path": "cl/_testgop/errwrap3/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nconst XGoo_Bar = \"BarOne,BarTwo\"\n\nfunc BarOne() int {\n\treturn 0\n}\nfunc BarTwo() (int, error) {\n\treturn 0, nil\n}\nfunc main() {\n\tfmt.Println(func() (_xgo_ret int) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = BarTwo()\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"bar\", \"cl/_testgop/errwrap3/in.xgo\", 14, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}())\n}\n"
  },
  {
    "path": "cl/_testgop/fatal/in.xgo",
    "content": "import \"os\"\n\nf, err := os.open(\"hello.txt\")\nif err != nil {\n    errorln \"[WARN] an error\"\n    fatal \"open file failed: ${err}\"\n}\nf.close\n"
  },
  {
    "path": "cl/_testgop/fatal/out.go",
    "content": "package main\n\nimport (\n\t\"github.com/qiniu/x/osx\"\n\t\"github.com/qiniu/x/stringutil\"\n\t\"os\"\n)\n\nfunc main() {\n\tf, err := os.Open(\"hello.txt\")\n\tif err != nil {\n\t\tosx.Errorln(\"[WARN] an error\")\n\t\tosx.Fatal(stringutil.Concat(\"open file failed: \", err.Error()))\n\t}\n\tf.Close()\n}\n"
  },
  {
    "path": "cl/_testgop/for-in/in.xgo",
    "content": "import \"github.com/goplus/xgo/cl/internal/dql\"\n\ndoc := dql.new\nfor user in doc.users {\n    echo user\n}\n"
  },
  {
    "path": "cl/_testgop/for-in/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/dql\"\n)\n\nfunc main() {\n\tdoc := dql.New()\n\tfor user := range doc.XGo_Elem(\"users\").XGo_Enum() {\n\t\tfmt.Println(user)\n\t}\n}\n"
  },
  {
    "path": "cl/_testgop/for-range/in.xgo",
    "content": "func foo(yield func() bool) {\n\tyield()\n}\n\nfunc bar(yield func(v string) bool) {\n\tyield(\"World\")\n}\n\nfunc weekdays(yield func(k string, v int) bool) {\n\tyield(\"Mon\", 1)\n}\n\nfor range foo {\n    echo \"Hi\"\n}\n\nfor v := range bar {\n    echo v\n}\n\nfor k, v := range weekdays {\n    echo k, v\n}\n"
  },
  {
    "path": "cl/_testgop/for-range/out.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc foo(yield func() bool) {\n\tyield()\n}\nfunc bar(yield func(v string) bool) {\n\tyield(\"World\")\n}\nfunc weekdays(yield func(k string, v int) bool) {\n\tyield(\"Mon\", 1)\n}\nfunc main() {\n\tfor range foo {\n\t\tfmt.Println(\"Hi\")\n\t}\n\tfor v := range bar {\n\t\tfmt.Println(v)\n\t}\n\tfor k, v := range weekdays {\n\t\tfmt.Println(k, v)\n\t}\n}\n"
  },
  {
    "path": "cl/_testgop/implicit-cast-2439/in.xgo",
    "content": "type BasePtr struct {\n}\n\ntype Base struct {\n    *BasePtr\n}\n\nfunc Walk(p *Base) {}\nfunc WalkPtr(p *BasePtr) {}\n\ntype T struct {\n    Base\n}\n\nfunc f() *T {\n    return {}\n}\n\nwalk new(T)\nwalkPtr f()\n"
  },
  {
    "path": "cl/_testgop/implicit-cast-2439/out.go",
    "content": "package main\n\ntype BasePtr struct {\n}\ntype Base struct {\n\t*BasePtr\n}\ntype T struct {\n\tBase\n}\n\nfunc Walk(p *Base) {\n}\nfunc WalkPtr(p *BasePtr) {\n}\nfunc f() *T {\n\treturn &T{}\n}\nfunc main() {\n\tWalk(&new(T).Base)\n\tWalkPtr(f().BasePtr)\n}\n"
  },
  {
    "path": "cl/_testgop/kwargs1/in.xgo",
    "content": "type Options struct {\n    Loop  bool\n    async bool\n}\n\nfunc PlaySound(path string, options *Options) {\n}\n\nplaySound \"1.mp3\", loop = true\nplaySound \"2.mp3\", loop = false, async = true\n"
  },
  {
    "path": "cl/_testgop/kwargs1/out.go",
    "content": "package main\n\ntype Options struct {\n\tLoop  bool\n\tasync bool\n}\n\nfunc PlaySound(path string, options *Options) {\n}\nfunc main() {\n\tPlaySound(\"1.mp3\", &Options{Loop: true})\n\tPlaySound(\"2.mp3\", &Options{Loop: false, async: true})\n}\n"
  },
  {
    "path": "cl/_testgop/kwargs2/in.xgo",
    "content": "type Options struct {\n    Loop  bool\n    async bool\n}\n\nfunc PlaySound(path string, options Options) {\n}\n\nplaySound \"1.mp3\", loop = true\nplaySound \"2.mp3\", loop = false, async = true\n"
  },
  {
    "path": "cl/_testgop/kwargs2/out.go",
    "content": "package main\n\ntype Options struct {\n\tLoop  bool\n\tasync bool\n}\n\nfunc PlaySound(path string, options Options) {\n}\nfunc main() {\n\tPlaySound(\"1.mp3\", Options{Loop: true})\n\tPlaySound(\"2.mp3\", Options{Loop: false, async: true})\n}\n"
  },
  {
    "path": "cl/_testgop/kwargs3/in.xgo",
    "content": "type Options map[string]bool\n\nfunc PlaySound(options Options, paths ...string) {\n}\n\nplaySound \"1.mp3\", \"foo.wav\", loop = false\nplaySound \"2.mp3\", loop = true, async = true\n"
  },
  {
    "path": "cl/_testgop/kwargs3/out.go",
    "content": "package main\n\ntype Options map[string]bool\n\nfunc PlaySound(options Options, paths ...string) {\n}\nfunc main() {\n\tPlaySound(Options{\"loop\": false}, \"1.mp3\", \"foo.wav\")\n\tPlaySound(Options{\"loop\": true, \"async\": true}, \"2.mp3\")\n}\n"
  },
  {
    "path": "cl/_testgop/kwargs4/in.xgo",
    "content": "import \"github.com/goplus/xgo/cl/internal/testutil\"\n\nfunc PlaySound(path string, options *testutil.Options) {\n}\n\nplaySound \"1.mp3\", loop = true\nplaySound \"2.mp3\", Loop = false, async = true\n"
  },
  {
    "path": "cl/_testgop/kwargs4/out.go",
    "content": "package main\n\nimport \"github.com/goplus/xgo/cl/internal/testutil\"\n\nfunc PlaySound(path string, options *testutil.Options) {\n}\nfunc main() {\n\tPlaySound(\"1.mp3\", &testutil.Options{Loop: true})\n\tPlaySound(\"2.mp3\", &testutil.Options{Loop: false, Async: true})\n}\n"
  },
  {
    "path": "cl/_testgop/list-compr1/in.xgo",
    "content": "a := [1, 3.4, 5]\nb := [x*x for x <- a]\n"
  },
  {
    "path": "cl/_testgop/list-compr1/out.go",
    "content": "package main\n\nfunc main() {\n\ta := []float64{1, 3.4, 5}\n\tb := func() (_xgo_ret []float64) {\n\t\tfor _, x := range a {\n\t\t\t_xgo_ret = append(_xgo_ret, x*x)\n\t\t}\n\t\treturn\n\t}()\n}\n"
  },
  {
    "path": "cl/_testgop/list-compr2/in.xgo",
    "content": "arr := [1, 2, 3, 4.1, 5, 6]\nx := [[a, b] for a <- arr, a < b for b <- arr, b > 2]\nprintln(\"x:\", x)\n"
  },
  {
    "path": "cl/_testgop/list-compr2/out.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tarr := []float64{1, 2, 3, 4.1, 5, 6}\n\tx := func() (_xgo_ret [][]float64) {\n\t\tfor _, b := range arr {\n\t\t\tif b > 2 {\n\t\t\t\tfor _, a := range arr {\n\t\t\t\t\tif a < b {\n\t\t\t\t\t\t_xgo_ret = append(_xgo_ret, []float64{a, b})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t}()\n\tfmt.Println(\"x:\", x)\n}\n"
  },
  {
    "path": "cl/_testgop/map-compr-cond1/in.xgo",
    "content": "z := {v: k for k, v <- {\"Hello\": 1, \"Hi\": 3, \"xsw\": 5, \"XGo\": 7}, v > 3}\n"
  },
  {
    "path": "cl/_testgop/map-compr-cond1/out.go",
    "content": "package main\n\nfunc main() {\n\tz := func() (_xgo_ret map[int]string) {\n\t\t_xgo_ret = map[int]string{}\n\t\tfor k, v := range map[string]int{\"Hello\": 1, \"Hi\": 3, \"xsw\": 5, \"XGo\": 7} {\n\t\t\tif v > 3 {\n\t\t\t\t_xgo_ret[v] = k\n\t\t\t}\n\t\t}\n\t\treturn\n\t}()\n}\n"
  },
  {
    "path": "cl/_testgop/map-compr-cond2/in.xgo",
    "content": "z := {t: k for k, v <- {\"Hello\": 1, \"Hi\": 3, \"xsw\": 5, \"XGo\": 7}, t := v; t > 3}\n"
  },
  {
    "path": "cl/_testgop/map-compr-cond2/out.go",
    "content": "package main\n\nfunc main() {\n\tz := func() (_xgo_ret map[int]string) {\n\t\t_xgo_ret = map[int]string{}\n\t\tfor k, v := range map[string]int{\"Hello\": 1, \"Hi\": 3, \"xsw\": 5, \"XGo\": 7} {\n\t\t\tif t := v; t > 3 {\n\t\t\t\t_xgo_ret[t] = k\n\t\t\t}\n\t\t}\n\t\treturn\n\t}()\n}\n"
  },
  {
    "path": "cl/_testgop/map-compr1/in.xgo",
    "content": "y := {x: i for i, x <- [\"1\", \"3\", \"5\", \"7\", \"11\"]}\n"
  },
  {
    "path": "cl/_testgop/map-compr1/out.go",
    "content": "package main\n\nfunc main() {\n\ty := func() (_xgo_ret map[string]int) {\n\t\t_xgo_ret = map[string]int{}\n\t\tfor i, x := range []string{\"1\", \"3\", \"5\", \"7\", \"11\"} {\n\t\t\t_xgo_ret[x] = i\n\t\t}\n\t\treturn\n\t}()\n}\n"
  },
  {
    "path": "cl/_testgop/map-field-access1/in.xgo",
    "content": "v := map[string]map[string]any{\"a\": {\"b\": 1}}\nc, ok := v[\"b\"][\"c\"].(int)\necho c, ok\n\nc, ok = v.b.c.(int)\necho c, ok\n"
  },
  {
    "path": "cl/_testgop/map-field-access1/out.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tv := map[string]map[string]interface{}{\"a\": map[string]interface{}{\"b\": 1}}\n\tc, ok := v[\"b\"][\"c\"].(int)\n\tfmt.Println(c, ok)\n\tc, ok = v[\"b\"][\"c\"].(int)\n\tfmt.Println(c, ok)\n}\n"
  },
  {
    "path": "cl/_testgop/map-field-access2/in.xgo",
    "content": "var v any\n\nc, ok := v[\"b\"][\"c\"].(int)\necho c, ok\n\nd, ok := v.b.c\necho d, ok\n"
  },
  {
    "path": "cl/_testgop/map-field-access2/out.go",
    "content": "package main\n\nimport \"fmt\"\n\nvar v interface{}\n\nfunc main() {\n\t_autoGo_1, _ := v.(map[string]any)\n\t_autoGo_2, _ := _autoGo_1[\"b\"].(map[string]any)\n\tc, ok := _autoGo_2[\"c\"].(int)\n\tfmt.Println(c, ok)\n\t_autoGo_3, _ := v.(map[string]any)\n\t_autoGo_4, _ := _autoGo_3[\"b\"].(map[string]any)\n\td, ok := _autoGo_4[\"c\"]\n\tfmt.Println(d, ok)\n}\n"
  },
  {
    "path": "cl/_testgop/optparam/in.xgo",
    "content": "func basic(a int, b int?) {\n\tprintln a, b\n}\n\nfunc multiple(name string, age int?, active bool?) {\n\tprintln name, age, active\n}\n\nfunc allOptional(x int?, y string?) {\n\tprintln x, y\n}\n\nfunc withVariadic(a int?, b ...string) {\n\tprintln a, b\n}\n\ntype Server struct{}\n\nfunc (s *Server) handle(req string, opts int?) {\n\tprintln req, opts\n}\n\nbasic 10, 20\nmultiple \"Alice\", 30, true\nallOptional 100, \"test\"\nwithVariadic 5, \"hello\", \"world\"\n\ns := Server{}\ns.handle \"request\", 42\n"
  },
  {
    "path": "cl/_testgop/optparam/out.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype Server struct {\n}\n\nfunc basic(a int, __xgo_optional_b int) {\n\tfmt.Println(a, __xgo_optional_b)\n}\nfunc multiple(name string, __xgo_optional_age int, __xgo_optional_active bool) {\n\tfmt.Println(name, __xgo_optional_age, __xgo_optional_active)\n}\nfunc allOptional(__xgo_optional_x int, __xgo_optional_y string) {\n\tfmt.Println(__xgo_optional_x, __xgo_optional_y)\n}\nfunc withVariadic(__xgo_optional_a int, b ...string) {\n\tfmt.Println(__xgo_optional_a, b)\n}\nfunc (s *Server) handle(req string, __xgo_optional_opts int) {\n\tfmt.Println(req, __xgo_optional_opts)\n}\nfunc main() {\n\tbasic(10, 20)\n\tmultiple(\"Alice\", 30, true)\n\tallOptional(100, \"test\")\n\twithVariadic(5, \"hello\", \"world\")\n\ts := Server{}\n\ts.handle(\"request\", 42)\n}\n"
  },
  {
    "path": "cl/_testgop/optparam2/in.xgo",
    "content": "func returnValue(x int?) int {\n\treturn x\n}\n\nfunc useInExpression(a int?, b int?) int {\n\tresult := a + b\n\treturn result * 2\n}\n\nfunc simpleNested(outer int?) {\n\tf := func() {\n\t\tprintln outer\n\t}\n\tf()\n}\n\nreturnValue 42\nuseInExpression 10, 5\nsimpleNested 100\n"
  },
  {
    "path": "cl/_testgop/optparam2/out.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc returnValue(__xgo_optional_x int) int {\n\treturn __xgo_optional_x\n}\nfunc useInExpression(__xgo_optional_a int, __xgo_optional_b int) int {\n\tresult := __xgo_optional_a + __xgo_optional_b\n\treturn result * 2\n}\nfunc simpleNested(__xgo_optional_outer int) {\n\tf := func() {\n\t\tfmt.Println(__xgo_optional_outer)\n\t}\n\tf()\n}\nfunc main() {\n\treturnValue(42)\n\tuseInExpression(10, 5)\n\tsimpleNested(100)\n}\n"
  },
  {
    "path": "cl/_testgop/rangeexpr/in.xgo",
    "content": "println [x for x <- 0:3:1]\n"
  },
  {
    "path": "cl/_testgop/rangeexpr/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/xgo\"\n)\n\nfunc main() {\n\tfmt.Println(func() (_xgo_ret []int) {\n\t\tfor _xgo_it := xgo.NewRange__0(0, 3, 1).XGo_Enum(); ; {\n\t\t\tvar _xgo_ok bool\n\t\t\tx, _xgo_ok := _xgo_it.Next()\n\t\t\tif !_xgo_ok {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\t_xgo_ret = append(_xgo_ret, x)\n\t\t}\n\t\treturn\n\t}())\n}\n"
  },
  {
    "path": "cl/_testgop/repeatuntil/in.xgo",
    "content": "func RepeatUntil(cond func() bool, body func()) {\n    for !cond() {\n        body()\n    }\n}\n\nx := 0\nrepeatUntil x >= 3, => {\n    echo x\n    x++\n}\nrepeatUntil func() bool { return false }, => {}\n"
  },
  {
    "path": "cl/_testgop/repeatuntil/out.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc RepeatUntil(cond func() bool, body func()) {\n\tfor !cond() {\n\t\tbody()\n\t}\n}\nfunc main() {\n\tx := 0\n\tRepeatUntil(func() bool {\n\t\treturn x >= 3\n\t}, func() {\n\t\tfmt.Println(x)\n\t\tx++\n\t})\n\tRepeatUntil(func() bool {\n\t\treturn false\n\t}, func() {\n\t})\n}\n"
  },
  {
    "path": "cl/_testgop/select-compr-twovalue1/in.xgo",
    "content": "y, ok := {i for i, x <- [\"1\", \"3\", \"5\", \"7\", \"11\"], x == \"5\"}\n"
  },
  {
    "path": "cl/_testgop/select-compr-twovalue1/out.go",
    "content": "package main\n\nfunc main() {\n\ty, ok := func() (_xgo_ret int, _xgo_ok bool) {\n\t\tfor i, x := range []string{\"1\", \"3\", \"5\", \"7\", \"11\"} {\n\t\t\tif x == \"5\" {\n\t\t\t\treturn i, true\n\t\t\t}\n\t\t}\n\t\treturn\n\t}()\n}\n"
  },
  {
    "path": "cl/_testgop/select-compr-twovalue2/in.xgo",
    "content": "func foo() (int, bool) {\n\treturn {i for i, x <- [\"1\", \"3\", \"5\", \"7\", \"11\"], x == \"5\"}\n}\n"
  },
  {
    "path": "cl/_testgop/select-compr-twovalue2/out.go",
    "content": "package main\n\nfunc foo() (int, bool) {\n\treturn func() (_xgo_ret int, _xgo_ok bool) {\n\t\tfor i, x := range []string{\"1\", \"3\", \"5\", \"7\", \"11\"} {\n\t\t\tif x == \"5\" {\n\t\t\t\treturn i, true\n\t\t\t}\n\t\t}\n\t\treturn\n\t}()\n}\n"
  },
  {
    "path": "cl/_testgop/select-compr1/in.xgo",
    "content": "y := {i for i, x <- [\"1\", \"3\", \"5\", \"7\", \"11\"], x == \"5\"}\n"
  },
  {
    "path": "cl/_testgop/select-compr1/out.go",
    "content": "package main\n\nfunc main() {\n\ty := func() (_xgo_ret int) {\n\t\tfor i, x := range []string{\"1\", \"3\", \"5\", \"7\", \"11\"} {\n\t\t\tif x == \"5\" {\n\t\t\t\treturn i\n\t\t\t}\n\t\t}\n\t\treturn\n\t}()\n}\n"
  },
  {
    "path": "cl/_testgop/structtag/in.xgo",
    "content": "type Start struct {\n\t_ \"Start recording meeting minutes\"\n}\n"
  },
  {
    "path": "cl/_testgop/structtag/out.go",
    "content": "package main\n\ntype Start struct {\n\t_ struct {\n\t} `_:\"Start recording meeting minutes\"`\n}\n"
  },
  {
    "path": "cl/_testgop/tuplelit/in.xgo",
    "content": "type T struct {\n\tx (int16, float32)\n}\n\nfunc dump(a (int16, float32), _ ...bool) {\n\tt := T{\n\t\tx: (1, 3.14),\n\t}\n\techo a, t\n}\n\nfunc demo(a int16, b float32) {\n\techo a, b\n}\n\nken := (\"Ken\", \"ken@abc.com\", 7)\necho ken\ndump (1, 3.14), true\ndump (1, 3.14)\ndemo (1, 3.14)\n\npairs := [](string, int16){\n    (\"a\", 1),\n    (\"b\", 2),\n}\necho pairs\n"
  },
  {
    "path": "cl/_testgop/tuplelit/out.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype T struct {\n\tx struct {\n\t\tX_0 int16\n\t\tX_1 float32\n\t}\n}\n\nfunc dump(a struct {\n\tX_0 int16\n\tX_1 float32\n}, _ ...bool) {\n\tt := T{x: struct {\n\t\tX_0 int16\n\t\tX_1 float32\n\t}{1, 3.14}}\n\tfmt.Println(a, t)\n}\nfunc demo(a int16, b float32) {\n\tfmt.Println(a, b)\n}\nfunc main() {\n\tken := struct {\n\t\tX_0 string\n\t\tX_1 string\n\t\tX_2 int\n\t}{\"Ken\", \"ken@abc.com\", 7}\n\tfmt.Println(ken)\n\tdump(struct {\n\t\tX_0 int16\n\t\tX_1 float32\n\t}{1, 3.14}, true)\n\tdump(struct {\n\t\tX_0 int16\n\t\tX_1 float32\n\t}{1, 3.14})\n\tdemo(1, 3.14)\n\tpairs := []struct {\n\t\tX_0 string\n\t\tX_1 int16\n\t}{struct {\n\t\tX_0 string\n\t\tX_1 int16\n\t}{\"a\", 1}, struct {\n\t\tX_0 string\n\t\tX_1 int16\n\t}{\"b\", 2}}\n\tfmt.Println(pairs)\n}\n"
  },
  {
    "path": "cl/_testgop/tupletype1/in.xgo",
    "content": "import \"io\"\n\n// Empty tuple\ntype Empty ()\n\n// Anonymous tuple types\ntype Pair (int, string)\n\ntype Triple (int, string, bool)\n\n// Named tuple types\ntype Point (x int, y int)\n\ntype Person (name string, age int)\n\n// Type shorthand syntax\ntype Point3D (x, y, z int)\n\ntype Mixed (a, b string, _ int)\n\n// Tuple as channel element type\nvar ch chan (int, error)\n\n// Tuple as map value type\nvar cache map[string](int, bool)\n\n// Tuple as slice element type\nvar pairs [](string, int)\n\n// Tuple with array types (covers token.LBRACK case)\ntype WithArray (arr []int, data [5]string)\n\n// Tuple with pointer types (covers token.MUL case)\ntype WithPointers (*int, *string)\n\n// Tuple with function types (covers token.FUNC case)\ntype WithFunc (fn func(int) string, callback func())\n\n// Tuple with channel types (covers token.CHAN case)\ntype WithChan (ch chan int, recv <-chan string)\n\n// Tuple with map types (covers token.MAP case)\ntype WithMap (m map[string]int, lookup map[int]bool)\n\n// Tuple with struct types (covers token.STRUCT case)\ntype WithStruct (s struct{ x int }, data struct{ name string })\n\n// Tuple with interface types (covers token.INTERFACE case)\ntype WithInterface (i interface{}, reader interface{ Read([]byte) int })\n\n// Tuple with parenthesized types (covers token.LPAREN case)\ntype WithParen ((int), (string))\n\n// Tuple with qualified type names (covers token.PERIOD case)\ntype WithQualified (io.Reader, io.Writer)\n\n// Tuple with mixed named and array types\ntype MixedArray (items []int, count int)\n\n// Single named field tuple\ntype SingleNamed (value int)\n\nvar ken Person\n\nken.0, ken.1 = \"Ken\", 18\nken.age++\n\necho \"name: ${ken.name}, age: ${ken.age}\"\n"
  },
  {
    "path": "cl/_testgop/tupletype1/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/stringutil\"\n\t\"io\"\n\t\"strconv\"\n)\n// Empty tuple\ntype Empty struct {\n}\n// Anonymous tuple types\ntype Pair struct {\n\tX_0 int\n\tX_1 string\n}\ntype Triple struct {\n\tX_0 int\n\tX_1 string\n\tX_2 bool\n}\n// Named tuple types\ntype Point struct {\n\tX_0 int\n\tX_1 int\n}\ntype Person struct {\n\tX_0 string\n\tX_1 int\n}\n// Type shorthand syntax\ntype Point3D struct {\n\tX_0 int\n\tX_1 int\n\tX_2 int\n}\ntype Mixed struct {\n\tX_0 string\n\tX_1 string\n\tX_2 int\n}\n// Tuple with array types (covers token.LBRACK case)\ntype WithArray struct {\n\tX_0 []int\n\tX_1 [5]string\n}\n// Tuple with pointer types (covers token.MUL case)\ntype WithPointers struct {\n\tX_0 *int\n\tX_1 *string\n}\n// Tuple with function types (covers token.FUNC case)\ntype WithFunc struct {\n\tX_0 func(int) string\n\tX_1 func()\n}\n// Tuple with channel types (covers token.CHAN case)\ntype WithChan struct {\n\tX_0 chan int\n\tX_1 <-chan string\n}\n// Tuple with map types (covers token.MAP case)\ntype WithMap struct {\n\tX_0 map[string]int\n\tX_1 map[int]bool\n}\n// Tuple with struct types (covers token.STRUCT case)\ntype WithStruct struct {\n\tX_0 struct {\n\t\tx int\n\t}\n\tX_1 struct {\n\t\tname string\n\t}\n}\n// Tuple with interface types (covers token.INTERFACE case)\ntype WithInterface struct {\n\tX_0 interface{}\n\tX_1 interface {\n\t\tRead([]byte) int\n\t}\n}\n// Tuple with parenthesized types (covers token.LPAREN case)\ntype WithParen struct {\n\tX_0 int\n\tX_1 string\n}\n// Tuple with qualified type names (covers token.PERIOD case)\ntype WithQualified struct {\n\tX_0 io.Reader\n\tX_1 io.Writer\n}\n// Tuple with mixed named and array types\ntype MixedArray struct {\n\tX_0 []int\n\tX_1 int\n}\n// Single named field tuple\ntype SingleNamed int\n// Tuple as channel element type\nvar ch chan struct {\n\tX_0 int\n\tX_1 error\n}\n// Tuple as map value type\nvar cache map[string]struct {\n\tX_0 int\n\tX_1 bool\n}\n// Tuple as slice element type\nvar pairs []struct {\n\tX_0 string\n\tX_1 int\n}\nvar ken Person\n\nfunc main() {\n\tken.X_0, ken.X_1 = \"Ken\", 18\n\tken.X_1++\n\tfmt.Println(stringutil.Concat(\"name: \", ken.X_0, \", age: \", strconv.Itoa(ken.X_1)))\n}\n"
  },
  {
    "path": "cl/_testgop/tupletype2/in.xgo",
    "content": "type Point (x, y int)\ntype Int (x int)\n\npt := Point{x: 2, y: 3}\necho pt.x, pt.y\necho Int(100)\n\npt2 := Point(100, 200)\necho pt2\n\npt3 := Point(y = 5, x = 3)\necho pt3.0, pt3.1\n"
  },
  {
    "path": "cl/_testgop/tupletype2/out.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype Point struct {\n\tX_0 int\n\tX_1 int\n}\ntype Int int\n\nfunc main() {\n\tpt := Point{X_0: 2, X_1: 3}\n\tfmt.Println(pt.X_0, pt.X_1)\n\tfmt.Println(Int(100))\n\tpt2 := Point{100, 200}\n\tfmt.Println(pt2)\n\tpt3 := Point(Point{X_1: 5, X_0: 3})\n\tfmt.Println(pt3.X_0, pt3.X_1)\n}\n"
  },
  {
    "path": "cl/_testgop/unit/in.xgo",
    "content": "import (\n    \"time\"\n\n    \"github.com/goplus/xgo/cl/internal/unit\"\n)\n\nfunc Wait(time.Duration) {}\nfunc Step(unit.Distance) {}\n\nwait 0.5µs\nwait 1m\nstep 1m\n"
  },
  {
    "path": "cl/_testgop/unit/out.go",
    "content": "package main\n\nimport (\n\t\"github.com/goplus/xgo/cl/internal/unit\"\n\t\"time\"\n)\n\nfunc Wait(time.Duration) {\n}\nfunc Step(unit.Distance) {\n}\nfunc main() {\n\tWait(500)\n\tWait(60000000000)\n\tStep(1000)\n}\n"
  },
  {
    "path": "cl/_testpy/_matrix/in.xgo",
    "content": "import (\n\t\"py/numpy\"\n\t\"py/std\"\n)\n\na := [\n\t[1.0, 2.0, 3.0],\n\t[4.0, 5.0, 6.0],\n\t[7.0, 8.0, 9.0],\n]\nb := [\n\t[9.0, 8.0, 7.0],\n\t[6.0, 5.0, 4.0],\n\t[3.0, 2.0, 1.0],\n]\nx := numpy.add(a, b)\nstd.print \"a+b =\", x\n"
  },
  {
    "path": "cl/_testpy/hello/in.xgo",
    "content": "import (\n\t\"py/std\"\n)\n\nstd.print py\"Hello, World!\"\n"
  },
  {
    "path": "cl/_testpy/hello/out.go",
    "content": "package main\n\nimport (\n\t\"github.com/goplus/lib/py\"\n\t\"github.com/goplus/lib/py/std\"\n)\n\nfunc main() {\n\tstd.Print(py.Str(\"Hello, World!\"))\n}\n"
  },
  {
    "path": "cl/_testpy/pycall/in.xgo",
    "content": "import (\n\t\"c\"\n\t\"py\"\n\t\"py/math\"\n)\n\nx := math.sqrt(py.float(2))\nc.printf c\"sqrt(2) = %f\\n\", x.float64\n"
  },
  {
    "path": "cl/_testpy/pycall/out.go",
    "content": "package main\n\nimport (\n\t\"github.com/goplus/lib/c\"\n\t\"github.com/goplus/lib/py\"\n\t\"github.com/goplus/lib/py/math\"\n)\n\nfunc main() {\n\tx := math.Sqrt(py.Float(2))\n\tc.Printf(c.Str(\"sqrt(2) = %f\\n\"), x.Float64())\n}\n"
  },
  {
    "path": "cl/_testspx/basic/Game.tgmx",
    "content": "func onInit() {\n\tfor {\n\t}\n}\n\ninitGameApp\n"
  },
  {
    "path": "cl/_testspx/basic/Kai.tspx",
    "content": "func onMsg(msg string) {\n\tfor {\n\t\tsay \"Hi\"\n\t}\n}\n"
  },
  {
    "path": "cl/_testspx/basic/out.go",
    "content": "package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\ntype Game struct {\n\t*spx.MyGame\n}\ntype Kai struct {\n\tspx.Sprite\n\t*Game\n}\n\nfunc (this *Game) onInit() {\n\tfor {\n\t\tspx.SchedNow()\n\t}\n}\nfunc (this *Game) MainEntry() {\n\tthis.InitGameApp()\n}\nfunc (this *Game) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *Kai) onMsg(msg string) {\n\tfor {\n\t\tspx.Sched()\n\t\tthis.Say(\"Hi\")\n\t}\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n"
  },
  {
    "path": "cl/_testspx/clsinit1/Rect.gox",
    "content": "var (\n    w, h = 10, 20\n    line = 3\n    color int\n    border float64 = 1.2\n)\n"
  },
  {
    "path": "cl/_testspx/clsinit1/out.go",
    "content": "package main\n\ntype Rect struct {\n\tw      int\n\th      int\n\tline   int\n\tcolor  int\n\tborder float64\n}\n\nfunc (this *Rect) XGo_Init() *Rect {\n\tthis.w, this.h = 10, 20\n\tthis.line = 3\n\tthis.border = 1.2\n\treturn this\n}\n"
  },
  {
    "path": "cl/_testspx/clsinit2/Rect.gox",
    "content": "var (\n    w, h = 10, 20\n    color int\n    border float64 = 1.2\n)\n\necho *this\n"
  },
  {
    "path": "cl/_testspx/clsinit2/out.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype Rect struct {\n\tw      int\n\th      int\n\tcolor  int\n\tborder float64\n}\n\nfunc (this *Rect) Main() {\n\tthis.XGo_Init()\n\tfmt.Println(*this)\n}\nfunc (this *Rect) XGo_Init() *Rect {\n\tthis.w, this.h = 10, 20\n\tthis.border = 1.2\n\treturn this\n}\nfunc main() {\n\tnew(Rect).Main()\n}\n"
  },
  {
    "path": "cl/_testspx/execgsh/demo.gsh",
    "content": "var (\n    score = 100\n)\n\necho score\nxgo \"run\", \"./foo\"\nexec \"ls $HOME\"\nls ${HOME}\n"
  },
  {
    "path": "cl/_testspx/execgsh/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/gsh\"\n)\n\ntype demo struct {\n\tgsh.App\n\tscore int\n}\n\nfunc (this *demo) MainEntry() {\n\tthis.XGo_Init()\n\tfmt.Println(this.score)\n\tthis.XGo_Exec(\"xgo\", \"run\", \"./foo\")\n\tthis.Exec__1(\"ls $HOME\")\n\tthis.XGo_Exec(\"ls\", this.XGo_Env(\"HOME\"))\n}\nfunc (this *demo) Main() {\n\tgsh.XGot_App_Main(this)\n}\nfunc (this *demo) XGo_Init() *demo {\n\tthis.score = 100\n\treturn this\n}\nfunc main() {\n\tnew(demo).Main()\n}\n"
  },
  {
    "path": "cl/_testspx/gshself/demo.gsh",
    "content": "import \"github.com/goplus/xgo/cl/internal/dql\"\n\nself := dql.Node{}\n\nls ${HOME}\n"
  },
  {
    "path": "cl/_testspx/gshself/out.go",
    "content": "package main\n\nimport (\n\t\"github.com/goplus/xgo/cl/internal/dql\"\n\t\"github.com/qiniu/x/gsh\"\n)\n\ntype demo struct {\n\tgsh.App\n}\n\nfunc (this *demo) MainEntry() {\n\tself := dql.Node{}\n\tthis.XGo_Exec(\"ls\", this.XGo_Env(\"HOME\"))\n}\nfunc (this *demo) Main() {\n\tgsh.XGot_App_Main(this)\n}\nfunc main() {\n\tnew(demo).Main()\n}\n"
  },
  {
    "path": "cl/_testspx/init/init.tspx",
    "content": ""
  },
  {
    "path": "cl/_testspx/init/out.go",
    "content": "package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\ntype _init struct {\n\tspx.Sprite\n\t*MyGame\n}\ntype MyGame struct {\n\t*spx.MyGame\n}\n\nfunc (this *MyGame) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *_init) Main() {\n}\nfunc main() {\n\tnew(MyGame).Main()\n}\n"
  },
  {
    "path": "cl/_testspx/multiworks/foo_prompt.gox",
    "content": "return \"Hi\"\n"
  },
  {
    "path": "cl/_testspx/multiworks/hello_tool.gox",
    "content": "return -1\n"
  },
  {
    "path": "cl/_testspx/multiworks/main_mcp.gox",
    "content": "server \"protos\"\n"
  },
  {
    "path": "cl/_testspx/multiworks/out.go",
    "content": "package main\n\nimport \"github.com/goplus/xgo/cl/internal/mcp\"\n\ntype foo struct {\n\tmcp.Prompt\n\t*Game\n}\ntype Tool_hello struct {\n\tmcp.Tool\n\t*Game\n}\ntype Game struct {\n\tmcp.Game\n\tfoo *foo\n}\n\nfunc (this *Game) MainEntry() {\n\tthis.Server(\"protos\")\n}\nfunc (this *Game) Main() {\n\t_xgo_obj0 := &Tool_hello{Game: this}\n\t_xgo_lst1 := []mcp.ToolProto{_xgo_obj0}\n\t_xgo_obj1 := &foo{Game: this}\n\tthis.foo = _xgo_obj1\n\t_xgo_lst2 := []mcp.PromptProto{_xgo_obj1}\n\tmcp.Gopt_Game_Main(this, nil, _xgo_lst1, _xgo_lst2)\n}\nfunc (this *foo) Main(_xgo_arg0 *mcp.Tool) string {\n\tthis.Prompt.Main(_xgo_arg0)\n\treturn \"Hi\"\n}\nfunc (this *Tool_hello) Main(_xgo_arg0 string) int {\n\tthis.Tool.Main(_xgo_arg0)\n\treturn -1\n}\nfunc main() {\n\tnew(Game).Main()\n}\n"
  },
  {
    "path": "cl/_testspx/newobj/Kai_spx.gox",
    "content": ""
  },
  {
    "path": "cl/_testspx/newobj/main_spx.gox",
    "content": "a := new\na.run\nb := new(Sprite)\necho b.name\n"
  },
  {
    "path": "cl/_testspx/newobj/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx3\"\n)\n\ntype Kai struct {\n\tspx3.Sprite\n\t*Game\n}\ntype Game struct {\n\tspx3.Game\n}\n\nfunc (this *Game) MainEntry() {\n\ta := spx3.New()\n\ta.Run()\n\tb := new(spx3.Sprite)\n\tfmt.Println(b.Name())\n}\nfunc (this *Game) Main() {\n\t_xgo_obj0 := &Kai{Game: this}\n\tspx3.Gopt_Game_Main(this, _xgo_obj0)\n}\nfunc (this *Kai) Main(_xgo_arg0 string) {\n\tthis.Sprite.Main(_xgo_arg0)\n}\nfunc (this *Kai) Classfname() string {\n\treturn \"Kai\"\n}\nfunc (this *Kai) Classclone() spx3.Handler {\n\t_xgo_ret := *this\n\treturn &_xgo_ret\n}\nfunc main() {\n\tnew(Game).Main()\n}\n"
  },
  {
    "path": "cl/_testspx/nogame/bar.tspx",
    "content": ""
  },
  {
    "path": "cl/_testspx/nogame/out.go",
    "content": "package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\ntype bar struct {\n\tspx.Sprite\n\t*MyGame\n}\ntype MyGame struct {\n\t*spx.MyGame\n}\n\nfunc (this *MyGame) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *bar) Main() {\n}\nfunc main() {\n\tnew(MyGame).Main()\n}\n"
  },
  {
    "path": "cl/_testspx/singlework/Kai_spx.gox",
    "content": "echo jwt.token(\"Hi\")\n"
  },
  {
    "path": "cl/_testspx/singlework/main_spx.gox",
    "content": "var (\n\tKai Kai\n)\n\nrun\n"
  },
  {
    "path": "cl/_testspx/singlework/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx3\"\n\t\"github.com/goplus/xgo/cl/internal/spx3/jwt\"\n)\n\ntype Kai struct {\n\tspx3.Sprite\n\t*Game\n}\ntype Game struct {\n\tspx3.Game\n\tKai Kai\n}\n\nfunc (this *Game) MainEntry() {\n\tthis.Run()\n}\nfunc (this *Game) Main() {\n\t_xgo_obj0 := &Kai{Game: this}\n\tspx3.Gopt_Game_Main(this, _xgo_obj0)\n}\nfunc (this *Kai) Main(_xgo_arg0 string) {\n\tthis.Sprite.Main(_xgo_arg0)\n\tfmt.Println(jwt.Token(\"Hi\"))\n}\nfunc (this *Kai) Classfname() string {\n\treturn \"Kai\"\n}\nfunc (this *Kai) Classclone() spx3.Handler {\n\t_xgo_ret := *this\n\treturn &_xgo_ret\n}\nfunc main() {\n\tnew(Game).Main()\n}\n"
  },
  {
    "path": "cl/_testspx/xgoinit_dup/Spr_spx.gox",
    "content": "echo \"sprite main called\"\n"
  },
  {
    "path": "cl/_testspx/xgoinit_dup/main_spx.gox",
    "content": "var (\n\tscore = 100\n\tSpr Spr\n)\n\nrun\n"
  },
  {
    "path": "cl/_testspx/xgoinit_dup/out.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx3\"\n)\n\ntype Spr struct {\n\tspx3.Sprite\n\t*Game\n}\ntype Game struct {\n\tspx3.Game\n\tscore int\n\tSpr   Spr\n}\n\nfunc (this *Game) MainEntry() {\n\tthis.XGo_Init()\n\tthis.Run()\n}\nfunc (this *Game) Main() {\n\t_xgo_obj0 := &Spr{Game: this}\n\tspx3.Gopt_Game_Main(this, _xgo_obj0)\n}\nfunc (this *Game) XGo_Init() *Game {\n\tthis.score = 100\n\treturn this\n}\nfunc (this *Spr) Main(_xgo_arg0 string) {\n\tthis.Sprite.Main(_xgo_arg0)\n\tfmt.Println(\"sprite main called\")\n}\nfunc (this *Spr) Classfname() string {\n\treturn \"Spr\"\n}\nfunc (this *Spr) Classclone() spx3.Handler {\n\t_xgo_ret := *this\n\treturn &_xgo_ret\n}\nfunc main() {\n\tnew(Game).Main()\n}\n"
  },
  {
    "path": "cl/builtin.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"github.com/goplus/gogen\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc initMathBig(_ *gogen.Package, conf *gogen.Config, big gogen.PkgRef) {\n\tconf.UntypedBigInt = big.Ref(\"UntypedBigint\").Type().(*types.Named)\n\tconf.UntypedBigRat = big.Ref(\"UntypedBigrat\").Type().(*types.Named)\n\tconf.UntypedBigFloat = big.Ref(\"UntypedBigfloat\").Type().(*types.Named)\n}\n\nfunc initBuiltinFns(builtin *types.Package, scope *types.Scope, pkg gogen.PkgRef, fns []string) {\n\tfor _, fn := range fns {\n\t\tfnTitle := string(fn[0]-'a'+'A') + fn[1:]\n\t\tscope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, fn, pkg.Ref(fnTitle)))\n\t}\n}\n\nfunc initBuiltin(_ *gogen.Package, builtin *types.Package, os, fmt, ng, osx, buil, reflect gogen.PkgRef) {\n\tscope := builtin.Scope()\n\tif ng.Types != nil {\n\t\ttyps := []string{\"bigint\", \"bigrat\", \"bigfloat\"}\n\t\tfor _, typ := range typs {\n\t\t\tname := string(typ[0]-('a'-'A')) + typ[1:]\n\t\t\tscope.Insert(types.NewTypeName(token.NoPos, builtin, typ, ng.Ref(name).Type()))\n\t\t}\n\t\tscope.Insert(types.NewTypeName(token.NoPos, builtin, \"uint128\", ng.Ref(\"Uint128\").Type()))\n\t\tscope.Insert(types.NewTypeName(token.NoPos, builtin, \"int128\", ng.Ref(\"Int128\").Type()))\n\t}\n\tif fmt.Types != nil {\n\t\tscope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, \"echo\", fmt.Ref(\"Println\")))\n\t\tinitBuiltinFns(builtin, scope, fmt, []string{\n\t\t\t\"print\", \"println\", \"printf\", \"errorf\",\n\t\t\t\"fprint\", \"fprintln\", \"fprintf\",\n\t\t\t\"sprint\", \"sprintln\", \"sprintf\",\n\t\t})\n\t}\n\tif os.Types != nil {\n\t\tinitBuiltinFns(builtin, scope, os, []string{\n\t\t\t\"open\", \"create\",\n\t\t})\n\t}\n\tif osx.Types != nil {\n\t\tinitBuiltinFns(builtin, scope, osx, []string{\n\t\t\t\"lines\", \"errorln\", \"fatal\",\n\t\t})\n\t\tscope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, \"blines\", osx.Ref(\"BLines\")))\n\t}\n\tif reflect.Types != nil {\n\t\tscope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, \"type\", reflect.Ref(\"TypeOf\")))\n\t}\n\tif buil.Types != nil {\n\t\tscope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, \"newRange\", buil.Ref(\"NewRange__0\")))\n\t}\n\tscope.Insert(types.NewTypeName(token.NoPos, builtin, \"any\", gogen.TyEmptyInterface))\n}\n\nconst (\n\tosxPkgPath = \"github.com/qiniu/x/osx\"\n)\n\nfunc (ctx *pkgCtx) newBuiltinDefault(pkg *gogen.Package, conf *gogen.Config) *types.Package {\n\tbuiltin := types.NewPackage(\"\", \"\")\n\tfmt := pkg.Import(\"fmt\")\n\tos := pkg.TryImport(\"os\")\n\treflect := pkg.TryImport(\"reflect\")\n\tosx := pkg.TryImport(osxPkgPath)\n\tbuil := pkg.TryImport(\"github.com/qiniu/x/xgo\")\n\tng := pkg.TryImport(\"github.com/qiniu/x/xgo/ng\")\n\tstrx := pkg.TryImport(\"github.com/qiniu/x/stringutil\")\n\tstringslice := pkg.TryImport(\"github.com/qiniu/x/stringslice\")\n\tpkg.TryImport(\"strconv\")\n\tpkg.TryImport(\"strings\")\n\tif ng.Types != nil {\n\t\tinitMathBig(pkg, conf, ng)\n\t}\n\tinitBuiltin(pkg, builtin, os, fmt, ng, osx, buil, reflect)\n\tgogen.InitBuiltin(pkg, builtin, conf)\n\tif strx.Types != nil {\n\t\tti := pkg.BuiltinTI(types.Typ[types.String])\n\t\tti.AddMethods(\n\t\t\t&gogen.BuiltinMethod{Name: \"Capitalize\", Fn: strx.Ref(\"Capitalize\")},\n\t\t)\n\t\tbuiltin.Scope().Insert(gogen.NewOverloadFunc(token.NoPos, builtin, \"contains\", strx.Ref(\"Contains\")))\n\t}\n\tif stringslice.Types != nil {\n\t\tti := pkg.BuiltinTI(types.NewSlice(types.Typ[types.String]))\n\t\tti.AddMethods(\n\t\t\t&gogen.BuiltinMethod{Name: \"Capitalize\", Fn: stringslice.Ref(\"Capitalize\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"ToTitle\", Fn: stringslice.Ref(\"ToTitle\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"ToUpper\", Fn: stringslice.Ref(\"ToUpper\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"ToLower\", Fn: stringslice.Ref(\"ToLower\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"Repeat\", Fn: stringslice.Ref(\"Repeat\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"Replace\", Fn: stringslice.Ref(\"Replace\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"ReplaceAll\", Fn: stringslice.Ref(\"ReplaceAll\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"Trim\", Fn: stringslice.Ref(\"Trim\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"TrimSpace\", Fn: stringslice.Ref(\"TrimSpace\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"TrimLeft\", Fn: stringslice.Ref(\"TrimLeft\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"TrimRight\", Fn: stringslice.Ref(\"TrimRight\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"TrimPrefix\", Fn: stringslice.Ref(\"TrimPrefix\")},\n\t\t\t&gogen.BuiltinMethod{Name: \"TrimSuffix\", Fn: stringslice.Ref(\"TrimSuffix\")},\n\t\t)\n\t}\n\treturn builtin\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/builtin_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\t\"go/types\"\n\t\"log\"\n\t\"testing\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/gogen/packages\"\n\t\"github.com/goplus/mod/modfile\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nvar (\n\tgoxConf = getGoxConf()\n)\n\nfunc getGoxConf() *gogen.Config {\n\tfset := token.NewFileSet()\n\timp := packages.NewImporter(fset)\n\treturn &gogen.Config{Fset: fset, Importer: imp}\n}\n\nfunc TestEmbeddedFieldCast(t *testing.T) {\n\tvar o = new(types.Struct)\n\tembeddedFieldCast(o, nil, nil, visitedT{o: none{}})\n\tembeddedFieldCast(o, nil, nil, visitedT{})\n}\n\nfunc TestNonClosure(t *testing.T) {\n\ttn := types.NewTypeName(0, nil, \"a\", nil)\n\tif !nonClosure(types.NewNamed(tn, types.Typ[types.Int], nil)) {\n\t\tt.Fatal(\"nonClosure\")\n\t}\n}\n\nfunc TestLoadExpr(t *testing.T) {\n\tvar ni nodeInterp\n\tif v := ni.LoadExpr(&ast.Ident{Name: \"x\"}); v != \"\" {\n\t\tt.Fatal(\"LoadExpr:\", v)\n\t}\n}\n\nfunc TestProjFile(t *testing.T) {\n\tvar ni nodeInterp\n\tif v := ni.ProjFile(); v != nil {\n\t\tt.Fatal(\"ProjFile:\", v)\n\t}\n}\n\nfunc TestSpriteOf(t *testing.T) {\n\tproj := &gmxProject{}\n\tif getSpxObj(proj, \"a\") != nil {\n\t\tt.Fatal(\"spriteOf: not nil?\")\n\t}\n}\n\nfunc TestGetGameClass(t *testing.T) {\n\tproj := &gmxProject{\n\t\tgameIsPtr:  true,\n\t\thasMain_:   false,\n\t\tgameClass_: \"\",\n\t\tgt: &modfile.Project{\n\t\t\tClass:    \"*App\",\n\t\t\tPkgPaths: []string{\"foo/bar\"},\n\t\t},\n\t}\n\tctx := &pkgCtx{\n\t\tprojs: map[string]*gmxProject{\".gmx\": proj, \".spx\": proj},\n\t\tnproj: 2,\n\t}\n\tif v := proj.getGameClass(ctx); v != \"BarApp\" {\n\t\tt.Fatal(\"getGameClass:\", v)\n\t}\n}\n\nfunc TestSimplifyPkgPath(t *testing.T) {\n\tif simplifyPkgPath(\"c/lua\") != \"github.com/goplus/lib/c/lua\" {\n\t\tt.Fatal(\"simplifyPkgPath: c/lua\")\n\t}\n\tif simplifyPkgPath(\"cpp/std\") != \"github.com/goplus/lib/cpp/std\" {\n\t\tt.Fatal(\"simplifyPkgPath: cpp/std\")\n\t}\n}\n\nfunc TestCompileLambdaExpr(t *testing.T) {\n\tctx := &blockCtx{\n\t\tpkgCtx: &pkgCtx{},\n\t}\n\tlhs := []*ast.Ident{ast.NewIdent(\"x\")}\n\tsig := types.NewSignatureType(nil, nil, nil, nil, nil, false)\n\te := compileLambdaExpr(ctx, &ast.LambdaExpr{Lhs: lhs}, sig)\n\tif ce := e.(*gogen.CodeError); ce.Msg != `too many arguments in lambda expression\n\thave (x)\n\twant ()` {\n\t\tt.Fatal(\"compileLambdaExpr:\", ce.Msg)\n\t}\n}\n\nfunc TestCompileLambda1(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tif ce := e.(*gogen.CodeError); ce.Msg != `too many arguments in lambda expression\n\thave (x)\n\twant ()` {\n\t\t\t\tt.Fatal(\"compileLambda:\", ce.Msg)\n\t\t\t}\n\t\t}\n\t}()\n\tctx := &blockCtx{\n\t\tpkgCtx: &pkgCtx{},\n\t}\n\tlhs := []*ast.Ident{ast.NewIdent(\"x\")}\n\tsig := types.NewSignatureType(nil, nil, nil, nil, nil, false)\n\tcompileLambda(ctx, &ast.LambdaExpr{Lhs: lhs}, sig)\n}\n\nfunc TestCompileLambda2(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tif ce := e.(*gogen.CodeError); ce.Msg != `too many arguments in lambda expression\n\thave (x)\n\twant ()` {\n\t\t\t\tt.Fatal(\"compileLambda:\", ce.Msg)\n\t\t\t}\n\t\t}\n\t}()\n\tctx := &blockCtx{\n\t\tpkgCtx: &pkgCtx{},\n\t}\n\tlhs := []*ast.Ident{ast.NewIdent(\"x\")}\n\tsig := types.NewSignatureType(nil, nil, nil, nil, nil, false)\n\tcompileLambda(ctx, &ast.LambdaExpr2{Lhs: lhs}, sig)\n}\n\nfunc TestCompileExpr(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tif ce := e.(*gogen.CodeError); ce.Msg != \"compileExpr failed: unknown - *ast.Ellipsis\" {\n\t\t\t\tt.Fatal(\"compileExpr:\", ce.Msg)\n\t\t\t}\n\t\t}\n\t}()\n\tctx := &blockCtx{pkgCtx: &pkgCtx{}}\n\tcompileExpr(ctx, 0, &ast.Ellipsis{})\n}\n\nfunc TestCompileStmt(t *testing.T) {\n\told := enableRecover\n\tdefer func() {\n\t\tenableRecover = old\n\t\tif e := recover(); e != \"compileStmt failed: unknown - *ast.BadStmt\\n\" {\n\t\t\tt.Fatal(\"compileStmt:\", e)\n\t\t}\n\t}()\n\tenableRecover = false\n\tctx := &blockCtx{}\n\tcompileStmt(ctx, &ast.BadStmt{})\n}\n\nfunc TestTryXGoExec(t *testing.T) {\n\tpkg := gogen.NewPackage(\"\", \"foo\", goxConf)\n\tif tryXGoExec(pkg.CB(), nil) {\n\t\tt.Fatal(\"tryXGoExec\")\n\t}\n}\n\nfunc TestCompileFuncAlias(t *testing.T) {\n\tctx := &blockCtx{\n\t\tpkgCtx: &pkgCtx{\n\t\t\tsyms: map[string]loader{\"Foo\": &baseLoader{\n\t\t\t\tfn: func() {},\n\t\t\t}},\n\t\t},\n\t}\n\tscope := types.NewScope(nil, 0, 0, \"\")\n\tx := ast.NewIdent(\"foo\")\n\tif compileFuncAlias(ctx, 0, scope, x, 0) {\n\t\tt.Fatal(\"compileFuncAlias: ok?\")\n\t}\n}\n\nfunc TestErrStringLit(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e == nil {\n\t\t\tt.Fatal(\"TestErrStringLit: no panic?\")\n\t\t}\n\t}()\n\tcompileStringLitEx(nil, nil, &ast.BasicLit{\n\t\tValue: \"Hello\",\n\t\tExtra: &ast.StringLitEx{\n\t\t\tParts: []any{1},\n\t\t},\n\t})\n}\n\nfunc TestErrPreloadFile(t *testing.T) {\n\tpkg := gogen.NewPackage(\"\", \"foo\", goxConf)\n\tctx := &blockCtx{pkgCtx: &pkgCtx{}}\n\tt.Run(\"unknown decl\", func(t *testing.T) {\n\t\tdefer func() {\n\t\t\tif e := recover(); e == nil || e != \"TODO - cl.preloadFile: unknown decl - *ast.BadDecl\\n\" {\n\t\t\t\tt.Fatal(\"TestErrPreloadFile:\", e)\n\t\t\t}\n\t\t}()\n\t\tdecls := []ast.Decl{\n\t\t\t&ast.BadDecl{},\n\t\t}\n\t\tpreloadFile(pkg, ctx, &ast.File{Decls: decls}, \"\", true)\n\t})\n}\n\nfunc TestErrParseTypeEmbedName(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e == nil {\n\t\t\tt.Fatal(\"TestErrParseTypeEmbedName: no panic?\")\n\t\t}\n\t}()\n\tparseTypeEmbedName(&ast.StructType{})\n}\n\nfunc TestGmxCheckProjs(t *testing.T) {\n\t_, multi := gmxCheckProjs(nil, &pkgCtx{\n\t\tprojs: map[string]*gmxProject{\n\t\t\t\".a\": {hasMain_: true}, \".b\": {hasMain_: true},\n\t\t},\n\t})\n\tif !multi {\n\t\tt.Fatal(\"gmxCheckProjs: not multi?\")\n\t}\n}\n\nfunc TestGmxCheckProjs2(t *testing.T) {\n\t_, multi := gmxCheckProjs(nil, &pkgCtx{\n\t\tprojs: map[string]*gmxProject{\n\t\t\t\".a\": {}, \".b\": {},\n\t\t},\n\t})\n\tif !multi {\n\t\tt.Fatal(\"gmxCheckProjs: not multi?\")\n\t}\n}\n\nfunc TestNodeInterp(t *testing.T) {\n\tni := &nodeInterp{}\n\tif v := ni.Caller(&ast.Ident{}); v != \"the function call\" {\n\t\tt.Fatal(\"TestNodeInterp:\", v)\n\t}\n\tdefer func() {\n\t\tif e := recover(); e == nil {\n\t\t\tlog.Fatal(\"TestNodeInterp: no error\")\n\t\t}\n\t}()\n\tni.Caller(&ast.CallExpr{})\n}\n\nfunc TestMarkAutogen(t *testing.T) {\n\told := noMarkAutogen\n\tnoMarkAutogen = false\n\n\tNewPackage(\"\", &ast.Package{Files: map[string]*ast.File{\n\t\t\"main.t2gmx\": {IsProj: true},\n\t}}, &Config{\n\t\tLookupClass: lookupClassErr,\n\t})\n\n\tnoMarkAutogen = old\n}\n\nfunc TestClassNameAndExt(t *testing.T) {\n\tname, clsfile, ext := ClassNameAndExt(\"/foo/bar.abc_yap.gox\")\n\tif name != \"bar_abc\" || clsfile != \"bar.abc\" || ext != \"_yap.gox\" {\n\t\tt.Fatal(\"classNameAndExt:\", name, ext)\n\t}\n\tname, clsfile, ext = ClassNameAndExt(\"/foo/get-bar_:id.yap\")\n\tif name != \"get_bar_id\" || clsfile != \"get-bar_:id\" || ext != \".yap\" {\n\t\tt.Fatal(\"classNameAndExt:\", name, ext)\n\t}\n}\n\nfunc TestFileClassType(t *testing.T) {\n\ttype testData struct {\n\t\tisClass     bool\n\t\tisNormalGox bool\n\t\tisProj      bool\n\t\tfileName    string\n\t\tclassType   string\n\t\tisTest      bool\n\t\tfound       bool\n\t}\n\ttests := []*testData{\n\t\t{false, false, false, \"abc.gop\", \"\", false, false},\n\t\t{false, false, false, \"abc.xgo\", \"\", false, false},\n\t\t{false, false, false, \"abc_test.gop\", \"\", true, false},\n\t\t{false, false, false, \"abc_test.xgo\", \"\", true, false},\n\n\t\t{true, true, false, \"abc.gox\", \"abc\", false, true},\n\t\t{true, true, false, \"Abc.gox\", \"Abc\", false, true},\n\t\t{true, true, false, \"abc_demo.gox\", \"abc\", false, true},\n\t\t{true, true, false, \"Abc_demo.gox\", \"Abc\", false, true},\n\n\t\t{true, true, false, \"main.gox\", \"_main\", false, true},\n\t\t{true, true, false, \"main_demo.gox\", \"_main\", false, true},\n\t\t{true, true, false, \"abc_xtest.gox\", \"abc\", false, true},\n\t\t{true, true, false, \"main_xtest.gox\", \"_main\", false, true},\n\n\t\t{true, true, false, \"abc_test.gox\", \"case_abc\", true, true},\n\t\t{true, true, false, \"Abc_test.gox\", \"caseAbc\", true, true},\n\t\t{true, true, false, \"main_test.gox\", \"case_main\", true, true},\n\n\t\t{true, false, false, \"get.yap\", \"get\", false, true},\n\t\t{true, false, false, \"get_p_#id.yap\", \"get_p_id\", false, true},\n\t\t{true, false, true, \"main.yap\", \"AppV2\", false, true},\n\n\t\t{true, false, false, \"abc_yap.gox\", \"abc\", false, true},\n\t\t{true, false, false, \"Abc_yap.gox\", \"Abc\", false, true},\n\t\t{true, false, true, \"main_yap.gox\", \"App\", false, true},\n\n\t\t{true, false, true, \"abc_yap.gox\", \"abc\", false, true},\n\t\t{true, false, true, \"Abc_yap.gox\", \"Abc\", false, true},\n\t\t{true, false, true, \"main_yap.gox\", \"App\", false, true},\n\n\t\t{true, false, false, \"abc_ytest.gox\", \"case_abc\", true, true},\n\t\t{true, false, false, \"Abc_ytest.gox\", \"caseAbc\", true, true},\n\t\t{true, false, true, \"main_ytest.gox\", \"App\", true, true},\n\t}\n\tlookupClass := func(ext string) (c *Project, ok bool) {\n\t\tswitch ext {\n\t\tcase \".yap\":\n\t\t\treturn &modfile.Project{\n\t\t\t\tExt: \".yap\", Class: \"AppV2\",\n\t\t\t\tWorks:    []*modfile.Class{{Ext: \".yap\", Class: \"Handler\"}},\n\t\t\t\tPkgPaths: []string{\"github.com/goplus/yap\"}}, true\n\t\tcase \"_yap.gox\":\n\t\t\treturn &modfile.Project{\n\t\t\t\tExt: \"_yap.gox\", Class: \"App\",\n\t\t\t\tPkgPaths: []string{\"github.com/goplus/yap\"}}, true\n\t\tcase \"_ytest.gox\":\n\t\t\treturn &modfile.Project{\n\t\t\t\tExt: \"_ytest.gox\", Class: \"App\",\n\t\t\t\tWorks:    []*modfile.Class{{Ext: \"_ytest.gox\", Class: \"Case\"}},\n\t\t\t\tPkgPaths: []string{\"github.com/goplus/yap/ytest\", \"testing\"}}, true\n\t\t}\n\t\treturn\n\t}\n\tfor _, test := range tests {\n\t\t_ = test.found\n\t\tf := &ast.File{IsClass: test.isClass, IsNormalGox: test.isNormalGox, IsProj: test.isProj}\n\t\tclassType, isTest := GetFileClassType(f, test.fileName, lookupClass)\n\t\tif isTest != test.isTest {\n\t\t\tt.Fatalf(\"%v check classType isTest want %v, got %v.\", test.fileName, test.isTest, isTest)\n\t\t}\n\t\tif classType != test.classType {\n\t\t\tt.Fatalf(\"%v getClassType want %v, got %v.\", test.fileName, test.classType, classType)\n\t\t}\n\t}\n}\n\nfunc TestErrMultiStarRecv(t *testing.T) {\n\t_, _, ok := getRecvType(&ast.StarExpr{\n\t\tX: &ast.StarExpr{},\n\t})\n\tif ok {\n\t\tt.Fatal(\"TestErrMultiStarRecv: no error?\")\n\t}\n}\n\nfunc TestErrAssign(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e == nil {\n\t\t\tt.Fatal(\"TestErrAssign: no panic?\")\n\t\t}\n\t}()\n\tctx := &blockCtx{}\n\tcompileAssignStmt(ctx, &ast.AssignStmt{\n\t\tTok: token.DEFINE,\n\t\tLhs: []ast.Expr{\n\t\t\t&ast.SelectorExpr{\n\t\t\t\tX:   ast.NewIdent(\"foo\"),\n\t\t\t\tSel: ast.NewIdent(\"bar\"),\n\t\t\t},\n\t\t},\n\t})\n}\n\nfunc TestErrPanicToRecv(t *testing.T) {\n\tctx := &blockCtx{\n\t\ttlookup: &typeParamLookup{\n\t\t\t[]*types.TypeParam{\n\t\t\t\ttypes.NewTypeParam(types.NewTypeName(0, nil, \"t\", nil), nil),\n\t\t\t},\n\t\t},\n\t}\n\trecv := &ast.FieldList{\n\t\tList: []*ast.Field{\n\t\t\t{Type: &ast.SelectorExpr{}},\n\t\t},\n\t}\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif e := recover(); e == nil {\n\t\t\t\tt.Fatal(\"TestErrPanicToRecv: no panic?\")\n\t\t\t}\n\t\t}()\n\t\ttoRecv(ctx, recv)\n\t}()\n}\n\nfunc TestCompileErrWrapExpr(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e != \"TODO: can't use expr? in global\" {\n\t\t\tt.Fatal(\"TestCompileErrWrapExpr failed\")\n\t\t}\n\t}()\n\tpkg := gogen.NewPackage(\"\", \"foo\", goxConf)\n\tctx := &blockCtx{pkg: pkg, cb: pkg.CB()}\n\tcompileErrWrapExpr(ctx, 0, &ast.ErrWrapExpr{Tok: token.QUESTION}, 0)\n}\n\nfunc TestToString(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e == nil {\n\t\t\tt.Fatal(\"toString: no error?\")\n\t\t}\n\t}()\n\ttoString(&ast.BasicLit{Kind: token.INT, Value: \"1\"})\n}\n\nfunc TestGetTypeName(t *testing.T) {\n\tif getTypeName(types.Typ[types.Int]) != \"int\" {\n\t\tt.Fatal(\"getTypeName int failed\")\n\t}\n\tdefer func() {\n\t\tif e := recover(); e == nil {\n\t\t\tt.Fatal(\"getTypeName: no error?\")\n\t\t}\n\t}()\n\tgetTypeName(types.NewSlice(types.Typ[types.Int]))\n}\n\nfunc TestHandleRecover(t *testing.T) {\n\tvar ctx pkgCtx\n\tctx.handleRecover(\"hello\", nil)\n\tif !(len(ctx.errs) == 1 && ctx.errs[0].Error() == \"hello\") {\n\t\tt.Fatal(\"TestHandleRecover failed:\", ctx.errs)\n\t}\n}\n\nfunc TestCheckCommandWithoutArgs(t *testing.T) {\n\tif checkCommandWithoutArgs(\n\t\t&ast.SelectorExpr{\n\t\t\tX:   &ast.SelectorExpr{X: ast.NewIdent(\"foo\"), Sel: ast.NewIdent(\"bar\")},\n\t\t\tSel: ast.NewIdent(\"val\"),\n\t\t}) != clCommandWithoutArgs {\n\t\tt.Fatal(\"TestCanAutoCall failed\")\n\t}\n}\n\nfunc TestClRangeStmt(t *testing.T) {\n\tctx := &blockCtx{\n\t\tcb: &gogen.CodeBuilder{},\n\t}\n\tstmt := &ast.RangeStmt{\n\t\tTok:  token.DEFINE,\n\t\tX:    &ast.SliceLit{},\n\t\tBody: &ast.BlockStmt{},\n\t}\n\tcompileRangeStmt(ctx, stmt)\n\tstmt.Tok = token.ASSIGN\n\tstmt.Value = &ast.Ident{Name: \"_\"}\n\tcompileRangeStmt(ctx, stmt)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc TestGetStringConst(t *testing.T) {\n\tspx := gogen.PkgRef{Types: types.NewPackage(\"\", \"foo\")}\n\tif v := getStringConst(spx, \"unknown\"); v != \"\" {\n\t\tt.Fatal(\"getStringConst:\", v)\n\t}\n}\n\nfunc TestSpxRef(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); !isError(e, \"foo.bar not found\") {\n\t\t\tt.Fatal(\"TestSpxRef:\", e)\n\t\t}\n\t}()\n\tpkg := gogen.PkgRef{\n\t\tTypes: types.NewPackage(\"foo\", \"foo\"),\n\t}\n\tspxRef(pkg, \"bar\")\n}\n\nfunc isError(e any, msg string) bool {\n\tif e != nil {\n\t\tif err, ok := e.(error); ok {\n\t\t\treturn err.Error() == msg\n\t\t}\n\t\tif err, ok := e.(string); ok {\n\t\t\treturn err == msg\n\t\t}\n\t}\n\treturn false\n}\n\nfunc TestGmxProject(t *testing.T) {\n\tpkg := gogen.NewPackage(\"\", \"foo\", goxConf)\n\tctx := &pkgCtx{\n\t\tprojs:   make(map[string]*gmxProject),\n\t\tclasses: make(map[*ast.File]*gmxClass),\n\t}\n\tgmx := loadClass(ctx, pkg, \"main.t2gmx\", &ast.File{IsProj: true}, &Config{\n\t\tLookupClass: lookupClass,\n\t})\n\tscheds := gmx.getScheds(pkg.CB())\n\tif len(scheds) != 2 || scheds[0] == nil || scheds[0] != scheds[1] {\n\t\tt.Fatal(\"TestGmxProject failed\")\n\t}\n\tgmx.hasScheds = false\n\tif gmx.getScheds(nil) != nil {\n\t\tt.Fatal(\"TestGmxProject failed: hasScheds?\")\n\t}\n\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif e := recover(); e != \"class not found: .abcx\" {\n\t\t\t\tt.Fatal(\"TestGmxProject failed:\", e)\n\t\t\t}\n\t\t}()\n\t\tloadClass(nil, pkg, \"main.abcx\", &ast.File{IsProj: true}, &Config{\n\t\t\tLookupClass: lookupClass,\n\t\t})\n\t}()\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif e := recover(); e != \"multiple project files found: main, main\" {\n\t\t\t\tt.Fatal(\"TestGmxProject failed:\", e)\n\t\t\t}\n\t\t}()\n\t\tloadClass(ctx, pkg, \"main.t2gmx\", &ast.File{IsProj: true}, &Config{\n\t\t\tLookupClass: lookupClass,\n\t\t})\n\t}()\n}\n\nfunc TestSpxLookup(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e == nil {\n\t\t\tt.Fatal(\"TestSpxLookup failed: no error?\")\n\t\t}\n\t}()\n\tspxLookup(nil, \"foo\")\n}\n\nfunc lookupClass(ext string) (c *modfile.Project, ok bool) {\n\tswitch ext {\n\tcase \".t2gmx\", \".t2spx\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \".t2gmx\", Class: \"Game\",\n\t\t\tWorks:    []*modfile.Class{{Ext: \".t2spx\", Class: \"Sprite\"}},\n\t\t\tPkgPaths: []string{\"github.com/goplus/xgo/cl/internal/spx2\"}}, true\n\t}\n\treturn\n}\n\nfunc lookupClassErr(ext string) (c *modfile.Project, ok bool) {\n\tswitch ext {\n\tcase \".t2gmx\", \".t2spx\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \".t2gmx\", Class: \"Game\",\n\t\t\tWorks:    []*modfile.Class{{Ext: \".t2spx\", Class: \"Sprite\"}},\n\t\t\tPkgPaths: []string{\"github.com/goplus/xgo/cl/internal/libc\"}}, true\n\t}\n\treturn\n}\n\nfunc TestGetGoFile(t *testing.T) {\n\tif f := genGoFile(\"a_test.gop\", false); f != testingGoFile {\n\t\tt.Fatal(\"TestGetGoFile:\", f)\n\t}\n\tif f := genGoFile(\"a_test.xgo\", false); f != testingGoFile {\n\t\tt.Fatal(\"TestGetGoFile:\", f)\n\t}\n\tif f := genGoFile(\"a_test.gox\", true); f != testingGoFile {\n\t\tt.Fatal(\"TestGetGoFile:\", f)\n\t}\n\tif f := genGoFile(\"a.gop\", false); f != defaultGoFile {\n\t\tt.Fatal(\"TestGetGoFile:\", f)\n\t}\n\tif f := genGoFile(\"a.xgo\", false); f != defaultGoFile {\n\t\tt.Fatal(\"TestGetGoFile:\", f)\n\t}\n}\n\nfunc TestErrNewType(t *testing.T) {\n\ttestPanic(t, `bar redeclared in this block\n\tprevious declaration at <TODO>\n`, func() {\n\t\tpkg := types.NewPackage(\"\", \"foo\")\n\t\tnewType(pkg, token.NoPos, \"bar\")\n\t\tnewType(pkg, token.NoPos, \"bar\")\n\t})\n}\n\nfunc TestErrCompileBasicLit(t *testing.T) {\n\ttestPanic(t, \"compileBasicLit: invalid syntax\\n\", func() {\n\t\tctx := &blockCtx{cb: new(gogen.CodeBuilder)}\n\t\tcompileBasicLit(ctx, &ast.BasicLit{Kind: token.CSTRING, Value: `\\\\x`})\n\t})\n}\n\nfunc testPanic(t *testing.T, panicMsg string, doPanic func()) {\n\tt.Run(panicMsg, func(t *testing.T) {\n\t\tdefer func() {\n\t\t\tif e := recover(); e == nil {\n\t\t\t\tt.Fatal(\"testPanic: no error?\")\n\t\t\t} else if msg := e.(string); msg != panicMsg {\n\t\t\t\tt.Fatalf(\"\\nResult:\\n%s\\nExpected Panic:\\n%s\\n\", msg, panicMsg)\n\t\t\t}\n\t\t}()\n\t\tdoPanic()\n\t})\n}\n\nfunc TestClassFileEnd(t *testing.T) {\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"get.yap\", `json {\"id\": ${id} }`, parser.ParseXGoClass)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif f.End() != f.ShadowEntry.End() {\n\t\tt.Fatal(\"class file end not shadow entry\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/c.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\t\"strings\"\n)\n\n// -----------------------------------------------------------------------------\n\nconst (\n\tpathLibc   = \"github.com/goplus/lib/c\"\n\tpathLibpy  = \"github.com/goplus/lib/py\"\n\tpathLibcpp = \"github.com/goplus/lib/cpp\"\n)\n\nfunc simplifyXgoPackage(pkgPath string) string {\n\tif strings.HasPrefix(pkgPath, \"xgo/\") || strings.HasPrefix(pkgPath, \"gop/\") {\n\t\treturn \"github.com/goplus/xgo/\" + pkgPath[4:]\n\t}\n\treturn pkgPath\n}\n\nfunc simplifyPkgPath(pkgPath string) string {\n\tswitch pkgPath {\n\tcase \"c\":\n\t\treturn pathLibc\n\tcase \"py\":\n\t\treturn pathLibpy\n\tdefault:\n\t\tif strings.HasPrefix(pkgPath, \"c/\") {\n\t\t\treturn pathLibc + pkgPath[1:]\n\t\t}\n\t\tif strings.HasPrefix(pkgPath, \"py/\") {\n\t\t\treturn pathLibpy + pkgPath[2:]\n\t\t}\n\t\tif strings.HasPrefix(pkgPath, \"cpp/\") {\n\t\t\treturn pathLibcpp + pkgPath[3:]\n\t\t}\n\t\treturn simplifyXgoPackage(pkgPath)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/classfile.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\tgoast \"go/ast\"\n\t\"go/constant\"\n\tgotoken \"go/token\"\n\t\"go/types\"\n\t\"log\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/mod/modfile\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/qiniu/x/stringutil\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype gmxClass struct {\n\tname    string // class type, empty for default project class\n\tclsfile string\n\text     string\n\tproj    *gmxProject\n\tsp      *spxObj\n}\n\nfunc (p *gmxClass) getName(ctx *pkgCtx) string {\n\ttname := p.name\n\tif tname == \"\" { // use default project class\n\t\ttname = p.proj.getGameClass(ctx)\n\t}\n\treturn tname\n}\n\ntype spxObj struct {\n\tobj    gogen.Ref // work base class\n\text    string\n\tproto  string           // work class prototype\n\tprefix string           // work class prefix\n\tfeats  spriteFeat       // work class features\n\tclone  *types.Signature // prototype of Classclone\n\ttypes  []string         // work classes (ie. <type>.spx)\n}\n\nfunc spriteByProto(sprites []*spxObj, proto string) *spxObj {\n\tfor _, sp := range sprites {\n\t\tif sp.proto == proto {\n\t\t\treturn sp\n\t\t}\n\t}\n\treturn nil\n}\n\ntype gmxProject struct {\n\tgameClass_ string    // <gmtype>.gmx\n\tgame       gogen.Ref // Game (project base class)\n\tsprites    []*spxObj // .spx => Sprite\n\tscheds     []string\n\tschedStmts []goast.Stmt // nil or len(scheds) == 2 (delayload)\n\tpkgImps    []gogen.PkgRef\n\tpkgPaths   []string\n\tautoimps   map[string]pkgImp // auto-import statement in gox.mod\n\tgt         *Project\n\thasScheds  bool\n\tgameIsPtr  bool\n\tisTest     bool\n\thasMain_   bool\n}\n\nfunc (p *gmxProject) embed(chk func(name string) bool, flds []*types.Var, pkg *gogen.Package) []*types.Var {\n\tfor _, sp := range p.sprites {\n\t\tif sp.feats&spriteEmbedded != 0 {\n\t\t\tfor _, spt := range sp.types {\n\t\t\t\tspto := pkg.Ref(spt) // work class\n\t\t\t\tif chk != nil && !chk(spto.Name()) {\n\t\t\t\t\tpt := types.NewPointer(spto.Type()) // pointer to work class\n\t\t\t\t\tflds = append(flds, types.NewField(token.NoPos, pkg.Types, spto.Name(), pt, false))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn flds\n}\n\ntype spriteFeat uint\n\nconst (\n\tspriteClassfname spriteFeat = 1 << iota\n\tspriteClassclone\n\n\tspriteEmbedded spriteFeat = 0x80\n)\n\nfunc spriteFeature(elt types.Type, sp *spxObj) {\n\tif intf, ok := elt.(*types.Interface); ok {\n\t\tfor i, n := 0, intf.NumMethods(); i < n; i++ {\n\t\t\tswitch m := intf.Method(i); m.Name() {\n\t\t\tcase \"Classfname\":\n\t\t\t\tsp.feats |= spriteClassfname\n\t\t\tcase \"Classclone\":\n\t\t\t\tsp.clone = m.Type().(*types.Signature)\n\t\t\t\tsp.feats |= spriteClassclone\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc spriteFeatures(game gogen.Ref, sprites []*spxObj) {\n\tif mainFn := findMethod(game, \"Main\"); mainFn != nil {\n\t\tsig := mainFn.Type().(*types.Signature)\n\t\tif t, ok := gogen.CheckSigFuncEx(sig); ok {\n\t\t\tif t, ok := t.(*gogen.TyTemplateRecvMethod); ok {\n\t\t\t\tsig = t.Func.Type().(*types.Signature)\n\t\t\t}\n\t\t}\n\t\tif n := len(sprites); n == 1 && sig.Variadic() {\n\t\t\t// single work class\n\t\t\tin := sig.Params()\n\t\t\tlast := in.At(in.Len() - 1)\n\t\t\telt := last.Type().(*types.Slice).Elem()\n\t\t\tif tn, ok := elt.(*types.Named); ok {\n\t\t\t\telt = tn.Underlying()\n\t\t\t}\n\t\t\tspriteFeature(elt, sprites[0])\n\t\t} else {\n\t\t\t// multiple work classes\n\t\t\tin := sig.Params()\n\t\t\tfor i, narg := 1, in.Len(); i < narg; i++ { // TODO(xsw): error handling\n\t\t\t\ttslice := in.At(i).Type().(*types.Slice)\n\t\t\t\ttn := tslice.Elem().(*types.Named)\n\t\t\t\tsp := spriteByProto(sprites, tn.Obj().Name())\n\t\t\t\tspriteFeature(tn.Underlying(), sp)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (p *gmxProject) getGameClass(ctx *pkgCtx) string {\n\ttname := p.gameClass_ // project class\n\tif tname != \"\" && tname != \"main\" {\n\t\treturn tname\n\t}\n\tgt := p.gt\n\ttname = gt.Class // project base class\n\tif p.gameIsPtr {\n\t\ttname = tname[1:]\n\t}\n\tif ctx.nproj > 1 && !p.hasMain_ {\n\t\ttname = stringutil.Capitalize(path.Base(gt.PkgPaths[0])) + tname\n\t}\n\treturn tname\n}\n\nfunc isTestClass(pkg gogen.PkgRef) bool {\n\tscope := pkg.Types.Scope()\n\treturn scope.Lookup(\"XGoTestClass\") != nil || scope.Lookup(\"GopTestClass\") != nil\n}\n\nfunc (p *gmxProject) hasMain() bool {\n\tif !p.hasMain_ {\n\t\timps := p.pkgImps\n\t\tp.hasMain_ = len(imps) > 0 && isTestClass(imps[0])\n\t}\n\treturn p.hasMain_\n}\n\nfunc (p *gmxProject) getScheds(cb *gogen.CodeBuilder) []goast.Stmt {\n\tif p == nil || !p.hasScheds {\n\t\treturn nil\n\t}\n\tif p.schedStmts == nil {\n\t\tp.schedStmts = make([]goast.Stmt, 2)\n\t\tfor i, v := range p.scheds {\n\t\t\tfn := cb.Val(spxLookup(p.pkgImps, v)).Call(0).InternalStack().Pop().Val\n\t\t\tp.schedStmts[i] = &goast.ExprStmt{X: fn}\n\t\t}\n\t\tif len(p.scheds) < 2 {\n\t\t\tp.schedStmts[1] = p.schedStmts[0]\n\t\t}\n\t}\n\treturn p.schedStmts\n}\n\nvar (\n\trepl = strings.NewReplacer(\":\", \"\", \"#\", \"\", \"-\", \"_\", \".\", \"_\")\n)\n\nfunc ClassNameAndExt(file string) (name, clsfile, ext string) {\n\tfname := filepath.Base(file)\n\tclsfile, ext = modfile.SplitFname(fname)\n\tname = clsfile\n\tif strings.ContainsAny(name, \":#-.\") {\n\t\tname = repl.Replace(name)\n\t}\n\treturn\n}\n\n// GetFileClassType get ast.File classType\n// TODO(xsw): to refactor\n//\n// Deprecated: Don't use it\nfunc GetFileClassType(file *ast.File, filename string, lookupClass func(ext string) (c *Project, ok bool)) (classType string, isTest bool) {\n\tif file.IsClass {\n\t\tvar ext string\n\t\tclassType, _, ext = ClassNameAndExt(filename)\n\t\tif file.IsNormalGox {\n\t\t\tisTest = strings.HasSuffix(ext, \"_test.gox\")\n\t\t\tif !isTest && classType == \"main\" {\n\t\t\t\tclassType = \"_main\"\n\t\t\t}\n\t\t} else {\n\t\t\tisTest = strings.HasSuffix(ext, \"test.gox\")\n\t\t}\n\t\tif file.IsProj && classType == \"main\" {\n\t\t\tif gt, ok := lookupClass(ext); ok {\n\t\t\t\tclassType = gt.Class\n\t\t\t}\n\t\t} else if isTest {\n\t\t\tclassType = casePrefix + testNameSuffix(classType)\n\t\t}\n\t} else if strings.HasSuffix(filename, \"_test.xgo\") || strings.HasSuffix(filename, \"_test.gop\") {\n\t\tisTest = true\n\t}\n\treturn\n}\n\nfunc isGoxTestFile(ext string) bool {\n\treturn strings.HasSuffix(ext, \"test.gox\")\n}\n\nfunc loadClass(ctx *pkgCtx, pkg *gogen.Package, file string, f *ast.File, conf *Config) *gmxProject {\n\ttname, clsfile, ext := ClassNameAndExt(file)\n\tgt, ok := conf.LookupClass(ext)\n\tif !ok {\n\t\tpanic(\"class not found: \" + ext)\n\t}\n\tp, ok := ctx.projs[gt.Ext]\n\tif !ok {\n\t\tpkgPaths := gt.PkgPaths\n\t\tp = &gmxProject{pkgPaths: pkgPaths, isTest: isGoxTestFile(ext), gt: gt}\n\t\tctx.projs[gt.Ext] = p\n\n\t\tp.pkgImps = make([]gogen.PkgRef, len(pkgPaths))\n\t\tfor i, pkgPath := range pkgPaths {\n\t\t\tp.pkgImps[i] = pkg.Import(pkgPath)\n\t\t}\n\n\t\tif len(gt.Import) > 0 {\n\t\t\tautoimps := make(map[string]pkgImp)\n\t\t\tfor _, imp := range gt.Import {\n\t\t\t\tpkgi := pkg.Import(imp.Path)\n\t\t\t\tname := imp.Name\n\t\t\t\tif name == \"\" {\n\t\t\t\t\tname = pkgi.Types.Name()\n\t\t\t\t}\n\t\t\t\tpkgName := types.NewPkgName(token.NoPos, pkg.Types, name, pkgi.Types)\n\t\t\t\tautoimps[name] = pkgImp{pkgi, pkgName}\n\t\t\t}\n\t\t\tp.autoimps = autoimps\n\t\t}\n\n\t\tspx := p.pkgImps[0]\n\t\tnWork := len(gt.Works)\n\t\tsprites := make([]*spxObj, nWork)\n\t\tfor i, v := range gt.Works {\n\t\t\tif nWork > 1 && v.Proto == \"\" {\n\t\t\t\tpanic(\"should have prototype if there are multiple work classes\")\n\t\t\t}\n\t\t\tobj, _ := spxRef(spx, v.Class)\n\t\t\tsp := &spxObj{obj: obj, ext: v.Ext, proto: v.Proto, prefix: v.Prefix}\n\t\t\tif v.Embedded {\n\t\t\t\tsp.feats |= spriteEmbedded\n\t\t\t}\n\t\t\tsprites[i] = sp\n\t\t}\n\t\tp.sprites = sprites\n\t\tif gt.Class != \"\" {\n\t\t\tp.game, p.gameIsPtr = spxRef(spx, gt.Class)\n\t\t\tspriteFeatures(p.game, sprites)\n\t\t}\n\t\tif x := getStringConst(spx, \"Gop_sched\"); x != \"\" { // keep Gop_sched\n\t\t\tp.scheds, p.hasScheds = strings.SplitN(x, \",\", 2), true\n\t\t}\n\t}\n\tcls := &gmxClass{clsfile: clsfile, ext: ext, proj: p}\n\tif f.IsProj {\n\t\tif p.gameClass_ != \"\" {\n\t\t\tpanic(\"multiple project files found: \" + tname + \", \" + p.gameClass_)\n\t\t}\n\t\tp.gameClass_ = tname\n\t\tp.hasMain_ = f.HasShadowEntry()\n\t\tif !p.isTest {\n\t\t\tctx.nproj++\n\t\t}\n\t\tif tname != \"main\" {\n\t\t\tcls.name = tname\n\t\t}\n\t} else {\n\t\tsp := getSpxObj(p, ext)\n\t\ttname := spName(sp, tname)\n\t\tsp.types = append(sp.types, tname)\n\t\tcls.sp = sp\n\t\tcls.name = tname\n\t}\n\tctx.classes[f] = cls\n\tif debugLoad {\n\t\tlog.Println(\"==> InitClass\", tname, \"isProj:\", f.IsProj)\n\t}\n\treturn p\n}\n\ntype none = struct{}\n\nvar specialNames = map[string]none{\n\t\"init\": {}, \"main\": {}, \"go\": {}, \"goto\": {}, \"type\": {}, \"var\": {}, \"import\": {},\n\t\"package\": {}, \"interface\": {}, \"struct\": {}, \"const\": {}, \"func\": {}, \"map\": {},\n\t\"for\": {}, \"if\": {}, \"else\": {}, \"switch\": {}, \"case\": {}, \"select\": {}, \"defer\": {},\n\t\"range\": {}, \"return\": {}, \"break\": {}, \"continue\": {}, \"fallthrough\": {}, \"default\": {},\n}\n\nfunc spName(sp *spxObj, name string) string {\n\tif sp.prefix != \"\" {\n\t\treturn sp.prefix + name\n\t}\n\tif _, ok := specialNames[name]; ok {\n\t\tname = \"_\" + name\n\t}\n\treturn name\n}\n\nfunc getSpxObj(p *gmxProject, ext string) *spxObj {\n\tfor _, sp := range p.sprites {\n\t\tif sp.ext == ext {\n\t\t\treturn sp\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc spxLookup(pkgImps []gogen.PkgRef, name string) gogen.Ref {\n\tfor _, pkg := range pkgImps {\n\t\tif o := pkg.TryRef(name); o != nil {\n\t\t\treturn o\n\t\t}\n\t}\n\tpanic(\"spxLookup: symbol not found - \" + name)\n}\n\nfunc spxTryRef(spx gogen.PkgRef, typ string) (obj types.Object, isPtr bool) {\n\tif strings.HasPrefix(typ, \"*\") {\n\t\ttyp, isPtr = typ[1:], true\n\t}\n\tobj = spx.TryRef(typ)\n\treturn\n}\n\nfunc spxRef(spx gogen.PkgRef, typ string) (obj gogen.Ref, isPtr bool) {\n\tobj, isPtr = spxTryRef(spx, typ)\n\tif obj == nil {\n\t\tpanic(spx.Types.Name() + \".\" + typ + \" not found\")\n\t}\n\treturn\n}\n\nfunc getStringConst(spx gogen.PkgRef, name string) string {\n\tif o := spx.TryRef(name); o != nil {\n\t\tif c, ok := o.(*types.Const); ok {\n\t\t\treturn constant.StringVal(c.Val())\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc setBodyHandler(ctx *blockCtx) {\n\tif proj := ctx.proj; proj != nil { // in an XGo class file\n\t\tif scheds := proj.getScheds(ctx.cb); scheds != nil {\n\t\t\tctx.cb.SetBodyHandler(func(body *goast.BlockStmt, kind int) {\n\t\t\t\tidx := 0\n\t\t\t\tif len(body.List) == 0 {\n\t\t\t\t\tidx = 1\n\t\t\t\t}\n\t\t\t\tgogen.InsertStmtFront(body, scheds[idx])\n\t\t\t})\n\t\t}\n\t}\n}\n\nconst (\n\tcasePrefix = \"case\"\n)\n\nfunc testNameSuffix(testType string) string {\n\tif c := testType[0]; c >= 'A' && c <= 'Z' {\n\t\treturn testType\n\t}\n\treturn \"_\" + testType\n}\n\nfunc gmxTestFunc(pkg *gogen.Package, testType string, isProj bool) {\n\tif isProj {\n\t\tgenTestFunc(pkg, \"TestMain\", testType, \"m\", \"M\")\n\t} else {\n\t\tname := testNameSuffix(testType)\n\t\tgenTestFunc(pkg, \"Test\"+name, casePrefix+name, \"t\", \"T\")\n\t}\n}\n\nfunc genTestFunc(pkg *gogen.Package, name, testType, param, paramType string) {\n\ttesting := pkg.Import(\"testing\")\n\tobjT := testing.Ref(paramType)\n\tparamT := types.NewParam(token.NoPos, pkg.Types, param, types.NewPointer(objT.Type()))\n\tparams := types.NewTuple(paramT)\n\n\tpkg.NewFunc(nil, name, params, nil, false).BodyStart(pkg).\n\t\tVal(pkg.Builtin().Ref(\"new\")).Val(pkg.Ref(testType)).Call(1).\n\t\tMemberVal(\"TestMain\", 0).Val(paramT).Call(1).EndStmt().\n\t\tEnd()\n}\n\nfunc gmxCheckProjs(pkg *gogen.Package, ctx *pkgCtx) (*gmxProject, bool) {\n\tvar projMain, projNoMain *gmxProject\n\tvar multiMain, multiNoMain bool\n\tfor _, v := range ctx.projs {\n\t\tif v.isTest {\n\t\t\tcontinue\n\t\t}\n\t\tif v.hasMain() {\n\t\t\tif projMain != nil {\n\t\t\t\tmultiMain = true\n\t\t\t} else {\n\t\t\t\tprojMain = v\n\t\t\t}\n\t\t} else {\n\t\t\tif projNoMain != nil {\n\t\t\t\tmultiNoMain = true\n\t\t\t} else {\n\t\t\t\tprojNoMain = v\n\t\t\t}\n\t\t}\n\t\tif v.game != nil {\n\t\t\tgmxProjMain(pkg, ctx, v)\n\t\t}\n\t}\n\tif projMain != nil {\n\t\treturn projMain, multiMain\n\t}\n\treturn projNoMain, multiNoMain\n}\n\nfunc gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {\n\tbase := proj.game                      // project base class\n\tclassType := proj.getGameClass(parent) // project class\n\tld := getTypeLoader(parent, parent.syms, token.NoPos, token.NoPos, classType)\n\tif ld.typ == nil { // no project class, use default\n\t\tld.typ = func() {\n\t\t\tif debugLoad {\n\t\t\t\tlog.Println(\"==> Load > NewType\", classType)\n\t\t\t}\n\t\t\told, _ := pkg.SetCurFile(defaultGoFile, true)\n\t\t\tdefer pkg.RestoreCurFile(old)\n\n\t\t\tbaseType := base.Type()\n\t\t\tif proj.gameIsPtr {\n\t\t\t\tbaseType = types.NewPointer(baseType)\n\t\t\t}\n\n\t\t\tflds := proj.embed(nil, []*types.Var{\n\t\t\t\ttypes.NewField(token.NoPos, pkg.Types, base.Name(), baseType, true),\n\t\t\t}, pkg)\n\n\t\t\tdecl := pkg.NewTypeDefs().NewType(classType)\n\t\t\tld.typInit = func() { // decycle\n\t\t\t\tif debugLoad {\n\t\t\t\t\tlog.Println(\"==> Load > InitType\", classType)\n\t\t\t\t}\n\t\t\t\told, _ := pkg.SetCurFile(defaultGoFile, true)\n\t\t\t\tdefer pkg.RestoreCurFile(old)\n\n\t\t\t\tdecl.InitType(pkg, types.NewStruct(flds, nil))\n\t\t\t}\n\t\t\tparent.tylds = append(parent.tylds, ld)\n\t\t}\n\t}\n\tld.methods = append(ld.methods, func() {\n\t\told, _ := pkg.SetCurFile(defaultGoFile, true)\n\t\tdefer pkg.RestoreCurFile(old)\n\t\tdoInitType(ld)\n\n\t\tt := pkg.Ref(classType).Type()\n\t\trecv := types.NewParam(token.NoPos, pkg.Types, \"this\", types.NewPointer(t))\n\t\tsig := types.NewSignatureType(recv, nil, nil, nil, nil, false)\n\t\tfn, err := pkg.NewFuncWith(token.NoPos, \"Main\", sig, func() gotoken.Pos {\n\t\t\t// parent.ProjFile() never be nil here\n\t\t\treturn parent.ProjFile().Pos()\n\t\t})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tparent.inits = append(parent.inits, func() {\n\t\t\told, _ := pkg.SetCurFile(defaultGoFile, true)\n\t\t\tdefer pkg.RestoreCurFile(old)\n\n\t\t\tcb := fn.BodyStart(pkg).Typ(base.Type()).MemberVal(\"Main\", 0)\n\t\t\tstk := cb.InternalStack()\n\n\t\t\t// force remove //line comments for main func\n\t\t\tcb.SetComments(nil, false)\n\n\t\t\tmainFn := stk.Pop()\n\t\t\tsigParams := mainFn.Type.(*types.Signature).Params()\n\t\t\tcallMain := func() {\n\t\t\t\tsrc := parent.lookupClassNode(proj.gameClass_)\n\t\t\t\tstk.Push(mainFn)\n\t\t\t\tif _, isPtr := sigParams.At(0).Type().(*types.Pointer); isPtr {\n\t\t\t\t\tcb.Val(recv, src).MemberRef(base.Name()).UnaryOp(gotoken.AND)\n\t\t\t\t} else {\n\t\t\t\t\tcb.Val(recv, src) // template recv method\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tiobj := 0\n\t\t\tnarg := sigParams.Len()\n\t\t\tif narg > 1 {\n\t\t\t\tsprites := proj.sprites\n\t\t\t\tif len(sprites) == 1 && sprites[0].proto == \"\" { // no work class prototype\n\t\t\t\t\tsp := sprites[0]\n\t\t\t\t\tnarg = 1 + len(sp.types)\n\t\t\t\t\tgenWorkClasses(pkg, parent, cb, recv, sp, iobj, -1, callMain)\n\t\t\t\t} else {\n\t\t\t\t\tlstNames := make([]string, narg)\n\t\t\t\t\tfor i := 1; i < narg; i++ {\n\t\t\t\t\t\ttslice := sigParams.At(i).Type()\n\t\t\t\t\t\ttn := tslice.(*types.Slice).Elem().(*types.Named)\n\t\t\t\t\t\tsp := spriteByProto(sprites, tn.Obj().Name()) // work class\n\t\t\t\t\t\tif n := len(sp.types); n > 0 {\n\t\t\t\t\t\t\tlstNames[i] = genWorkClasses(pkg, parent, cb, recv, sp, iobj, i, nil)\n\t\t\t\t\t\t\tcb.SliceLitEx(tslice, n, false).EndInit(1)\n\t\t\t\t\t\t\tiobj += n\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcallMain()\n\t\t\t\t\tfor i := 1; i < narg; i++ {\n\t\t\t\t\t\tif lstName := lstNames[i]; lstName != \"\" {\n\t\t\t\t\t\t\tcb.VarVal(lstName)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcb.Val(nil)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcallMain()\n\t\t\t}\n\n\t\t\tcb.Call(narg).EndStmt().End()\n\t\t})\n\t})\n}\n\nfunc genWorkClasses(\n\tpkg *gogen.Package, parent *pkgCtx, cb *gogen.CodeBuilder, recv *types.Var,\n\tsp *spxObj, iobj, ilst int, callMain func()) (lstName string) {\n\tconst (\n\t\tindexGame     = 1\n\t\tobjNamePrefix = \"_xgo_obj\"\n\t\tlstNamePrefix = \"_xgo_lst\"\n\t)\n\tembedded := (sp.feats&spriteEmbedded != 0)\n\tsptypes := sp.types\n\tfor i, spt := range sptypes {\n\t\tsrc := parent.lookupClassNode(spt)\n\t\tspto := pkg.Ref(spt)\n\t\tobjName := objNamePrefix + strconv.Itoa(iobj+i)\n\t\tcb.DefineVarStart(token.NoPos, objName).\n\t\t\tVal(indexGame, src).Val(recv, src).StructLit(spto.Type(), 2, true, src).\n\t\t\tUnaryOp(gotoken.AND).EndInit(1)\n\t\tif embedded {\n\t\t\tcb.Val(recv, src).MemberRef(spt, src).VarVal(objName, src).Assign(1)\n\t\t}\n\t}\n\tif ilst > 0 {\n\t\tlstName = lstNamePrefix + strconv.Itoa(ilst-1)\n\t\tcb.DefineVarStart(token.NoPos, lstName)\n\t} else {\n\t\tcallMain()\n\t}\n\tfor i, spt := range sptypes {\n\t\tsrc := parent.lookupClassNode(spt)\n\t\tobjName := objNamePrefix + strconv.Itoa(iobj+i)\n\t\tcb.VarVal(objName, src)\n\t}\n\treturn\n}\n\nfunc genMainFunc(pkg *gogen.Package, gameClass string) {\n\tif o := pkg.TryRef(gameClass); o != nil {\n\t\t// force remove //line comments for main func\n\t\tpkg.CB().SetComments(nil, false)\n\t\t// new(gameClass).Main()\n\t\tnew := pkg.Builtin().Ref(\"new\")\n\t\tpkg.NewFunc(nil, \"main\", nil, nil, false).BodyStart(pkg).\n\t\t\tVal(new).Val(o).Call(1).MemberVal(\"Main\", 0).Call(0).EndStmt().\n\t\t\tEnd()\n\t}\n}\n\nfunc findMethod(o types.Object, name string) *types.Func {\n\tif obj, ok := o.(*types.TypeName); ok {\n\t\treturn findMethodByType(obj.Type(), name)\n\t}\n\treturn nil\n}\n\nfunc findMethodByType(typ types.Type, name string) *types.Func {\n\tif t, ok := typ.(*types.Named); ok {\n\t\tfor i, n := 0, t.NumMethods(); i < n; i++ {\n\t\t\tf := t.Method(i)\n\t\t\tif f.Name() == name {\n\t\t\t\treturn f\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc makeMainSig(recv *types.Var, f *types.Func) *types.Signature {\n\tconst (\n\t\tnamePrefix = \"_xgo_arg\"\n\t)\n\tsig := f.Type().(*types.Signature)\n\tin := sig.Params()\n\tnin := in.Len()\n\tpkg := recv.Pkg()\n\tparams := make([]*types.Var, nin)\n\tfor i := 0; i < nin; i++ {\n\t\tparamName := namePrefix + strconv.Itoa(i)\n\t\tparams[i] = types.NewParam(token.NoPos, pkg, paramName, in.At(i).Type())\n\t}\n\treturn types.NewSignatureType(recv, nil, nil, types.NewTuple(params...), sig.Results(), false)\n}\n\nfunc genClassfname(ctx *blockCtx, c *gmxClass) {\n\tpkg := ctx.pkg\n\trecv := toRecv(ctx, ctx.classRecv)\n\tret := types.NewTuple(pkg.NewParam(token.NoPos, \"\", types.Typ[types.String]))\n\tpkg.NewFunc(recv, \"Classfname\", nil, ret, false).BodyStart(pkg).\n\t\tVal(c.clsfile).Return(1).\n\t\tEnd()\n}\n\nfunc genClassclone(ctx *blockCtx, classclone *types.Signature) {\n\tconst (\n\t\tnameRet = \"_xgo_ret\"\n\t)\n\tpkg := ctx.pkg\n\trecv := toRecv(ctx, ctx.classRecv)\n\tret := classclone.Results()\n\tpkg.NewFunc(recv, \"Classclone\", nil, ret, false).BodyStart(pkg).\n\t\tDefineVarStart(token.NoPos, nameRet).VarVal(\"this\").Elem().EndInit(1).\n\t\tVarVal(nameRet).UnaryOp(gotoken.AND).Return(1).\n\t\tEnd()\n}\n\nfunc astEmptyEntrypoint(f *ast.File) {\n\tvar entry = getEntrypoint(f)\n\tvar hasEntry bool\n\tfor _, decl := range f.Decls {\n\t\tswitch d := decl.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tif d.Name.Name == entry {\n\t\t\t\thasEntry = true\n\t\t\t}\n\t\t}\n\t}\n\tif !hasEntry {\n\t\tf.Decls = append(f.Decls, &ast.FuncDecl{\n\t\t\tName: &ast.Ident{\n\t\t\t\tName: entry,\n\t\t\t},\n\t\t\tType: &ast.FuncType{\n\t\t\t\tParams: &ast.FieldList{},\n\t\t\t},\n\t\t\tBody:   &ast.BlockStmt{},\n\t\t\tShadow: true,\n\t\t})\n\t}\n}\n\nfunc getEntrypoint(f *ast.File) string {\n\tswitch {\n\tcase f.IsProj:\n\t\treturn \"MainEntry\"\n\tcase f.IsClass:\n\t\treturn \"Main\"\n\tcase inMainPkg(f):\n\t\treturn \"main\"\n\tdefault:\n\t\treturn \"init\"\n\t}\n}\n\nfunc inMainPkg(f *ast.File) bool {\n\treturn f.Name.Name == \"main\"\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/cltest/cltest.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cltest\n\nimport (\n\t\"bytes\"\n\t\"io/fs\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/mod\"\n\t\"github.com/goplus/mod/env\"\n\t\"github.com/goplus/mod/modfile\"\n\t\"github.com/goplus/mod/xgomod\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/parser/fsx\"\n\t\"github.com/goplus/xgo/parser/fsx/memfs\"\n\t\"github.com/goplus/xgo/scanner\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/qiniu/x/test\"\n)\n\nvar (\n\tXGoRoot string\n\tXGo     *env.XGo\n\tConf    *cl.Config\n)\n\nfunc init() {\n\tXGo = &env.XGo{Version: \"1.0\"}\n\tgogen.SetDebug(gogen.DbgFlagAll)\n\tcl.SetDebug(cl.DbgFlagAll | cl.FlagNoMarkAutogen)\n\tfset := token.NewFileSet()\n\timp := tool.NewImporter(nil, XGo, fset)\n\tXGoRoot, _, _ = mod.FindGoMod(\"\")\n\tConf = &cl.Config{\n\t\tFset:          fset,\n\t\tImporter:      imp,\n\t\tRecorder:      gopRecorder{},\n\t\tLookupClass:   LookupClass,\n\t\tNoFileLine:    true,\n\t\tNoAutoGenMain: true,\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\nfunc LookupClass(ext string) (c *modfile.Project, ok bool) {\n\tswitch ext {\n\tcase \".tgmx\", \".tspx\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \".tgmx\", Class: \"*MyGame\",\n\t\t\tWorks:    []*modfile.Class{{Ext: \".tspx\", Class: \"Sprite\"}},\n\t\t\tPkgPaths: []string{\"github.com/goplus/xgo/cl/internal/spx\", \"math\"}}, true\n\tcase \".t2gmx\", \".t2spx\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \".t2gmx\", Class: \"Game\",\n\t\t\tWorks: []*modfile.Class{\n\t\t\t\t{Ext: \".t2spx\", Class: \"Sprite\"},\n\t\t\t},\n\t\t\tPkgPaths: []string{\"github.com/goplus/xgo/cl/internal/spx2\"}}, true\n\tcase \".t4gmx\", \".t4spx\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \".t4gmx\", Class: \"*MyGame\",\n\t\t\tWorks:    []*modfile.Class{{Ext: \".t4spx\", Class: \"Sprite\"}},\n\t\t\tPkgPaths: []string{\"github.com/goplus/xgo/cl/internal/spx4\", \"math\"}}, true\n\tcase \".t5gmx\", \".t5spx\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \".t5gmx\", Class: \"*MyGame\",\n\t\t\tWorks:    []*modfile.Class{{Ext: \".t5spx\", Class: \"Sprite\", Embedded: true}},\n\t\t\tPkgPaths: []string{\"github.com/goplus/xgo/cl/internal/spx4\", \"math\"}}, true\n\tcase \"_spx.gox\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \"_spx.gox\", Class: \"Game\",\n\t\t\tWorks:    []*modfile.Class{{Ext: \"_spx.gox\", Class: \"Sprite\"}},\n\t\t\tPkgPaths: []string{\"github.com/goplus/xgo/cl/internal/spx3\", \"math\"},\n\t\t\tImport:   []*modfile.Import{{Path: \"github.com/goplus/xgo/cl/internal/spx3/jwt\"}}}, true\n\tcase \"_xtest.gox\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \"_xtest.gox\", Class: \"App\",\n\t\t\tWorks:    []*modfile.Class{{Ext: \"_xtest.gox\", Class: \"Case\"}},\n\t\t\tPkgPaths: []string{\"github.com/goplus/xgo/test\", \"testing\"}}, true\n\tcase \"_mcp.gox\", \"_tool.gox\", \"_prompt.gox\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \"_mcp.gox\", Class: \"Game\",\n\t\t\tWorks: []*modfile.Class{\n\t\t\t\t{Ext: \"_tool.gox\", Class: \"Tool\", Proto: \"ToolProto\", Prefix: \"Tool_\"},\n\t\t\t\t{Ext: \"_prompt.gox\", Class: \"Prompt\", Proto: \"PromptProto\", Embedded: true},\n\t\t\t\t{Ext: \"_res.gox\", Class: \"Resource\", Proto: \"ResourceProto\"},\n\t\t\t},\n\t\t\tPkgPaths: []string{\"github.com/goplus/xgo/cl/internal/mcp\"}}, true\n\tcase \".gsh\":\n\t\treturn &modfile.Project{\n\t\t\tExt: \".gsh\", Class: \"App\",\n\t\t\tPkgPaths: []string{\"github.com/qiniu/x/gsh\", \"math\"},\n\t\t}, true\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\nfunc Named(t *testing.T, name string, gopcode, expected string) {\n\tt.Run(name, func(t *testing.T) {\n\t\tDo(t, gopcode, expected)\n\t})\n}\n\nfunc Do(t *testing.T, gopcode, expected string) {\n\tDoExt(t, Conf, \"main\", gopcode, expected)\n}\n\nfunc DoWithFname(t *testing.T, gopcode, expected string, fname string) {\n\tfs := memfs.SingleFile(\"/foo\", fname, gopcode)\n\tDoFS(t, Conf, fs, \"/foo\", nil, \"main\", expected)\n}\n\nfunc DoExt(t *testing.T, conf *cl.Config, pkgname, gopcode, expected string) {\n\tfs := memfs.SingleFile(\"/foo\", \"bar.xgo\", gopcode)\n\tDoFS(t, conf, fs, \"/foo\", nil, pkgname, expected)\n}\n\nfunc Mixed(t *testing.T, pkgname, gocode, gopcode, expected string, outline ...bool) {\n\tconf := *Conf\n\tconf.Outline = (outline != nil && outline[0])\n\tfs := memfs.TwoFiles(\"/foo\", \"a.go\", gocode, \"b.xgo\", gopcode)\n\tDoFS(t, &conf, fs, \"/foo\", nil, pkgname, expected)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc DoFS(\n\tt *testing.T, conf *cl.Config,\n\tfs parser.FileSystem, dir string, filter func(fs.FileInfo) bool, pkgname string, exp any) {\n\tcl.SetDisableRecover(true)\n\tdefer cl.SetDisableRecover(false)\n\n\tfset := conf.Fset\n\tpkgs, err := parser.ParseFSDir(fset, fs, dir, parser.Config{\n\t\tMode:   parser.ParseComments,\n\t\tFilter: filter,\n\t})\n\tif err != nil {\n\t\tscanner.PrintError(os.Stderr, err)\n\t\tt.Fatal(\"ParseFSDir:\", err)\n\t}\n\tbar := pkgs[pkgname]\n\tpkg, err := cl.NewPackage(\"github.com/goplus/xgo/cl\", bar, conf)\n\tif err != nil {\n\t\tt.Fatal(\"NewPackage:\", err)\n\t}\n\tvar b bytes.Buffer\n\terr = pkg.WriteTo(&b)\n\tif err != nil {\n\t\tt.Fatal(\"gogen.WriteTo failed:\", err)\n\t}\n\tif expected, ok := exp.(string); ok {\n\t\tresult := b.String()\n\t\tif result != expected {\n\t\t\tt.Fatalf(\"\\nResult:\\n%s\\nExpected:\\n%s\\n\", result, expected)\n\t\t}\n\t} else if test.Diff(t, dir+\"/result.txt\", b.Bytes(), exp.([]byte)) {\n\t\tt.Fatal(dir, \": unexpect result\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\nfunc FromDir(t *testing.T, sel, relDir string) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = path.Join(dir, relDir)\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"ReadDir failed:\", err)\n\t}\n\tfor _, fi := range fis {\n\t\tname := fi.Name()\n\t\tif !fi.IsDir() || strings.HasPrefix(name, \"_\") {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttestFrom(t, dir+\"/\"+name, sel)\n\t\t})\n\t}\n}\n\nfunc testFrom(t *testing.T, pkgDir, sel string) {\n\tif sel != \"\" && !strings.Contains(pkgDir, sel) {\n\t\treturn\n\t}\n\tlog.Println(\"Parsing\", pkgDir)\n\tout := pkgDir + \"/out.go\"\n\tb, _ := os.ReadFile(out)\n\tfilter := func(fi fs.FileInfo) bool {\n\t\treturn fi.Name() == \"in.xgo\"\n\t}\n\tconf := Conf\n\tgoMod := pkgDir + \"/go.mod\"\n\tif _, err := os.Stat(goMod); err == nil {\n\t\tif mod, err := xgomod.Load(pkgDir); err == nil {\n\t\t\tconfCopy := *Conf\n\t\t\tconfCopy.Importer = tool.NewImporter(mod, XGo, conf.Fset)\n\t\t\tconf = &confCopy\n\t\t}\n\t} else {\n\t\tconfCopy := *Conf\n\t\tconfCopy.RelativeBase = XGoRoot\n\t\tconf = &confCopy\n\t}\n\tDoFS(t, conf, fsx.Local, pkgDir, filter, \"main\", b)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/cltest/error_msg.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cltest\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/parser/fsx/memfs\"\n\t\"github.com/goplus/xgo/scanner\"\n)\n\nfunc Error(t *testing.T, msg, src string) {\n\tErrorEx(t, \"main\", \"bar.xgo\", msg, src)\n}\n\nfunc ErrorEx(t *testing.T, pkgname, filename, msg, src string) {\n\tfs := memfs.SingleFile(\"/foo\", filename, src)\n\tpkgs, err := parser.ParseFSDir(Conf.Fset, fs, \"/foo\", parser.Config{})\n\tif err != nil {\n\t\tscanner.PrintError(os.Stderr, err)\n\t\tt.Fatal(\"parser.ParseFSDir failed\")\n\t}\n\tconf := *Conf\n\tconf.NoFileLine = false\n\tconf.RelativeBase = \"/foo\"\n\tbar := pkgs[pkgname]\n\t_, err = cl.NewPackage(\"\", bar, &conf)\n\tif err == nil {\n\t\tt.Fatal(\"no error?\")\n\t}\n\tif ret := err.Error(); ret != msg {\n\t\tt.Fatalf(\"\\nError: \\\"%s\\\"\\nExpected: \\\"%s\\\"\\n\", ret, msg)\n\t}\n}\n\nfunc ErrorAst(t *testing.T, pkgname, filename, msg, src string) {\n\tf, _ := parser.ParseFile(Conf.Fset, filename, src, parser.AllErrors)\n\tpkg := &ast.Package{\n\t\tName:  pkgname,\n\t\tFiles: map[string]*ast.File{filename: f},\n\t}\n\tconf := *Conf\n\tconf.NoFileLine = false\n\tconf.RelativeBase = \"/foo\"\n\t_, err := cl.NewPackage(\"\", pkg, &conf)\n\tif err == nil {\n\t\tt.Fatal(\"no error?\")\n\t}\n\tif ret := err.Error(); ret != msg {\n\t\tt.Fatalf(\"\\nError: \\\"%s\\\"\\nExpected: \\\"%s\\\"\\n\", ret, msg)\n\t}\n}\n"
  },
  {
    "path": "cl/cltest/recorder.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cltest\n\nimport (\n\t\"go/types\"\n\n\t\"github.com/goplus/xgo/ast\"\n)\n\ntype gopRecorder struct {\n}\n\n// Type maps expressions to their types, and for constant\n// expressions, also their values. Invalid expressions are\n// omitted.\n//\n// For (possibly parenthesized) identifiers denoting built-in\n// functions, the recorded signatures are call-site specific:\n// if the call result is not a constant, the recorded type is\n// an argument-specific signature. Otherwise, the recorded type\n// is invalid.\n//\n// The Types map does not record the type of every identifier,\n// only those that appear where an arbitrary expression is\n// permitted. For instance, the identifier f in a selector\n// expression x.f is found only in the Selections map, the\n// identifier z in a variable declaration 'var z int' is found\n// only in the Defs map, and identifiers denoting packages in\n// qualified identifiers are collected in the Uses map.\nfunc (info gopRecorder) Type(e ast.Expr, tv types.TypeAndValue) {\n}\n\n// Instantiate maps identifiers denoting generic types or functions to their\n// type arguments and instantiated type.\n//\n// For example, Instantiate will map the identifier for 'T' in the type\n// instantiation T[int, string] to the type arguments [int, string] and\n// resulting instantiated *Named type. Given a generic function\n// func F[A any](A), Instances will map the identifier for 'F' in the call\n// expression F(int(1)) to the inferred type arguments [int], and resulting\n// instantiated *Signature.\n//\n// Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs\n// results in an equivalent of Instances[id].Type.\nfunc (info gopRecorder) Instantiate(id *ast.Ident, inst types.Instance) {\n}\n\n// Def maps identifiers to the objects they define (including\n// package names, dots \".\" of dot-imports, and blank \"_\" identifiers).\n// For identifiers that do not denote objects (e.g., the package name\n// in package clauses, or symbolic variables t in t := x.(type) of\n// type switch headers), the corresponding objects are nil.\n//\n// For an embedded field, Def maps the field *Var it defines.\n//\n// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()\nfunc (info gopRecorder) Def(id *ast.Ident, obj types.Object) {\n}\n\n// Use maps identifiers to the objects they denote.\n//\n// For an embedded field, Use maps the *TypeName it denotes.\n//\n// Invariant: Uses[id].Pos() != id.Pos()\nfunc (info gopRecorder) Use(id *ast.Ident, obj types.Object) {\n}\n\n// Implicit maps nodes to their implicitly declared objects, if any.\n// The following node and object types may appear:\n//\n//\tnode               declared object\n//\n//\t*ast.ImportSpec    *PkgName for imports without renames\n//\t*ast.CaseClause    type-specific *Var for each type switch case clause (incl. default)\n//\t*ast.Field         anonymous parameter *Var (incl. unnamed results)\nfunc (info gopRecorder) Implicit(node ast.Node, obj types.Object) {\n}\n\n// Select maps selector expressions (excluding qualified identifiers)\n// to their corresponding selections.\nfunc (info gopRecorder) Select(e *ast.SelectorExpr, sel *types.Selection) {\n}\n\n// Scope maps ast.Nodes to the scopes they define. Package scopes are not\n// associated with a specific node but with all files belonging to a package.\n// Thus, the package scope can be found in the type-checked Package object.\n// Scopes nest, with the Universe scope being the outermost scope, enclosing\n// the package scope, which contains (one or more) files scopes, which enclose\n// function scopes which in turn enclose statement and function literal scopes.\n// Note that even though package-level functions are declared in the package\n// scope, the function scopes are embedded in the file scope of the file\n// containing the function declaration.\n//\n// The following node types may appear in Scopes:\n//\n//\t*ast.File\n//\t*ast.FuncType\n//\t*ast.TypeSpec\n//\t*ast.BlockStmt\n//\t*ast.IfStmt\n//\t*ast.SwitchStmt\n//\t*ast.TypeSwitchStmt\n//\t*ast.CaseClause\n//\t*ast.CommClause\n//\t*ast.ForStmt\n//\t*ast.RangeStmt\nfunc (info gopRecorder) Scope(n ast.Node, scope *types.Scope) {\n}\n"
  },
  {
    "path": "cl/cltest/spx.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cltest\n\nimport (\n\t\"bytes\"\n\t\"io/fs\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/goplus/mod/modfile\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/parser/fsx\"\n\t\"github.com/goplus/xgo/parser/fsx/memfs\"\n\t\"github.com/goplus/xgo/scanner\"\n\t\"github.com/qiniu/x/test\"\n)\n\nfunc spxParserConf() parser.Config {\n\treturn parser.Config{\n\t\tClassKind: func(fname string) (isProj bool, ok bool) {\n\t\t\text := modfile.ClassExt(fname)\n\t\t\tc, ok := LookupClass(ext)\n\t\t\tif ok {\n\t\t\t\tisProj = c.IsProj(ext, fname)\n\t\t\t}\n\t\t\treturn\n\t\t},\n\t}\n}\n\nfunc Spx(t *testing.T, gmx, spxcode, expected string) {\n\tSpxEx(t, gmx, spxcode, expected, \"index.tgmx\", \"bar.tspx\")\n}\n\nfunc SpxEx(t *testing.T, gmx, spxcode, expected, gmxfile, spxfile string) {\n\tSpxWithConf(t, \"gopSpxTest\", Conf, gmx, spxcode, expected, gmxfile, spxfile, \"\")\n}\n\nfunc SpxEx2(t *testing.T, gmx, spxcode, expected, gmxfile, spxfile, resultFile string) {\n\tSpxWithConf(t, \"gopSpxTest\", Conf, gmx, spxcode, expected, gmxfile, spxfile, resultFile)\n}\n\nfunc SpxWithConf(t *testing.T, name string, conf *cl.Config, gmx, spxcode, expected, gmxfile, spxfile, resultFile string) {\n\tt.Run(name, func(t *testing.T) {\n\t\tfs := memfs.TwoFiles(\"/foo\", spxfile, spxcode, gmxfile, gmx)\n\t\tif gmxfile == \"\" {\n\t\t\tfs = memfs.SingleFile(\"/foo\", spxfile, spxcode)\n\t\t}\n\t\tSpxTest(t, fs, \"/foo\", spxParserConf(), conf, expected, resultFile)\n\t})\n}\n\nfunc SpxTest(t *testing.T, fs fsx.FileSystem, dir string, parseConf parser.Config, clConf *cl.Config, exp any, resultFile string) {\n\tcl.SetDisableRecover(true)\n\tdefer cl.SetDisableRecover(false)\n\n\tpkgs, err := parser.ParseFSDir(Conf.Fset, fs, dir, parseConf)\n\tif err != nil {\n\t\tscanner.PrintError(os.Stderr, err)\n\t\tt.Fatal(\"ParseFSDir:\", err)\n\t}\n\tbar := pkgs[\"main\"]\n\tpkg, err := cl.NewPackage(\"\", bar, clConf)\n\tif err != nil {\n\t\tt.Fatal(\"NewPackage:\", err)\n\t}\n\tvar b bytes.Buffer\n\terr = pkg.WriteTo(&b, resultFile)\n\tif err != nil {\n\t\tt.Fatal(\"gogen.WriteTo failed:\", err)\n\t}\n\tif expected, ok := exp.(string); ok {\n\t\tresult := b.String()\n\t\tif result != expected {\n\t\t\tt.Fatalf(\"\\nResult:\\n%s\\nExpected:\\n%s\\n\", result, expected)\n\t\t}\n\t} else if test.Diff(t, dir+\"/result.txt\", b.Bytes(), exp.([]byte)) {\n\t\tt.Fatal(dir, \": unexpect result\")\n\t}\n}\n\nfunc SpxErrorFS(t *testing.T, msg string, fs parser.FileSystem) {\n\tpkgs, err := parser.ParseFSDir(Conf.Fset, fs, \"/foo\", spxParserConf())\n\tif err != nil {\n\t\tscanner.PrintError(os.Stderr, err)\n\t\tt.Fatal(\"ParseFSDir:\", err)\n\t}\n\tconf := *Conf\n\tconf.RelativeBase = \"/foo\"\n\tconf.Recorder = nil\n\tconf.NoFileLine = false\n\tbar := pkgs[\"main\"]\n\t_, err = cl.NewPackage(\"\", bar, &conf)\n\tif err == nil {\n\t\tt.Fatal(\"no error?\")\n\t}\n\tif ret := err.Error(); ret != msg {\n\t\tt.Fatalf(\"\\nError: \\\"%s\\\"\\nExpected: \\\"%s\\\"\\n\", ret, msg)\n\t}\n}\n\nfunc SpxErrorEx(t *testing.T, msg, gmx, spxcode, gmxfile, spxfile string) {\n\tfs := memfs.TwoFiles(\"/foo\", spxfile, spxcode, gmxfile, gmx)\n\tpkgs, err := parser.ParseFSDir(Conf.Fset, fs, \"/foo\", spxParserConf())\n\tif err != nil {\n\t\tscanner.PrintError(os.Stderr, err)\n\t\tt.Fatal(\"ParseFSDir:\", err)\n\t}\n\tconf := *Conf\n\tconf.RelativeBase = \"/foo\"\n\tconf.Recorder = nil\n\tconf.NoFileLine = false\n\tbar := pkgs[\"main\"]\n\t_, err = cl.NewPackage(\"\", bar, &conf)\n\tif err == nil {\n\t\tt.Fatal(\"no error?\")\n\t}\n\tif ret := err.Error(); ret != msg {\n\t\tt.Fatalf(\"\\nError: \\\"%s\\\"\\nExpected: \\\"%s\\\"\\n\", ret, msg)\n\t}\n}\n\nfunc SpxFromDir(t *testing.T, sel, relDir string) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = path.Join(dir, relDir)\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"ReadDir failed:\", err)\n\t}\n\tfor _, fi := range fis {\n\t\tname := fi.Name()\n\t\tif !fi.IsDir() || strings.HasPrefix(name, \"_\") {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttestSpxFrom(t, dir+\"/\"+name, sel)\n\t\t})\n\t}\n}\n\nfunc testSpxFrom(t *testing.T, pkgDir, sel string) {\n\tif sel != \"\" && !strings.Contains(pkgDir, sel) {\n\t\treturn\n\t}\n\tlog.Println(\"Parsing\", pkgDir)\n\tparseConf := spxParserConf()\n\tparseConf.Filter = func(fi fs.FileInfo) bool {\n\t\treturn !strings.HasSuffix(fi.Name(), \".go\")\n\t}\n\texpect, _ := os.ReadFile(pkgDir + \"/out.go\")\n\tSpxTest(t, fsx.Local, pkgDir, parseConf, Conf, expect, \"\")\n}\n"
  },
  {
    "path": "cl/compile.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package cl compiles XGo syntax trees (ast).\npackage cl\n\nimport (\n\t\"fmt\"\n\tgoast \"go/ast\"\n\tgotoken \"go/token\"\n\t\"go/types\"\n\t\"log\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/mod/modfile\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/ast/fromgo\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/qiniu/x/errors\"\n)\n\ntype dbgFlags int\n\nconst (\n\tDbgFlagLoad dbgFlags = 1 << iota\n\tDbgFlagLookup\n\tFlagNoMarkAutogen\n\tDbgFlagAll = DbgFlagLoad | DbgFlagLookup\n)\n\nvar (\n\tenableRecover = true\n)\n\nvar (\n\tdebugLoad     bool\n\tdebugLookup   bool\n\tnoMarkAutogen bool // add const _ = true\n)\n\nfunc SetDisableRecover(disableRecover bool) {\n\tenableRecover = !disableRecover\n}\n\nfunc SetDebug(flags dbgFlags) {\n\tdebugLoad = (flags & DbgFlagLoad) != 0\n\tdebugLookup = (flags & DbgFlagLookup) != 0\n\tnoMarkAutogen = (flags & FlagNoMarkAutogen) != 0\n}\n\n// -----------------------------------------------------------------------------\n\n// Recorder represents a compiling event recorder.\ntype Recorder interface {\n\t// Type maps expressions to their types, and for constant\n\t// expressions, also their values. Invalid expressions are\n\t// omitted.\n\t//\n\t// For (possibly parenthesized) identifiers denoting built-in\n\t// functions, the recorded signatures are call-site specific:\n\t// if the call result is not a constant, the recorded type is\n\t// an argument-specific signature. Otherwise, the recorded type\n\t// is invalid.\n\t//\n\t// The Types map does not record the type of every identifier,\n\t// only those that appear where an arbitrary expression is\n\t// permitted. For instance, the identifier f in a selector\n\t// expression x.f is found only in the Selections map, the\n\t// identifier z in a variable declaration 'var z int' is found\n\t// only in the Defs map, and identifiers denoting packages in\n\t// qualified identifiers are collected in the Uses map.\n\tType(ast.Expr, types.TypeAndValue)\n\n\t// Instantiate maps identifiers denoting generic types or functions to their\n\t// type arguments and instantiated type.\n\t//\n\t// For example, Instantiate will map the identifier for 'T' in the type\n\t// instantiation T[int, string] to the type arguments [int, string] and\n\t// resulting instantiated *Named type. Given a generic function\n\t// func F[A any](A), Instances will map the identifier for 'F' in the call\n\t// expression F(int(1)) to the inferred type arguments [int], and resulting\n\t// instantiated *Signature.\n\t//\n\t// Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs\n\t// results in an equivalent of Instances[id].Type.\n\tInstantiate(*ast.Ident, types.Instance)\n\n\t// Def maps identifiers to the objects they define (including\n\t// package names, dots \".\" of dot-imports, and blank \"_\" identifiers).\n\t// For identifiers that do not denote objects (e.g., the package name\n\t// in package clauses, or symbolic variables t in t := x.(type) of\n\t// type switch headers), the corresponding objects are nil.\n\t//\n\t// For an embedded field, Def maps the field *Var it defines.\n\t//\n\t// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()\n\tDef(id *ast.Ident, obj types.Object)\n\n\t// Use maps identifiers to the objects they denote.\n\t//\n\t// For an embedded field, Use maps the *TypeName it denotes.\n\t//\n\t// Invariant: Uses[id].Pos() != id.Pos()\n\tUse(id *ast.Ident, obj types.Object)\n\n\t// Implicit maps nodes to their implicitly declared objects, if any.\n\t// The following node and object types may appear:\n\t//\n\t//     node               declared object\n\t//\n\t//     *ast.ImportSpec    *PkgName for imports without renames\n\t//     *ast.CaseClause    type-specific *Var for each type switch case clause (incl. default)\n\t//     *ast.Field         anonymous parameter *Var (incl. unnamed results)\n\t//     *ast.FunLit        function literal in *ast.OverloadFuncDecl\n\t//\n\tImplicit(node ast.Node, obj types.Object)\n\n\t// Select maps selector expressions (excluding qualified identifiers)\n\t// to their corresponding selections.\n\tSelect(*ast.SelectorExpr, *types.Selection)\n\n\t// Scope maps ast.Nodes to the scopes they define. Package scopes are not\n\t// associated with a specific node but with all files belonging to a package.\n\t// Thus, the package scope can be found in the type-checked Package object.\n\t// Scopes nest, with the Universe scope being the outermost scope, enclosing\n\t// the package scope, which contains (one or more) files scopes, which enclose\n\t// function scopes which in turn enclose statement and function literal scopes.\n\t// Note that even though package-level functions are declared in the package\n\t// scope, the function scopes are embedded in the file scope of the file\n\t// containing the function declaration.\n\t//\n\t// The following node types may appear in Scopes:\n\t//\n\t//     *ast.File\n\t//     *ast.FuncType\n\t//     *ast.TypeSpec\n\t//     *ast.BlockStmt\n\t//     *ast.IfStmt\n\t//     *ast.SwitchStmt\n\t//     *ast.TypeSwitchStmt\n\t//     *ast.CaseClause\n\t//     *ast.CommClause\n\t//     *ast.ForStmt\n\t//     *ast.RangeStmt\n\t//     *ast.ForPhraseStmt\n\t//     *ast.ForPhrase\n\t//     *ast.LambdaExpr\n\t//     *ast.LambdaExpr2\n\t//\n\tScope(ast.Node, *types.Scope)\n}\n\n// -----------------------------------------------------------------------------\n\ntype Project = modfile.Project\ntype Class = modfile.Class\n\n// Config of loading XGo packages.\ntype Config struct {\n\t// Types provides type information for the package (optional).\n\tTypes *types.Package\n\n\t// Fset provides source position information for syntax trees and types (required).\n\tFset *token.FileSet\n\n\t// RelativeBase is the root directory of relative path.\n\tRelativeBase string\n\n\t// LookupClass lookups a class by specified file extension (required).\n\t// See (*github.com/goplus/mod/gopmod.Module).LookupClass.\n\tLookupClass func(ext string) (c *Project, ok bool)\n\n\t// An Importer resolves import paths to Packages (optional).\n\tImporter types.Importer\n\n\t// A Recorder records existing objects including constants, variables and\n\t// types etc (optional).\n\tRecorder Recorder\n\n\t// NoFileLine = true means not to generate file line comments.\n\tNoFileLine bool\n\n\t// NoAutoGenMain = true means not to auto generate main func is no entry.\n\tNoAutoGenMain bool\n\n\t// NoSkipConstant = true means to disable optimization of skipping constants.\n\tNoSkipConstant bool\n\n\t// Outline = true means to skip compiling function bodies.\n\tOutline bool\n}\n\ntype nodeInterp struct {\n\tfset       *token.FileSet\n\tfiles      map[string]*ast.File\n\trelBaseDir string\n}\n\nfunc (p *nodeInterp) Position(start token.Pos) (pos token.Position) {\n\tpos = p.fset.Position(start)\n\tpos.Filename = relFile(p.relBaseDir, pos.Filename)\n\treturn\n}\n\nfunc (p *nodeInterp) Caller(node ast.Node) string {\n\tif expr, ok := node.(*ast.CallExpr); ok {\n\t\treturn p.LoadExpr(expr.Fun)\n\t}\n\treturn \"the function call\"\n}\n\nfunc (p *nodeInterp) LoadExpr(node ast.Node) string {\n\tstart := node.Pos()\n\tif start == token.NoPos {\n\t\treturn \"\"\n\t}\n\tpos := p.fset.Position(start)\n\tf := p.files[pos.Filename]\n\tn := int(node.End() - start)\n\treturn string(f.Code[pos.Offset : pos.Offset+n])\n}\n\nfunc (p *nodeInterp) ProjFile() *ast.File {\n\tfor _, f := range p.files {\n\t\tif f.IsProj {\n\t\t\treturn f\n\t\t}\n\t}\n\treturn nil\n}\n\ntype loader interface {\n\tload()\n\tpos() token.Pos\n}\n\ntype baseLoader struct {\n\tfn    func()\n\tstart token.Pos\n}\n\nfunc initLoader(ctx *pkgCtx, syms map[string]loader, start, end token.Pos, name string, fn func(), genBody bool) bool {\n\tif name == \"_\" {\n\t\tif genBody {\n\t\t\tctx.inits = append(ctx.inits, fn)\n\t\t}\n\t\treturn false\n\t}\n\tif old, ok := syms[name]; ok {\n\t\toldpos := ctx.Position(old.pos())\n\t\tctx.handleErrorf(\n\t\t\tstart, end, \"%s redeclared in this block\\n\\tprevious declaration at %v\", name, oldpos)\n\t\treturn false\n\t}\n\tsyms[name] = &baseLoader{start: start, fn: fn}\n\treturn true\n}\n\nfunc (p *baseLoader) load() {\n\tp.fn()\n}\n\nfunc (p *baseLoader) pos() token.Pos {\n\treturn p.start\n}\n\ntype typeLoader struct {\n\ttyp, typInit func()\n\tmethods      []func()\n\tstart        token.Pos\n}\n\nfunc getTypeLoader(ctx *pkgCtx, syms map[string]loader, start, end token.Pos, name string) *typeLoader {\n\tt, ok := syms[name]\n\tif ok {\n\t\tif start != token.NoPos {\n\t\t\tld := t.(*typeLoader)\n\t\t\tif ld.start == token.NoPos {\n\t\t\t\tld.start = start\n\t\t\t} else {\n\t\t\t\tctx.handleErrorf(\n\t\t\t\t\tstart, end, \"%s redeclared in this block\\n\\tprevious declaration at %v\",\n\t\t\t\t\tname, ctx.Position(ld.pos()))\n\t\t\t}\n\t\t\treturn ld\n\t\t}\n\t} else {\n\t\tt = &typeLoader{start: start}\n\t\tsyms[name] = t\n\t}\n\treturn t.(*typeLoader)\n}\n\nfunc (p *typeLoader) pos() token.Pos {\n\treturn p.start\n}\n\nfunc (p *typeLoader) load() {\n\tdoNewType(p)\n\tdoInitType(p)\n\tdoInitMethods(p)\n}\n\nfunc doNewType(ld *typeLoader) {\n\tif typ := ld.typ; typ != nil {\n\t\tld.typ = nil\n\t\ttyp()\n\t}\n}\n\nfunc doInitType(ld *typeLoader) {\n\tif typInit := ld.typInit; typInit != nil {\n\t\tld.typInit = nil\n\t\ttypInit()\n\t}\n}\n\nfunc doInitMethods(ld *typeLoader) {\n\tif methods := ld.methods; methods != nil {\n\t\tld.methods = nil\n\t\tfor _, method := range methods {\n\t\t\tmethod()\n\t\t}\n\t}\n}\n\ntype pkgCtx struct {\n\t*nodeInterp\n\tnproj    int                    // number of non-test projects\n\tprojs    map[string]*gmxProject // .gmx => project\n\tclasses  map[*ast.File]*gmxClass\n\toverpos  map[string]token.Pos // overload => pos\n\tfset     *token.FileSet\n\tsyms     map[string]loader\n\tlbinames []any // names that should load before initXGoPkg (can be string/func or *ast.Ident/type)\n\tinits    []func()\n\ttylds    []*typeLoader\n\terrs     errors.List\n\n\tgenerics map[string]bool // generic type record\n\tidents   []*ast.Ident    // toType ident recored\n\tinInst   int             // toType in generic instance\n\n\tgoxMainClass string\n\tgoxMain      int32 // normal gox files with main func\n}\n\ntype pkgImp struct {\n\tgogen.PkgRef\n\tpkgName *types.PkgName\n}\n\ntype blockCtx struct {\n\t*pkgCtx\n\tproj       *gmxProject\n\tpkg        *gogen.Package\n\tcb         *gogen.CodeBuilder\n\timports    map[string]pkgImp\n\tautoimps   map[string]pkgImp\n\tlookups    []gogen.PkgRef\n\ttlookup    *typeParamLookup\n\tcstr_      gogen.Ref\n\tpystr_     gogen.Ref\n\trelBaseDir string\n\n\tclassDecl *ast.GenDecl   // available when isClass\n\tclassRecv *ast.FieldList // available when isClass\n\tbaseClass types.Object   // available when isClass\n\n\tfileScope *types.Scope // available when isXGoFile\n\trec       *goxRecorder\n\n\tfileLine  bool\n\tisClass   bool\n\tisXgoFile bool // is XGo file or not\n}\n\nfunc (p *blockCtx) cstr() gogen.Ref {\n\tif p.cstr_ == nil {\n\t\tp.cstr_ = p.pkg.Import(pathLibc).Ref(\"Str\")\n\t}\n\treturn p.cstr_\n}\n\nfunc (p *blockCtx) pystr() gogen.Ref {\n\tif p.pystr_ == nil {\n\t\tp.pystr_ = p.pkg.Import(pathLibpy).Ref(\"Str\")\n\t}\n\treturn p.pystr_\n}\n\nfunc (p *blockCtx) recorder() *goxRecorder {\n\tif p.isXgoFile {\n\t\treturn p.rec\n\t}\n\treturn nil\n}\n\nfunc (p *blockCtx) findImport(name string) (pi pkgImp, ok bool) {\n\tpi, ok = p.imports[name]\n\tif !ok && p.autoimps != nil {\n\t\tpi, ok = p.autoimps[name]\n\t}\n\treturn\n}\n\nfunc (p *pkgCtx) newCodeError(pos, end token.Pos, msg string) error {\n\treturn &gogen.CodeError{Fset: p.nodeInterp, Pos: pos, End: end, Msg: msg}\n}\n\nfunc (p *pkgCtx) newCodeErrorf(pos, end token.Pos, format string, args ...any) error {\n\treturn &gogen.CodeError{Fset: p.nodeInterp, Pos: pos, End: end, Msg: fmt.Sprintf(format, args...)}\n}\n\nfunc (p *pkgCtx) handleErrorf(pos, end token.Pos, format string, args ...any) {\n\tp.handleErr(p.newCodeErrorf(pos, end, format, args...))\n}\n\nfunc (p *pkgCtx) handleErr(err error) {\n\tp.errs = append(p.errs, err)\n}\n\nfunc (p *pkgCtx) loadNamed(at *gogen.Package, t *types.Named) {\n\to := t.Obj()\n\tif o.Pkg() == at.Types {\n\t\tp.loadType(o.Name())\n\t}\n}\n\nfunc (p *pkgCtx) complete() error {\n\treturn p.errs.ToError()\n}\n\nfunc (p *pkgCtx) loadType(name string) {\n\tif sym, ok := p.syms[name]; ok {\n\t\tif ld, ok := sym.(*typeLoader); ok {\n\t\t\tld.load()\n\t\t}\n\t}\n}\n\nfunc (p *pkgCtx) loadSymbol(name string) bool {\n\tif enableRecover {\n\t\tdefer func() {\n\t\t\tif e := recover(); e != nil {\n\t\t\t\tp.handleRecover(e, nil)\n\t\t\t}\n\t\t}()\n\t}\n\tif f, ok := p.syms[name]; ok {\n\t\tif ld, ok := f.(*typeLoader); ok {\n\t\t\tdoNewType(ld) // create this type, but don't init\n\t\t\treturn true\n\t\t}\n\t\tdelete(p.syms, name)\n\t\tf.load()\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (p *pkgCtx) handleRecover(e any, src ast.Node) {\n\terr := p.recoverErr(e, src)\n\tp.handleErr(err)\n}\n\nfunc (p *pkgCtx) recoverErr(e any, src ast.Node) error {\n\terr, ok := e.(error)\n\tif !ok {\n\t\tif src != nil {\n\t\t\ttext := p.LoadExpr(src)\n\t\t\terr = p.newCodeErrorf(src.Pos(), src.End(), \"compile `%v`: %v\", text, e)\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"%v\", e)\n\t\t}\n\t}\n\treturn err\n}\n\n// lookupClassFile looks up the class file by name.\nfunc (p *pkgCtx) lookupClassNode(name string) ast.Node {\n\tfor f, cls := range p.classes {\n\t\tif name == cls.clsfile {\n\t\t\treturn f.Name\n\t\t}\n\t}\n\treturn nil\n}\n\ntype visitedT = map[*types.Struct]none\n\n// see https://github.com/goplus/xgo/issues/2439\nfunc embeddedFieldCast(o *types.Struct, tn *types.Named, pv *gogen.Element, visited visitedT) bool {\n\tif _, ok := visited[o]; ok {\n\t\treturn false\n\t}\n\tvisited[o] = none{}\n\tfor i, n := 0, o.NumFields(); i < n; i++ {\n\t\tif fld := o.Field(i); fld.Embedded() {\n\t\t\tvar ptr bool\n\t\t\tvar ftn *types.Named\n\t\t\tswitch ft := fld.Type().(type) {\n\t\t\tcase *types.Named:\n\t\t\t\tftn = ft\n\t\t\tcase *types.Pointer:\n\t\t\t\tif ft, ok := ft.Elem().(*types.Named); ok {\n\t\t\t\t\tftn, ptr = ft, true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ftn != nil {\n\t\t\t\tif ftn == tn {\n\t\t\t\t\tpv.Val = &goast.SelectorExpr{\n\t\t\t\t\t\tX:   pv.Val,\n\t\t\t\t\t\tSel: goast.NewIdent(fld.Name()),\n\t\t\t\t\t}\n\t\t\t\t\tif !ptr {\n\t\t\t\t\t\tpv.Val = &goast.UnaryExpr{\n\t\t\t\t\t\t\tOp: gotoken.AND,\n\t\t\t\t\t\t\tX:  pv.Val,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif fldStruct, ok := ftn.Underlying().(*types.Struct); ok {\n\t\t\t\t\tif embeddedFieldCast(fldStruct, tn, pv, visited) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc implicitCast(pkg *gogen.Package, V, T types.Type, pv *gogen.Element) bool {\n\tif pv != nil {\n\t\tif t, ok := T.(*types.Pointer); ok {\n\t\t\tif tn, ok := t.Elem().(*types.Named); ok {\n\t\t\t\tif v, ok := V.(*types.Pointer); ok {\n\t\t\t\t\tif o, ok := v.Elem().Underlying().(*types.Struct); ok {\n\t\t\t\t\t\treturn embeddedFieldCast(o, tn, pv, visitedT{})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nconst (\n\tdefaultGoFile  = \"\"\n\tskippingGoFile = \"_skip\"\n\ttestingGoFile  = \"_test\"\n)\n\n// NewPackage creates an XGo package instance.\nfunc NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gogen.Package, err error) {\n\trelBaseDir := conf.RelativeBase\n\tfset := conf.Fset\n\tfiles := pkg.Files\n\tinterp := &nodeInterp{\n\t\tfset: fset, files: files, relBaseDir: relBaseDir,\n\t}\n\tctx := &pkgCtx{\n\t\tfset:       fset,\n\t\tnodeInterp: interp,\n\t\tprojs:      make(map[string]*gmxProject),\n\t\tclasses:    make(map[*ast.File]*gmxClass),\n\t\toverpos:    make(map[string]token.Pos),\n\t\tsyms:       make(map[string]loader),\n\t\tgenerics:   make(map[string]bool),\n\t}\n\tconfGox := &gogen.Config{\n\t\tTypes:           conf.Types,\n\t\tFset:            fset,\n\t\tImporter:        conf.Importer,\n\t\tLoadNamed:       ctx.loadNamed,\n\t\tHandleErr:       ctx.handleErr,\n\t\tNodeInterpreter: interp,\n\t\tNewBuiltin:      ctx.newBuiltinDefault,\n\t\tDefaultGoFile:   defaultGoFile,\n\t\tNoSkipConstant:  conf.NoSkipConstant,\n\t\tPkgPathOsx:      osxPkgPath,\n\t\tDbgPositioner:   interp,\n\t\tCanImplicitCast: implicitCast,\n\t}\n\tvar rec *goxRecorder\n\tif conf.Recorder != nil {\n\t\trec = newRecorder(conf.Recorder)\n\t\tconfGox.Recorder = rec\n\t\tdefer func() {\n\t\t\trec.Complete(p.Types.Scope())\n\t\t}()\n\t}\n\tif enableRecover {\n\t\tdefer func() {\n\t\t\tif e := recover(); e != nil {\n\t\t\t\tctx.handleRecover(e, nil)\n\t\t\t\terr = ctx.errs.ToError()\n\t\t\t}\n\t\t}()\n\t}\n\tp = gogen.NewPackage(pkgPath, pkg.Name, confGox)\n\n\tif !noMarkAutogen {\n\t\tp.CB().NewConstStart(nil, \"_\").Val(true).EndInit(1)\n\t}\n\n\t// sort files\n\ttype File struct {\n\t\t*ast.File\n\t\tpath string\n\t}\n\tsfiles := make([]*File, 0, len(files))\n\tfor fpath, f := range files {\n\t\tsfiles = append(sfiles, &File{f, fpath})\n\t}\n\tsort.Slice(sfiles, func(i, j int) bool {\n\t\treturn sfiles[i].path < sfiles[j].path\n\t})\n\n\tfor _, f := range sfiles {\n\t\tgmx := f.File\n\t\tif gmx.IsClass && !gmx.IsNormalGox {\n\t\t\tif debugLoad {\n\t\t\t\tlog.Println(\"==> ClassFile\", f.path)\n\t\t\t}\n\t\t\tloadClass(ctx, p, f.path, gmx, conf)\n\t\t}\n\t}\n\n\tfor _, f := range sfiles {\n\t\tfileLine := !conf.NoFileLine\n\t\tfileScope := types.NewScope(p.Types.Scope(), f.Pos(), f.End(), f.path)\n\t\tctx := &blockCtx{\n\t\t\tpkg: p, pkgCtx: ctx, cb: p.CB(), relBaseDir: relBaseDir, fileScope: fileScope,\n\t\t\tfileLine: fileLine, isClass: f.IsClass, rec: rec, imports: make(map[string]pkgImp),\n\t\t\tisXgoFile: true,\n\t\t}\n\t\tif rec := ctx.rec; rec != nil {\n\t\t\trec.Scope(f.File, fileScope)\n\t\t}\n\t\tpreloadXGoFile(p, ctx, f.path, f.File, conf)\n\t}\n\n\tproj, multi := gmxCheckProjs(p, ctx)\n\n\tgopSyms := make(map[string]bool) // TODO(xsw): remove this map\n\tfor name := range ctx.syms {\n\t\tgopSyms[name] = true\n\t}\n\n\tgofiles := make([]*ast.File, 0, len(pkg.GoFiles))\n\tfor _, gof := range pkg.GoFiles {\n\t\tf := fromgo.ASTFile(gof, 0)\n\t\tgofiles = append(gofiles, f)\n\t\tctx := &blockCtx{\n\t\t\tpkg: p, pkgCtx: ctx, cb: p.CB(), relBaseDir: relBaseDir,\n\t\t\timports: make(map[string]pkgImp),\n\t\t}\n\t\tpreloadFile(p, ctx, f, skippingGoFile, false)\n\t}\n\n\tinitXGoPkg(ctx, p, gopSyms)\n\n\t// genMain = true if it is main package and no main func\n\tvar genMain bool\n\tvar mainClass string\n\tif pkg.Name == \"main\" {\n\t\t_, hasMain := ctx.syms[\"main\"]\n\t\tgenMain = !hasMain\n\t}\n\n\tfor _, f := range sfiles {\n\t\tif f.IsProj {\n\t\t\tloadFile(ctx, f.File)\n\t\t}\n\t}\n\n\tif genMain { // make classfile main func if need\n\t\tif proj != nil {\n\t\t\tif !multi { // only one project file\n\t\t\t\tmainClass = proj.getGameClass(ctx)\n\t\t\t}\n\t\t} else if ctx.goxMain == 1 {\n\t\t\tmainClass = ctx.goxMainClass // main func in normal gox file\n\t\t}\n\t}\n\n\tfor _, f := range sfiles {\n\t\tif !f.IsProj {\n\t\t\tloadFile(ctx, f.File)\n\t\t}\n\t}\n\tif conf.Outline {\n\t\tfor _, f := range gofiles {\n\t\t\tloadFile(ctx, f)\n\t\t}\n\t}\n\tfor _, ld := range ctx.tylds {\n\t\tld.load()\n\t}\n\tfor _, load := range ctx.inits {\n\t\tload()\n\t}\n\terr = ctx.complete()\n\n\tif mainClass != \"\" { // generate classfile main func\n\t\tgenMainFunc(p, mainClass)\n\t} else if genMain && !conf.NoAutoGenMain { // generate empty main func\n\t\told, _ := p.SetCurFile(defaultGoFile, false)\n\t\tp.NewFunc(nil, \"main\", nil, nil, false).BodyStart(p).End()\n\t\tp.RestoreCurFile(old)\n\t}\n\treturn\n}\n\nfunc isOverloadFunc(name string) bool {\n\tn := len(name)\n\treturn n > 3 && name[n-3:n-1] == \"__\"\n}\n\nfunc initXGoPkg(ctx *pkgCtx, pkg *gogen.Package, gopSyms map[string]bool) {\n\tfor name, f := range ctx.syms {\n\t\tif gopSyms[name] {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := f.(*typeLoader); ok {\n\t\t\tctx.loadType(name)\n\t\t} else if isOverloadFunc(name) {\n\t\t\tctx.loadSymbol(name)\n\t\t}\n\t}\n\tfor _, lbi := range ctx.lbinames {\n\t\tif name, ok := lbi.(string); ok {\n\t\t\tctx.loadSymbol(name)\n\t\t} else {\n\t\t\tctx.loadType(lbi.(*ast.Ident).Name)\n\t\t}\n\t}\n\tgogen.InitXGoPackageEx(pkg.Types, ctx.overpos)\n}\n\nfunc loadFile(ctx *pkgCtx, f *ast.File) {\n\tfor _, decl := range f.Decls {\n\t\tswitch d := decl.(type) {\n\t\tcase *ast.GenDecl:\n\t\t\tswitch d.Tok {\n\t\t\tcase token.TYPE:\n\t\t\t\tfor _, spec := range d.Specs {\n\t\t\t\t\tctx.loadType(spec.(*ast.TypeSpec).Name.Name)\n\t\t\t\t}\n\t\t\tcase token.CONST, token.VAR:\n\t\t\t\tfor _, spec := range d.Specs {\n\t\t\t\t\tfor _, name := range spec.(*ast.ValueSpec).Names {\n\t\t\t\t\t\tctx.loadSymbol(name.Name)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase *ast.FuncDecl:\n\t\t\tif d.Recv == nil {\n\t\t\t\tname := d.Name.Name\n\t\t\t\tif name != \"init\" {\n\t\t\t\t\tctx.loadSymbol(name)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif name, ok := getRecvTypeName(ctx, d.Recv, false); ok {\n\t\t\t\t\tgetTypeLoader(ctx, ctx.syms, token.NoPos, token.NoPos, name).load()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// gen testingGoFile for:\n//\n//\t*_test.xgo\n//\t*_test.gop\n//\t*test.gox\nfunc genGoFile(file string, goxTestFile bool) string {\n\tif goxTestFile || strings.HasSuffix(file, \"_test.xgo\") || strings.HasSuffix(file, \"_test.gop\") {\n\t\treturn testingGoFile\n\t}\n\treturn defaultGoFile\n}\n\nfunc preloadXGoFile(p *gogen.Package, ctx *blockCtx, file string, f *ast.File, conf *Config) {\n\tvar proj *gmxProject\n\tvar c *gmxClass\n\tvar classType, gameClass string\n\tvar testType string\n\tvar baseTypeName string\n\tvar baseType types.Type\n\tvar sp *spxObj\n\tvar goxTestFile bool\n\tvar parent = ctx.pkgCtx\n\tif f.IsClass {\n\t\tif f.IsNormalGox {\n\t\t\tclassType, _, _ = ClassNameAndExt(file)\n\t\t\tif f.ShadowEntry != nil {\n\t\t\t\tparent.goxMainClass = classType\n\t\t\t\tparent.goxMain++\n\t\t\t}\n\t\t} else {\n\t\t\tc = parent.classes[f]\n\t\t\tproj, ctx.proj = c.proj, c.proj\n\t\t\tclassType, gameClass = c.getName(parent), proj.getGameClass(parent)\n\t\t\tctx.autoimps = proj.autoimps\n\t\t\tgoxTestFile = proj.isTest\n\t\t\tif goxTestFile { // test classfile\n\t\t\t\ttestType = classType\n\t\t\t\tif !f.IsProj {\n\t\t\t\t\tclassType = casePrefix + testNameSuffix(testType)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif f.IsProj {\n\t\t\t\tclassType = gameClass\n\t\t\t\to := proj.game\n\t\t\t\tctx.baseClass = o\n\t\t\t\tbaseTypeName, baseType = o.Name(), o.Type()\n\t\t\t\tif proj.gameIsPtr {\n\t\t\t\t\tbaseType = types.NewPointer(baseType)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsp = c.sp\n\t\t\t\to := sp.obj\n\t\t\t\tctx.baseClass = o\n\t\t\t\tbaseTypeName, baseType = o.Name(), o.Type()\n\t\t\t}\n\t\t}\n\t}\n\tgoFile := genGoFile(file, goxTestFile)\n\tif classType != \"\" {\n\t\tif debugLoad {\n\t\t\tlog.Println(\"==> Preload type\", classType)\n\t\t}\n\t\tif proj != nil {\n\t\t\tctx.lookups = make([]gogen.PkgRef, len(proj.pkgPaths))\n\t\t\tfor i, pkgPath := range proj.pkgPaths {\n\t\t\t\tctx.lookups[i] = p.Import(pkgPath)\n\t\t\t}\n\t\t}\n\t\tsyms := parent.syms\n\t\tpos := f.Pos()\n\t\tend := f.End()\n\t\tctx.classDecl = f.ClassFieldsDecl()\n\t\tld := getTypeLoader(parent, syms, pos, end, classType)\n\t\tld.typ = func() {\n\t\t\tif debugLoad {\n\t\t\t\tlog.Println(\"==> Load > NewType\", classType)\n\t\t\t}\n\t\t\told, _ := p.SetCurFile(goFile, true)\n\t\t\tdefer p.RestoreCurFile(old)\n\n\t\t\tdecl := p.NewTypeDefs().NewType(classType)\n\t\t\tld.typInit = func() { // decycle\n\t\t\t\tif debugLoad {\n\t\t\t\t\tlog.Println(\"==> Load > InitType\", classType)\n\t\t\t\t}\n\t\t\t\told, _ := p.SetCurFile(goFile, true)\n\t\t\t\tdefer p.RestoreCurFile(old)\n\n\t\t\t\tpkg := p.Types\n\t\t\t\tvar flds []*types.Var\n\t\t\t\tvar tags []string\n\t\t\t\tchk := newCheckRedecl()\n\t\t\t\tif baseTypeName != \"\" { // base class (not normal classfile)\n\t\t\t\t\tflds = append(flds, types.NewField(pos, pkg, baseTypeName, baseType, true))\n\t\t\t\t\ttags = append(tags, \"\")\n\t\t\t\t\tchk.chkRedecl(ctx, baseTypeName, pos, end, fieldKindClass)\n\t\t\t\t\tif sp != nil { // for work class\n\t\t\t\t\t\tif !goxTestFile && gameClass != \"\" { // has project class\n\t\t\t\t\t\t\ttyp := toType(ctx, &ast.Ident{Name: gameClass})\n\t\t\t\t\t\t\tgetUnderlying(ctx, typ) // ensure type is loaded\n\t\t\t\t\t\t\ttyp = types.NewPointer(typ)\n\t\t\t\t\t\t\tname := getTypeName(typ)\n\t\t\t\t\t\t\tif !chk.chkRedecl(ctx, name, pos, end, fieldKindClass) {\n\t\t\t\t\t\t\t\tfld := types.NewField(pos, pkg, name, typ, true)\n\t\t\t\t\t\t\t\tflds = append(flds, fld)\n\t\t\t\t\t\t\t\ttags = append(tags, \"\")\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else { // embed work classes for project class\n\t\t\t\t\t\tflds = proj.embed(func(name string) bool {\n\t\t\t\t\t\t\treturn chk.chkRedecl(ctx, name, pos, end, fieldKindClass)\n\t\t\t\t\t\t}, flds, p)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trec := ctx.recorder()\n\t\t\t\tif classDecl := ctx.classDecl; classDecl != nil {\n\t\t\t\t\tvar spec *ast.ValueSpec\n\t\t\t\t\trecvType := types.NewPointer(decl.Type())\n\t\t\t\t\trecv := types.NewParam(token.NoPos, pkg, \"this\", recvType)\n\t\t\t\t\tdefs := p.ClassDefsStart(recv, func(idx int, name string, typ types.Type, embed bool) {\n\t\t\t\t\t\tvar id *ast.Ident\n\t\t\t\t\t\tif embed {\n\t\t\t\t\t\t\tid = parseTypeEmbedName(spec.Type)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tid = spec.Names[idx]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpos := id.Pos()\n\t\t\t\t\t\tif chk.chkRedecl(ctx, name, pos, id.End(), fieldKindUser) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfld := types.NewField(pos, pkg, name, typ, embed)\n\t\t\t\t\t\tif rec != nil {\n\t\t\t\t\t\t\trec.Def(id, fld)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tflds = append(flds, fld)\n\t\t\t\t\t\ttags = append(tags, toFieldTag(spec.Tag))\n\t\t\t\t\t})\n\t\t\t\t\tfor _, v := range classDecl.Specs {\n\t\t\t\t\t\tvar pos token.Pos\n\t\t\t\t\t\tvar names []string\n\t\t\t\t\t\tvar fldType types.Type\n\t\t\t\t\t\tspec = v.(*ast.ValueSpec)\n\t\t\t\t\t\tif spec.Type != nil {\n\t\t\t\t\t\t\tfldType = toType(ctx, spec.Type)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif specNames := spec.Names; len(specNames) > 0 {\n\t\t\t\t\t\t\tnames = makeNames(specNames)\n\t\t\t\t\t\t\tpos = specNames[0].Pos()\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpos = spec.Type.Pos()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tinitExpr := makeInitExpr(ctx, spec, fldType, names)\n\t\t\t\t\t\tdefs.NewAndInit(initExpr, pos, fldType, names...)\n\t\t\t\t\t}\n\t\t\t\t\tdefs.End()\n\t\t\t\t}\n\t\t\t\tdecl.InitType(p, types.NewStruct(flds, tags))\n\t\t\t}\n\t\t\tparent.tylds = append(parent.tylds, ld)\n\t\t}\n\n\t\t// bugfix: see TestGoxNoFunc\n\t\tparent.lbinames = append(parent.lbinames, classType)\n\n\t\tctx.classRecv = &ast.FieldList{List: []*ast.Field{{\n\t\t\tNames: []*ast.Ident{\n\t\t\t\t{NamePos: f.Pos(), Name: \"this\"},\n\t\t\t},\n\t\t\tType: &ast.StarExpr{\n\t\t\t\tStar: f.Pos(),\n\t\t\t\tX:    ast.NewIdentEx(f.Pos(), classType, ast.ImplicitFun),\n\t\t\t},\n\t\t}}}\n\t}\n\n\tif d := f.ShadowEntry; d != nil {\n\t\td.Name.Name = getEntrypoint(f)\n\t} else if baseTypeName != \"\" { // isClass && not isNormalGox\n\t\tastEmptyEntrypoint(f)\n\t}\n\n\tpreloadFile(p, ctx, f, goFile, !conf.Outline)\n\tif sp != nil && sp.feats != 0 {\n\t\tspfeats := sp.feats\n\t\tld := getTypeLoader(parent, parent.syms, token.NoPos, token.NoPos, classType)\n\t\tif (spfeats & spriteClassfname) != 0 { // Classfname() string\n\t\t\tld.methods = append(ld.methods, func() {\n\t\t\t\told, _ := p.SetCurFile(goFile, true)\n\t\t\t\tdefer p.RestoreCurFile(old)\n\t\t\t\tdoInitType(ld)\n\t\t\t\tgenClassfname(ctx, c)\n\t\t\t})\n\t\t}\n\t\tif (spfeats & spriteClassclone) != 0 { // Classclone() clonetype\n\t\t\tld.methods = append(ld.methods, func() {\n\t\t\t\told, _ := p.SetCurFile(goFile, true)\n\t\t\t\tdefer p.RestoreCurFile(old)\n\t\t\t\tdoInitType(ld)\n\t\t\t\tgenClassclone(ctx, sp.clone)\n\t\t\t})\n\t\t}\n\t}\n\tif goxTestFile {\n\t\tparent.inits = append(parent.inits, func() {\n\t\t\told, _ := p.SetCurFile(testingGoFile, true)\n\t\t\tgmxTestFunc(p, testType, f.IsProj)\n\t\t\tp.RestoreCurFile(old)\n\t\t})\n\t}\n}\n\nfunc parseTypeEmbedName(typ ast.Expr) *ast.Ident {\nretry:\n\tswitch t := typ.(type) {\n\tcase *ast.Ident:\n\t\treturn t\n\tcase *ast.SelectorExpr:\n\t\treturn t.Sel\n\tcase *ast.StarExpr:\n\t\ttyp = t.X\n\t\tgoto retry\n\t}\n\tpanic(\"TODO: parseTypeEmbedName unexpected\")\n}\n\nfunc preloadFile(p *gogen.Package, ctx *blockCtx, f *ast.File, goFile string, genFnBody bool) {\n\tparent := ctx.pkgCtx\n\tclassDecl := ctx.classDecl\n\tsyms := parent.syms\n\told, _ := p.SetCurFile(goFile, true)\n\tdefer p.RestoreCurFile(old)\n\n\tpreloadFuncDecl := func(d *ast.FuncDecl) {\n\t\tif ctx.classRecv != nil { // in class file (.spx/.gmx)\n\t\t\tif recv := d.Recv; recv == nil || len(recv.List) == 0 {\n\t\t\t\td.Recv = ctx.classRecv\n\t\t\t\td.IsClass = true\n\t\t\t}\n\t\t}\n\t\tname := d.Name\n\t\tfname := name.Name\n\t\tif d.Recv == nil {\n\t\t\tfn := func() {\n\t\t\t\told, _ := p.SetCurFile(goFile, true)\n\t\t\t\tdefer p.RestoreCurFile(old)\n\t\t\t\tloadFunc(ctx, nil, fname, d, genFnBody)\n\t\t\t}\n\t\t\tif fname == \"init\" {\n\t\t\t\tif genFnBody {\n\t\t\t\t\tif debugLoad {\n\t\t\t\t\t\tlog.Println(\"==> Preload func init\")\n\t\t\t\t\t}\n\t\t\t\t\tparent.inits = append(parent.inits, fn)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif debugLoad {\n\t\t\t\t\tlog.Println(\"==> Preload func\", fname)\n\t\t\t\t}\n\t\t\t\tif initLoader(parent, syms, name.Pos(), name.End(), fname, fn, genFnBody) {\n\t\t\t\t\tif strings.HasPrefix(fname, \"XGox_\") { // XGox_xxx func\n\t\t\t\t\t\tctx.lbinames = append(ctx.lbinames, fname)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if tname, ok := getRecvTypeName(parent, d.Recv, true); ok {\n\t\t\tif d.Static {\n\t\t\t\tif debugLoad {\n\t\t\t\t\tlog.Printf(\"==> Preload static method %s.%s\\n\", tname, fname)\n\t\t\t\t}\n\t\t\t\tfname = staticMethod(tname, fname)\n\t\t\t\tfn := func() {\n\t\t\t\t\told, _ := p.SetCurFile(goFile, true)\n\t\t\t\t\tdefer p.RestoreCurFile(old)\n\t\t\t\t\tloadFunc(ctx, nil, fname, d, genFnBody)\n\t\t\t\t}\n\t\t\t\tinitLoader(parent, syms, name.Pos(), name.End(), fname, fn, genFnBody)\n\t\t\t\tctx.lbinames = append(ctx.lbinames, fname)\n\t\t\t} else {\n\t\t\t\tif debugLoad {\n\t\t\t\t\tlog.Printf(\"==> Preload method %s.%s\\n\", tname, fname)\n\t\t\t\t}\n\t\t\t\tld := getTypeLoader(parent, syms, token.NoPos, token.NoPos, tname)\n\t\t\t\tfn := func() {\n\t\t\t\t\told, _ := p.SetCurFile(goFile, true)\n\t\t\t\t\tdefer p.RestoreCurFile(old)\n\t\t\t\t\tdoInitType(ld)\n\t\t\t\t\trecv := toRecv(ctx, d.Recv)\n\t\t\t\t\tloadFunc(ctx, recv, fname, d, genFnBody)\n\t\t\t\t}\n\t\t\t\tld.methods = append(ld.methods, fn)\n\t\t\t}\n\t\t}\n\t}\n\n\tpreloadConst := func(d *ast.GenDecl) {\n\t\tpkg := ctx.pkg\n\t\tcdecl := pkg.NewConstDefs(pkg.Types.Scope())\n\t\tfor _, spec := range d.Specs {\n\t\t\tvSpec := spec.(*ast.ValueSpec)\n\t\t\tif debugLoad {\n\t\t\t\tlog.Println(\"==> Preload const\", vSpec.Names)\n\t\t\t}\n\t\t\tsetNamesLoader(parent, syms, vSpec.Names, func() {\n\t\t\t\tif c := cdecl; c != nil {\n\t\t\t\t\tcdecl = nil\n\t\t\t\t\tloadConstSpecs(ctx, c, d.Specs)\n\t\t\t\t\tfor _, s := range d.Specs {\n\t\t\t\t\t\tv := s.(*ast.ValueSpec)\n\t\t\t\t\t\tremoveNames(syms, v.Names)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\tfor _, decl := range f.Decls {\n\t\tswitch d := decl.(type) {\n\t\tcase *ast.GenDecl:\n\t\t\tswitch d.Tok {\n\t\t\tcase token.IMPORT:\n\t\t\t\tfor _, item := range d.Specs {\n\t\t\t\t\tloadImport(ctx, item.(*ast.ImportSpec))\n\t\t\t\t}\n\t\t\tcase token.TYPE:\n\t\t\t\tfor _, spec := range d.Specs {\n\t\t\t\t\tt := spec.(*ast.TypeSpec)\n\t\t\t\t\ttName := t.Name\n\t\t\t\t\tname := tName.Name\n\t\t\t\t\tif debugLoad {\n\t\t\t\t\t\tlog.Println(\"==> Preload type\", name)\n\t\t\t\t\t}\n\t\t\t\t\tpos := tName.Pos()\n\t\t\t\t\tend := tName.End()\n\t\t\t\t\tld := getTypeLoader(parent, syms, pos, end, name)\n\t\t\t\t\tdefs := ctx.pkg.NewTypeDefs()\n\t\t\t\t\tif goFile != skippingGoFile { // is XGo file\n\t\t\t\t\t\tld.typ = func() {\n\t\t\t\t\t\t\told, _ := p.SetCurFile(goFile, true)\n\t\t\t\t\t\t\tdefer p.RestoreCurFile(old)\n\t\t\t\t\t\t\tif t.Assign != token.NoPos { // alias type\n\t\t\t\t\t\t\t\tif debugLoad {\n\t\t\t\t\t\t\t\t\tlog.Println(\"==> Load > AliasType\", name)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\ttyp := defs.AliasType(name, toType(ctx, t.Type), tName)\n\t\t\t\t\t\t\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\t\t\t\t\t\t\tif obj, ok := typ.(interface{ Obj() *types.TypeName }); ok {\n\t\t\t\t\t\t\t\t\t\trec.Def(tName, obj.Obj())\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif debugLoad {\n\t\t\t\t\t\t\t\tlog.Println(\"==> Load > NewType\", name)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdecl := defs.NewType(name, tName)\n\t\t\t\t\t\t\tif t.Doc != nil {\n\t\t\t\t\t\t\t\tdefs.SetComments(t.Doc)\n\t\t\t\t\t\t\t} else if d.Doc != nil {\n\t\t\t\t\t\t\t\tdefs.SetComments(d.Doc)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tld.typInit = func() { // decycle\n\t\t\t\t\t\t\t\tif debugLoad {\n\t\t\t\t\t\t\t\t\tlog.Println(\"==> Load > InitType\", name)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tdecl.InitType(ctx.pkg, toType(ctx, t.Type))\n\t\t\t\t\t\t\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\t\t\t\t\t\t\trec.Def(tName, decl.Type().Obj())\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tctx.generics[name] = true\n\t\t\t\t\t\tld.typ = func() {\n\t\t\t\t\t\t\tpkg := ctx.pkg.Types\n\t\t\t\t\t\t\tif t.Assign != token.NoPos { // alias type\n\t\t\t\t\t\t\t\tif debugLoad {\n\t\t\t\t\t\t\t\t\tlog.Println(\"==> Load > AliasType\", name)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\taliasType(ctx, pkg, t.Pos(), name, t)\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif debugLoad {\n\t\t\t\t\t\t\t\tlog.Println(\"==> Load > NewType\", name)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnamed := newType(pkg, t.Pos(), name)\n\n\t\t\t\t\t\t\tld.typInit = func() { // decycle\n\t\t\t\t\t\t\t\tif debugLoad {\n\t\t\t\t\t\t\t\t\tlog.Println(\"==> Load > InitType\", name)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tinitType(ctx, named, t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase token.CONST:\n\t\t\t\tpreloadConst(d)\n\t\t\tcase token.VAR:\n\t\t\t\tif d == classDecl { // skip class fields\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor _, spec := range d.Specs {\n\t\t\t\t\tvSpec := spec.(*ast.ValueSpec)\n\t\t\t\t\tif debugLoad {\n\t\t\t\t\t\tlog.Println(\"==> Preload var\", vSpec.Names)\n\t\t\t\t\t}\n\t\t\t\t\tsetNamesLoader(parent, syms, vSpec.Names, func() {\n\t\t\t\t\t\tif v := vSpec; v != nil { // only init once\n\t\t\t\t\t\t\tvSpec = nil\n\t\t\t\t\t\t\told, _ := p.SetCurFile(goFile, true)\n\t\t\t\t\t\t\tdefer p.RestoreCurFile(old)\n\t\t\t\t\t\t\tloadVars(ctx, v, d.Doc, true)\n\t\t\t\t\t\t\tremoveNames(syms, v.Names)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tlog.Panicln(\"TODO - tok:\", d.Tok, \"spec:\", reflect.TypeOf(d.Specs).Elem())\n\t\t\t}\n\n\t\tcase *ast.FuncDecl:\n\t\t\tpreloadFuncDecl(d)\n\n\t\tcase *ast.OverloadFuncDecl:\n\t\t\tvar recv *ast.Ident\n\t\t\tif ctx.classRecv != nil { // in class file (.spx/.gmx)\n\t\t\t\tif recv := d.Recv; recv == nil || len(recv.List) == 0 {\n\t\t\t\t\td.Recv = &ast.FieldList{\n\t\t\t\t\t\tList: []*ast.Field{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType: ctx.classRecv.List[0].Type.(*ast.StarExpr).X,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t\td.IsClass = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif d.Recv != nil {\n\t\t\t\tvar ok bool\n\t\t\t\trecv, ok = d.Recv.List[0].Type.(*ast.Ident)\n\t\t\t\tif !ok {\n\t\t\t\t\tctx.handleErrorf(d.Recv.List[0].Type.Pos(), d.Recv.List[0].Type.End(), \"invalid recv type %v\", ctx.LoadExpr(d.Recv.List[0].Type))\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tctx.lbinames = append(ctx.lbinames, recv)\n\t\t\t\tif ctx.rec != nil {\n\t\t\t\t\tctx.rec.ReferUse(recv, recv.Name)\n\t\t\t\t}\n\t\t\t}\n\t\t\tonames := make([]string, 0, 4)\n\t\t\texov := false\n\t\t\tname := d.Name\n\t\tLoopFunc:\n\n\t\t\tfor idx, fn := range d.Funcs {\n\t\t\t\tswitch expr := fn.(type) {\n\t\t\t\tcase *ast.Ident:\n\t\t\t\t\tif d.Recv != nil && !d.Operator && !d.IsClass {\n\t\t\t\t\t\tctx.handleErrorf(expr.Pos(), expr.End(), \"invalid method %v\", ctx.LoadExpr(expr))\n\t\t\t\t\t\tbreak LoopFunc\n\t\t\t\t\t}\n\t\t\t\t\texov = true\n\t\t\t\t\tif d.IsClass {\n\t\t\t\t\t\tonames = append(onames, \".\"+expr.Name)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tonames = append(onames, expr.Name)\n\t\t\t\t\t\tctx.lbinames = append(ctx.lbinames, expr.Name)\n\t\t\t\t\t}\n\t\t\t\t\tif ctx.rec != nil {\n\t\t\t\t\t\tif d.IsClass {\n\t\t\t\t\t\t\tctx.rec.ReferUse(expr, recv.Name+\".\"+expr.Name)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tctx.rec.ReferUse(expr, expr.Name)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase *ast.SelectorExpr:\n\t\t\t\t\tif d.Recv == nil || d.IsClass {\n\t\t\t\t\t\tctx.handleErrorf(expr.Pos(), expr.End(), \"invalid func %v\", ctx.LoadExpr(expr))\n\t\t\t\t\t\tbreak LoopFunc\n\t\t\t\t\t}\n\t\t\t\t\trtyp, ok := checkOverloadMethodRecvType(recv, expr.X)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tctx.handleErrorf(expr.Pos(), expr.End(), \"invalid recv type %v\", ctx.LoadExpr(expr.X))\n\t\t\t\t\t\tbreak LoopFunc\n\t\t\t\t\t}\n\n\t\t\t\t\tonames = append(onames, \".\"+expr.Sel.Name)\n\t\t\t\t\texov = true\n\t\t\t\t\tif ctx.rec != nil {\n\t\t\t\t\t\tctx.rec.ReferUse(rtyp, rtyp.Name)\n\t\t\t\t\t\tctx.rec.ReferUse(expr.Sel, rtyp.Name+\".\"+expr.Sel.Name)\n\t\t\t\t\t}\n\t\t\t\tcase *ast.FuncLit:\n\t\t\t\t\tif d.Recv != nil && !d.Operator && !d.IsClass {\n\t\t\t\t\t\tctx.handleErrorf(expr.Pos(), expr.End(), \"invalid method %v\", ctx.LoadExpr(expr))\n\t\t\t\t\t\tbreak LoopFunc\n\t\t\t\t\t}\n\t\t\t\t\tname1 := overloadFuncName(name.Name, idx)\n\t\t\t\t\tonames = append(onames, \"\") // const XGoo_xxx = \"xxxInt,,xxxFloat\"\n\t\t\t\t\tctx.lbinames = append(ctx.lbinames, name1)\n\t\t\t\t\tid := &ast.Ident{NamePos: expr.Pos(), Name: name1}\n\t\t\t\t\tif ctx.rec != nil {\n\t\t\t\t\t\tctx.rec.ReferDef(id, expr)\n\t\t\t\t\t}\n\t\t\t\t\tpreloadFuncDecl(&ast.FuncDecl{\n\t\t\t\t\t\tDoc:  d.Doc,\n\t\t\t\t\t\tName: id,\n\t\t\t\t\t\tType: expr.Type,\n\t\t\t\t\t\tBody: expr.Body,\n\t\t\t\t\t})\n\t\t\t\tdefault:\n\t\t\t\t\tctx.handleErrorf(expr.Pos(), expr.End(), \"unknown func %v\", ctx.LoadExpr(expr))\n\t\t\t\t\tbreak LoopFunc\n\t\t\t\t}\n\t\t\t}\n\t\t\tif exov { // need XGoo_xxx\n\t\t\t\toname, err := overloadName(recv, name.Name, d.Operator)\n\t\t\t\tif err != nil {\n\t\t\t\t\tctx.handleErrorf(name.Pos(), name.End(), \"%v\", err)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif recv != nil {\n\t\t\t\t\tctx.overpos[recv.Name+\".\"+name.Name] = name.NamePos\n\t\t\t\t} else {\n\t\t\t\t\tctx.overpos[name.Name] = name.NamePos\n\t\t\t\t}\n\t\t\t\toval := strings.Join(onames, \",\")\n\t\t\t\tpreloadConst(&ast.GenDecl{\n\t\t\t\t\tDoc: d.Doc,\n\t\t\t\t\tTok: token.CONST,\n\t\t\t\t\tSpecs: []ast.Spec{\n\t\t\t\t\t\t&ast.ValueSpec{\n\t\t\t\t\t\t\tNames:  []*ast.Ident{{Name: oname}},\n\t\t\t\t\t\t\tValues: []ast.Expr{stringLit(oval)},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tctx.lbinames = append(ctx.lbinames, oname)\n\t\t\t} else {\n\t\t\t\tctx.overpos[name.Name] = name.NamePos\n\t\t\t}\n\t\t\tif ctx.rec != nil {\n\t\t\t\tctx.rec.ReferDef(d.Name, d)\n\t\t\t}\n\n\t\tdefault:\n\t\t\tlog.Panicf(\"TODO - cl.preloadFile: unknown decl - %T\\n\", decl)\n\t\t}\n\t}\n}\n\nfunc checkOverloadMethodRecvType(ot *ast.Ident, recv ast.Expr) (*ast.Ident, bool) {\n\trtyp, _, ok := getRecvType(recv)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\trt, ok := rtyp.(*ast.Ident)\n\tif !ok || ot.Name != rt.Name {\n\t\treturn nil, false\n\t}\n\treturn rt, true\n}\n\nconst (\n\tindexTable = \"0123456789abcdefghijklmnopqrstuvwxyz\"\n)\n\nfunc overloadFuncName(name string, idx int) string {\n\treturn name + \"__\" + indexTable[idx:idx+1]\n}\n\nfunc overloadName(recv *ast.Ident, name string, isOp bool) (string, error) {\n\tif isOp {\n\t\tif oname, ok := binaryXGoNames[name]; ok {\n\t\t\tname = oname\n\t\t} else {\n\t\t\treturn \"\", fmt.Errorf(\"invalid overload operator %v\", name)\n\t\t}\n\t}\n\tsep := \"_\"\n\tif strings.ContainsRune(name, '_') || (recv != nil && strings.ContainsRune(recv.Name, '_')) {\n\t\tsep = \"__\"\n\t}\n\ttyp := \"\"\n\tif recv != nil {\n\t\ttyp = recv.Name + sep\n\t}\n\treturn \"XGoo\" + sep + typ + name, nil\n}\n\nfunc staticMethod(tname, name string) string {\n\tsep := \"_\"\n\tif strings.ContainsRune(name, '_') || strings.ContainsRune(tname, '_') {\n\t\tsep = \"__\"\n\t}\n\treturn \"XGos\" + sep + tname + sep + name\n}\n\nfunc stringLit(val string) *ast.BasicLit {\n\treturn &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(val)}\n}\n\nfunc newType(pkg *types.Package, pos token.Pos, name string) *types.Named {\n\ttypName := types.NewTypeName(pos, pkg, name, nil)\n\tif old := pkg.Scope().Insert(typName); old != nil {\n\t\tlog.Panicf(\"%s redeclared in this block\\n\\tprevious declaration at %v\\n\", name, \"<TODO>\")\n\t}\n\treturn types.NewNamed(typName, nil, nil)\n}\n\nfunc aliasType(ctx *blockCtx, pkg *types.Package, pos token.Pos, name string, t *ast.TypeSpec) {\n\tvar typeParams []*types.TypeParam\n\tif t.TypeParams != nil {\n\t\ttypeParams = toTypeParams(ctx, t.TypeParams)\n\t\tctx.tlookup = &typeParamLookup{typeParams}\n\t\tdefer func() {\n\t\t\tctx.tlookup = nil\n\t\t}()\n\t\torg := ctx.inInst\n\t\tctx.inInst = 0\n\t\tdefer func() {\n\t\t\tctx.inInst = org\n\t\t}()\n\t}\n\to := types.NewAlias(types.NewTypeName(pos, pkg, name, nil), toType(ctx, t.Type))\n\tif typeParams != nil {\n\t\to.SetTypeParams(typeParams)\n\t}\n\tpkg.Scope().Insert(o.Obj())\n}\n\nfunc loadFunc(ctx *blockCtx, recv *types.Var, name string, d *ast.FuncDecl, genBody bool) {\n\tif debugLoad {\n\t\tif recv == nil {\n\t\t\tlog.Println(\"==> Load func\", name)\n\t\t} else {\n\t\t\tlog.Printf(\"==> Load method %v.%s\\n\", recv.Type(), name)\n\t\t}\n\t}\n\tvar pkg = ctx.pkg\n\tvar initClass bool\n\tvar sigBase *types.Signature\n\tif d.Shadow {\n\t\tif recv != nil && (name == \"Main\" || name == \"MainEntry\") {\n\t\t\tinitClass = true // should call XGo_Init method\n\t\t\tif base := ctx.baseClass; base != nil {\n\t\t\t\tif f := findMethod(base, name); f != nil {\n\t\t\t\t\tsigBase = makeMainSig(recv, f)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else if d.Operator {\n\t\tif recv != nil { // binary op\n\t\t\tif v, ok := binaryXGoNames[name]; ok {\n\t\t\t\tname = v\n\t\t\t}\n\t\t} else { // unary op\n\t\t\tif v, ok := unaryXGoNames[name]; ok {\n\t\t\t\tname = v\n\t\t\t\tat := pkg.Types\n\t\t\t\targ1 := d.Type.Params.List[0]\n\t\t\t\ttyp := toType(ctx, arg1.Type)\n\t\t\t\trecv = types.NewParam(arg1.Pos(), at, arg1.Names[0].Name, typ)\n\t\t\t\td.Type.Params.List = nil\n\t\t\t}\n\t\t}\n\t}\n\tsig := sigBase\n\tif sig == nil {\n\t\tsig = toFuncType(ctx, d.Type, recv, d)\n\t}\n\tfn, err := pkg.NewFuncWith(d.Name.Pos(), name, sig, func() token.Pos {\n\t\treturn d.Recv.List[0].Type.Pos()\n\t})\n\tif err != nil {\n\t\tctx.handleErr(err)\n\t\treturn\n\t}\n\tcommentFunc(ctx, fn, d)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Def(d.Name, fn.Func)\n\t\tif recv == nil && name != \"_\" {\n\t\t\tctx.fileScope.Insert(fn.Func)\n\t\t}\n\t}\n\tif genBody {\n\t\tif body := d.Body; body != nil {\n\t\t\tif recv != nil {\n\t\t\t\tfile := pkg.CurFile()\n\t\t\t\tctx.inits = append(ctx.inits, func() { // interface issue: #795\n\t\t\t\t\told := pkg.RestoreCurFile(file)\n\t\t\t\t\tloadFuncBody(ctx, fn, body, sigBase, d, initClass)\n\t\t\t\t\tpkg.RestoreCurFile(old)\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tloadFuncBody(ctx, fn, body, nil, d, initClass)\n\t\t\t}\n\t\t}\n\t}\n}\n\nvar binaryXGoNames = map[string]string{\n\t\"+\": \"XGo_Add\",\n\t\"-\": \"XGo_Sub\",\n\t\"*\": \"XGo_Mul\",\n\t\"/\": \"XGo_Quo\",\n\t\"%\": \"XGo_Rem\",\n\n\t\"&\":  \"XGo_And\",\n\t\"|\":  \"XGo_Or\",\n\t\"^\":  \"XGo_Xor\",\n\t\"<<\": \"XGo_Lsh\",\n\t\">>\": \"XGo_Rsh\",\n\t\"&^\": \"XGo_AndNot\",\n\n\t\"+=\": \"XGo_AddAssign\",\n\t\"-=\": \"XGo_SubAssign\",\n\t\"*=\": \"XGo_MulAssign\",\n\t\"/=\": \"XGo_QuoAssign\",\n\t\"%=\": \"XGo_RemAssign\",\n\n\t\"&=\":  \"XGo_AndAssign\",\n\t\"|=\":  \"XGo_OrAssign\",\n\t\"^=\":  \"XGo_XorAssign\",\n\t\"<<=\": \"XGo_LshAssign\",\n\t\">>=\": \"XGo_RshAssign\",\n\t\"&^=\": \"XGo_AndNotAssign\",\n\n\t\"==\": \"XGo_EQ\",\n\t\"!=\": \"XGo_NE\",\n\t\"<=\": \"XGo_LE\",\n\t\"<\":  \"XGo_LT\",\n\t\">=\": \"XGo_GE\",\n\t\">\":  \"XGo_GT\",\n\n\t\"->\": \"XGo_PointTo\",\n\t\"<>\": \"XGo_PointBi\",\n\n\t\"&&\": \"XGo_LAnd\",\n\t\"||\": \"XGo_LOr\",\n\n\t\"<-\": \"XGo_Send\",\n}\n\nvar unaryXGoNames = map[string]string{\n\t\"++\": \"XGo_Inc\",\n\t\"--\": \"XGo_Dec\",\n\t\"-\":  \"XGo_Neg\",\n\t\"+\":  \"XGo_Dup\",\n\t\"^\":  \"XGo_Not\",\n\t\"!\":  \"XGo_LNot\",\n\t\"<-\": \"XGo_Recv\",\n}\n\n// shouldCallXGoInit checks if XGo_Init is directly defined on the receiver\n// type (not through embedding).\nfunc shouldCallXGoInit(recv *types.Var) bool {\n\ttyp := recv.Type()\n\tif pt, ok := typ.(*types.Pointer); ok {\n\t\ttyp = pt.Elem()\n\t}\n\treturn findMethodByType(typ, \"XGo_Init\") != nil\n}\n\nfunc loadFuncBody(ctx *blockCtx, fn *gogen.Func, body *ast.BlockStmt, sigBase *types.Signature, src ast.Node, initClass bool) {\n\tcb := fn.BodyStart(ctx.pkg, body)\n\tcb.SetComments(nil, false)\n\tif initClass {\n\t\trecv := fn.Type().(*types.Signature).Recv()\n\t\tif shouldCallXGoInit(recv) {\n\t\t\t// this.XGo_Init()\n\t\t\tcb.Val(recv).MemberVal(\"XGo_Init\", 0).Call(0).EndStmt()\n\t\t}\n\t\tif sigBase != nil {\n\t\t\t// this.Sprite.Main(...) or this.Game.MainEntry(...)\n\t\t\tcb.Val(recv).MemberVal(ctx.baseClass.Name(), 0).MemberVal(fn.Name(), 0)\n\t\t\tparams := sigBase.Params()\n\t\t\tn := params.Len()\n\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\tcb.Val(params.At(i))\n\t\t\t}\n\t\t\tcb.Call(n).EndStmt()\n\t\t}\n\t}\n\tcompileStmts(ctx, body.List)\n\tif rec := ctx.recorder(); rec != nil {\n\t\tswitch fn := src.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\trec.Scope(fn.Type, cb.Scope())\n\t\tcase *ast.FuncLit:\n\t\t\trec.Scope(fn.Type, cb.Scope())\n\t\t}\n\t}\n\tcb.End(src)\n}\n\nfunc loadImport(ctx *blockCtx, spec *ast.ImportSpec) {\n\tif enableRecover {\n\t\tdefer func() {\n\t\t\tif e := recover(); e != nil {\n\t\t\t\tctx.handleRecover(e, spec)\n\t\t\t}\n\t\t}()\n\t}\n\tpkgPath := simplifyPkgPath(toString(spec.Path))\n\tpkg := ctx.pkg.Import(pkgPath, spec)\n\n\tvar pos token.Pos\n\tvar name string\n\tvar specName = spec.Name\n\tif specName != nil {\n\t\tname, pos = specName.Name, specName.Pos()\n\t} else {\n\t\tname, pos = pkg.Types.Name(), spec.Path.Pos()\n\t}\n\tpkgName := types.NewPkgName(pos, ctx.pkg.Types, name, pkg.Types)\n\tif rec := ctx.recorder(); rec != nil {\n\t\tif specName != nil {\n\t\t\trec.Def(specName, pkgName)\n\t\t} else {\n\t\t\trec.Implicit(spec, pkgName)\n\t\t}\n\t\tif name != \"_\" {\n\t\t\tctx.fileScope.Insert(pkgName)\n\t\t}\n\t}\n\n\tif specName != nil {\n\t\tif name == \".\" {\n\t\t\tctx.lookups = append(ctx.lookups, pkg)\n\t\t\treturn\n\t\t}\n\t\tif name == \"_\" {\n\t\t\tpkg.MarkForceUsed(ctx.pkg)\n\t\t\treturn\n\t\t}\n\t}\n\tctx.imports[name] = pkgImp{pkg, pkgName}\n}\n\nfunc loadConstSpecs(ctx *blockCtx, cdecl *gogen.ConstDefs, specs []ast.Spec) {\n\tfor iotav, spec := range specs {\n\t\tvSpec := spec.(*ast.ValueSpec)\n\t\tloadConsts(ctx, cdecl, vSpec, iotav)\n\t}\n}\n\nfunc loadConsts(ctx *blockCtx, cdecl *gogen.ConstDefs, v *ast.ValueSpec, iotav int) {\n\tvNames := v.Names\n\tnames := makeNames(vNames)\n\tif v.Values == nil {\n\t\tif debugLoad {\n\t\t\tlog.Println(\"==> Load const\", names)\n\t\t}\n\t\tcdecl.Next(iotav, v.Pos(), names...)\n\t\tdefNames(ctx, vNames, nil)\n\t\treturn\n\t}\n\tvar typ types.Type\n\tif v.Type != nil {\n\t\ttyp = toType(ctx, v.Type)\n\t}\n\tif debugLoad {\n\t\tlog.Println(\"==> Load const\", names, typ)\n\t}\n\tfn := func(cb *gogen.CodeBuilder) int {\n\t\tfor _, val := range v.Values {\n\t\t\tcompileExpr(ctx, 1, val)\n\t\t}\n\t\treturn len(v.Values)\n\t}\n\tcdecl.New(fn, iotav, v.Pos(), typ, names...)\n\tdefNames(ctx, v.Names, nil)\n}\n\nfunc loadVars(ctx *blockCtx, v *ast.ValueSpec, doc *ast.CommentGroup, global bool) {\n\tvar typ types.Type\n\tif v.Type != nil {\n\t\ttyp = toType(ctx, v.Type)\n\t}\n\tnames := makeNames(v.Names)\n\tif debugLoad {\n\t\tlog.Println(\"==> Load var\", typ, names)\n\t}\n\tvar scope *types.Scope\n\tif global {\n\t\tscope = ctx.pkg.Types.Scope()\n\t} else {\n\t\tscope = ctx.cb.Scope()\n\t}\n\tvarDefs := ctx.pkg.NewVarDefs(scope).SetComments(doc)\n\tinitExpr := makeInitExpr(ctx, v, typ, names)\n\tvarDefs.NewAndInit(initExpr, v.Names[0].Pos(), typ, names...)\n\tdefNames(ctx, v.Names, scope)\n}\n\nfunc makeInitExpr(ctx *blockCtx, v *ast.ValueSpec, typ types.Type, names []string) gogen.F {\n\tnv := len(v.Values)\n\tif nv == 0 {\n\t\treturn nil\n\t}\n\treturn func(cb *gogen.CodeBuilder) int {\n\t\tif nv == 1 && len(names) == 2 {\n\t\t\tcompileExpr(ctx, len(names), v.Values[0], 0)\n\t\t} else {\n\t\t\tfor _, val := range v.Values {\n\t\t\t\tswitch e := val.(type) {\n\t\t\t\tcase *ast.LambdaExpr, *ast.LambdaExpr2:\n\t\t\t\t\tif len(v.Values) == 1 {\n\t\t\t\t\t\tsig, err := checkLambdaFuncType(ctx, e, typ, clLambaAssign, v.Names[0])\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tpanic(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcompileLambda(ctx, e, sig)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpanic(ctx.newCodeErrorf(e.Pos(), e.End(), \"lambda unsupport multiple assignment\"))\n\t\t\t\t\t}\n\t\t\t\tcase *ast.SliceLit:\n\t\t\t\t\tcompileSliceLit(ctx, e, typ)\n\t\t\t\tcase *ast.CompositeLit:\n\t\t\t\t\tcompileCompositeLit(ctx, e, typ, false)\n\t\t\t\tdefault:\n\t\t\t\t\tcompileExpr(ctx, 1, val)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nv\n\t}\n}\n\nfunc defNames(ctx *blockCtx, names []*ast.Ident, scope *types.Scope) {\n\tif rec := ctx.recorder(); rec != nil {\n\t\tif scope == nil {\n\t\t\tscope = ctx.cb.Scope()\n\t\t}\n\t\tfor _, name := range names {\n\t\t\tif o := scope.Lookup(name.Name); o != nil {\n\t\t\t\trec.Def(name, o)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc makeNames(vals []*ast.Ident) []string {\n\tnames := make([]string, len(vals))\n\tfor i, v := range vals {\n\t\tnames[i] = v.Name\n\t}\n\treturn names\n}\n\nfunc removeNames(syms map[string]loader, names []*ast.Ident) {\n\tfor _, name := range names {\n\t\tdelete(syms, name.Name)\n\t}\n}\n\nfunc setNamesLoader(ctx *pkgCtx, syms map[string]loader, names []*ast.Ident, load func()) {\n\tfor _, name := range names {\n\t\tinitLoader(ctx, syms, name.Pos(), name.End(), name.Name, load, true)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/compile_spx_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cl/cltest\"\n\t\"github.com/goplus/xgo/parser/fsx/memfs\"\n)\n\nfunc gopSpxTest(t *testing.T, gmx, spxcode, expected string) {\n\tcltest.SpxEx(t, gmx, spxcode, expected, \"index.tgmx\", \"bar.tspx\")\n}\n\nfunc gopSpxTestEx(t *testing.T, gmx, spxcode, expected, gmxfile, spxfile string) {\n\tcltest.SpxWithConf(t, \"gopSpxTest\", cltest.Conf, gmx, spxcode, expected, gmxfile, spxfile, \"\")\n}\n\nfunc gopSpxTestEx2(t *testing.T, gmx, spxcode, expected, gmxfile, spxfile, resultFile string) {\n\tcltest.SpxWithConf(t, \"gopSpxTest\", cltest.Conf, gmx, spxcode, expected, gmxfile, spxfile, resultFile)\n}\n\nfunc gopSpxTestExConf(t *testing.T, name string, conf *cl.Config, gmx, spxcode, expected, gmxfile, spxfile, resultFile string) {\n\tcltest.SpxWithConf(t, name, conf, gmx, spxcode, expected, gmxfile, spxfile, resultFile)\n}\n\nfunc gopSpxErrorTestEx(t *testing.T, msg, gmx, spxcode, gmxfile, spxfile string) {\n\tcltest.SpxErrorEx(t, msg, gmx, spxcode, gmxfile, spxfile)\n}\n\nfunc gopSpxErrorTestMap(t *testing.T, msg string, dirs map[string][]string, files map[string]string) {\n\tcltest.SpxErrorFS(t, msg, memfs.New(dirs, files))\n}\n\nfunc TestSpxError(t *testing.T) {\n\tgopSpxErrorTestEx(t, `Game.tgmx:7:16: cannot use *Kai (type Kai) as type github.com/goplus/xgo/cl/internal/spx.Sprite in slice literal`, `\nvar (\n\tfoo []Sprite\n\tKai *Kai\n)\n\nfoo = []Sprite{*Kai}\n`, ``, \"Game.tgmx\", \"Kai.tspx\")\n\n\tgopSpxErrorTestEx(t, `Game.tgmx:1:6: Game redeclared in this block\n\tprevious declaration at Game.tgmx:1:1\nGame.tgmx:1:1: invalid receiver type Game (Game is an interface type)\nGame.tgmx:1:1: invalid receiver type Game (Game is an interface type)`, `type Game interface {}`, ``, \"Game.tgmx\", \"Kai.tspx\")\n\n\tgopSpxErrorTestEx(t, `Kai.tspx:1:1: Kai redeclared in this block\n\tprevious declaration at Game.tgmx:1:6`, `type Kai interface {}`, ``, \"Game.tgmx\", \"Kai.tspx\")\n\n\tgopSpxErrorTestEx(t, `Game.tgmx:6:2: userScore redeclared\n\tGame.tgmx:5:2 other declaration of userScore`, `\nimport \"bytes\"\nvar (\n\tKai Kai\n\tuserScore int\n\tuserScore string\n)\n`, `\nprintln \"hi\"\n`, \"Game.tgmx\", \"Kai.tspx\")\n\n\tgopSpxErrorTestEx(t, `Kai.tspx:4:2: id redeclared\n\tKai.tspx:3:2 other declaration of id`, `\nvar (\n\tKai Kai\n\tuserScore int\n)\n`, `\nvar (\n\tid int\n\tid string\n)\nprintln \"hi\"\n`, \"Game.tgmx\", \"Kai.tspx\")\n\n\tgopSpxErrorTestEx(t, `Game.t4gmx:6:2: userScore redeclared\n\tGame.t4gmx:5:2 other declaration of userScore\nKai.t4spx:1:1: cannot use  (type *Kai) as type github.com/goplus/xgo/cl/internal/spx4.Sprite in argument to `, `\nimport \"bytes\"\nvar (\n\tKai Kai\n\tuserScore int\n\tuserScore string\n)\n`, `\nprintln \"hi\"\n`, \"Game.t4gmx\", \"Kai.t4spx\")\n\n\tgopSpxErrorTestMap(t, `Kai.t4spx:4:2: userScore redeclared\n\tKai.t4spx:3:2 other declaration of userScore\nGreem.t4spx:1:1: cannot use  (type *Greem) as type github.com/goplus/xgo/cl/internal/spx4.Sprite in argument to `, map[string][]string{\n\t\t\"/foo\": {\"Game.t4gmx\", \"Kai.t4spx\", \"Greem.t4spx\"},\n\t}, map[string]string{\n\t\t\"/foo/Game.t4gmx\": `println \"hi\"`,\n\t\t\"/foo/Kai.t4spx\": `var (\n\tKai Kai\n\tuserScore int\n\tuserScore string\n)`,\n\t\t\"/foo/Greem.t4spx\": ``,\n\t})\n\n\tgopSpxErrorTestMap(t, `Game.t4gmx:1:9: cannot use backdropName (type string) as type error in assignment`, map[string][]string{\n\t\t\"/foo\": {\"Game.t4gmx\"},\n\t}, map[string]string{\n\t\t\"/foo/Game.t4gmx\": `println backdropName!`,\n\t})\n\n\tgopSpxErrorTestEx(t, `Game.t5gmx:4:2: Kai conflicts with class name.\n\trename the field to resolve the naming conflict.\nKai.t5spx:1:1: cannot use  (type *Kai) as type github.com/goplus/xgo/cl/internal/spx4.Sprite in argument to `, `\n\nvar (\n\tKai Kai\n)\n`, `\nprintln \"hi\"\n`, \"Game.t5gmx\", \"Kai.t5spx\")\n}\n\nfunc TestSpxBasic(t *testing.T) {\n\tgopSpxTest(t, `\nimport (\n\t\"fmt\"\n)\n\nconst (\n\tFoo = 1\n)\n\nfunc bar() {\n}\n\nfunc onInit() {\n\tFoo\n\tbar\n\tfmt.Println(\"Hi\")\n}\n`, ``, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx\"\n)\n\nconst Foo = 1\n\ntype bar struct {\n\tspx.Sprite\n\t*index\n}\ntype index struct {\n\t*spx.MyGame\n}\n\nfunc (this *index) bar() {\n}\nfunc (this *index) onInit() {\n\tthis.bar()\n\tfmt.Println(\"Hi\")\n}\nfunc (this *index) MainEntry() {\n}\nfunc (this *index) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *bar) Main() {\n}\nfunc main() {\n\tnew(index).Main()\n}\n`)\n}\n\nfunc TestEnvOp(t *testing.T) {\n\tgopSpxTest(t, `\necho ${PATH}, $id\n`, ``, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx\"\n)\n\ntype bar struct {\n\tspx.Sprite\n\t*index\n}\ntype index struct {\n\t*spx.MyGame\n}\n\nfunc (this *index) MainEntry() {\n\tfmt.Println(this.Gop_Env(\"PATH\"), this.Gop_Env(\"id\"))\n}\nfunc (this *index) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *bar) Main() {\n}\nfunc main() {\n\tnew(index).Main()\n}\n`)\n}\n\nfunc TestSpxXGoEnv(t *testing.T) {\n\tgopSpxTest(t, `\necho \"${PATH}\"\n`, ``, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx\"\n\t\"strconv\"\n)\n\ntype bar struct {\n\tspx.Sprite\n\t*index\n}\ntype index struct {\n\t*spx.MyGame\n}\n\nfunc (this *index) MainEntry() {\n\tfmt.Println(strconv.Itoa(this.Gop_Env(\"PATH\")))\n}\nfunc (this *index) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *bar) Main() {\n}\nfunc main() {\n\tnew(index).Main()\n}\n`)\n}\n\nfunc TestSpxXGoExec(t *testing.T) {\n\tgopSpxTest(t, `\nvim \"a.txt\"\nvim\nls 10\ncapout => { ls }\ncapout => { ls \"-l\" }\n`, ``, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\ntype bar struct {\n\tspx.Sprite\n\t*index\n}\ntype index struct {\n\t*spx.MyGame\n}\n\nfunc (this *index) MainEntry() {\n\tthis.Gop_Exec(\"vim\", \"a.txt\")\n\tthis.Gop_Exec(\"vim\")\n\tthis.Ls(10)\n\tthis.Capout(func() {\n\t\tthis.Gop_Exec(\"ls\")\n\t})\n\tthis.Capout(func() {\n\t\tthis.Gop_Exec(\"ls\", \"-l\")\n\t})\n}\nfunc (this *index) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *bar) Main() {\n}\nfunc main() {\n\tnew(index).Main()\n}\n`)\n}\n\nfunc TestSpxMethod(t *testing.T) {\n\tgopSpxTestEx(t, `\nfunc onInit() {\n\tsched\n\tbroadcast \"msg1\"\n\tTestIntValue = 1\n\tx := round(1.2)\n}\n`, `\nfunc onInit() {\n\tsetCostume \"kai-a\"\n\tplay \"recordingWhere\"\n\tsay \"Where do you come from?\", 2\n\tbroadcast \"msg2\"\n}\n`, `package main\n\nimport (\n\t\"github.com/goplus/xgo/cl/internal/spx\"\n\t\"math\"\n)\n\ntype Game struct {\n\t*spx.MyGame\n}\ntype bar struct {\n\tspx.Sprite\n\t*Game\n}\n\nfunc (this *Game) onInit() {\n\tspx.Sched()\n\tthis.Broadcast__0(\"msg1\")\n\tspx.TestIntValue = 1\n\tx := math.Round(1.2)\n}\nfunc (this *Game) MainEntry() {\n}\nfunc (this *Game) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *bar) onInit() {\n\tthis.SetCostume(\"kai-a\")\n\tthis.Play(\"recordingWhere\")\n\tthis.Say(\"Where do you come from?\", 2)\n\tthis.Broadcast__0(\"msg2\")\n}\nfunc (this *bar) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.tgmx\", \"bar.tspx\")\n}\n\nfunc TestSpxVar(t *testing.T) {\n\tgopSpxTestEx(t, `\nvar (\n\tKai Kai\n)\n\nfunc onInit() {\n\tKai.clone()\n\tbroadcast(\"msg1\")\n}\n`, `\nvar (\n\ta int\n)\n\nfunc onInit() {\n\ta = 1\n}\n\nfunc onCloned() {\n\tsay(\"Hi\")\n}\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\ntype Game struct {\n\t*spx.MyGame\n\tKai Kai\n}\ntype Kai struct {\n\tspx.Sprite\n\t*Game\n\ta int\n}\n\nfunc (this *Game) onInit() {\n\tspx.Gopt_Sprite_Clone__0(this.Kai)\n\tthis.Broadcast__0(\"msg1\")\n}\nfunc (this *Game) MainEntry() {\n}\nfunc (this *Game) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *Kai) onInit() {\n\tthis.a = 1\n}\nfunc (this *Kai) onCloned() {\n\tthis.Say(\"Hi\")\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.tgmx\", \"Kai.tspx\")\n}\n\nfunc TestSpxRun(t *testing.T) {\n\tgopSpxTestEx(t, `\nvar (\n\tKai Kai\n\tt   Sound\n)\n\nvar x float64 = rand(1.2)\n\nrun \"hzip://open.qiniu.us/weather/res.zip\"\n`, `\nprintln \"Hi\"\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx\"\n)\n\ntype Kai struct {\n\tspx.Sprite\n\t*index\n}\ntype index struct {\n\t*spx.MyGame\n\tKai Kai\n\tt   spx.Sound\n}\n\nvar x float64 = spx.Rand__1(1.2)\n\nfunc (this *index) MainEntry() {\n\tspx.Gopt_MyGame_Run(this, \"hzip://open.qiniu.us/weather/res.zip\")\n}\nfunc (this *index) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *Kai) Main() {\n\tfmt.Println(\"Hi\")\n}\nfunc main() {\n\tnew(index).Main()\n}\n`, \"index.tgmx\", \"Kai.tspx\")\n}\n\nfunc TestSpx2(t *testing.T) {\n\tgopSpxTestEx(t, `\nprintln(\"Hi\")\n`, `\nfunc onMsg(msg string) {\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx2\"\n)\n\ntype Game struct {\n\tspx2.Game\n}\ntype Kai struct {\n\tspx2.Sprite\n\t*Game\n}\n\nfunc (this *Game) MainEntry() {\n\tfmt.Println(\"Hi\")\n}\nfunc (this *Game) Main() {\n\t(*spx2.Game).Main(&this.Game)\n}\nfunc (this *Kai) onMsg(msg string) {\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.t2gmx\", \"Kai.t2spx\")\n}\n\nfunc TestSpxMainEntry(t *testing.T) {\n\tconf := *cltest.Conf\n\tconf.Importer = nil\n\tconf.NoAutoGenMain = false\n\n\tgopSpxTestExConf(t, \"Nocode\", &conf, `\n`, `\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx2\"\n\ntype Game struct {\n\tspx2.Game\n}\ntype Kai struct {\n\tspx2.Sprite\n\t*Game\n}\n\nfunc (this *Game) MainEntry() {\n}\nfunc (this *Game) Main() {\n\t(*spx2.Game).Main(&this.Game)\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.t2gmx\", \"Kai.t2spx\", \"\")\n\tgopSpxTestExConf(t, \"OnlyGmx\", &conf, `\nvar (\n\tKai Kai\n)\n`, `\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx2\"\n\ntype Game struct {\n\tspx2.Game\n\tKai Kai\n}\ntype Kai struct {\n\tspx2.Sprite\n\t*Game\n}\n\nfunc (this *Game) MainEntry() {\n}\nfunc (this *Game) Main() {\n\t(*spx2.Game).Main(&this.Game)\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.t2gmx\", \"Kai.t2spx\", \"\")\n\n\tgopSpxTestExConf(t, \"KaiAndGmx\", &conf, `\nvar (\n\tKai Kai\n)\nfunc MainEntry() {\n\tprintln \"Hi\"\n}\n`, `\nfunc Main() {\n\tprintln \"Hello\"\n}\nfunc onMsg(msg string) {\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx2\"\n)\n\ntype Game struct {\n\tspx2.Game\n\tKai Kai\n}\ntype Kai struct {\n\tspx2.Sprite\n\t*Game\n}\n\nfunc (this *Game) MainEntry() {\n\tfmt.Println(\"Hi\")\n}\nfunc (this *Game) Main() {\n\t(*spx2.Game).Main(&this.Game)\n}\nfunc (this *Kai) Main() {\n\tfmt.Println(\"Hello\")\n}\nfunc (this *Kai) onMsg(msg string) {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.t2gmx\", \"Kai.t2spx\", \"\")\n}\n\nfunc TestSpxGoxBasic(t *testing.T) {\n\tgopSpxTestEx(t, `\nfunc onInit() {\n\tfor {\n\t}\n}\n`, `\nfunc onMsg(msg string) {\n\tfor {\n\t\tsay \"Hi\"\n\t}\n}\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\ntype Game struct {\n\t*spx.MyGame\n}\ntype Kai struct {\n\tspx.Sprite\n\t*Game\n}\n\nfunc (this *Game) onInit() {\n\tfor {\n\t\tspx.SchedNow()\n\t}\n}\nfunc (this *Game) MainEntry() {\n}\nfunc (this *Game) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *Kai) onMsg(msg string) {\n\tfor {\n\t\tspx.Sched()\n\t\tthis.Say(\"Hi\")\n\t}\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.tgmx\", \"Kai.tspx\")\n}\n\nfunc TestSpxClone(t *testing.T) {\n\tgopSpxTestEx(t, `\nvar (\n\tKai Kai\n)\n\nfunc onInit() {\n\tKai.clone()\n\tbroadcast(\"msg1\")\n}\n`, `\nvar (\n\ta int\n)\n\ntype info struct {\n\tx int\n\ty int\n}\n\nfunc onInit() {\n\ta = 1\n\tclone\n\tclone info{1,2}\n\tclone &info{1,2}\n}\n\nfunc onCloned() {\n\tsay(\"Hi\")\n}\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\ntype info struct {\n\tx int\n\ty int\n}\ntype Game struct {\n\t*spx.MyGame\n\tKai Kai\n}\ntype Kai struct {\n\tspx.Sprite\n\t*Game\n\ta int\n}\n\nfunc (this *Game) onInit() {\n\tspx.Gopt_Sprite_Clone__0(this.Kai)\n\tthis.Broadcast__0(\"msg1\")\n}\nfunc (this *Game) MainEntry() {\n}\nfunc (this *Game) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *Kai) onInit() {\n\tthis.a = 1\n\tspx.Gopt_Sprite_Clone__0(this)\n\tspx.Gopt_Sprite_Clone__1(this, info{1, 2})\n\tspx.Gopt_Sprite_Clone__1(this, &info{1, 2})\n}\nfunc (this *Kai) onCloned() {\n\tthis.Say(\"Hi\")\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.tgmx\", \"Kai.tspx\")\n}\n\nfunc TestSpxErrorEnv(t *testing.T) {\n\tgopSpxErrorTestEx(t, `Game.t2gmx:2:9: undefined: PATH`, `\necho \"${PATH}\"\n`, ``, \"Game.t2gmx\", \"Kai.t2spx\")\n}\n\nfunc TestSpxErrorSel(t *testing.T) {\n\tgopSpxErrorTestEx(t, `Kai.tspx:2:9: this.pos undefined (type *Kai has no field or method pos)`, `\nprintln \"hi\"\n`, `\nprintln this.pos\n`, \"Game.tgmx\", \"Kai.tspx\")\n}\n\nfunc TestSpxMethodSel(t *testing.T) {\n\tgopSpxTestEx(t, `\nsendMessage \"Hi\"\n`, `\nfunc onMsg(msg string) {\n}\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\ntype Game struct {\n\t*spx.MyGame\n}\ntype Kai struct {\n\tspx.Sprite\n\t*Game\n}\n\nfunc (this *Game) MainEntry() {\n\tthis.SendMessage(\"Hi\")\n}\nfunc (this *Game) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *Kai) onMsg(msg string) {\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.tgmx\", \"Kai.tspx\")\n}\n\nfunc TestSpxPkgOverload(t *testing.T) {\n\tgopSpxTestEx(t, `\nprintln \"Hi\"\n`, `\nfunc onMsg(msg string) {\n\tthis.position.add 100,200\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx\"\n)\n\ntype Game struct {\n\t*spx.MyGame\n}\ntype Kai struct {\n\tspx.Sprite\n\t*Game\n}\n\nfunc (this *Game) MainEntry() {\n\tfmt.Println(\"Hi\")\n}\nfunc (this *Game) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *Kai) onMsg(msg string) {\n\tthis.Position().Add__0(100, 200)\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.tgmx\", \"Kai.tspx\")\n}\n\nfunc TestSpxSelection(t *testing.T) {\n\tgopSpxTestEx(t, `\nprintln \"hi\"\n`, `\nimport \"fmt\"\nfunc onMsg(msg string) {\n\tfmt.println msg\n\tthis.position.add 100,200\n\tposition.add 100,200\n\tposition.X += 100\n\tprintln position.X\n\tthis.vector.add 100,200\n\tvector.add 100,200\n\tvector.X += 100\n\tvector.self.X += 100\n\tvector.self.Y += 200\n\tvector.self.add position.X,position.Y\n\tprintln vector.X\n\tprintln vector.self.self\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx\"\n)\n\ntype Game struct {\n\t*spx.MyGame\n}\ntype Kai struct {\n\tspx.Sprite\n\t*Game\n}\n\nfunc (this *Game) MainEntry() {\n\tfmt.Println(\"hi\")\n}\nfunc (this *Game) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *Kai) onMsg(msg string) {\n\tfmt.Println(msg)\n\tthis.Position().Add__0(100, 200)\n\tthis.Position().Add__0(100, 200)\n\tthis.Position().X += 100\n\tfmt.Println(this.Position().X)\n\tthis.Vector().Add__0(100, 200)\n\tthis.Vector().Add__0(100, 200)\n\tthis.Vector().X += 100\n\tthis.Vector().Self().X += 100\n\tthis.Vector().Self().Y += 200\n\tthis.Vector().Self().Add__0(this.Position().X, this.Position().Y)\n\tfmt.Println(this.Vector().X)\n\tfmt.Println(this.Vector().Self().Self())\n}\nfunc (this *Kai) Main() {\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.tgmx\", \"Kai.tspx\")\n}\n\nfunc TestSpxOverload(t *testing.T) {\n\tgopSpxTestEx(t, `\nvar (\n\tKai Kai\n)\n\nfunc onInit() {\n\tKai.onKey \"hello\", key => {\n\t}\n}\n`, `\nvar (\n\ta int\n)\n\ntype Mesh struct {\n}\n\nfunc (p *Mesh) Name() string {\n\treturn \"hello\"\n}\n\nvar (\n\tm1 = &Mesh{}\n\tm2 = &Mesh{}\n)\n\nonKey \"hello\", => {\n}\nonKey \"hello\", key => {\n}\nonKey [\"1\"], => {\n}\nonKey [\"2\"], key => {\n}\nonKey [m1, m2], => {\n}\nonKey [m1, m2], key => {\n}\nonKey [\"a\"], [\"b\"], key => {\n}\nonKey [\"a\"], [m1, m2], key => {\n}\nonKey [\"a\"], nil, key => {\n}\nonKey 100, 200\nonKey2 \"hello\", key => {\n}\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\ntype Mesh struct {\n}\ntype Game struct {\n\t*spx.MyGame\n\tKai Kai\n}\ntype Kai struct {\n\tspx.Sprite\n\t*Game\n\ta int\n}\n\nfunc (this *Game) onInit() {\n\tspx.Gopt_Sprite_OnKey__1(this.Kai, \"hello\", func(key string) {\n\t})\n}\nfunc (this *Game) MainEntry() {\n}\nfunc (this *Game) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (p *Mesh) Name() string {\n\treturn \"hello\"\n}\n\nvar m1 = &Mesh{}\nvar m2 = &Mesh{}\n\nfunc (this *Kai) Main() {\n\tspx.Gopt_Sprite_OnKey__0(this, \"hello\", func() {\n\t})\n\tspx.Gopt_Sprite_OnKey__1(this, \"hello\", func(key string) {\n\t})\n\tspx.Gopt_Sprite_OnKey__2(this, []string{\"1\"}, func() {\n\t})\n\tspx.Gopt_Sprite_OnKey__3(this, []string{\"2\"}, func(key string) {\n\t})\n\tspx.Gopt_Sprite_OnKey__4(this, []spx.Mesher{m1, m2}, func() {\n\t})\n\tspx.Gopt_Sprite_OnKey__5(this, []spx.Mesher{m1, m2}, func(key spx.Mesher) {\n\t})\n\tspx.Gopt_Sprite_OnKey__6(this, []string{\"a\"}, []string{\"b\"}, func(key string) {\n\t})\n\tspx.Gopt_Sprite_OnKey__7(this, []string{\"a\"}, []spx.Mesher{m1, m2}, func(key string) {\n\t})\n\tspx.Gopt_Sprite_OnKey__6(this, []string{\"a\"}, nil, func(key string) {\n\t})\n\tspx.Gopt_Sprite_OnKey__8(this, 100, 200)\n\tspx.Gopt_Sprite_OnKey2(this, \"hello\", func(key string) {\n\t})\n}\nfunc main() {\n\tnew(Game).Main()\n}\n`, \"Game.tgmx\", \"Kai.tspx\")\n}\n\nfunc TestTestClassFile(t *testing.T) {\n\tgopSpxTestEx2(t, `\nprintln \"Hi\"\n`, `\nt.log \"Hi\"\nt.run \"a test\", t => {\n\tt.fatal \"failed\"\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/test\"\n\t\"testing\"\n)\n\ntype caseFoo struct {\n\ttest.Case\n}\ntype App struct {\n\ttest.App\n}\n\nfunc (this *App) MainEntry() {\n\tfmt.Println(\"Hi\")\n}\nfunc (this *caseFoo) Main() {\n\tthis.T().Log(\"Hi\")\n\tthis.T().Run(\"a test\", func(t *testing.T) {\n\t\tt.Fatal(\"failed\")\n\t})\n}\nfunc TestFoo(t *testing.T) {\n\ttest.Gopt_Case_TestMain(new(caseFoo), t)\n}\nfunc TestMain(m *testing.M) {\n\ttest.Gopt_App_TestMain(new(App), m)\n}\n`, \"main_xtest.gox\", \"Foo_xtest.gox\", \"_test\")\n}\n\nfunc TestTestClassFile2(t *testing.T) {\n\tgopSpxTestEx2(t, `\nprintln \"Hi\"\n`, `\nt.log \"Hi\"\n`, `package main\n\nimport (\n\t\"github.com/goplus/xgo/test\"\n\t\"testing\"\n)\n\ntype case_foo struct {\n\ttest.Case\n}\n\nfunc (this *case_foo) Main() {\n\tthis.T().Log(\"Hi\")\n}\nfunc Test_foo(t *testing.T) {\n\ttest.Gopt_Case_TestMain(new(case_foo), t)\n}\n`, \"main.gox\", \"foo_xtest.gox\", \"_test\")\n}\n\nfunc TestGoxNoFunc(t *testing.T) {\n\tgopClTestFile(t, `\nvar (\n\ta int\n)\n`, `package main\n\ntype foo struct {\n\ta int\n}\n`, \"foo.gox\")\n}\n\nfunc TestGoxOverload(t *testing.T) {\n\tgopClTestFile(t, `\nfunc addString(a, b string) string {\n\treturn a + b\n}\n\nfunc addInt(a, b int) int {\n\treturn a + b\n}\n\nfunc add = (\n\taddInt\n\tfunc(a, b float64) float64 {\n\t\treturn a + b\n\t}\n\taddString\n)\n`, `package main\n\nconst XGoo_Rect_add = \".addInt,,.addString\"\n\ntype Rect struct {\n}\n\nfunc (this *Rect) addString(a string, b string) string {\n\treturn a + b\n}\nfunc (this *Rect) addInt(a int, b int) int {\n\treturn a + b\n}\nfunc (this *Rect) add__1(a float64, b float64) float64 {\n\treturn a + b\n}\n`, \"Rect.gox\")\n}\n\nfunc TestClassFileGox(t *testing.T) {\n\tgopClTestFile(t, `\nvar (\n\tBaseClass\n\tWidth, Height float64\n\t*AggClass\n)\n\ntype BaseClass struct{\n\tx int\n\ty int\n}\ntype AggClass struct{}\n\nfunc Area() float64 {\n\treturn Width * Height\n}\n`, `package main\n\ntype BaseClass struct {\n\tx int\n\ty int\n}\ntype AggClass struct {\n}\ntype Rect struct {\n\tBaseClass\n\tWidth  float64\n\tHeight float64\n\t*AggClass\n}\n\nfunc (this *Rect) Area() float64 {\n\treturn this.Width * this.Height\n}\n`, \"Rect.gox\")\n\tgopClTestFile(t, `\nimport \"bytes\"\nvar (\n\tbytes.Buffer\n)\nfunc test(){}\n`, `package main\n\nimport \"bytes\"\n\ntype Rect struct {\n\tbytes.Buffer\n}\n\nfunc (this *Rect) test() {\n}\n`, \"Rect.gox\")\n\tgopClTestFile(t, `\nimport \"bytes\"\nvar (\n\t*bytes.Buffer\n)\nfunc test(){}\n`, `package main\n\nimport \"bytes\"\n\ntype Rect struct {\n\t*bytes.Buffer\n}\n\nfunc (this *Rect) test() {\n}\n`, \"Rect.gox\")\n\tgopClTestFile(t, `\nimport \"bytes\"\nvar (\n\t*bytes.Buffer \"buffer\"\n\ta int \"a\"\n\tb int\n)\nfunc test(){}\n`, `package main\n\nimport \"bytes\"\n\ntype Rect struct {\n\t*bytes.Buffer `+\"`_:\\\"buffer\\\"`\"+`\n\ta             int `+\"`_:\\\"a\\\"`\"+`\n\tb             int\n}\n\nfunc (this *Rect) test() {\n}\n`, \"Rect.gox\")\n}\n\nfunc TestClassFileMember(t *testing.T) {\n\tgopClTestFile(t, `type Engine struct {\n}\n\nfunc (e *Engine) EnterPointerLock() {\n}\n\nfunc (e *Engine) SetEnable(b bool) {\n}\n\nfunc Engine() *Engine {\n\treturn &Engine{}\n}\n\nfunc Test() {\n\tengine.setEnable true\n\tengine.enterPointerLock\n}\n`, `package main\n\ntype Engine struct {\n}\ntype Rect struct {\n}\n\nfunc (e *Engine) EnterPointerLock() {\n}\nfunc (e *Engine) SetEnable(b bool) {\n}\nfunc (this *Rect) Engine() *Engine {\n\treturn &Engine{}\n}\nfunc (this *Rect) Test() {\n\tthis.Engine().SetEnable(true)\n\tthis.Engine().EnterPointerLock()\n}\n`, \"Rect.gox\")\n}\n"
  },
  {
    "path": "cl/compile_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl_test\n\nimport (\n\t\"os\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cl/cltest\"\n)\n\nconst (\n\tgopRootDir = \"..\"\n)\n\nvar (\n\tgblConfLine *cl.Config\n)\n\nfunc init() {\n\tcltest.XGo.Root = gopRootDir\n\tconf := cltest.Conf\n\tgblConfLine = &cl.Config{\n\t\tFset:          conf.Fset,\n\t\tImporter:      conf.Importer,\n\t\tRecorder:      cltest.Conf.Recorder,\n\t\tLookupClass:   cltest.LookupClass,\n\t\tNoFileLine:    false,\n\t\tNoAutoGenMain: true,\n\t}\n}\n\nfunc gopClNamedTest(t *testing.T, name string, gopcode, expected string) {\n\tcltest.Named(t, name, gopcode, expected)\n}\n\nfunc gopClTest(t *testing.T, gopcode, expected string) {\n\tcltest.DoExt(t, cltest.Conf, \"main\", gopcode, expected)\n}\n\nfunc gopClTestFile(t *testing.T, gopcode, expected string, fname string) {\n\tcltest.DoWithFname(t, gopcode, expected, fname)\n}\n\nfunc gopClTestEx(t *testing.T, conf *cl.Config, pkgname, gopcode, expected string) {\n\tcltest.DoExt(t, conf, pkgname, gopcode, expected)\n}\n\nfunc gopMixedClTest(t *testing.T, pkgname, gocode, gopcode, expected string, outline ...bool) {\n\tcltest.Mixed(t, pkgname, gocode, gopcode, expected, outline...)\n}\n\nfunc TestTypeDoc(t *testing.T) {\n\tgopClTest(t, `\ntype (\n\t// doc\n\tA int\n)\n`, `package main\n// doc\ntype A int\n`)\n}\n\nfunc TestUnsafe(t *testing.T) {\n\tgopClTest(t, `\nimport \"unsafe\"\n\nprintln unsafe.Sizeof(0)\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"unsafe\"\n)\n\nfunc main() {\n\tfmt.Println(unsafe.Sizeof(0))\n}\n`)\n}\n\nfunc Test_CastSlice_Issue1240(t *testing.T) {\n\tgopClTest(t, `\ntype fvec []float64\ntype foo float64\na := []float64([1, 2])\nb := fvec([1, 2])\nc := foo([1, 2])\nd := fvec([])\nprintln a, b, c, d\n`, `package main\n\nimport \"fmt\"\n\ntype fvec []float64\ntype foo float64\n\nfunc main() {\n\ta := []float64{1, 2}\n\tb := fvec{1, 2}\n\tc := foo([]int{1, 2})\n\td := fvec{}\n\tfmt.Println(a, b, c, d)\n}\n`)\n}\n\nfunc TestUnderscoreRedeclared_Issue1197(t *testing.T) {\n\tgopClTest(t, `\nfunc() (_ [2]int) { type _ int; return }()\n`, `package main\n\nfunc main() {\n\tfunc() (_ [2]int) {\n\t\treturn\n\t}()\n}\n`)\n}\n\nfunc TestInterfaceBugNilUnderlying_Issue1198(t *testing.T) {\n\tgopClTest(t, `\nimport \"runtime\"\n\ntype Outer interface{ Inner }\n\ntype impl struct{}\n\nfunc New() Outer { return &impl{} }\n\ntype Inner interface {\n\tDoStuff() error\n}\n\nfunc (a *impl) DoStuff() error {\n\treturn nil\n}\n\nfunc main() {\n\tvar outer Outer = New()\n}\n`, `package main\n\ntype Outer interface {\n\tInner\n}\ntype impl struct {\n}\ntype Inner interface {\n\tDoStuff() error\n}\n\nfunc (a *impl) DoStuff() error {\n\treturn nil\n}\nfunc New() Outer {\n\treturn &impl{}\n}\nfunc main() {\n\tvar outer Outer = New()\n}\n`)\n}\n\nfunc TestInterfaceBugNilUnderlying_Issue1196(t *testing.T) {\n\tgopClTest(t, `\nfunc main() {\n\ti := I(A{})\n\n\tb := make(chan I, 1)\n\tb <- B{}\n\n\tvar ok bool\n\ti, ok = <-b\n}\n\ntype I interface{ M() int }\n\ntype T int\n\nfunc (T) M() int { return 0 }\n\ntype A struct{ T }\ntype B struct{ T }\n`, `package main\n\ntype I interface {\n\tM() int\n}\ntype T int\ntype A struct {\n\tT\n}\ntype B struct {\n\tT\n}\n\nfunc main() {\n\ti := I(A{})\n\tb := make(chan I, 1)\n\tb <- B{}\n\tvar ok bool\n\ti, ok = <-b\n}\nfunc (T) M() int {\n\treturn 0\n}\n`)\n}\n\nfunc TestMyIntInc_Issue1195(t *testing.T) {\n\tgopClTest(t, `\ntype MyInt int\nvar c MyInt\nc++\n`, `package main\n\ntype MyInt int\n\nvar c MyInt\n\nfunc main() {\n\tc++\n}\n`)\n}\n\nfunc TestAutoPropMixedName_Issue1194(t *testing.T) {\n\tgopClTest(t, `\ntype Point struct {\n\tMin, Max int\n}\n\ntype Obj struct {\n\tbbox Point\n}\n\nfunc (o *Obj) Bbox() Point {\n\treturn o.bbox\n}\n\nfunc (o *Obj) Points() [2]int{\n\treturn [2]int{o.bbox.Min, o.bbox.Max}\n}\n`, `package main\n\ntype Point struct {\n\tMin int\n\tMax int\n}\ntype Obj struct {\n\tbbox Point\n}\n\nfunc (o *Obj) Bbox() Point {\n\treturn o.bbox\n}\nfunc (o *Obj) Points() [2]int {\n\treturn [2]int{o.bbox.Min, o.bbox.Max}\n}\n`)\n}\n\nfunc TestShiftUntypedInt_Issue1193(t *testing.T) {\n\tgopClTest(t, `\nfunc GetValue(shift uint) uint {\n\treturn 1 << shift\n}`, `package main\n\nfunc GetValue(shift uint) uint {\n\treturn 1 << shift\n}\n`)\n}\n\nfunc TestInitFunc(t *testing.T) {\n\tgopClTest(t, `\n\nfunc init() {}\nfunc init() {}\n`, `package main\n\nfunc init() {\n}\nfunc init() {\n}\n`)\n}\n\nfunc TestSlogan(t *testing.T) {\n\tgopClTest(t, `\nfields := [\"engineering\", \"STEM education\", \"data science\"]\nprintln \"The XGo Language for\", fields.join(\", \")\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tfields := []string{\"engineering\", \"STEM education\", \"data science\"}\n\tfmt.Println(\"The XGo Language for\", strings.Join(fields, \", \"))\n}\n`)\n}\n\nfunc TestAssignPrintln(t *testing.T) {\n\tgopClTest(t, `\np := println\np \"Hello world\"\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tp := fmt.Println\n\tp(\"Hello world\")\n}\n`)\n}\n\nfunc TestRedefineBuiltin(t *testing.T) {\n\tgopClTest(t, `\nfunc main() {\n\tconst a = append + len\n}\n\nconst (\n\tappend = iota\n\tlen\n)\n`, `package main\n\nconst (\n\tappend = iota\n\tlen\n)\n\nfunc main() {\n\tconst a = append + len\n}\n`)\n}\n\nfunc TestTypeConvIssue804(t *testing.T) {\n\tgopClTest(t, `\nc := make(chan int)\nd := (chan<- int)(c)\ne := (<-chan int)(c)\nf := (*int)(nil)\na := c == d\nb := c == e\n`, `package main\n\nfunc main() {\n\tc := make(chan int)\n\td := (chan<- int)(c)\n\te := (<-chan int)(c)\n\tf := (*int)(nil)\n\ta := c == d\n\tb := c == e\n}\n`)\n}\n\nfunc TestUntypedFloatIssue798(t *testing.T) {\n\tgopClTest(t, `\nfunc isPow10(x uint64) bool {\n\tswitch x {\n\tcase 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,\n\t\t1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19:\n\t\treturn true\n\t}\n\treturn false\n}\n`, `package main\n\nfunc isPow10(x uint64) bool {\n\tswitch x {\n\tcase 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19:\n\t\treturn true\n\t}\n\treturn false\n}\n`)\n}\n\nfunc TestInterfaceIssue795(t *testing.T) {\n\tgopClTest(t, `\ntype I interface {\n\ta(s string) I\n\tb(s string) string\n}\n\ntype T1 int\n\nfunc (t T1) a(s string) I {\n\treturn t\n}\n\nfunc (T1) b(s string) string {\n\treturn s\n}\n`, `package main\n\ntype I interface {\n\ta(s string) I\n\tb(s string) string\n}\ntype T1 int\n\nfunc (t T1) a(s string) I {\n\treturn t\n}\nfunc (T1) b(s string) string {\n\treturn s\n}\n`)\n}\n\nfunc TestChanRecvIssue789(t *testing.T) {\n\tgopClTest(t, `\nfunc foo(ch chan int) (int, bool) {\n\tx, ok := (<-ch)\n\treturn x, ok\n}\n`, `package main\n\nfunc foo(ch chan int) (int, bool) {\n\tx, ok := <-ch\n\treturn x, ok\n}\n`)\n}\n\nfunc TestNamedChanCloseIssue790(t *testing.T) {\n\tgopClTest(t, `\ntype XChan chan int\n\nfunc foo(ch XChan) {\n\tclose(ch)\n}\n`, `package main\n\ntype XChan chan int\n\nfunc foo(ch XChan) {\n\tclose(ch)\n}\n`)\n}\n\nfunc TestUntypedFloatIssue793(t *testing.T) {\n\tgopClTest(t, `\nvar a [1e1]int\n`, `package main\n\nvar a [10]int\n`)\n}\n\nfunc TestUntypedFloatIssue788(t *testing.T) {\n\tgopClTest(t, `\nfunc foo(v int) bool {\n    return v > 1.1e5\n}\n`, `package main\n\nfunc foo(v int) bool {\n\treturn v > 1.1e5\n}\n`)\n}\n\nfunc TestSwitchCompositeLitIssue801(t *testing.T) {\n\tgopClTest(t, `\ntype T struct {\n\tX int\n}\n\nswitch (T{}) {\ncase T{1}:\n\tpanic(\"bad\")\n}\n`, `package main\n\ntype T struct {\n\tX int\n}\n\nfunc main() {\n\tswitch (T{}) {\n\tcase T{1}:\n\t\tpanic(\"bad\")\n\t}\n}\n`)\n}\n\nfunc TestConstIssue800(t *testing.T) {\n\tgopClTest(t, `\nconst (\n\th0_0, h0_1 = 1.0 / (iota + 1), 1.0 / (iota + 2)\n\th1_0, h1_1\n)\n`, `package main\n\nconst (\n\th0_0, h0_1 = 1.0 / (iota + 1), 1.0 / (iota + 2)\n\th1_0, h1_1\n)\n`)\n}\n\nfunc TestConstIssue805(t *testing.T) {\n\tgopClTest(t, `\nconst (\n\tn1 = +5\n\td1 = +3\n\n\tq1 = +1\n\tr1 = +2\n)\n\nconst (\n\tret1 = n1/d1 != q1\n\tret2 = n1%d1 != r1\n\tret3 = n1/d1 != q1 || n1%d1 != r1\n)\n`, `package main\n\nconst (\n\tn1 = +5\n\td1 = +3\n\tq1 = +1\n\tr1 = +2\n)\nconst (\n\tret1 = n1/d1 != q1\n\tret2 = n1%d1 != r1\n\tret3 = false\n)\n`)\n}\n\nfunc TestUntypedNilIssue806(t *testing.T) {\n\tgopClTest(t, `\nswitch f := func() {}; f {\ncase nil:\n}\n`, `package main\n\nfunc main() {\n\tswitch f := func() {\n\t}; f {\n\tcase nil:\n\t}\n}\n`)\n}\n\nfunc TestSwitchIssue807(t *testing.T) {\n\tgopClTest(t, `\nswitch {\ncase interface{}(true):\n}\n`, `package main\n\nfunc main() {\n\tswitch {\n\tcase interface{}(true):\n\t}\n}\n`)\n}\n\nfunc TestUntypedComplexIssue799(t *testing.T) {\n\tgopClTest(t, `\nconst ulp1 = imag(1i + 2i / 3 - 5i / 3)\nconst ulp2 = imag(1i + complex(0, 2) / 3 - 5i / 3)\n\nfunc main() {\n\tconst a = (ulp1 == ulp2)\n}\n`, `package main\n\nconst ulp1 = imag(1i + 2i/3 - 5i/3)\nconst ulp2 = imag(1i + complex(0, 2)/3 - 5i/3)\n\nfunc main() {\n\tconst a = ulp1 == ulp2\n}\n`)\n}\n\nfunc TestUnderscoreConstAndVar(t *testing.T) {\n\tgopClTest(t, `\nconst (\n\tc0 = 1 << iota\n\t_\n\t_\n\t_\n\tc4\n)\n\nfunc i() int {\n\treturn 23\n}\n\nvar (\n\t_ = i()\n\t_ = i()\n)\n`, `package main\n\nconst (\n\tc0 = 1 << iota\n\t_\n\t_\n\t_\n\tc4\n)\n\nfunc i() int {\n\treturn 23\n}\n\nvar _ = i()\nvar _ = i()\n`)\n}\n\nfunc TestUnderscoreFuncAndMethod(t *testing.T) {\n\tgopClTest(t, `\nfunc _() {\n}\n\ntype T struct {\n\t_, _, _ int\n}\n\nfunc (T) _() {\n}\n\nfunc (T) _() {\n}\n`, `package main\n\ntype T struct {\n\t_ int\n\t_ int\n\t_ int\n}\n\nfunc (T) _() {\n}\nfunc (T) _() {\n}\nfunc _() {\n}\n`)\n}\n\nfunc TestErrWrapIssue772(t *testing.T) {\n\tgopClTest(t, `\npackage main\n\nfunc t() (int,int,error){\n\treturn 0, 0, nil\n}\n\nfunc main() {\n\ta, b := t()!\n\tprintln(a, b)\n}`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc t() (int, int, error) {\n\treturn 0, 0, nil\n}\nfunc main() {\n\ta, b := func() (_xgo_ret int, _xgo_ret2 int) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_ret2, _xgo_err = t()\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"t()\", \"/foo/bar.xgo\", 9, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n\tfmt.Println(a, b)\n}\n`)\n}\n\nfunc TestErrWrapIssue778(t *testing.T) {\n\tgopClTest(t, `\npackage main\n\nfunc t() error {\n\treturn nil\n}\n\nfunc main() {\n\tt()!\n}`, `package main\n\nimport \"github.com/qiniu/x/errors\"\n\nfunc t() error {\n\treturn nil\n}\nfunc main() {\n\tfunc() {\n\t\tvar _xgo_err error\n\t\t_xgo_err = t()\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"t()\", \"/foo/bar.xgo\", 9, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n}\n`)\n}\n\nfunc TestIssue774(t *testing.T) {\n\tgopClNamedTest(t, \"InterfaceTypeAssert\", `\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar a AA = &A{str: \"hello\"}\n\tfmt.Println(a.(*A))\n}\n\ntype AA interface {\n\tString() string\n}\n\ntype A struct {\n\tstr string\n}\n\nfunc (a *A) String() string {\n\treturn a.str\n}\n`, `package main\n\nimport \"fmt\"\n\ntype AA interface {\n\tString() string\n}\ntype A struct {\n\tstr string\n}\n\nfunc main() {\n\tvar a AA = &A{str: \"hello\"}\n\tfmt.Println(a.(*A))\n}\nfunc (a *A) String() string {\n\treturn a.str\n}\n`)\n\tgopClNamedTest(t, \"getInterface\", `\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := get()\n\tfmt.Println(a.(*A))\n}\n\ntype AA interface {\n\tString() string\n}\n\nfunc get() AA {\n\tvar a AA\n\treturn a\n}\n\ntype A struct {\n\tstr string\n}\n\nfunc (a *A) String() string {\n\treturn a.str\n}\n`, `package main\n\nimport \"fmt\"\n\ntype AA interface {\n\tString() string\n}\ntype A struct {\n\tstr string\n}\n\nfunc main() {\n\ta := get()\n\tfmt.Println(a.(*A))\n}\nfunc get() AA {\n\tvar a AA\n\treturn a\n}\nfunc (a *A) String() string {\n\treturn a.str\n}\n`)\n}\n\nfunc TestBlockStmt(t *testing.T) {\n\tgopClTest(t, `\npackage main\n\nfunc main() {\n\t{\n\t\ttype T int\n\t\tt := T(100)\n\t\tprintln(t)\n\t}\n\t{\n\t\ttype T string\n\t\tt := \"hello\"\n\t\tprintln(t)\n\t}\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\t{\n\t\ttype T int\n\t\tt := T(100)\n\t\tfmt.Println(t)\n\t}\n\t{\n\t\ttype T string\n\t\tt := \"hello\"\n\t\tfmt.Println(t)\n\t}\n}\n`)\n}\n\nfunc TestConstTypeConvIssue792(t *testing.T) {\n\tgopClTest(t, `\nconst dots = \". . . \" + \". . . . . \"\nconst n = uint(len(dots))\n`, `package main\n\nconst dots = \". . . \" + \". . . . . \"\nconst n = uint(len(dots))\n`)\n}\n\nfunc TestVarInitTwoValueIssue791(t *testing.T) {\n\tgopClTest(t, `\nvar (\n\tm      = map[string]string{\"a\": \"A\"}\n\ta, ok  = m[\"a\"]\n)\n`, `package main\n\nvar m = map[string]string{\"a\": \"A\"}\nvar a, ok = m[\"a\"]\n`)\n}\n\nfunc TestVarAfterMain(t *testing.T) {\n\tgopClTest(t, `\npackage main\n\nfunc main() {\n\tprintln(i)\n}\n\nvar i int\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(i)\n}\n\nvar i int\n`)\n\tgopClTest(t, `\npackage main\n\nfunc f(v float64) float64 {\n\treturn v\n}\nfunc main() {\n\tsink = f(100)\n}\n\nvar sink float64\n`, `package main\n\nfunc f(v float64) float64 {\n\treturn v\n}\nfunc main() {\n\tsink = f(100)\n}\n\nvar sink float64\n`)\n}\n\nfunc TestVarAfterMain2(t *testing.T) {\n\tgopClTest(t, `\npackage main\n\nfunc main() {\n\tprintln(i)\n}\n\nvar i = 100\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(i)\n}\n\nvar i = 100\n`)\n}\n\nfunc TestVarInMain(t *testing.T) {\n\tgopClTest(t, `\npackage main\n\nfunc main() {\n\tv := []uint64{2, 3, 5}\n\tvar n = len(v)\n\tprintln(n)\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tv := []uint64{2, 3, 5}\n\tvar n = len(v)\n\tfmt.Println(n)\n}\n`)\n}\n\nfunc TestSelect(t *testing.T) {\n\tgopClTest(t, `\n\nfunc consume(xchg chan int) {\n\tselect {\n\tcase c := <-xchg:\n\t\tprintln(c)\n\tcase xchg <- 1:\n\t\tprintln(\"send ok\")\n\tdefault:\n\t\tprintln(0)\n\t}\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc consume(xchg chan int) {\n\tselect {\n\tcase c := <-xchg:\n\t\tfmt.Println(c)\n\tcase xchg <- 1:\n\t\tfmt.Println(\"send ok\")\n\tdefault:\n\t\tfmt.Println(0)\n\t}\n}\n`)\n}\n\nfunc TestTypeSwitch(t *testing.T) {\n\tgopClTest(t, `\n\nfunc bar(p *interface{}) {\n}\n\nfunc foo(v interface{}) {\n\tswitch t := v.(type) {\n\tcase int, string:\n\t\tbar(&v)\n\tcase bool:\n\t\tvar x bool = t\n\tdefault:\n\t\tbar(nil)\n\t}\n}\n`, `package main\n\nfunc bar(p *interface{}) {\n}\nfunc foo(v interface{}) {\n\tswitch t := v.(type) {\n\tcase int, string:\n\t\tbar(&v)\n\tcase bool:\n\t\tvar x bool = t\n\tdefault:\n\t\tbar(nil)\n\t}\n}\n`)\n}\n\nfunc TestTypeSwitch2(t *testing.T) {\n\tgopClTest(t, `\n\nfunc bar(p *interface{}) {\n}\n\nfunc foo(v interface{}) {\n\tswitch bar(nil); v.(type) {\n\tcase int, string:\n\t\tbar(&v)\n\t}\n}\n`, `package main\n\nfunc bar(p *interface{}) {\n}\nfunc foo(v interface{}) {\n\tswitch bar(nil); v.(type) {\n\tcase int, string:\n\t\tbar(&v)\n\t}\n}\n`)\n}\n\nfunc TestTypeAssert(t *testing.T) {\n\tgopClTest(t, `\n\nfunc foo(v interface{}) {\n\tx := v.(int)\n\ty, ok := v.(string)\n}\n`, `package main\n\nfunc foo(v interface{}) {\n\tx := v.(int)\n\ty, ok := v.(string)\n}\n`)\n}\n\nfunc TestInterface(t *testing.T) {\n\tgopClTest(t, `\n\ntype Shape interface {\n\tArea() float64\n}\n\nfunc foo(shape Shape) {\n\tshape.Area()\n}\n`, `package main\n\ntype Shape interface {\n\tArea() float64\n}\n\nfunc foo(shape Shape) {\n\tshape.Area()\n}\n`)\n}\n\nfunc TestInterfaceEmbedded(t *testing.T) {\n\tgopClTest(t, `\ntype Shape interface {\n\tArea() float64\n}\n\ntype Bar interface {\n\tShape\n}\n`, `package main\n\ntype Shape interface {\n\tArea() float64\n}\ntype Bar interface {\n\tShape\n}\n`)\n}\n\nfunc TestInterfaceExample(t *testing.T) {\n\tgopClTest(t, `\ntype Shape interface {\n\tArea() float64\n}\n\ntype Rect struct {\n\tx, y, w, h float64\n}\n\nfunc (p *Rect) Area() float64 {\n\treturn p.w * p.h\n}\n\ntype Circle struct {\n\tx, y, r float64\n}\n\nfunc (p *Circle) Area() float64 {\n\treturn 3.14 * p.r * p.r\n}\n\nfunc Area(shapes ...Shape) float64 {\n\ts := 0.0\n\tfor shape <- shapes {\n\t\ts += shape.Area()\n\t}\n\treturn s\n}\n\nfunc main() {\n\trect := &Rect{0, 0, 2, 5}\n\tcircle := &Circle{0, 0, 3}\n\tprintln(Area(circle, rect))\n}\n`, `package main\n\nimport \"fmt\"\n\ntype Shape interface {\n\tArea() float64\n}\ntype Rect struct {\n\tx float64\n\ty float64\n\tw float64\n\th float64\n}\ntype Circle struct {\n\tx float64\n\ty float64\n\tr float64\n}\n\nfunc (p *Rect) Area() float64 {\n\treturn p.w * p.h\n}\nfunc (p *Circle) Area() float64 {\n\treturn 3.14 * p.r * p.r\n}\nfunc Area(shapes ...Shape) float64 {\n\ts := 0.0\n\tfor _, shape := range shapes {\n\t\ts += shape.Area()\n\t}\n\treturn s\n}\nfunc main() {\n\trect := &Rect{0, 0, 2, 5}\n\tcircle := &Circle{0, 0, 3}\n\tfmt.Println(Area(circle, rect))\n}\n`)\n}\n\nfunc TestEmbeddField(t *testing.T) {\n\tgopClTest(t, `import \"math/big\"\n\ntype BigInt struct {\n\t*big.Int\n}`, `package main\n\nimport \"math/big\"\n\ntype BigInt struct {\n\t*big.Int\n}\n`)\n}\n\nfunc TestAutoProperty(t *testing.T) {\n\tgopClTest(t, `import \"github.com/goplus/xgo/ast/goptest\"\n\nfunc foo(script string) {\n\tdoc := goptest.New(script)!\n\n\techo doc.any.funcDecl.name\n\techo doc.any.importSpec.name\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/ast/gopq\"\n\t\"github.com/goplus/xgo/ast/goptest\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc foo(script string) {\n\tdoc := func() (_xgo_ret gopq.NodeSet) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = goptest.New(script)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"goptest.New(script)\", \"/foo/bar.xgo\", 4, \"main.foo\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n\tfmt.Println(doc.Any().FuncDecl__0().Name())\n\tfmt.Println(doc.Any().ImportSpec().Name())\n}\n`)\n}\n\nfunc TestSimplifyAutoProperty(t *testing.T) {\n\tgopClTest(t, `import \"gop/ast/goptest\"\n\nfunc foo(script string) {\n\tdoc := goptest.New(script)!\n\n\tprintln(doc.any.funcDecl.name)\n\tprintln(doc.any.importSpec.name)\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/ast/gopq\"\n\t\"github.com/goplus/xgo/ast/goptest\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc foo(script string) {\n\tdoc := func() (_xgo_ret gopq.NodeSet) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = goptest.New(script)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"goptest.New(script)\", \"/foo/bar.xgo\", 4, \"main.foo\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n\tfmt.Println(doc.Any().FuncDecl__0().Name())\n\tfmt.Println(doc.Any().ImportSpec().Name())\n}\n`)\n}\n\nfunc TestErrWrapBasic(t *testing.T) {\n\tgopClTest(t, `\nimport \"strconv\"\n\nfunc add(x, y string) (int, error) {\n\treturn strconv.Atoi(x)? + strconv.Atoi(y)?, nil\n}\n`, `package main\n\nimport (\n\t\"github.com/qiniu/x/errors\"\n\t\"strconv\"\n)\n\nfunc add(x string, y string) (int, error) {\n\tvar _autoGo_1 int\n\t{\n\t\tvar _xgo_err error\n\t\t_autoGo_1, _xgo_err = strconv.Atoi(x)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"strconv.Atoi(x)\", \"/foo/bar.xgo\", 5, \"main.add\")\n\t\t\treturn 0, _xgo_err\n\t\t}\n\t\tgoto _autoGo_2\n\t_autoGo_2:\n\t}\n\tvar _autoGo_3 int\n\t{\n\t\tvar _xgo_err error\n\t\t_autoGo_3, _xgo_err = strconv.Atoi(y)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"strconv.Atoi(y)\", \"/foo/bar.xgo\", 5, \"main.add\")\n\t\t\treturn 0, _xgo_err\n\t\t}\n\t\tgoto _autoGo_4\n\t_autoGo_4:\n\t}\n\treturn _autoGo_1 + _autoGo_3, nil\n}\n`)\n}\n\nfunc TestErrWrapDefVal(t *testing.T) {\n\tgopClTest(t, `\nimport \"strconv\"\n\nfunc addSafe(x, y string) int {\n\treturn strconv.Atoi(x)?:0 + strconv.Atoi(y)?:0\n}\n`, `package main\n\nimport \"strconv\"\n\nfunc addSafe(x string, y string) int {\n\treturn func() (_xgo_ret int) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = strconv.Atoi(x)\n\t\tif _xgo_err != nil {\n\t\t\treturn 0\n\t\t}\n\t\treturn\n\t}() + func() (_xgo_ret int) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = strconv.Atoi(y)\n\t\tif _xgo_err != nil {\n\t\t\treturn 0\n\t\t}\n\t\treturn\n\t}()\n}\n`)\n}\n\nfunc TestErrWrapPanic(t *testing.T) {\n\tgopClTest(t, `\nvar ret int = println(\"Hi\")!\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nvar ret int = func() (_xgo_ret int) {\n\tvar _xgo_err error\n\t_xgo_ret, _xgo_err = fmt.Println(\"Hi\")\n\tif _xgo_err != nil {\n\t\t_xgo_err = errors.NewFrame(_xgo_err, \"println(\\\"Hi\\\")\", \"/foo/bar.xgo\", 2, \"main.main\")\n\t\tpanic(_xgo_err)\n\t}\n\treturn\n}()\n`)\n}\n\nfunc TestErrWrapCommand(t *testing.T) {\n\tgopClTest(t, `\nfunc mkdir(name string) error {\n\treturn nil\n}\n\nmkdir! \"foo\"\n`, `package main\n\nimport \"github.com/qiniu/x/errors\"\n\nfunc mkdir(name string) error {\n\treturn nil\n}\nfunc main() {\n\tfunc() {\n\t\tvar _xgo_err error\n\t\t_xgo_err = mkdir(\"foo\")\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"mkdir \\\"foo\\\"\", \"/foo/bar.xgo\", 6, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()\n}\n`)\n}\n\nfunc TestErrWrapCall(t *testing.T) {\n\tgopClTest(t, `\nfunc foo() (func(), error) {\n\treturn nil, nil\n}\n\nfoo()!()\n`, `package main\n\nimport \"github.com/qiniu/x/errors\"\n\nfunc foo() (func(), error) {\n\treturn nil, nil\n}\nfunc main() {\n\tfunc() (_xgo_ret func()) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = foo()\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"foo()\", \"/foo/bar.xgo\", 6, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()()\n}\n`)\n}\n\nfunc TestMakeAndNew(t *testing.T) {\n\tgopClTest(t, `\nvar a *int = new(int)\nvar b map[string]int = make(map[string]int)\nvar c []byte = make([]byte, 0, 2)\n`, `package main\n\nvar a *int = new(int)\nvar b map[string]int = make(map[string]int)\nvar c []byte = make([]byte, 0, 2)\n`)\n}\n\nfunc TestVarDecl(t *testing.T) {\n\tgopClTest(t, `\nvar a int\nvar x, y = 1, \"Hi\"\n`, `package main\n\nvar a int\nvar x, y = 1, \"Hi\"\n`)\n}\n\nfunc TestUint128Add(t *testing.T) {\n\tgopClTest(t, `\nvar x, y uint128\nvar z uint128 = x + y\n`, `package main\n\nimport \"github.com/qiniu/x/xgo/ng\"\n\nvar x, y ng.Uint128\nvar z ng.Uint128 = (ng.Uint128).XGo_Add__1(x, y)\n`)\n}\n\nfunc TestInt128Add(t *testing.T) {\n\tgopClTest(t, `\nvar x, y int128\nvar z int128 = x + y\n`, `package main\n\nimport \"github.com/qiniu/x/xgo/ng\"\n\nvar x, y ng.Int128\nvar z ng.Int128 = (ng.Int128).XGo_Add__1(x, y)\n`)\n}\n\nfunc TestBigIntAdd(t *testing.T) {\n\tgopClTest(t, `\nvar x, y bigint\nvar z bigint = x + y\n`, `package main\n\nimport \"github.com/qiniu/x/xgo/ng\"\n\nvar x, y ng.Bigint\nvar z ng.Bigint = (ng.Bigint).XGo_Add(x, y)\n`)\n}\n\nfunc TestBigIntLit(t *testing.T) {\n\tgopClTest(t, `\nvar x = 1r\n`, `package main\n\nimport (\n\t\"github.com/qiniu/x/xgo/ng\"\n\t\"math/big\"\n)\n\nvar x = ng.Bigint_Init__1(big.NewInt(1))\n`)\n}\n\nfunc TestUint128Lit(t *testing.T) {\n\tgopClTest(t, `\nvar x uint128 = 1\n`, `package main\n\nimport \"github.com/qiniu/x/xgo/ng\"\n\nvar x ng.Uint128 = ng.Uint128_Init__0(1)\n`)\n}\n\nfunc TestInt128Lit(t *testing.T) {\n\tgopClTest(t, `\nvar x int128 = 1\n`, `package main\n\nimport \"github.com/qiniu/x/xgo/ng\"\n\nvar x ng.Int128 = ng.Int128_Init__0(1)\n`)\n}\n\nfunc TestBigRatLit(t *testing.T) {\n\tgopClTest(t, `\nvar x = 1/2r\n`, `package main\n\nimport (\n\t\"github.com/qiniu/x/xgo/ng\"\n\t\"math/big\"\n)\n\nvar x = ng.Bigrat_Init__2(big.NewRat(1, 2))\n`)\n}\n\nfunc TestBigRatLitAdd(t *testing.T) {\n\tgopClTest(t, `\nvar x = 3 + 1/2r\n`, `package main\n\nimport (\n\t\"github.com/qiniu/x/xgo/ng\"\n\t\"math/big\"\n)\n\nvar x = ng.Bigrat_Init__2(big.NewRat(7, 2))\n`)\n}\n\nfunc TestBigRatAdd(t *testing.T) {\n\tgogen.SetDebug(gogen.DbgFlagAll)\n\tgopClTest(t, `\nvar x = 3 + 1/2r\nvar y = x + 100\nvar z = 100 + y\n`, `package main\n\nimport (\n\t\"github.com/qiniu/x/xgo/ng\"\n\t\"math/big\"\n)\n\nvar x = ng.Bigrat_Init__2(big.NewRat(7, 2))\nvar y = (ng.Bigrat).XGo_Add(x, ng.Bigrat_Init__0(100))\nvar z = (ng.Bigrat).XGo_Add(ng.Bigrat_Init__0(100), y)\n`)\n}\n\nfunc TestTypeConv(t *testing.T) {\n\tgopClTest(t, `\nvar a = (*struct{})(nil)\nvar b = interface{}(nil)\nvar c = (func())(nil)\nvar x uint32 = uint32(0)\nvar y *uint32 = (*uint32)(nil)\n`, `package main\n\nvar a = (*struct {\n})(nil)\nvar b = interface{}(nil)\nvar c = (func())(nil)\nvar x uint32 = uint32(0)\nvar y *uint32 = (*uint32)(nil)\n`)\n}\n\nfunc TestStar(t *testing.T) {\n\tgopClTest(t, `\nvar x *uint32 = (*uint32)(nil)\nvar y uint32 = *x\n`, `package main\n\nvar x *uint32 = (*uint32)(nil)\nvar y uint32 = *x\n`)\n}\n\nfunc TestLHS(t *testing.T) {\n\tgopClTest(t, `\ntype T struct {\n\ta int\n}\n\nfunc foo() *T {\n\treturn nil\n}\n\nfoo().a = 123\n`, `package main\n\ntype T struct {\n\ta int\n}\n\nfunc foo() *T {\n\treturn nil\n}\nfunc main() {\n\tfoo().a = 123\n}\n`)\n}\n\nfunc TestSend(t *testing.T) {\n\tgopClTest(t, `\nvar x chan bool\nx <- true\n`, `package main\n\nvar x chan bool\n\nfunc main() {\n\tx <- true\n}\n`)\n}\n\nfunc TestIncDec(t *testing.T) {\n\tgopClTest(t, `\nvar x uint32\nx++\n`, `package main\n\nvar x uint32\n\nfunc main() {\n\tx++\n}\n`)\n}\n\nfunc TestAssignOp(t *testing.T) {\n\tgopClTest(t, `\nvar x uint32\nx += 3\n`, `package main\n\nvar x uint32\n\nfunc main() {\n\tx += 3\n}\n`)\n}\n\nfunc TestBigIntAssignOp(t *testing.T) {\n\tgopClTest(t, `\nvar x bigint\nx += 3\n`, `package main\n\nimport \"github.com/qiniu/x/xgo/ng\"\n\nvar x ng.Bigint\n\nfunc main() {\n\tx.XGo_AddAssign(ng.Bigint_Init__0(3))\n}\n`)\n}\n\nfunc TestBigIntAssignOp2(t *testing.T) {\n\tgopClTest(t, `\nx := 3r\nx *= 2\n`, `package main\n\nimport (\n\t\"github.com/qiniu/x/xgo/ng\"\n\t\"math/big\"\n)\n\nfunc main() {\n\tx := ng.Bigint_Init__1(big.NewInt(3))\n\tx.XGo_MulAssign(ng.Bigint_Init__0(2))\n}\n`)\n}\n\nfunc TestBigIntAssignOp3(t *testing.T) {\n\tgopClTest(t, `\nx := 3r\nx *= 2r\n`, `package main\n\nimport (\n\t\"github.com/qiniu/x/xgo/ng\"\n\t\"math/big\"\n)\n\nfunc main() {\n\tx := ng.Bigint_Init__1(big.NewInt(3))\n\tx.XGo_MulAssign(ng.Bigint_Init__1(big.NewInt(2)))\n}\n`)\n}\n\nfunc TestCompositeLit(t *testing.T) {\n\tgopClTest(t, `\nx := []float64{1, 3.4, 5}\ny := map[string]int{\"Hello\": 1, \"XGo\": 5}\nz := [...]int{1, 3, 5}\na := {\"Hello\": 1, \"XGo\": 5.1}\n`, `package main\n\nfunc main() {\n\tx := []float64{1, 3.4, 5}\n\ty := map[string]int{\"Hello\": 1, \"XGo\": 5}\n\tz := [...]int{1, 3, 5}\n\ta := map[string]float64{\"Hello\": 1, \"XGo\": 5.1}\n}\n`)\n}\n\nfunc TestCompositeLit2(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n\tA int\n}\n\nx := []*struct{a int}{\n\t{1}, {3}, {5},\n}\ny := map[foo]struct{a string}{\n\t{1}: {\"Hi\"},\n}\nz := [...]foo{\n\t{1}, {3}, {5},\n}\n`, `package main\n\ntype foo struct {\n\tA int\n}\n\nfunc main() {\n\tx := []*struct {\n\t\ta int\n\t}{&struct {\n\t\ta int\n\t}{1}, &struct {\n\t\ta int\n\t}{3}, &struct {\n\t\ta int\n\t}{5}}\n\ty := map[foo]struct {\n\t\ta string\n\t}{foo{1}: struct {\n\t\ta string\n\t}{\"Hi\"}}\n\tz := [...]foo{foo{1}, foo{3}, foo{5}}\n}\n`)\n}\n\n// deduce struct type as parameters of a function call\nfunc TestCompositeLit3(t *testing.T) {\n\tgopClTest(t, `\ntype Config struct {\n\tA int\n}\n\nfunc foo(conf *Config) {\n}\n\nfunc bar(conf ...Config) {\n}\n\nfoo({A: 1})\nbar({A: 2})\nfoo({})\nbar({})\n`, `package main\n\ntype Config struct {\n\tA int\n}\n\nfunc foo(conf *Config) {\n}\nfunc bar(conf ...Config) {\n}\nfunc main() {\n\tfoo(&Config{A: 1})\n\tbar(Config{A: 2})\n\tfoo(&Config{})\n\tbar(Config{})\n}\n`)\n}\n\n// deduce struct type as results of a function call\nfunc TestCompositeLit4(t *testing.T) {\n\tgopClTest(t, `\ntype Result struct {\n\tA int\n}\n\nfunc foo() *Result {\n\treturn {A: 1}\n}\n`, `package main\n\ntype Result struct {\n\tA int\n}\n\nfunc foo() *Result {\n\treturn &Result{A: 1}\n}\n`)\n}\n\nfunc TestCompositeLit5(t *testing.T) {\n\tgopClTest(t, `\ntype mymap map[float64]string\nvar x = {1:\"hello\", 2:\"world\"}\nvar y map[float64]string = {1:\"hello\", 2:\"world\"}\nvar z mymap = {1:\"hello\", 2:\"world\"}\n`, `package main\n\ntype mymap map[float64]string\n\nvar x = map[int]string{1: \"hello\", 2: \"world\"}\nvar y map[float64]string = map[float64]string{1: \"hello\", 2: \"world\"}\nvar z mymap = mymap{1: \"hello\", 2: \"world\"}\n`)\n}\n\nfunc TestSliceLit(t *testing.T) {\n\tgopClTest(t, `\nx := [1, 3.4, 5]\ny := [1]\nz := []\n`, `package main\n\nfunc main() {\n\tx := []float64{1, 3.4, 5}\n\ty := []int{1}\n\tz := []interface{}{}\n}\n`)\n\tgopClTest(t, `\ntype vector []float64\nvar x = [1, 2, 3]\nvar y []float64 = [1, 2, 3]\nvar z vector = [1, 2, 3]\n`, `package main\n\ntype vector []float64\n\nvar x = []int{1, 2, 3}\nvar y []float64 = []float64{1, 2, 3}\nvar z vector = vector{1, 2, 3}\n`)\n}\n\nfunc TestChan(t *testing.T) {\n\tgopClTest(t, `\na := make(chan int, 10)\na <- 3\nvar b int = <-a\nx, ok := <-a\n`, `package main\n\nfunc main() {\n\ta := make(chan int, 10)\n\ta <- 3\n\tvar b int = <-a\n\tx, ok := <-a\n}\n`)\n}\n\nfunc TestKeyValModeLit(t *testing.T) {\n\tgopClTest(t, `\na := [...]float64{1, 3: 3.4, 5}\nb := []float64{2: 1.2, 3, 6: 4.5}\n`, `package main\n\nfunc main() {\n\ta := [...]float64{1, 3: 3.4, 5}\n\tb := []float64{2: 1.2, 3, 6: 4.5}\n}\n`)\n}\n\nfunc TestStructLit(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n\tA int\n\tB string \"tag1:123\"\n}\n\na := struct {\n\tA int\n\tB string \"tag1:123\"\n}{1, \"Hello\"}\n\nb := foo{1, \"Hello\"}\nc := foo{B: \"Hi\"}\n`, `package main\n\ntype foo struct {\n\tA int\n\tB string `+\"`tag1:123`\"+`\n}\n\nfunc main() {\n\ta := struct {\n\t\tA int\n\t\tB string `+\"`tag1:123`\"+`\n\t}{1, \"Hello\"}\n\tb := foo{1, \"Hello\"}\n\tc := foo{B: \"Hi\"}\n}\n`)\n}\n\nfunc TestStructType(t *testing.T) {\n\tgopClTest(t, `\ntype bar = foo\n\ntype foo struct {\n\tp *bar\n\tA int\n\tB string \"tag1:123\"\n}\n\nfunc main() {\n\ttype a struct {\n\t\tp *a\n\t}\n\ttype b = a\n}\n`, `package main\n\ntype bar = foo\ntype foo struct {\n\tp *bar\n\tA int\n\tB string `+\"`tag1:123`\"+`\n}\n\nfunc main() {\n\ttype a struct {\n\t\tp *a\n\t}\n\ttype b = a\n}\n`)\n}\n\nfunc TestDeferGo(t *testing.T) {\n\tgopClTest(t, `\ngo println(\"Hi\")\ndefer println(\"XGo\")\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tgo fmt.Println(\"Hi\")\n\tdefer fmt.Println(\"XGo\")\n}\n`)\n}\n\nfunc TestFor(t *testing.T) {\n\tgopClTest(t, `\na := [1, 3.4, 5]\nfor i := 0; i < 3; i=i+1 {\n\tprintln(i)\n}\nfor {\n\tprintln(\"loop\")\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := []float64{1, 3.4, 5}\n\tfor i := 0; i < 3; i = i + 1 {\n\t\tfmt.Println(i)\n\t}\n\tfor {\n\t\tfmt.Println(\"loop\")\n\t}\n}\n`)\n}\n\nfunc TestRangeStmt(t *testing.T) {\n\tgopClTest(t, `\na := [1, 3.4, 5]\nfor _, x := range a {\n\tprintln(x)\n}\nfor i, x := range a {\n\tprintln(i, x)\n}\n\nvar i int\nvar x float64\nfor _, x = range a {\n\tprintln(i, x)\n}\nfor i, x = range a {\n\tprintln(i, x)\n}\nfor range a {\n\tprintln(\"Hi\")\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := []float64{1, 3.4, 5}\n\tfor _, x := range a {\n\t\tfmt.Println(x)\n\t}\n\tfor i, x := range a {\n\t\tfmt.Println(i, x)\n\t}\n\tvar i int\n\tvar x float64\n\tfor _, x = range a {\n\t\tfmt.Println(i, x)\n\t}\n\tfor i, x = range a {\n\t\tfmt.Println(i, x)\n\t}\n\tfor range a {\n\t\tfmt.Println(\"Hi\")\n\t}\n}\n`)\n}\n\nfunc TestRangeStmtUDT(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n}\n\nfunc (p *foo) Gop_Enum(c func(key int, val string)) {\n}\n\nfor k, v := range new(foo) {\n\tprintln(k, v)\n}\n`, `package main\n\nimport \"fmt\"\n\ntype foo struct {\n}\n\nfunc (p *foo) Gop_Enum(c func(key int, val string)) {\n}\nfunc main() {\n\tnew(foo).Gop_Enum(func(k int, v string) {\n\t\tfmt.Println(k, v)\n\t})\n}\n`)\n}\n\nfunc TestForPhraseUDT(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n}\n\nfunc (p *foo) Gop_Enum(c func(val string)) {\n}\n\nfor v <- new(foo) {\n\tprintln(v)\n}\n`, `package main\n\nimport \"fmt\"\n\ntype foo struct {\n}\n\nfunc (p *foo) Gop_Enum(c func(val string)) {\n}\nfunc main() {\n\tnew(foo).Gop_Enum(func(v string) {\n\t\tfmt.Println(v)\n\t})\n}\n`)\n}\n\nfunc TestForPhraseUDT2(t *testing.T) {\n\tgopClTest(t, `\ntype fooIter struct {\n}\n\nfunc (p fooIter) Next() (key string, val int, ok bool) {\n\treturn\n}\n\ntype foo struct {\n}\n\nfunc (p *foo) Gop_Enum() fooIter {\n\treturn fooIter{}\n}\n\nfor k, v <- new(foo) {\n\tprintln(k, v)\n}\n`, `package main\n\nimport \"fmt\"\n\ntype fooIter struct {\n}\ntype foo struct {\n}\n\nfunc (p fooIter) Next() (key string, val int, ok bool) {\n\treturn\n}\nfunc (p *foo) Gop_Enum() fooIter {\n\treturn fooIter{}\n}\nfunc main() {\n\tfor _xgo_it := new(foo).Gop_Enum(); ; {\n\t\tvar _xgo_ok bool\n\t\tk, v, _xgo_ok := _xgo_it.Next()\n\t\tif !_xgo_ok {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Println(k, v)\n\t}\n}\n`)\n}\n\nfunc TestForPhraseUDT3(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n}\n\nfunc (p *foo) Gop_Enum(c func(val string)) {\n}\n\nprintln([v for v <- new(foo)])\n`, `package main\n\nimport \"fmt\"\n\ntype foo struct {\n}\n\nfunc (p *foo) Gop_Enum(c func(val string)) {\n}\nfunc main() {\n\tfmt.Println(func() (_xgo_ret []string) {\n\t\tnew(foo).Gop_Enum(func(v string) {\n\t\t\t_xgo_ret = append(_xgo_ret, v)\n\t\t})\n\t\treturn\n\t}())\n}\n`)\n}\n\nfunc TestForPhraseUDT4(t *testing.T) {\n\tgopClTest(t, `\ntype fooIter struct {\n\tdata *foo\n\tidx  int\n}\n\nfunc (p *fooIter) Next() (key int, val string, ok bool) {\n\tif p.idx < len(p.data.key) {\n\t\tkey, val, ok = p.data.key[p.idx], p.data.val[p.idx], true\n\t\tp.idx++\n\t}\n\treturn\n}\n\ntype foo struct {\n\tkey []int\n\tval []string\n}\n\nfunc newFoo() *foo {\n\treturn &foo{key: [3, 7], val: [\"Hi\", \"XGo\"]}\n}\n\nfunc (p *foo) Gop_Enum() *fooIter {\n\treturn &fooIter{data: p}\n}\n\nfor k, v <- newFoo() {\n\tprintln(k, v)\n}\n`, `package main\n\nimport \"fmt\"\n\ntype fooIter struct {\n\tdata *foo\n\tidx  int\n}\ntype foo struct {\n\tkey []int\n\tval []string\n}\n\nfunc (p *fooIter) Next() (key int, val string, ok bool) {\n\tif p.idx < len(p.data.key) {\n\t\tkey, val, ok = p.data.key[p.idx], p.data.val[p.idx], true\n\t\tp.idx++\n\t}\n\treturn\n}\nfunc (p *foo) Gop_Enum() *fooIter {\n\treturn &fooIter{data: p}\n}\nfunc newFoo() *foo {\n\treturn &foo{key: []int{3, 7}, val: []string{\"Hi\", \"XGo\"}}\n}\nfunc main() {\n\tfor _xgo_it := newFoo().Gop_Enum(); ; {\n\t\tvar _xgo_ok bool\n\t\tk, v, _xgo_ok := _xgo_it.Next()\n\t\tif !_xgo_ok {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Println(k, v)\n\t}\n}\n`)\n}\n\nfunc TestForPhrase(t *testing.T) {\n\tgopClTest(t, `\nsum := 0\nfor x <- [1, 3, 5, 7, 11, 13, 17] if x > 3 {\n\tsum = sum + x\n}\nfor i, x <- [1, 3, 5, 7, 11, 13, 17] {\n\tsum = sum + i*x\n}\nprintln(\"sum(5,7,11,13,17):\", sum)\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tsum := 0\n\tfor _, x := range []int{1, 3, 5, 7, 11, 13, 17} {\n\t\tif x > 3 {\n\t\t\tsum = sum + x\n\t\t}\n\t}\n\tfor i, x := range []int{1, 3, 5, 7, 11, 13, 17} {\n\t\tsum = sum + i*x\n\t}\n\tfmt.Println(\"sum(5,7,11,13,17):\", sum)\n}\n`)\n}\n\nfunc TestExistsComprehension(t *testing.T) {\n\tgopClTest(t, `\nhasFive := {for x <- [\"1\", \"3\", \"5\", \"7\", \"11\"] if x == \"5\"}\n`, `package main\n\nfunc main() {\n\thasFive := func() (_xgo_ok bool) {\n\t\tfor _, x := range []string{\"1\", \"3\", \"5\", \"7\", \"11\"} {\n\t\t\tif x == \"5\" {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn\n\t}()\n}\n`)\n}\n\nfunc TestSliceGet(t *testing.T) {\n\tgopClTest(t, `\na := [1, 3, 5, 7, 9]\nb := a[:3]\nc := a[1:]\nd := a[1:2:3]\ne := \"Hello, XGo\"[7:]\n`, `package main\n\nfunc main() {\n\ta := []int{1, 3, 5, 7, 9}\n\tb := a[:3]\n\tc := a[1:]\n\td := a[1:2:3]\n\te := \"Hello, XGo\"[7:]\n}\n`)\n}\n\nfunc TestIndexGetTwoValue(t *testing.T) {\n\tgopClTest(t, `\na := {\"Hello\": 1, \"Hi\": 3, \"xsw\": 5, \"XGo\": 7}\nx, ok := a[\"Hi\"]\ny := a[\"XGo\"]\n`, `package main\n\nfunc main() {\n\ta := map[string]int{\"Hello\": 1, \"Hi\": 3, \"xsw\": 5, \"XGo\": 7}\n\tx, ok := a[\"Hi\"]\n\ty := a[\"XGo\"]\n}\n`)\n}\n\nfunc TestIndexGet(t *testing.T) {\n\tgopClTest(t, `\na := [1, 3.4, 5]\nb := a[1]\n`, `package main\n\nfunc main() {\n\ta := []float64{1, 3.4, 5}\n\tb := a[1]\n}\n`)\n}\n\nfunc TestIndexRef(t *testing.T) {\n\tgopClTest(t, `\na := [1, 3.4, 5]\na[1] = 2.1\n`, `package main\n\nfunc main() {\n\ta := []float64{1, 3.4, 5}\n\ta[1] = 2.1\n}\n`)\n}\n\nfunc TestIndexArrayPtrIssue784(t *testing.T) {\n\tgopClTest(t, `\ntype intArr [2]int\n\nfunc foo(a *intArr) {\n\ta[1] = 10\n}\n`, `package main\n\ntype intArr [2]int\n\nfunc foo(a *intArr) {\n\ta[1] = 10\n}\n`)\n}\n\nfunc TestMemberVal(t *testing.T) {\n\tgopClTest(t, `import \"strings\"\n\nx := strings.NewReplacer(\"?\", \"!\").Replace(\"hello, world???\")\nprintln(\"x:\", x)\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tx := strings.NewReplacer(\"?\", \"!\").Replace(\"hello, world???\")\n\tfmt.Println(\"x:\", x)\n}\n`)\n}\n\nfunc TestNamedPtrMemberIssue786(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n\treq int\n}\n\ntype pfoo *foo\n\nfunc bar(p pfoo) {\n\tprintln(p.req)\n}\n`, `package main\n\nimport \"fmt\"\n\ntype foo struct {\n\treq int\n}\ntype pfoo *foo\n\nfunc bar(p pfoo) {\n\tfmt.Println(p.req)\n}\n`)\n}\n\nfunc TestMember(t *testing.T) {\n\tgopClTest(t, `\n\nimport \"flag\"\n\na := &struct {\n\tA int\n\tB string\n}{1, \"Hello\"}\n\nx := a.A\na.B = \"Hi\"\n\nflag.Usage = nil\n`, `package main\n\nimport \"flag\"\n\nfunc main() {\n\ta := &struct {\n\t\tA int\n\t\tB string\n\t}{1, \"Hello\"}\n\tx := a.A\n\ta.B = \"Hi\"\n\tflag.Usage = nil\n}\n`)\n}\n\nfunc TestElem(t *testing.T) {\n\tgopClTest(t, `\n\nfunc foo(a *int, b int) {\n\tb = *a\n\t*a = b\n}\n`, `package main\n\nfunc foo(a *int, b int) {\n\tb = *a\n\t*a = b\n}\n`)\n}\n\nfunc TestNamedPtrIssue797(t *testing.T) {\n\tgopClTest(t, `\ntype Bar *int\n\nfunc foo(a Bar) {\n\tvar b int = *a\n}\n`, `package main\n\ntype Bar *int\n\nfunc foo(a Bar) {\n\tvar b int = *a\n}\n`)\n}\n\nfunc TestMethod(t *testing.T) {\n\tgopClTest(t, `\ntype M int\n\nfunc (m M) Foo() {\n\tprintln(\"foo\", m)\n}\n\nfunc (M) Bar() {\n\tprintln(\"bar\")\n}\n`, `package main\n\nimport \"fmt\"\n\ntype M int\n\nfunc (m M) Foo() {\n\tfmt.Println(\"foo\", m)\n}\nfunc (M) Bar() {\n\tfmt.Println(\"bar\")\n}\n`)\n}\n\nfunc TestCmdlineNoEOL(t *testing.T) {\n\tgopClTest(t, `println \"Hi\"`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hi\")\n}\n`)\n}\n\nfunc TestImport(t *testing.T) {\n\tgopClTest(t, `import \"fmt\"\n\nfunc main() {\n\tfmt.println \"Hi\"\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hi\")\n}\n`)\n}\n\nfunc TestDotImport(t *testing.T) {\n\tgopClTest(t, `import . \"math\"\n\nvar a = round(1.2)\n`, `package main\n\nimport \"math\"\n\nvar a = math.Round(1.2)\n`)\n}\n\nfunc TestLocalImport(t *testing.T) {\n\tgopClTest(t, `import \"./internal/spx\"\n\nvar a = spx.TestIntValue\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/spx\"\n\nvar a = spx.TestIntValue\n`)\n}\n\nfunc TestImportUnused(t *testing.T) {\n\tgopClTest(t, `import \"fmt\"\n\nfunc main() {\n}`, `package main\n\nfunc main() {\n}\n`)\n}\n\nfunc TestImportForceUsed(t *testing.T) {\n\tgopClTest(t, `import _ \"fmt\"\n\nfunc main() {\n}`, `package main\n\nimport _ \"fmt\"\n\nfunc main() {\n}\n`)\n}\n\nfunc TestAnonymousImport(t *testing.T) {\n\tgopClTest(t, `println(\"Hello\")\nprintf(\"Hello XGo\\n\")\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello\")\n\tfmt.Printf(\"Hello XGo\\n\")\n}\n`)\n}\n\nfunc TestVarAndConst(t *testing.T) {\n\tgopClTest(t, `\nconst (\n\ti = 1\n\tx float64 = 1\n)\nvar j int = i\n`, `package main\n\nconst (\n\ti         = 1\n\tx float64 = 1\n)\n\nvar j int = i\n`)\n}\n\nfunc TestDeclStmt(t *testing.T) {\n\tgopClTest(t, `import \"fmt\"\n\nfunc main() {\n\tconst (\n\t\ti = 1\n\t\tx float64 = 1\n\t)\n\tvar j int = i\n\tfmt.Println(\"Hi\")\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tconst (\n\t\ti         = 1\n\t\tx float64 = 1\n\t)\n\tvar j int = i\n\tfmt.Println(\"Hi\")\n}\n`)\n}\n\nfunc TestIf(t *testing.T) {\n\tgopClTest(t, `x := 0\nif t := false; t {\n\tx = 3\n} else if !t {\n\tx = 5\n} else {\n\tx = 7\n}\nprintln(\"x:\", x)\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tx := 0\n\tif t := false; t {\n\t\tx = 3\n\t} else if !t {\n\t\tx = 5\n\t} else {\n\t\tx = 7\n\t}\n\tfmt.Println(\"x:\", x)\n}\n`)\n}\n\nfunc TestSwitch(t *testing.T) {\n\tgopClTest(t, `x := 0\nswitch s := \"Hello\"; s {\ndefault:\n\tx = 7\ncase \"world\", \"hi\":\n\tx = 5\ncase \"xsw\":\n\tx = 3\n}\nprintln(\"x:\", x)\n\nv := \"Hello\"\nswitch {\ncase v == \"xsw\":\n\tx = 3\ncase v == \"hi\", v == \"world\":\n\tx = 9\ndefault:\n\tx = 11\n}\nprintln(\"x:\", x)\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tx := 0\n\tswitch s := \"Hello\"; s {\n\tdefault:\n\t\tx = 7\n\tcase \"world\", \"hi\":\n\t\tx = 5\n\tcase \"xsw\":\n\t\tx = 3\n\t}\n\tfmt.Println(\"x:\", x)\n\tv := \"Hello\"\n\tswitch {\n\tcase v == \"xsw\":\n\t\tx = 3\n\tcase v == \"hi\", v == \"world\":\n\t\tx = 9\n\tdefault:\n\t\tx = 11\n\t}\n\tfmt.Println(\"x:\", x)\n}\n`)\n}\n\nfunc TestSwitchFallthrough(t *testing.T) {\n\tgopClTest(t, `v := \"Hello\"\nswitch v {\ncase \"Hello\":\n\tprintln(v)\n\tfallthrough\ncase \"hi\":\n\tprintln(v)\n\tfallthrough\ndefault:\n\tprintln(v)\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tv := \"Hello\"\n\tswitch v {\n\tcase \"Hello\":\n\t\tfmt.Println(v)\n\t\tfallthrough\n\tcase \"hi\":\n\t\tfmt.Println(v)\n\t\tfallthrough\n\tdefault:\n\t\tfmt.Println(v)\n\t}\n}\n`)\n}\n\nfunc TestBranchStmt(t *testing.T) {\n\tgopClTest(t, `\n\ta := [1, 3.4, 5]\nlabel:\n\tfor i := 0; i < 3; i=i+1 {\n\t\tprintln(i)\n\t\tbreak\n\t\tbreak label\n\t\tcontinue\n\t\tcontinue label\n\t\tgoto label\n\t}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := []float64{1, 3.4, 5}\nlabel:\n\tfor i := 0; i < 3; i = i + 1 {\n\t\tfmt.Println(i)\n\t\tbreak\n\t\tbreak label\n\t\tcontinue\n\t\tcontinue label\n\t\tgoto label\n\t}\n}\n`)\n}\n\nfunc TestReturn(t *testing.T) {\n\tgopClTest(t, `\nfunc foo(format string, args ...interface{}) (int, error) {\n\treturn printf(format, args...)\n}\n\nfunc main() {\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc foo(format string, args ...interface{}) (int, error) {\n\treturn fmt.Printf(format, args...)\n}\nfunc main() {\n}\n`)\n}\n\nfunc TestReturnExpr(t *testing.T) {\n\tgopClTest(t, `\nfunc foo(format string, args ...interface{}) (int, error) {\n\treturn 0, nil\n}\n\nfunc main() {\n}\n`, `package main\n\nfunc foo(format string, args ...interface{}) (int, error) {\n\treturn 0, nil\n}\nfunc main() {\n}\n`)\n}\n\nfunc TestClosure(t *testing.T) {\n\tgopClTest(t, `import \"fmt\"\n\nfunc(v string) {\n\tfmt.Println(v)\n}(\"Hello\")\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfunc(v string) {\n\t\tfmt.Println(v)\n\t}(\"Hello\")\n}\n`)\n}\n\nfunc TestFunc(t *testing.T) {\n\tgopClTest(t, `func foo(format string, a [10]int, args ...interface{}) {\n}\n\nfunc main() {\n}`, `package main\n\nfunc foo(format string, a [10]int, args ...interface{}) {\n}\nfunc main() {\n}\n`)\n}\n\nfunc TestLambdaExpr(t *testing.T) {\n\tgopClTest(t, `\nfunc Map(c []float64, t func(float64) float64) {\n\t// ...\n}\n\nfunc Map2(c []float64, t func(float64) (float64, float64)) {\n\t// ...\n}\n\nMap([1.2, 3.5, 6], x => x * x)\nMap2([1.2, 3.5, 6], x => (x * x, x + x))\n`, `package main\n\nfunc Map(c []float64, t func(float64) float64) {\n}\nfunc Map2(c []float64, t func(float64) (float64, float64)) {\n}\nfunc main() {\n\tMap([]float64{1.2, 3.5, 6}, func(x float64) float64 {\n\t\treturn x * x\n\t})\n\tMap2([]float64{1.2, 3.5, 6}, func(x float64) (float64, float64) {\n\t\treturn x * x, x + x\n\t})\n}\n`)\n\tgopClTest(t, `type Foo struct {\n\tPlot func(x float64) (float64, float64)\n}\nfoo := &Foo{\n\tPlot: x => (x * 2, x * x),\n}`, `package main\n\ntype Foo struct {\n\tPlot func(x float64) (float64, float64)\n}\n\nfunc main() {\n\tfoo := &Foo{Plot: func(x float64) (float64, float64) {\n\t\treturn x * 2, x * x\n\t}}\n}\n`)\n\tgopClTest(t, `\ntype Fn func(x float64) (float64, float64)\ntype Foo struct {\n\tPlot Fn\n}\nfoo := &Foo{\n\tPlot: x => (x * 2, x * x),\n}`, `package main\n\ntype Fn func(x float64) (float64, float64)\ntype Foo struct {\n\tPlot Fn\n}\n\nfunc main() {\n\tfoo := &Foo{Plot: func(x float64) (float64, float64) {\n\t\treturn x * 2, x * x\n\t}}\n}\n`)\n\tgopClTest(t, `\ntype Fn func() (int, error)\nfunc Do(fn Fn) {\n}\n\nDo => (100, nil)\n`, `package main\n\ntype Fn func() (int, error)\n\nfunc Do(fn Fn) {\n}\nfunc main() {\n\tDo(func() (int, error) {\n\t\treturn 100, nil\n\t})\n}\n`)\n\tgopClTest(t, `\nvar fn func(int) (int,error) = x => (x*x, nil)\n`, `package main\n\nvar fn func(int) (int, error) = func(x int) (int, error) {\n\treturn x * x, nil\n}\n`)\n\tgopClTest(t, `\nvar fn func(int) (int,error)\nfn = x => (x*x, nil)\n`, `package main\n\nvar fn func(int) (int, error)\n\nfunc main() {\n\tfn = func(x int) (int, error) {\n\t\treturn x * x, nil\n\t}\n}\n`)\n}\n\nfunc TestLambdaExpr2(t *testing.T) {\n\tgopClTest(t, `\nfunc Do(func()) {\n\t// ...\n}\n\nDo => {\n\tprintln \"Hi\"\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc Do(func()) {\n}\nfunc main() {\n\tDo(func() {\n\t\tfmt.Println(\"Hi\")\n\t})\n}\n`)\n\tgopClTest(t, `\nfunc Do(fn func() (int, error)) {\n\t// ...\n}\n\nDo => {\n\treturn 100, nil\n}\n`, `package main\n\nfunc Do(fn func() (int, error)) {\n}\nfunc main() {\n\tDo(func() (int, error) {\n\t\treturn 100, nil\n\t})\n}\n`)\n\tgopClTest(t, `type Foo struct {\n\tPlot func(x float64) (float64, float64)\n}\nfoo := &Foo{\n\tPlot: x => {\n\t\treturn x * 2, x * x\n\t},\n}`, `package main\n\ntype Foo struct {\n\tPlot func(x float64) (float64, float64)\n}\n\nfunc main() {\n\tfoo := &Foo{Plot: func(x float64) (float64, float64) {\n\t\treturn x * 2, x * x\n\t}}\n}\n`)\n\tgopClTest(t, `\ntype Fn func(x float64) (float64, float64)\ntype Foo struct {\n\tPlot Fn\n}\nfoo := &Foo{\n\tPlot: x => {\n\t\treturn x * 2, x * x\n\t},\n}`, `package main\n\ntype Fn func(x float64) (float64, float64)\ntype Foo struct {\n\tPlot Fn\n}\n\nfunc main() {\n\tfoo := &Foo{Plot: func(x float64) (float64, float64) {\n\t\treturn x * 2, x * x\n\t}}\n}\n`)\n\n\tgopClTest(t, `\ntype Fn func() (int, error)\nfunc Do(fn Fn) {\n}\n\nDo => {\n\treturn 100, nil\n}\n`, `package main\n\ntype Fn func() (int, error)\n\nfunc Do(fn Fn) {\n}\nfunc main() {\n\tDo(func() (int, error) {\n\t\treturn 100, nil\n\t})\n}\n`)\n\tgopClTest(t, `\nvar fn func(int) (int,error) = x => {\n\treturn x * x, nil\n}\n`, `package main\n\nvar fn func(int) (int, error) = func(x int) (int, error) {\n\treturn x * x, nil\n}\n`)\n\tgopClTest(t, `\nvar fn func(int) (int,error)\nfn = x => {\n\treturn x * x, nil\n}\n`, `package main\n\nvar fn func(int) (int, error)\n\nfunc main() {\n\tfn = func(x int) (int, error) {\n\t\treturn x * x, nil\n\t}\n}\n`)\n}\n\nfunc TestLambdaExpr3(t *testing.T) {\n\tgopClTest(t, `\nfunc intSeq() func() int {\n\ti := 0\n\treturn => {\n\t\ti++\n\t\treturn i\n\t}\n}\n`, `package main\n\nfunc intSeq() func() int {\n\ti := 0\n\treturn func() int {\n\t\ti++\n\t\treturn i\n\t}\n}\n`)\n\tgopClTest(t, `\nfunc intDouble() func(int) int {\n\treturn i => i*2\n}\n`, `package main\n\nfunc intDouble() func(int) int {\n\treturn func(i int) int {\n\t\treturn i * 2\n\t}\n}\n`)\n}\n\nfunc TestUnnamedMainFunc(t *testing.T) {\n\tgopClTest(t, `i := 1`, `package main\n\nfunc main() {\n\ti := 1\n}\n`)\n}\n\nfunc TestFuncAsParam(t *testing.T) {\n\tgopClTest(t, `import \"fmt\"\n\nfunc bar(foo func(string, ...interface{}) (int, error)) {\n\tfoo(\"Hello, %v!\\n\", \"XGo\")\n}\n\nbar(fmt.Printf)\n`, `package main\n\nimport \"fmt\"\n\nfunc bar(foo func(string, ...interface{}) (int, error)) {\n\tfoo(\"Hello, %v!\\n\", \"XGo\")\n}\nfunc main() {\n\tbar(fmt.Printf)\n}\n`)\n}\n\nfunc TestFuncAsParam2(t *testing.T) {\n\tgopClTest(t, `import (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc foo(x string) string {\n\treturn strings.NewReplacer(\"?\", \"!\").Replace(x)\n}\n\nfunc printf(format string, args ...interface{}) (n int, err error) {\n\tn, err = fmt.Printf(format, args...)\n\treturn\n}\n\nfunc bar(foo func(string, ...interface{}) (int, error)) {\n\tfoo(\"Hello, %v!\\n\", \"XGo\")\n}\n\nbar(printf)\nfmt.Println(foo(\"Hello, world???\"))\nfmt.Println(printf(\"Hello, %v\\n\", \"XGo\"))\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc foo(x string) string {\n\treturn strings.NewReplacer(\"?\", \"!\").Replace(x)\n}\nfunc printf(format string, args ...interface{}) (n int, err error) {\n\tn, err = fmt.Printf(format, args...)\n\treturn\n}\nfunc bar(foo func(string, ...interface{}) (int, error)) {\n\tfoo(\"Hello, %v!\\n\", \"XGo\")\n}\nfunc main() {\n\tbar(printf)\n\tfmt.Println(foo(\"Hello, world???\"))\n\tfmt.Println(printf(\"Hello, %v\\n\", \"XGo\"))\n}\n`)\n}\n\nfunc TestFuncCall(t *testing.T) {\n\tgopClTest(t, `import \"fmt\"\n\nfmt.Println(\"Hello\")`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello\")\n}\n`)\n}\n\nfunc TestFuncCallEllipsis(t *testing.T) {\n\tgopClTest(t, `import \"fmt\"\n\nfunc foo(args ...interface{}) {\n\tfmt.Println(args...)\n}\n\nfunc main() {\n}`, `package main\n\nimport \"fmt\"\n\nfunc foo(args ...interface{}) {\n\tfmt.Println(args...)\n}\nfunc main() {\n}\n`)\n}\n\nfunc TestFuncCallCodeOrder(t *testing.T) {\n\tgopClTest(t, `import \"fmt\"\n\nfunc main() {\n\tfoo(\"Hello\", 123)\n}\n\nfunc foo(args ...interface{}) {\n\tfmt.Println(args...)\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfoo(\"Hello\", 123)\n}\nfunc foo(args ...interface{}) {\n\tfmt.Println(args...)\n}\n`)\n}\n\nfunc TestInterfaceMethods(t *testing.T) {\n\tgopClTest(t, `package main\n\nfunc foo(v ...interface { Bar() }) {\n}\n\nfunc main() {\n}`, `package main\n\nfunc foo(v ...interface {\n\tBar()\n}) {\n}\nfunc main() {\n}\n`)\n}\n\nfunc TestAssignUnderscore(t *testing.T) {\n\tgopClTest(t, `import log \"fmt\"\n\n_, err := log.Println(\"Hello\")\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\t_, err := fmt.Println(\"Hello\")\n}\n`)\n}\n\nfunc TestOperator(t *testing.T) {\n\tgopClTest(t, `\na := \"Hi\"\nb := a + \"!\"\nc := 13\nd := -c\n`, `package main\n\nfunc main() {\n\ta := \"Hi\"\n\tb := a + \"!\"\n\tc := 13\n\td := -c\n}\n`)\n}\n\nvar (\n\tautogen sync.Mutex\n)\n\nfunc removeAutogenFiles() {\n\tos.Remove(\"./internal/gop-in-go/foo/gop_autogen.go\")\n\tos.Remove(\"./internal/gop-in-go/foo/gop_autogen_test.go\")\n\tos.Remove(\"./internal/gop-in-go/foo/gop_autogen2_test.go\")\n}\n\nfunc TestImportGopPkg(t *testing.T) {\n\tautogen.Lock()\n\tdefer autogen.Unlock()\n\n\tremoveAutogenFiles()\n\tgopClTest(t, `import \"github.com/goplus/xgo/cl/internal/gop-in-go/foo\"\n\nrmap := foo.ReverseMap(map[string]int{\"Hi\": 1, \"Hello\": 2})\nprintln(rmap)\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/gop-in-go/foo\"\n)\n\nfunc main() {\n\trmap := foo.ReverseMap(map[string]int{\"Hi\": 1, \"Hello\": 2})\n\tfmt.Println(rmap)\n}\n`)\n}\n\nfunc TestCallDep(t *testing.T) {\n\tfor i := 0; i < 2; i++ {\n\t\tgopClTest(t, `\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestNew(t *testing.T) {\n\tret := New()\n\texpected := Result{}\n\tif reflect.DeepEqual(ret, expected) {\n\t\tt.Fatal(\"Test failed:\", ret, expected)\n\t}\n}\n\ntype Repo struct {\n\tTitle string\n}\n\nfunc newRepo() Repo {\n\treturn {Title: \"Hi\"}\n}\n\ntype Result struct {\n\tRepo Repo\n}\n\nfunc New() Result {\n\trepo := newRepo()\n\treturn {Repo: repo}\n}\n`, `package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype Repo struct {\n\tTitle string\n}\ntype Result struct {\n\tRepo Repo\n}\n\nfunc TestNew(t *testing.T) {\n\tret := New()\n\texpected := Result{}\n\tif reflect.DeepEqual(ret, expected) {\n\t\tt.Fatal(\"Test failed:\", ret, expected)\n\t}\n}\nfunc New() Result {\n\trepo := newRepo()\n\treturn Result{Repo: repo}\n}\nfunc newRepo() Repo {\n\treturn Repo{Title: \"Hi\"}\n}\n`)\n\t}\n}\n\nfunc TestGoFuncInstr(t *testing.T) {\n\tgopClTest(t, `package main\n\n//go:noinline\n//go:uintptrescapes\nfunc test(s string, p, q uintptr, rest ...uintptr) int {\n\treturn 0\n}`, `package main\n//go:noinline\n//go:uintptrescapes\nfunc test(s string, p uintptr, q uintptr, rest ...uintptr) int {\n\treturn 0\n}\n`)\n}\n\nfunc TestGoTypeInstr(t *testing.T) {\n\tgopClTest(t, `package main\n\n//go:notinheap\ntype S struct{ x int }\n`, `package main\n//go:notinheap\ntype S struct {\n\tx int\n}\n`)\n}\n\nfunc TestNoEntrypoint(t *testing.T) {\n\tgopClTest(t, `println(\"init\")\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"init\")\n}\n`)\n\tgopClTestEx(t, cltest.Conf, \"bar\", `package bar\nprintln(\"init\")\n`, `package bar\n\nimport \"fmt\"\n\nfunc init() {\n\tfmt.Println(\"init\")\n}\n`)\n}\n\nfunc TestParentExpr(t *testing.T) {\n\tgopClTest(t, `var t1 *(int)\nvar t2 chan (int)\n`, `package main\n\nvar t1 *int\nvar t2 chan int\n`)\n}\n\nfunc TestCommandStyle(t *testing.T) {\n\tgopClTest(t, `\nprintln []\nprintln {}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println([]interface{}{})\n\tfmt.Println(map[string]interface{}{})\n}\n`)\n}\n\nfunc TestTypeLoader(t *testing.T) {\n\tgopClTest(t, `import \"fmt\"\n\nfunc (p *Point) String() string {\n\treturn fmt.Sprintf(\"%v-%v\",p.X,p.Y)\n}\n\ntype Point struct {\n\tX int\n\tY int\n}\n`, `package main\n\nimport \"fmt\"\n\ntype Point struct {\n\tX int\n\tY int\n}\n\nfunc (p *Point) String() string {\n\treturn fmt.Sprintf(\"%v-%v\", p.X, p.Y)\n}\n`)\n}\n\nfunc TestCallPrintln(t *testing.T) {\n\tgopClTest(t, `\nprint\nprint \"hello\"\nprint(\"hello\")\nprintln\nprintln \"hello\"\nprintln(\"hello\")\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Print()\n\tfmt.Print(\"hello\")\n\tfmt.Print(\"hello\")\n\tfmt.Println()\n\tfmt.Println(\"hello\")\n\tfmt.Println(\"hello\")\n}\n`)\n}\n\nfunc TestAnyAlias(t *testing.T) {\n\tgopClTest(t, `\nvar a any = 100\nprintln(a)\n`, `package main\n\nimport \"fmt\"\n\nvar a interface{} = 100\n\nfunc main() {\n\tfmt.Println(a)\n}\n`)\n}\n\nfunc TestMainEntry(t *testing.T) {\n\tconf := *cltest.Conf\n\tconf.NoAutoGenMain = false\n\n\tgopClTestEx(t, &conf, \"main\", `\n`, `package main\n\nfunc main() {\n}\n`)\n\tgopClTestEx(t, &conf, \"main\", `\nfunc test() {\n\tprintln \"hello\"\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc test() {\n\tfmt.Println(\"hello\")\n}\nfunc main() {\n}\n`)\n\n\tgopClTestEx(t, &conf, \"main\", `\nfunc main() {\n\tprintln \"hello\"\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"hello\")\n}\n`)\n}\n\nfunc TestCommandNotExpr(t *testing.T) {\n\tgopClTest(t, `\nprintln !true\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(false)\n}\n`)\n\tgopClTest(t, `\na := true\nprintln !a\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := true\n\tfmt.Println(!a)\n}\n`)\n\tgopClTest(t, `\nprintln !func() bool { return true }()\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(!func() bool {\n\t\treturn true\n\t}())\n}\n`)\n}\n\nfunc TestCommentLine(t *testing.T) {\n\tgopClTestEx(t, gblConfLine, \"main\", `\ntype Point struct {\n\tx int\n\ty int\n}\n\nfunc (pt *Point) Test() {\n\tprintln(pt.x, pt.y)\n}\n\n// testPoint is test point\nfunc testPoint() {\n\tvar pt Point\n\tpt.Test()\n}\n\nprintln \"hello\"\ntestPoint()\n`, `package main\n\nimport \"fmt\"\n\ntype Point struct {\n\tx int\n\ty int\n}\n//line /foo/bar.xgo:7:1\nfunc (pt *Point) Test() {\n//line /foo/bar.xgo:8:1\n\tfmt.Println(pt.x, pt.y)\n}\n//line /foo/bar.xgo:11:1\n// testPoint is test point\nfunc testPoint() {\n//line /foo/bar.xgo:13:1\n\tvar pt Point\n//line /foo/bar.xgo:14:1\n\tpt.Test()\n}\n//line /foo/bar.xgo:17\nfunc main() {\n//line /foo/bar.xgo:17:1\n\tfmt.Println(\"hello\")\n//line /foo/bar.xgo:18:1\n\ttestPoint()\n}\n`)\n}\n\nfunc TestCommentLineRoot(t *testing.T) {\n\tconf := *cltest.Conf\n\tconf.NoFileLine = false\n\tconf.RelativeBase = \"/foo/root\"\n\tvar src = `\ntype Point struct {\n\tx int\n\ty int\n}\n\nfunc (pt *Point) Test() {\n\tprintln(pt.x, pt.y)\n}\n\n// testPoint is test point\nfunc testPoint() {\n\tvar pt Point\n\tpt.Test()\n}\n\nprintln \"hello\"\ntestPoint()\n`\n\tvar expected = `package main\n\nimport \"fmt\"\n\ntype Point struct {\n\tx int\n\ty int\n}\n//line ../bar.xgo:7:1\nfunc (pt *Point) Test() {\n//line ../bar.xgo:8:1\n\tfmt.Println(pt.x, pt.y)\n}\n//line ../bar.xgo:11:1\n// testPoint is test point\nfunc testPoint() {\n//line ../bar.xgo:13:1\n\tvar pt Point\n//line ../bar.xgo:14:1\n\tpt.Test()\n}\n//line ../bar.xgo:17\nfunc main() {\n//line ../bar.xgo:17:1\n\tfmt.Println(\"hello\")\n//line ../bar.xgo:18:1\n\ttestPoint()\n}\n`\n\tgopClTestEx(t, &conf, \"main\", src, expected)\n}\n\nfunc TestRangeScope(t *testing.T) {\n\tgopClTest(t, `\nar := []int{100, 200}\nfor k, v := range ar {\n\tprintln(k, v, ar)\n\tvar k, v, ar int\n\tprintln(ar, k, v)\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tar := []int{100, 200}\n\tfor k, v := range ar {\n\t\tfmt.Println(k, v, ar)\n\t\tvar k, v, ar int\n\t\tfmt.Println(ar, k, v)\n\t}\n}\n`)\n}\n\nfunc TestSelectScope(t *testing.T) {\n\tgopClTest(t, `\nc1 := make(chan int)\nc2 := make(chan int)\ngo func() {\n\tc1 <- 100\n}()\nselect {\ncase i := <-c1:\n\tprintln i\ncase i := <-c2:\n\tprintln i\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tc1 := make(chan int)\n\tc2 := make(chan int)\n\tgo func() {\n\t\tc1 <- 100\n\t}()\n\tselect {\n\tcase i := <-c1:\n\t\tfmt.Println(i)\n\tcase i := <-c2:\n\t\tfmt.Println(i)\n\t}\n}\n`)\n}\n\nfunc TestCommentVar(t *testing.T) {\n\tgopClTestEx(t, gblConfLine, \"main\", `\n// doc a line2\nvar a int\nprintln a\n\n// doc b line6\nvar b int\nprintln b\n\nvar c int\nprintln c\n`, `package main\n\nimport \"fmt\"\n// doc a line2\nvar a int\n//line /foo/bar.xgo:4\nfunc main() {\n//line /foo/bar.xgo:4:1\n\tfmt.Println(a)\n//line /foo/bar.xgo:6:1\n\t// doc b line6\n\tvar b int\n//line /foo/bar.xgo:8:1\n\tfmt.Println(b)\n//line /foo/bar.xgo:10:1\n\tvar c int\n//line /foo/bar.xgo:11:1\n\tfmt.Println(c)\n}\n`)\n\n\tgopClTestEx(t, gblConfLine, \"main\", `\nfunc demo() {\n\t// doc a line3\n\tvar a int\n\tprintln a\n\t\n\t// doc b line7\n\tvar b int\n\tprintln b\n\t\n\tvar c int\n\tprintln c\n}\n`, `package main\n\nimport \"fmt\"\n//line /foo/bar.xgo:2:1\nfunc demo() {\n//line /foo/bar.xgo:3:1\n\t// doc a line3\n\tvar a int\n//line /foo/bar.xgo:5:1\n\tfmt.Println(a)\n//line /foo/bar.xgo:7:1\n\t// doc b line7\n\tvar b int\n//line /foo/bar.xgo:9:1\n\tfmt.Println(b)\n//line /foo/bar.xgo:11:1\n\tvar c int\n//line /foo/bar.xgo:12:1\n\tfmt.Println(c)\n}\n`)\n}\n\nfunc TestForPhraseScope(t *testing.T) {\n\tgopClTest(t, `sum := 0\nfor x <- [1, 3, 5, 7, 11, 13, 17] {\n\tsum = sum + x\n\tprintln x\n\tx := 200\n\tprintln x\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tsum := 0\n\tfor _, x := range []int{1, 3, 5, 7, 11, 13, 17} {\n\t\tsum = sum + x\n\t\tfmt.Println(x)\n\t\tx := 200\n\t\tfmt.Println(x)\n\t}\n}\n`)\n\tgopClTest(t, `sum := 0\nfor x <- [1, 3, 5, 7, 11, 13, 17] if x > 3 {\n\tsum = sum + x\n\tprintln x\n\tx := 200\n\tprintln x\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tsum := 0\n\tfor _, x := range []int{1, 3, 5, 7, 11, 13, 17} {\n\t\tif x > 3 {\n\t\t\tsum = sum + x\n\t\t\tfmt.Println(x)\n\t\t\tx := 200\n\t\t\tfmt.Println(x)\n\t\t}\n\t}\n}\n`)\n}\n\nfunc TestAddress(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct{ c int }\n\nfunc (f foo) ptr() *foo { return &f }\nfunc (f foo) clone() foo { return f }\n\ntype nested struct {\n\tf foo\n\ta [2]foo\n\ts []foo\n}\n\nfunc _() {\n\tgetNested := func() nested { return nested{} }\n\n\t_ = getNested().f.c\n\t_ = getNested().a[0].c\n\t_ = getNested().s[0].c\n\t_ = getNested().f.ptr().c\n\t_ = getNested().f.clone().c\n\t_ = getNested().f.clone().ptr().c\n}\n`, `package main\n\ntype foo struct {\n\tc int\n}\ntype nested struct {\n\tf foo\n\ta [2]foo\n\ts []foo\n}\n\nfunc (f foo) ptr() *foo {\n\treturn &f\n}\nfunc (f foo) clone() foo {\n\treturn f\n}\nfunc _() {\n\tgetNested := func() nested {\n\t\treturn nested{}\n\t}\n\t_ = getNested().f.c\n\t_ = getNested().a[0].c\n\t_ = getNested().s[0].c\n\t_ = getNested().f.ptr().c\n\t_ = getNested().f.clone().c\n\t_ = getNested().f.clone().ptr().c\n}\n`)\n}\n\nfunc TestSliceLitAssign(t *testing.T) {\n\tgopClTest(t, `\nvar n = 1\nvar a []any = [10, 3.14, 200]\nn, a = 100, [10, 3.14, 200]\necho a, n\n`, `package main\n\nimport \"fmt\"\n\nvar n = 1\nvar a []interface{} = []interface{}{10, 3.14, 200}\n\nfunc main() {\n\tn, a = 100, []interface{}{10, 3.14, 200}\n\tfmt.Println(a, n)\n}\n`)\n}\n\nfunc TestSliceLitReturn(t *testing.T) {\n\tgopClTest(t, `\nfunc anyslice() (int, []any) {\n\treturn 100, [10, 3.14, 200]\n}\nn, a := anyslice()\necho n, a\n`, `package main\n\nimport \"fmt\"\n\nfunc anyslice() (int, []interface{}) {\n\treturn 100, []interface{}{10, 3.14, 200}\n}\nfunc main() {\n\tn, a := anyslice()\n\tfmt.Println(n, a)\n}\n`)\n}\n\nfunc TestCompositeLitAssign(t *testing.T) {\n\tgopClTest(t, `\nvar a map[any]any = {10: \"A\", 3.14: \"B\", 200: \"C\"}\nvar b map[any]string = {10: \"A\", 3.14: \"B\", 200: \"C\"}\necho a\necho b\nvar n int\nn, a = 1, {10: \"A\", 3.14: \"B\", 200: \"C\"}\necho a, n\nn, b = 1, {10: \"A\", 3.14: \"B\", 200: \"C\"}\necho b, n\n`, `package main\n\nimport \"fmt\"\n\nvar a map[interface{}]interface{} = map[interface{}]interface{}{10: \"A\", 3.14: \"B\", 200: \"C\"}\nvar b map[interface{}]string = map[interface{}]string{10: \"A\", 3.14: \"B\", 200: \"C\"}\n\nfunc main() {\n\tfmt.Println(a)\n\tfmt.Println(b)\n\tvar n int\n\tn, a = 1, map[interface{}]interface{}{10: \"A\", 3.14: \"B\", 200: \"C\"}\n\tfmt.Println(a, n)\n\tn, b = 1, map[interface{}]string{10: \"A\", 3.14: \"B\", 200: \"C\"}\n\tfmt.Println(b, n)\n}\n`)\n}\n\nfunc TestCompositeLitStruct(t *testing.T) {\n\tgopClTest(t, `\ntype T struct {\n\ts  []any\n\tm  map[any]any\n\tfn func(int) int\n}\n\necho &T{[10, 3.14, 200], {10: \"A\", 3.14: \"B\", 200: \"C\"}, (x => x)}\necho &T{s: [10, 3.14, 200], m: {10: \"A\", 3.14: \"B\", 200: \"C\"}, fn: (x => x)}\n`, `package main\n\nimport \"fmt\"\n\ntype T struct {\n\ts  []interface{}\n\tm  map[interface{}]interface{}\n\tfn func(int) int\n}\n\nfunc main() {\n\tfmt.Println(&T{[]interface{}{10, 3.14, 200}, map[interface{}]interface{}{10: \"A\", 3.14: \"B\", 200: \"C\"}, func(x int) int {\n\t\treturn x\n\t}})\n\tfmt.Println(&T{s: []interface{}{10, 3.14, 200}, m: map[interface{}]interface{}{10: \"A\", 3.14: \"B\", 200: \"C\"}, fn: func(x int) int {\n\t\treturn x\n\t}})\n}\n`)\n}\n\nfunc TestCompositeLitEx(t *testing.T) {\n\tgopClTest(t, `\nvar a [][]any = {[10, 3.14, 200], [100, 200]}\nvar m map[any][]any = {10: [10, 3.14, 200]}\nvar f map[any]func(int) int = {10: x => x}\n\necho a\necho m\necho f\n`, `package main\n\nimport \"fmt\"\n\nvar a [][]interface{} = [][]interface{}{[]interface{}{10, 3.14, 200}, []interface{}{100, 200}}\nvar m map[interface{}][]interface{} = map[interface{}][]interface{}{10: []interface{}{10, 3.14, 200}}\nvar f map[interface{}]func(int) int = map[interface{}]func(int) int{10: func(x int) int {\n\treturn x\n}}\n\nfunc main() {\n\tfmt.Println(a)\n\tfmt.Println(m)\n\tfmt.Println(f)\n}\n`)\n}\n\nfunc TestCommentFunc(t *testing.T) {\n\tgopClTestEx(t, gblConfLine, \"main\", `\nimport (\n\t\"strconv\"\n)\n\nfunc add(x, y string) (int, error) {\n\treturn strconv.atoi(x)? + strconv.atoi(y)?, nil\n}\n\nfunc addSafe(x, y string) int {\n\treturn strconv.atoi(x)?:0 + strconv.atoi(y)?:0\n}\n\necho add(\"100\", \"23\")!\n\nsum, err := add(\"10\", \"abc\")\necho sum, err\n\necho addSafe(\"10\", \"abc\")\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/errors\"\n\t\"strconv\"\n)\n//line /foo/bar.xgo:6:1\nfunc add(x string, y string) (int, error) {\n//line /foo/bar.xgo:7:1\n\tvar _autoGo_1 int\n//line /foo/bar.xgo:7:1\n\t{\n//line /foo/bar.xgo:7:1\n\t\tvar _xgo_err error\n//line /foo/bar.xgo:7:1\n\t\t_autoGo_1, _xgo_err = strconv.Atoi(x)\n//line /foo/bar.xgo:7:1\n\t\tif _xgo_err != nil {\n//line /foo/bar.xgo:7:1\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"strconv.atoi(x)\", \"/foo/bar.xgo\", 7, \"main.add\")\n//line /foo/bar.xgo:7:1\n\t\t\treturn 0, _xgo_err\n\t\t}\n//line /foo/bar.xgo:7:1\n\t\tgoto _autoGo_2\n\t_autoGo_2:\n//line /foo/bar.xgo:7:1\n\t}\n//line /foo/bar.xgo:7:1\n\tvar _autoGo_3 int\n//line /foo/bar.xgo:7:1\n\t{\n//line /foo/bar.xgo:7:1\n\t\tvar _xgo_err error\n//line /foo/bar.xgo:7:1\n\t\t_autoGo_3, _xgo_err = strconv.Atoi(y)\n//line /foo/bar.xgo:7:1\n\t\tif _xgo_err != nil {\n//line /foo/bar.xgo:7:1\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"strconv.atoi(y)\", \"/foo/bar.xgo\", 7, \"main.add\")\n//line /foo/bar.xgo:7:1\n\t\t\treturn 0, _xgo_err\n\t\t}\n//line /foo/bar.xgo:7:1\n\t\tgoto _autoGo_4\n\t_autoGo_4:\n//line /foo/bar.xgo:7:1\n\t}\n//line /foo/bar.xgo:7:1\n\treturn _autoGo_1 + _autoGo_3, nil\n}\n//line /foo/bar.xgo:10:1\nfunc addSafe(x string, y string) int {\n//line /foo/bar.xgo:11:1\n\treturn func() (_xgo_ret int) {\n//line /foo/bar.xgo:11:1\n\t\tvar _xgo_err error\n//line /foo/bar.xgo:11:1\n\t\t_xgo_ret, _xgo_err = strconv.Atoi(x)\n//line /foo/bar.xgo:11:1\n\t\tif _xgo_err != nil {\n//line /foo/bar.xgo:11:1\n\t\t\treturn 0\n\t\t}\n//line /foo/bar.xgo:11:1\n\t\treturn\n\t}() + func() (_xgo_ret int) {\n//line /foo/bar.xgo:11:1\n\t\tvar _xgo_err error\n//line /foo/bar.xgo:11:1\n\t\t_xgo_ret, _xgo_err = strconv.Atoi(y)\n//line /foo/bar.xgo:11:1\n\t\tif _xgo_err != nil {\n//line /foo/bar.xgo:11:1\n\t\t\treturn 0\n\t\t}\n//line /foo/bar.xgo:11:1\n\t\treturn\n\t}()\n}\n//line /foo/bar.xgo:14\nfunc main() {\n//line /foo/bar.xgo:14:1\n\tfmt.Println(func() (_xgo_ret int) {\n//line /foo/bar.xgo:14:1\n\t\tvar _xgo_err error\n//line /foo/bar.xgo:14:1\n\t\t_xgo_ret, _xgo_err = add(\"100\", \"23\")\n//line /foo/bar.xgo:14:1\n\t\tif _xgo_err != nil {\n//line /foo/bar.xgo:14:1\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"add(\\\"100\\\", \\\"23\\\")\", \"/foo/bar.xgo\", 14, \"main.main\")\n//line /foo/bar.xgo:14:1\n\t\t\tpanic(_xgo_err)\n\t\t}\n//line /foo/bar.xgo:14:1\n\t\treturn\n\t}())\n//line /foo/bar.xgo:16:1\n\tsum, err := add(\"10\", \"abc\")\n//line /foo/bar.xgo:17:1\n\tfmt.Println(sum, err)\n//line /foo/bar.xgo:19:1\n\tfmt.Println(addSafe(\"10\", \"abc\"))\n}\n`)\n}\n"
  },
  {
    "path": "cl/compile_testdir_test.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/cl/cltest\"\n)\n\nfunc TestTestspx(t *testing.T) {\n\tcltest.SpxFromDir(t, \"\", \"./_testspx\")\n}\n\nfunc TestTestgop(t *testing.T) {\n\tcltest.FromDir(t, \"\", \"./_testgop\")\n}\n\nfunc TestTestc(t *testing.T) {\n\tcltest.FromDir(t, \"\", \"./_testc\")\n}\n\nfunc TestTestpy(t *testing.T) {\n\tcltest.FromDir(t, \"\", \"./_testpy\")\n}\n"
  },
  {
    "path": "cl/compile_xgo_test.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestArrowOp(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n}\n\nfunc (a foo) -> (b foo) {\n\tprintln \"a -> b\"\n}\n\nfunc (a foo) <> (b foo) {\n\tprintln \"a <> b\"\n}\n`, `package main\n\nimport \"fmt\"\n\ntype foo struct {\n}\n\nfunc (a foo) XGo_PointTo(b foo) {\n\tfmt.Println(\"a -> b\")\n}\nfunc (a foo) XGo_PointBi(b foo) {\n\tfmt.Println(\"a <> b\")\n}\n`)\n}\n\nfunc TestMapLit(t *testing.T) {\n\tgopClTest(t, `\nfunc foo(map[string]string) {}\n\nfoo {}\n`, `package main\n\nfunc foo(map[string]string) {\n}\nfunc main() {\n\tfoo(map[string]string{})\n}\n`)\n}\n\nfunc TestMayBuiltinDelete(t *testing.T) {\n\tgopClTest(t, `\nfunc Delete(a int) {}\nfunc Foo(m map[string]int) {\n\tdelete(m, \"a\")\n}\n\ndelete 10\n`, `package main\n\nfunc Delete(a int) {\n}\nfunc Foo(m map[string]int) {\n\tdelete(m, \"a\")\n}\nfunc main() {\n\tDelete(10)\n}\n`)\n}\n\nfunc TestVargCommand(t *testing.T) {\n\tgopClTest(t, `\ntype foo int\n\nfunc (f foo) Ls(args ...string) {\n}\n\nvar f foo\nf.ls\n`, `package main\n\ntype foo int\n\nfunc (f foo) Ls(args ...string) {\n}\n\nvar f foo\n\nfunc main() {\n\tf.Ls()\n}\n`)\n}\n\nfunc TestCommandInPkg(t *testing.T) {\n\tgopClTest(t, `\nfunc Ls(args ...string) {\n}\n\nls\n`, `package main\n\nfunc Ls(args ...string) {\n}\nfunc main() {\n\tLs()\n}\n`)\n}\n\nfunc TestFuncAlias(t *testing.T) {\n\tgopClTest(t, `\nfunc Foo(a ...int) {}\n\nfoo 100\nfoo\n`, `package main\n\nfunc Foo(a ...int) {\n}\nfunc main() {\n\tFoo(100)\n\tFoo()\n}\n`)\n}\n\nfunc TestOverloadOp(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n}\n\nfunc (a *foo) + (b *foo) *foo {\n\tprintln(\"a + b\")\n\treturn &foo{}\n}\n\nfunc (a foo) - (b foo) foo {\n\tprintln(\"a - b\")\n\treturn foo{}\n}\n\nfunc -(a foo) {\n\tprintln(\"-a\")\n}\n\nfunc ++(a foo) {\n\tprintln(\"a++\")\n}\n\nfunc (a foo) != (b foo) bool{\n\tprintln(\"a!=b\")\n\treturn true\n}\n\nvar a, b foo\nvar c = a - b\nvar d = -a       // TODO: -a have no return value!\nvar e = a!=b\n`, `package main\n\nimport \"fmt\"\n\ntype foo struct {\n}\n\nfunc (a *foo) XGo_Add(b *foo) *foo {\n\tfmt.Println(\"a + b\")\n\treturn &foo{}\n}\nfunc (a foo) XGo_Sub(b foo) foo {\n\tfmt.Println(\"a - b\")\n\treturn foo{}\n}\nfunc (a foo) XGo_NE(b foo) bool {\n\tfmt.Println(\"a!=b\")\n\treturn true\n}\nfunc (a foo) XGo_Neg() {\n\tfmt.Println(\"-a\")\n}\nfunc (a foo) XGo_Inc() {\n\tfmt.Println(\"a++\")\n}\n\nvar a, b foo\nvar c = (foo).XGo_Sub(a, b)\nvar d = a.XGo_Neg()\nvar e = (foo).XGo_NE(a, b)\n`)\n}\n\nfunc TestOverloadOp2(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n}\n\nfunc (a foo) mulInt(b int) (ret foo) {\n\treturn\n}\n\nfunc (a foo) mulFoo(b foo) (ret foo) {\n\treturn\n}\n\nfunc intMulFoo(a int, b foo) (ret foo) {\n\treturn\n}\n\nfunc (foo).* = (\n\t(foo).mulInt\n\t(foo).mulFoo\n\tintMulFoo\n)\n\nvar a, b foo\n\nprintln a * 10\nprintln a * b\nprintln 10 * a\n`, `package main\n\nimport \"fmt\"\n\ntype foo struct {\n}\n\nconst XGoo__foo__XGo_Mul = \".mulInt,.mulFoo,intMulFoo\"\n\nfunc (a foo) mulInt(b int) (ret foo) {\n\treturn\n}\nfunc (a foo) mulFoo(b foo) (ret foo) {\n\treturn\n}\nfunc intMulFoo(a int, b foo) (ret foo) {\n\treturn\n}\n\nvar a, b foo\n\nfunc main() {\n\tfmt.Println((foo).mulInt(a, 10))\n\tfmt.Println((foo).mulFoo(a, b))\n\tfmt.Println(intMulFoo(10, a))\n}\n`)\n}\n\nfunc TestOverloadMethod(t *testing.T) {\n\tgopClTest(t, `\ntype foo struct {\n}\n\nfunc (a *foo) mulInt(b int) *foo {\n\tprintln \"mulInt\"\n\treturn a\n}\n\nfunc (a *foo) mulFoo(b *foo) *foo {\n\tprintln \"mulFoo\"\n\treturn a\n}\n\nfunc (foo).mul = (\n\t(foo).mulInt\n\t(foo).mulFoo\n)\n\nvar a, b foo\nvar c = a.mul(100)\nvar d = a.mul(c)\n`, `package main\n\nimport \"fmt\"\n\ntype foo struct {\n}\n\nconst XGoo_foo_mul = \".mulInt,.mulFoo\"\n\nfunc (a *foo) mulInt(b int) *foo {\n\tfmt.Println(\"mulInt\")\n\treturn a\n}\nfunc (a *foo) mulFoo(b *foo) *foo {\n\tfmt.Println(\"mulFoo\")\n\treturn a\n}\n\nvar a, b foo\nvar c = a.mulInt(100)\nvar d = a.mulFoo(c)\n`)\n}\n\nfunc TestOverloadFunc(t *testing.T) {\n\tgopClTest(t, `\nfunc add = (\n\tfunc(a, b int) int {\n\t\treturn a + b\n\t}\n\tfunc(a, b string) string {\n\t\treturn a + b\n\t}\n)\n\nprintln add(100, 7)\nprintln add(\"Hello\", \"World\")\n`, `package main\n\nimport \"fmt\"\n\nfunc add__0(a int, b int) int {\n\treturn a + b\n}\nfunc add__1(a string, b string) string {\n\treturn a + b\n}\nfunc main() {\n\tfmt.Println(add__0(100, 7))\n\tfmt.Println(add__1(\"Hello\", \"World\"))\n}\n`)\n}\n\nfunc TestOverloadFunc2(t *testing.T) {\n\tgopClTest(t, `\nfunc mulInt(a, b int) int {\n\treturn a * b\n}\n\nfunc mulFloat(a, b float64) float64 {\n\treturn a * b\n}\n\nfunc mul = (\n\tmulInt\n\tmulFloat\n)\n\nprintln mul(100, 7)\nprintln mul(1.2, 3.14)\n`, `package main\n\nimport \"fmt\"\n\nconst XGoo_mul = \"mulInt,mulFloat\"\n\nfunc mulInt(a int, b int) int {\n\treturn a * b\n}\nfunc mulFloat(a float64, b float64) float64 {\n\treturn a * b\n}\nfunc main() {\n\tfmt.Println(mulInt(100, 7))\n\tfmt.Println(mulFloat(1.2, 3.14))\n}\n`)\n}\n\nfunc TestOverloadFunc3(t *testing.T) {\n\tgopClTest(t, `\nfunc addInt(a, b int) int {\n\treturn a + b\n}\n\nfunc addFloat(a, b float64) float64 {\n\treturn a * b\n}\n\nfunc add = (\n\tfunc (a,b string) string { return a + b }\n\taddInt\n\taddFloat\n)\n\nprintln add(100, 7)\nprintln add(1.2, 3.14)\n`, `package main\n\nimport \"fmt\"\n\nconst XGoo_add = \",addInt,addFloat\"\n\nfunc add__0(a string, b string) string {\n\treturn a + b\n}\nfunc addInt(a int, b int) int {\n\treturn a + b\n}\nfunc addFloat(a float64, b float64) float64 {\n\treturn a * b\n}\nfunc main() {\n\tfmt.Println(addInt(100, 7))\n\tfmt.Println(addFloat(1.2, 3.14))\n}\n`)\n}\n\nfunc TestOverload(t *testing.T) {\n\tgopClTest(t, `\nimport \"github.com/goplus/xgo/cl/internal/overload/foo\"\n\ntype Mesh struct {\n}\n\nfunc (p *Mesh) Name() string {\n\treturn \"hello\"\n}\n\nvar (\n\tm1 = &Mesh{}\n\tm2 = &Mesh{}\n)\n\nfoo.onKey \"hello\", => {\n}\nfoo.onKey \"hello\", key => {\n}\nfoo.onKey [\"1\"], => {\n}\nfoo.onKey [\"2\"], key => {\n}\nfoo.onKey [m1, m2], => {\n}\nfoo.onKey [m1, m2], key => {\n}\nfoo.onKey [\"a\"], [\"b\"], key => {\n}\nfoo.onKey [\"a\"], [m1, m2], key => {\n}\nfoo.onKey [\"a\"], nil, key => {\n}\nfoo.onKey 100, 200\n\nn := &foo.N{}\nn.onKey \"hello\", => {\n}\nn.onKey \"hello\", key => {\n}\nn.onKey [\"1\"], => {\n}\nn.onKey [\"2\"], key => {\n}\nn.onKey [m1, m2], => {\n}\nn.onKey [m1, m2], key => {\n}\nn.onKey [\"a\"], [\"b\"], key => {\n}\nn.onKey [\"a\"], [m1, m2], key => {\n}\nn.onKey [\"a\"], nil, key => {\n}\nn.onKey 100, 200\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/overload/foo\"\n\ntype Mesh struct {\n}\n\nfunc (p *Mesh) Name() string {\n\treturn \"hello\"\n}\n\nvar m1 = &Mesh{}\nvar m2 = &Mesh{}\n\nfunc main() {\n\tfoo.OnKey__0(\"hello\", func() {\n\t})\n\tfoo.OnKey__1(\"hello\", func(key string) {\n\t})\n\tfoo.OnKey__2([]string{\"1\"}, func() {\n\t})\n\tfoo.OnKey__3([]string{\"2\"}, func(key string) {\n\t})\n\tfoo.OnKey__4([]foo.Mesher{m1, m2}, func() {\n\t})\n\tfoo.OnKey__5([]foo.Mesher{m1, m2}, func(key foo.Mesher) {\n\t})\n\tfoo.OnKey__6([]string{\"a\"}, []string{\"b\"}, func(key string) {\n\t})\n\tfoo.OnKey__7([]string{\"a\"}, []foo.Mesher{m1, m2}, func(key string) {\n\t})\n\tfoo.OnKey__6([]string{\"a\"}, nil, func(key string) {\n\t})\n\tfoo.OnKey__8(100, 200)\n\tn := &foo.N{}\n\tn.OnKey__0(\"hello\", func() {\n\t})\n\tn.OnKey__1(\"hello\", func(key string) {\n\t})\n\tn.OnKey__2([]string{\"1\"}, func() {\n\t})\n\tn.OnKey__3([]string{\"2\"}, func(key string) {\n\t})\n\tn.OnKey__4([]foo.Mesher{m1, m2}, func() {\n\t})\n\tn.OnKey__5([]foo.Mesher{m1, m2}, func(key foo.Mesher) {\n\t})\n\tn.OnKey__6([]string{\"a\"}, []string{\"b\"}, func(key string) {\n\t})\n\tn.OnKey__7([]string{\"a\"}, []foo.Mesher{m1, m2}, func(key string) {\n\t})\n\tn.OnKey__6([]string{\"a\"}, nil, func(key string) {\n\t})\n\tn.OnKey__8(100, 200)\n}\n`)\n}\n\nfunc TestMixedOverload(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `\npackage main\n\ntype Mesher interface {\n\tName() string\n}\n\ntype N struct {\n}\n\nfunc (m *N) OnKey__0(a string, fn func()) {\n}\n\nfunc (m *N) OnKey__1(a string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__2(a []string, fn func()) {\n}\n\nfunc (m *N) OnKey__3(a []string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__4(a []Mesher, fn func()) {\n}\n\nfunc (m *N) OnKey__5(a []Mesher, fn func(key Mesher)) {\n}\n\nfunc (m *N) OnKey__6(a []string, b []string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__7(a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__8(x int, y int) {\n}\n\n\nfunc OnKey__0(a string, fn func()) {\n}\n\nfunc OnKey__1(a string, fn func(key string)) {\n}\n\nfunc OnKey__2(a []string, fn func()) {\n}\n\nfunc OnKey__3(a []string, fn func(key string)) {\n}\n\nfunc OnKey__4(a []Mesher, fn func()) {\n}\n\nfunc OnKey__5(a []Mesher, fn func(key Mesher)) {\n}\n\nfunc OnKey__6(a []string, b []string, fn func(key string)) {\n}\n\nfunc OnKey__7(a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc OnKey__8(x int, y int) {\n}\n\nfunc OnKey__9(a, b string, fn ...func(x int) int) {\n}\n\nfunc OnKey__a(a, b string, v ...int) {\n}\n`, `\ntype Mesh struct {\n}\n\nfunc (p *Mesh) Name() string {\n\treturn \"hello\"\n}\n\nvar (\n\tm1 = &Mesh{}\n\tm2 = &Mesh{}\n)\n\nOnKey \"hello\", => {\n}\nOnKey \"hello\", key => {\n}\nOnKey [\"1\"], => {\n}\nOnKey [\"2\"], key => {\n}\nOnKey [m1, m2], => {\n}\nOnKey [m1, m2], key => {\n}\nOnKey [\"a\"], [\"b\"], key => {\n}\nOnKey [\"a\"], [m1, m2], key => {\n}\nOnKey [\"a\"], nil, key => {\n}\nOnKey 100, 200\nOnKey \"a\", \"b\", x => x * x, x => {\n\treturn x * 2\n}\nOnKey \"a\", \"b\", 1, 2, 3\nOnKey(\"a\", \"b\", [1, 2, 3]...)\n\nn := &N{}\nn.onKey \"hello\", => {\n}\nn.onKey \"hello\", key => {\n}\nn.onKey [\"1\"], => {\n}\nn.onKey [\"2\"], key => {\n}\nn.onKey [m1, m2], => {\n}\nn.onKey [m1, m2], key => {\n}\nn.onKey [\"a\"], [\"b\"], key => {\n}\nn.onKey [\"a\"], [m1, m2], key => {\n}\nn.onKey [\"a\"], nil, key => {\n}\nn.onKey 100, 200\n`, `package main\n\ntype Mesh struct {\n}\n\nfunc (p *Mesh) Name() string {\n\treturn \"hello\"\n}\n\nvar m1 = &Mesh{}\nvar m2 = &Mesh{}\n\nfunc main() {\n\tOnKey__0(\"hello\", func() {\n\t})\n\tOnKey__1(\"hello\", func(key string) {\n\t})\n\tOnKey__2([]string{\"1\"}, func() {\n\t})\n\tOnKey__3([]string{\"2\"}, func(key string) {\n\t})\n\tOnKey__4([]Mesher{m1, m2}, func() {\n\t})\n\tOnKey__5([]Mesher{m1, m2}, func(key Mesher) {\n\t})\n\tOnKey__6([]string{\"a\"}, []string{\"b\"}, func(key string) {\n\t})\n\tOnKey__7([]string{\"a\"}, []Mesher{m1, m2}, func(key string) {\n\t})\n\tOnKey__6([]string{\"a\"}, nil, func(key string) {\n\t})\n\tOnKey__8(100, 200)\n\tOnKey__9(\"a\", \"b\", func(x int) int {\n\t\treturn x * x\n\t}, func(x int) int {\n\t\treturn x * 2\n\t})\n\tOnKey__a(\"a\", \"b\", 1, 2, 3)\n\tOnKey__a(\"a\", \"b\", []int{1, 2, 3}...)\n\tn := &N{}\n\tn.OnKey__0(\"hello\", func() {\n\t})\n\tn.OnKey__1(\"hello\", func(key string) {\n\t})\n\tn.OnKey__2([]string{\"1\"}, func() {\n\t})\n\tn.OnKey__3([]string{\"2\"}, func(key string) {\n\t})\n\tn.OnKey__4([]Mesher{m1, m2}, func() {\n\t})\n\tn.OnKey__5([]Mesher{m1, m2}, func(key Mesher) {\n\t})\n\tn.OnKey__6([]string{\"a\"}, []string{\"b\"}, func(key string) {\n\t})\n\tn.OnKey__7([]string{\"a\"}, []Mesher{m1, m2}, func(key string) {\n\t})\n\tn.OnKey__6([]string{\"a\"}, nil, func(key string) {\n\t})\n\tn.OnKey__8(100, 200)\n}\n`)\n}\n\nfunc TestMixedOverloadOp(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\n\nimport \"fmt\"\n\ntype foo struct {\n}\n\nfunc (a *foo) XGo_Add(b *foo) *foo {\n\tfmt.Println(\"a + b\")\n\treturn &foo{}\n}\nfunc (a foo) XGo_Sub(b foo) foo {\n\tfmt.Println(\"a - b\")\n\treturn foo{}\n}\nfunc (a foo) XGo_NE(b foo) bool {\n\tfmt.Println(\"a!=b\")\n\treturn true\n}\nfunc (a foo) XGo_Neg() *foo {\n\tfmt.Println(\"-a\")\n\treturn &foo{}\n}\nfunc (a foo) XGo_Inc() {\n\tfmt.Println(\"a++\")\n}\n`, `\nvar a, b foo\nvar c = a - b\nvar d = -a\nvar e = a!=b\n`, `package main\n\nvar a, b foo\nvar c = (foo).XGo_Sub(a, b)\nvar d = a.XGo_Neg()\nvar e = (foo).XGo_NE(a, b)\n`)\n}\n\nfunc TestMixedVector3(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\ntype Vector3 struct {\n\tx, y, z float64\n}\nfunc (a Vector3) XGo_Add__0(n int) Vector3 {\n\treturn Vector3{}\n}\nfunc (a Vector3) XGo_Add__1(n float64) Vector3 {\n\treturn Vector3{}\n}\nfunc (a Vector3) XGo_Add__2(n Vector3) Vector3 {\n\treturn Vector3{}\n}\nfunc (a *Vector3) XGo_AddAssign(n Vector3) {\n}\n\nfunc (a Vector3) XGo_Rcast__0() int {\n\treturn 0\n}\nfunc (a Vector3) XGo_Rcast__1() float64 {\n\treturn 0\n}\n\nfunc Vector3_Cast__0(x int) Vector3 {\n\treturn Vector3{}\n}\nfunc Vector3_Cast__1(x float64) Vector3 {\n\treturn Vector3{}\n}\nfunc Vector3_Init__0(x int) Vector3 {\n\treturn Vector3{}\n}\nfunc Vector3_Init__1(x float64) Vector3 {\n\treturn Vector3{}\n}\n`, `\nvar a Vector3\nvar b int\nvar c float64\n_ = a+b\n_ = a+100\n_ = a+c\n_ = 100+a\n_ = Vector3(b)+a\n_ = b+int(a)\na += b\na += c\n`, `package main\n\nvar a Vector3\nvar b int\nvar c float64\n\nfunc main() {\n\t_ = (Vector3).XGo_Add__0(a, b)\n\t_ = (Vector3).XGo_Add__0(a, 100)\n\t_ = (Vector3).XGo_Add__1(a, c)\n\t_ = (Vector3).XGo_Add__2(Vector3_Init__0(100), a)\n\t_ = (Vector3).XGo_Add__2(Vector3_Cast__0(b), a)\n\t_ = b + a.XGo_Rcast__0()\n\ta.XGo_AddAssign(Vector3_Init__0(b))\n\ta.XGo_AddAssign(Vector3_Init__1(c))\n}\n`)\n}\n\nfunc TestMixedInterfaceOverload(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `\npackage main\n\ntype N[T any] struct {\n\tv T\n}\n\nfunc (m *N[T]) OnKey__0(a string, fn func()) {\n}\n\nfunc (m *N[T]) OnKey__1(a string, fn func(key string)) {\n}\n\nfunc (m *N[T]) OnKey__2(a []string, fn func()) {\n}\n\nfunc (m *N[T]) OnKey__3(a []string, fn func(key string)) {\n}\n\ntype I interface {\n\tOnKey__0(a string, fn func())\n\tOnKey__1(a string, fn func(key string))\n\tOnKey__2(a []string, fn func())\n\tOnKey__3(a []string, fn func(key string))\n}\n`, `\nn := &N[int]{}\nn.onKey \"1\", => {\n}\nkeys := [\"1\",\"2\"]\nn.onKey keys, key => {\n\tprintln key\n}\nn.onKey keys, => {\n\tprintln keys\n}\n\nvar i I = n\ni.onKey \"1\", key => {\n\tprintln key\n}\ni.onKey [\"1\",\"2\"], key => {\n\tprintln key\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tn := &N[int]{}\n\tn.OnKey__0(\"1\", func() {\n\t})\n\tkeys := []string{\"1\", \"2\"}\n\tn.OnKey__3(keys, func(key string) {\n\t\tfmt.Println(key)\n\t})\n\tn.OnKey__2(keys, func() {\n\t\tfmt.Println(keys)\n\t})\n\tvar i I = n\n\ti.OnKey__1(\"1\", func(key string) {\n\t\tfmt.Println(key)\n\t})\n\ti.OnKey__3([]string{\"1\", \"2\"}, func(key string) {\n\t\tfmt.Println(key)\n\t})\n}\n`)\n}\n\nfunc TestMixedOverloadCommand(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\n\nfunc Test__0() {\n}\nfunc Test__1(n int) {\n}\ntype N struct {\n}\nfunc (p *N) Test__0() {\n}\nfunc (p *N) Test__1(n int) {\n}`, `\nTest\nTest 100\nvar n N\nn.test\nn.test 100\n`, `package main\n\nfunc main() {\n\tTest__0()\n\tTest__1(100)\n\tvar n N\n\tn.Test__0()\n\tn.Test__1(100)\n}\n`)\n}\n\nfunc TestOverloadNamed(t *testing.T) {\n\tgopClTest(t, `\nimport \"github.com/goplus/xgo/cl/internal/overload/bar\"\n\nvar a bar.Var[int]\nvar b bar.Var[bar.M]\nc := bar.Var(string)\nd := bar.Var(bar.M)\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/overload/bar\"\n\nvar a bar.Var__0[int]\nvar b bar.Var__1[bar.M]\n\nfunc main() {\n\tc := bar.XGox_Var_Cast__0[string]()\n\td := bar.XGox_Var_Cast__1[bar.M]()\n}\n`)\n}\n\nfunc TestMixedOverloadNamed(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\n\ntype M = map[string]any\n\ntype basetype interface {\n\tstring | int | bool | float64\n}\n\ntype Var__0[T basetype] struct {\n\tval T\n}\n\ntype Var__1[T map[string]any] struct {\n\tval T\n}\n\nfunc XGox_Var_Cast__0[T basetype]() *Var__0[T] {\n\treturn new(Var__0[T])\n}\n\nfunc XGox_Var_Cast__1[T map[string]any]() *Var__1[T] {\n\treturn new(Var__1[T])\n}\n`, `\nvar a Var[int]\nvar b Var[M]\nc := Var(string)\nd := Var(M)\n`, `package main\n\nvar a Var__0[int]\nvar b Var__1[M]\n\nfunc main() {\n\tc := XGox_Var_Cast__0[string]()\n\td := XGox_Var_Cast__1[M]()\n}\n`)\n}\n\nfunc TestStringLitBasic(t *testing.T) {\n\tgopClTest(t, `echo \"$$\"`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"$\")\n}\n`)\n}\n\nfunc TestStringLitVar(t *testing.T) {\n\tgopClTest(t, `\nx := 1\nprintln \"Hi, \" + \"a${x}b\"`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/stringutil\"\n\t\"strconv\"\n)\n\nfunc main() {\n\tx := 1\n\tfmt.Println(\"Hi, \" + stringutil.Concat(\"a\", strconv.Itoa(x), \"b\"))\n}\n`)\n}\n\nfunc TestFileOpen(t *testing.T) {\n\tgopClTest(t, `\nfor line <- open(\"foo.txt\")! {\n\tprintln line\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/errors\"\n\t\"github.com/qiniu/x/osx\"\n\t\"os\"\n)\n\nfunc main() {\n\tfor _xgo_it := osx.EnumLines(func() (_xgo_ret *os.File) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = os.Open(\"foo.txt\")\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"open(\\\"foo.txt\\\")\", \"/foo/bar.xgo\", 2, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}()); ; {\n\t\tvar _xgo_ok bool\n\t\tline, _xgo_ok := _xgo_it.Next()\n\t\tif !_xgo_ok {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Println(line)\n\t}\n}\n`)\n}\n\nfunc TestMixedGo(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\n\nimport \"strconv\"\n\nconst n = 10\n\nfunc f(v int) string {\n\treturn strconv.Itoa(v)\n}\n\ntype foo struct {\n\tv int\n}\n\nfunc (a foo) _() {\n}\n\nfunc (a foo) Str() string {\n\treturn f(a.v)\n}\n\nfunc (a *foo) Bar() int {\n\treturn 0\n}\n\ntype foo2 = foo\ntype foo3 foo2\n`, `\nvar a [n]int\nvar b string = f(n)\nvar c foo2\nvar d int = c.v\nvar e = foo3{}\nvar x string = c.str\n`, `package main\n\nvar a [10]int\nvar b string = f(n)\nvar c foo2\nvar d int = c.v\nvar e = foo3{}\nvar x string = c.Str()\n`, true)\n\tgopMixedClTest(t, \"main\", `package main\ntype Point struct {\n\tX int\n\tY int\n}\n`, `\ntype T struct{}\nprintln(&T{},&Point{10,20})\n`, `package main\n\nimport \"fmt\"\n\ntype T struct {\n}\n\nfunc main() {\n\tfmt.Println(&T{}, &Point{10, 20})\n}\n`, false)\n}\n\nfunc TestTypeAsParamsFunc(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype basetype interface {\n\tint | string\n}\n\nfunc XGox_Row__0[T basetype](name string) {\n}\n\nfunc XGox_Row__1[Array any](v int) {\n}\n\nfunc XGox_Col[T any](name string) {\n\tfmt.Printf(\"%v: %s\\n\", reflect.TypeOf((*T)(nil)).Elem(), name)\n}\n\ntype Table struct {\n}\n\nfunc Gopt_Table_XGox_Col__0[T basetype](p *Table, name string) {\n}\n\nfunc Gopt_Table_XGox_Col__1[Array any](p *Table, v int) {\n}\n`, `\nvar tbl *Table\n\ncol string, \"name\"\ncol int, \"age\"\n\nrow string, 100\n\ntbl.col string, \"foo\"\ntbl.col int, 100\n`, `package main\n\nvar tbl *Table\n\nfunc main() {\n\tXGox_Col[string](\"name\")\n\tXGox_Col[int](\"age\")\n\tXGox_Row__1[string](100)\n\tGopt_Table_XGox_Col__0[string](tbl, \"foo\")\n\tGopt_Table_XGox_Col__1[int](tbl, 100)\n}\n`)\n}\n\nfunc TestYaptest(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\n\nimport (\n\t\"github.com/goplus/xgo/cl/internal/test\"\n)\n\ntype Class struct {\n\ttest.Case\n}\n`, `var c Class\nvar a int\n\nc.match a, \"b\"\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/test\"\n\nvar c Class\nvar a int\n\nfunc main() {\n\ttest.Gopt_Case_MatchAny(c, a, \"b\")\n}\n`)\n}\n\nfunc testRangeExpr(t *testing.T, codeTpl, expect string) {\n\tfor k, s := range []string{\" <- \", \" := range \", \" = range \"} {\n\t\tif k == 2 {\n\t\t\tcodeTpl = \"i:=0\\n\" + codeTpl\n\t\t\texpect = strings.Replace(expect, \"for i := \", \"i := 0\\n\\tfor i = \", -1)\n\t\t}\n\t\tgopClTest(t, strings.Replace(codeTpl, \"$\", s, -1), expect)\n\t}\n}\n\nfunc TestRangeExpr(t *testing.T) {\n\ttestRangeExpr(t, `\nfor i $ :10 {\n\tprintln(i)\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 0; i < 10; i += 1 {\n\t\tfmt.Println(i)\n\t}\n}\n`)\n\ttestRangeExpr(t, `\nfor i $ 1:10:3 {\n\tprintln(i)\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 1; i < 10; i += 3 {\n\t\tfmt.Println(i)\n\t}\n}\n`)\n}\n\nfunc TestRangeExpr2(t *testing.T) {\n\ttestRangeExpr(t, `\nfor i $ 1:10:2 {\n\tprintln(i)\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 1; i < 10; i += 2 {\n\t\tfmt.Println(i)\n\t}\n}\n`)\n}\n\nfunc TestRangeExpr3(t *testing.T) {\n\ttestRangeExpr(t, `\nfor i $ 1:10 {\n\tprintln(i)\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 1; i < 10; i += 1 {\n\t\tfmt.Println(i)\n\t}\n}\n`)\n}\n\nfunc TestRangeExpr4(t *testing.T) {\n\ttestRangeExpr(t, `\nfor i $ :10:2 {\n\tprintln(i)\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 0; i < 10; i += 2 {\n\t\tfmt.Println(i)\n\t}\n}\n`)\n}\n\nfunc TestRangeExpr5(t *testing.T) {\n\tgopClTest(t, `\nfor range :10 {\n\tprintln(\"Hi\")\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor _xgo_k := 0; _xgo_k < 10; _xgo_k += 1 {\n\t\tfmt.Println(\"Hi\")\n\t}\n}\n`)\n}\n\nfunc TestRangeExpr6(t *testing.T) {\n\tgopClTest(t, `\nfor _ <- :10 {\n\tprintln(\"Hi\")\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor _xgo_k := 0; _xgo_k < 10; _xgo_k += 1 {\n\t\tfmt.Println(\"Hi\")\n\t}\n}\n`)\n}\n\nfunc testRangeExpr8(t *testing.T, codeTpl, expect string) {\n\tfor _, s := range []string{\" <- \", \" := range \"} {\n\t\tgopClTest(t, strings.Replace(codeTpl, \"$\", s, -1), expect)\n\t}\n}\n\nfunc TestRangeExpr8(t *testing.T) {\n\ttestRangeExpr8(t, `\ntype T struct{}\n\nfunc (t T) start() int {\n\treturn 0\n}\nfunc (t T) end() int{\n\treturn 3\n}\nfunc (t T) step() int{\n\treturn 1\n}\n\nt:=T{}\n\nfor i <- t.start():t.end():t.step(){\n\tprintln i\n}\n`, `package main\n\nimport \"fmt\"\n\ntype T struct {\n}\n\nfunc (t T) start() int {\n\treturn 0\n}\nfunc (t T) end() int {\n\treturn 3\n}\nfunc (t T) step() int {\n\treturn 1\n}\nfunc main() {\n\tt := T{}\n\tfor i, _xgo_end, _xgo_step := t.start(), t.end(), t.step(); i < _xgo_end; i += _xgo_step {\n\t\tfmt.Println(i)\n\t}\n}\n`)\n}\n\nfunc TestRangeExpr9(t *testing.T) {\n\ttestRangeExpr8(t, `\ntype T struct{}\n\nfunc (t T) start() int {\n\treturn 0\n}\nfunc (t T) end() int{\n\treturn 3\n}\nfunc (t T) step() int{\n\treturn 1\n}\n\nt:=T{}\ni:=0\nfor i =range t.start():t.end():t.step(){\n\tprintln i\n}\n`, `package main\n\nimport \"fmt\"\n\ntype T struct {\n}\n\nfunc (t T) start() int {\n\treturn 0\n}\nfunc (t T) end() int {\n\treturn 3\n}\nfunc (t T) step() int {\n\treturn 1\n}\nfunc main() {\n\tt := T{}\n\ti := 0\n\tfor _xgo_k, _xgo_end, _xgo_step := t.start(), t.end(), t.step(); _xgo_k < _xgo_end; _xgo_k += _xgo_step {\n\t\ti = _xgo_k\n\t\tfmt.Println(i)\n\t}\n}\n`)\n}\n\nfunc TestRangeExpr10(t *testing.T) {\n\tgopClTest(t, `\nfor :10 {\n\techo \"Hi\"\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor _xgo_k := 0; _xgo_k < 10; _xgo_k += 1 {\n\t\tfmt.Println(\"Hi\")\n\t}\n}\n`)\n}\n\nfunc Test_RangeExpressionIf_Issue1243(t *testing.T) {\n\tgopClTest(t, `\nfor i <- :10, i%3 == 0 {\n\tprintln i\n}`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 0; i < 10; i += 1 {\n\t\tif i%3 == 0 {\n\t\t\tfmt.Println(i)\n\t\t}\n\t}\n}\n`)\n}\n\nfunc TestStaticMethod(t *testing.T) {\n\tgopClTest(t, `\ntype foo int\n\nfunc foo.New(a int) *foo {\n\treturn new(foo)\n}\n\nfunc foo._add() *foo {\n\treturn new(foo)\n}\n\na := foo.new(100)\n`, `package main\n\ntype foo int\n\nfunc XGos_foo_New(a int) *foo {\n\treturn new(foo)\n}\nfunc XGos__foo___add() *foo {\n\treturn new(foo)\n}\nfunc main() {\n\ta := XGos_foo_New(100)\n}\n`)\n}\n\nfunc TestOverlodOptions(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `\npackage main\n\ntype PlayOptions struct {\n\tAction int\n\tWait   bool\n\tLoop   bool\n}\ntype Game struct {\n}\nfunc (g *Game) Play__0(options *PlayOptions) {\n}\nfunc (g *Game) Play__1(name string, options *PlayOptions) {\n}\n`, `\ng := &Game{}\ng.play \"work\", { Action: 0, Loop: true }\n`, `package main\n\nfunc main() {\n\tg := &Game{}\n\tg.Play__1(\"work\", &PlayOptions{Action: 0, Loop: true})\n}\n`)\n}\n\nfunc TestEmbedField(t *testing.T) {\n\tgopClTest(t, `package main\n\ntype Info struct {\n\tid int\n}\ntype T struct {\n\tInfo\n\tid string\n}\nfunc demo(t *T) {\n\tt.id = \"0\"\n}\nfunc main() {\n}\n`, `package main\n\ntype Info struct {\n\tid int\n}\ntype T struct {\n\tInfo\n\tid string\n}\n\nfunc demo(t *T) {\n\tt.id = \"0\"\n}\nfunc main() {\n}\n`)\n}\n\nfunc TestOverloadUntyped(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `\npackage main\n\ntype specialObj int\ntype SpriteName string\n\ntype SpriteImpl struct {\n}\n\nfunc (p *SpriteImpl) turn(v any) {\n}\nfunc (p *SpriteImpl) TurnTo__0(sprite *SpriteImpl) {\n}\nfunc (p *SpriteImpl) TurnTo__1(sprite SpriteName) {\n}\nfunc (p *SpriteImpl) TurnTo__2(obj specialObj) {\n}\nfunc (p *SpriteImpl) TurnTo__3(degree float64) {\n}\n`, `\np := &SpriteImpl{}\np.turnTo 180.0\np.turnTo 180.1\n`, `package main\n\nfunc main() {\n\tp := &SpriteImpl{}\n\tp.TurnTo__2(180.0)\n\tp.TurnTo__3(180.1)\n}\n`)\n}\n\nfunc TestOverloadUntyped2(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `\npackage main\n\ntype SpriteImpl struct {\n}\n\nfunc (p *SpriteImpl) Rand__0(from int, to int) {\n}\nfunc (p *SpriteImpl) Rand__1(from float64, to float64) {\n}\n`, `\np := &SpriteImpl{}\np.rand(1.0,2.0)\np.rand(float64(1),float64(2))\n`, `package main\n\nfunc main() {\n\tp := &SpriteImpl{}\n\tp.Rand__0(1.0, 2.0)\n\tp.Rand__1(float64(1), float64(2))\n}\n`)\n}\n\nfunc TestSliceType(t *testing.T) {\n\tgopClTest(t, `\na := [1, \"a\"]\na[0] = [1, 2, 3]\necho a\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := []interface{}{1, \"a\"}\n\ta[0] = []int{1, 2, 3}\n\tfmt.Println(a)\n}\n`)\n}\n\nfunc TestMapLitType(t *testing.T) {\n\tgopClTest(t, `\nvar a any = {\n    \"Monday\": 1,\n    \"Tuesday\": 2,\n}\necho a\n`, `package main\n\nimport \"fmt\"\n\nvar a interface{} = map[string]int{\"Monday\": 1, \"Tuesday\": 2}\n\nfunc main() {\n\tfmt.Println(a)\n}\n`)\n}\n"
  },
  {
    "path": "cl/error_msg_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl_test\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/cl/cltest\"\n)\n\nfunc codeErrorTest(t *testing.T, msg, src string) {\n\tcltest.ErrorEx(t, \"main\", \"bar.xgo\", msg, src)\n}\n\nfunc codeErrorTestEx(t *testing.T, pkgname, filename, msg, src string) {\n\tcltest.ErrorEx(t, pkgname, filename, msg, src)\n}\n\nfunc codeErrorTestAst(t *testing.T, pkgname, filename, msg, src string) {\n\tcltest.ErrorAst(t, pkgname, filename, msg, src)\n}\n\nfunc TestErrTplLit(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:1:18: not enough arguments to return\n\thave ()\n\twant (interface{})`, \"tpl`a = INT => { return }`\")\n}\n\nfunc TestErrSendStmt(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:3:7: can't send multiple values to a channel`, `\n\tvar a chan int\n\ta <- 1, 2\n`)\n}\n\nfunc TestErrVargCommand(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:5:1: not enough arguments in call to Ls\n\thave ()\n\twant (int)`, `\nfunc Ls(int) {\n}\n\nls\n`)\n\tcodeErrorTest(t, `bar.xgo:8:1: not enough arguments in call to f.Ls\n\thave ()\n\twant (int)`, `\ntype foo int\n\nfunc (f foo) Ls(int) {\n}\n\nvar f foo\nf.ls\n`)\n}\n\nfunc TestErrUnsafe(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:2:9: undefined: Sizeof`, `\nprintln Sizeof(0)\n`)\n}\n\nfunc TestErrLambdaExpr(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:7:6: too few arguments in lambda expression\\n\\thave ()\\n\\twant (int, int)\", `\n\nfunc foo(func(int, int)) {\n}\n\nfunc main() {\n\tfoo(=> {})\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:7:6: too many arguments in lambda expression\\n\\thave (x, y, z)\\n\\twant (int, int)\", `\n\nfunc foo(func(int, int)) {\n}\n\nfunc main() {\n\tfoo((x, y, z) => {})\n}\n`)\n\tcodeErrorTest(t, \"bar.xgo:6:8: cannot use lambda literal as type int in field value to Plot\", `\ntype Foo struct {\n\tPlot int\n}\nfoo := &Foo{\n\tPlot: x => (x * 2, x * x),\n}\n`)\n\tcodeErrorTest(t, \"bar.xgo:6:8: cannot use lambda literal as type int in field value to Plot\", `\ntype Foo struct {\n\tPlot int\n}\nfoo := &Foo{\n\tPlot: x => {\n\t\treturn x * 2, x * x\n\t},\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:5: cannot use lambda literal as type int in argument to foo\", `\nfunc foo(int) {\n}\nfoo(=> {})\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:5: cannot use lambda literal as type func() in argument to foo\", `\nfunc foo(func()) {\n}\nfoo => (100)\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:6:8: cannot use lambda literal as type func() int in field value to Plot\", `\ntype Foo struct {\n\tPlot func() int\n}\nfoo := &Foo{\n\tPlot: x => (x * 2, x * x),\n}\n`)\n\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:18: cannot use lambda literal as type func() in assignment to foo\", `\nvar foo func() = => (100)\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:3:7: cannot use lambda literal as type func() in assignment to foo\", `\nvar foo func()\nfoo = => (100)\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:29: lambda unsupport multiple assignment\", `\nvar foo, foo1 func() = nil, => {}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:3:15: lambda unsupport multiple assignment\", `\nvar foo func()\n_, foo = nil, => {}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:9: cannot use lambda expression as type int in return statement\", `\nfunc intSeq() int {\n\ti := 0\n\treturn => {\n\t\ti++\n\t\treturn i\n\t}\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:6:10: cannot use i (type int) as type string in return argument\", `\nfunc intSeq() func() string {\n\ti := 0\n\treturn => {\n\t\ti++\n\t\treturn i\n\t}\n}\n`)\n}\n\nfunc TestErrErrWrap(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:2: undefined: a\", `func main() {\n\ta!\n}\n`)\n}\n\nfunc TestErrVar(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:6:5: assignment mismatch: 1 variables but fmt.Println returns 2 values\", `import \"fmt\"\n\nfunc main() {\n}\n\nvar a = fmt.Println(1)\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:5: assignment mismatch: 1 variables but 2 values\", `func main() {\n}\n\nvar a = 1, 2\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:2: undefined: foo\", `func main() {\n\tfoo.x = 1\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:2: use of builtin len not in function call\", `func main() {\n\tlen.x = 1\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:10: undefined: foo\", `func main() {\n\tprintln(foo.x)\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:10: use of builtin len not in function call\", `func main() {\n\tprintln(len.x)\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:10: undefined: foo\", `func main() {\n\tprintln(foo)\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:3:20: use of builtin len not in function call\", `package main\n\nfunc foo(v map[int]len) {\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:5:20: bar is not a type\", `package main\n\nvar bar = 1\n\nfunc foo(v map[int]bar) {\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:6: use of builtin len not in function call\", `func main() {\n\tnew(len)\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:2: undefined: foo\", `func main() {\n\tfoo = 1\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:9: cannot use _ as value\", `func main() {\n\tfoo := _\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:9: use of builtin len not in function call\", `func main() {\n\tfoo := len\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:2: println is not a variable\", `func main() {\n\tprintln = \"hello\"\n}\n`)\n}\n\nfunc TestErrImport(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:8:2: confliction: NewEncoding declared both in \"encoding/base64\" and \"encoding/base32\"`, `\nimport (\n\t. \"encoding/base32\"\n\t. \"encoding/base64\"\n)\n\nfunc foo() {\n\tNewEncoding(\"Hi\")\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:5:2: cannot refer to unexported name os.undefined\", `\nimport \"os\"\n\nfunc foo() {\n\tos.undefined\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:5:2: undefined: os.UndefinedObject\", `\nimport \"os\"\n\nfunc foo() {\n\tos.UndefinedObject\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:13: undefined: testing\", `\nfunc foo(t *testing.T) {\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:12: testing.Verbose is not a type\", `\nimport \"testing\"\n\nfunc foo(t testing.Verbose) {\n}`)\n}\n\nfunc TestErrConst(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:3:7: a redeclared in this block\\n\\tprevious declaration at bar.xgo:2:5\", `\nvar a int\nconst a = 1\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:2: missing value in const declaration\", `\nconst (\n\ta = iota\n\tb, c\n)\n`)\n}\n\nfunc TestErrNewVar(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:3:5: a redeclared in this block\\n\\tprevious declaration at bar.xgo:2:5\", `\nvar a int\nvar a string\n`)\n}\n\nfunc TestErrDefineVar(t *testing.T) {\n\tcodeErrorTest(t, \"bar.xgo:3:1: no new variables on left side of :=\\n\"+\n\t\t\"bar.xgo:3:6: cannot use \\\"Hi\\\" (type untyped string) as type int in assignment\", `\na := 1\na := \"Hi\"\n`)\n}\n\nfunc TestErrAssignMismatchT(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:16: cannot use []string{} (type []string) as type string in assignment`, `\nvar a string = []string{}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:16: cannot use [2]string{} (type [2]string) as type string in assignment`, `\nvar a string = [2]string{}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:16: cannot use map[int]string{} (type map[int]string) as type string in assignment`, `\nvar a string = map[int]string{}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:16: cannot use T{} (type T) as type string in assignment`, `\ntype T struct{}\nvar a string = T{}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:16: cannot use func(){} (type func()) as type string in assignment`, `\nvar a string = func(){}\n`)\n}\n\nfunc TestErrAssign(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:8:1: assignment mismatch: 1 variables but bar returns 2 values`, `\n\nfunc bar() (n int, err error) {\n\treturn\n}\n\nx := 1\nx = bar()\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:4:1: assignment mismatch: 1 variables but 2 values`, `\n\nx := 1\nx = 1, \"Hi\"\n`)\n}\n\nfunc TestErrReturn(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:2: too few arguments to return\\n\\thave (untyped int)\\n\\twant (int, error)\", `\n\nfunc foo() (int, error) {\n\treturn 1\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:2: too many arguments to return\\n\\thave (untyped int, untyped int, untyped string)\\n\\twant (int, error)\", `\n\nfunc foo() (int, error) {\n\treturn 1, 2, \"Hi\"\n}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:4:12: cannot use \"Hi\" (type untyped string) as type error in return argument`, `\n\nfunc foo() (int, error) {\n\treturn 1, \"Hi\"\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:8:2: too few arguments to return\\n\\thave (byte)\\n\\twant (int, error)\", `\n\nfunc bar() (v byte) {\n\treturn\n}\n\nfunc foo() (int, error) {\n\treturn bar()\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:8:2: too many arguments to return\\n\\thave (n int, err error)\\n\\twant (v byte)\", `\n\nfunc bar() (n int, err error) {\n\treturn\n}\n\nfunc foo() (v byte) {\n\treturn bar()\n}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:8:2: cannot use byte value as type error in return argument`, `\n\nfunc bar() (n int, v byte) {\n\treturn\n}\n\nfunc foo() (int, error) {\n\treturn bar()\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:2: not enough arguments to return\\n\\thave ()\\n\\twant (byte)\", `\n\nfunc foo() byte {\n\treturn\n}\n`)\n}\n\nfunc TestErrForRange(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:4:8: cannot assign type string to a (type int) in range`, `\na := 1\nvar b []string\nfor _, a = range b {\n}\n`)\n}\n\nfunc TestErrInitFunc(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:6: func init must have no arguments and no return values`, `\nfunc init(v byte) {\n}\n`)\n}\n\nfunc TestErrRecv(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:5:9: invalid receiver type a (a is a pointer type)`, `\n\ntype a *int\n\nfunc (p a) foo() {\n}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:9: invalid receiver type error (error is an interface type)`, `\nfunc (p error) foo() {\n}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:9: invalid receiver type []byte ([]byte is not a defined type)`, `\nfunc (p []byte) foo() {\n}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:10: invalid receiver type []byte ([]byte is not a defined type)`, `\nfunc (p *[]byte) foo() {\n}\n`)\n}\n\nfunc TestErrEnvOp(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:2:6: operator $name undefined`, `\necho ${name}\n`)\n\tcodeErrorTest(t, `bar.xgo:2:1: operator $id undefined`, `\n$id\n`)\n}\n\nfunc TestErrStringLit(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:2:9: [].string undefined (type []interface{} has no field or method string)`, `\necho \"${[]}\"\n`)\n}\n\nfunc TestErrStructLit(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:39: too many values in struct{x int; y string}{...}`, `\nx := 1\na := struct{x int; y string}{1, \"Hi\", 2}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:30: too few values in struct{x int; y string}{...}`, `\nx := 1\na := struct{x int; y string}{1}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:33: cannot use x (type int) as type string in value of field y`, `\nx := 1\na := struct{x int; y string}{1, x}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:30: z undefined (type struct{x int; y string} has no field or method z)`, `\na := struct{x int; y string}{z: 1}\n`)\n}\n\nfunc TestErrArray(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:8: non-constant array bound n`, `\nvar n int\nvar a [n]int\n`)\n}\n\nfunc TestErrArrayLit(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:14: cannot use a as index which must be non-negative integer constant`,\n\t\t`\na := \"Hi\"\nb := [10]int{a: 1}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:20: array index 10 out of bounds [0:10]`,\n\t\t`\na := \"Hi\"\nb := [10]int{9: 1, 3}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:16: array index 1 out of bounds [0:1]`,\n\t\t`\na := \"Hi\"\nb := [1]int{1, 2}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:14: array index 12 (value 12) out of bounds [0:10]`,\n\t\t`\na := \"Hi\"\nb := [10]int{12: 2}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:14: cannot use a+\"!\" (type string) as type int in array literal`,\n\t\t`\na := \"Hi\"\nb := [10]int{a+\"!\"}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:17: cannot use a (type string) as type int in array literal`,\n\t\t`\na := \"Hi\"\nb := [10]int{2: a}\n`)\n}\n\nfunc TestErrSliceLit(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:12: cannot use a as index which must be non-negative integer constant`,\n\t\t`\na := \"Hi\"\nb := []int{a: 1}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:12: cannot use a (type string) as type int in slice literal`,\n\t\t`\na := \"Hi\"\nb := []int{a}\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:15: cannot use a (type string) as type int in slice literal`,\n\t\t`\na := \"Hi\"\nb := []int{2: a}\n`)\n}\n\nfunc TestErrMapLit(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:4:6: cannot use 1 (type untyped int) as type string in map key`, `\nfunc foo(map[string]string) {}\n\nfoo {1: 2}\n`)\n\tcodeErrorTest(t, `bar.xgo:2:1: invalid composite literal type int`, `\nint{2}\n`)\n\tcodeErrorTest(t, `bar.xgo:2:1: missing key in map literal`, `\nmap[string]int{2}\n`)\n\tcodeErrorTest(t, `bar.xgo:2:21: cannot use 1+2 (type untyped int) as type string in map key\nbar.xgo:3:27: cannot use \"Go\" + \"+\" (type untyped string) as type int in map value`,\n\t\t`\na := map[string]int{1+2: 2}\nb := map[string]int{\"Hi\": \"Go\" + \"+\"}\n`)\n\tcodeErrorTest(t, `bar.xgo:2:13: invalid map literal`, `\nvar v any = {1:2,1}\n`)\n\tcodeErrorTest(t, `bar.xgo:2:21: invalid map literal`, `\nvar v map[int]int = {1:2,1}\n`)\n}\n\nfunc TestErrSlice(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:4:6: cannot slice a (type *byte)`,\n\t\t`\nvar a *byte\nx := 1\nb := a[x:2]\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:6: cannot slice a (type bool)`,\n\t\t`\na := true\nb := a[1:2]\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:6: invalid operation a[1:2:5] (3-index slice of string)`,\n\t\t`\na := \"Hi\"\nb := a[1:2:5]\n`)\n}\n\nfunc TestErrIndex(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:10: assignment mismatch: 2 variables but 1 values`,\n\t\t`\na := \"Hi\"\nb, ok := a[1]\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:6: invalid operation: a[1] (type bool does not support indexing)`,\n\t\t`\na := true\nb := a[1]\n`)\n}\n\nfunc TestErrIndexRef(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:1: cannot assign to a[1] (strings are immutable)`,\n\t\t`\na := \"Hi\"\na[1] = 'e'\n`)\n}\n\nfunc TestErrStar(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:2: invalid indirect of a (type string)`,\n\t\t`\na := \"Hi\"\n*a = 'e'\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:7: invalid indirect of a (type string)`,\n\t\t`\na := \"Hi\"\nb := *a\n`)\n}\n\nfunc TestErrCondExpr(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:5:6: assignment mismatch: 2 variables but self.XGo_first returns 1 values\ndon't call End(), please use EndInit() instead`, `\nimport \"github.com/goplus/xgo/cl/internal/dql\"\n\ndoc := dql.new2\necho doc.users@($age < 18).$name\n`)\n}\n\nfunc TestErrMember(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:5:6: a.* undefined (type interface{Read(p []byte) (n int, err error)} has no field or method XGo_Child)`,\n\t\t`\nvar a interface {\n\tRead(p []byte) (n int, err error)\n}\nb := a.*.$x\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:6: a.$x undefined (type string has no field or method XGo_Attr)`,\n\t\t`\na := \"Hello\"\nb := a.$x\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:6: a.x undefined (type string has no field or method x)`,\n\t\t`\na := \"Hello\"\nb := a.x\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:6: a.1 undefined (type string has no field or method 1)`,\n\t\t`\na := \"Hello\"\nb := a.1\n`)\n}\n\nfunc TestErrMemberRef(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:1: a.x undefined (type string has no field or method x)`,\n\t\t`\na := \"Hello\"\na.x = 1\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:5:1: a.x undefined (type aaa has no field or method x)`,\n\t\t`\ntype aaa byte\n\na := aaa(0)\na.x = 1\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:5:1: a.z undefined (type aaa has no field or method z)`,\n\t\t`\ntype aaa struct {x int; y string}\n\na := aaa{}\na.z = 1\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:3:1: a.z undefined (type struct{x int; y string} has no field or method z)`,\n\t\t`\na := struct{x int; y string}{}\na.z = 1\n`)\n}\n\nfunc TestErrLabel(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:4:1: label foo already defined at bar.xgo:2:1\nbar.xgo:2:1: label foo defined and not used`,\n\t\t`x := 1\nfoo:\n\ti := 1\nfoo:\n\ti++\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:6: label foo is not defined`,\n\t\t`x := 1\ngoto foo`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:7: label foo is not defined`,\n\t\t`x := 1\nbreak foo`)\n}\n\nfunc TestErrBranchStmt(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t`bar.xgo:2:2: fallthrough statement out of place`,\n\t\t`func foo() {\n\tfallthrough\n}`)\n}\n\nfunc TestErrNoEntrypoint(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:2: undefined: println1\", `func main() {\n\tprintln1 \"hello\"\n}\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:1:1: undefined: println1\", `println1 \"hello\"`)\n\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:2: undefined: println1\", `\n\tprintln1 \"hello\"\n`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:2:2: undefined: println1\", `package main\n\tprintln1 \"hello\"\n`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:1:9: undefined: abc`,\n\t\t`println abc\n`)\n\tcodeErrorTestEx(t, \"bar\", \"bar.xgo\",\n\t\t`bar.xgo:2:9: undefined: abc`,\n\t\t`package bar\nprintln abc\n`)\n}\n\nfunc TestErrTypeRedefine(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:9:6: Point redeclared in this block\\n\\tprevious declaration at bar.xgo:5:6\",\n\t\t`import \"fmt\"\nfunc (p *Point) String() string {\n\treturn fmt.Sprintf(\"%v-%v\",p.X,p.Y)\n}\ntype Point struct {\n\tX int\n\tY int\n}\ntype Point struct {\n\tX int\n\tY int\n}\n`)\n}\n\nfunc TestErrSwitchDuplicate(t *testing.T) {\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:7: duplicate case 100 in switch\\n\\tprevious case at bar.xgo:3:7\",\n\t\t`var n int\nswitch n {\n\tcase 100:\n\tcase 100:\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:7: duplicate case int(100) (value 100) in switch\\n\\tprevious case at bar.xgo:3:7\",\n\t\t`var n int\nswitch n {\n\tcase 100:\n\tcase int(100):\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:7: duplicate case 50 + 50 (value 100) in switch\\n\\tprevious case at bar.xgo:3:7\",\n\t\t`var n int\nswitch n {\n\tcase 100:\n\tcase 50 + 50:\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:5:7: duplicate case int(100) (value 100) in switch\\n\\tprevious case at bar.xgo:3:7\",\n\t\t`var n interface{}\nswitch n {\n\tcase 100:\n\tcase uint(100):\n\tcase int(100):\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:7: duplicate case 100.0 in switch\\n\\tprevious case at bar.xgo:3:7\",\n\t\t`var n interface{}\nswitch n {\n\tcase 100.0:\n\tcase 100.0:\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:5:7: duplicate case v (value 100) in switch\\n\\tprevious case at bar.xgo:4:7\",\n\t\t`var n interface{}\nconst v = 100.0\nswitch n {\n\tcase 100.0:\n\tcase v:\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:5:7: duplicate case v (value \\\"hello\\\") in switch\\n\\tprevious case at bar.xgo:4:7\",\n\t\t`var n interface{}\nconst v = \"hello\"\nswitch n {\n\tcase \"hello\":\n\tcase v:\n}`)\n\tcodeErrorTest(t,\n\t\t`bar.xgo:4:7: duplicate case 100 in switch\n\tprevious case at bar.xgo:3:7\nbar.xgo:5:7: duplicate case 50 + 50 (value 100) in switch\n\tprevious case at bar.xgo:3:7`,\n\t\t`var n int\nswitch n {\n\tcase 100:\n\tcase 100:\n\tcase 50 + 50:\n}`)\n\tcodeErrorTest(t,\n\t\t\"bar.xgo:4:2: multiple defaults in switch (first at bar.xgo:3:2)\",\n\t\t`var n interface{}\nswitch n {\n\tdefault:\n\tdefault:\n}`)\n\tcodeErrorTest(t, `bar.xgo:4:2: multiple defaults in switch (first at bar.xgo:3:2)\nbar.xgo:5:2: multiple defaults in switch (first at bar.xgo:3:2)`,\n\t\t`var n interface{}\nswitch n {\n\tdefault:\n\tdefault:\n\tdefault:\n}`)\n}\n\nfunc TestErrTypeSwitchDuplicate(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:4:7: duplicate case int in type switch\n\tprevious case at bar.xgo:3:7\nbar.xgo:5:7: duplicate case int in type switch\n\tprevious case at bar.xgo:3:7`,\n\t\t`var n interface{} = 100\nswitch n.(type) {\n\tcase int:\n\tcase int:\n\tcase int:\n}\n`)\n\tcodeErrorTest(t, `bar.xgo:4:7: multiple nil cases in type switch (first at bar.xgo:3:7)\nbar.xgo:5:7: multiple nil cases in type switch (first at bar.xgo:3:7)`,\n\t\t`var n interface{} = 100\nswitch n.(type) {\n\tcase nil:\n\tcase nil:\n\tcase nil:\n}\n`)\n\tcodeErrorTest(t, `bar.xgo:4:2: multiple defaults in type switch (first at bar.xgo:3:2)\nbar.xgo:5:2: multiple defaults in type switch (first at bar.xgo:3:2)`,\n\t\t`var n interface{} = 100\nswitch n.(type) {\n\tdefault:\n\tdefault:\n\tdefault:\n}\n`)\n}\n\nfunc TestErrAutoProperty(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:4:11: cannot refer to unexported name fmt.println`, `\nimport \"fmt\"\n\nn, err := fmt.println\n`)\n}\n\nfunc TestFiledsNameRedecl(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:6:2: Id redeclared\n\tbar.xgo:5:2 other declaration of Id\nbar.xgo:7:2: Id redeclared\n\tbar.xgo:5:2 other declaration of Id\nbar.xgo:9:2: name redeclared\n\tbar.xgo:8:2 other declaration of name`, `\ntype Id struct {\n}\ntype A struct {\n\tId   int\n\tId   string\n\tId\n\tname string\n\tname string\n}\n`)\n}\n\nfunc TestErrImportPkg(t *testing.T) {\n\troot := filepath.Join(runtime.GOROOT(), \"src\", \"fmt2\")\n\twhere := \"GOROOT\"\n\tver := runtime.Version()[:6]\n\tif ver >= \"go1.21\" {\n\t\twhere = \"std\"\n\t}\n\tcodeErrorTest(t,\n\t\tfmt.Sprintf(`bar.xgo:3:2: package fmt2 is not in `+where+` (%v)\n`, root), `\nimport (\n\t\"fmt2\"\n)\n`)\n\n\tcodeErrorTest(t, `bar.xgo:3:2: no required module provides package github.com/goplus/xgo/fmt2; to add it:\n\tgo get github.com/goplus/xgo/fmt2\n`, `\nimport (\n\t\"github.com/goplus/xgo/fmt2\"\n)\n`)\n}\n\nfunc TestErrClassFileGopx(t *testing.T) {\n\tcodeErrorTestEx(t, \"main\", \"Rect.gox\",\n\t\t`Rect.gox:5:2: A redeclared\n\tRect.gox:3:2 other declaration of A`, `\nvar (\n\tA\n\ti int\n\tA\n)\ntype A struct{}\nprintln \"hello\"\n`)\n}\n\nfunc TestErrVarInFunc(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:6:10: not enough arguments in call to set\n\thave (untyped string)\n\twant (name string, v int)\nbar.xgo:7:10: undefined: a`, `\nfunc set(name string, v int) string {\n\treturn name\n}\nfunc test() {\n\tvar a = set(\"box\")\n\tprintln(a)\n}\n`)\n}\n\nfunc TestErrInt128(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:2:16: cannot use 1<<127 (type untyped int) as type github.com/qiniu/x/xgo/ng.Int128 in assignment`, `\nvar a int128 = 1<<127\n`)\n\tcodeErrorTest(t, `bar.xgo:2:13: cannot convert 1<<127 (untyped int constant 170141183460469231731687303715884105728) to type Int128`, `\na := int128(1<<127)\n`)\n\tcodeErrorTest(t, `bar.xgo:2:13: cannot convert -1<<127-1 (untyped int constant -170141183460469231731687303715884105729) to type Int128`, `\na := int128(-1<<127-1)\n`)\n\tcodeErrorTest(t, `bar.xgo:3:13: cannot convert b (untyped int constant -170141183460469231731687303715884105729) to type Int128`, `\nconst b = -1<<127-1\na := int128(b)\n`)\n}\n\nfunc TestErrUint128(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:2:17: cannot use 1<<128 (type untyped int) as type github.com/qiniu/x/xgo/ng.Uint128 in assignment`, `\nvar a uint128 = 1<<128\n`)\n\tcodeErrorTest(t, `bar.xgo:2:14: cannot convert 1<<128 (untyped int constant 340282366920938463463374607431768211456) to type Uint128`, `\na := uint128(1<<128)\n`)\n\tcodeErrorTest(t, `bar.xgo:2:17: cannot use -1 (type untyped int) as type github.com/qiniu/x/xgo/ng.Uint128 in assignment`, `\nvar a uint128 = -1\n`)\n\tcodeErrorTest(t, `bar.xgo:2:14: cannot convert -1 (untyped int constant -1) to type Uint128`, `\na := uint128(-1)\n`)\n\tcodeErrorTest(t, `bar.xgo:3:14: cannot convert b (untyped int constant -1) to type Uint128`, `\nconst b = -1\na := uint128(b)\n`)\n}\n\nfunc TestErrCompileFunc(t *testing.T) {\n\tcodeErrorTest(t, \"bar.xgo:2:1: compile `printf(\\\"%+v\\\\n\\\", int32)`: unreachable\", `\nprintf(\"%+v\\n\", int32)\n`)\n}\n\nfunc TestToTypeError(t *testing.T) {\n\tcodeErrorTestAst(t, \"main\", \"bar.xgo\", `bar.xgo:3:3: toType unexpected: *ast.BadExpr`, `\ntype\na := 1\n`)\n}\n\nfunc TestCompileExprError(t *testing.T) {\n\tcodeErrorTestAst(t, \"main\", \"bar.go\", `bar.go:5:1: compileExpr failed: unknown - *ast.BadExpr`, `\nfunc Foo(){}\nfunc _() {\n\tFoo(\n}\n`)\n\tcodeErrorTestAst(t, \"main\", \"bar.go\", `bar.go:3:2: compileExprLHS failed: unknown - *ast.StructType`, `\nfunc _() {\n\tstruct() = nil\n}\n`)\n}\n\nfunc TestOverloadFuncDecl(t *testing.T) {\n\tcodeErrorTest(t, \"bar.xgo:3:2: invalid func (foo).mulInt\", `\nfunc mul = (\n\t(foo).mulInt\n)\n`)\n\tcodeErrorTest(t, \"bar.xgo:2:7: invalid recv type *foo\", `\nfunc (*foo).mul = (\n\t(foo).mulInt\n)\n`)\n\tcodeErrorTest(t, \"bar.xgo:3:2: invalid recv type (foo2)\", `\nfunc (foo).mul = (\n\t(foo2).mulInt\n)\n`)\n\tcodeErrorTest(t, \"bar.xgo:3:2: invalid method mulInt\", `\nfunc (foo).mul = (\n\tmulInt\n)\n`)\n\tcodeErrorTest(t, \"bar.xgo:3:2: invalid recv type (**foo)\", `\nfunc (foo).mul = (\n\t(**foo).mulInt\n)\n`)\n\tcodeErrorTest(t, `bar.xgo:3:9: unknown func (\"ok\")`, `\nfunc mul = (\n\tprintln(\"ok\")\n)\n`)\n\tcodeErrorTest(t, \"bar.xgo:3:2: invalid method func(){}\", `\nfunc (foo).mul = (\n\tfunc(){}\n)\n`)\n\tcodeErrorTest(t, \"bar.xgo:2:12: invalid overload operator ++\", `\nfunc (foo).++ = (\n\tmulInt\n)\n`)\n}\n\nfunc TestCompositeLitError(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:2:22: cannot use 3.14 (type untyped float) as type int in slice literal`, `\nvar a [][]int = {[10,3.14,200],[100,200]}\necho a\n`)\n\tcodeErrorTest(t, `bar.xgo:2:17: cannot use lambda literal as type int in assignment`, `\nvar a []int = {(x => x)}\necho a\n`)\n\tcodeErrorTest(t, `bar.xgo:2:35: cannot use x (type int) as type string in return argument`, `\nvar a []func(int) string = {(x => x)}\necho a\n`)\n\tcodeErrorTest(t, `bar.xgo:2:27: cannot use lambda literal as type int in assignment to \"A\"`, `\nvar a map[any]int = {\"A\": x => x}\n`)\n\tcodeErrorTest(t, `bar.xgo:2:45: cannot use x (type int) as type string in return argument`, `\nvar a map[any]func(int) string = {\"A\": x => x}\n`)\n\tcodeErrorTest(t, `bar.xgo:2:24: cannot use lambda literal as type int in field value`, `\nvar a = struct{v int}{(x => x)}\n`)\n\tcodeErrorTest(t, `bar.xgo:2:27: cannot use lambda literal as type int in field value to v`, `\nvar a = struct{v int}{v: (x => x)}\n`)\n}\n"
  },
  {
    "path": "cl/expr.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\tgoast \"go/ast\"\n\tgotoken \"go/token\"\n\t\"go/types\"\n\t\"log\"\n\t\"math/big\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/printer\"\n\t\"github.com/goplus/xgo/token\"\n\ttpl \"github.com/goplus/xgo/tpl/ast\"\n\t\"github.com/qiniu/x/stringutil\"\n)\n\n/*-----------------------------------------------------------------------------\n\nName context:\n- varVal               (ident)\n- varRef = expr        (identLHS)\n- pkgRef.member        (selectorExpr)\n- pkgRef.member = expr (selectorExprLHS)\n- pkgRef.fn(args)      (callExpr)\n- fn(args)             (callExpr)\n- spx.fn(args)         (callExpr)\n- this.member          (classMember)\n- this.method(args)    (classMember)\n\nName lookup:\n- local variables\n- $recv members (only in class files)\n- package globals (variables, constants, types, imported packages etc.)\n- $spx package exports (only in class files)\n- $universe package exports (including builtins)\n\n// ---------------------------------------------------------------------------*/\n\nconst (\n\tclIdentCanAutoCall = 1 << iota // allow auto property\n\tclIdentAllowBuiltin\n\tclIdentLHS\n\tclIdentSelectorExpr // this ident is X (not Sel) of ast.SelectorExpr\n\tclIdentGoto\n\tclCommandWithoutArgs // this expr is a command without args (eg. ls)\n\tclCommandIdent       // this expr is a command and an ident (eg. mkdir \"abc\")\n\tclIdentInStringLitEx // this expr is an ident in a string extended literal (eg. ${PATH})\n\tclInCallExpr\n)\n\nconst (\n\tobjNormal = iota\n\tobjPkgRef\n\tobjXGoExecOrEnv\n\n\tobjXGoEnv  = objXGoExecOrEnv\n\tobjXGoExec = objXGoExecOrEnv\n)\n\nfunc compileIdent(ctx *blockCtx, lhs int, ident *ast.Ident, flags int) (pkg gogen.PkgRef, kind int) {\n\tfvalue := (flags&clIdentSelectorExpr) != 0 || (flags&clIdentLHS) == 0\n\tcb := ctx.cb\n\tname := ident.Name\n\tif name == \"_\" {\n\t\tif fvalue {\n\t\t\tpanic(ctx.newCodeError(ident.Pos(), ident.End(), \"cannot use _ as value\"))\n\t\t}\n\t\tcb.VarRef(nil)\n\t\treturn\n\t}\n\n\tvar recv *types.Var\n\tvar oldo types.Object\n\tscope := ctx.pkg.Types.Scope()\n\tat, o := cb.Scope().LookupParent(name, token.NoPos)\n\tif o != nil {\n\t\tif at != scope && at != types.Universe { // local object\n\t\t\tgoto find\n\t\t}\n\t}\n\n\tif ctx.isClass { // in an XGo class file\n\t\tif recv = classRecv(cb); recv != nil {\n\t\t\tcb.Val(recv)\n\t\t\tchkFlag := flags\n\t\t\tif chkFlag&clIdentSelectorExpr != 0 { // TODO(xsw): remove this condition\n\t\t\t\tchkFlag = clIdentCanAutoCall\n\t\t\t}\n\t\t\tif compileMember(ctx, lhs, ident, name, chkFlag) == nil { // class member object\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcb.InternalStack().PopN(1)\n\t\t}\n\t}\n\n\t// global object\n\tif ctx.loadSymbol(name) {\n\t\to, at = scope.Lookup(name), scope\n\t}\n\tif o != nil && at != types.Universe {\n\t\tgoto find\n\t}\n\n\t// pkgRef object\n\tif (flags & clIdentSelectorExpr) != 0 {\n\t\tif pi, ok := ctx.findImport(name); ok {\n\t\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\t\trec.Use(ident, pi.pkgName)\n\t\t\t}\n\t\t\treturn pi.PkgRef, objPkgRef\n\t\t}\n\t}\n\n\t// function alias\n\tif compileFuncAlias(ctx, lhs, scope, ident, flags) {\n\t\treturn\n\t}\n\n\t// object from import . \"xxx\"\n\tif compilePkgRef(ctx, lhs, gogen.PkgRef{}, ident, flags, objPkgRef) {\n\t\treturn\n\t}\n\n\t// universe object\n\tif obj := ctx.pkg.Builtin().TryRef(name); obj != nil {\n\t\tif (flags&clIdentAllowBuiltin) == 0 && isBuiltin(o) && !strings.HasPrefix(o.Name(), \"print\") {\n\t\t\tpanic(ctx.newCodeErrorf(ident.Pos(), ident.End(), \"use of builtin %s not in function call\", name))\n\t\t}\n\t\toldo, o = o, obj\n\t} else if o == nil {\n\t\t// for support XGo_Exec, see TestSpxXGoExec\n\t\tif (clCommandIdent&flags) != 0 && recv != nil && xgoOp(cb, recv, \"XGo_Exec\", \"Gop_Exec\", ident) == nil {\n\t\t\tkind = objXGoExec\n\t\t\treturn\n\t\t}\n\t\t// for support XGo_Env, see TestSpxGopEnv\n\t\tif (clIdentInStringLitEx&flags) != 0 && recv != nil && xgoOp(cb, recv, \"XGo_Env\", \"Gop_Env\", ident) == nil {\n\t\t\tkind = objXGoEnv\n\t\t\treturn\n\t\t}\n\t\tif (clIdentGoto & flags) != 0 {\n\t\t\tl := ident.Obj.Data.(*ast.Ident)\n\t\t\tpanic(ctx.newCodeErrorf(l.Pos(), l.End(), \"label %v is not defined\", l.Name))\n\t\t}\n\t\tpanic(ctx.newCodeErrorf(ident.Pos(), ident.End(), \"undefined: %s\", name))\n\t}\n\nfind:\n\tif fvalue {\n\t\tcb.Val(o, ident)\n\t} else {\n\t\tcb.VarRef(o, ident)\n\t}\n\tif rec := ctx.recorder(); rec != nil {\n\t\te := cb.Get(-1)\n\t\tif oldo != nil && gogen.IsTypeEx(e.Type) { // for builtin object\n\t\t\trec.recordIdent(ident, oldo)\n\t\t\treturn\n\t\t}\n\t\trec.recordIdent(ident, o)\n\t}\n\treturn\n}\n\n/*\nfunc compileMatrixLit(ctx *blockCtx, v *ast.MatrixLit) {\n\tcb := ctx.cb\n\tncol := -1\n\tfor _, elts := range v.Elts {\n\t\tswitch n := len(elts); n {\n\t\tcase 1:\n\t\t\telt := elts[0]\n\t\t\tif e, ok := elt.(*ast.Ellipsis); ok {\n\t\t\t\tcompileExpr(ctx, e.Elt)\n\t\t\t\tpanic(\"TODO\") // TODO(xsw): matrixLit with ellipsis\n\t\t\t}\n\t\t\tfallthrough\n\t\tdefault:\n\t\t\tif ncol < 0 {\n\t\t\t\tncol = n\n\t\t\t} else if ncol != n {\n\t\t\t\tctx.handleErrorf(elts[0].Pos(), \"inconsistent matrix column count: got %v, want %v\", n, ncol)\n\t\t\t}\n\t\t\tfor _, elt := range elts {\n\t\t\t\tcompileExpr(ctx, elt)\n\t\t\t}\n\t\t\tcb.SliceLitEx(...)\n\t\t}\n\t}\n}\n*/\n\nfunc compileEnvExpr(ctx *blockCtx, lhs int, v *ast.EnvExpr) {\n\tcb := ctx.cb\n\tif _, self := cb.Scope().LookupParent(\"self\", 0); self != nil { // self.$attr\n\t\tname := v.Name\n\t\tcb.Val(self, v) // push self\n\t\tif compileAttr(cb, lhs, name.Name, name) == nil {\n\t\t\treturn\n\t\t}\n\t\tcb.InternalStack().PopN(1) // pop self if failed\n\t}\n\tif ctx.isClass { // in an XGo class file\n\t\tif recv := classRecv(cb); recv != nil {\n\t\t\tif xgoOp(cb, recv, \"XGo_Env\", \"Gop_Env\", v) == nil {\n\t\t\t\tname := v.Name\n\t\t\t\tcb.Val(name.Name, name).CallWith(1, lhs, 0, v)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\tinvalidVal(cb)\n\tctx.handleErrorf(v.Pos(), v.End(), \"operator $%v undefined\", v.Name)\n}\n\nfunc classRecv(cb *gogen.CodeBuilder) *types.Var {\n\tif fn := cb.Func(); fn != nil {\n\t\tsig := fn.Ancestor().Type().(*types.Signature)\n\t\treturn sig.Recv()\n\t}\n\treturn nil\n}\n\nfunc xgoOp(cb *gogen.CodeBuilder, recv *types.Var, op1, op2 string, src ...ast.Node) error {\n\tcb.Val(recv)\n\tkind, e := cb.Member(op1, 0, gogen.MemberFlagVal, src...)\n\tif kind == gogen.MemberInvalid {\n\t\tif _, e = cb.Member(op2, 0, gogen.MemberFlagVal, src...); e != nil {\n\t\t\tcb.InternalStack().PopN(1) // pop recv\n\t\t}\n\t}\n\treturn e\n}\n\nfunc isBuiltin(o types.Object) bool {\n\tif _, ok := o.(*types.Builtin); ok {\n\t\treturn ok\n\t}\n\treturn false\n}\n\nfunc compileMember(ctx *blockCtx, lhs int, v ast.Node, name string, flags int) error {\n\tvar mflag gogen.MemberFlag\n\tswitch {\n\tcase (flags & clIdentLHS) != 0:\n\t\tmflag = gogen.MemberFlagRef\n\tcase (flags & clIdentCanAutoCall) != 0:\n\t\tmflag = gogen.MemberFlagAutoProperty\n\tdefault:\n\t\tmflag = gogen.MemberFlagMethodAlias\n\t}\n\t_, err := ctx.cb.Member(name, lhs, mflag, v)\n\treturn err\n}\n\nfunc compileExprLHS(ctx *blockCtx, expr ast.Expr) {\n\tswitch v := expr.(type) {\n\tcase *ast.Ident:\n\t\tcompileIdent(ctx, 1, v, clIdentLHS)\n\tcase *ast.IndexExpr:\n\t\tcompileIndexExprLHS(ctx, v)\n\tcase *ast.SelectorExpr:\n\t\tcompileSelectorExprLHS(ctx, v)\n\tcase *ast.StarExpr:\n\t\tcompileStarExprLHS(ctx, v)\n\tdefault:\n\t\tpanic(ctx.newCodeErrorf(v.Pos(), v.End(), \"compileExprLHS failed: unknown - %T\", expr))\n\t}\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.recordExpr(ctx, expr, true)\n\t}\n}\n\nfunc identOrSelectorFlags(inFlags []int) (flags int, cmdNoArgs bool) {\n\tif inFlags == nil {\n\t\treturn clIdentCanAutoCall, false\n\t}\n\tflags = inFlags[0]\n\tif flags&clInCallExpr != 0 {\n\t\treturn\n\t}\n\tif cmdNoArgs = (flags & clCommandWithoutArgs) != 0; cmdNoArgs {\n\t\tflags &^= clCommandWithoutArgs\n\t} else {\n\t\tflags |= clIdentCanAutoCall\n\t}\n\treturn\n}\n\nfunc callCmdNoArgs(ctx *blockCtx, src ast.Node, panicErr bool) (err error) {\n\tif gogen.IsFunc(ctx.cb.InternalStack().Get(-1).Type) {\n\t\tif err = ctx.cb.CallWithEx(0, 0, 0, src); err != nil {\n\t\t\tif panicErr {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// compileExpr compiles expr.\n// lhs indicates how many values are expected on the left-hand side.\nfunc compileExpr(ctx *blockCtx, lhs int, expr ast.Expr, inFlags ...int) {\n\tswitch v := expr.(type) {\n\tcase *ast.Ident:\n\t\tflags, cmdNoArgs := identOrSelectorFlags(inFlags)\n\t\tif cmdNoArgs {\n\t\t\tflags |= clCommandIdent // for support XGo_Exec, see TestSpxXGoExec\n\t\t}\n\t\t_, kind := compileIdent(ctx, lhs, v, flags)\n\t\tif cmdNoArgs || kind == objXGoExecOrEnv {\n\t\t\tcb := ctx.cb\n\t\t\tif kind == objXGoExecOrEnv {\n\t\t\t\tcb.Val(v.Name, v)\n\t\t\t} else {\n\t\t\t\terr := callCmdNoArgs(ctx, expr, false)\n\t\t\t\tif err == nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !(ctx.isClass && tryXGoExec(cb, v)) {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tcb.CallWith(1, 0, 0, v)\n\t\t}\n\tcase *ast.BasicLit:\n\t\tcompileBasicLit(ctx, v)\n\tcase *ast.CallExpr:\n\t\tflags := 0\n\t\tif inFlags != nil {\n\t\t\tflags = inFlags[0]\n\t\t}\n\t\tcompileCallExpr(ctx, lhs, v, flags)\n\tcase *ast.SelectorExpr:\n\t\tflags, cmdNoArgs := identOrSelectorFlags(inFlags)\n\t\tcompileSelectorExpr(ctx, lhs, v, flags)\n\t\tif cmdNoArgs {\n\t\t\tcallCmdNoArgs(ctx, expr, true)\n\t\t\treturn\n\t\t}\n\tcase *ast.BinaryExpr:\n\t\tcompileBinaryExpr(ctx, v)\n\tcase *ast.UnaryExpr:\n\t\tcompileUnaryExpr(ctx, lhs, v)\n\tcase *ast.FuncLit:\n\t\tcompileFuncLit(ctx, v)\n\tcase *ast.CompositeLit:\n\t\tcompileCompositeLit(ctx, v, nil, false)\n\tcase *ast.TupleLit:\n\t\tcompileTupleLit(ctx, v, nil)\n\tcase *ast.SliceLit:\n\t\tcompileSliceLit(ctx, v, nil)\n\tcase *ast.RangeExpr:\n\t\tcompileRangeExpr(ctx, v)\n\tcase *ast.IndexExpr:\n\t\tcompileIndexExpr(ctx, lhs, v, inFlags...)\n\tcase *ast.IndexListExpr:\n\t\tcompileIndexListExpr(ctx, lhs, v, inFlags...)\n\tcase *ast.SliceExpr:\n\t\tcompileSliceExpr(ctx, v)\n\tcase *ast.StarExpr:\n\t\tcompileStarExpr(ctx, v)\n\tcase *ast.ArrayType:\n\t\tctx.cb.Typ(toArrayType(ctx, v), v)\n\tcase *ast.MapType:\n\t\tctx.cb.Typ(toMapType(ctx, v), v)\n\tcase *ast.StructType:\n\t\tctx.cb.Typ(toStructType(ctx, v), v)\n\tcase *ast.ChanType:\n\t\tctx.cb.Typ(toChanType(ctx, v), v)\n\tcase *ast.InterfaceType:\n\t\tctx.cb.Typ(toInterfaceType(ctx, v), v)\n\tcase *ast.ComprehensionExpr:\n\t\tcompileComprehensionExpr(ctx, lhs, v)\n\tcase *ast.TypeAssertExpr:\n\t\tcompileTypeAssertExpr(ctx, lhs, v)\n\tcase *ast.ParenExpr:\n\t\tcompileExpr(ctx, lhs, v.X, inFlags...)\n\tcase *ast.ErrWrapExpr:\n\t\tcompileErrWrapExpr(ctx, lhs, v, 0)\n\tcase *ast.FuncType:\n\t\tctx.cb.Typ(toFuncType(ctx, v, nil, nil), v)\n\tcase *ast.EnvExpr:\n\t\tcompileEnvExpr(ctx, lhs, v)\n\t/* case *ast.MatrixLit:\n\tcompileMatrixLit(ctx, v) */\n\tcase *ast.DomainTextLit:\n\t\tcompileDomainTextLit(ctx, v)\n\tcase *ast.AnySelectorExpr:\n\t\tcompileAnySelectorExpr(ctx, lhs, v)\n\tcase *ast.CondExpr:\n\t\tcompileCondExpr(ctx, v)\n\tdefault:\n\t\tpanic(ctx.newCodeErrorf(v.Pos(), v.End(), \"compileExpr failed: unknown - %T\", v))\n\t}\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.recordExpr(ctx, expr, false)\n\t}\n}\n\nfunc compileExprOrNone(ctx *blockCtx, expr ast.Expr) {\n\tif expr != nil {\n\t\tcompileExpr(ctx, 1, expr)\n\t} else {\n\t\tctx.cb.None()\n\t}\n}\n\nfunc compileUnaryExpr(ctx *blockCtx, lhs int, v *ast.UnaryExpr) {\n\tcompileExpr(ctx, 1, v.X)\n\tctx.cb.UnaryOpEx(gotoken.Token(v.Op), lhs, v)\n}\n\nfunc compileBinaryExpr(ctx *blockCtx, v *ast.BinaryExpr) {\n\tcompileExpr(ctx, 1, v.X)\n\tcompileExpr(ctx, 1, v.Y)\n\tctx.cb.BinaryOp(gotoken.Token(v.Op), v)\n}\n\nfunc compileIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr) {\n\tcompileExpr(ctx, 1, v.X)\n\tcompileExpr(ctx, 1, v.Index)\n\tctx.cb.IndexRef(1, v)\n}\n\nfunc compileStarExprLHS(ctx *blockCtx, v *ast.StarExpr) { // *x = ...\n\tcompileExpr(ctx, 1, v.X)\n\tctx.cb.ElemRef()\n}\n\nfunc compileStarExpr(ctx *blockCtx, v *ast.StarExpr) { // ... = *x\n\tcompileExpr(ctx, 1, v.X)\n\tctx.cb.Star(v)\n}\n\nfunc compileTypeAssertExpr(ctx *blockCtx, lhs int, v *ast.TypeAssertExpr) {\n\tcompileExpr(ctx, 1, v.X)\n\tif v.Type == nil {\n\t\tpanic(\"TODO: x.(type) is only used in type switch\")\n\t}\n\ttyp := toType(ctx, v.Type)\n\tctx.cb.TypeAssert(typ, lhs, v)\n}\n\nfunc compileIndexExpr(ctx *blockCtx, lhs int, v *ast.IndexExpr, inFlags ...int) { // x[i]\n\tcompileExpr(ctx, 1, v.X, inFlags...)\n\tcompileExpr(ctx, 1, v.Index)\n\tctx.cb.Index(1, lhs, v)\n}\n\nfunc compileIndexListExpr(ctx *blockCtx, lhs int, v *ast.IndexListExpr, inFlags ...int) { // fn[t1,t2]\n\tcompileExpr(ctx, 1, v.X, inFlags...)\n\tn := len(v.Indices)\n\tfor i := 0; i < n; i++ {\n\t\tcompileExpr(ctx, 1, v.Indices[i])\n\t}\n\tctx.cb.Index(n, lhs, v)\n}\n\nfunc compileSliceExpr(ctx *blockCtx, v *ast.SliceExpr) { // x[i:j:k]\n\tcompileExpr(ctx, 1, v.X)\n\tcompileExprOrNone(ctx, v.Low)\n\tcompileExprOrNone(ctx, v.High)\n\tif v.Slice3 {\n\t\tcompileExprOrNone(ctx, v.Max)\n\t}\n\tctx.cb.Slice(v.Slice3, v)\n}\n\nfunc compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr) {\n\tswitch x := v.X.(type) {\n\tcase *ast.Ident:\n\t\tif at, kind := compileIdent(ctx, 1, x, clIdentLHS|clIdentSelectorExpr); kind != objNormal {\n\t\t\tctx.cb.VarRef(at.Ref(v.Sel.Name))\n\t\t\treturn\n\t\t}\n\tdefault:\n\t\tcompileExpr(ctx, 1, v.X)\n\t}\n\tctx.cb.MemberRef(v.Sel.Name, v)\n}\n\nfunc compileCondExpr(ctx *blockCtx, v *ast.CondExpr) {\n\tconst (\n\t\tnameVal = \"_xgo_val\"\n\t\tnameErr = \"_xgo_err\"\n\t)\n\txExpr := v.X\n\tcondExpr := v.Cond\n\tcb := ctx.cb\n\tcompileExpr(ctx, 1, xExpr)\n\tif id, ok := condExpr.(*ast.Ident); ok {\n\t\tname := id.Name\n\t\tswitch name[0] {\n\t\tcase '\"', '`': // @\"elem-name\"\n\t\t\tname = unquote(name)\n\t\t}\n\t\tcb.MemberVal(\"XGo_Select\", 0, v).Val(name, id).CallWith(1, 1, 0, v)\n\t\treturn\n\t}\n\tpkg := ctx.pkg\n\tx := cb.Get(-1) // x.Type is NodeSet\n\tnsType := x.Type\n\tpkgTypes := pkg.Types\n\tcb.MemberVal(\"XGo_Enum\", 0, xExpr).CallWith(0, 1, 0, xExpr)\n\tvarSelf := types.NewParam(0, pkgTypes, \"self\", nsType)\n\tyieldParams := types.NewTuple(varSelf)\n\tyieldRets := types.NewTuple(types.NewParam(0, nil, \"\", types.Typ[types.Bool]))\n\tsigYield := types.NewSignatureType(nil, nil, nil, yieldParams, yieldRets, false)\n\tcb.NewClosureWith(sigYield).BodyStart(pkg, condExpr).\n\t\tIf(condExpr)\n\tcompileExpr(ctx, 1, condExpr)\n\tcb.Then(condExpr).\n\t\tIf().DefineVarStart(0, nameVal, nameErr).\n\t\tVal(varSelf).MemberVal(\"XGo_first\", 0, v).CallWith(0, 2, 0, v)\n\tfirstRet := cb.Get(-1)\n\tnodeType := firstRet.Type.(*types.Tuple).At(0).Type()\n\tvarYield := newNodeSeqParam(pkgTypes, nodeType)\n\tcb.EndInit(1).VarVal(nameErr).Val(nil).BinaryOp(gotoken.EQL).Then().\n\t\tIf().Val(varYield).VarVal(nameVal).CallWith(1, 1, 0).UnaryOpEx(gotoken.NOT, 1).Then().\n\t\tVal(false).Return(1).\n\t\tEnd().End().End().\n\t\tVal(true).Return(1).\n\t\tEnd().               // end func\n\t\tCallWith(1, 0, 0, v) // ns.XGo_Enum()(func(self NodeSet) bool { ... })\n\tstk := cb.InternalStack()\n\tseq := stk.Pop()\n\tsigSeq := types.NewSignatureType(nil, nil, nil, types.NewTuple(varYield), nil, false)\n\tcb.Typ(nsType).\n\t\tNewClosureWith(sigSeq).BodyStart(pkg)\n\tstk.Push(seq)\n\tcb.EndStmt().\n\t\tEnd(). // end func\n\t\tCallWith(1, 1, 0, v)\n}\n\nfunc newNodeSeqParam(pkgTypes *types.Package, nodeType types.Type) *types.Var {\n\tyieldParams := types.NewTuple(types.NewParam(0, pkgTypes, \"\", nodeType))\n\tyieldRets := types.NewTuple(types.NewParam(0, nil, \"\", types.Typ[types.Bool]))\n\tsigYield := types.NewSignatureType(nil, nil, nil, yieldParams, yieldRets, false)\n\treturn types.NewParam(0, nil, \"_xgo_yield\", sigYield)\n}\n\nfunc compileAnySelectorExpr(ctx *blockCtx, lhs int, v *ast.AnySelectorExpr) {\n\tcompileExpr(ctx, 0, v.X)\n\t// DQL (DOM Query Language) rules:\n\t// - selector.**.name         -> XGo_Any(\"name\")      - descendants by name\n\t// - selector.**.\"elem-name\"  -> XGo_Any(\"elem-name\") - descendants by name\n\t// - selector.**.*            -> XGo_Any(\"\")          - all descendants\n\tcb, sel := ctx.cb, v.Sel\n\tname := sel.Name\n\tswitch name[0] {\n\tcase '\"', '`': // .\"elem-name\"\n\t\tname = unquote(name)\n\tcase '*':\n\t\tname = \"\"\n\t}\n\tconvMapToNodeSet(cb)\n\tcb.MemberVal(\"XGo_Any\", 0, v).Val(name).CallWith(1, lhs, 0, v)\n}\n\nfunc checkAnyOrMap(cb *gogen.CodeBuilder) *gogen.Element {\n\te := cb.Get(-1)\n\tswitch t := types.Unalias(e.Type).(type) {\n\tcase *types.Interface:\n\t\tif !t.Empty() {\n\t\t\treturn nil\n\t\t}\n\tcase *types.Map:\n\tdefault:\n\t\treturn nil\n\t}\n\treturn e\n}\n\nfunc convMapToNodeSet(cb *gogen.CodeBuilder) {\n\tif e := checkAnyOrMap(cb); e != nil {\n\t\tstk := cb.InternalStack()\n\t\tstk.Pop()\n\t\tcb.Val(cb.Pkg().Import(\"github.com/goplus/xgo/dql/maps\").Ref(\"New\"))\n\t\tstk.Push(e)\n\t\tcb.CallWith(1, 1, 0)\n\t}\n}\n\nfunc compileSelectorExpr(ctx *blockCtx, lhs int, v *ast.SelectorExpr, flags int) {\n\tswitch x := v.X.(type) {\n\tcase *ast.Ident:\n\t\tif at, kind := compileIdent(ctx, 1, x, flags|clIdentCanAutoCall|clIdentSelectorExpr); kind != objNormal {\n\t\t\tif compilePkgRef(ctx, 1, at, v.Sel, flags, kind) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif token.IsExported(v.Sel.Name) {\n\t\t\t\tpanic(ctx.newCodeErrorf(x.Pos(), x.End(), \"undefined: %s.%s\", x.Name, v.Sel.Name))\n\t\t\t}\n\t\t\tpanic(ctx.newCodeErrorf(x.Pos(), x.End(), \"cannot refer to unexported name %s.%s\", x.Name, v.Sel.Name))\n\t\t}\n\tdefault:\n\t\tcompileExpr(ctx, 1, x)\n\t}\n\n\t// DQL (DOM Query Language) rules:\n\t// - selector.name    -> XGo_Elem(\"name\")   - children by name (fallback)\n\t// - selector.\"name\"  -> XGo_Elem(\"name\")   - children by name (fallback)\n\t// - selector.$attr   -> XGo_Attr(\"attr\")   - attribute access\n\t// - selector.$\"attr\" -> XGo_Attr(\"attr\")   - attribute access\n\t// - selector.*       -> XGo_Child()        - direct children\n\tcb, sel := ctx.cb, v.Sel\n\tname := sel.Name\n\tswitch name[0] {\n\tcase '*':\n\t\tconvMapToNodeSet(cb)\n\t\tcb.MemberVal(\"XGo_Child\", 0, v).CallWith(0, lhs, 0, v)\n\tcase '$':\n\t\tif err := compileAttr(cb, lhs, name[1:], v); err != nil {\n\t\t\tpanic(err) // throw error\n\t\t}\n\tcase '\"', '`':\n\t\tname = unquote(name)\n\t\tfallthrough\n\tdefault:\n\t\tif err := compileMember(ctx, lhs, v, name, flags); err != nil {\n\t\t\tif kind, _ := cb.Member(\"XGo_Elem\", 0, 0, v); kind == gogen.MemberInvalid {\n\t\t\t\tpanic(err) // rethrow original error\n\t\t\t}\n\t\t\tcb.Val(name).CallWith(1, lhs, 0, v)\n\t\t}\n\t}\n}\n\nfunc compileAttr(cb *gogen.CodeBuilder, lhs int, name string, v ast.Node) (err error) {\n\tswitch name[0] {\n\tcase '\"', '`': // @\"attr-name\"\n\t\tname = unquote(name)\n\t}\n\tif e := checkAnyOrMap(cb); e != nil {\n\t\t// v.$name => v[\"name\"] as fallback if v is a map or empty interface\n\t\tcb.MemberVal(name, lhs, v)\n\t} else if _, err = cb.Member(\"XGo_Attr\", 1, gogen.MemberFlagVal, v); err == nil {\n\t\tcb.Val(name).CallWith(1, lhs, 0, v)\n\t}\n\treturn\n}\n\nfunc unquote(name string) string {\n\t// parser package already checks the syntax of the quoted string,\n\t// so we can ignore the error here\n\ts, _ := strconv.Unquote(name)\n\treturn s\n}\n\nfunc compileFuncAlias(ctx *blockCtx, lhs int, scope *types.Scope, x *ast.Ident, flags int) bool {\n\tname := x.Name\n\tif c := name[0]; c >= 'a' && c <= 'z' {\n\t\tname = string(rune(c)+('A'-'a')) + name[1:]\n\t\to := scope.Lookup(name)\n\t\tif o == nil && ctx.loadSymbol(name) {\n\t\t\to = scope.Lookup(name)\n\t\t}\n\t\tif o != nil {\n\t\t\treturn identVal(ctx, lhs, x, flags, o, true)\n\t\t}\n\t}\n\treturn false\n}\n\nfunc pkgRef(at gogen.PkgRef, name string) (o types.Object, alias bool) {\n\tif c := name[0]; c >= 'a' && c <= 'z' {\n\t\tname = string(rune(c)+('A'-'a')) + name[1:]\n\t\tif v := at.TryRef(name); v != nil && gogen.IsFunc(v.Type()) {\n\t\t\treturn v, true\n\t\t}\n\t\treturn\n\t}\n\treturn at.TryRef(name), false\n}\n\n// allow pkg.Types to be nil\nfunc lookupPkgRef(ctx *blockCtx, pkg gogen.PkgRef, x *ast.Ident, pkgKind int) (o types.Object, alias bool) {\n\tif pkg.Types != nil {\n\t\treturn pkgRef(pkg, x.Name)\n\t}\n\tif pkgKind == objPkgRef {\n\t\tfor _, at := range ctx.lookups {\n\t\t\tif o2, alias2 := pkgRef(at, x.Name); o2 != nil {\n\t\t\t\tif o != nil {\n\t\t\t\t\tpanic(ctx.newCodeErrorf(\n\t\t\t\t\t\tx.Pos(), x.End(), \"confliction: %s declared both in \\\"%s\\\" and \\\"%s\\\"\",\n\t\t\t\t\t\tx.Name, at.Types.Path(), pkg.Types.Path()))\n\t\t\t\t}\n\t\t\t\tpkg, o, alias = at, o2, alias2\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// allow at.Types to be nil\nfunc compilePkgRef(ctx *blockCtx, lhs int, at gogen.PkgRef, x *ast.Ident, flags, pkgKind int) bool {\n\tif v, alias := lookupPkgRef(ctx, at, x, pkgKind); v != nil {\n\t\tif (flags & clIdentLHS) != 0 {\n\t\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\t\trec.Use(x, v)\n\t\t\t}\n\t\t\tctx.cb.VarRef(v, x)\n\t\t\treturn true\n\t\t}\n\t\treturn identVal(ctx, lhs, x, flags, v, alias)\n\t}\n\treturn false\n}\n\nfunc identVal(ctx *blockCtx, lhs int, x *ast.Ident, flags int, v types.Object, alias bool) bool {\n\tautocall := false\n\tif alias {\n\t\tif autocall = (flags & clIdentCanAutoCall) != 0; autocall {\n\t\t\tif !gogen.HasAutoProperty(v.Type()) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Use(x, v)\n\t}\n\tcb := ctx.cb.Val(v, x)\n\tif autocall {\n\t\tcb.CallWith(0, lhs, 0, x)\n\t}\n\treturn true\n}\n\ntype fnType struct {\n\tnext         *fnType\n\tparams       *types.Tuple\n\tsig          *types.Signature\n\tbase         int\n\tsize         int\n\tvariadic     bool\n\ttypetype     bool\n\ttypeparam    bool\n\ttypeAsParams bool\n}\n\nfunc (p *fnType) arg(i int, ellipsis bool) types.Type {\n\tif i+p.base < p.size {\n\t\treturn p.params.At(i + p.base).Type()\n\t}\n\tif p.variadic {\n\t\tt := p.params.At(p.size).Type()\n\t\tif ellipsis {\n\t\t\treturn t\n\t\t}\n\t\treturn t.(*types.Slice).Elem()\n\t}\n\treturn nil\n}\n\nfunc (p *fnType) init(base int, t *types.Signature, typeAsParams bool) {\n\tp.base = base\n\tp.sig = t\n\tp.typeAsParams = typeAsParams\n\tp.params, p.variadic, p.typeparam = t.Params(), t.Variadic(), t.TypeParams() != nil\n\tp.size = p.params.Len()\n\tif p.variadic {\n\t\tp.size--\n\t}\n}\n\nfunc (p *fnType) initTypeType(t *gogen.TypeType) {\n\tparam := types.NewParam(0, nil, \"\", t.Type())\n\tp.params, p.typetype = types.NewTuple(param), true\n\tp.size = 1\n}\n\nfunc (p *fnType) unpackTupleLit(cb *gogen.CodeBuilder) bool {\n\treturn p.size != 1 || !cb.IsTupleType(p.params.At(0).Type())\n}\n\nfunc (p *fnType) load(fnt types.Type) {\n\tswitch v := fnt.(type) {\n\tcase *gogen.TypeType:\n\t\tp.initTypeType(v)\n\tcase *types.Signature:\n\t\ttyp, objs := gogen.CheckSigFuncExObjects(v)\n\t\tswitch typ.(type) {\n\t\tcase *gogen.TyOverloadFunc, *gogen.TyOverloadMethod:\n\t\t\tp.initFuncs(0, objs, false)\n\t\t\treturn\n\t\tcase *gogen.TyTemplateRecvMethod:\n\t\t\tp.initFuncs(1, objs, false)\n\t\t\treturn\n\t\tcase *gogen.TyTypeAsParams:\n\t\t\tp.initFuncs(1, objs, true)\n\t\t\treturn\n\t\t}\n\t\tp.init(0, v, false)\n\t}\n}\n\nfunc (p *fnType) initFuncs(base int, funcs []types.Object, typeAsParams bool) {\n\tfor i, obj := range funcs {\n\t\tif sig, ok := obj.Type().(*types.Signature); ok {\n\t\t\tif i == 0 {\n\t\t\t\tp.init(base, sig, typeAsParams)\n\t\t\t} else {\n\t\t\t\tfn := &fnType{}\n\t\t\t\tfn.init(base, sig, typeAsParams)\n\t\t\t\tp.next = fn\n\t\t\t\tp = p.next\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc compileCallExpr(ctx *blockCtx, lhs int, v *ast.CallExpr, inFlags int) {\n\t// If you need to confirm the callExpr format, you can turn on\n\t// if !v.NoParenEnd.IsValid() && !v.Rparen.IsValid() {\n\t// \t   panic(\"unexpected invalid Rparen and NoParenEnd in CallExpr\")\n\t// }\n\tvar ifn *ast.Ident\n\tswitch fn := v.Fun.(type) {\n\tcase *ast.Ident:\n\t\tif v.IsCommand() { // for support XGo_Exec, see TestSpxXGoExec\n\t\t\tinFlags |= clCommandIdent\n\t\t}\n\t\tif _, kind := compileIdent(ctx, 1, fn, clIdentAllowBuiltin|inFlags); kind == objXGoExec {\n\t\t\targs := make([]ast.Expr, 1, len(v.Args)+1)\n\t\t\targs[0] = toBasicLit(fn)\n\t\t\targs = append(args, v.Args...)\n\t\t\tv = &ast.CallExpr{Fun: fn, Args: args, Ellipsis: v.Ellipsis, NoParenEnd: v.NoParenEnd}\n\t\t} else {\n\t\t\tifn = fn\n\t\t}\n\tcase *ast.SelectorExpr:\n\t\tcompileSelectorExpr(ctx, 1, fn, 0)\n\tcase *ast.ErrWrapExpr:\n\t\tif v.IsCommand() {\n\t\t\tcallExpr := *v\n\t\t\tcallExpr.Fun = fn.X\n\t\t\tewExpr := *fn\n\t\t\tewExpr.X = &callExpr\n\t\t\tcompileErrWrapExpr(ctx, 0, &ewExpr, inFlags)\n\t\t\treturn\n\t\t}\n\t\tcompileErrWrapExpr(ctx, 1, fn, 0)\n\tdefault:\n\t\tcompileExpr(ctx, 1, fn, clInCallExpr)\n\t}\n\tvar err error\n\tvar stk = ctx.cb.InternalStack()\n\tvar base = stk.Len()\n\tvar flags gogen.InstrFlags\n\tvar ellipsis = v.Ellipsis != token.NoPos\n\tif ellipsis {\n\t\tflags = gogen.InstrFlagEllipsis\n\t}\n\tpfn := stk.Get(-1)\n\tfnt := pfn.Type\n\tfn := &fnType{}\n\tfn.load(fnt)\n\tif len(v.Kwargs) > 0 { // https://github.com/goplus/xgo/issues/2443\n\t\tn := len(v.Args)\n\t\targs := make([]ast.Expr, n+1)\n\t\tif fn.variadic { // has variadic parameter\n\t\t\tidx := fn.size - 1\n\t\t\tif idx < 0 {\n\t\t\t\tpanic(ctx.newCodeError(v.Pos(), v.End(), msgNoKwargsOVF))\n\t\t\t}\n\t\t\tif len(v.Args) < idx {\n\t\t\t\tpanic(ctx.newCodeError(v.Pos(), v.End(), msgNoEnoughArgToKwargs))\n\t\t\t}\n\t\t\tcopy(args, v.Args[:idx])\n\t\t\targs[idx] = mergeKwargs(ctx, v, fn.params.At(idx).Type())\n\t\t\tcopy(args[idx+1:], v.Args[idx:])\n\t\t} else {\n\t\t\tcopy(args, v.Args)\n\t\t\targs[n] = mergeKwargs(ctx, v, fn.arg(n, false))\n\t\t}\n\t\tne := *v\n\t\tne.Args, ne.Kwargs = args, nil\n\t\tv = &ne\n\t}\n\tfor fn != nil {\n\t\tif err = compileCallArgs(ctx, lhs, pfn, fn, v, ellipsis, flags); err == nil {\n\t\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\t\trec.recordCallExpr(ctx, v, fnt)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tstk.SetLen(base)\n\t\tfn = fn.next\n\t}\n\tif ifn != nil && builtinOrXGoExec(ctx, lhs, ifn, v, flags) == nil {\n\t\treturn\n\t}\n\tpanic(err)\n}\n\nconst (\n\tmsgNoKwargsOVF         = \"keyword arguments are not supported for a function with only variadic parameters\"\n\tmsgNoEnoughArgToKwargs = \"not enough arguments for function call with keyword arguments\"\n\tmsgUnexpectedKwargs    = \"keyword arguments can only be used for struct or map[string]T types, but got %v\"\n)\n\nfunc inThisPkg(ctx *blockCtx, t types.Type) bool {\n\tif named, ok := t.(*types.Named); ok {\n\t\tif named.Obj().Pkg() == ctx.pkg.Types {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc mergeKwargs(ctx *blockCtx, v *ast.CallExpr, t types.Type) ast.Expr {\n\tif t != nil {\n\t\tswitch u := t.Underlying().(type) {\n\t\tcase *types.Pointer:\n\t\t\tt = u.Elem()\n\t\t\tif u, ok := t.Underlying().(*types.Struct); ok {\n\t\t\t\treturn mergeStructKwargs(v.Kwargs, u, inThisPkg(ctx, t))\n\t\t\t}\n\t\t\tpanic(ctx.newCodeErrorf(v.Pos(), v.End(), msgUnexpectedKwargs, t))\n\t\tcase *types.Struct:\n\t\t\treturn mergeStructKwargs(v.Kwargs, u, inThisPkg(ctx, t))\n\t\t}\n\t}\n\treturn mergeStringMapKwargs(v.Kwargs) // fallback to map[string]T\n}\n\nfunc mergeStringMapKwargs(kwargs []*ast.KwargExpr) ast.Expr {\n\tn := len(kwargs)\n\telts := make([]ast.Expr, n)\n\tfor i, arg := range kwargs {\n\t\telts[i] = &ast.KeyValueExpr{\n\t\t\tKey:   toBasicLit(arg.Name),\n\t\t\tValue: arg.Value,\n\t\t}\n\t}\n\treturn &ast.CompositeLit{\n\t\tLbrace: kwargs[0].Pos() - 1,\n\t\tElts:   elts,\n\t\tRbrace: kwargs[n-1].End(),\n\t}\n}\n\nfunc mergeStructKwargs(kwargs []*ast.KwargExpr, u *types.Struct, inPkg bool) ast.Expr {\n\tn := len(kwargs)\n\telts := make([]ast.Expr, n)\n\tfor i, arg := range kwargs {\n\t\telts[i] = &ast.KeyValueExpr{\n\t\t\tKey:   getFldName(arg.Name, u, inPkg),\n\t\t\tValue: arg.Value,\n\t\t}\n\t}\n\treturn &ast.CompositeLit{\n\t\tLbrace: kwargs[0].Pos() - 1,\n\t\tElts:   elts,\n\t\tRbrace: kwargs[n-1].End(),\n\t}\n}\n\nfunc getFldName(name *ast.Ident, u *types.Struct, inPkg bool) *ast.Ident {\n\tif name.IsExported() {\n\t\treturn name\n\t}\n\tcapName := stringutil.Capitalize(name.Name)\n\tif !inPkg {\n\t\treturn &ast.Ident{NamePos: name.NamePos, Name: capName}\n\t}\n\tfor i, n := 0, u.NumFields(); i < n; i++ {\n\t\tfld := u.Field(i)\n\t\tif fld.Name() == name.Name {\n\t\t\treturn name\n\t\t}\n\t\tif fld.Exported() && fld.Name() == capName {\n\t\t\treturn &ast.Ident{NamePos: name.NamePos, Name: capName}\n\t\t}\n\t}\n\treturn name // fallback to origin name\n}\n\nfunc toBasicLit(fn *ast.Ident) *ast.BasicLit {\n\treturn &ast.BasicLit{ValuePos: fn.NamePos, Kind: token.STRING, Value: strconv.Quote(fn.Name)}\n}\n\n// maybe builtin new/delete: see TestSpxNewObj, TestMayBuiltinDelete\n// maybe XGo_Exec: see TestSpxXGoExec\nfunc builtinOrXGoExec(ctx *blockCtx, lhs int, ifn *ast.Ident, v *ast.CallExpr, flags gogen.InstrFlags) error {\n\tcb := ctx.cb\n\tswitch name := ifn.Name; name {\n\tcase \"new\", \"delete\":\n\t\tcb.InternalStack().PopN(1)\n\t\tcb.Val(ctx.pkg.Builtin().Ref(name), ifn)\n\t\treturn fnCall(ctx, lhs, v, flags, 0)\n\tdefault:\n\t\t// for support XGo_Exec, see TestSpxXGoExec\n\t\tif v.IsCommand() && ctx.isClass && tryXGoExec(cb, ifn) {\n\t\t\treturn fnCall(ctx, lhs, v, flags, 1)\n\t\t}\n\t}\n\treturn syscall.ENOENT\n}\n\nfunc tryXGoExec(cb *gogen.CodeBuilder, ifn *ast.Ident) bool {\n\tif recv := classRecv(cb); recv != nil {\n\t\tcb.InternalStack().PopN(1)\n\t\tif xgoOp(cb, recv, \"XGo_Exec\", \"Gop_Exec\", ifn) == nil {\n\t\t\tcb.Val(ifn.Name, ifn)\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc fnCall(ctx *blockCtx, lhs int, v *ast.CallExpr, flags gogen.InstrFlags, extra int) error {\n\tfor _, arg := range v.Args {\n\t\tcompileExpr(ctx, 1, arg)\n\t}\n\treturn ctx.cb.CallWithEx(len(v.Args)+extra, lhs, flags, v)\n}\n\nfunc compileCallArgs(ctx *blockCtx, lhs int, pfn *gogen.Element, fn *fnType, v *ast.CallExpr, ellipsis bool, flags gogen.InstrFlags) (err error) {\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\terr = ctx.recoverErr(r, v)\n\t\t}\n\t}()\n\n\tcb := ctx.cb\n\tvargs := v.Args\n\tif len(vargs) == 1 && !ellipsis {\n\t\tif tupleLit, ok := vargs[0].(*ast.TupleLit); ok {\n\t\t\tisEll := tupleLit.Ellipsis != token.NoPos\n\t\t\tif isEll || fn.unpackTupleLit(cb) {\n\t\t\t\tvargs, ellipsis = tupleLit.Elts, isEll\n\t\t\t}\n\t\t}\n\t}\n\n\tvargsOrg := vargs\n\tif fn.typeAsParams && fn.typeparam {\n\t\tn := fn.sig.TypeParams().Len()\n\t\tfor i := 0; i < n; i++ {\n\t\t\tcompileExpr(ctx, 1, vargs[i])\n\t\t}\n\t\targs := cb.InternalStack().GetArgs(n)\n\t\tvar targs []types.Type\n\t\tfor i, arg := range args {\n\t\t\ttyp := arg.Type\n\t\t\tt, ok := typ.(*gogen.TypeType)\n\t\t\tif !ok {\n\t\t\t\treturn ctx.newCodeErrorf(vargs[i].Pos(), vargs[i].End(), \"%v not type\", ctx.LoadExpr(vargs[i]))\n\t\t\t}\n\t\t\ttargs = append(targs, t.Type())\n\t\t}\n\t\tret, err := types.Instantiate(nil, fn.sig, targs, true)\n\t\tif err != nil {\n\t\t\treturn ctx.newCodeError(v.Pos(), v.End(), err.Error())\n\t\t}\n\t\tfn.init(1, ret.(*types.Signature), false)\n\t\tvargs = vargs[n:]\n\t}\n\n\tvar needInferFunc bool\n\tfor i, arg := range vargs {\n\t\tt := fn.arg(i, ellipsis)\n\t\tswitch expr := arg.(type) {\n\t\tcase *ast.LambdaExpr:\n\t\t\tif fn.typeparam {\n\t\t\t\tneedInferFunc = true\n\t\t\t\tcompileIdent(ctx, 0, ast.NewIdent(\"nil\"), 0) // TODO(xsw): check lhs\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsig, e := checkLambdaFuncType(ctx, expr, t, clLambaArgument, v.Fun)\n\t\t\tif e != nil {\n\t\t\t\treturn e\n\t\t\t}\n\t\t\tif err = compileLambdaExpr(ctx, expr, sig); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *ast.LambdaExpr2:\n\t\t\tif fn.typeparam {\n\t\t\t\tneedInferFunc = true\n\t\t\t\tcompileIdent(ctx, 0, ast.NewIdent(\"nil\"), 0) // TODO(xsw): check lhs\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsig, e := checkLambdaFuncType(ctx, expr, t, clLambaArgument, v.Fun)\n\t\t\tif e != nil {\n\t\t\t\treturn e\n\t\t\t}\n\t\t\tif err = compileLambdaExpr2(ctx, expr, sig); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *ast.CompositeLit:\n\t\t\tif err = compileCompositeLitEx(ctx, expr, t, true); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *ast.TupleLit:\n\t\t\tif err = compileTupleLit(ctx, expr, t, true); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *ast.SliceLit:\n\t\t\tswitch t.(type) {\n\t\t\tcase *types.Slice:\n\t\t\tcase *types.Named:\n\t\t\t\tif _, ok := getUnderlying(ctx, t).(*types.Slice); !ok {\n\t\t\t\t\tt = nil\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tt = nil\n\t\t\t}\n\t\t\ttypetype := fn.typetype && t != nil\n\t\t\tif typetype {\n\t\t\t\tcb.InternalStack().PopN(1)\n\t\t\t}\n\t\t\tif err = compileSliceLit(ctx, expr, t, true); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif typetype {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *ast.NumberUnitLit:\n\t\t\tcompileNumberUnitLit(ctx, expr, t)\n\t\tdefault:\n\t\t\tcompileExpr(ctx, 1, arg)\n\t\t\tif sigParamLen(t) == 0 {\n\t\t\t\tif nonClosure(cb.Get(-1).Type) {\n\t\t\t\t\tcb.ConvertToClosure()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif needInferFunc {\n\t\targs := cb.InternalStack().GetArgs(len(vargsOrg))\n\t\ttyp, err := gogen.InferFunc(ctx.pkg, pfn, fn.sig, nil, args, flags)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tnext := &fnType{}\n\t\tnext.init(fn.base, typ.(*types.Signature), false)\n\t\tnext.next = fn.next\n\t\tfn.next = next\n\t\treturn errCallNext\n\t}\n\treturn cb.CallWithEx(len(vargsOrg), lhs, flags, v)\n}\n\nvar (\n\terrCallNext = errors.New(\"call next\")\n)\n\ntype clLambaFlag string\n\nconst (\n\tclLambaAssign   clLambaFlag = \"assignment\"\n\tclLambaField    clLambaFlag = \"field value\"\n\tclLambaArgument clLambaFlag = \"argument\"\n)\n\n// check lambda func type\nfunc checkLambdaFuncType(ctx *blockCtx, lambda ast.Expr, ftyp types.Type, flag clLambaFlag, toNode ast.Node) (*types.Signature, error) {\n\ttyp := ftyp\nretry:\n\tswitch t := typ.(type) {\n\tcase *types.Signature:\n\t\tif l, ok := lambda.(*ast.LambdaExpr); ok {\n\t\t\tif len(l.Rhs) != t.Results().Len() {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\treturn t, nil\n\tcase *types.Named:\n\t\ttyp = t.Underlying()\n\t\tgoto retry\n\t}\n\tvar to string\n\tif toNode != nil {\n\t\tto = \" to \" + ctx.LoadExpr(toNode)\n\t}\n\treturn nil, ctx.newCodeErrorf(lambda.Pos(), lambda.End(), \"cannot use lambda literal as type %v in %v%v\", ftyp, flag, to)\n}\n\nfunc sigParamLen(typ types.Type) int {\nretry:\n\tswitch t := typ.(type) {\n\tcase *types.Signature:\n\t\treturn t.Params().Len()\n\tcase *types.Named:\n\t\ttyp = t.Underlying()\n\t\tgoto retry\n\t}\n\treturn -1\n}\n\nfunc nonClosure(typ types.Type) bool {\nretry:\n\tswitch t := typ.(type) {\n\tcase *types.Signature:\n\t\treturn false\n\tcase *types.Basic:\n\t\tif t.Kind() == types.UntypedNil {\n\t\t\treturn false\n\t\t}\n\tcase *types.Named:\n\t\ttyp = t.Underlying()\n\t\tgoto retry\n\t}\n\treturn true\n}\n\nfunc compileLambda(ctx *blockCtx, lambda ast.Expr, sig *types.Signature) {\n\tswitch expr := lambda.(type) {\n\tcase *ast.LambdaExpr2:\n\t\tif err := compileLambdaExpr2(ctx, expr, sig); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\tcase *ast.LambdaExpr:\n\t\tif err := compileLambdaExpr(ctx, expr, sig); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n}\n\nfunc makeLambdaParams(ctx *blockCtx, pos, end token.Pos, lhs []*ast.Ident, in *types.Tuple) (*types.Tuple, error) {\n\tpkg := ctx.pkg\n\tn := len(lhs)\n\tif nin := in.Len(); n != nin {\n\t\tfewOrMany := \"few\"\n\t\tif n > nin {\n\t\t\tfewOrMany = \"many\"\n\t\t}\n\t\thas := make([]string, n)\n\t\tfor i, v := range lhs {\n\t\t\thas[i] = v.Name\n\t\t}\n\t\treturn nil, ctx.newCodeErrorf(\n\t\t\tpos, end, \"too %s arguments in lambda expression\\n\\thave (%s)\\n\\twant %v\", fewOrMany, strings.Join(has, \", \"), in)\n\t}\n\tif n == 0 {\n\t\treturn nil, nil\n\t}\n\tparams := make([]*types.Var, n)\n\tfor i, name := range lhs {\n\t\tparam := pkg.NewParam(name.Pos(), name.Name, in.At(i).Type())\n\t\tparams[i] = param\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Def(name, param)\n\t\t}\n\t}\n\treturn types.NewTuple(params...), nil\n}\n\nfunc makeLambdaResults(pkg *gogen.Package, out *types.Tuple) *types.Tuple {\n\tnout := out.Len()\n\tif nout == 0 {\n\t\treturn nil\n\t}\n\tresults := make([]*types.Var, nout)\n\tfor i := 0; i < nout; i++ {\n\t\tresults[i] = pkg.NewParam(token.NoPos, \"\", out.At(i).Type())\n\t}\n\treturn types.NewTuple(results...)\n}\n\nfunc compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, sig *types.Signature) error {\n\tpkg := ctx.pkg\n\tparams, err := makeLambdaParams(ctx, v.Pos(), v.End(), v.Lhs, sig.Params())\n\tif err != nil {\n\t\treturn err\n\t}\n\tresults := makeLambdaResults(pkg, sig.Results())\n\tctx.cb.NewClosure(params, results, false).BodyStart(pkg)\n\tif len(v.Lhs) > 0 {\n\t\tdefNames(ctx, v.Lhs, ctx.cb.Scope())\n\t}\n\tfor _, v := range v.Rhs {\n\t\tcompileExpr(ctx, 1, v)\n\t}\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v, ctx.cb.Scope())\n\t}\n\tctx.cb.Return(len(v.Rhs)).End(v)\n\treturn nil\n}\n\nfunc compileLambdaExpr2(ctx *blockCtx, v *ast.LambdaExpr2, sig *types.Signature) error {\n\tpkg := ctx.pkg\n\tparams, err := makeLambdaParams(ctx, v.Pos(), v.End(), v.Lhs, sig.Params())\n\tif err != nil {\n\t\treturn err\n\t}\n\tresults := makeLambdaResults(pkg, sig.Results())\n\tcomments, once := ctx.cb.BackupComments()\n\tfn := ctx.cb.NewClosure(params, results, false)\n\tcb := fn.BodyStart(ctx.pkg, v.Body)\n\tif len(v.Lhs) > 0 {\n\t\tdefNames(ctx, v.Lhs, cb.Scope())\n\t}\n\tcompileStmts(ctx, v.Body.List)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v, ctx.cb.Scope())\n\t}\n\tcb.End(v)\n\tctx.cb.SetComments(comments, once)\n\treturn nil\n}\n\nfunc compileFuncLit(ctx *blockCtx, v *ast.FuncLit) {\n\tcb := ctx.cb\n\tcomments, once := cb.BackupComments()\n\tsig := toFuncType(ctx, v.Type, nil, nil)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.recordFuncLit(v, sig)\n\t}\n\tfn := cb.NewClosureWith(sig)\n\tif body := v.Body; body != nil {\n\t\tloadFuncBody(ctx, fn, body, nil, v, false)\n\t\tcb.SetComments(comments, once)\n\t}\n}\n\nfunc compileNumberUnitLit(ctx *blockCtx, v *ast.NumberUnitLit, expected types.Type) {\n\tctx.cb.ValWithUnit(\n\t\t&goast.BasicLit{ValuePos: v.ValuePos, Kind: gotoken.Token(v.Kind), Value: v.Value},\n\t\texpected, v.Unit)\n}\n\nfunc compileBasicLit(ctx *blockCtx, v *ast.BasicLit) {\n\tcb := ctx.cb\n\tswitch kind := v.Kind; kind {\n\tcase token.RAT:\n\t\tval := v.Value\n\t\tbi, _ := new(big.Int).SetString(val[:len(val)-1], 10) // remove r suffix\n\t\tcb.UntypedBigInt(bi, v)\n\tcase token.CSTRING, token.PYSTRING:\n\t\ts, err := strconv.Unquote(v.Value)\n\t\tif err != nil {\n\t\t\tlog.Panicln(\"compileBasicLit:\", err)\n\t\t}\n\t\tvar xstr gogen.Ref\n\t\tswitch kind {\n\t\tcase token.CSTRING:\n\t\t\txstr = ctx.cstr()\n\t\tdefault:\n\t\t\txstr = ctx.pystr()\n\t\t}\n\t\tcb.Val(xstr).Val(s).Call(1)\n\tdefault:\n\t\tif v.Extra == nil {\n\t\t\tbasicLit(cb, v)\n\t\t\treturn\n\t\t}\n\t\tcompileStringLitEx(ctx, cb, v)\n\t}\n}\n\nfunc invalidVal(cb *gogen.CodeBuilder) {\n\tcb.Val(&gogen.Element{Type: types.Typ[types.Invalid]})\n}\n\nfunc basicLit(cb *gogen.CodeBuilder, v *ast.BasicLit) {\n\tcb.Val(&goast.BasicLit{Kind: gotoken.Token(v.Kind), Value: v.Value}, v)\n}\n\nconst (\n\tstringutilPkgPath = \"github.com/qiniu/x/stringutil\"\n)\n\nfunc compileStringLitEx(ctx *blockCtx, cb *gogen.CodeBuilder, lit *ast.BasicLit) {\n\tpos := lit.ValuePos + 1\n\tquote := lit.Value[:1]\n\tparts := lit.Extra.Parts\n\tn := len(parts)\n\tif n != 1 {\n\t\tcb.Val(ctx.pkg.Import(stringutilPkgPath).Ref(\"Concat\"))\n\t}\n\tfor _, part := range parts {\n\t\tswitch v := part.(type) {\n\t\tcase string: // normal string literal or end with \"$$\"\n\t\t\tnext := pos + token.Pos(len(v))\n\t\t\tif strings.HasSuffix(v, \"$$\") {\n\t\t\t\tv = v[:len(v)-1]\n\t\t\t}\n\t\t\tbasicLit(cb, &ast.BasicLit{ValuePos: pos - 1, Value: quote + v + quote, Kind: token.STRING})\n\t\t\tpos = next\n\t\tcase ast.Expr:\n\t\t\tflags := 0\n\t\t\tif _, ok := v.(*ast.Ident); ok {\n\t\t\t\tflags = clIdentInStringLitEx\n\t\t\t}\n\t\t\tcompileExpr(ctx, 1, v, flags)\n\t\t\tt := cb.Get(-1).Type\n\t\t\tif t.Underlying() != types.Typ[types.String] {\n\t\t\t\tif _, err := cb.Member(\"string\", 0, gogen.MemberFlagAutoProperty); err != nil {\n\t\t\t\t\tif kind, _ := cb.Member(\"error\", 0, gogen.MemberFlagAutoProperty); kind == gogen.MemberInvalid {\n\t\t\t\t\t\tif e, ok := err.(*gogen.CodeError); ok {\n\t\t\t\t\t\t\terr = ctx.newCodeErrorf(v.Pos(), v.End(), \"%s.string%s\", ctx.LoadExpr(v), e.Msg)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tctx.handleErr(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tpos = v.End()\n\t\tdefault:\n\t\t\tpanic(\"compileStringLitEx TODO: unexpected part\")\n\t\t}\n\t}\n\tif n != 1 {\n\t\tcb.CallWith(n, 0, 0, lit)\n\t}\n}\n\nconst (\n\ttplPkgPath        = \"github.com/goplus/xgo/tpl\"\n\tencodingPkgPrefix = \"github.com/goplus/xgo/encoding/\"\n)\n\n// A DomainTextLit node represents a domain-specific text literal.\n// https://github.com/goplus/xgo/issues/2143\n//\n//\tdomainTag`...`\n//\tdomainTag`> arg1, arg2, ...\n//\t  ...\n//\t`\nfunc compileDomainTextLit(ctx *blockCtx, v *ast.DomainTextLit) {\n\tvar cb = ctx.cb\n\tvar imp gogen.PkgRef\n\tvar name = v.Domain.Name\n\tvar path string\n\tif pi, ok := ctx.findImport(name); ok {\n\t\timp = pi.PkgRef\n\t\tpath = pi.Path()\n\t} else {\n\t\tif name == \"tpl\" {\n\t\t\tpath = tplPkgPath\n\t\t} else {\n\t\t\tpath = encodingPkgPrefix + name\n\t\t}\n\t\timp = ctx.pkg.Import(path)\n\t\t/* TODO(xsw):\n\t\tif imp = ctx.pkg.TryImport(path); imp.Types == nil {\n\t\t\tpanic(\"compileDomainTextLit TODO: unknown domain: \" + name)\n\t\t}\n\t\t*/\n\t}\n\n\tn := 1\n\tif path == tplPkgPath {\n\t\tpos := ctx.fset.Position(v.ValuePos)\n\t\tfilename := relFile(ctx.relBaseDir, pos.Filename)\n\t\tcb.Val(imp.Ref(\"NewEx\")).\n\t\t\tVal(&goast.BasicLit{Kind: gotoken.STRING, Value: v.Value}, v).\n\t\t\tVal(filename).Val(pos.Line).Val(pos.Column)\n\t\tn += 3\n\t\tif f, ok := v.Extra.(*tpl.File); ok {\n\t\t\tdecls := f.Decls\n\t\t\tfor _, decl := range decls {\n\t\t\t\tif r, ok := decl.(*tpl.Rule); ok {\n\t\t\t\t\tif expr, ok := r.RetProc.(*ast.LambdaExpr2); ok {\n\t\t\t\t\t\tcb.Val(r.Name.Name)\n\t\t\t\t\t\tsig := sigRetFunc(ctx.pkg, r.IsList())\n\t\t\t\t\t\tcompileLambdaExpr2(ctx, lambdaRetFunc(expr), sig)\n\t\t\t\t\t\tn += 2\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tcb.Val(imp.Ref(\"New\"))\n\t\tif lit, ok := v.Extra.(*ast.DomainTextLitEx); ok {\n\t\t\tcb.Val(lit.Raw)\n\t\t\tfor _, arg := range lit.Args {\n\t\t\t\tcompileExpr(ctx, 1, arg)\n\t\t\t}\n\t\t\tn += len(lit.Args)\n\t\t} else {\n\t\t\tcb.Val(&goast.BasicLit{Kind: gotoken.STRING, Value: v.Value}, v)\n\t\t}\n\t}\n\tcb.CallWith(n, 0, 0, v)\n}\n\nfunc lambdaRetFunc(expr *ast.LambdaExpr2) *ast.LambdaExpr2 {\n\tv := *expr\n\tv.Lhs = []*ast.Ident{\n\t\t{NamePos: expr.Pos(), Name: \"self\"},\n\t}\n\treturn &v\n}\n\nfunc sigRetFunc(pkg *gogen.Package, isList bool) *types.Signature {\n\trets := types.NewTuple(anyParam(pkg))\n\tvar args *types.Tuple\n\tif isList {\n\t\targs = types.NewTuple(anySliceParam(pkg))\n\t} else {\n\t\targs = rets\n\t}\n\treturn types.NewSignatureType(nil, nil, nil, args, rets, false)\n}\n\nfunc anyParam(pkg *gogen.Package) *types.Var {\n\treturn pkg.NewParam(token.NoPos, \"\", gogen.TyEmptyInterface)\n}\n\nfunc anySliceParam(pkg *gogen.Package) *types.Var {\n\treturn pkg.NewParam(token.NoPos, \"\", types.NewSlice(gogen.TyEmptyInterface))\n}\n\nconst (\n\tcompositeLitVal    = 0\n\tcompositeLitKeyVal = 1\n)\n\nfunc checkCompositeLitElts(elts []ast.Expr) (kind int) {\n\tfor _, elt := range elts {\n\t\tif _, ok := elt.(*ast.KeyValueExpr); ok {\n\t\t\treturn compositeLitKeyVal\n\t\t}\n\t}\n\treturn compositeLitVal\n}\n\nfunc compileCompositeLitElts(ctx *blockCtx, elts []ast.Expr, kind int, expected *kvType) error {\n\tfor _, elt := range elts {\n\t\tif kv, ok := elt.(*ast.KeyValueExpr); ok {\n\t\t\tif key, ok := kv.Key.(*ast.CompositeLit); ok && key.Type == nil {\n\t\t\t\tcompileCompositeLit(ctx, key, expected.Key(), false)\n\t\t\t} else {\n\t\t\t\tcompileExpr(ctx, 1, kv.Key)\n\t\t\t}\n\t\t\terr := compileCompositeLitElt(ctx, kv.Value, expected.Elem(), clLambaAssign, kv.Key)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif kind == compositeLitKeyVal {\n\t\t\t\tctx.cb.None()\n\t\t\t}\n\t\t\terr := compileCompositeLitElt(ctx, elt, expected.Elem(), clLambaAssign, nil)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc compileCompositeLitElt(ctx *blockCtx, e ast.Expr, typ types.Type, flag clLambaFlag, toNode ast.Node) error {\n\tswitch v := unparen(e).(type) {\n\tcase *ast.LambdaExpr, *ast.LambdaExpr2:\n\t\tsig, err := checkLambdaFuncType(ctx, v, typ, flag, toNode)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcompileLambda(ctx, v, sig)\n\tcase *ast.TupleLit:\n\t\tcompileTupleLit(ctx, v, typ)\n\tcase *ast.SliceLit:\n\t\tcompileSliceLit(ctx, v, typ)\n\tcase *ast.CompositeLit:\n\t\tcompileCompositeLit(ctx, v, typ, false)\n\tdefault:\n\t\tcompileExpr(ctx, 1, v)\n\t}\n\treturn nil\n}\n\nfunc unparen(x ast.Expr) ast.Expr {\n\tif e, ok := x.(*ast.ParenExpr); ok {\n\t\treturn e.X\n\t}\n\treturn x\n}\n\nfunc compileStructLit(ctx *blockCtx, elts []ast.Expr, t *types.Struct, typ types.Type, src *ast.CompositeLit) error {\n\tfor idx, elt := range elts {\n\t\tif idx >= t.NumFields() {\n\t\t\treturn ctx.newCodeErrorf(elt.Pos(), elt.End(), \"too many values in %v{...}\", typ)\n\t\t}\n\t\terr := compileCompositeLitElt(ctx, elt, t.Field(idx).Type(), clLambaField, nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tctx.cb.StructLit(typ, len(elts), false, src)\n\treturn nil\n}\n\nfunc compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, typ types.Type, src *ast.CompositeLit) error {\n\tcb := ctx.cb\n\tfor _, elt := range elts {\n\t\tkv := elt.(*ast.KeyValueExpr)\n\t\tname := kv.Key.(*ast.Ident)\n\t\tidx := cb.LookupField(t, name.Name)\n\t\tif idx >= 0 {\n\t\t\tcb.Val(idx, src)\n\t\t} else {\n\t\t\tsrc := ctx.LoadExpr(name)\n\t\t\treturn ctx.newCodeErrorf(name.Pos(), name.End(), \"%s undefined (type %v has no field or method %s)\", src, typ, name.Name)\n\t\t}\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Use(name, t.Field(idx))\n\t\t}\n\t\terr := compileCompositeLitElt(ctx, kv.Value, t.Field(idx).Type(), clLambaField, kv.Key)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tcb.StructLit(typ, len(elts)<<1, true, src)\n\treturn nil\n}\n\ntype kvType struct {\n\tunderlying types.Type\n\tkey, val   types.Type\n\tcached     bool\n}\n\nfunc (p *kvType) required() *kvType {\n\tif !p.cached {\n\t\tp.cached = true\n\t\tswitch t := p.underlying.(type) {\n\t\tcase *types.Slice:\n\t\t\tp.key, p.val = types.Typ[types.Int], t.Elem()\n\t\tcase *types.Array:\n\t\t\tp.key, p.val = types.Typ[types.Int], t.Elem()\n\t\tcase *types.Map:\n\t\t\tp.key, p.val = t.Key(), t.Elem()\n\t\t}\n\t}\n\treturn p\n}\n\nfunc (p *kvType) Key() types.Type {\n\treturn p.required().key\n}\n\nfunc (p *kvType) Elem() types.Type {\n\treturn p.required().val\n}\n\nfunc getUnderlying(ctx *blockCtx, typ types.Type) types.Type {\n\tu := typ.Underlying()\n\tif u == nil {\n\t\tif t, ok := typ.(*types.Named); ok {\n\t\t\tctx.loadNamed(ctx.pkg, t)\n\t\t\tu = t.Underlying()\n\t\t}\n\t}\n\treturn u\n}\n\nfunc compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit, expected types.Type, mapOrStructOnly bool) {\n\tif err := compileCompositeLitEx(ctx, v, expected, mapOrStructOnly); err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// mapOrStructOnly means only map/struct can omit type\nfunc compileCompositeLitEx(ctx *blockCtx, v *ast.CompositeLit, expected types.Type, mapOrStructOnly bool) error {\n\tvar hasPtr bool\n\tvar typ, underlying types.Type\n\tvar kind = checkCompositeLitElts(v.Elts)\n\tif v.Type != nil {\n\t\ttyp = toType(ctx, v.Type)\n\t\tunderlying = getUnderlying(ctx, typ)\n\t\t// Auto-reference typed composite literal when expected type is pointer\n\t\tif expected != nil {\n\t\t\tif t, ok := expected.(*types.Pointer); ok {\n\t\t\t\ttelem := t.Elem()\n\t\t\t\tif types.Identical(typ, telem) {\n\t\t\t\t\thasPtr = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else if expected != nil {\n\t\tif t, ok := expected.(*types.Pointer); ok {\n\t\t\ttelem := t.Elem()\n\t\t\ttu := getUnderlying(ctx, telem)\n\t\t\tif _, ok := tu.(*types.Struct); ok { // struct pointer\n\t\t\t\ttyp, underlying, hasPtr = telem, tu, true\n\t\t\t}\n\t\t} else if tu := getUnderlying(ctx, expected); !mapOrStructOnly || isMapOrStruct(tu) {\n\t\t\ttyp, underlying = expected, tu\n\t\t}\n\t}\n\tif t, ok := underlying.(*types.Struct); ok {\n\t\tvar err error\n\t\tif kind == compositeLitKeyVal {\n\t\t\terr = compileStructLitInKeyVal(ctx, v.Elts, t, typ, v)\n\t\t} else {\n\t\t\terr = compileStructLit(ctx, v.Elts, t, typ, v)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\terr := compileCompositeLitElts(ctx, v.Elts, kind, &kvType{underlying: underlying})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tn := len(v.Elts)\n\t\tswitch underlying.(type) {\n\t\tcase *types.Slice:\n\t\t\tctx.cb.SliceLitEx(typ, n<<kind, kind == compositeLitKeyVal, v)\n\t\tcase *types.Array:\n\t\t\tctx.cb.ArrayLitEx(typ, n<<kind, kind == compositeLitKeyVal, v)\n\t\tcase *types.Map:\n\t\t\tif kind == compositeLitVal && n > 0 {\n\t\t\t\treturn ctx.newCodeError(v.Pos(), v.End(), \"missing key in map literal\")\n\t\t\t}\n\t\t\tif err := compileMapLitEx(ctx, typ, n, v); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif kind == compositeLitVal && n > 0 {\n\t\t\t\treturn ctx.newCodeErrorf(v.Pos(), v.End(), \"invalid composite literal type %v\", typ)\n\t\t\t}\n\t\t\tif err := compileMapLitEx(ctx, nil, n, v); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tif hasPtr {\n\t\tctx.cb.UnaryOp(gotoken.AND)\n\t\ttyp = expected\n\t}\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.recordCompositeLit(v, typ)\n\t}\n\treturn nil\n}\n\nfunc compileMapLitEx(ctx *blockCtx, typ types.Type, n int, v *ast.CompositeLit) (err error) {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\terr = ctx.newCodeError(v.Pos(), v.End(), \"invalid map literal\")\n\t\t}\n\t}()\n\terr = ctx.cb.MapLitEx(typ, n<<1, v)\n\treturn\n}\n\nfunc isMapOrStruct(tu types.Type) bool {\n\tswitch tu.(type) {\n\tcase *types.Struct:\n\t\treturn true\n\tcase *types.Map:\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc compileSliceLit(ctx *blockCtx, v *ast.SliceLit, typ types.Type, noPanic ...bool) (err error) {\n\tif noPanic != nil {\n\t\tdefer func() {\n\t\t\tif e := recover(); e != nil { // TODO: don't use defer to capture error\n\t\t\t\terr = ctx.recoverErr(e, v)\n\t\t\t}\n\t\t}()\n\t}\n\tn := len(v.Elts)\n\tfor _, elt := range v.Elts {\n\t\tcompileExpr(ctx, 1, elt)\n\t}\n\tif isSpecificSliceType(ctx, typ) {\n\t\tctx.cb.SliceLitEx(typ, n, false, v)\n\t} else {\n\t\tctx.cb.SliceLitEx(nil, n, false, v)\n\t}\n\treturn\n}\n\nfunc compileTupleLit(ctx *blockCtx, v *ast.TupleLit, typ types.Type, noPanic ...bool) (err error) {\n\tif noPanic != nil {\n\t\tdefer func() {\n\t\t\tif e := recover(); e != nil { // TODO: don't use defer to capture error\n\t\t\t\terr = ctx.recoverErr(e, v)\n\t\t\t}\n\t\t}()\n\t}\n\tn := len(v.Elts)\n\tfor _, elt := range v.Elts {\n\t\tcompileExpr(ctx, 1, elt)\n\t}\n\tctx.cb.TupleLit(typ, n, v)\n\treturn\n}\n\nfunc compileRangeExpr(ctx *blockCtx, v *ast.RangeExpr) {\n\tpkg, cb := ctx.pkg, ctx.cb\n\tcb.Val(pkg.Builtin().Ref(\"newRange\"))\n\tif v.First == nil {\n\t\tctx.cb.Val(0, v)\n\t} else {\n\t\tcompileExpr(ctx, 1, v.First)\n\t}\n\tcompileExpr(ctx, 1, v.Last)\n\tif v.Expr3 == nil {\n\t\tctx.cb.Val(1, v)\n\t} else {\n\t\tcompileExpr(ctx, 1, v.Expr3)\n\t}\n\tcb.Call(3)\n}\n\nconst (\n\tcomprehensionInvalid = iota\n\tcomprehensionList\n\tcomprehensionMap\n\tcomprehensionSelect\n)\n\nfunc comprehensionKind(v *ast.ComprehensionExpr) int {\n\tswitch v.Tok {\n\tcase token.LBRACK: // [\n\t\treturn comprehensionList\n\tcase token.LBRACE: // {\n\t\tif _, ok := v.Elt.(*ast.KeyValueExpr); ok {\n\t\t\treturn comprehensionMap\n\t\t}\n\t\treturn comprehensionSelect\n\t}\n\tpanic(\"TODO: invalid comprehensionExpr\")\n}\n\n// [expr for k, v in container, cond]\n// {for k, v in container, cond}\n// {expr for k, in container, cond}\n// {kexpr: vexpr for k, v in container, cond}\nfunc compileComprehensionExpr(ctx *blockCtx, lhs int, v *ast.ComprehensionExpr) {\n\tconst (\n\t\tnameOk  = \"_xgo_ok\"\n\t\tnameRet = \"_xgo_ret\"\n\t)\n\tkind := comprehensionKind(v)\n\tpkg, cb := ctx.pkg, ctx.cb\n\tvar results *types.Tuple\n\tvar ret *gogen.Param\n\tif v.Elt == nil {\n\t\tboolean := pkg.NewParam(token.NoPos, nameOk, types.Typ[types.Bool])\n\t\tresults = types.NewTuple(boolean)\n\t} else {\n\t\tret = pkg.NewAutoParam(nameRet)\n\t\tif kind == comprehensionSelect && lhs == 2 {\n\t\t\tboolean := pkg.NewParam(token.NoPos, nameOk, types.Typ[types.Bool])\n\t\t\tresults = types.NewTuple(ret, boolean)\n\t\t} else {\n\t\t\tresults = types.NewTuple(ret)\n\t\t}\n\t}\n\tcb.NewClosure(nil, results, false).BodyStart(pkg)\n\tif kind == comprehensionMap {\n\t\tcb.VarRef(ret).ZeroLit(ret.Type()).Assign(1)\n\t}\n\tend := 0\n\tfor i := len(v.Fors) - 1; i >= 0; i-- {\n\t\tnames := make([]string, 0, 2)\n\t\tdefineNames := make([]*ast.Ident, 0, 2)\n\t\tforStmt := v.Fors[i]\n\t\tif forStmt.Key != nil {\n\t\t\tnames = append(names, forStmt.Key.Name)\n\t\t\tdefineNames = append(defineNames, forStmt.Key)\n\t\t} else {\n\t\t\tnames = append(names, \"_\")\n\t\t}\n\t\tnames = append(names, forStmt.Value.Name)\n\t\tdefineNames = append(defineNames, forStmt.Value)\n\t\tcb.ForRange(names...)\n\t\tcompileExpr(ctx, 1, forStmt.X)\n\t\tcb.RangeAssignThen(forStmt.TokPos)\n\t\tdefNames(ctx, defineNames, cb.Scope())\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Scope(forStmt, cb.Scope())\n\t\t}\n\t\tif forStmt.Cond != nil {\n\t\t\tcb.If()\n\t\t\tif forStmt.Init != nil {\n\t\t\t\tcompileStmt(ctx, forStmt.Init)\n\t\t\t}\n\t\t\tcompileExpr(ctx, 1, forStmt.Cond)\n\t\t\tcb.Then()\n\t\t\tend++\n\t\t}\n\t\tend++\n\t}\n\tswitch kind {\n\tcase comprehensionList:\n\t\t// _xgo_ret = append(_xgo_ret, elt)\n\t\tcb.VarRef(ret)\n\t\tcb.Val(pkg.Builtin().Ref(\"append\"))\n\t\tcb.Val(ret)\n\t\tcompileExpr(ctx, 1, v.Elt)\n\t\tcb.Call(2).Assign(1)\n\tcase comprehensionMap:\n\t\t// _xgo_ret[key] = val\n\t\tcb.Val(ret)\n\t\tkv := v.Elt.(*ast.KeyValueExpr)\n\t\tcompileExpr(ctx, 1, kv.Key)\n\t\tcb.IndexRef(1)\n\t\tcompileExpr(ctx, 1, kv.Value)\n\t\tcb.Assign(1)\n\tdefault:\n\t\tif v.Elt == nil {\n\t\t\t// return true\n\t\t\tcb.Val(true)\n\t\t\tcb.Return(1)\n\t\t} else {\n\t\t\t// return elt, true\n\t\t\tcompileExpr(ctx, 1, v.Elt)\n\t\t\tn := 1\n\t\t\tif lhs == 2 {\n\t\t\t\tcb.Val(true)\n\t\t\t\tn++\n\t\t\t}\n\t\t\tcb.Return(n)\n\t\t}\n\t}\n\tfor i := 0; i < end; i++ {\n\t\tcb.End()\n\t}\n\tcb.Return(0).End().Call(0)\n}\n\nconst (\n\terrorPkgPath = \"github.com/qiniu/x/errors\"\n)\n\nvar (\n\ttyError = types.Universe.Lookup(\"error\").Type()\n)\n\nfunc compileErrWrapExpr(ctx *blockCtx, lhs int, v *ast.ErrWrapExpr, inFlags int) {\n\tconst (\n\t\tnameErr = \"_xgo_err\"\n\t\tnameRet = \"_xgo_ret\"\n\t)\n\tpkg, cb := ctx.pkg, ctx.cb\n\tuseClosure := v.Tok == token.NOT || v.Default != nil\n\tif !useClosure && (cb.Scope().Parent() == types.Universe) {\n\t\tpanic(\"TODO: can't use expr? in global\")\n\t}\n\tif lhs != 0 {\n\t\t// lhs == 0 means the result is discarded\n\t\t// +1 accounts for the error value that will be stripped from the result tuple\n\t\tlhs++\n\t}\n\tcompileExpr(ctx, lhs, v.X, inFlags)\n\tx := cb.InternalStack().Pop()\n\tn := 0\n\tresults, ok := x.Type.(*types.Tuple)\n\tif ok {\n\t\tn = results.Len() - 1\n\t}\n\n\tvar ret []*types.Var\n\tif n > 0 {\n\t\ti, retName := 0, nameRet\n\t\tret = make([]*gogen.Param, n)\n\t\tfor {\n\t\t\tret[i] = pkg.NewAutoParam(retName)\n\t\t\ti++\n\t\t\tif i >= n {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tretName = nameRet + strconv.Itoa(i+1)\n\t\t}\n\t}\n\tsig := types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(ret...), false)\n\tif useClosure {\n\t\tcb.NewClosureWith(sig).BodyStart(pkg)\n\t} else {\n\t\tcb.CallInlineClosureStart(sig, 0, false)\n\t}\n\n\tcb.NewVar(tyError, nameErr)\n\terr := cb.Scope().Lookup(nameErr)\n\n\tfor _, retVar := range ret {\n\t\tcb.VarRef(retVar)\n\t}\n\tcb.VarRef(err)\n\tcb.InternalStack().Push(x)\n\tcb.Assign(n+1, 1)\n\n\tcb.If().Val(err).CompareNil(gotoken.NEQ).Then()\n\tif v.Default == nil {\n\t\tpos := pkg.Fset.Position(v.Pos())\n\t\tcurFn := cb.Func().Ancestor()\n\t\tcurFnName := curFn.Name()\n\t\tif curFnName == \"\" {\n\t\t\tcurFnName = \"main\"\n\t\t}\n\n\t\tcb.VarRef(err).\n\t\t\tVal(pkg.Import(errorPkgPath).Ref(\"NewFrame\")).\n\t\t\tVal(err).\n\t\t\tVal(sprintAst(pkg.Fset, v.X)).\n\t\t\tVal(relFile(ctx.relBaseDir, pos.Filename)).\n\t\t\tVal(pos.Line).\n\t\t\tVal(curFn.Pkg().Name() + \".\" + curFnName).\n\t\t\tCall(5).\n\t\t\tAssign(1)\n\t}\n\n\tif v.Tok == token.NOT { // expr!\n\t\tcb.Val(pkg.Builtin().Ref(\"panic\")).Val(err).Call(1).EndStmt()\n\t} else if v.Default == nil { // expr?\n\t\tcb.Val(err).ReturnErr(true)\n\t} else { // expr?:val\n\t\tcompileExpr(ctx, 1, v.Default)\n\t\tcb.Return(1)\n\t}\n\tcb.End().Return(0).End()\n\tif useClosure {\n\t\tcb.Call(0)\n\t}\n}\n\nfunc sprintAst(fset *token.FileSet, x ast.Node) string {\n\tvar buf bytes.Buffer\n\terr := printer.Fprint(&buf, fset, x)\n\tif err != nil {\n\t\tpanic(\"Unexpected error: \" + err.Error())\n\t}\n\n\treturn buf.String()\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/func_type_and_var.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\t\"go/constant\"\n\t\"go/types\"\n\t\"log\"\n\t\"math/big\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc toRecv(ctx *blockCtx, recv *ast.FieldList) *types.Var {\n\tv := recv.List[0]\n\tvar name string\n\tif len(v.Names) > 0 {\n\t\tname = v.Names[0].Name\n\t}\n\ttyp, star, _ := getRecvType(v.Type)\n\tid, ok := typ.(*ast.Ident)\n\tif !ok {\n\t\tpanic(\"TODO: getRecvType\")\n\t}\n\tt := toIdentType(ctx, id)\n\tif star {\n\t\tt = types.NewPointer(t)\n\t}\n\tret := ctx.pkg.NewParam(v.Pos(), name, t)\n\tif rec := ctx.recorder(); rec != nil {\n\t\tdRecv := recv.List[0]\n\t\tif names := dRecv.Names; len(names) == 1 {\n\t\t\trec.Def(names[0], ret)\n\t\t}\n\t}\n\treturn ret\n}\n\nfunc getRecvTypeName(ctx *pkgCtx, recv *ast.FieldList, handleErr bool) (string, bool) {\n\ttyp, _, _ := getRecvType(recv.List[0].Type)\n\tif t, ok := typ.(*ast.Ident); ok {\n\t\treturn t.Name, true\n\t}\n\tif handleErr {\n\t\tsrc := ctx.LoadExpr(typ)\n\t\tctx.handleErrorf(typ.Pos(), typ.End(), \"invalid receiver type %v (%v is not a defined type)\", src, src)\n\t}\n\treturn \"\", false\n}\n\nfunc toResults(ctx *blockCtx, in *ast.FieldList) *types.Tuple {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tflds := in.List\n\tn := len(flds)\n\targs := make([]*types.Var, 0, n)\n\tfor _, fld := range flds {\n\t\targs = toParam(ctx, fld, args)\n\t}\n\treturn types.NewTuple(args...)\n}\n\nfunc toParams(ctx *blockCtx, flds []*ast.Field) (typ *types.Tuple, variadic bool) {\n\tn := len(flds)\n\tif n == 0 {\n\t\treturn nil, false\n\t}\n\targs := make([]*types.Var, 0, n)\n\tfor _, fld := range flds {\n\t\targs = toParam(ctx, fld, args)\n\t}\n\t_, ok := flds[n-1].Type.(*ast.Ellipsis)\n\treturn types.NewTuple(args...), ok\n}\n\nfunc toParam(ctx *blockCtx, fld *ast.Field, args []*gogen.Param) []*gogen.Param {\n\ttyp := toType(ctx, fld.Type)\n\tpkg := ctx.pkg\n\tisOptional := fld.Optional.IsValid()\n\tif len(fld.Names) == 0 {\n\t\treturn append(args, pkg.NewParamEx(fld.Pos(), \"\", typ, isOptional))\n\t}\n\tfor _, name := range fld.Names {\n\t\tparam := pkg.NewParamEx(name.Pos(), name.Name, typ, isOptional)\n\t\targs = append(args, param)\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Def(name, param)\n\t\t}\n\t}\n\treturn args\n}\n\n// -----------------------------------------------------------------------------\n\nfunc toType(ctx *blockCtx, typ ast.Expr) (t types.Type) {\n\tif rec := ctx.recorder(); rec != nil {\n\t\tdefer func() {\n\t\t\trec.recordType(typ, t)\n\t\t}()\n\t}\n\tswitch v := typ.(type) {\n\tcase *ast.Ident:\n\t\tctx.idents = append(ctx.idents, v)\n\t\tdefer func() {\n\t\t\tctx.idents = ctx.idents[:len(ctx.idents)-1]\n\t\t}()\n\t\ttyp := toIdentType(ctx, v)\n\t\tif ctx.inInst == 0 {\n\t\t\tif t, ok := typ.(*types.Named); ok {\n\t\t\t\tif namedIsTypeParams(ctx, t) {\n\t\t\t\t\tpos := ctx.idents[0].Pos()\n\t\t\t\t\tend := ctx.idents[0].End()\n\t\t\t\t\tfor _, i := range ctx.idents {\n\t\t\t\t\t\tif i.Name == v.Name {\n\t\t\t\t\t\t\tpos = i.Pos()\n\t\t\t\t\t\t\tend = i.End()\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tctx.handleErrorf(pos, end, \"cannot use generic type %v without instantiation\", t.Obj().Type())\n\t\t\t\t\treturn types.Typ[types.Invalid]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn typ\n\tcase *ast.StarExpr:\n\t\telem := toType(ctx, v.X)\n\t\treturn types.NewPointer(elem)\n\tcase *ast.ArrayType:\n\t\treturn toArrayType(ctx, v)\n\tcase *ast.InterfaceType:\n\t\treturn toInterfaceType(ctx, v)\n\tcase *ast.Ellipsis:\n\t\telem := toType(ctx, v.Elt)\n\t\treturn types.NewSlice(elem)\n\tcase *ast.MapType:\n\t\treturn toMapType(ctx, v)\n\tcase *ast.TupleType:\n\t\treturn toTupleType(ctx, v)\n\tcase *ast.StructType:\n\t\treturn toStructType(ctx, v)\n\tcase *ast.ChanType:\n\t\treturn toChanType(ctx, v)\n\tcase *ast.FuncType:\n\t\treturn toFuncType(ctx, v, nil, nil)\n\tcase *ast.SelectorExpr:\n\t\ttyp := toExternalType(ctx, v)\n\t\tif ctx.inInst == 0 {\n\t\t\tif t, ok := typ.(*types.Named); ok {\n\t\t\t\tif namedIsTypeParams(ctx, t) {\n\t\t\t\t\tpanic(ctx.newCodeErrorf(v.Pos(), v.End(), \"cannot use generic type %v without instantiation\", t.Obj().Type()))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn typ\n\tcase *ast.ParenExpr:\n\t\treturn toType(ctx, v.X)\n\tcase *ast.BinaryExpr:\n\t\treturn toBinaryExprType(ctx, v)\n\tcase *ast.UnaryExpr:\n\t\treturn toUnaryExprType(ctx, v)\n\tcase *ast.IndexExpr:\n\t\treturn toIndexType(ctx, v)\n\tcase *ast.IndexListExpr:\n\t\treturn toIndexListType(ctx, v)\n\tdefault:\n\t\tctx.handleErrorf(v.Pos(), v.End(), \"toType unexpected: %T\", v)\n\t\treturn types.Typ[types.Invalid]\n\t}\n}\n\nvar (\n\ttypesChanDirs = [...]types.ChanDir{\n\t\tast.RECV:            types.RecvOnly,\n\t\tast.SEND:            types.SendOnly,\n\t\tast.SEND | ast.RECV: types.SendRecv,\n\t}\n)\n\nfunc toChanType(ctx *blockCtx, v *ast.ChanType) *types.Chan {\n\treturn types.NewChan(typesChanDirs[v.Dir], toType(ctx, v.Value))\n}\n\nfunc toExternalType(ctx *blockCtx, v *ast.SelectorExpr) types.Type {\n\tid := v.X.(*ast.Ident)\n\tname := id.Name\n\tif pi, ok := ctx.findImport(name); ok {\n\t\trec := ctx.recorder()\n\t\tif rec != nil {\n\t\t\trec.Use(id, pi.pkgName)\n\t\t}\n\t\to := pi.TryRef(v.Sel.Name)\n\t\tif t, ok := o.(*types.TypeName); ok {\n\t\t\tif rec != nil {\n\t\t\t\trec.Use(v.Sel, t)\n\t\t\t}\n\t\t\treturn t.Type()\n\t\t}\n\t\tctx.handleErrorf(v.Pos(), v.End(), \"%s.%s is not a type\", name, v.Sel.Name)\n\t} else {\n\t\tctx.handleErrorf(v.Pos(), v.End(), \"undefined: %s\", name)\n\t}\n\treturn types.Typ[types.Invalid]\n}\n\n/*-----------------------------------------------------------------------------\n\nName context:\n- type\n- pkgRef.type\n- spx.type\n\n// ---------------------------------------------------------------------------*/\n\nfunc toIdentType(ctx *blockCtx, ident *ast.Ident) (ret types.Type) {\n\tvar obj types.Object\n\tif rec := ctx.recorder(); rec != nil {\n\t\tdefer func() {\n\t\t\tif obj != nil {\n\t\t\t\trec.recordIdent(ident, obj)\n\t\t\t}\n\t\t}()\n\t}\n\tif ctx.tlookup != nil {\n\t\tif typ := ctx.tlookup.Lookup(ident.Name); typ != nil {\n\t\t\tobj = typ.Obj()\n\t\t\treturn typ\n\t\t}\n\t}\n\tv, builtin := lookupType(ctx, ident.Name)\n\tif isBuiltin(builtin) {\n\t\tctx.handleErrorf(ident.Pos(), ident.End(), \"use of builtin %s not in function call\", ident.Name)\n\t\treturn types.Typ[types.Invalid]\n\t}\n\tif t, ok := v.(*types.TypeName); ok {\n\t\tobj = t\n\t\treturn t.Type()\n\t}\n\tif v, _ := lookupPkgRef(ctx, gogen.PkgRef{}, ident, objPkgRef); v != nil {\n\t\tif t, ok := v.(*types.TypeName); ok {\n\t\t\tobj = t\n\t\t\treturn t.Type()\n\t\t}\n\t}\n\tctx.handleErrorf(ident.Pos(), ident.End(), \"%s is not a type\", ident.Name)\n\treturn types.Typ[types.Invalid]\n}\n\n// TODO: optimization\nfunc lookupType(ctx *blockCtx, name string) (types.Object, types.Object) {\n\tat, o := ctx.cb.Scope().LookupParent(name, token.NoPos)\n\tif o != nil && at != types.Universe {\n\t\tif debugLookup {\n\t\t\tlog.Println(\"==> LookupParent\", name, \"=>\", o)\n\t\t}\n\t\treturn o, nil\n\t}\n\tif ctx.loadSymbol(name) {\n\t\tif v := ctx.pkg.Types.Scope().Lookup(name); v != nil {\n\t\t\tif debugLookup {\n\t\t\t\tlog.Println(\"==> Lookup (LoadSymbol)\", name, \"=>\", v)\n\t\t\t}\n\t\t\treturn v, nil\n\t\t}\n\t}\n\tif obj := ctx.pkg.Builtin().TryRef(name); obj != nil {\n\t\treturn obj, o\n\t}\n\treturn o, o\n}\n\ntype fieldKind int\n\nconst (\n\tfieldKindUser fieldKind = iota\n\tfieldKindClass\n)\n\ntype fieldElem struct {\n\tpos  token.Pos\n\tend  token.Pos\n\tkind fieldKind\n}\n\ntype checkRedecl struct {\n\tnames map[string]fieldElem\n}\n\nfunc newCheckRedecl() *checkRedecl {\n\treturn &checkRedecl{names: make(map[string]fieldElem)}\n}\n\nfunc (p *checkRedecl) chkRedecl(ctx *blockCtx, name string, pos, end token.Pos, kind fieldKind) bool {\n\tif name == \"_\" {\n\t\treturn false\n\t}\n\n\tif existing, ok := p.names[name]; ok {\n\t\tswitch existing.kind {\n\t\tcase fieldKindClass:\n\t\t\tctx.handleErrorf(\n\t\t\t\tpos, end, \"%s conflicts with class name.\\n\\trename the field to resolve the naming conflict.\",\n\t\t\t\tname)\n\t\tcase fieldKindUser:\n\t\t\tctx.handleErrorf(\n\t\t\t\tpos, end, \"%v redeclared\\n\\t%v other declaration of %v\",\n\t\t\t\tname, ctx.Position(existing.pos), name)\n\t\t}\n\t\treturn true\n\t}\n\n\tp.names[name] = fieldElem{\n\t\tpos:  pos,\n\t\tend:  end,\n\t\tkind: kind,\n\t}\n\treturn false\n}\n\n// toTupleType converts an AST TupleType node to a types.Struct.\n// Tuple types are syntactic sugar for structs with ordinal field names (_0, _1, ...).\n// Named fields in the tuple are compile-time aliases converted to ordinal fields.\nfunc toTupleType(ctx *blockCtx, v *ast.TupleType) types.Type {\n\tfieldList := v.Fields.List\n\tswitch len(fieldList) {\n\tcase 0:\n\t\treturn types.NewStruct(nil, nil)\n\tcase 1:\n\t\t// single-field tuple is equivalent to the field type itself\n\t\tif len(fieldList[0].Names) <= 1 {\n\t\t\treturn toType(ctx, fieldList[0].Type)\n\t\t}\n\t}\n\n\tpkg := ctx.pkg\n\tpkgTypes := pkg.Types\n\tfields := make([]*types.Var, 0, len(fieldList))\n\tchk := newCheckRedecl()\n\trec := ctx.recorder()\n\tnamedCount := 0\n\tfor _, field := range fieldList {\n\t\tfieldType := field.Type\n\t\ttyp := toType(ctx, fieldType)\n\t\tif len(field.Names) == 0 {\n\t\t\tfld := types.NewField(fieldType.Pos(), pkgTypes, \"\", typ, true)\n\t\t\tfields = append(fields, fld)\n\t\t\tcontinue\n\t\t}\n\t\tfor _, id := range field.Names {\n\t\t\tname := id.Name\n\t\t\tif name != \"\" {\n\t\t\t\tnamedCount++\n\t\t\t\tif chk.chkRedecl(ctx, name, id.Pos(), id.End(), fieldKindUser) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif name == \"_\" {\n\t\t\t\t\tname = \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t\tfld := types.NewField(id.NamePos, pkgTypes, name, typ, false)\n\t\t\tfields = append(fields, fld)\n\t\t\tif rec != nil {\n\t\t\t\trec.Def(id, fld)\n\t\t\t}\n\t\t}\n\t}\n\twithName := namedCount == len(fields)\n\treturn pkg.NewTuple(withName, fields...)\n}\n\nfunc toStructType(ctx *blockCtx, v *ast.StructType) *types.Struct {\n\tpkg := ctx.pkg.Types\n\tfieldList := v.Fields.List\n\tfields := make([]*types.Var, 0, len(fieldList))\n\ttags := make([]string, 0, len(fieldList))\n\tchk := newCheckRedecl()\n\trec := ctx.recorder()\n\tfor _, field := range fieldList {\n\t\t// Struct Tags (#2488): Check before calling toType to\n\t\t// avoid \"_ is not a type\" error\n\t\tif len(field.Names) == 0 && field.Tag != nil {\n\t\t\tif ident, ok := field.Type.(*ast.Ident); ok && ident.Name == \"_\" {\n\t\t\t\temptyStruct := types.NewStruct(nil, nil)\n\t\t\t\tfld := types.NewField(ident.NamePos, pkg, \"_\", emptyStruct, false)\n\t\t\t\tfields = append(fields, fld)\n\t\t\t\ttags = append(tags, toFieldTag(field.Tag))\n\t\t\t\tif rec != nil {\n\t\t\t\t\trec.Def(ident, fld)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\ttyp := toType(ctx, field.Type)\n\t\tif len(field.Names) == 0 { // embedded\n\t\t\tname := getTypeName(typ)\n\t\t\tif chk.chkRedecl(ctx, name, field.Type.Pos(), field.Type.End(), fieldKindUser) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif t, ok := typ.(*types.Named); ok { // #1196: embedded type should ensure loaded\n\t\t\t\tctx.loadNamed(ctx.pkg, t)\n\t\t\t}\n\t\t\tident := parseTypeEmbedName(field.Type)\n\t\t\tfld := types.NewField(ident.NamePos, pkg, name, typ, true)\n\t\t\tfields = append(fields, fld)\n\t\t\ttags = append(tags, toFieldTag(field.Tag))\n\t\t\tif rec != nil {\n\t\t\t\trec.Def(ident, fld)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tfor _, name := range field.Names {\n\t\t\tif chk.chkRedecl(ctx, name.Name, name.Pos(), name.End(), fieldKindUser) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfld := types.NewField(name.NamePos, pkg, name.Name, typ, false)\n\t\t\tfields = append(fields, fld)\n\t\t\ttags = append(tags, toFieldTag(field.Tag))\n\t\t\tif rec != nil {\n\t\t\t\trec.Def(name, fld)\n\t\t\t}\n\t\t}\n\t}\n\treturn types.NewStruct(fields, tags)\n}\n\nfunc toFieldTag(v *ast.BasicLit) string {\n\tif v != nil {\n\t\tdata := v.Value\n\t\tif len(data) > 0 && data[0] == '\"' && noTagKey(data) {\n\t\t\treturn \"_:\" + data\n\t\t}\n\t\ttag, err := strconv.Unquote(data)\n\t\tif err != nil {\n\t\t\tlog.Panicln(\"TODO: toFieldTag -\", err)\n\t\t}\n\t\treturn tag\n\t}\n\treturn \"\"\n}\n\nfunc noTagKey(data string) bool {\n\tpos := strings.IndexByte(data, ':')\n\tif pos < 0 {\n\t\treturn true\n\t}\n\treturn strings.IndexByte(data[:pos], ' ') >= 0\n}\n\nfunc getTypeName(typ types.Type) string {\n\tif t, ok := typ.(*types.Pointer); ok {\n\t\ttyp = t.Elem()\n\t}\n\tswitch t := typ.(type) {\n\tcase *types.Named:\n\t\treturn t.Obj().Name()\n\tcase *types.Basic:\n\t\treturn t.Name()\n\tdefault:\n\t\tpanic(\"TODO: getTypeName\")\n\t}\n}\n\nfunc toMapType(ctx *blockCtx, v *ast.MapType) *types.Map {\n\tkey := toType(ctx, v.Key)\n\tval := toType(ctx, v.Value)\n\treturn types.NewMap(key, val)\n}\n\nfunc toArrayType(ctx *blockCtx, v *ast.ArrayType) types.Type {\n\telem := toType(ctx, v.Elt)\n\tif v.Len == nil {\n\t\treturn types.NewSlice(elem)\n\t}\n\tif _, ok := v.Len.(*ast.Ellipsis); ok {\n\t\treturn types.NewArray(elem, -1) // A negative length indicates an unknown length\n\t}\n\treturn types.NewArray(elem, toInt64(ctx, v.Len, \"non-constant array bound %s\"))\n}\n\nfunc toInt64(ctx *blockCtx, e ast.Expr, emsg string) int64 {\n\tcb := ctx.pkg.ConstStart()\n\tcompileExpr(ctx, 1, e)\n\ttv := cb.EndConst()\n\tif val := tv.CVal; val != nil {\n\t\tif val.Kind() == constant.Float {\n\t\t\tif v, ok := constant.Val(val).(*big.Rat); ok && v.IsInt() {\n\t\t\t\treturn v.Num().Int64()\n\t\t\t}\n\t\t} else if v, ok := constant.Int64Val(val); ok {\n\t\t\treturn v\n\t\t}\n\t}\n\tsrc := ctx.LoadExpr(e)\n\tpanic(ctx.newCodeErrorf(e.Pos(), e.End(), emsg, src))\n}\n\nfunc toInterfaceType(ctx *blockCtx, v *ast.InterfaceType) types.Type {\n\tmethodsList := v.Methods.List\n\tif methodsList == nil {\n\t\treturn types.NewInterfaceType(nil, nil)\n\t}\n\tvar rec = ctx.recorder()\n\tvar pkg = ctx.pkg.Types\n\tvar methods []*types.Func\n\tvar embeddeds []types.Type\n\tfor _, m := range methodsList {\n\t\tif len(m.Names) == 0 { // embedded\n\t\t\ttyp := toType(ctx, m.Type)\n\t\t\tif t, ok := typ.(*types.Named); ok { // #1198: embedded type should ensure loaded\n\t\t\t\tctx.loadNamed(ctx.pkg, t)\n\t\t\t}\n\t\t\tembeddeds = append(embeddeds, typ)\n\t\t\tcontinue\n\t\t}\n\t\tname := m.Names[0]\n\t\tsig := toFuncType(ctx, m.Type.(*ast.FuncType), nil, nil)\n\t\tmthd := types.NewFunc(name.NamePos, pkg, name.Name, sig)\n\t\tmethods = append(methods, mthd)\n\t\tif rec != nil {\n\t\t\trec.Def(name, mthd)\n\t\t}\n\t}\n\tintf := types.NewInterfaceType(methods, embeddeds).Complete()\n\treturn intf\n}\n\nfunc instantiate(ctx *blockCtx, exprX ast.Expr, indices ...ast.Expr) types.Type {\n\tctx.inInst++\n\tdefer func() {\n\t\tctx.inInst--\n\t}()\n\n\tx := toType(ctx, exprX)\n\tidx := make([]types.Type, len(indices))\n\tfor i, index := range indices {\n\t\tidx[i] = toType(ctx, index)\n\t}\n\ttyp := ctx.pkg.Instantiate(x, idx, exprX)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.instantiate(exprX, x, typ)\n\t}\n\treturn typ\n}\n\nfunc toIndexType(ctx *blockCtx, v *ast.IndexExpr) types.Type {\n\treturn instantiate(ctx, v.X, v.Index)\n}\n\nfunc toIndexListType(ctx *blockCtx, v *ast.IndexListExpr) types.Type {\n\treturn instantiate(ctx, v.X, v.Indices...)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc toString(l *ast.BasicLit) string {\n\tif l.Kind == token.STRING {\n\t\ts, err := strconv.Unquote(l.Value)\n\t\tif err == nil {\n\t\t\treturn s\n\t\t}\n\t}\n\tpanic(\"TODO: toString - convert ast.BasicLit to string failed\")\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/internal/.gitignore",
    "content": ".gop/\n.xgo/\ngo.mod\ngop_autogen*.go\nxgo_autogen*.go\n"
  },
  {
    "path": "cl/internal/dql/dql.go",
    "content": "package dql\n\nimport \"iter\"\n\nconst (\n\tXGoPackage = true\n)\n\ntype Node struct {\n}\n\ntype NodeSet struct {\n}\n\nfunc New() NodeSet {\n\treturn NodeSet{}\n}\n\n// NodeSet(seq func(func(*Node) bool))\nfunc NodeSet_Cast(func(yield func(*Node) bool)) NodeSet {\n\treturn NodeSet{}\n}\n\n// XGo_Enum returns an iterator over the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Enum() iter.Seq[NodeSet] {\n\treturn nil\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes with the specified name.\nfunc (p NodeSet) XGo_Any(name string) NodeSet {\n\treturn NodeSet{}\n}\n\nfunc (p NodeSet) XGo_Select(name string) NodeSet {\n\treturn NodeSet{}\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Child() NodeSet {\n\treturn NodeSet{}\n}\n\n// XGo_first returns the first node in the NodeSet, or an error if the NodeSet is empty.\nfunc (p NodeSet) XGo_first() (*Node, error) {\n\treturn nil, nil\n}\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\nfunc (p NodeSet) XGo_Elem(name string) NodeSet {\n\treturn NodeSet{}\n}\n\nfunc (p NodeSet) XGo_Attr__0(name string) int {\n\treturn 0\n}\n\nfunc (p NodeSet) XGo_Attr__1(name string) (int, error) {\n\treturn 0, nil\n}\n\ntype NodeSet2 struct {\n}\n\nfunc New2() NodeSet2 {\n\treturn NodeSet2{}\n}\n\nfunc NodeSet2_Cast(func(yield func(*Node) bool)) NodeSet2 {\n\treturn NodeSet2{}\n}\n\nfunc (p NodeSet2) XGo_first() *Node {\n\treturn nil\n}\n\nfunc (p NodeSet2) XGo_Enum() iter.Seq[NodeSet2] {\n\treturn nil\n}\n\nfunc (p NodeSet2) XGo_Elem(name string) NodeSet2 {\n\treturn NodeSet2{}\n}\n\nfunc (p NodeSet2) XGo_Attr(name string) int {\n\treturn 0\n}\n"
  },
  {
    "path": "cl/internal/gop-in-go/foo/foo.xgo",
    "content": "package foo\n\nfunc ReverseMap(m map[string]int) map[int]string {\n\treturn {v: k for k, v <- m}\n}\n"
  },
  {
    "path": "cl/internal/gop-in-go/foo/foo_test.xgo",
    "content": "package foo\n\nimport (\n\t\"testing\"\n)\n\nfunc TestReverseMap(t *testing.T) {\n\tout := ReverseMap({\"a\": 1})\n\tif len(out) != 1 || out[1] != \"a\" {\n\t\tt.Fatal(\"ReverseMap failed:\", out)\n\t}\n}\n"
  },
  {
    "path": "cl/internal/gop-in-go/foo/footest_test.xgo",
    "content": "package foo_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/cl/internal/gop-in-go/foo\"\n)\n\nfunc TestReverseMap(t *testing.T) {\n\tout := foo.ReverseMap({\"b\": 2})\n\tif len(out) != 1 || out[2] != \"b\" {\n\t\tt.Fatal(\"ReverseMap failed:\", out)\n\t}\n}\n"
  },
  {
    "path": "cl/internal/huh/huh.go",
    "content": "package huh\n\n// -----------------------------------------------------------------------------\n\ntype Form int\n\nfunc (f Form) Run() {\n}\n\nfunc New(string, string, int) Form {\n\treturn 0\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/internal/llgo-hello/hello.go",
    "content": "package hello\n\nimport \"github.com/goplus/lib/c\"\n\nfunc Main() {\n\tc.Printf(c.Str(\"Hello world\\n\"))\n}\n"
  },
  {
    "path": "cl/internal/mcp/classfile.go",
    "content": "package mcp\n\nconst (\n\tGopPackage = true\n)\n\ntype Game struct {\n}\n\nfunc New() *Game {\n\treturn nil\n}\n\nfunc (p *Game) initGame() {}\n\nfunc (p *Game) Server(name string) {}\n\ntype Tool struct {\n}\n\nfunc (p *Tool) Main(name string) int {\n\treturn 0\n}\n\ntype Prompt struct {\n}\n\nfunc (p *Prompt) Main(*Tool) string {\n\treturn \"\"\n}\n\ntype Resource struct {\n}\n\nfunc (p *Resource) Main() {\n}\n\ntype ToolProto interface {\n\tMain(name string) int\n}\n\ntype PromptProto interface {\n\tMain(*Tool) string\n}\n\ntype ResourceProto interface {\n\tMain()\n}\n\nfunc Gopt_Game_Main(game interface{ initGame() }, resources []ResourceProto, tools []ToolProto, prompts []PromptProto) {\n}\n"
  },
  {
    "path": "cl/internal/overload/bar/bar.go",
    "content": "package bar\n\nconst GopPackage = true\n\ntype M = map[string]any\n\ntype basetype interface {\n\tstring | int | bool | float64\n}\n\ntype Var__0[T basetype] struct {\n\tval T\n}\n\nfunc (p *Var__0[T]) Value() T {\n\treturn p.val\n}\n\ntype Var__1[T map[string]any] struct {\n\tval T\n}\n\nfunc (p *Var__1[T]) Value() T {\n\treturn p.val\n}\n\nfunc XGox_Var_Cast__0[T basetype]() *Var__0[T] {\n\treturn new(Var__0[T])\n}\n\nfunc XGox_Var_Cast__1[T map[string]any]() *Var__1[T] {\n\treturn new(Var__1[T])\n}\n\ntype Player struct {\n}\n\nfunc XGot_Player_XGox_OnCmd__0[T any](p *Player, handler func(cmd T) error) {\n\tvar t T\n\thandler(t)\n}\n\nfunc XGot_Player_XGox_OnCmd__1[T1 ~int, T2 any](p *Player, n T1, handler func(n T1, cmd T2) error) {\n\tvar t T2\n\thandler(n, t)\n}\n"
  },
  {
    "path": "cl/internal/overload/foo/foo.go",
    "content": "package foo\n\nconst GopPackage = true\n\ntype Mesher interface {\n\tName() string\n}\n\ntype N struct {\n}\n\nfunc (m *N) OnKey__0(a string, fn func()) {\n}\n\nfunc (m *N) OnKey__1(a string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__2(a []string, fn func()) {\n}\n\nfunc (m *N) OnKey__3(a []string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__4(a []Mesher, fn func()) {\n}\n\nfunc (m *N) OnKey__5(a []Mesher, fn func(key Mesher)) {\n}\n\nfunc (m *N) OnKey__6(a []string, b []string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__7(a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__8(x int, y int) {\n}\n\nfunc OnKey__0(a string, fn func()) {\n}\n\nfunc OnKey__1(a string, fn func(key string)) {\n}\n\nfunc OnKey__2(a []string, fn func()) {\n}\n\nfunc OnKey__3(a []string, fn func(key string)) {\n}\n\nfunc OnKey__4(a []Mesher, fn func()) {\n}\n\nfunc OnKey__5(a []Mesher, fn func(key Mesher)) {\n}\n\nfunc OnKey__6(a []string, b []string, fn func(key string)) {\n}\n\nfunc OnKey__7(a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc OnKey__8(x int, y int) {\n}\n\nfunc Test__0() {\n}\n\nfunc Test__1[N any](n N) {\n}\n\nfunc Test__2[N1, N2 any](n1 N1, n2 N2) {\n}\n"
  },
  {
    "path": "cl/internal/spx/game.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage spx\n\nconst (\n\tGopPackage = \"github.com/goplus/xgo/cl/internal/spx/pkg\"\n\tGop_sched  = \"Sched,SchedNow\"\n)\n\ntype Sound string\n\ntype MyGame struct {\n}\n\nfunc Gopt_MyGame_Main(game any) {\n}\n\nfunc (p *MyGame) Ls(n int) {}\n\nfunc (p *MyGame) Capout(doSth func()) (string, error) {\n\treturn \"\", nil\n}\n\nfunc (p *MyGame) Gop_Env(name string) int {\n\treturn 0\n}\n\nfunc (p *MyGame) Gop_Exec(name string, args ...any) {\n}\n\nfunc (p *MyGame) InitGameApp(args ...string) {\n}\n\nfunc (p *MyGame) Broadcast__0(msg string) {\n}\n\nfunc (p *MyGame) Broadcast__1(msg string, wait bool) {\n}\n\nfunc (p *MyGame) Broadcast__2(msg string, data any, wait bool) {\n}\n\nfunc (p *MyGame) Play(media string, wait ...bool) {\n}\n\nfunc (p *MyGame) sendMessage(data any) {\n}\n\nfunc (p *MyGame) SendMessage(data any) {\n\tp.sendMessage(data)\n}\n\nfunc Gopt_MyGame_Run(game any, resource string) error {\n\treturn nil\n}\n\nfunc Sched() {\n}\n\nfunc SchedNow() {\n}\n\nfunc Rand__0(int) int {\n\treturn 0\n}\n\nfunc Rand__1(float64) float64 {\n\treturn 0\n}\n\nvar (\n\tTestIntValue int\n)\n"
  },
  {
    "path": "cl/internal/spx/pkg/pkg.go",
    "content": "package pkg\n\nconst (\n\tGopPackage = true\n)\n\ntype Vector struct {\n\tX int\n\tY int\n}\n\nfunc NewVector(x, y int) *Vector {\n\treturn &Vector{x, y}\n}\n\nfunc (v *Vector) Add__0(x int, y int) {\n\tv.X += x\n\tv.Y += y\n}\n\nfunc (v *Vector) Add__1(o *Vector) {\n\tv.Add__0(o.X, o.Y)\n}\n\nfunc (v *Vector) Self() *Vector {\n\treturn v\n}\n"
  },
  {
    "path": "cl/internal/spx/sprite.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage spx\n\nimport (\n\t\"github.com/goplus/xgo/cl/internal/spx/pkg\"\n)\n\ntype Sprite struct {\n\tpos pkg.Vector\n\tEntry\n}\n\ntype Entry struct {\n\tvec pkg.Vector\n}\n\nfunc (p *Entry) Vector() *pkg.Vector {\n\treturn &p.vec\n}\n\nfunc (p *Sprite) SetCostume(costume any) {\n}\n\nfunc (p *Sprite) Say(msg string, secs ...float64) {\n}\n\nfunc (p *Sprite) Position() *pkg.Vector {\n\treturn &p.pos\n}\n\ntype Mesher interface {\n\tName() string\n}\n\nfunc Gopt_Sprite_Clone__0(sprite any) {\n}\n\nfunc Gopt_Sprite_Clone__1(sprite any, data any) {\n}\n\nfunc Gopt_Sprite_OnKey__0(sprite any, a string, fn func()) {\n}\n\nfunc Gopt_Sprite_OnKey__1(sprite any, a string, fn func(key string)) {\n}\n\nfunc Gopt_Sprite_OnKey__2(sprite any, a []string, fn func()) {\n}\n\nfunc Gopt_Sprite_OnKey__3(sprite any, a []string, fn func(key string)) {\n}\n\nfunc Gopt_Sprite_OnKey__4(sprite any, a []Mesher, fn func()) {\n}\n\nfunc Gopt_Sprite_OnKey__5(sprite any, a []Mesher, fn func(key Mesher)) {\n}\n\nfunc Gopt_Sprite_OnKey__6(sprite any, a []string, b []string, fn func(key string)) {\n}\n\nfunc Gopt_Sprite_OnKey__7(sprite any, a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc Gopt_Sprite_OnKey__8(sprite any, x int, y int) {\n}\n\nfunc Gopt_Sprite_OnKey2(sprite any, a string, fn func(key string)) {\n}\n"
  },
  {
    "path": "cl/internal/spx2/spx2.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage spx2\n\nconst (\n\tGop_sched = \"Sched\"\n\tGop_work  = \"Sprite\"\n)\n\ntype Game struct {\n}\n\nfunc (p *Game) Main() {\n}\n\ntype Sprite struct {\n}\n\nfunc Sched() {\n}\n"
  },
  {
    "path": "cl/internal/spx3/jwt/jwt.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jwt\n\nfunc Token(v string) string {\n\treturn \"token: \" + v\n}\n"
  },
  {
    "path": "cl/internal/spx3/spx3.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage spx3\n\nconst (\n\tGopPackage = true\n)\n\ntype Game struct {\n}\n\nfunc New() *Game {\n\treturn nil\n}\n\nfunc (p *Game) initGame() {}\n\nfunc (p *Game) Run() {}\n\ntype Sprite struct {\n}\n\nfunc (p *Sprite) Name() string {\n\treturn \"sprite\"\n}\n\nfunc (p *Sprite) Main(name string) {}\n\ntype Handler interface {\n\tMain(name string)\n\tClassfname() string\n\tClassclone() Handler\n}\n\nfunc Gopt_Game_Main(game interface{ initGame() }, workers ...Handler) {\n}\n"
  },
  {
    "path": "cl/internal/spx4/game.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage spx4\n\nconst (\n\tGopPackage = \"github.com/goplus/xgo/cl/internal/spx/pkg\"\n\tGop_sched  = \"Sched,SchedNow\"\n)\n\ntype Sound string\n\ntype MyGame struct {\n}\n\nfunc Gopt_MyGame_Main(game any, sprites ...Sprite) {\n}\n\nfunc (p *MyGame) Ls(n int) {}\n\nfunc (p *MyGame) Capout(doSth func()) (string, error) {\n\treturn \"\", nil\n}\n\nfunc (p *MyGame) Gop_Env(name string) int {\n\treturn 0\n}\n\nfunc (p *MyGame) Gop_Exec(name string, args ...any) {\n}\n\nfunc (p *MyGame) InitGameApp(args ...string) {\n}\n\nfunc (p *MyGame) Broadcast__0(msg string) {\n}\n\nfunc (p *MyGame) Broadcast__1(msg string, wait bool) {\n}\n\nfunc (p *MyGame) Broadcast__2(msg string, data any, wait bool) {\n}\n\nfunc (p *MyGame) BackdropName() string {\n\treturn \"\"\n}\n\nfunc (p *MyGame) Play(media string, wait ...bool) {\n}\n\nfunc (p *MyGame) sendMessage(data any) {\n}\n\nfunc (p *MyGame) SendMessage(data any) {\n\tp.sendMessage(data)\n}\n\nfunc Gopt_MyGame_Run(game any, resource string) error {\n\treturn nil\n}\n\nfunc Sched() {\n}\n\nfunc SchedNow() {\n}\n\nfunc Rand__0(int) int {\n\treturn 0\n}\n\nfunc Rand__1(float64) float64 {\n\treturn 0\n}\n\nvar (\n\tTestIntValue int\n)\n"
  },
  {
    "path": "cl/internal/spx4/pkg/pkg.go",
    "content": "package pkg\n\nconst (\n\tGopPackage = true\n)\n\ntype Vector struct {\n\tX int\n\tY int\n}\n\nfunc NewVector(x, y int) *Vector {\n\treturn &Vector{x, y}\n}\n\nfunc (v *Vector) Add__0(x int, y int) {\n\tv.X += x\n\tv.Y += y\n}\n\nfunc (v *Vector) Add__1(o *Vector) {\n\tv.Add__0(o.X, o.Y)\n}\n\nfunc (v *Vector) Self() *Vector {\n\treturn v\n}\n"
  },
  {
    "path": "cl/internal/spx4/sprite.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage spx4\n\nimport (\n\t\"github.com/goplus/xgo/cl/internal/spx/pkg\"\n)\n\ntype Sprite struct {\n\tpos pkg.Vector\n\tEntry\n}\n\ntype Entry struct {\n\tvec pkg.Vector\n}\n\nfunc (p *Entry) Vector() *pkg.Vector {\n\treturn &p.vec\n}\n\nfunc (p *Sprite) SetCostume(costume any) {\n}\n\nfunc (p *Sprite) Say(msg string, secs ...float64) {\n}\n\nfunc (p *Sprite) Position() *pkg.Vector {\n\treturn &p.pos\n}\n\ntype Mesher interface {\n\tName() string\n}\n\nfunc Gopt_Sprite_Clone__0(sprite any) {\n}\n\nfunc Gopt_Sprite_Clone__1(sprite any, data any) {\n}\n\nfunc Gopt_Sprite_OnKey__0(sprite any, a string, fn func()) {\n}\n\nfunc Gopt_Sprite_OnKey__1(sprite any, a string, fn func(key string)) {\n}\n\nfunc Gopt_Sprite_OnKey__2(sprite any, a []string, fn func()) {\n}\n\nfunc Gopt_Sprite_OnKey__3(sprite any, a []string, fn func(key string)) {\n}\n\nfunc Gopt_Sprite_OnKey__4(sprite any, a []Mesher, fn func()) {\n}\n\nfunc Gopt_Sprite_OnKey__5(sprite any, a []Mesher, fn func(key Mesher)) {\n}\n\nfunc Gopt_Sprite_OnKey__6(sprite any, a []string, b []string, fn func(key string)) {\n}\n\nfunc Gopt_Sprite_OnKey__7(sprite any, a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc Gopt_Sprite_OnKey__8(sprite any, x int, y int) {\n}\n\nfunc Gopt_Sprite_OnKey2(sprite any, a string, fn func(key string)) {\n}\n"
  },
  {
    "path": "cl/internal/test/case.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype CaseT interface {\n\t// Name returns the name of the running (sub-) test or benchmark.\n\t//\n\t// The name will include the name of the test along with the names of\n\t// any nested sub-tests. If two sibling sub-tests have the same name,\n\t// Name will append a suffix to guarantee the returned name is unique.\n\tName() string\n\n\t// Fail marks the function as having failed but continues execution.\n\tFail()\n\n\t// Failed reports whether the function has failed.\n\tFailed() bool\n\n\t// FailNow marks the function as having failed and stops its execution\n\t// by calling runtime.Goexit (which then runs all deferred calls in the\n\t// current goroutine).\n\t// Execution will continue at the next test or benchmark.\n\t// FailNow must be called from the goroutine running the\n\t// test or benchmark function, not from other goroutines\n\t// created during the test. Calling FailNow does not stop\n\t// those other goroutines.\n\tFailNow()\n\n\t// Log formats its arguments using default formatting, analogous to Println,\n\t// and records the text in the error log. For tests, the text will be printed only if\n\t// the test fails or the -test.v flag is set. For benchmarks, the text is always\n\t// printed to avoid having performance depend on the value of the -test.v flag.\n\tLog(args ...any)\n\n\t// Logf formats its arguments according to the format, analogous to Printf, and\n\t// records the text in the error log. A final newline is added if not provided. For\n\t// tests, the text will be printed only if the test fails or the -test.v flag is\n\t// set. For benchmarks, the text is always printed to avoid having performance\n\t// depend on the value of the -test.v flag.\n\tLogf(format string, args ...any)\n\n\t// Errorln is equivalent to Log followed by Fail.\n\tErrorln(args ...any)\n\n\t// Errorf is equivalent to Logf followed by Fail.\n\tErrorf(format string, args ...any)\n\n\t// Fatal is equivalent to Log followed by FailNow.\n\tFatal(args ...any)\n\n\t// Fatalf is equivalent to Logf followed by FailNow.\n\tFatalf(format string, args ...any)\n\n\t// Skip is equivalent to Log followed by SkipNow.\n\tSkip(args ...any)\n\n\t// Skipf is equivalent to Logf followed by SkipNow.\n\tSkipf(format string, args ...any)\n\n\t// SkipNow marks the test as having been skipped and stops its execution\n\t// by calling runtime.Goexit.\n\t// If a test fails (see Error, Errorf, Fail) and is then skipped,\n\t// it is still considered to have failed.\n\t// Execution will continue at the next test or benchmark. See also FailNow.\n\t// SkipNow must be called from the goroutine running the test, not from\n\t// other goroutines created during the test. Calling SkipNow does not stop\n\t// those other goroutines.\n\tSkipNow()\n\n\t// Skipped reports whether the test was skipped.\n\tSkipped() bool\n\n\t// Helper marks the calling function as a test helper function.\n\t// When printing file and line information, that function will be skipped.\n\t// Helper may be called simultaneously from multiple goroutines.\n\tHelper()\n\n\t// Cleanup registers a function to be called when the test (or subtest) and all its\n\t// subtests complete. Cleanup functions will be called in last added,\n\t// first called order.\n\tCleanup(f func())\n\n\t// TempDir returns a temporary directory for the test to use.\n\t// The directory is automatically removed by Cleanup when the test and\n\t// all its subtests complete.\n\t// Each subsequent call to t.TempDir returns a unique directory;\n\t// if the directory creation fails, TempDir terminates the test by calling Fatal.\n\tTempDir() string\n\n\t// Run runs f as a subtest of t called name.\n\t//\n\t// Run may be called simultaneously from multiple goroutines, but all such calls\n\t// must return before the outer test function for t returns.\n\tRun(name string, f func()) bool\n\n\t// Deadline reports the time at which the test binary will have\n\t// exceeded the timeout specified by the -timeout flag.\n\t//\n\t// The ok result is false if the -timeout flag indicates “no timeout” (0).\n\tDeadline() (deadline time.Time, ok bool)\n}\n\n// -----------------------------------------------------------------------------\n\ntype TestingT struct {\n\t*testing.T\n}\n\n// NewT creates a testing object.\nfunc NewT(t *testing.T) TestingT {\n\treturn TestingT{t}\n}\n\n// Errorln is equivalent to Log followed by Fail.\nfunc (p TestingT) Errorln(args ...any) {\n\tt := p.T\n\tt.Helper()\n\tt.Error(args...)\n}\n\n// Run runs f as a subtest of t called name.\n//\n// Run may be called simultaneously from multiple goroutines, but all such calls\n// must return before the outer test function for t returns.\nfunc (p TestingT) Run(name string, doSth func()) bool {\n\treturn p.T.Run(name, func(t *testing.T) {\n\t\tdoSth()\n\t})\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/internal/test/match.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage test\n\nconst (\n\tGopPackage = true\n)\n\ntype basetype interface {\n\tstring | int | bool | float64\n}\n\ntype Case struct {\n\tCaseT\n}\n\nconst (\n\tXGoo_Gopt_Case_Match = \"Gopt_Case_MatchTBase,Gopt_Case_MatchAny\"\n)\n\nfunc Gopt_Case_MatchTBase[T basetype](t CaseT, got, expected T, name ...string) {\n}\n\nfunc Gopt_Case_MatchAny(t CaseT, got, expected any, name ...string) {\n}\n"
  },
  {
    "path": "cl/internal/testutil/testutil.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage testutil\n\ntype Options struct {\n\tLoop  bool\n\tAsync bool\n}\n"
  },
  {
    "path": "cl/internal/typesutil/api.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\nimport (\n\t\"go/constant\"\n\t\"go/types\"\n\t\"unsafe\"\n\n\t\"github.com/goplus/gogen\"\n)\n\n// An OperandMode specifies the (addressing) mode of an operand.\ntype OperandMode byte\n\n// TypeAndValue reports the type and value (for constants)\n// of the corresponding expression.\ntype TypeAndValue struct {\n\tmode  OperandMode\n\tType  types.Type\n\tValue constant.Value\n}\n\nfunc NewTypeAndValueForType(typ types.Type) (ret types.TypeAndValue) {\n\tswitch t := typ.(type) {\n\tcase *gogen.TypeType:\n\t\ttyp = t.Type()\n\t}\n\tret.Type = typ\n\t(*TypeAndValue)(unsafe.Pointer(&ret)).mode = TypExpr\n\treturn\n}\n\nfunc NewTypeAndValueForValue(typ types.Type, val constant.Value, mode OperandMode) (ret types.TypeAndValue) {\n\tswitch t := typ.(type) {\n\tcase *gogen.TypeType:\n\t\ttyp = t.Type()\n\t}\n\tif val != nil {\n\t\tmode = Constant\n\t}\n\tret.Type = typ\n\tret.Value = val\n\t(*TypeAndValue)(unsafe.Pointer(&ret)).mode = mode\n\treturn\n}\n\nfunc NewTypeAndValueForCallResult(typ types.Type, val constant.Value) (ret types.TypeAndValue) {\n\tvar mode OperandMode\n\tif typ == nil {\n\t\tret.Type = &types.Tuple{}\n\t\tmode = NoValue\n\t} else {\n\t\tret.Type = typ\n\t\tif val != nil {\n\t\t\tret.Value = val\n\t\t\tmode = Constant\n\t\t} else {\n\t\t\tmode = Value\n\t\t}\n\t}\n\t(*TypeAndValue)(unsafe.Pointer(&ret)).mode = mode\n\treturn\n}\n\nfunc NewTypeAndValueForObject(obj types.Object) (ret types.TypeAndValue) {\n\tvar mode OperandMode\n\tvar val constant.Value\n\tswitch v := obj.(type) {\n\tcase *types.Const:\n\t\tmode = Constant\n\t\tval = v.Val()\n\tcase *types.TypeName:\n\t\tmode = TypExpr\n\tcase *types.Var:\n\t\tmode = Variable\n\tcase *types.Func:\n\t\tmode = Value\n\tcase *types.Builtin:\n\t\tmode = Builtin\n\tcase *types.Nil:\n\t\tmode = Value\n\t}\n\tret.Type = obj.Type()\n\tret.Value = val\n\t(*TypeAndValue)(unsafe.Pointer(&ret)).mode = mode\n\treturn\n}\n"
  },
  {
    "path": "cl/internal/typesutil/api_test.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\nimport (\n\t\"go/constant\"\n\t\"go/types\"\n\t\"testing\"\n\n\t\"github.com/goplus/gogen\"\n)\n\nfunc TestTypeAndValue(t *testing.T) {\n\ttyInt := types.Typ[types.Int]\n\tty := gogen.NewTypeType(tyInt)\n\tret := NewTypeAndValueForType(ty)\n\tif !ret.IsType() {\n\t\tt.Fatal(\"NewTypeAndValueForType: not type?\")\n\t}\n\tret = NewTypeAndValueForValue(tyInt, constant.MakeInt64(1), Constant)\n\tif ret.Value == nil {\n\t\tt.Fatal(\"NewTypeAndValueForValue: not const?\")\n\t}\n\tret = NewTypeAndValueForValue(ty, constant.MakeInt64(1), Constant)\n\tif ret.Value == nil {\n\t\tt.Fatal(\"NewTypeAndValueForValue: not const?\")\n\t}\n\tret = NewTypeAndValueForCallResult(tyInt, nil)\n\tif !ret.IsValue() {\n\t\tt.Fatal(\"NewTypeAndValueForCall: not value?\")\n\t}\n\tret = NewTypeAndValueForCallResult(tyInt, constant.MakeInt64(1))\n\tif !ret.IsValue() || ret.Value == nil {\n\t\tt.Fatal(\"NewTypeAndValueForCall: not const?\")\n\t}\n\tret = NewTypeAndValueForCallResult(nil, nil)\n\tif !ret.IsVoid() {\n\t\tt.Fatal(\"NewTypeAndValueForCall: not void?\")\n\t}\n\tpkg := types.NewPackage(\"main\", \"main\")\n\tret = NewTypeAndValueForObject(types.NewConst(0, pkg, \"v\", tyInt, constant.MakeInt64(100)))\n\tif ret.Value == nil {\n\t\tt.Fatal(\"NewTypeAndValueForObject: not const?\")\n\t}\n\tret = NewTypeAndValueForObject(types.NewTypeName(0, pkg, \"MyInt\", tyInt))\n\tif !ret.IsType() {\n\t\tt.Fatal(\"NewTypeAndValueForObject: not type?\")\n\t}\n\tret = NewTypeAndValueForObject(types.NewVar(0, pkg, \"v\", tyInt))\n\tif !ret.Addressable() {\n\t\tt.Fatal(\"NewTypeAndValueForObject: not variable?\")\n\t}\n\tret = NewTypeAndValueForObject(types.NewFunc(0, pkg, \"fn\", types.NewSignature(nil, nil, nil, false)))\n\tif !ret.IsValue() {\n\t\tt.Fatal(\"NewTypeAndValueForObject: not value?\")\n\t}\n\tret = NewTypeAndValueForValue(types.Typ[types.UntypedNil], nil, Value)\n\tif !ret.IsNil() {\n\t\tt.Fatal(\"NewTypeAndValueForValue: not nil?\")\n\t}\n\tret = NewTypeAndValueForObject(types.Universe.Lookup(\"nil\"))\n\tif !ret.IsNil() {\n\t\tt.Fatal(\"NewTypeAndValueForObject: not nil?\")\n\t}\n\tret = NewTypeAndValueForObject(types.Universe.Lookup(\"len\"))\n\tif !ret.IsBuiltin() {\n\t\tt.Fatal(\"NewTypeAndValueForObject: not builtin?\")\n\t}\n}\n"
  },
  {
    "path": "cl/internal/typesutil/mode.go",
    "content": "//go:build !go1.23\n// +build !go1.23\n\n/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\nconst (\n\tInvalid  OperandMode = iota // operand is invalid\n\tNoValue                     // operand represents no value (result of a function call w/o result)\n\tBuiltin                     // operand is a built-in function\n\tTypExpr                     // operand is a type\n\tConstant                    // operand is a constant; the operand's typ is a Basic type\n\tVariable                    // operand is an addressable variable\n\tMapIndex                    // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)\n\tValue                       // operand is a computed value\n\tCommaOK                     // like value, but operand may be used in a comma,ok expression\n\tCommaErr                    // like commaok, but second value is error, not boolean\n\tCgoFunc                     // operand is a cgo function\n)\n"
  },
  {
    "path": "cl/internal/typesutil/mode_go123.go",
    "content": "//go:build go1.23\n// +build go1.23\n\n/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\nconst (\n\tInvalid  OperandMode = iota // operand is invalid\n\tNoValue                     // operand represents no value (result of a function call w/o result)\n\tBuiltin                     // operand is a built-in function\n\tTypExpr                     // operand is a type\n\tConstant                    // operand is a constant; the operand's typ is a Basic type\n\tVariable                    // operand is an addressable variable\n\tMapIndex                    // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)\n\tValue                       // operand is a computed value\n\tNilValue                    // operand is the nil value - only used by types2\n\tCommaOK                     // like value, but operand may be used in a comma,ok expression\n\tCommaErr                    // like commaok, but second value is error, not boolean\n\tCgoFunc                     // operand is a cgo function\n)\n"
  },
  {
    "path": "cl/internal/unit/unit.go",
    "content": "package unit\n\n// -----------------------------------------------------------------------------\n\ntype Distance int\n\nconst XGou_Distance = \"mm=1,cm=10,dm=100,m=1000\"\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/outline/outline.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage outline\n\nimport (\n\t\"go/types\"\n\t\"strings\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/gogen/typeutil\"\n\t\"github.com/goplus/mod/modfile\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype Project = modfile.Project\n\ntype Config struct {\n\t// Fset provides source position information for syntax trees and types.\n\t// If Fset is nil, Load will use a new fileset, but preserve Fset's value.\n\tFset *token.FileSet\n\n\t// LookupClass lookups a class by specified file extension.\n\tLookupClass func(ext string) (c *Project, ok bool)\n\n\t// An Importer resolves import paths to Packages.\n\tImporter types.Importer\n}\n\ntype Package struct {\n\tpkg  *types.Package\n\tdocs gogen.ObjectDocs\n}\n\n// NewPackage creates a Go/XGo outline package.\nfunc NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (_ Package, err error) {\n\tret, err := cl.NewPackage(pkgPath, pkg, &cl.Config{\n\t\tFset:           conf.Fset,\n\t\tLookupClass:    conf.LookupClass,\n\t\tImporter:       conf.Importer,\n\t\tNoFileLine:     true,\n\t\tNoAutoGenMain:  true,\n\t\tNoSkipConstant: true,\n\t\tOutline:        true,\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\treturn Package{ret.Types, ret.Docs}, nil\n}\n\nfunc (p Package) Pkg() *types.Package {\n\treturn p.pkg\n}\n\nfunc (p Package) Valid() bool {\n\treturn p.pkg != nil\n}\n\n// -----------------------------------------------------------------------------\n\ntype All struct {\n\tConsts []Const\n\tVars   []Var\n\tFuncs  []Func\n\tTypes  []*TypeName\n\n\tPackage\n\tnamed map[*types.TypeName]*TypeName\n}\n\nfunc setAlias(aliasr *typeutil.Map, t types.Type, named *TypeName) {\n\treal := indirect(t)\n\tif aliasr.Set(real, named) != nil { // conflict: has old value\n\t\taliasr.Set(real, nil)\n\t}\n}\n\n// aliasr typeutil.Map // types.Type => *TypeName\nfunc (p *All) checkAlias(aliasr *typeutil.Map, t types.Type, withBasic bool) *TypeName {\n\tif _, ok := t.(*types.Basic); !ok || withBasic {\n\t\tif v := aliasr.At(t); v != nil {\n\t\t\tnamed := v.(*TypeName)\n\t\t\tp.markUsed(named)\n\t\t\treturn named\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *All) markUsed(named *TypeName) {\n\tif !named.isUsed {\n\t\tnamed.isUsed = true\n\t\to := named.TypeName\n\t\ttyp := o.Type()\n\t\tp.checkUsed(typ.Underlying())\n\t\tif !o.IsAlias() {\n\t\t\tif t, ok := typ.(*types.Named); ok {\n\t\t\t\tfor i, n := 0, t.NumMethods(); i < n; i++ {\n\t\t\t\t\tp.checkUsedMethod(t.Method(i))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (p *All) checkUsed(typ types.Type) {\n\tswitch t := typ.(type) {\n\tcase *types.Basic:\n\tcase *types.Pointer:\n\t\tp.checkUsed(t.Elem())\n\tcase *types.Signature:\n\t\tp.checkUsedSig(t)\n\tcase *types.Slice:\n\t\tp.checkUsed(t.Elem())\n\tcase *types.Map:\n\t\tp.checkUsed(t.Key())\n\t\tp.checkUsed(t.Elem())\n\tcase *types.Struct:\n\t\tfor i, n := 0, t.NumFields(); i < n; i++ {\n\t\t\tfld := t.Field(i)\n\t\t\tif fld.Exported() {\n\t\t\t\tp.checkUsed(fld.Type())\n\t\t\t}\n\t\t}\n\tcase *types.Named:\n\t\to := t.Obj()\n\t\tif p.pkg == o.Pkg() {\n\t\t\tp.markUsed(p.getNamed(o))\n\t\t}\n\tcase *types.Interface:\n\t\tfor i, n := 0, t.NumExplicitMethods(); i < n; i++ {\n\t\t\tp.checkUsedMethod(t.ExplicitMethod(i))\n\t\t}\n\t\tfor i, n := 0, t.NumEmbeddeds(); i < n; i++ {\n\t\t\tp.checkUsed(t.EmbeddedType(i))\n\t\t}\n\tcase *types.Chan:\n\t\tp.checkUsed(t.Elem())\n\tcase *types.Array:\n\t\tp.checkUsed(t.Elem())\n\tdefault:\n\t\tpanic(\"checkUsed: unknown type - \" + typ.String())\n\t}\n}\n\nfunc (p *All) checkUsedMethod(fn *types.Func) {\n\tif fn.Exported() {\n\t\tp.checkUsedSig(fn.Type().(*types.Signature))\n\t}\n}\n\nfunc (p *All) checkUsedSig(sig *types.Signature) {\n\tp.checkUsedTuple(sig.Params())\n\tp.checkUsedTuple(sig.Results())\n}\n\nfunc (p *All) checkUsedTuple(t *types.Tuple) {\n\tfor i, n := 0, t.Len(); i < n; i++ {\n\t\tp.checkUsed(t.At(i).Type())\n\t}\n}\n\nfunc (p *All) getNamed(t *types.TypeName) *TypeName {\n\tif named, ok := p.named[t]; ok {\n\t\treturn named\n\t}\n\tpanic(\"getNamed: type not found - \" + t.Name())\n}\n\nfunc (p *All) initNamed(aliasr *typeutil.Map, objs []types.Object) {\n\tfor _, o := range objs {\n\t\tif t, ok := o.(*types.TypeName); ok {\n\t\t\tnamed := &TypeName{TypeName: t}\n\t\t\tp.named[t] = named\n\t\t\tp.Types = append(p.Types, named)\n\t\t\tif t.IsAlias() {\n\t\t\t\tsetAlias(aliasr, t.Type(), named)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (p *All) lookupNamed(pkg *types.Package, name string) (_ *TypeName, ok bool) {\n\to := pkg.Scope().Lookup(name)\n\tif o == nil {\n\t\treturn\n\t}\n\tt, ok := o.(*types.TypeName)\n\tif !ok {\n\t\treturn\n\t}\n\treturn p.getNamed(t), true\n}\n\nfunc (p Package) Outline(withUnexported ...bool) (ret *All) {\n\tpkg := p.Pkg()\n\tret = &All{\n\t\tPackage: p,\n\t\tnamed:   make(map[*types.TypeName]*TypeName),\n\t}\n\tall := (withUnexported != nil && withUnexported[0])\n\taliasr := &typeutil.Map{}\n\tscope := pkg.Scope()\n\tnames := scope.Names()\n\tobjs := make([]types.Object, len(names))\n\tfor i, name := range names {\n\t\tobjs[i] = scope.Lookup(name)\n\t}\n\tret.initNamed(aliasr, objs)\n\tfor _, o := range objs {\n\t\tif !(all || o.Exported()) {\n\t\t\tcontinue\n\t\t}\n\t\tif obj, ok := o.(*types.TypeName); ok {\n\t\t\tif !all {\n\t\t\t\tret.markUsed(ret.getNamed(obj))\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tswitch v := o.(type) {\n\t\tcase *types.Func:\n\t\t\tsig := v.Type().(*types.Signature)\n\t\t\tif !all {\n\t\t\t\tret.checkUsedSig(sig)\n\t\t\t}\n\t\t\tif name, ok := checkGoptFunc(o.Name()); ok {\n\t\t\t\tif named, ok := ret.lookupNamed(pkg, name); ok {\n\t\t\t\t\tnamed.GoptFuncs = append(named.GoptFuncs, Func{v, p.docs})\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tkind, named := ret.sigKind(aliasr, sig)\n\t\t\tswitch kind {\n\t\t\tcase sigNormal:\n\t\t\t\tret.Funcs = append(ret.Funcs, Func{v, p.docs})\n\t\t\tcase sigCreator:\n\t\t\t\tnamed.Creators = append(named.Creators, Func{v, p.docs})\n\t\t\tcase sigHelper:\n\t\t\t\tnamed.Helpers = append(named.Helpers, Func{v, p.docs})\n\t\t\t}\n\t\tcase *types.Const:\n\t\t\tif name := v.Name(); strings.HasPrefix(name, \"Gop\") || strings.HasPrefix(name, \"XGo\") {\n\t\t\t\tif name == \"GopPackage\" || name == \"XGoPackage\" || name == \"Gop_sched\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\ttyp := v.Type()\n\t\t\tif !all {\n\t\t\t\tret.checkUsed(typ)\n\t\t\t}\n\t\t\tif named := ret.checkLocal(aliasr, typ, true); named != nil {\n\t\t\t\tnamed.Consts = append(named.Consts, Const{v})\n\t\t\t} else {\n\t\t\t\tret.Consts = append(ret.Consts, Const{v})\n\t\t\t}\n\t\tcase *types.Var:\n\t\t\tif !all {\n\t\t\t\tret.checkUsed(v.Type())\n\t\t\t}\n\t\t\tret.Vars = append(ret.Vars, Var{v})\n\t\t}\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\ntype sigKindType int\n\nconst (\n\tsigNormal sigKindType = iota\n\tsigCreator\n\tsigHelper\n)\n\nfunc (p *All) sigKind(aliasr *typeutil.Map, sig *types.Signature) (sigKindType, *TypeName) {\n\trets := sig.Results()\n\tif rets.Len() > 0 {\n\t\tif t := p.checkLocal(aliasr, rets.At(0).Type(), false); t != nil {\n\t\t\treturn sigCreator, t\n\t\t}\n\t}\n\tparams := sig.Params()\n\tif params.Len() > 0 {\n\t\tif t := p.checkLocal(aliasr, params.At(0).Type(), false); t != nil {\n\t\t\treturn sigHelper, t\n\t\t}\n\t}\n\treturn sigNormal, nil\n}\n\nfunc (p *All) checkLocal(aliasr *typeutil.Map, first types.Type, withBasic bool) *TypeName {\n\tfirst = indirect(first)\n\tif t, ok := first.(*types.Named); ok {\n\t\to := t.Obj()\n\t\tif o.Pkg() == p.pkg {\n\t\t\treturn p.getNamed(o)\n\t\t}\n\t}\n\treturn p.checkAlias(aliasr, first, withBasic)\n}\n\nfunc indirect(typ types.Type) types.Type {\n\tif t, ok := typ.(*types.Pointer); ok {\n\t\treturn t.Elem()\n\t}\n\treturn typ\n}\n\n// -----------------------------------------------------------------------------\n\ntype Const struct {\n\t*types.Const\n}\n\nfunc (p Const) Obj() types.Object {\n\treturn p.Const\n}\n\nfunc (p Const) Doc() string {\n\treturn \"\"\n}\n\ntype Var struct {\n\t*types.Var\n}\n\nfunc (p Var) Obj() types.Object {\n\treturn p.Var\n}\n\nfunc (p Var) Doc() string {\n\treturn \"\"\n}\n\ntype Func struct {\n\t*types.Func\n\tdocs gogen.ObjectDocs\n}\n\nfunc (p Func) Obj() types.Object {\n\treturn p.Func\n}\n\nfunc (p Func) Doc() string {\n\treturn p.docs[p.Func].Text()\n}\n\nfunc CheckOverload(obj types.Object) (name string, fn *types.Func, ok bool) {\n\tif fn, ok = obj.(*types.Func); ok {\n\t\tname, ok = checkOverloadFunc(fn.Name())\n\t}\n\treturn\n}\n\nconst (\n\tgoptPrefix = \"Gopt_\"\n)\n\nfunc isGoptFunc(name string) bool {\n\treturn strings.HasPrefix(name, goptPrefix)\n}\n\nfunc isOverloadFunc(name string) bool {\n\tn := len(name)\n\treturn n > 3 && name[n-3:n-1] == \"__\"\n}\n\nfunc checkGoptFunc(name string) (string, bool) {\n\tif isGoptFunc(name) {\n\t\tname = name[len(goptPrefix):]\n\t\tif pos := strings.IndexByte(name, '_'); pos > 0 {\n\t\t\treturn name[:pos], true\n\t\t}\n\t}\n\treturn \"\", false\n}\n\nfunc checkOverloadFunc(name string) (string, bool) {\n\tif isOverloadFunc(name) {\n\t\treturn name[:len(name)-3], true\n\t}\n\treturn \"\", false\n}\n\n// -----------------------------------------------------------------------------\n\ntype TypeName struct {\n\t*types.TypeName\n\tConsts    []Const\n\tCreators  []Func\n\tGoptFuncs []Func\n\tHelpers   []Func\n\tisUsed    bool\n}\n\nfunc (p *TypeName) IsUsed() bool {\n\treturn p.isUsed\n}\n\nfunc (p *TypeName) ObjWith(all bool) *types.TypeName {\n\to := p.TypeName\n\tif all {\n\t\treturn o\n\t}\n\treturn hideUnexported(o)\n}\n\nfunc (p *TypeName) Obj() types.Object {\n\treturn p.TypeName\n}\n\nfunc (p *TypeName) Doc() string {\n\treturn \"\"\n}\n\nfunc (p *TypeName) Type() Type {\n\treturn Type{p.TypeName.Type()}\n}\n\nfunc hideUnexported(o *types.TypeName) *types.TypeName {\n\tif o.IsAlias() {\n\t\tif t, ok := typeHideUnexported(o.Type()); ok {\n\t\t\treturn types.NewTypeName(o.Pos(), o.Pkg(), o.Name(), t)\n\t\t}\n\t} else if named, ok := o.Type().(*types.Named); ok {\n\t\tif t, ok := typeHideUnexported(named.Underlying()); ok {\n\t\t\tname := types.NewTypeName(o.Pos(), o.Pkg(), o.Name(), nil)\n\t\t\tn := named.NumMethods()\n\t\t\tvar fns []*types.Func\n\t\t\tif n > 0 {\n\t\t\t\tfns = make([]*types.Func, n)\n\t\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\t\tfns[i] = named.Method(i)\n\t\t\t\t}\n\t\t\t}\n\t\t\ttypes.NewNamed(name, t, fns)\n\t\t\treturn name\n\t\t}\n\t}\n\treturn o\n}\n\nfunc typeHideUnexported(typ types.Type) (ret types.Type, ok bool) {\n\tswitch t := typ.(type) {\n\tcase *types.Struct:\n\t\tn := t.NumFields()\n\t\tfor i := 0; i < n; i++ {\n\t\t\tfld := t.Field(i)\n\t\t\tif !fld.Exported() || t.Tag(i) != \"\" {\n\t\t\t\tok = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif ok {\n\t\t\tflds := make([]*types.Var, 0, n)\n\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\tfld := t.Field(i)\n\t\t\t\tif fld.Exported() {\n\t\t\t\t\tflds = append(flds, fld)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(flds) < n {\n\t\t\t\tflds = append(flds, types.NewField(token.NoPos, nil, \"\", tyUnexp, true))\n\t\t\t}\n\t\t\tret = types.NewStruct(flds, nil)\n\t\t}\n\t}\n\treturn\n}\n\ntype tyUnexpImp struct{}\n\nfunc (p tyUnexpImp) String() string         { return \"...\" }\nfunc (p tyUnexpImp) Underlying() types.Type { return p }\n\nvar (\n\ttyUnexp types.Type = tyUnexpImp{}\n)\n\n// -----------------------------------------------------------------------------\n\ntype Type struct {\n\ttypes.Type\n}\n\nfunc (p Type) CheckNamed(pkg Package) (_ Named, ok bool) {\n\tret, ok := p.Type.(*types.Named)\n\tif ok && ret.Obj().Pkg() == pkg.pkg {\n\t\treturn Named{ret, pkg.docs}, true\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\ntype Named struct {\n\t*types.Named\n\tdocs gogen.ObjectDocs\n}\n\nfunc (p Named) Methods() []Func {\n\tn := p.NumMethods()\n\tret := make([]Func, n)\n\tfor i := 0; i < n; i++ {\n\t\tfn := p.Method(i)\n\t\tret[i] = Func{fn, p.docs}\n\t}\n\treturn ret\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/recorder.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\t\"go/types\"\n\t\"strings\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/ast/fromgo\"\n\t\"github.com/goplus/xgo/cl/internal/typesutil\"\n\t\"github.com/goplus/xgo/token\"\n)\n\ntype goxRecorder struct {\n\tRecorder\n\ttypes     map[ast.Expr]types.TypeAndValue\n\treferDefs map[*ast.Ident]ast.Node\n\treferUses map[string][]*ast.Ident\n}\n\nfunc newRecorder(rec Recorder) *goxRecorder {\n\ttypes := make(map[ast.Expr]types.TypeAndValue)\n\treferDefs := make(map[*ast.Ident]ast.Node)\n\treferUses := make(map[string][]*ast.Ident)\n\treturn &goxRecorder{rec, types, referDefs, referUses}\n}\n\n// Refer uses maps identifiers to name for ast.OverloadFuncDecl.\nfunc (p *goxRecorder) ReferUse(ident *ast.Ident, name string) {\n\tp.referUses[name] = append(p.referUses[name], ident)\n}\n\n// Refer def maps for ast.FuncLit or ast.OverloadFuncDecl.\nfunc (p *goxRecorder) ReferDef(ident *ast.Ident, node ast.Node) {\n\tp.referDefs[ident] = node\n}\n\n// Complete computes the types record.\nfunc (p *goxRecorder) Complete(scope *types.Scope) {\n\tfor id, node := range p.referDefs {\n\t\tswitch fn := node.(type) {\n\t\tcase *ast.FuncLit:\n\t\t\tif obj := scope.Lookup(id.Name); obj != nil {\n\t\t\t\tp.recordFuncLit(fn, obj.Type())\n\t\t\t\tp.Implicit(node, obj)\n\t\t\t}\n\t\tcase *ast.OverloadFuncDecl:\n\t\t\tif fn.Recv == nil {\n\t\t\t\tif obj := scope.Lookup(id.Name); obj != nil {\n\t\t\t\t\tp.Def(id, obj)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif obj := scope.Lookup(fn.Recv.List[0].Type.(*ast.Ident).Name); obj != nil {\n\t\t\t\t\tif named, ok := obj.Type().(*types.Named); ok {\n\t\t\t\t\t\tn := named.NumMethods()\n\t\t\t\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\t\t\t\tif m := named.Method(i); m.Name() == id.Name {\n\t\t\t\t\t\t\t\tp.Def(id, m)\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfor name, idents := range p.referUses {\n\t\tpos := strings.Index(name, \".\")\n\t\tif pos == -1 {\n\t\t\tif obj := scope.Lookup(name); obj != nil {\n\t\t\t\tfor _, id := range idents {\n\t\t\t\t\tp.Use(id, obj)\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif obj := scope.Lookup(name[:pos]); obj != nil {\n\t\t\tif named, ok := obj.Type().(*types.Named); ok {\n\t\t\t\tn := named.NumMethods()\n\t\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\t\tif m := named.Method(i); m.Name() == name[pos+1:] {\n\t\t\t\t\t\tfor _, id := range idents {\n\t\t\t\t\t\t\tp.Use(id, m)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tp.types = nil\n\tp.referDefs = nil\n\tp.referUses = nil\n}\n\n// Member maps identifiers to the objects they denote.\nfunc (p *goxRecorder) Member(id ast.Node, obj types.Object) {\n\tswitch v := id.(type) {\n\tcase *ast.SelectorExpr:\n\t\tsel := v.Sel\n\t\t// TODO: record event for a Go ident\n\t\tif _, ok := fromgo.CheckIdent(sel); !ok {\n\t\t\tvar tv types.TypeAndValue\n\t\t\t// check v.X call result by value\n\t\t\tif f, ok := obj.(*types.Var); ok && f.IsField() && p.checkExprByValue(v.X) {\n\t\t\t\ttv = typesutil.NewTypeAndValueForValue(obj.Type(), nil, typesutil.Value)\n\t\t\t} else {\n\t\t\t\ttv = typesutil.NewTypeAndValueForObject(obj)\n\t\t\t}\n\t\t\tp.Use(sel, obj)\n\t\t\tp.Type(v, tv)\n\t\t}\n\tcase *ast.Ident: // it's in a classfile and impossible converted from Go\n\t\tp.Use(v, obj)\n\t\tp.Type(v, typesutil.NewTypeAndValueForObject(obj))\n\t}\n}\n\nfunc (p *goxRecorder) Call(id ast.Node, obj types.Object) {\n\tswitch v := id.(type) {\n\tcase *ast.Ident:\n\t\tp.Use(v, obj)\n\t\tp.Type(v, typesutil.NewTypeAndValueForObject(obj))\n\tcase *ast.SelectorExpr:\n\t\tp.Use(v.Sel, obj)\n\t\tp.Type(v, typesutil.NewTypeAndValueForObject(obj))\n\tcase *ast.CallExpr:\n\t\tswitch id := v.Fun.(type) {\n\t\tcase *ast.Ident:\n\t\t\tp.Use(id, obj)\n\t\tcase *ast.SelectorExpr:\n\t\t\tp.Use(id.Sel, obj)\n\t\t}\n\t\tp.Type(v.Fun, typesutil.NewTypeAndValueForObject(obj))\n\t}\n}\n\nfunc (rec *goxRecorder) checkExprByValue(v ast.Expr) bool {\n\tif tv, ok := rec.types[v]; ok {\n\t\tswitch v.(type) {\n\t\tcase *ast.CallExpr:\n\t\t\tif _, ok := tv.Type.(*types.Pointer); !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\tdefault:\n\t\t\tif tv, ok := rec.types[v]; ok {\n\t\t\t\treturn !tv.Addressable()\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (rec *goxRecorder) Type(expr ast.Expr, tv types.TypeAndValue) {\n\trec.types[expr] = tv\n\trec.Recorder.Type(expr, tv)\n}\n\nfunc (rec *goxRecorder) instantiate(expr ast.Expr, _, typ types.Type) {\n\t// check gox TyOverloadNamed\n\tif tv, ok := rec.types[expr]; ok {\n\t\ttv.Type = typ\n\t\trec.Recorder.Type(expr, tv)\n\t}\n\tvar ident *ast.Ident\n\tswitch id := expr.(type) {\n\tcase *ast.Ident:\n\t\tident = id\n\tcase *ast.SelectorExpr:\n\t\tident = id.Sel\n\t}\n\tif ident != nil {\n\t\tif named, ok := typ.(*types.Named); ok {\n\t\t\trec.Use(ident, named.Obj())\n\t\t}\n\t}\n}\n\nfunc (rec *goxRecorder) recordTypeValue(ctx *blockCtx, expr ast.Expr, mode typesutil.OperandMode) {\n\te := ctx.cb.Get(-1)\n\tt, _ := gogen.DerefType(e.Type)\n\trec.Type(expr, typesutil.NewTypeAndValueForValue(t, e.CVal, mode))\n}\n\nfunc (rec *goxRecorder) indexExpr(ctx *blockCtx, expr *ast.IndexExpr) {\n\tif tv, ok := rec.types[expr.X]; ok {\n\t\tswitch tv.Type.(type) {\n\t\tcase *types.Map:\n\t\t\trec.recordTypeValue(ctx, expr, typesutil.MapIndex)\n\t\t\treturn\n\t\tcase *types.Slice:\n\t\t\trec.recordTypeValue(ctx, expr, typesutil.Variable)\n\t\t\treturn\n\t\t}\n\t}\n\top := typesutil.Variable\n\tswitch e := expr.X.(type) {\n\tcase *ast.CompositeLit:\n\t\top = typesutil.Value\n\tcase *ast.SelectorExpr:\n\t\tif rec.checkExprByValue(e.X) {\n\t\t\top = typesutil.Value\n\t\t}\n\t}\n\trec.recordTypeValue(ctx, expr, op)\n}\n\nfunc (rec *goxRecorder) unaryExpr(ctx *blockCtx, expr *ast.UnaryExpr) {\n\tswitch expr.Op {\n\tcase token.ARROW:\n\t\trec.recordTypeValue(ctx, expr, typesutil.CommaOK)\n\tdefault:\n\t\trec.recordTypeValue(ctx, expr, typesutil.Value)\n\t}\n}\n\nfunc (rec *goxRecorder) recordCallExpr(ctx *blockCtx, v *ast.CallExpr, fnt types.Type) {\n\te := ctx.cb.Get(-1)\n\tif _, ok := rec.types[v.Fun]; !ok {\n\t\trec.Type(v.Fun, typesutil.NewTypeAndValueForValue(fnt, nil, typesutil.Value))\n\t}\n\trec.Type(v, typesutil.NewTypeAndValueForCallResult(e.Type, e.CVal))\n}\n\nfunc (rec *goxRecorder) recordCompositeLit(v *ast.CompositeLit, typ types.Type) {\n\trec.Type(v.Type, typesutil.NewTypeAndValueForType(typ))\n\trec.Type(v, typesutil.NewTypeAndValueForValue(typ, nil, typesutil.Value))\n}\n\nfunc (rec *goxRecorder) recordFuncLit(v *ast.FuncLit, typ types.Type) {\n\trec.Type(v.Type, typesutil.NewTypeAndValueForType(typ))\n\trec.Type(v, typesutil.NewTypeAndValueForValue(typ, nil, typesutil.Value))\n}\n\nfunc (rec *goxRecorder) recordType(typ ast.Expr, t types.Type) {\n\trec.Type(typ, typesutil.NewTypeAndValueForType(t))\n}\n\nfunc (rec *goxRecorder) recordIdent(ident *ast.Ident, obj types.Object) {\n\trec.Use(ident, obj)\n\trec.Type(ident, typesutil.NewTypeAndValueForObject(obj))\n}\n\nfunc (rec *goxRecorder) recordExpr(ctx *blockCtx, expr ast.Expr, _ bool) {\n\tswitch v := expr.(type) {\n\tcase *ast.Ident:\n\tcase *ast.BasicLit:\n\t\trec.recordTypeValue(ctx, v, typesutil.Value)\n\tcase *ast.CallExpr:\n\tcase *ast.SelectorExpr:\n\t\tif _, ok := rec.types[v]; !ok {\n\t\t\trec.recordTypeValue(ctx, v, typesutil.Variable)\n\t\t}\n\tcase *ast.BinaryExpr:\n\t\trec.recordTypeValue(ctx, v, typesutil.Value)\n\tcase *ast.UnaryExpr:\n\t\trec.unaryExpr(ctx, v)\n\tcase *ast.FuncLit:\n\tcase *ast.CompositeLit:\n\tcase *ast.SliceLit:\n\tcase *ast.RangeExpr:\n\tcase *ast.IndexExpr:\n\t\trec.indexExpr(ctx, v)\n\tcase *ast.IndexListExpr:\n\tcase *ast.SliceExpr:\n\t\trec.recordTypeValue(ctx, v, typesutil.Value)\n\tcase *ast.StarExpr:\n\t\trec.recordTypeValue(ctx, v, typesutil.Variable)\n\tcase *ast.ArrayType:\n\t\trec.recordTypeValue(ctx, v, typesutil.TypExpr)\n\tcase *ast.MapType:\n\t\trec.recordTypeValue(ctx, v, typesutil.TypExpr)\n\tcase *ast.StructType:\n\t\trec.recordTypeValue(ctx, v, typesutil.TypExpr)\n\tcase *ast.ChanType:\n\t\trec.recordTypeValue(ctx, v, typesutil.TypExpr)\n\tcase *ast.InterfaceType:\n\t\trec.recordTypeValue(ctx, v, typesutil.TypExpr)\n\tcase *ast.ComprehensionExpr:\n\tcase *ast.TypeAssertExpr:\n\t\trec.recordTypeValue(ctx, v, typesutil.CommaOK)\n\tcase *ast.ParenExpr:\n\t\trec.recordTypeValue(ctx, v, typesutil.Value)\n\tcase *ast.ErrWrapExpr:\n\tcase *ast.FuncType:\n\t\trec.recordTypeValue(ctx, v, typesutil.TypExpr)\n\tcase *ast.Ellipsis:\n\tcase *ast.KeyValueExpr:\n\tdefault:\n\t}\n}\n"
  },
  {
    "path": "cl/run_test.go",
    "content": "package cl_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cl/cltest\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/parser/fsx/memfs\"\n\t\"github.com/goplus/xgo/scanner\"\n)\n\nvar (\n\ttmpDir     string\n\ttmpFileIdx int64\n)\n\nfunc init() {\n\thome, err := os.Getwd()\n\tcheck(err)\n\n\ttmpDir = home + \"/.xgo/tmp/\"\n\terr = os.MkdirAll(tmpDir, 0755)\n\tcheck(err)\n}\n\nfunc check(err error) {\n\tif err != nil {\n\t\tlog.Panicln(err)\n\t}\n}\n\nfunc checkWith(err error, stdout, stderr io.Writer) {\n\tif err != nil {\n\t\tfatalWith(err, stdout, stderr)\n\t}\n}\n\nfunc fatalWith(err error, stdout, stderr io.Writer) {\n\tif o, ok := getBytes(stdout, stderr); ok {\n\t\tos.Stderr.Write(o.Bytes())\n\t}\n\tlog.Panicln(err)\n}\n\ntype iBytes interface {\n\tBytes() []byte\n}\n\nfunc getBytes(stdout, stderr io.Writer) (o iBytes, ok bool) {\n\tif o, ok = stderr.(iBytes); ok {\n\t\treturn\n\t}\n\to, ok = stdout.(iBytes)\n\treturn\n}\n\nfunc goRun(_ *testing.T, code []byte) string {\n\tidx := atomic.AddInt64(&tmpFileIdx, 1)\n\tinfile := tmpDir + strconv.FormatInt(idx, 10) + \".go\"\n\terr := os.WriteFile(infile, []byte(code), 0666)\n\tcheck(err)\n\n\tvar stdout, stderr bytes.Buffer\n\tcmd := exec.Command(\"go\", \"run\", infile)\n\tcmd.Dir = tmpDir\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\terr = cmd.Run()\n\tos.Remove(infile)\n\tcheckWith(err, &stdout, &stderr)\n\treturn stdout.String()\n}\n\nfunc genGo(t *testing.T, conf *cl.Config, gopcode string) []byte {\n\tcl.SetDisableRecover(true)\n\tdefer cl.SetDisableRecover(false)\n\n\tfs := memfs.SingleFile(\"/foo\", \"bar.xgo\", gopcode)\n\tpkgs, err := parser.ParseFSDir(cltest.Conf.Fset, fs, \"/foo\", parser.Config{Mode: parser.ParseComments})\n\tif err != nil {\n\t\tscanner.PrintError(os.Stderr, err)\n\t\tt.Fatal(\"ParseFSDir:\", err)\n\t}\n\tpkg, err := cl.NewPackage(\"\", pkgs[\"main\"], conf)\n\tif err != nil {\n\t\tt.Fatal(\"NewPackage:\", err)\n\t}\n\tvar b bytes.Buffer\n\terr = pkg.WriteTo(&b)\n\tif err != nil {\n\t\tt.Fatal(\"gogen.WriteTo failed:\", err)\n\t}\n\treturn b.Bytes()\n}\n\nfunc testRun(t *testing.T, gopcode, expected string) {\n\tcode := genGo(t, cltest.Conf, gopcode)\n\tresult := goRun(t, code)\n\tif result != expected {\n\t\tt.Fatalf(\"=> Result:\\n%s\\n=> Expected:\\n%s\\n\", result, expected)\n\t}\n}\n\nfunc testRunType(t *testing.T, typ, gopcodeT, expected string) {\n\tgopcode := strings.ReplaceAll(gopcodeT, \"$(type)\", typ)\n\ttestRun(t, gopcode, expected)\n}\n\n// -----------------------------------------------------------------------------\n\nconst (\n\ttestType_inc_code, testType_inc_ret = `\n{\n\tvar x $(type) = 1\n\tvar y = +x\n\tx++\n\tx+=10\n\tprintln x, y\n}\n`, `12 1\n`\n\ttestType_dec_code, testType_dec_ret = `\n{\n\tvar x $(type) = 0\n\tx--\n\tprintln x\n}\n`, `-1\n`\n\ttestType_init_code, testType_init_ret = `\n{\n\tvar x $(type) = 1 << 65\n\tvar y = x >> 63\n\tvar z = x >> 65\n\tprintln x\n\tprintln y, z\n}\n`, `36893488147419103232\n4 1\n`\n\ttestType_twoval_code, testType_twoval_ret = `\n{\n\tvar x $(type) = 1 << 65\n\tvar y = (x >> 2) - 1\n\tv1, ok1 := int64(x)\n\tv2, ok2 := int64(y)\n\tprintln v1, ok1\n\tprintln v2, ok2\n}\n`, `0 false\n9223372036854775807 true\n`\n\ttestType_cast_code, testType_cast_ret = `\n{\n\tprintln $(type)(1 << 65), $(type)()\n}\n`, `36893488147419103232 0\n`\n\ttestType_printf_code, testType_printf_ret = `\n{\n\tvar x $(type) = 1\n\tprintf \"%4d\\n\", x\n}\n`, `   1\n`\n\ttestTypescanf_code, testType_scanf_ret = `\nimport \"fmt\"\n{\n\tvar name string\n\tvar age $(type)\n\tfmt.Sscanf(\"Kim is 22 years old\", \"%s is %d years old\", &name, &age)\n\tprintln name, age\n}\n`, `Kim 22\n`\n)\n\nconst (\n\ttestType_com_code, testType_com_ret = testType_inc_code + testType_init_code + testType_cast_code + testType_printf_code + testType_twoval_code,\n\t\ttestType_inc_ret + testType_init_ret + testType_cast_ret + testType_printf_ret + testType_twoval_ret\n\ttestType_fixint_code, testType_fixint_ret = testTypescanf_code + testType_com_code,\n\t\ttestType_scanf_ret + testType_com_ret\n)\n\nfunc TestUint128_run(t *testing.T) {\n\ttestRunType(t, \"uint128\", testType_fixint_code, testType_fixint_ret)\n}\n\nfunc TestInt128_run(t *testing.T) {\n\ttestRunType(t, \"int128\", testType_fixint_code+testType_dec_code, testType_fixint_ret+testType_dec_ret)\n}\n\nfunc TestBigint_run(t *testing.T) {\n\ttestRunType(t, \"bigint\", testType_com_code+testType_dec_code, testType_com_ret+testType_dec_ret)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/stmt.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\t\"fmt\"\n\t\"go/constant\"\n\t\"log\"\n\t\"path/filepath\"\n\n\tgoast \"go/ast\"\n\tgotoken \"go/token\"\n\t\"go/types\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nfunc fileLineFile(relBaseDir, absFile string) string {\n\tif ret, err := filepath.Rel(relBaseDir, absFile); err == nil {\n\t\treturn filepath.ToSlash(ret)\n\t}\n\treturn absFile\n}\n\nfunc relFile(dir string, absFile string) string {\n\tif dir != \"\" {\n\t\treturn fileLineFile(dir, absFile)\n\t}\n\treturn absFile\n}\n\nfunc commentStmt(ctx *blockCtx, stmt ast.Stmt) {\n\tif ctx.fileLine {\n\t\tcommentStmtEx(ctx.cb, ctx.pkgCtx, stmt)\n\t}\n}\n\nfunc commentStmtEx(cb *gogen.CodeBuilder, ctx *pkgCtx, stmt ast.Stmt) {\n\tstart := stmt.Pos()\n\tif start == token.NoPos {\n\t\tcb.SetComments(nil, false)\n\t\treturn\n\t}\n\tif doc := checkStmtDoc(stmt); doc != nil {\n\t\tstart = doc.Pos()\n\t}\n\tpos := ctx.fset.Position(start)\n\tif ctx.relBaseDir != \"\" {\n\t\tpos.Filename = fileLineFile(ctx.relBaseDir, pos.Filename)\n\t}\n\tline := fmt.Sprintf(\"\\n//line %s:%d:1\", pos.Filename, pos.Line)\n\tcomments := &goast.CommentGroup{\n\t\tList: []*goast.Comment{{Text: line}},\n\t}\n\tcb.SetComments(comments, false)\n}\n\nfunc checkStmtDoc(stmt ast.Stmt) *ast.CommentGroup {\n\tif decl, ok := stmt.(*ast.DeclStmt); ok {\n\t\tif d, ok := decl.Decl.(*ast.GenDecl); ok {\n\t\t\treturn d.Doc\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc commentFunc(ctx *blockCtx, fn *gogen.Func, decl *ast.FuncDecl) {\n\tstart := decl.Name.Pos()\n\tif ctx.fileLine && start != token.NoPos {\n\t\tif decl.Doc != nil {\n\t\t\tstart = decl.Doc.Pos()\n\t\t}\n\t\tpos := ctx.fset.Position(start)\n\t\tif ctx.relBaseDir != \"\" {\n\t\t\tpos.Filename = fileLineFile(ctx.relBaseDir, pos.Filename)\n\t\t}\n\t\tvar line string\n\t\tif decl.Shadow {\n\t\t\tline = fmt.Sprintf(\"//line %s:%d\", pos.Filename, pos.Line)\n\t\t} else {\n\t\t\tline = fmt.Sprintf(\"//line %s:%d:1\", pos.Filename, pos.Line)\n\t\t}\n\t\tdoc := &goast.CommentGroup{}\n\t\tdoc.List = append(doc.List, &goast.Comment{Text: line})\n\t\tif decl.Doc != nil {\n\t\t\tdoc.List = append(doc.List, decl.Doc.List...)\n\t\t}\n\t\tfn.SetComments(ctx.pkg, doc)\n\t} else if decl.Doc != nil {\n\t\tfn.SetComments(ctx.pkg, decl.Doc)\n\t}\n}\n\nfunc compileStmts(ctx *blockCtx, body []ast.Stmt) {\n\tfor _, stmt := range body {\n\t\tif v, ok := stmt.(*ast.LabeledStmt); ok {\n\t\t\texpr := v.Label\n\t\t\tctx.cb.NewLabel(expr.Pos(), expr.End(), expr.Name)\n\t\t}\n\t}\n\tfor _, stmt := range body {\n\t\tcompileStmt(ctx, stmt)\n\t}\n}\n\nfunc compileStmt(ctx *blockCtx, stmt ast.Stmt) {\n\tif enableRecover {\n\t\tdefer func() {\n\t\t\tif e := recover(); e != nil {\n\t\t\t\tctx.handleRecover(e, stmt)\n\t\t\t\tctx.cb.ResetStmt()\n\t\t\t}\n\t\t}()\n\t}\n\tcommentStmt(ctx, stmt)\n\tswitch v := stmt.(type) {\n\tcase *ast.ExprStmt:\n\t\tx := v.X\n\t\tinFlags := checkCommandWithoutArgs(x)\n\t\tcompileExpr(ctx, 0, x, inFlags)\n\tcase *ast.AssignStmt:\n\t\tcompileAssignStmt(ctx, v)\n\tcase *ast.ReturnStmt:\n\t\tcompileReturnStmt(ctx, v)\n\tcase *ast.IfStmt:\n\t\tcompileIfStmt(ctx, v)\n\tcase *ast.SwitchStmt:\n\t\tcompileSwitchStmt(ctx, v)\n\tcase *ast.RangeStmt:\n\t\tcompileRangeStmt(ctx, v)\n\tcase *ast.ForStmt:\n\t\tcompileForStmt(ctx, v)\n\tcase *ast.ForPhraseStmt:\n\t\tcompileForPhraseStmt(ctx, v)\n\tcase *ast.IncDecStmt:\n\t\tcompileIncDecStmt(ctx, v)\n\tcase *ast.DeferStmt:\n\t\tcompileDeferStmt(ctx, v)\n\tcase *ast.GoStmt:\n\t\tcompileGoStmt(ctx, v)\n\tcase *ast.DeclStmt:\n\t\tcompileDeclStmt(ctx, v)\n\tcase *ast.TypeSwitchStmt:\n\t\tcompileTypeSwitchStmt(ctx, v)\n\tcase *ast.SendStmt:\n\t\tcompileSendStmt(ctx, v)\n\tcase *ast.BranchStmt:\n\t\tcompileBranchStmt(ctx, v)\n\tcase *ast.LabeledStmt:\n\t\tcompileLabeledStmt(ctx, v)\n\tcase *ast.BlockStmt:\n\t\tctx.cb.Block()\n\t\tcompileStmts(ctx, v.List)\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Scope(v, ctx.cb.Scope())\n\t\t}\n\t\tctx.cb.End()\n\t\treturn\n\tcase *ast.SelectStmt:\n\t\tcompileSelectStmt(ctx, v)\n\tcase *ast.EmptyStmt:\n\t\t// do nothing\n\tdefault:\n\t\tlog.Panicf(\"compileStmt failed: unknown - %T\\n\", v)\n\t}\n\tctx.cb.EndStmt()\n}\n\nfunc checkCommandWithoutArgs(x ast.Expr) int {\nretry:\n\tif v, ok := x.(*ast.SelectorExpr); ok {\n\t\tx = v.X\n\t\tgoto retry\n\t}\n\tif _, ok := x.(*ast.Ident); ok {\n\t\treturn clCommandWithoutArgs\n\t}\n\treturn 0\n}\n\nfunc compileReturnStmt(ctx *blockCtx, expr *ast.ReturnStmt) {\n\t// Use defer to ensure Return is always called, even if argument compilation\n\t// fails. This guarantees the return statement is recorded in AST for control\n\t// flow analysis, preventing spurious \"missing return\" errors.\n\tdefer ctx.cb.Return(len(expr.Results), expr)\n\n\tvar n = -1\n\tvar results *types.Tuple\n\tfor i, ret := range expr.Results {\n\t\tif c, ok := ret.(*ast.CompositeLit); ok && c.Type == nil {\n\t\t\tif n < 0 {\n\t\t\t\tresults = ctx.cb.Func().Type().(*types.Signature).Results()\n\t\t\t\tn = results.Len()\n\t\t\t}\n\t\t\tvar typ types.Type\n\t\t\tif i < n {\n\t\t\t\ttyp = results.At(i).Type()\n\t\t\t}\n\t\t\tcompileCompositeLit(ctx, c, typ, true)\n\t\t} else {\n\t\t\tlhs := 0\n\t\t\tif len(expr.Results) == 1 {\n\t\t\t\tif _, ok := ret.(*ast.ComprehensionExpr); ok {\n\t\t\t\t\tresults = ctx.cb.Func().Type().(*types.Signature).Results()\n\t\t\t\t\tif results.Len() == 2 { // TODO(xsw): check this\n\t\t\t\t\t\tlhs = 2\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tswitch v := ret.(type) {\n\t\t\tcase *ast.LambdaExpr, *ast.LambdaExpr2:\n\t\t\t\trtyp := ctx.cb.Func().Type().(*types.Signature).Results().At(i).Type()\n\t\t\t\tsig, ok := rtyp.(*types.Signature)\n\t\t\t\tif !ok {\n\t\t\t\t\tpanic(ctx.newCodeErrorf(\n\t\t\t\t\t\tret.Pos(), ret.End(), \"cannot use lambda expression as type %v in return statement\", rtyp))\n\t\t\t\t}\n\t\t\t\tcompileLambda(ctx, v, sig)\n\t\t\tcase *ast.SliceLit:\n\t\t\t\trtyp := ctx.cb.Func().Type().(*types.Signature).Results().At(i).Type()\n\t\t\t\tcompileSliceLit(ctx, v, rtyp)\n\t\t\tdefault:\n\t\t\t\tcompileExpr(ctx, lhs, ret)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc compileIncDecStmt(ctx *blockCtx, expr *ast.IncDecStmt) {\n\tcompileExprLHS(ctx, expr.X)\n\tctx.cb.IncDec(gotoken.Token(expr.Tok))\n}\n\n// issue #2107:\n//\n//\ta <- v\n//\tfoo.a <- v\nfunc isAppendable(x ast.Expr) bool {\n\tswitch x := x.(type) {\n\tcase *ast.Ident:\n\t\treturn true\n\tcase *ast.SelectorExpr:\n\t\t_, ok := x.X.(*ast.Ident)\n\t\treturn ok\n\t}\n\treturn false\n}\n\nfunc compileSendStmt(ctx *blockCtx, expr *ast.SendStmt) {\n\tcb := ctx.cb\n\tch, vals := expr.Chan, expr.Values\n\tstk := cb.InternalStack()\n\tif isAppendable(ch) { // a <- v1, v2, v3 (issue #2107)\n\t\tcompileExpr(ctx, 1, ch)\n\t\ta := stk.Get(-1)\n\t\tt := a.Type.Underlying()\n\t\tif _, ok := t.(*types.Slice); ok { // a = append(a, v1, v2, v3)\n\t\t\tstk.Pop()\n\t\t\tcompileExprLHS(ctx, ch)\n\t\t\tcb.Val(ctx.pkg.Builtin().Ref(\"append\"))\n\t\t\tstk.Push(a)\n\t\t\tfor _, v := range vals {\n\t\t\t\tcompileExpr(ctx, 1, v)\n\t\t\t}\n\t\t\tflags := gogen.InstrFlags(0)\n\t\t\tif expr.Ellipsis != 0 { // a = append(a, b...)\n\t\t\t\tflags |= gogen.InstrFlagEllipsis\n\t\t\t}\n\t\t\tcb.CallWith(len(vals)+1, 0, flags, expr).AssignWith(1, 1, expr)\n\t\t\treturn\n\t\t}\n\t\tgoto normal\n\t}\n\tcompileExpr(ctx, 1, ch)\n\nnormal:\n\tif len(vals) != 1 || expr.Ellipsis != 0 {\n\t\tpanic(ctx.newCodeError(vals[0].Pos(), vals[0].End(), \"can't send multiple values to a channel\"))\n\t}\n\tcompileExpr(ctx, 1, vals[0])\n\tctx.cb.Send()\n}\n\nfunc compileAssignStmt(ctx *blockCtx, expr *ast.AssignStmt) {\n\ttok := expr.Tok\n\tlhs := 1\n\tif len(expr.Lhs) > 1 && len(expr.Rhs) == 1 {\n\t\tlhs = len(expr.Lhs)\n\t}\n\tif tok == token.DEFINE {\n\t\tstk := ctx.cb.InternalStack()\n\t\tbase := stk.Len()\n\t\tnames := make([]string, len(expr.Lhs))\n\t\tfor i, lhs := range expr.Lhs {\n\t\t\tif v, ok := lhs.(*ast.Ident); ok {\n\t\t\t\tnames[i] = v.Name\n\t\t\t} else {\n\t\t\t\tcompileExprLHS(ctx, lhs) // only for typesutil.Check\n\t\t\t\tlog.Panicln(\"TODO: non-name $v on left side of :=\")\n\t\t\t}\n\t\t}\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\tnewNames := make([]*ast.Ident, 0, len(names))\n\t\t\tscope := ctx.cb.Scope()\n\t\t\tfor _, lhs := range expr.Lhs {\n\t\t\t\tv := lhs.(*ast.Ident)\n\t\t\t\tif scope.Lookup(v.Name) == nil {\n\t\t\t\t\tnewNames = append(newNames, v)\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefer defNames(ctx, newNames, scope)\n\t\t}\n\t\tctx.cb.DefineVarStart(expr.Pos(), names...)\n\t\tif enableRecover {\n\t\t\tdefer func() {\n\t\t\t\tif e := recover(); e != nil {\n\t\t\t\t\tctx.cb.ResetInit()\n\t\t\t\t\tpanic(e)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t\tfor _, rhs := range expr.Rhs {\n\t\t\tcompileExpr(ctx, lhs, rhs)\n\t\t}\n\t\tctx.cb.EndInit(stk.Len() - base)\n\t\treturn\n\t}\n\tfor _, lhs := range expr.Lhs {\n\t\tcompileExprLHS(ctx, lhs)\n\t}\n\tfor i, rhs := range expr.Rhs {\n\t\tswitch e := unparen(rhs).(type) {\n\t\tcase *ast.LambdaExpr, *ast.LambdaExpr2:\n\t\t\tif len(expr.Lhs) == 1 && len(expr.Rhs) == 1 {\n\t\t\t\ttyp := ctx.cb.Get(-1).Type.(interface{ Elem() types.Type }).Elem()\n\t\t\t\tsig, err := checkLambdaFuncType(ctx, e, typ, clLambaAssign, expr.Lhs[0])\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\t\t\t\tcompileLambda(ctx, e, sig)\n\t\t\t} else {\n\t\t\t\tpanic(ctx.newCodeErrorf(e.Pos(), e.End(), \"lambda unsupport multiple assignment\"))\n\t\t\t}\n\t\tcase *ast.SliceLit:\n\t\t\tvar typ types.Type\n\t\t\tif len(expr.Lhs) == len(expr.Rhs) {\n\t\t\t\ttyp, _ = gogen.DerefType(ctx.cb.Get(-1 - i).Type)\n\t\t\t}\n\t\t\tcompileSliceLit(ctx, e, typ)\n\t\tcase *ast.CompositeLit:\n\t\t\tvar typ types.Type\n\t\t\tif len(expr.Lhs) == len(expr.Rhs) {\n\t\t\t\ttyp, _ = gogen.DerefType(ctx.cb.Get(-1 - i).Type)\n\t\t\t}\n\t\t\tcompileCompositeLit(ctx, e, typ, false)\n\t\tdefault:\n\t\t\tcompileExpr(ctx, lhs, rhs)\n\t\t}\n\t}\n\tif tok == token.ASSIGN {\n\t\tctx.cb.AssignWith(len(expr.Lhs), len(expr.Rhs), expr)\n\t\treturn\n\t}\n\tif len(expr.Lhs) != 1 || len(expr.Rhs) != 1 {\n\t\tpanic(\"TODO: invalid syntax of assign by operator\")\n\t}\n\tctx.cb.AssignOp(gotoken.Token(tok), expr)\n}\n\n// forRange(names...) x rangeAssignThen\n//\n//\tbody\n//\n// end\n// forRange k v x rangeAssignThen\n//\n//\tbody\n//\n// end\nfunc compileRangeStmt(ctx *blockCtx, v *ast.RangeStmt) {\n\tif re, ok := v.X.(*ast.RangeExpr); ok {\n\t\ttok := token.DEFINE\n\t\tif v.Tok == token.ASSIGN {\n\t\t\ttok = v.Tok\n\t\t}\n\t\tcompileForStmt(ctx, toForStmt(v.For, v.Key, v.Body, re, tok, nil))\n\t\treturn\n\t}\n\tcb := ctx.cb\n\tdefer cb.End(v)\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\tctx.handleRecover(r, v)\n\t\t\tcb.ResetStmt()\n\t\t}\n\t}()\n\tcomments, once := cb.BackupComments()\n\tdefineNames := make([]*ast.Ident, 0, 2)\n\tif v.Tok == token.DEFINE {\n\t\tnames := make([]string, 1, 2)\n\t\tif v.Key == nil {\n\t\t\tnames[0] = \"_\"\n\t\t} else {\n\t\t\tkey := v.Key.(*ast.Ident)\n\t\t\tnames[0] = key.Name\n\t\t\tdefineNames = append(defineNames, key)\n\t\t}\n\t\tif v.Value != nil {\n\t\t\tvalue := v.Value.(*ast.Ident)\n\t\t\tnames = append(names, value.Name)\n\t\t\tdefineNames = append(defineNames, value)\n\t\t}\n\t\tcb.ForRangeEx(names, v)\n\t} else {\n\t\tcb.ForRangeEx(nil, v)\n\t\tn := 0\n\t\tif v.Key == nil {\n\t\t\tif v.Value != nil {\n\t\t\t\tctx.cb.VarRef(nil) // underscore\n\t\t\t\tn++\n\t\t\t}\n\t\t} else {\n\t\t\tcompileExprLHS(ctx, v.Key)\n\t\t\tn++\n\t\t}\n\t\tif v.Value != nil {\n\t\t\tcompileExprLHS(ctx, v.Value)\n\t\t\tn++\n\t\t}\n\t}\n\tcompileExpr(ctx, 1, v.X)\n\tpos := v.TokPos\n\tif pos == 0 {\n\t\tpos = v.For\n\t}\n\tcb.RangeAssignThen(pos)\n\tif len(defineNames) > 0 {\n\t\tdefNames(ctx, defineNames, cb.Scope())\n\t}\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v, cb.Scope())\n\t}\n\tcb.VBlock()\n\tcompileStmts(ctx, v.Body.List)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v.Body, cb.Scope())\n\t}\n\tcb.End(v.Body)\n\tcb.SetComments(comments, once)\n\tsetBodyHandler(ctx)\n}\n\nfunc compileForPhraseStmt(ctx *blockCtx, v *ast.ForPhraseStmt) {\n\tif re, ok := v.X.(*ast.RangeExpr); ok {\n\t\tcompileForStmt(ctx, toForStmt(v.For, v.Value, v.Body, re, token.DEFINE, v.ForPhrase))\n\t\treturn\n\t}\n\tcb := ctx.cb\n\tdefer cb.End(v)\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\tctx.handleRecover(r, v)\n\t\t\tcb.ResetStmt()\n\t\t}\n\t}()\n\tcomments, once := cb.BackupComments()\n\tnames := make([]string, 1, 2)\n\tdefineNames := make([]*ast.Ident, 0, 2)\n\tif v.Key == nil {\n\t\tnames[0] = \"_\"\n\t} else {\n\t\tnames[0] = v.Key.Name\n\t\tdefineNames = append(defineNames, v.Key)\n\t}\n\tif v.Value != nil {\n\t\tnames = append(names, v.Value.Name)\n\t\tdefineNames = append(defineNames, v.Value)\n\t}\n\tcb.ForRange(names...)\n\tcompileExpr(ctx, 1, v.X)\n\tcb.RangeAssignThen(v.TokPos)\n\tif len(defineNames) > 0 {\n\t\tdefNames(ctx, defineNames, cb.Scope())\n\t}\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v, cb.Scope())\n\t}\n\tif v.Cond != nil {\n\t\tcb.If()\n\t\tcompileExpr(ctx, 1, v.Cond)\n\t\tcb.Then()\n\t\tcompileStmts(ctx, v.Body.List)\n\t\tcb.SetComments(comments, once)\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Scope(v.Body, cb.Scope())\n\t\t}\n\t\tcb.End()\n\t} else {\n\t\tcb.VBlock()\n\t\tcompileStmts(ctx, v.Body.List)\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Scope(v.Body, cb.Scope())\n\t\t}\n\t\tcb.End(v.Body)\n\t\tcb.SetComments(comments, once)\n\t}\n\tsetBodyHandler(ctx)\n}\n\nfunc toForStmt(forPos token.Pos, value ast.Expr, body *ast.BlockStmt, re *ast.RangeExpr, tok token.Token, fp *ast.ForPhrase) *ast.ForStmt {\n\tconst (\n\t\tnameK    = \"_xgo_k\"\n\t\tnameStep = \"_xgo_step\"\n\t\tnameEnd  = \"_xgo_end\"\n\t)\n\tnilIdent := value == nil\n\tif !nilIdent {\n\t\tif v, ok := value.(*ast.Ident); ok && v.Name == \"_\" {\n\t\t\tnilIdent = true\n\t\t}\n\t}\n\tif nilIdent {\n\t\tvalue = &ast.Ident{NamePos: forPos, Name: nameK}\n\t}\n\tfirst := re.First\n\tif first == nil {\n\t\tfirst = &ast.BasicLit{ValuePos: forPos, Kind: token.INT, Value: \"0\"}\n\t}\n\tinitLhs := []ast.Expr{value}\n\tinitRhs := []ast.Expr{first}\n\treplaceValue := false\n\tvar cond ast.Expr\n\tvar post ast.Expr\n\tswitch re.Last.(type) {\n\tcase *ast.Ident, *ast.BasicLit:\n\t\tcond = re.Last\n\tdefault:\n\t\treplaceValue = true\n\t\tcond = &ast.Ident{NamePos: forPos, Name: nameEnd}\n\t\tinitLhs = append(initLhs, cond)\n\t\tinitRhs = append(initRhs, re.Last)\n\t}\n\tif re.Expr3 == nil {\n\t\tpost = &ast.BasicLit{ValuePos: forPos, Kind: token.INT, Value: \"1\"}\n\t} else {\n\t\tswitch re.Expr3.(type) {\n\t\tcase *ast.Ident, *ast.BasicLit:\n\t\t\tpost = re.Expr3\n\t\tdefault:\n\t\t\treplaceValue = true\n\t\t\tpost = &ast.Ident{NamePos: forPos, Name: nameStep}\n\t\t\tinitLhs = append(initLhs, post)\n\t\t\tinitRhs = append(initRhs, re.Expr3)\n\t\t}\n\t}\n\tif tok == token.ASSIGN && replaceValue {\n\t\toldValue := value\n\t\tvalue = &ast.Ident{NamePos: forPos, Name: nameK}\n\t\tinitLhs[0] = value\n\t\tbody.List = append([]ast.Stmt{&ast.AssignStmt{\n\t\t\tLhs:    []ast.Expr{oldValue},\n\t\t\tTokPos: forPos,\n\t\t\tTok:    token.ASSIGN,\n\t\t\tRhs:    []ast.Expr{value},\n\t\t}}, body.List...)\n\t\ttok = token.DEFINE\n\t}\n\tif fp != nil && fp.Cond != nil {\n\t\tcondStmt := &ast.IfStmt{\n\t\t\tIf:   fp.IfPos,\n\t\t\tInit: fp.Init,\n\t\t\tCond: fp.Cond,\n\t\t\tBody: body,\n\t\t}\n\t\tbody = &ast.BlockStmt{\n\t\t\tList: []ast.Stmt{condStmt},\n\t\t}\n\t}\n\treturn &ast.ForStmt{\n\t\tFor: forPos,\n\t\tInit: &ast.AssignStmt{\n\t\t\tLhs:    initLhs,\n\t\t\tTokPos: re.To,\n\t\t\tTok:    tok,\n\t\t\tRhs:    initRhs,\n\t\t},\n\t\tCond: &ast.BinaryExpr{\n\t\t\tX:     value,\n\t\t\tOpPos: re.To,\n\t\t\tOp:    token.LSS,\n\t\t\tY:     cond,\n\t\t},\n\t\tPost: &ast.AssignStmt{\n\t\t\tLhs:    []ast.Expr{value},\n\t\t\tTokPos: re.Colon2,\n\t\t\tTok:    token.ADD_ASSIGN,\n\t\t\tRhs:    []ast.Expr{post},\n\t\t},\n\t\tBody: body,\n\t}\n}\n\n// for init; cond then\n//\n//\tbody\n//\tpost\n//\n// end\nfunc compileForStmt(ctx *blockCtx, v *ast.ForStmt) {\n\tcb := ctx.cb\n\tdefer cb.End(v)\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\tctx.handleRecover(r, v)\n\t\t\tcb.ResetStmt()\n\t\t}\n\t}()\n\tcomments, once := cb.BackupComments()\n\tcb.For(v)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v, cb.Scope())\n\t}\n\tif v.Init != nil {\n\t\tcompileStmt(ctx, v.Init)\n\t}\n\tif v.Cond != nil {\n\t\tcompileExpr(ctx, 1, v.Cond)\n\t} else {\n\t\tcb.None()\n\t}\n\tcb.Then(v.Body)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v.Body, cb.Scope())\n\t}\n\tcompileStmts(ctx, v.Body.List)\n\tif v.Post != nil {\n\t\tcb.Post()\n\t\tcompileStmt(ctx, v.Post)\n\t}\n\tcb.SetComments(comments, once)\n\tsetBodyHandler(ctx)\n}\n\n// if init; cond then\n//\n//\tbody\n//\n// end\nfunc compileIfStmt(ctx *blockCtx, v *ast.IfStmt) {\n\tcb := ctx.cb\n\tdefer cb.End(v)\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\tctx.handleRecover(r, v)\n\t\t\tcb.ResetStmt()\n\t\t}\n\t}()\n\tcomments, once := cb.BackupComments()\n\tcb.If(v)\n\tif v.Init != nil {\n\t\tcompileStmt(ctx, v.Init)\n\t}\n\tcompileExpr(ctx, 1, v.Cond)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v, cb.Scope())\n\t}\n\tcb.Then(v.Body)\n\tcompileStmts(ctx, v.Body.List)\n\tif e := v.Else; e != nil {\n\t\tcb.Else(e)\n\t\tif stmts, ok := e.(*ast.BlockStmt); ok {\n\t\t\tcompileStmts(ctx, stmts.List)\n\t\t} else {\n\t\t\tcompileStmt(ctx, e)\n\t\t}\n\t}\n\tcb.SetComments(comments, once)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v.Body, cb.Scope())\n\t}\n}\n\n// typeSwitch(name) init; expr typeAssertThen()\n// type1 type2 ... typeN typeCase(N)\n//\n//\t...\n//\tend\n//\n// type1 type2 ... typeM typeCase(M)\n//\n//\t...\n//\tend\n//\n// end\nfunc compileTypeSwitchStmt(ctx *blockCtx, v *ast.TypeSwitchStmt) {\n\tvar cb = ctx.cb\n\tdefer cb.End(v)\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\tctx.handleRecover(r, v)\n\t\t\tcb.ResetStmt()\n\t\t}\n\t}()\n\tcomments, once := cb.BackupComments()\n\tvar name string\n\tvar ta *ast.TypeAssertExpr\n\tswitch stmt := v.Assign.(type) {\n\tcase *ast.AssignStmt:\n\t\tif stmt.Tok != token.DEFINE || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 {\n\t\t\tpanic(\"TODO: type switch syntax error\")\n\t\t}\n\t\tname = stmt.Lhs[0].(*ast.Ident).Name\n\t\tta = stmt.Rhs[0].(*ast.TypeAssertExpr)\n\tcase *ast.ExprStmt:\n\t\tta = stmt.X.(*ast.TypeAssertExpr)\n\t}\n\tif ta.Type != nil {\n\t\tpanic(\"TODO: type switch syntax error, please use x.(type)\")\n\t}\n\tcb.TypeSwitch(name, v)\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v, cb.Scope())\n\t}\n\tif v.Init != nil {\n\t\tcompileStmt(ctx, v.Init)\n\t}\n\tcompileExpr(ctx, 1, ta.X)\n\tcb.TypeAssertThen()\n\tseen := make(map[types.Type]ast.Expr)\n\tvar firstDefault ast.Stmt\n\tfor _, stmt := range v.Body.List {\n\t\tc, ok := stmt.(*ast.CaseClause)\n\t\tif !ok {\n\t\t\tlog.Panicln(\"TODO: compile TypeSwitchStmt failed - case clause expected.\")\n\t\t}\n\t\tcb.TypeCase(c)\n\t\tfor _, citem := range c.List {\n\t\t\tcompileExpr(ctx, 1, citem)\n\t\t\tT := cb.Get(-1).Type\n\t\t\tif tt, ok := T.(*gogen.TypeType); ok {\n\t\t\t\tT = tt.Type()\n\t\t\t}\n\t\t\tvar haserr bool\n\t\t\tfor t, other := range seen {\n\t\t\t\tif T == nil && t == nil || T != nil && t != nil && types.Identical(T, t) {\n\t\t\t\t\thaserr = true\n\t\t\t\t\tpos := citem.Pos()\n\t\t\t\t\tend := citem.End()\n\t\t\t\t\tif T == types.Typ[types.UntypedNil] {\n\t\t\t\t\t\tctx.handleErrorf(\n\t\t\t\t\t\t\tpos, end, \"multiple nil cases in type switch (first at %v)\",\n\t\t\t\t\t\t\tctx.Position(other.Pos()))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tctx.handleErrorf(\n\t\t\t\t\t\t\tpos, end, \"duplicate case %s in type switch\\n\\tprevious case at %v\",\n\t\t\t\t\t\t\tT, ctx.Position(other.Pos()))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !haserr {\n\t\t\t\tseen[T] = citem\n\t\t\t}\n\t\t}\n\t\tif c.List == nil {\n\t\t\tif firstDefault != nil {\n\t\t\t\tctx.handleErrorf(\n\t\t\t\t\tc.Pos(), c.End(), \"multiple defaults in type switch (first at %v)\", ctx.Position(firstDefault.Pos()))\n\t\t\t} else {\n\t\t\t\tfirstDefault = c\n\t\t\t}\n\t\t}\n\t\tcb.Then()\n\t\tcompileStmts(ctx, c.Body)\n\t\tcommentStmt(ctx, stmt)\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Scope(c, cb.Scope())\n\t\t}\n\t\tcb.End(c)\n\t}\n\tcb.SetComments(comments, once)\n}\n\n// switch init; tag then\n// expr1 expr2 ... exprN case(N)\n//\n//\t...\n//\tend\n//\n// expr1 expr2 ... exprM case(M)\n//\n//\t...\n//\tend\n//\n// end\nfunc compileSwitchStmt(ctx *blockCtx, v *ast.SwitchStmt) {\n\tcb := ctx.cb\n\tdefer cb.End(v)\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\tctx.handleRecover(r, v)\n\t\t\tcb.ResetStmt()\n\t\t}\n\t}()\n\tcomments, once := cb.BackupComments()\n\tcb.Switch(v)\n\tif v.Init != nil {\n\t\tcompileStmt(ctx, v.Init)\n\t}\n\tif v.Tag != nil { // switch tag {....}\n\t\tcompileExpr(ctx, 1, v.Tag)\n\t} else {\n\t\tcb.None() // switch {...}\n\t}\n\tif rec := ctx.recorder(); rec != nil {\n\t\trec.Scope(v, cb.Scope())\n\t}\n\tcb.Then(v.Body)\n\tseen := make(valueMap)\n\tvar firstDefault ast.Stmt\n\tfor _, stmt := range v.Body.List {\n\t\tc, ok := stmt.(*ast.CaseClause)\n\t\tif !ok {\n\t\t\tlog.Panicln(\"TODO: compile SwitchStmt failed - case clause expected.\")\n\t\t}\n\t\tcb.Case(c)\n\t\tfor _, citem := range c.List {\n\t\t\tcompileExpr(ctx, 1, citem)\n\t\t\tv := cb.Get(-1)\n\t\t\tif val := goVal(v.CVal); val != nil {\n\t\t\t\t// look for duplicate types for a given value\n\t\t\t\t// (quadratic algorithm, but these lists tend to be very short)\n\t\t\t\ttyp := types.Default(v.Type)\n\t\t\t\tvar haserr bool\n\t\t\t\tfor _, vt := range seen[val] {\n\t\t\t\t\tif types.Identical(typ, vt.typ) {\n\t\t\t\t\t\thaserr = true\n\t\t\t\t\t\tsrc := ctx.LoadExpr(v.Src)\n\t\t\t\t\t\tif lit, ok := v.Src.(*ast.BasicLit); ok {\n\t\t\t\t\t\t\tctx.handleErrorf(lit.Pos(), lit.End(), \"duplicate case %s in switch\\n\\tprevious case at %v\",\n\t\t\t\t\t\t\t\tsrc, ctx.Position(vt.pos))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tctx.handleErrorf(v.Src.Pos(), v.Src.End(), \"duplicate case %s (value %#v) in switch\\n\\tprevious case at %v\",\n\t\t\t\t\t\t\t\tsrc, val, ctx.Position(vt.pos))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !haserr {\n\t\t\t\t\tseen[val] = append(seen[val], valueType{v.Src.Pos(), typ})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif c.List == nil {\n\t\t\tif firstDefault != nil {\n\t\t\t\tctx.handleErrorf(c.Pos(), c.End(), \"multiple defaults in switch (first at %v)\", ctx.Position(firstDefault.Pos()))\n\t\t\t} else {\n\t\t\t\tfirstDefault = c\n\t\t\t}\n\t\t}\n\t\tcb.Then()\n\t\tbody, has := hasFallthrough(c.Body)\n\t\tcompileStmts(ctx, body)\n\t\tif has {\n\t\t\tcb.Fallthrough()\n\t\t}\n\t\tcommentStmt(ctx, stmt)\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Scope(c, cb.Scope())\n\t\t}\n\t\tcb.End(c)\n\t}\n\tcb.SetComments(comments, once)\n}\n\nfunc hasFallthrough(body []ast.Stmt) ([]ast.Stmt, bool) {\n\tif n := len(body); n > 0 {\n\t\tif bs, ok := body[n-1].(*ast.BranchStmt); ok && bs.Tok == token.FALLTHROUGH {\n\t\t\treturn body[:n-1], true\n\t\t}\n\t}\n\treturn body, false\n}\n\n// select\n// stmt1 commCase(1)\n//\n//\t...\n//\tend\n//\n// stmt2 commCase(1)\n//\n//\t...\n//\tend\n//\n// ...\n// commCase(0)\n//\n//\t...\n//\tend\n//\n// end\nfunc compileSelectStmt(ctx *blockCtx, v *ast.SelectStmt) {\n\tcb := ctx.cb\n\tdefer cb.End(v)\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\tctx.handleRecover(r, v)\n\t\t\tcb.ResetStmt()\n\t\t}\n\t}()\n\tcomments, once := cb.BackupComments()\n\tcb.Select(v)\n\tfor _, stmt := range v.Body.List {\n\t\tc, ok := stmt.(*ast.CommClause)\n\t\tif !ok {\n\t\t\tlog.Panicln(\"TODO: compile SelectStmt failed - comm clause expected.\")\n\t\t}\n\t\tcb.CommCase(c)\n\t\tif c.Comm != nil {\n\t\t\tcompileStmt(ctx, c.Comm)\n\t\t}\n\t\tcb.Then()\n\t\tcompileStmts(ctx, c.Body)\n\t\tcommentStmt(ctx, stmt)\n\t\tif rec := ctx.recorder(); rec != nil {\n\t\t\trec.Scope(c, cb.Scope())\n\t\t}\n\t\tcb.End(c)\n\t}\n\tcb.SetComments(comments, once)\n}\n\nfunc compileBranchStmt(ctx *blockCtx, v *ast.BranchStmt) {\n\tlabel := v.Label\n\tswitch v.Tok {\n\tcase token.GOTO:\n\t\tcb := ctx.cb\n\t\tif l, ok := cb.LookupLabel(label.Name); ok {\n\t\t\tcb.Goto(l)\n\t\t\treturn\n\t\t}\n\t\tcompileCallExpr(ctx, 0, &ast.CallExpr{\n\t\t\tFun:        &ast.Ident{NamePos: v.TokPos, Name: \"goto\", Obj: &ast.Object{Data: label}},\n\t\t\tArgs:       []ast.Expr{label},\n\t\t\tNoParenEnd: label.End(),\n\t\t}, clIdentGoto)\n\tcase token.BREAK:\n\t\tctx.cb.Break(getLabel(ctx, label))\n\tcase token.CONTINUE:\n\t\tctx.cb.Continue(getLabel(ctx, label))\n\tcase token.FALLTHROUGH:\n\t\tctx.handleErrorf(v.Pos(), v.End(), \"fallthrough statement out of place\")\n\tdefault:\n\t\tpanic(\"unknown branch statement\")\n\t}\n}\n\nfunc getLabel(ctx *blockCtx, label *ast.Ident) *gogen.Label {\n\tif label != nil {\n\t\tif l, ok := ctx.cb.LookupLabel(label.Name); ok {\n\t\t\treturn l\n\t\t}\n\t\tctx.handleErrorf(label.Pos(), label.End(), \"label %v is not defined\", label.Name)\n\t}\n\treturn nil\n}\n\nfunc compileLabeledStmt(ctx *blockCtx, v *ast.LabeledStmt) {\n\tl, _ := ctx.cb.LookupLabel(v.Label.Name)\n\tctx.cb.Label(l)\n\tcompileStmt(ctx, v.Stmt)\n}\n\nfunc compileGoStmt(ctx *blockCtx, v *ast.GoStmt) {\n\tcompileCallExpr(ctx, 0, v.Call, 0)\n\tctx.cb.Go()\n}\n\nfunc compileDeferStmt(ctx *blockCtx, v *ast.DeferStmt) {\n\tcompileCallExpr(ctx, 0, v.Call, 0)\n\tctx.cb.Defer()\n}\n\nfunc compileDeclStmt(ctx *blockCtx, expr *ast.DeclStmt) {\n\tswitch d := expr.Decl.(type) {\n\tcase *ast.GenDecl:\n\t\tswitch d.Tok {\n\t\tcase token.TYPE:\n\t\t\tfor _, spec := range d.Specs {\n\t\t\t\tcompileType(ctx, spec.(*ast.TypeSpec))\n\t\t\t}\n\t\tcase token.CONST:\n\t\t\tcdecl := ctx.pkg.NewConstDefs(ctx.cb.Scope())\n\t\t\tloadConstSpecs(ctx, cdecl, d.Specs)\n\t\tcase token.VAR:\n\t\t\tfor _, spec := range d.Specs {\n\t\t\t\tv := spec.(*ast.ValueSpec)\n\t\t\t\tloadVars(ctx, v, d.Doc, false)\n\t\t\t}\n\t\tdefault:\n\t\t\tlog.Panicln(\"TODO: compileDeclStmt - unknown\")\n\t\t}\n\t}\n}\n\nfunc compileType(ctx *blockCtx, t *ast.TypeSpec) {\n\tname := t.Name.Name\n\tif name == \"_\" {\n\t\treturn\n\t}\n\tif t.Assign != token.NoPos { // alias type\n\t\tctx.cb.AliasType(name, toType(ctx, t.Type))\n\t} else {\n\t\tctx.cb.NewType(name).InitType(ctx.pkg, toType(ctx, t.Type))\n\t}\n}\n\ntype (\n\tvalueMap  map[any][]valueType // underlying Go value -> valueType\n\tvalueType struct {\n\t\tpos token.Pos\n\t\ttyp types.Type\n\t}\n)\n\n// goVal returns the Go value for val, or nil.\nfunc goVal(val constant.Value) any {\n\t// val should exist, but be conservative and check\n\tif val == nil {\n\t\treturn nil\n\t}\n\t// Match implementation restriction of other compilers.\n\t// gc only checks duplicates for integer, floating-point\n\t// and string values, so only create Go values for these\n\t// types.\n\tswitch val.Kind() {\n\tcase constant.Int:\n\t\tif x, ok := constant.Int64Val(val); ok {\n\t\t\treturn x\n\t\t}\n\t\tif x, ok := constant.Uint64Val(val); ok {\n\t\t\treturn x\n\t\t}\n\tcase constant.Float:\n\t\tif x, ok := constant.Float64Val(val); ok {\n\t\t\treturn x\n\t\t}\n\tcase constant.String:\n\t\treturn constant.StringVal(val)\n\t}\n\treturn nil\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cl/typeparams.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\t\"go/types\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nfunc toTermList(ctx *blockCtx, expr ast.Expr) []*types.Term {\nretry:\n\tswitch v := expr.(type) {\n\tcase *ast.UnaryExpr:\n\t\tif v.Op != token.TILDE {\n\t\t\tpanic(ctx.newCodeErrorf(v.Pos(), v.End(), \"invalid op %v must ~\", v.Op))\n\t\t}\n\t\treturn []*types.Term{types.NewTerm(true, toType(ctx, v.X))}\n\tcase *ast.BinaryExpr:\n\t\tif v.Op != token.OR {\n\t\t\tpanic(ctx.newCodeErrorf(v.Pos(), v.End(), \"invalid op %v must |\", v.Op))\n\t\t}\n\t\treturn append(toTermList(ctx, v.X), toTermList(ctx, v.Y)...)\n\tcase *ast.ParenExpr:\n\t\texpr = v.X\n\t\tgoto retry\n\t}\n\treturn []*types.Term{types.NewTerm(false, toType(ctx, expr))}\n}\n\nfunc toBinaryExprType(ctx *blockCtx, v *ast.BinaryExpr) types.Type {\n\treturn types.NewInterfaceType(nil, []types.Type{types.NewUnion(toTermList(ctx, v))})\n}\n\nfunc toUnaryExprType(ctx *blockCtx, v *ast.UnaryExpr) types.Type {\n\treturn types.NewInterfaceType(nil, []types.Type{types.NewUnion(toTermList(ctx, v))})\n}\n\nfunc toTypeParams(ctx *blockCtx, params *ast.FieldList) []*types.TypeParam {\n\tif params == nil {\n\t\treturn nil\n\t}\n\treturn collectTypeParams(ctx, params)\n}\n\nfunc recvTypeParams(ctx *blockCtx, typ ast.Expr, named *types.Named) (tparams []*types.TypeParam) {\n\torgTypeParams := named.TypeParams()\n\tif orgTypeParams == nil {\n\t\treturn nil\n\t}\nL:\n\tfor {\n\t\tswitch t := typ.(type) {\n\t\tcase *ast.ParenExpr:\n\t\t\ttyp = t.X\n\t\tcase *ast.StarExpr:\n\t\t\ttyp = t.X\n\t\tdefault:\n\t\t\tbreak L\n\t\t}\n\t}\n\tswitch t := typ.(type) {\n\tcase *ast.IndexExpr:\n\t\tif orgTypeParams.Len() != 1 {\n\t\t\tpanic(ctx.newCodeErrorf(typ.Pos(), typ.End(), \"got 1 type parameter, but receiver base type declares %v\", orgTypeParams.Len()))\n\t\t}\n\t\tv := t.Index.(*ast.Ident)\n\t\ttp := orgTypeParams.At(0)\n\t\tobj := types.NewTypeName(v.Pos(), tp.Obj().Pkg(), v.Name, nil)\n\t\ttparams = []*types.TypeParam{types.NewTypeParam(obj, tp.Constraint())}\n\tcase *ast.IndexListExpr:\n\t\tn := len(t.Indices)\n\t\tif n != orgTypeParams.Len() {\n\t\t\tpanic(ctx.newCodeErrorf(typ.Pos(), typ.End(), \"got %v arguments but %v type parameters\", n, orgTypeParams.Len()))\n\t\t}\n\t\ttparams = make([]*types.TypeParam, n)\n\t\tfor i := 0; i < n; i++ {\n\t\t\tv := t.Indices[i].(*ast.Ident)\n\t\t\ttp := orgTypeParams.At(i)\n\t\t\tobj := types.NewTypeName(v.Pos(), tp.Obj().Pkg(), v.Name, nil)\n\t\t\ttparams[i] = types.NewTypeParam(obj, tp.Constraint())\n\t\t}\n\tdefault:\n\t\tpanic(ctx.newCodeErrorf(typ.Pos(), typ.End(), \"cannot use generic type %v without instantiation\", named))\n\t}\n\treturn\n}\n\nfunc toFuncType(ctx *blockCtx, typ *ast.FuncType, recv *types.Var, d *ast.FuncDecl) *types.Signature {\n\tvar typeParams []*types.TypeParam\n\tif recv != nil && d.Recv != nil {\n\t\ttyp := recv.Type()\n\t\tif pt, ok := typ.(*types.Pointer); ok {\n\t\t\ttyp = pt.Elem()\n\t\t}\n\t\ttypeParams = recvTypeParams(ctx, d.Recv.List[0].Type, typ.(*types.Named))\n\t} else {\n\t\ttypeParams = toTypeParams(ctx, typ.TypeParams)\n\t}\n\tif len(typeParams) > 0 {\n\t\tctx.tlookup = &typeParamLookup{typeParams}\n\t\tdefer func() {\n\t\t\tctx.tlookup = nil\n\t\t}()\n\t}\n\tparams, variadic := toParams(ctx, typ.Params.List)\n\tresults := toResults(ctx, typ.Results)\n\tif recv != nil {\n\t\treturn types.NewSignatureType(recv, typeParams, nil, params, results, variadic)\n\t}\n\treturn types.NewSignatureType(recv, nil, typeParams, params, results, variadic)\n}\n\ntype typeParamLookup struct {\n\ttypeParams []*types.TypeParam\n}\n\nfunc (p *typeParamLookup) Lookup(name string) *types.TypeParam {\n\tfor _, t := range p.typeParams {\n\t\ttname := t.Obj().Name()\n\t\tif tname != \"_\" && name == tname {\n\t\t\treturn t\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc initType(ctx *blockCtx, named *types.Named, spec *ast.TypeSpec) {\n\ttypeParams := toTypeParams(ctx, spec.TypeParams)\n\tif len(typeParams) > 0 {\n\t\tnamed.SetTypeParams(typeParams)\n\t\tctx.tlookup = &typeParamLookup{typeParams}\n\t\tdefer func() {\n\t\t\tctx.tlookup = nil\n\t\t}()\n\t}\n\torg := ctx.inInst\n\tctx.inInst = 0\n\tdefer func() {\n\t\tctx.inInst = org\n\t}()\n\ttyp := toType(ctx, spec.Type)\nretry:\n\tswitch t := typ.(type) {\n\tcase *types.Named:\n\t\ttyp = getUnderlying(ctx, t)\n\tcase *types.Alias:\n\t\ttyp = types.Unalias(t)\n\t\tgoto retry\n\t}\n\tnamed.SetUnderlying(typ)\n}\n\nfunc getRecvType(expr ast.Expr) (typ ast.Expr, ptr bool, ok bool) {\n\ttyp = expr\nL:\n\tfor {\n\t\tswitch t := typ.(type) {\n\t\tcase *ast.ParenExpr:\n\t\t\ttyp = t.X\n\t\tcase *ast.StarExpr:\n\t\t\tif ptr {\n\t\t\t\tok = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\tptr = true\n\t\t\ttyp = t.X\n\t\tdefault:\n\t\t\tbreak L\n\t\t}\n\t}\n\tswitch t := typ.(type) {\n\tcase *ast.IndexExpr:\n\t\ttyp = t.X\n\tcase *ast.IndexListExpr:\n\t\ttyp = t.X\n\t}\n\tok = true\n\treturn\n}\n\nfunc collectTypeParams(ctx *blockCtx, list *ast.FieldList) []*types.TypeParam {\n\tvar tparams []*types.TypeParam\n\t// Declare type parameters up-front, with empty interface as type bound.\n\t// The scope of type parameters starts at the beginning of the type parameter\n\t// list (so we can have mutually recursive parameterized interfaces).\n\tfor _, f := range list.List {\n\t\ttparams = declareTypeParams(ctx, tparams, f.Names)\n\t}\n\n\tctx.tlookup = &typeParamLookup{tparams}\n\tdefer func() {\n\t\tctx.tlookup = nil\n\t}()\n\n\tindex := 0\n\tfor _, f := range list.List {\n\t\tvar bound types.Type\n\t\t// NOTE: we may be able to assert that f.Type != nil here, but this is not\n\t\t// an invariant of the AST, so we are cautious.\n\t\tif f.Type != nil {\n\t\t\tbound = boundTypeParam(ctx, f.Type)\n\t\t\tif isTypeParam(bound) {\n\t\t\t\t// We may be able to allow this since it is now well-defined what\n\t\t\t\t// the underlying type and thus type set of a type parameter is.\n\t\t\t\t// But we may need some additional form of cycle detection within\n\t\t\t\t// type parameter lists.\n\t\t\t\t//check.error(f.Type, MisplacedTypeParam, \"cannot use a type parameter as constraint\")\n\t\t\t\tbound = types.Typ[types.Invalid]\n\t\t\t} else if t, ok := bound.(*types.Named); ok {\n\t\t\t\tif t.Underlying() == nil { // check named underlying is nil\n\t\t\t\t\tctx.loadNamed(ctx.pkg, t)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tbound = types.Typ[types.Invalid]\n\t\t}\n\t\tfor i := range f.Names {\n\t\t\ttparams[index+i].SetConstraint(bound)\n\t\t}\n\t\tindex += len(f.Names)\n\t}\n\treturn tparams\n}\n\nfunc declareTypeParams(ctx *blockCtx, tparams []*types.TypeParam, names []*ast.Ident) []*types.TypeParam {\n\t// Use Typ[Invalid] for the type constraint to ensure that a type\n\t// is present even if the actual constraint has not been assigned\n\t// yet.\n\t// TODO(gri) Need to systematically review all uses of type parameter\n\t//           constraints to make sure we don't rely on them if they\n\t//           are not properly set yet.\n\tfor _, name := range names {\n\t\ttname := types.NewTypeName(name.Pos(), ctx.pkg.Types, name.Name, nil)\n\t\ttpar := types.NewTypeParam(tname, types.Typ[types.Invalid]) // assigns type to tpar as a side-effect\n\t\t// check.declare(check.scope, name, tname, check.scope.pos)    // TODO(gri) check scope position\n\t\ttparams = append(tparams, tpar)\n\t}\n\n\treturn tparams\n}\n\nfunc isTypeParam(t types.Type) bool {\n\t_, ok := t.(*types.TypeParam)\n\treturn ok\n}\n\nfunc isSpecificSliceType(ctx *blockCtx, typ types.Type) bool {\n\tif typ == nil {\n\t\treturn false\n\t}\n\tvar t *types.Slice\n\tswitch tt := typ.(type) {\n\tcase *types.Named:\n\t\tt = getUnderlying(ctx, tt).(*types.Slice)\n\tcase *types.Slice:\n\t\tt = tt\n\tdefault:\n\t\treturn false\n\t}\n\t_, ok := t.Elem().(*types.TypeParam)\n\treturn !ok\n}\n\nfunc boundTypeParam(ctx *blockCtx, x ast.Expr) types.Type {\n\t// A type set literal of the form ~T and A|B may only appear as constraint;\n\t// embed it in an implicit interface so that only interface type-checking\n\t// needs to take care of such type expressions.\n\twrap := false\n\tswitch op := x.(type) {\n\tcase *ast.UnaryExpr:\n\t\twrap = op.Op == token.TILDE\n\tcase *ast.BinaryExpr:\n\t\twrap = op.Op == token.OR\n\t}\n\tif wrap {\n\t\tx = &ast.InterfaceType{Methods: &ast.FieldList{List: []*ast.Field{{Type: x}}}}\n\t\tt := toType(ctx, x)\n\t\t// mark t as implicit interface if all went well\n\t\tif t, _ := t.(*types.Interface); t != nil {\n\t\t\tt.MarkImplicit()\n\t\t}\n\t\treturn t\n\t}\n\treturn toType(ctx, x)\n}\n\nfunc namedIsTypeParams(ctx *blockCtx, t *types.Named) bool {\n\to := t.Obj()\n\tif o.Pkg() == ctx.pkg.Types {\n\t\tif _, ok := ctx.generics[o.Name()]; !ok {\n\t\t\treturn false\n\t\t}\n\t\tctx.loadType(o.Name())\n\t}\n\treturn t.Obj() != nil && t.TypeArgs() == nil && t.TypeParams() != nil\n}\n"
  },
  {
    "path": "cl/typeparams_test.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl_test\n\nimport (\n\t\"go/scanner\"\n\t\"os\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cl/cltest\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/parser/fsx/memfs\"\n)\n\nfunc TestTypeParams(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\n\ntype Data[X, Y any] struct {\n\tv X\n}\n\nfunc (p *Data[T, R]) foo() {\n\tfmt.Println(p.v)\n}\n`, `\nv := Data[int, float64]{1}\nv.foo()\n`, `package main\n\nfunc main() {\n\tv := Data[int, float64]{1}\n\tv.foo()\n}\n`)\n}\n\nfunc TestTypeParamsFunc(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\n\ntype Number interface {\n\t~int | ~uint | (float64)\n}\n\nfunc Sum[T Number](vec []T) T {\n\tvar sum T\n\tfor _, elt := range vec {\n\t\tsum = sum + elt\n\t}\n\treturn sum\n}\n\nfunc At[T interface{ ~[]E }, E any](x T, i int) E {\n\treturn x[i]\n}\n\nfunc Loader[T1 any, T2 any](p1 T1, p2 T2) T1 {\n\treturn p1\n}\n\nfunc Add[T1 any, T2 ~int|~uint](v1 T1, v2 ...T2) (sum T2) {\n\tprintln(v1)\n\tfor _, v := range v2 {\n\t\tsum += v\n\t}\n\treturn sum\n}\n\ntype Int []int\nvar MyInts = Int{1,2,3,4}\n`, `\n_ = At[[]int]\n_ = At[[]int,int]\n_ = Sum[int]\n_ = Loader[*int,int]\n_ = Add[string,int]\nvar s1 int = Sum([1, 2, 3])\nvar s2 int = Sum[int]([1, 2, 3])\nvar v1 int = At([1, 2, 3], 1)\nvar v2 int = At[[]int]([1, 2, 3], 1)\nvar v3 int = At[[]int, int]([1, 2, 3], 1)\nvar n1 int = Add(\"hello\", 1, 2, 3)\nvar n2 int = Add[string](\"hello\", 1, 2, 3)\nvar n3 int = Add[string, int](\"hello\", 1, 2, 3)\nvar n4 int = Add(\"hello\", [1, 2, 3]...)\nvar n5 int = Add(\"hello\", [1, 2, 3]...)\nvar n6 int = Add[string](\"hello\", MyInts...)\nvar n7 int = Add[string, int](\"hello\", [1, 2, 3]...)\nvar p1 *int = Loader[*int](nil, 1)\nvar p2 *int = Loader[*int, int](nil, 1)\nvar fn1 func(p1 *int, p2 int) *int\nfn1 = Loader[*int, int]\nfn1(nil, 1)\n`, `package main\n\nfunc main() {\n\t_ = At[[]int]\n\t_ = At[[]int, int]\n\t_ = Sum[int]\n\t_ = Loader[*int, int]\n\t_ = Add[string, int]\n\tvar s1 int = Sum([]int{1, 2, 3})\n\tvar s2 int = Sum[int]([]int{1, 2, 3})\n\tvar v1 int = At([]int{1, 2, 3}, 1)\n\tvar v2 int = At[[]int]([]int{1, 2, 3}, 1)\n\tvar v3 int = At[[]int, int]([]int{1, 2, 3}, 1)\n\tvar n1 int = Add(\"hello\", 1, 2, 3)\n\tvar n2 int = Add[string](\"hello\", 1, 2, 3)\n\tvar n3 int = Add[string, int](\"hello\", 1, 2, 3)\n\tvar n4 int = Add(\"hello\", []int{1, 2, 3}...)\n\tvar n5 int = Add(\"hello\", []int{1, 2, 3}...)\n\tvar n6 int = Add[string](\"hello\", MyInts...)\n\tvar n7 int = Add[string, int](\"hello\", []int{1, 2, 3}...)\n\tvar p1 *int = Loader[*int](nil, 1)\n\tvar p2 *int = Loader[*int, int](nil, 1)\n\tvar fn1 func(p1 *int, p2 int) *int\n\tfn1 = Loader[*int, int]\n\tfn1(nil, 1)\n}\n`)\n}\n\nfunc TestTypeParamsType(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\ntype Data[T any] struct {\n\tv T\n}\nfunc(p *Data[T]) Set(v T) {\n\tp.v = v\n}\nfunc(p *(Data[T1])) Set2(v T1) {\n\tp.v = v\n}\ntype sliceOf[E any] interface {\n\t~[]E\n}\ntype Slice[S sliceOf[T], T any] struct {\n\tData S\n}\nfunc (p *Slice[S, T]) Append(t ...T) S {\n\tp.Data = append(p.Data, t...)\n\treturn p.Data\n}\nfunc (p *Slice[S1, T1]) Append2(t ...T1) S1 {\n\tp.Data = append(p.Data, t...)\n\treturn p.Data\n}\ntype (\n\tDataInt = Data[int]\n\tSliceInt = Slice[[]int,int]\n)\n`, `\ntype DataString = Data[string]\ntype SliceString = Slice[[]string,string]\nprintln(DataInt{1}.v)\nprintln(DataString{\"hello\"}.v)\nprintln(Data[int]{100}.v)\nprintln(Data[string]{\"hello\"}.v)\nprintln(Data[struct{X int;Y int}]{}.v.X)\n\nv1 := SliceInt{}\nv2 := SliceString{}\nv3 := Slice[[]int,int]{}\nv3.Append([1,2,3,4]...)\nv3.Append2([1,2,3,4]...)\n`, `package main\n\nimport \"fmt\"\n\ntype DataString = Data[string]\ntype SliceString = Slice[[]string, string]\n\nfunc main() {\n\tfmt.Println(DataInt{1}.v)\n\tfmt.Println(DataString{\"hello\"}.v)\n\tfmt.Println(Data[int]{100}.v)\n\tfmt.Println(Data[string]{\"hello\"}.v)\n\tfmt.Println(Data[struct {\n\t\tX int\n\t\tY int\n\t}]{}.v.X)\n\tv1 := SliceInt{}\n\tv2 := SliceString{}\n\tv3 := Slice[[]int, int]{}\n\tv3.Append([]int{1, 2, 3, 4}...)\n\tv3.Append2([]int{1, 2, 3, 4}...)\n}\n`)\n}\n\nfunc TestTypeParamsComparable(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\n// Index returns the index of x in s, or -1 if not found.\nfunc Index[T comparable](s []T, x T) int {\n\tfor i, v := range s {\n\t\t// v and x are type T, which has the comparable\n\t\t// constraint, so we can use == here.\n\t\tif v == x {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nvar IndexInt = Index[int]\n`, `\nv1 := IndexInt([1,2,3,4],1)\nv2 := Index([1,2,3,4],1)\nv3 := Index[int]([1,2,3,4],1)\nv4 := Index([\"a\",\"b\",\"c\",\"d\"],\"b\")\n`, `package main\n\nfunc main() {\n\tv1 := IndexInt([]int{1, 2, 3, 4}, 1)\n\tv2 := Index([]int{1, 2, 3, 4}, 1)\n\tv3 := Index[int]([]int{1, 2, 3, 4}, 1)\n\tv4 := Index([]string{\"a\", \"b\", \"c\", \"d\"}, \"b\")\n}\n`)\n}\n\nfunc mixedErrorTest(t *testing.T, msg, gocode, gopcode string) {\n\tmixedErrorTestEx(t, \"main\", msg, gocode, gopcode)\n}\n\nfunc mixedErrorTestEx(t *testing.T, pkgname, msg, gocode, gopcode string) {\n\tfs := memfs.TwoFiles(\"/foo\", \"a.go\", gocode, \"b.xgo\", gopcode)\n\tpkgs, err := parser.ParseFSDir(cltest.Conf.Fset, fs, \"/foo\", parser.Config{})\n\tif err != nil {\n\t\tscanner.PrintError(os.Stderr, err)\n\t\tt.Fatal(\"parser.ParseFSDir failed\")\n\t}\n\tconf := *cltest.Conf\n\tconf.NoFileLine = false\n\tconf.RelativeBase = \"/foo\"\n\tbar := pkgs[pkgname]\n\t_, err = cl.NewPackage(\"\", bar, &conf)\n\tif err == nil {\n\t\tt.Fatal(\"no error?\")\n\t}\n\tif ret := err.Error(); ret != msg {\n\t\tt.Fatalf(\"\\nError: \\\"%s\\\"\\nExpected: \\\"%s\\\"\\n\", ret, msg)\n\t}\n}\n\nfunc TestTypeParamsErrorInstantiate(t *testing.T) {\n\tvar msg string\n\tswitch runtime.Version()[:6] {\n\tcase \"go1.18\":\n\t\tmsg = `b.xgo:2:1: uint does not implement Number`\n\tcase \"go1.19\":\n\t\tmsg = `b.xgo:2:1: uint does not implement Number (uint missing in ~int | float64)`\n\tdefault:\n\t\tmsg = `b.xgo:2:1: uint does not satisfy Number (uint missing in ~int | float64)`\n\t}\n\n\tmixedErrorTest(t, msg, `\npackage main\n\ntype Number interface {\n\t~int | float64\n}\n\nfunc Sum[T Number](vec []T) T {\n\tvar sum T\n\tfor _, elt := range vec {\n\t\tsum = sum + elt\n\t}\n\treturn sum\n}\n\nvar\tSumInt = Sum[int]\n`, `\nSum[uint]\n`)\n}\n\nfunc TestTypeParamsErrorMatch(t *testing.T) {\n\tvar msg string\n\tswitch runtime.Version()[:6] {\n\tcase \"go1.18\", \"go1.19\":\n\t\tmsg = `b.xgo:2:5: T does not match ~[]E`\n\tcase \"go1.20\":\n\t\tmsg = `b.xgo:2:5: int does not match ~[]E`\n\tdefault:\n\t\tmsg = `b.xgo:2:5: T (type int) does not satisfy interface{interface{~[]E}}`\n\t}\n\tmixedErrorTest(t, msg, `\npackage main\n\nfunc At[T interface{ ~[]E }, E any](x T, i int) E {\n\treturn x[i]\n}\n\nvar\tAtInt = At[[]int]\n`, `\n_ = At[int]\n`)\n}\n\nfunc TestTypeParamsErrInferFunc(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:5: cannot infer T2 (declared at /foo/a.go:4:21)`, `\npackage main\n\nfunc Loader[T1 any, T2 any](p1 T1, p2 T2) T1 {\n\treturn p1\n}\n`, `\n_ = Loader[int]\n`)\n}\n\nfunc TestTypeParamsErrArgumentsParameters1(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:7: got 1 type arguments but Data[T1, T2 interface{}] has 2 type parameters`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\n`, `\nvar v Data[int]\n`)\n}\n\nfunc TestTypeParamsErrArgumentsParameters2(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:7: got 3 type arguments but Data[T1, T2 interface{}] has 2 type parameters`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\n`, `\nvar v Data[int,int,int]\n`)\n}\n\nfunc TestTypeParamsErrArgumentsParameters3(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:1: got 3 type arguments but func[T1, T2 interface{}](t1 T1, t2 T2) has 2 type parameters`, `\npackage main\n\nfunc Test[T1 any, T2 any](t1 T1, t2 T2) {\n\tprintln(t1,t2)\n}\n`, `\nTest[int,int,int](1,2)\n`)\n}\n\nfunc TestTypeParamsErrCallArguments1(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:1: not enough arguments in call to Test\n\thave (untyped int)\n\twant (T1, T2)`, `\npackage main\n\nfunc Test[T1 any, T2 any](t1 T1, t2 T2) {\n\tprintln(t1,t2)\n}\n`, `\nTest(1)\n`)\n}\n\nfunc TestTypeParamsErrCallArguments2(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:1: too many arguments in call to Test\n\thave (untyped int, untyped int, untyped int)\n\twant (T1, T2)`, `\npackage main\n\nfunc Test[T1 any, T2 any](t1 T1, t2 T2) {\n\tprintln(t1,t2)\n}\n`, `\nTest(1,2,3)\n`)\n}\n\nfunc TestTypeParamsErrCallArguments3(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:1: too many arguments in call to Test\n\thave (untyped int, untyped int)\n\twant ()`, `\npackage main\n\nfunc Test[T1 any, T2 any]() {\n\tvar t1 T1\n\tvar t2 T2\n\tprintln(t1,t2)\n}\n`, `\nTest(1,2)\n`)\n}\n\nfunc TestTypeParamsErrCallVariadicArguments1(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:1: not enough arguments in call to Add\n\thave ()\n\twant (T1, ...T2)`, `\npackage main\n\nfunc Add[T1 any, T2 ~int|~uint](v1 T1, v2 ...T2) (sum T2) {\n\tprintln(v1)\n\tfor _, v := range v2 {\n\t\tsum += v\n\t}\n\treturn sum\n}\n`, `\nAdd()\n`)\n}\n\nfunc TestTypeParamsErrCallVariadicArguments2(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:1: cannot infer T2 (a.go:4:18)`, `\npackage main\n\nfunc Add[T1 any, T2 ~int|~uint](v1 T1, v2 ...T2) (sum T2) {\n\tprintln(v1)\n\tfor _, v := range v2 {\n\t\tsum += v\n\t}\n\treturn sum\n}\n`, `\nAdd(1)\n`)\n}\n\nfunc TestTypeParamsRecvTypeError1(t *testing.T) {\n\tmixedErrorTest(t, `a.go:7:9: cannot use generic type Data[T interface{}] without instantiation`, `\npackage main\n\ntype Data[T any] struct {\n\tv T\n}\nfunc(p *Data) Test() {\n}\n`, `\nData[int]{}.Test()\n`)\n}\n\nfunc TestTypeParamsRecvTypeError2(t *testing.T) {\n\tmixedErrorTest(t, `a.go:7:9: got 2 arguments but 1 type parameters`, `\npackage main\n\ntype Data[T any] struct {\n\tv T\n}\nfunc(p *Data[T1,T2]) Test() {\n}\n`, `\nData[int]{}.Test()\n`)\n}\n\nfunc TestTypeParamsRecvTypeError3(t *testing.T) {\n\tmixedErrorTest(t, `a.go:8:9: got 1 type parameter, but receiver base type declares 2`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\nfunc(p *Data[T1]) Test() {\n}\n`, `\nData[int,int]{}.Test()\n`)\n}\n\nfunc TestGenericTypeWithoutInst1(t *testing.T) {\n\tmixedErrorTest(t, `a.go:8:9: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\nfunc(p *Data) Test() {\n}\n`, `\nvar v Data[int,int]\n`)\n}\n\nfunc TestGenericTypeWithoutInst2(t *testing.T) {\n\tmixedErrorTest(t, `a.go:10:2: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\n\ntype My[T any] struct {\n\tData\n}\n`, `\nvar v My[int]\n`)\n}\n\nfunc TestGenericTypeWithoutInst3(t *testing.T) {\n\tmixedErrorTest(t, `a.go:10:2: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\n\ntype My struct {\n\tData\n}\n`, `\nvar v My\n`)\n}\n\nfunc TestGenericTypeWithoutInst4(t *testing.T) {\n\tmixedErrorTest(t, `a.go:10:15: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\n\ntype My struct {\n\tv map[string]Data\n}\n`, `\nvar v My\n`)\n}\n\nfunc TestGenericTypeWithoutInst5(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:7: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\n`, `\nvar v Data\n`)\n}\n\nfunc TestGenericTypeWithoutInst6(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:8: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\n`, `\ntype T Data\n`)\n}\n\nfunc TestGenericTypeWithoutInst7(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:3:2: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\n`, `\ntype My struct {\n\tData\n}\n`)\n}\n\nfunc TestGenericTypeWithoutInst8(t *testing.T) {\n\tmixedErrorTest(t, `b.xgo:2:23: cannot use generic type Data[T1, T2 interface{}] without instantiation`, `\npackage main\n\ntype Data[T1 any, T2 any] struct {\n\tv1 T1\n\tv2 T2\n}\n`, `\nfunc test(v1 int, v2 *Data) {\n}\n`)\n}\n\nfunc TestGenericTypeCompositeLit(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\ntype A[T any] struct {\n\tm T\n}\n\ntype B[T any] struct {\n\tn A[T]\n}\n\n`, `\nvar a [2]int\nif 0 == a[1] {\n\tprintln \"world\"\n}\nprintln B[int]{}.n\nif 0 < (B[int]{}).n.m {\n}\n`, `package main\n\nimport \"fmt\"\n\nvar a [2]int\n\nfunc main() {\n\tif 0 == a[1] {\n\t\tfmt.Println(\"world\")\n\t}\n\tfmt.Println(B[int]{}.n)\n\tif 0 < (B[int]{}).n.m {\n\t}\n}\n`)\n}\n\nfunc TestInferFuncLambda(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\nfunc ListMap[T any](ar []T, fn func(v T) T)[]T {\n\tfor i, v := range ar {\n\t\tar[i] = fn(v)\n\t}\n\treturn ar\n}\n`, `\nprintln ListMap([1,2,3,4], x => x*x)\nListMap [1,2,3,4], x => {\n\tprintln x\n\treturn x\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(ListMap([]int{1, 2, 3, 4}, func(x int) int {\n\t\treturn x * x\n\t}))\n\tListMap([]int{1, 2, 3, 4}, func(x int) int {\n\t\tfmt.Println(x)\n\t\treturn x\n\t})\n}\n`)\n}\n\nfunc TestInferOverloadFuncLambda(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\nfunc ListMap__0[T any](ar []T, fn func(v T) T)[]T {\n\tfor i, v := range ar {\n\t\tar[i] = fn(v)\n\t}\n\treturn ar\n}\nfunc ListMap__1(a string, fn func(s string)) {\n\tfor _, c := range a {\n\t\tfn(string(c))\n\t}\n}\n`, `\nprintln ListMap([1,2,3,4], x => x*x)\nListMap [1,2,3,4], x => {\n\tprintln x\n\treturn x\n}\nListMap \"hello\", x => {\n\tprintln x\n}\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(ListMap__0([]int{1, 2, 3, 4}, func(x int) int {\n\t\treturn x * x\n\t}))\n\tListMap__0([]int{1, 2, 3, 4}, func(x int) int {\n\t\tfmt.Println(x)\n\t\treturn x\n\t})\n\tListMap__1(\"hello\", func(x string) {\n\t\tfmt.Println(x)\n\t})\n}\n`)\n}\n\nfunc TestGenericFuncAlias(t *testing.T) {\n\tgopClTest(t, `import \"github.com/goplus/xgo/cl/internal/overload/foo\"\nfoo.test(100)\nfoo.test(\"hello\",100)\nfoo.test__1(100)\nfoo.test__1[int](100)\nfoo.test__2(1, true)\nfoo.test__2[int, string](1, \"hello\")\n`, `package main\n\nimport \"github.com/goplus/xgo/cl/internal/overload/foo\"\n\nfunc main() {\n\tfoo.Test__1(100)\n\tfoo.Test__2(\"hello\", 100)\n\tfoo.Test__1(100)\n\tfoo.Test__1[int](100)\n\tfoo.Test__2(1, true)\n\tfoo.Test__2[int, string](1, \"hello\")\n}\n`)\n}\n\nfunc TestGoptLambdaFunc(t *testing.T) {\n\tgopClTest(t, `\nimport \"github.com/goplus/xgo/cl/internal/overload/bar\"\n\ntype Message struct {\n\tinfo string\n}\np := &bar.Player{}\np.onCmd Message, msg => {\n\techo msg.info\n\treturn nil\n}\np.onCmd int, Message, 100, (n,msg) => {\n\techo n, msg.info\n\treturn nil\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/overload/bar\"\n)\n\ntype Message struct {\n\tinfo string\n}\n\nfunc main() {\n\tp := &bar.Player{}\n\tbar.XGot_Player_XGox_OnCmd__0[Message](p, func(msg Message) error {\n\t\tfmt.Println(msg.info)\n\t\treturn nil\n\t})\n\tbar.XGot_Player_XGox_OnCmd__1[int, Message](p, 100, func(n int, msg Message) error {\n\t\tfmt.Println(n, msg.info)\n\t\treturn nil\n\t})\n}\n`)\n}\n\nfunc TestGoptLambdaError(t *testing.T) {\n\tcodeErrorTest(t, `bar.xgo:8:9: 100 not type`, `\nimport \"github.com/goplus/xgo/cl/internal/overload/bar\"\n\ntype Message struct {\n\tinfo string\n}\np := &bar.Player{}\np.onCmd 100, Message, 100, (n, msg) => {\n\techo msg.info\n\treturn nil\n}\n`)\n\tvar msg string\n\tswitch runtime.Version()[:6] {\n\tcase \"go1.18\":\n\t\tmsg = \"bar.xgo:8:1: string does not implement ~int\"\n\tcase \"go1.19\":\n\t\tmsg = \"bar.xgo:8:1: string does not implement ~int (string missing in ~int)\"\n\tdefault:\n\t\tmsg = \"bar.xgo:8:1: string does not satisfy ~int (string missing in ~int)\"\n\t}\n\tcodeErrorTest(t, msg, `\nimport \"github.com/goplus/xgo/cl/internal/overload/bar\"\n\ntype Message struct {\n\tinfo string\n}\np := &bar.Player{}\np.onCmd string, Message,\"hello\",(n, msg) => {\n\techo msg.info\n\treturn nil\n}\n`)\n}\n\nfunc TestAliasTypeparams(t *testing.T) {\n\tgopMixedClTest(t, \"main\", `package main\n\ntype Set[T comparable] = map[T]struct{}\n`, `\nset := Set[string]{\n\t\"go\":  {},\n\t\"gop\": {},\n\t\"gox\": {},\n}\necho set\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tset := Set[string]{\"go\": struct {\n\t}{}, \"gop\": struct {\n\t}{}, \"gox\": struct {\n\t}{}}\n\tfmt.Println(set)\n}\n`)\n\n\tgopMixedClTest(t, \"main\", `package main\n\ntype Pair[T, U any] = struct {\n\tFirst  T\n\tSecond U\n}\n`, `\nc := &Pair[string, bool]{\"hello\", true}\necho c\n`, `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tc := &Pair[string, bool]{\"hello\", true}\n\tfmt.Println(c)\n}\n`)\n}\n"
  },
  {
    "path": "cmd/chore/goptestgo/goptestgo.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"go/build\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/gocmd\"\n\t\"github.com/goplus/xgo/x/xgoenv\"\n)\n\nfunc fileIsDirty(srcMod time.Time, destFile string) bool {\n\tfiDest, err := os.Stat(destFile)\n\tif err != nil {\n\t\treturn true\n\t}\n\treturn srcMod.After(fiDest.ModTime())\n}\n\nfunc runGoFile(dir, file, fname string) {\n\txgo := xgoenv.Get()\n\tconf := &tool.Config{XGo: xgo}\n\tconfCmd := &gocmd.BuildConfig{XGo: xgo}\n\tfi, err := os.Stat(file)\n\tif err != nil {\n\t\tlog.Panicln(err)\n\t}\n\tabsFile, _ := filepath.Abs(file)\n\thash := sha1.Sum([]byte(absFile))\n\toutFile := dir + \"g\" + base64.RawURLEncoding.EncodeToString(hash[:]) + fname\n\tif fileIsDirty(fi.ModTime(), outFile) {\n\t\terr = tool.RunFiles(outFile, []string{file}, nil, conf, confCmd)\n\t\tif err != nil {\n\t\t\tos.Remove(outFile)\n\t\t\tswitch e := err.(type) {\n\t\t\tcase *exec.ExitError:\n\t\t\t\tos.Exit(e.ExitCode())\n\t\t\tdefault:\n\t\t\t\tlog.Fatalln(\"runGoFile:\", err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nvar (\n\tgoRunPrefix = []byte(\"// run\\n\")\n)\n\nvar (\n\tskipFileNames = map[string]struct{}{\n\t\t\"convert4.go\":       {},\n\t\t\"peano.go\":          {},\n\t\t\"bug295.go\":         {}, // import . \"XXX\"\n\t\t\"issue15071.dir\":    {}, // dir\n\t\t\"issue29612.dir\":    {},\n\t\t\"issue31959.dir\":    {},\n\t\t\"issue29504.go\":     {}, // line\n\t\t\"issue18149.go\":     {},\n\t\t\"issue22662.go\":     {},\n\t\t\"issue27201.go\":     {},\n\t\t\"issue46903.go\":     {},\n\t\t\"issue50190.go\":     {}, // interesting, should be fixed\n\t\t\"nilptr_aix.go\":     {},\n\t\t\"inline_literal.go\": {},\n\t\t\"returntype.go\":     {}, // not a problem\n\t\t\"unsafebuiltins.go\": {},\n\t}\n)\n\nfunc gopTestRunGo(dir string) {\n\thome, _ := os.UserHomeDir()\n\ttargetDir := home + \"/.xgo/run/\"\n\tos.MkdirAll(targetDir, 0777)\n\tfilepath.Walk(dir, func(file string, fi os.FileInfo, err error) error {\n\t\tname := fi.Name()\n\t\tif err != nil || fi.IsDir() {\n\t\t\tif _, ok := skipFileNames[name]; ok {\n\t\t\t\treturn filepath.SkipDir\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tif _, ok := skipFileNames[name]; ok {\n\t\t\treturn nil\n\t\t}\n\t\text := filepath.Ext(name)\n\t\tif ext != \".go\" {\n\t\t\treturn nil\n\t\t}\n\t\tdata, err := os.ReadFile(file)\n\t\tif err != nil {\n\t\t\tlog.Panicln(err)\n\t\t}\n\t\tif !bytes.HasPrefix(data, goRunPrefix) {\n\t\t\treturn nil\n\t\t}\n\t\tlog.Println(\"==> gop run -v\", file)\n\t\trunGoFile(targetDir, file, name)\n\t\treturn nil\n\t})\n}\n\n// goptestgo: run all $GOROOT/test/*.go\nfunc main() {\n\tdir := filepath.Join(build.Default.GOROOT, \"test\")\n\tgopTestRunGo(dir)\n}\n"
  },
  {
    "path": "cmd/chore/xgobuiltingen/builtin.gox",
    "content": "import (\n\t\"go/ast\"\n)\n\nvar (\n\tName string\n\tFn   reference\n)\n\nfunc genAST() *ast.FuncDecl {\n\tf := Fn\n\tref := f.getAST()\n\tnewName := Name.capitalize\n\treturn {\n\t\tDoc:  docForFunc(ref.Doc, f.Name, newName),\n\t\tName: ast.newIdent(newName),\n\t\tType: reference.toFuncType(ref.Type, f.Pkg),\n\t}\n}\n\nfunc genMethodAST(methods []builtin) *ast.FuncDecl {\n\tf := Fn\n\tat := f.Pkg\n\tref := f.getAST()\n\tex := f.Exargs\n\tmt, recvType := reference.toMethodType(ref.Type, ex, at)\n\tif at == \"\" { // builtin\n\t\trecvType = methods[methods.len-1].genAST().Type.Params.List[0].Type\n\t}\n\trecvName := recvNameOf(recvType)\n\treturn {\n\t\tDoc:  docForMethod(ref.Doc, at, f.Name, Name, recvName, ex),\n\t\tName: ast.newIdent(Name),\n\t\tType: mt,\n\t\tRecv: {List: {{\n\t\t\tNames: []*ast.Ident{ast.newIdent(recvName)},\n\t\t\tType:  recvType,\n\t\t}}},\n\t}\n}\n"
  },
  {
    "path": "cmd/chore/xgobuiltingen/builtingen.gox",
    "content": "import (\n\t\"bytes\"\n\t\"go/ast\"\n\t\"go/format\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"os\"\n\txgoast \"xgo/ast\"\n\t\"xgo/ast/gopq\"\n\t\"xgo/parser\"\n)\n\ntype builtinTI struct {\n\tMethods []builtin\n}\n\nvar (\n\tBuiltins []builtin\n\tTypes    []*builtinTI\n)\n\nfunc gen() []byte {\n\tf := &ast.File{\n\t\tName:  ast.newIdent(\"builtin\"),\n\t\tDecls: make([]ast.Decl, 0, 128),\n\t}\n\tf.Decls <- &ast.GenDecl{\n\t\tTok: token.IMPORT,\n\t\tSpecs: []ast.Spec{\n\t\t\timportSpec(\"github.com/qiniu/x/osx\"),\n\t\t\timportSpec(\"io\"),\n\t\t\timportSpec(\"os\"),\n\t\t\timportSpec(\"reflect\"),\n\t\t},\n\t}\n\tgenDecls f\n\tb := new(bytes.Buffer)\n\tformat.node! b, fset, f\n\treturn format.source(b.bytes)!\n}\n\nfunc genDecls(f *ast.File) {\n\tfor built in Builtins {\n\t\tf.Decls <- built.genAST()\n\t}\n\tfor t in Types {\n\t\tmthds := t.Methods\n\t\tfor m in mthds {\n\t\t\tf.Decls <- m.genMethodAST(mthds)\n\t\t}\n\t}\n}\n\nfunc initBuiltinTIs(fn gopq.NodeSet, f *xgoast.File) (tistr *builtinTI) {\n\tti := fn.body.any.assignStmt.rhs(0).x.compositeLit(\"BuiltinTI\")\n\tmethods := ti.elt(\"methods\").cache\n\tfor method in methods {\n\t\taTI := &builtinTI{}\n\t\tfor item in method.elt {\n\t\t\tmthd := item.elt(0).unquotedString!\n\t\t\tfn := item.elt(1).one\n\t\t\tif ref := fn.callExpr.one; ref.ok {\n\t\t\t\tpkg := ref.fun.x.ident!\n\t\t\t\tname := ref.arg(0).unquotedString!\n\t\t\t\tvar ex *exargs\n\t\t\t\tif e := item.elt(2).compositeLit(\"bmExargs\").one; e.ok {\n\t\t\t\t\tpos := e.positions!\n\t\t\t\t\tcode := gopq.codeOf(fset, f, pos[2]+1, pos[3])\n\t\t\t\t\tex = &exargs{e.eltLen!, code}\n\t\t\t\t}\n\t\t\t\taTI.Methods <- builtin{mthd, {pkg, name, ex}}\n\t\t\t} else {\n\t\t\t\tname := fn.ident!\n\t\t\t\taTI.Methods <- builtin{mthd, {\"\", name, nil}}\n\t\t\t}\n\t\t}\n\t\tTypes <- aTI\n\t\tif len(aTI.Methods) > 10 {\n\t\t\ttistr = aTI\n\t\t}\n\t}\n\treturn\n}\n\nfunc newBuiltinDefault(fn gopq.NodeSet, tistr *builtinTI) {\n\tmethods := fn.body.any.exprStmt.x.callExpr(\"ti.AddMethods\").cache\n\tfor method in methods {\n\t\taTI := &builtinTI{}\n\t\tfor arg in method.varg(0).x {\n\t\t\tmthd := arg.elt(\"Name\").unquotedString!\n\t\t\tref := arg.elt(\"Fn\").callExpr.one\n\t\t\tpkg := ref.fun.x.ident!\n\t\t\tname := ref.arg(0).unquotedString!\n\t\t\tif pkg == \"strx\" {\n\t\t\t\ttistr.Methods <- builtin{mthd, {pkg, name, nil}}\n\t\t\t} else {\n\t\t\t\taTI.Methods <- builtin{mthd, {pkg, name, nil}}\n\t\t\t}\n\t\t}\n\t\tif len(aTI.Methods) > 0 {\n\t\t\tTypes <- aTI\n\t\t}\n\t}\n}\n\nfunc initBuiltin(fn gopq.NodeSet) {\n\tstmt := fn.body.any.exprStmt.x.cache\n\titem := stmt.callExpr(\"scope.Insert\").arg(0).cache\n\tfor call in item.callExpr(\"gogen.NewOverloadFunc\") {\n\t\tbuilt := call.arg(2).unquotedString!\n\t\tif built != \"newRange\" { // hide builtin `newRange`\n\t\t\tref := call.arg(3).callExpr.one\n\t\t\tpkg := ref.fun.x.ident!\n\t\t\tname := ref.arg(0).unquotedString!\n\t\t\tBuiltins <- builtin{built, {pkg, name, nil}}\n\t\t}\n\t}\n\tfor call in stmt.callExpr(\"initBuiltinFns\") {\n\t\tpkg := call.arg(2).ident!\n\t\tbuiltins := call.arg(3).unquotedStringElts!\n\t\tfor built in builtins {\n\t\t\tBuiltins <- builtin{built, {pkg, built.capitalize, nil}}\n\t\t}\n\t}\n}\n\nf := parser.parseFile(fset, \"${root}/../gogen/builtin.go\", nil, parser.ParseComments)!\nfns := gopq.one(f).funcs\ntistr := initBuiltinTIs(fns.funcDecl(\"initBuiltinTIs\").one, f)\n\nfns = gopq.fromFile(fset, \"${root}/cl/builtin.go\", nil, parser.ParseComments)!.funcs\ninitBuiltin fns.funcDecl(\"initBuiltin\").one\nnewBuiltinDefault fns.funcDecl(\"newBuiltinDefault\").one, tistr\n\nb := gen()\nos.Stdout.write b\nos.writeFile \"${root}/builtin/doc.xgo\", b, 0777\n"
  },
  {
    "path": "cmd/chore/xgobuiltingen/helper.xgo",
    "content": "import (\n\t\"go/ast\"\n)\n\nfunc recvNameOf(recvType ast.Expr) string {\n\tvar name string\n\tswitch t := recvType.(type) {\n\tcase *ast.Ident:\n\t\tname = t.Name[:1]\n\tcase *ast.SelectorExpr:\n\t\tname = t.Sel.Name[:1]\n\tdefault:\n\t\treturn \"v\"\n\t}\n\treturn name.toLower\n}\n\nfunc importSpec(path string) *ast.ImportSpec {\n\treturn {\n\t\tPath: {\n\t\t\tValue: path.quote,\n\t\t},\n\t}\n}\n\nfunc rmExargs(list []*ast.Field, ex *exargs) []*ast.Field {\n\tif ex == nil {\n\t\treturn list\n\t}\n\tn := len(list)\n\tret := make([]*ast.Field, n)\n\tfor i, f in list {\n\t\tret[i] = f\n\t}\n\texargs := ex.N\n\tfor n > 0 && exargs > 0 {\n\t\tf := ret[n-1]\n\t\tif c := len(f.Names); c > exargs {\n\t\t\tf.Names = f.Names[:c-exargs]\n\t\t\tbreak\n\t\t} else {\n\t\t\texargs -= c\n\t\t}\n\t\tn--\n\t}\n\treturn ret[:n]\n}\n\nfunc toParams(params *ast.FieldList, at string) *ast.FieldList {\n\tif params == nil {\n\t\treturn nil\n\t}\n\tlist := make([]*ast.Field, len(params.List))\n\tfor i, p in params.List {\n\t\ttyp := p.Type\n\t\tswitch t := typ.(type) {\n\t\tcase *ast.Ident:\n\t\t\tif t.isExported {\n\t\t\t\ttyp = &ast.SelectorExpr{\n\t\t\t\t\tX:   ast.newIdent(at),\n\t\t\t\t\tSel: t,\n\t\t\t\t}\n\t\t\t}\n\t\tcase *ast.StarExpr:\n\t\t\tif x, ok := t.X.(*ast.Ident); ok && x.isExported {\n\t\t\t\ttyp = &ast.StarExpr{\n\t\t\t\t\tX: &ast.SelectorExpr{\n\t\t\t\t\t\tX:   ast.newIdent(at),\n\t\t\t\t\t\tSel: x,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlist[i] = {\n\t\t\tDoc:     p.Doc,\n\t\t\tNames:   p.Names,\n\t\t\tType:    typ,\n\t\t\tTag:     p.Tag,\n\t\t\tComment: p.Comment,\n\t\t}\n\t}\n\treturn {List: list}\n}\n\nfunc docForFunc(doc *ast.CommentGroup, oldName, newName string) *ast.CommentGroup {\n\tif doc == nil {\n\t\treturn nil\n\t}\n\tlist := make([]*ast.Comment, len(doc.List))\n\tfor i, c := range doc.List {\n\t\ttext := c.Text.replaceAll(oldName, newName)\n\t\tlist[i] = {Text: text}\n\t}\n\treturn {List: list}\n}\n\nfunc docForMethod(doc *ast.CommentGroup, at, oldName, newName, recvName string, ex *exargs) *ast.CommentGroup {\n\tif doc == nil {\n\t\treturn nil\n\t}\n\tif ex == nil {\n\t\treturn docForFunc(doc, oldName, newName)\n\t}\n\tlist := make([]*ast.Comment, 0, len(doc.List)+2)\n\tlist <- &ast.Comment{Text: \"// ${newName} is equivalent to ${at}.${oldName}(${recvName}, ${ex.Code})\"}\n\tlist <- &ast.Comment{Text: \"//\"}\n\tfor _, c := range doc.List {\n\t\tlist <- &ast.Comment{Text: c.Text}\n\t}\n\treturn {List: list}\n}\n"
  },
  {
    "path": "cmd/chore/xgobuiltingen/reference.gox",
    "content": "import (\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"runtime\"\n\t\"xgo/env\"\n)\n\ntype exargs struct {\n\tN    int\n\tCode string\n}\n\nvar (\n\tPkg    string\n\tName   string\n\tExargs *exargs\n)\n\nfunc .toFuncType(t *ast.FuncType, at string) *ast.FuncType {\n\treturn {\n\t\tParams:  t.Params,\n\t\tResults: toParams(t.Results, at),\n\t}\n}\n\nfunc .toMethodType(t *ast.FuncType, ex *exargs, at string) (mt *ast.FuncType, recvType ast.Expr) {\n\tlist := t.Params.List\n\tfirst := list[0]\n\trecvType = first.Type\n\tif len(first.Names) == 1 {\n\t\tlist = list[1:]\n\t} else {\n\t\tlist[0] = {\n\t\t\tDoc:     first.Doc,\n\t\t\tNames:   first.Names[1:],\n\t\t\tType:    recvType,\n\t\t\tComment: first.Comment,\n\t\t}\n\t}\n\tmt = {\n\t\tParams:  {List: rmExargs(list, ex)},\n\t\tResults: toParams(t.Results, at),\n\t}\n\treturn\n}\n\nvar (\n\tfset    = token.newFileSet\n\tpkgs    = map[string]*ast.Package{}\n\troot    = env.XGOROOT()\n\tgoroot  = runtime.GOROOT()\n\tpkgDirs = {\n\t\t\"\":        \"${goroot}/src/builtin\",\n\t\t\"fmt\":     \"${goroot}/src/fmt\",\n\t\t\"os\":      \"${goroot}/src/os\",\n\t\t\"reflect\": \"${goroot}/src/reflect\",\n\t\t\"strconv\": \"${goroot}/src/strconv\",\n\t\t\"strings\": \"${goroot}/src/strings\",\n\n\t\t\"buil\":        \"${root}/../x/xgo\",\n\t\t\"osx\":         \"${root}/../x/osx\",\n\t\t\"stringslice\": \"${root}/../x/stringslice\",\n\t\t\"strx\":        \"${root}/../x/stringutil\",\n\t}\n)\n\nfunc getAST() *ast.FuncDecl {\n\tname := Name\n\tif Pkg == \"\" {\n\t\tname = name.trimPrefix(\"bto\").toLower\n\t}\n\tpkg := reference.pkgOf(Pkg)\n\treturn reference.funcDecl(pkg, name)\n}\n\nfunc .funcDecl(pkg *ast.Package, name string) *ast.FuncDecl {\n\tfor file in pkg.Files {\n\t\tfor decl in file.Decls {\n\t\t\tif fn, ok := decl.(*ast.FuncDecl); ok && fn.Name.Name == name {\n\t\t\t\treturn fn\n\t\t\t}\n\t\t}\n\t}\n\tpanic(\"function not found: ${pkg.Name}.${name}\")\n}\n\nfunc .pkgOf(pkgPath string) *ast.Package {\n\tif pkg, ok := pkgs[pkgPath]; ok {\n\t\treturn pkg\n\t}\n\n\tdir, ok := pkgDirs[pkgPath]\n\tif !ok {\n\t\tpanic(\"unknown package: ${pkgPath}\")\n\t}\n\n\tfor name, pkg in parser.parseDir(fset, dir, nil, parser.ParseComments)! {\n\t\tif name.hasSuffix(\"_test\") {\n\t\t\tcontinue\n\t\t}\n\t\tpkgs[pkgPath] = pkg\n\t\treturn pkg\n\t}\n\tpanic(\"package not found: ${pkgPath}\")\n}\n"
  },
  {
    "path": "cmd/chore/xgofullspec/fullspec.xgo",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport (\n\t\"flag\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"xgo/parser\"\n\t\"xgo/parser/parsertest\"\n\t\"xgo/token\"\n\t\"xgo/x/xgoprojs\"\n)\n\nfunc dump(f any) {\n\tparsertest.FprintNode os.Stdout, \"\", f, \"\", \"  \"\n}\n\nfunc isGopFile(file string) bool {\n\tif len(file) > 4 {\n\t\tswitch file[len(file)-4:] {\n\t\tcase \".xgo\", \".gop\", \".gox\", \".gsh\", \".spx\", \".yap\":\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc parseFiles(files []string, dumpAST bool) (nErr int) {\n\tfset := token.newFileSet\n\tfor file in files {\n\t\tfprintln os.Stderr, \"\\n==> Parsing ${file}\"\n\t\tf, err := parser.parseFile(fset, file, nil, parser.ParseComments)\n\t\tif err != nil {\n\t\t\tfprintln os.Stderr, err\n\t\t\tnErr++\n\t\t} else if dumpAST {\n\t\t\tdump f\n\t\t}\n\t}\n\treturn\n}\n\nfunc parseDir(dir string, recursively, dumpAST bool) (nErr int) {\n\tfset := token.newFileSet\n\tfilepath.walkDir dir, (file, d, err) => {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if fname := d.name; d.isDir {\n\t\t\tif recursively {\n\t\t\t\tif fname.hasPrefix(\"_\") || fname == \"fullspec\" {\n\t\t\t\t\treturn filepath.SkipDir\n\t\t\t\t}\n\t\t\t} else if file != dir {\n\t\t\t\treturn filepath.SkipDir\n\t\t\t}\n\t\t} else if !fname.hasPrefix(\"_\") && isGopFile(fname) {\n\t\t\tfprintln os.Stderr, \"\\n==> Parsing ${file}\"\n\t\t\tf, err := parser.parseFile(fset, file, nil, parser.ParseComments)\n\t\t\tif err != nil {\n\t\t\t\tfprintln os.Stderr, err\n\t\t\t\tnErr++\n\t\t\t} else if dumpAST {\n\t\t\t\tdump f\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\treturn\n}\n\nvar (\n\tflagDumpAST = flag.Bool(\"ast\", false, \"dump AST\")\n)\n\nflag.parse\n\npattern := flag.args\nif pattern.len == 0 {\n\techo \"Usage: gopfullspec [-ast] packages\"\n\treturn\n}\n\ndumpAST := *flagDumpAST\nnErr := 0\n\nprojs, err := xgoprojs.parseAll(pattern...)\nif err != nil {\n\tfprintln os.Stderr, err\n\tos.exit 1\n}\n\nfor proj in projs {\n\tswitch v := proj.(type) {\n\tcase *xgoprojs.DirProj:\n\t\tdir := v.Dir\n\t\trecursively := dir.hasSuffix(\"/...\")\n\t\tif recursively {\n\t\t\tdir = dir[:len(dir)-4]\n\t\t}\n\t\tnErr += parseDir(dir, recursively, dumpAST)\n\tcase *xgoprojs.FilesProj:\n\t\tnErr += parseFiles(v.Files, dumpAST)\n\tdefault:\n\t\tpanic \"only support local files and directories\"\n\t}\n}\nfprintln os.Stderr\n\nif nErr > 0 {\n\tos.exit 1\n}\n"
  },
  {
    "path": "cmd/chore/xgominispec/minispec.xgo",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport (\n\t\"flag\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"xgo/doc/spec/mini\"\n\t\"xgo/tpl\"\n\t\"xgo/tpl/matcher\"\n\t\"xgo/x/xgoprojs\"\n)\n\nfunc isGopFile(file string) bool {\n\tif len(file) > 4 {\n\t\tswitch file[len(file)-4:] {\n\t\tcase \".xgo\", \".gop\", \".gox\", \".gsh\", \".spx\", \".yap\":\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc parseFiles(files []string, dumpAST bool) (nErr int) {\n\tfor file in files {\n\t\tfprintln os.Stderr, \"\\n==> Parsing ${file}\"\n\t\tf, err := mini.parseFile(nil, file, nil)\n\t\tif err != nil {\n\t\t\tfprintln os.Stderr, err\n\t\t\tnErr++\n\t\t} else if dumpAST {\n\t\t\ttpl.dump f\n\t\t}\n\t}\n\treturn\n}\n\nfunc parseDir(dir string, recursively, dumpAST bool) (nErr int) {\n\tfilepath.walkDir dir, (file, d, err) => {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if fname := d.name; d.isDir {\n\t\t\tif recursively {\n\t\t\t\tif fname.hasPrefix(\"_\") || fname == \"fullspec\" {\n\t\t\t\t\treturn filepath.SkipDir\n\t\t\t\t}\n\t\t\t} else if file != dir {\n\t\t\t\treturn filepath.SkipDir\n\t\t\t}\n\t\t} else if !fname.hasPrefix(\"_\") && isGopFile(fname) {\n\t\t\tfprintln os.Stderr, \"\\n==> Parsing ${file}\"\n\t\t\tf, err := mini.parseFile(nil, file, nil)\n\t\t\tif err != nil {\n\t\t\t\tfprintln os.Stderr, err\n\t\t\t\tnErr++\n\t\t\t} else if dumpAST {\n\t\t\t\ttpl.dump f\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\treturn\n}\n\nvar (\n\tflagVerbose = flag.Bool(\"v\", false, \"print verbose information\")\n\tflagDumpAST = flag.Bool(\"ast\", false, \"dump AST\")\n)\n\nflag.parse\n\npattern := flag.args\nif pattern.len == 0 {\n\techo \"Usage: gopminispec [-v -ast] packages\"\n\treturn\n}\n\nif *flagVerbose {\n\tmatcher.setDebug matcher.DbgFlagAll\n}\n\ndumpAST := *flagDumpAST\nnErr := 0\n\nprojs, err := xgoprojs.parseAll(pattern...)\nif err != nil {\n\tfprintln os.Stderr, err\n\tos.exit 1\n}\n\nfor proj in projs {\n\tswitch v := proj.(type) {\n\tcase *xgoprojs.DirProj:\n\t\tdir := v.Dir\n\t\trecursively := dir.hasSuffix(\"/...\")\n\t\tif recursively {\n\t\t\tdir = dir[:len(dir)-4]\n\t\t}\n\t\tnErr += parseDir(dir, recursively, dumpAST)\n\tcase *xgoprojs.FilesProj:\n\t\tnErr += parseFiles(v.Files, dumpAST)\n\tdefault:\n\t\tpanic \"only support local files and directories\"\n\t}\n}\nfprintln os.Stderr\n\nif nErr > 0 {\n\tos.exit 1\n}\n"
  },
  {
    "path": "cmd/hdq/fetch_cmd.gox",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *\t http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"xgo/dql/fetcher\"\n)\n\nuse \"fetch [flags] fetchType [input ... | -]\"\n\nshort \"Fetch objects from the html source with the specified fetchType and inputs\"\n\nrun args => {\n\tif args.len < 1 {\n\t\thelp\n\t\treturn\n\t}\n\tfetchType := args[0]\n\tinputs := args[1:]\n\tif len(inputs) == 1 && inputs[0] == \"-\" {\n\t\tinputs = string(io.readAll(os.Stdin)!).fields\n\t}\n\tdocs := make([]any, 0, len(inputs))\n\tfor input in inputs {\n\t\tlog.println \"==> Fetch\", input\n\t\tdoc := fetcher.do(fetchType, input)!\n\t\tdocs <- doc\n\t}\n\tenc := json.newEncoder(os.Stdout)\n\tenc.setIndent \"\", \"  \"\n\tenc.encode! docs\n}\n"
  },
  {
    "path": "cmd/hdq/list_cmd.gox",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *\t http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\t\"xgo/dql/fetcher\"\n)\n\nuse \"list\"\n\nshort \"List all supported fetchTypes.\"\n\nrun => {\n\tfor ft in fetcher.list {\n\t\techo ft\n\t}\n}\n"
  },
  {
    "path": "cmd/hdq/main_app.gox",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *\t http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\t_ \"github.com/qiniu/x/stream/http/cached\"\n\n\t_ \"xgo/dql/fetcher/github.com/issueTask\"\n\t_ \"xgo/dql/fetcher/github.com/repoList\"\n\t_ \"xgo/dql/fetcher/hrefs\"\n\t_ \"xgo/dql/fetcher/pkg.go.dev/importedBy\"\n\t_ \"xgo/dql/fetcher/pytorch.org/fndoc\"\n)\n\nuse \"hdq\"\n\nshort \"hdq - An HTML DOM Query Tool (powered by XGo DQL)\"\n"
  },
  {
    "path": "cmd/hdq/xgo_autogen.go",
    "content": "// Code generated by xgo (XGo); DO NOT EDIT.\n\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/goplus/cobra/xcmd\"\n\t\"github.com/goplus/xgo/dql/fetcher\"\n\t_ \"github.com/goplus/xgo/dql/fetcher/github.com/issueTask\"\n\t_ \"github.com/goplus/xgo/dql/fetcher/github.com/repoList\"\n\t_ \"github.com/goplus/xgo/dql/fetcher/hrefs\"\n\t_ \"github.com/goplus/xgo/dql/fetcher/pkg.go.dev/importedBy\"\n\t_ \"github.com/goplus/xgo/dql/fetcher/pytorch.org/fndoc\"\n\t\"github.com/qiniu/x/errors\"\n\t_ \"github.com/qiniu/x/stream/http/cached\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n)\n\nconst _ = true\n\ntype Cmd_fetch struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_list struct {\n\txcmd.Command\n\t*App\n}\ntype App struct {\n\txcmd.App\n}\n//line cmd/hdq/main_app.gox:26\nfunc (this *App) MainEntry() {\n//line cmd/hdq/main_app.gox:26:1\n\tthis.Use(\"hdq\")\n//line cmd/hdq/main_app.gox:28:1\n\tthis.Short(\"hdq - An HTML DOM Query Tool (powered by XGo DQL)\")\n}\nfunc (this *App) Main() {\n\t_xgo_obj0 := &Cmd_fetch{App: this}\n\t_xgo_obj1 := &Cmd_list{App: this}\n\txcmd.XGot_App_Main(this, _xgo_obj0, _xgo_obj1)\n}\n//line cmd/hdq/fetch_cmd.gox:24\nfunc (this *Cmd_fetch) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/hdq/fetch_cmd.gox:24:1\n\tthis.Use(\"fetch [flags] fetchType [input ... | -]\")\n//line cmd/hdq/fetch_cmd.gox:26:1\n\tthis.Short(\"Fetch objects from the html source with the specified fetchType and inputs\")\n//line cmd/hdq/fetch_cmd.gox:28:1\n\tthis.Run__1(func(args []string) {\n//line cmd/hdq/fetch_cmd.gox:29:1\n\t\tif len(args) < 1 {\n//line cmd/hdq/fetch_cmd.gox:30:1\n\t\t\tthis.Help()\n//line cmd/hdq/fetch_cmd.gox:31:1\n\t\t\treturn\n\t\t}\n//line cmd/hdq/fetch_cmd.gox:33:1\n\t\tfetchType := args[0]\n//line cmd/hdq/fetch_cmd.gox:34:1\n\t\tinputs := args[1:]\n//line cmd/hdq/fetch_cmd.gox:35:1\n\t\tif len(inputs) == 1 && inputs[0] == \"-\" {\n//line cmd/hdq/fetch_cmd.gox:36:1\n\t\t\tinputs = strings.Fields(string(func() (_xgo_ret []byte) {\n//line cmd/hdq/fetch_cmd.gox:36:1\n\t\t\t\tvar _xgo_err error\n//line cmd/hdq/fetch_cmd.gox:36:1\n\t\t\t\t_xgo_ret, _xgo_err = io.ReadAll(os.Stdin)\n//line cmd/hdq/fetch_cmd.gox:36:1\n\t\t\t\tif _xgo_err != nil {\n//line cmd/hdq/fetch_cmd.gox:36:1\n\t\t\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"io.readAll(os.Stdin)\", \"cmd/hdq/fetch_cmd.gox\", 36, \"main.Main\")\n//line cmd/hdq/fetch_cmd.gox:36:1\n\t\t\t\t\tpanic(_xgo_err)\n\t\t\t\t}\n//line cmd/hdq/fetch_cmd.gox:36:1\n\t\t\t\treturn\n\t\t\t}()))\n\t\t}\n//line cmd/hdq/fetch_cmd.gox:38:1\n\t\tdocs := make([]interface{}, 0, len(inputs))\n\t\tfor\n//line cmd/hdq/fetch_cmd.gox:39:1\n\t\t_, input := range inputs {\n//line cmd/hdq/fetch_cmd.gox:40:1\n\t\t\tlog.Println(\"==> Fetch\", input)\n//line cmd/hdq/fetch_cmd.gox:41:1\n\t\t\tdoc := func() (_xgo_ret any) {\n//line cmd/hdq/fetch_cmd.gox:41:1\n\t\t\t\tvar _xgo_err error\n//line cmd/hdq/fetch_cmd.gox:41:1\n\t\t\t\t_xgo_ret, _xgo_err = fetcher.Do(fetchType, input)\n//line cmd/hdq/fetch_cmd.gox:41:1\n\t\t\t\tif _xgo_err != nil {\n//line cmd/hdq/fetch_cmd.gox:41:1\n\t\t\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"fetcher.do(fetchType, input)\", \"cmd/hdq/fetch_cmd.gox\", 41, \"main.Main\")\n//line cmd/hdq/fetch_cmd.gox:41:1\n\t\t\t\t\tpanic(_xgo_err)\n\t\t\t\t}\n//line cmd/hdq/fetch_cmd.gox:41:1\n\t\t\t\treturn\n\t\t\t}()\n//line cmd/hdq/fetch_cmd.gox:42:1\n\t\t\tdocs = append(docs, doc)\n\t\t}\n//line cmd/hdq/fetch_cmd.gox:44:1\n\t\tenc := json.NewEncoder(os.Stdout)\n//line cmd/hdq/fetch_cmd.gox:45:1\n\t\tenc.SetIndent(\"\", \"  \")\n//line cmd/hdq/fetch_cmd.gox:46:1\n\t\tfunc() {\n//line cmd/hdq/fetch_cmd.gox:46:1\n\t\t\tvar _xgo_err error\n//line cmd/hdq/fetch_cmd.gox:46:1\n\t\t\t_xgo_err = enc.Encode(docs)\n//line cmd/hdq/fetch_cmd.gox:46:1\n\t\t\tif _xgo_err != nil {\n//line cmd/hdq/fetch_cmd.gox:46:1\n\t\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"enc.encode docs\", \"cmd/hdq/fetch_cmd.gox\", 46, \"main.Main\")\n//line cmd/hdq/fetch_cmd.gox:46:1\n\t\t\t\tpanic(_xgo_err)\n\t\t\t}\n//line cmd/hdq/fetch_cmd.gox:46:1\n\t\t\treturn\n\t\t}()\n\t})\n}\nfunc (this *Cmd_fetch) Classfname() string {\n\treturn \"fetch\"\n}\n//line cmd/hdq/list_cmd.gox:20\nfunc (this *Cmd_list) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/hdq/list_cmd.gox:20:1\n\tthis.Use(\"list\")\n//line cmd/hdq/list_cmd.gox:22:1\n\tthis.Short(\"List all supported fetchTypes.\")\n//line cmd/hdq/list_cmd.gox:24:1\n\tthis.Run__0(func() {\n\t\tfor\n//line cmd/hdq/list_cmd.gox:25:1\n\t\t_, ft := range fetcher.List() {\n//line cmd/hdq/list_cmd.gox:26:1\n\t\t\tfmt.Println(ft)\n\t\t}\n\t})\n}\nfunc (this *Cmd_list) Classfname() string {\n\treturn \"list\"\n}\nfunc main() {\n\tnew(App).Main()\n}\n"
  },
  {
    "path": "cmd/internal/base/base.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package base defines shared basic pieces of the gop command,\n// in particular logging and the Command structure.\npackage base\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n)\n\n// A Command is an implementation of a gop command\n// like gop export or gop install.\ntype Command struct {\n\t// Run runs the command.\n\t// The args are the arguments after the command name.\n\tRun func(cmd *Command, args []string)\n\n\t// UsageLine is the one-line usage message.\n\t// The words between \"gop\" and the first flag or argument in the line are taken to be the command name.\n\tUsageLine string\n\n\t// Short is the short description shown in the 'gop help' output.\n\tShort string\n\n\t// Flag is a set of flags specific to this command.\n\tFlag flag.FlagSet\n\n\t// Commands lists the available commands and help topics.\n\t// The order here is the order in which they are printed by 'gop help'.\n\t// Note that subcommands are in general best avoided.\n\tCommands []*Command\n}\n\n// Gop command\nvar Gop = &Command{\n\tUsageLine: \"gop\",\n\tShort:     `Gop is a tool for managing XGo source code.`,\n\t// Commands initialized in package main\n}\n\n// LongName returns the command's long name: all the words in the usage line between \"gop\" and a flag or argument,\nfunc (c *Command) LongName() string {\n\tname := c.UsageLine\n\tif i := strings.Index(name, \" [\"); i >= 0 {\n\t\tname = name[:i]\n\t}\n\tif name == \"gop\" {\n\t\treturn \"\"\n\t}\n\treturn strings.TrimPrefix(name, \"gop \")\n}\n\n// Name returns the command's short name: the last word in the usage line before a flag or argument.\nfunc (c *Command) Name() string {\n\tname := c.LongName()\n\tif i := strings.LastIndex(name, \" \"); i >= 0 {\n\t\tname = name[i+1:]\n\t}\n\treturn name\n}\n\n// Usage show the command usage.\nfunc (c *Command) Usage(w io.Writer) {\n\tfmt.Fprintf(w, \"%s\\n\\nUsage: %s\\n\", c.Short, c.UsageLine)\n\n\t// restore output of flag\n\tdefer c.Flag.SetOutput(c.Flag.Output())\n\n\tc.Flag.SetOutput(w)\n\tc.Flag.PrintDefaults()\n\tfmt.Fprintln(w)\n\tos.Exit(2)\n}\n\n// Runnable reports whether the command can be run; otherwise\n// it is a documentation pseudo-command.\nfunc (c *Command) Runnable() bool {\n\treturn c.Run != nil\n}\n\n// Usage is the usage-reporting function, filled in by package main\n// but here for reference by other packages.\n//\n// flag.Usage func()\n\n// CmdName - \"build\", \"install\", \"list\", \"mod tidy\", etc.\nvar CmdName string\n\n// Main runs a command.\nfunc Main(c *Command, app string, args []string) {\n\tname := c.UsageLine\n\tif i := strings.Index(name, \" [\"); i >= 0 {\n\t\tc.UsageLine = app + name[i:]\n\t}\n\tc.Run(c, args)\n}\n"
  },
  {
    "path": "cmd/internal/base/pass.go",
    "content": "package base\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype stringValue struct {\n\tp    *PassArgs\n\tname string\n}\n\nfunc (p *stringValue) String() string {\n\treturn \"\"\n}\n\nfunc (p *stringValue) Set(v string) error {\n\tp.p.Args = append(p.p.Args, fmt.Sprintf(\"-%v=%v\", p.name, v))\n\treturn nil\n}\n\ntype boolValue struct {\n\tp    *PassArgs\n\tname string\n}\n\nfunc (p *boolValue) String() string {\n\treturn \"\"\n}\n\nfunc (p *boolValue) Set(v string) error {\n\tp.p.Args = append(p.p.Args, fmt.Sprintf(\"-%v=%v\", p.name, v))\n\treturn nil\n}\n\nfunc (p *boolValue) IsBoolFlag() bool {\n\treturn true\n}\n\ntype PassArgs struct {\n\tArgs []string\n\tFlag *flag.FlagSet\n}\n\nfunc (p *PassArgs) Tags() string {\n\tfor _, v := range p.Args {\n\t\tif strings.HasPrefix(v, \"-tags=\") {\n\t\t\treturn v[6:]\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc (p *PassArgs) Var(names ...string) {\n\tfor _, name := range names {\n\t\tp.Flag.Var(&stringValue{p: p, name: name}, name, \"\")\n\t}\n}\n\nfunc (p *PassArgs) Bool(names ...string) {\n\tfor _, name := range names {\n\t\tp.Flag.Var(&boolValue{p: p, name: name}, name, \"\")\n\t}\n}\n\nfunc NewPassArgs(flag *flag.FlagSet) *PassArgs {\n\tp := &PassArgs{Flag: flag}\n\treturn p\n}\n\nfunc PassBuildFlags(cmd *Command) *PassArgs {\n\tp := NewPassArgs(&cmd.Flag)\n\tp.Bool(\"v\")\n\tp.Bool(\"n\", \"x\")\n\tp.Bool(\"a\")\n\tp.Bool(\"linkshared\", \"race\", \"msan\", \"asan\",\n\t\t\"trimpath\", \"work\")\n\tp.Var(\"p\", \"asmflags\", \"compiler\", \"buildmode\",\n\t\t\"gcflags\", \"gccgoflags\", \"installsuffix\",\n\t\t\"ldflags\", \"pkgdir\", \"tags\", \"toolexec\", \"buildvcs\")\n\treturn p\n}\n"
  },
  {
    "path": "cmd/internal/bug/bug.go",
    "content": "/*\n * Copyright (c) 2021-2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage bug\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"time\"\n\n\turlpkg \"net/url\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/env\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Cmd - xgo bug\nvar Cmd = &base.Command{\n\tUsageLine: \"xgo bug\",\n\tShort:     \"Start a bug report\",\n}\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(_ *base.Command, args []string) {\n\tif len(args) > 0 {\n\t\tlog.Fatalf(\"xgo: bug takes no arguments\")\n\t}\n\tvar buf bytes.Buffer\n\tbuf.WriteString(bugHeader)\n\tprintXgoVersion(&buf)\n\tbuf.WriteString(\"### Does this issue reproduce with the latest release?\\n\\n\\n\")\n\tprintEnvDetails(&buf)\n\tbuf.WriteString(bugFooter)\n\n\tbody := buf.String()\n\turl := \"https://github.com/goplus/xgo/issues/new?body=\" + urlpkg.QueryEscape(body)\n\tif !open(url) {\n\t\tfmt.Print(\"Please file a new issue at golang.org/issue/new using this template:\\n\\n\")\n\t\tfmt.Print(body)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\nconst bugHeader = `<!-- Please answer these questions before submitting your issue. Thanks! -->\n\n`\nconst bugFooter = `### What did you do?\n\n<!--\nIf possible, provide a recipe for reproducing the error.\nA complete runnable program is good.\nA link on play.xgo.dev is best.\n-->\n\n\n\n### What did you expect to see?\n\n\n\n### What did you see instead?\n\n`\n\nfunc printXgoVersion(w io.Writer) {\n\tfmt.Fprintf(w, \"### What version of xgo are you using (`xgo version`)?\\n\\n\")\n\tfmt.Fprintf(w, \"<pre>\\n\")\n\tfmt.Fprintf(w, \"$ xgo version\\n\")\n\tfmt.Fprintf(w, \"xgo version %s %s/%s\\n\", env.Version(), runtime.GOOS, runtime.GOARCH)\n\tfmt.Fprintf(w, \"</pre>\\n\")\n\tfmt.Fprintf(w, \"\\n\")\n}\n\nfunc printEnvDetails(w io.Writer) {\n\tfmt.Fprintf(w, \"### What operating system and processor architecture are you using (`xgo env`)?\\n\\n\")\n\tfmt.Fprintf(w, \"<details><summary><code>xgo env</code> Output</summary><br><pre>\\n\")\n\tfmt.Fprintf(w, \"$ xgo env\\n\")\n\tprintXgoEnv(w)\n\tprintXgoDetails(w)\n\tprintOSDetails(w)\n\tprintCDetails(w)\n\tfmt.Fprintf(w, \"</pre></details>\\n\\n\")\n}\n\nfunc printXgoEnv(w io.Writer) {\n\tcmd := exec.Command(\"xgo\", \"env\")\n\tcmd.Env = os.Environ()\n\tcmd.Stdout = w\n\n\terr := cmd.Run()\n\tif err != nil {\n\t\tlog.Fatalln(\"run xgo env failed:\", err)\n\t}\n}\n\nfunc printXgoDetails(w io.Writer) {\n\txgocmd := filepath.Join(runtime.GOROOT(), \"bin/xgo\")\n\tprintCmdOut(w, \"GOROOT/bin/xgo version: \", xgocmd, \"version\")\n\tprintCmdOut(w, \"GOROOT/bin/xgo tool compile -V: \", xgocmd, \"tool\", \"compile\", \"-V\")\n}\n\nfunc printOSDetails(w io.Writer) {\n\tswitch runtime.GOOS {\n\tcase \"darwin\", \"ios\":\n\t\tprintCmdOut(w, \"uname -v: \", \"uname\", \"-v\")\n\t\tprintCmdOut(w, \"\", \"sw_vers\")\n\tcase \"linux\":\n\t\tprintCmdOut(w, \"uname -sr: \", \"uname\", \"-sr\")\n\t\tprintCmdOut(w, \"\", \"lsb_release\", \"-a\")\n\t\tprintGlibcVersion(w)\n\tcase \"openbsd\", \"netbsd\", \"freebsd\", \"dragonfly\":\n\t\tprintCmdOut(w, \"uname -v: \", \"uname\", \"-v\")\n\tcase \"illumos\", \"solaris\":\n\t\t// Be sure to use the OS-supplied uname, in \"/usr/bin\":\n\t\tprintCmdOut(w, \"uname -srv: \", \"/usr/bin/uname\", \"-srv\")\n\t\tout, err := os.ReadFile(\"/etc/release\")\n\t\tif err == nil {\n\t\t\tfmt.Fprintf(w, \"/etc/release: %s\\n\", out)\n\t\t}\n\t}\n}\n\nfunc printCDetails(w io.Writer) {\n\tprintCmdOut(w, \"lldb --version: \", \"lldb\", \"--version\")\n\tcmd := exec.Command(\"gdb\", \"--version\")\n\tout, err := cmd.Output()\n\tif err == nil {\n\t\t// There's apparently no combination of command line flags\n\t\t// to get gdb to spit out its version without the license and warranty.\n\t\t// Print up to the first newline.\n\t\tfmt.Fprintf(w, \"gdb --version: %s\\n\", firstLine(out))\n\t}\n}\n\n// printCmdOut prints the output of running the given command.\n// It ignores failures; 'xgo bug' is best effort.\nfunc printCmdOut(w io.Writer, prefix, path string, args ...string) {\n\tcmd := exec.Command(path, args...)\n\tout, err := cmd.Output()\n\tif err != nil {\n\t\treturn\n\t}\n\tfmt.Fprintf(w, \"%s%s\\n\", prefix, bytes.TrimSpace(out))\n}\n\n// firstLine returns the first line of a given byte slice.\nfunc firstLine(buf []byte) []byte {\n\tidx := bytes.IndexByte(buf, '\\n')\n\tif idx > 0 {\n\t\tbuf = buf[:idx]\n\t}\n\treturn bytes.TrimSpace(buf)\n}\n\n// printGlibcVersion prints information about the glibc version.\n// It ignores failures.\nfunc printGlibcVersion(w io.Writer) {\n\ttempdir := os.TempDir()\n\tif tempdir == \"\" {\n\t\treturn\n\t}\n\tsrc := []byte(`int main() {}`)\n\tsrcfile := filepath.Join(tempdir, \"go-bug.c\")\n\toutfile := filepath.Join(tempdir, \"go-bug\")\n\terr := os.WriteFile(srcfile, src, 0644)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer os.Remove(srcfile)\n\tcmd := exec.Command(\"gcc\", \"-o\", outfile, srcfile)\n\tif _, err = cmd.CombinedOutput(); err != nil {\n\t\treturn\n\t}\n\tdefer os.Remove(outfile)\n\n\tcmd = exec.Command(\"ldd\", outfile)\n\tout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn\n\t}\n\tre := regexp.MustCompile(`libc\\.so[^ ]* => ([^ ]+)`)\n\tm := re.FindStringSubmatch(string(out))\n\tif m == nil {\n\t\treturn\n\t}\n\tcmd = exec.Command(m[1])\n\tout, err = cmd.Output()\n\tif err != nil {\n\t\treturn\n\t}\n\tfmt.Fprintf(w, \"%s: %s\\n\", m[1], firstLine(out))\n\n\t// print another line (the one containing version string) in case of musl libc\n\tif idx := bytes.IndexByte(out, '\\n'); bytes.Contains(out, []byte(\"musl\")) && idx > -1 {\n\t\tfmt.Fprintf(w, \"%s\\n\", firstLine(out[idx+1:]))\n\t}\n}\n\n// Commands returns a list of possible commands to use to open a url.\nfunc commands() [][]string {\n\tvar cmds [][]string\n\tif exe := os.Getenv(\"BROWSER\"); exe != \"\" {\n\t\tcmds = append(cmds, []string{exe})\n\t}\n\tswitch runtime.GOOS {\n\tcase \"darwin\":\n\t\tcmds = append(cmds, []string{\"/usr/bin/open\"})\n\tcase \"windows\":\n\t\tcmds = append(cmds, []string{\"cmd\", \"/c\", \"start\"})\n\tdefault:\n\t\tif os.Getenv(\"DISPLAY\") != \"\" {\n\t\t\t// xdg-open is only for use in a desktop environment.\n\t\t\tcmds = append(cmds, []string{\"xdg-open\"})\n\t\t}\n\t}\n\tcmds = append(cmds,\n\t\t[]string{\"chrome\"},\n\t\t[]string{\"google-chrome\"},\n\t\t[]string{\"chromium\"},\n\t\t[]string{\"firefox\"},\n\t)\n\treturn cmds\n}\n\n// Open tries to open url in a browser and reports whether it succeeded.\nfunc open(url string) bool {\n\tfor _, args := range commands() {\n\t\tcmd := exec.Command(args[0], append(args[1:], url)...)\n\t\tif cmd.Start() == nil && appearsSuccessful(cmd, 3*time.Second) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// appearsSuccessful reports whether the command appears to have run successfully.\n// If the command runs longer than the timeout, it's deemed successful.\n// If the command runs within the timeout, it's deemed successful if it exited cleanly.\nfunc appearsSuccessful(cmd *exec.Cmd, timeout time.Duration) bool {\n\terrc := make(chan error, 1)\n\tgo func() {\n\t\terrc <- cmd.Wait()\n\t}()\n\n\tselect {\n\tcase <-time.After(timeout):\n\t\treturn true\n\tcase err := <-errc:\n\t\treturn err == nil\n\t}\n}\n"
  },
  {
    "path": "cmd/internal/build/build.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package build implements the “gop build” command.\npackage build\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/gocmd\"\n\t\"github.com/goplus/xgo/x/xgoprojs\"\n)\n\n// gop build\nvar Cmd = &base.Command{\n\tUsageLine: \"gop build [-debug -o output] [packages]\",\n\tShort:     \"Build XGo files\",\n}\n\nvar (\n\tflag       = &Cmd.Flag\n\tflagDebug  = flag.Bool(\"debug\", false, \"print debug information\")\n\tflagOutput = flag.String(\"o\", \"\", \"gop build output file\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\tpass := base.PassBuildFlags(cmd)\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Panicln(\"parse input arguments failed:\", err)\n\t}\n\n\tif *flagDebug {\n\t\tgogen.SetDebug(gogen.DbgFlagAll &^ gogen.DbgFlagComments)\n\t\tcl.SetDebug(cl.DbgFlagAll)\n\t\tcl.SetDisableRecover(true)\n\t}\n\n\targs = flag.Args()\n\tif len(args) == 0 {\n\t\targs = []string{\".\"}\n\t}\n\n\tproj, args, err := xgoprojs.ParseOne(args...)\n\tif err != nil {\n\t\tlog.Panicln(err)\n\t}\n\tif len(args) != 0 {\n\t\tlog.Panicln(\"too many arguments:\", args)\n\t}\n\n\tconf, err := tool.NewDefaultConf(\".\", tool.ConfFlagNoTestFiles, pass.Tags())\n\tif err != nil {\n\t\tlog.Panicln(\"tool.NewDefaultConf:\", err)\n\t}\n\tdefer conf.UpdateCache()\n\n\tconfCmd := conf.NewGoCmdConf()\n\tif *flagOutput != \"\" {\n\t\toutput, err := filepath.Abs(*flagOutput)\n\t\tif err != nil {\n\t\t\tlog.Panicln(err)\n\t\t}\n\t\tconfCmd.Flags = []string{\"-o\", output}\n\t}\n\tconfCmd.Flags = append(confCmd.Flags, pass.Args...)\n\tbuild(proj, conf, confCmd)\n}\n\nfunc build(proj xgoprojs.Proj, conf *tool.Config, build *gocmd.BuildConfig) {\n\tconst flags = tool.GenFlagPrompt\n\tvar obj string\n\tvar err error\n\tswitch v := proj.(type) {\n\tcase *xgoprojs.DirProj:\n\t\tobj = v.Dir\n\t\terr = tool.BuildDir(obj, conf, build, flags)\n\tcase *xgoprojs.PkgPathProj:\n\t\tobj = v.Path\n\t\terr = tool.BuildPkgPath(\"\", v.Path, conf, build, flags)\n\tcase *xgoprojs.FilesProj:\n\t\terr = tool.BuildFiles(v.Files, conf, build)\n\tdefault:\n\t\tlog.Panicln(\"`gop build` doesn't support\", reflect.TypeOf(v))\n\t}\n\tif tool.NotFound(err) {\n\t\tfmt.Fprintf(os.Stderr, \"gop build %v: not found\\n\", obj)\n\t} else if err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t} else {\n\t\treturn\n\t}\n\tos.Exit(1)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/clean/clean.go",
    "content": "/*\n * Copyright (c) 2021-2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage clean\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n)\n\nconst (\n\tautoGenFileSuffix   = \"_autogen.go\"\n\tautoGenGopTestFile  = \"gop_autogen_test.go\"\n\tautoGen2GopTestFile = \"gop_autogen2_test.go\"\n\tautoGenXgoTestFile  = \"xgo_autogen_test.go\"\n\tautoGen2XgoTestFile = \"xgo_autogen2_test.go\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc cleanAGFiles(dir string, execAct bool) {\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\treturn\n\t}\n\tfor _, fi := range fis {\n\t\tfname := fi.Name()\n\t\tif strings.HasPrefix(fname, \"_\") {\n\t\t\tcontinue\n\t\t}\n\t\tif fi.IsDir() {\n\t\t\tpkgDir := filepath.Join(dir, fname)\n\t\t\tif fname == \".xgo\" || fname == \".gop\" {\n\t\t\t\tremoveGopDir(pkgDir, execAct)\n\t\t\t} else {\n\t\t\t\tcleanAGFiles(pkgDir, execAct)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasSuffix(fname, autoGenFileSuffix) {\n\t\t\tfile := filepath.Join(dir, fname)\n\t\t\tfmt.Printf(\"Cleaning %s ...\\n\", file)\n\t\t\tif execAct {\n\t\t\t\tos.Remove(file)\n\t\t\t}\n\t\t}\n\t}\n\tautogens := []string{\n\t\tautoGenGopTestFile, autoGen2GopTestFile,\n\t\tautoGenXgoTestFile, autoGen2XgoTestFile,\n\t}\n\tfor _, autogen := range autogens {\n\t\tfile := filepath.Join(dir, autogen)\n\t\tif _, err = os.Stat(file); err == nil {\n\t\t\tfmt.Printf(\"Cleaning %s ...\\n\", file)\n\t\t\tif execAct {\n\t\t\t\tos.Remove(file)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc removeGopDir(dir string, execAct bool) {\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\treturn\n\t}\n\tfor _, fi := range fis {\n\t\tfname := fi.Name()\n\t\tif strings.HasSuffix(fname, \".xgo.go\") || strings.HasSuffix(fname, \".gop.go\") {\n\t\t\tgenfile := filepath.Join(dir, fname)\n\t\t\tfmt.Printf(\"Cleaning %s ...\\n\", genfile)\n\t\t\tif execAct {\n\t\t\t\tos.Remove(genfile)\n\t\t\t}\n\t\t}\n\t}\n\tif execAct {\n\t\tos.Remove(dir)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// Cmd - gop clean\nvar Cmd = &base.Command{\n\tUsageLine: \"gop clean [flags] <gopSrcDir>\",\n\tShort:     \"Clean all XGo auto generated files\",\n}\n\nvar (\n\tflag = &Cmd.Flag\n\n\t_        = flag.Bool(\"v\", false, \"print verbose information.\")\n\ttestMode = flag.Bool(\"t\", false, \"test mode: display files to clean but don't clean them.\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(_ *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\tvar dir string\n\tif flag.NArg() == 0 {\n\t\tdir = \".\"\n\t} else {\n\t\tdir = flag.Arg(0)\n\t}\n\tcleanAGFiles(dir, !*testMode)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/deps/deps.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage deps\n\n/*\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/x/gopmod\"\n)\n\n// -----------------------------------------------------------------------------\n\n// gop deps\nvar Cmd = &base.Command{\n\tUsageLine: \"gop deps [-v] [package]\",\n\tShort:     \"Show dependencies of a package or module\",\n}\n\nvar (\n\tflag = &Cmd.Flag\n\t_    = flag.Bool(\"v\", false, \"print verbose information.\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\n\tvar dir string\n\tnarg := flag.NArg()\n\tif narg < 1 {\n\t\tdir = \".\"\n\t} else {\n\t\tdir = flag.Arg(0)\n\t}\n\n\timports, err := gopmod.Imports(dir)\n\tcheck(err)\n\tfor _, imp := range imports {\n\t\tfmt.Println(imp)\n\t}\n}\n\nfunc check(err error) {\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n*/\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/doc/doc.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage doc\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\t\"log\"\n\t\"os\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cl/outline\"\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/xgoenv\"\n\t\"github.com/goplus/xgo/x/xgoprojs\"\n)\n\n// -----------------------------------------------------------------------------\n\n// gop doc\nvar Cmd = &base.Command{\n\tUsageLine: \"gop doc [-u -all -debug] [pkgPath]\",\n\tShort:     \"Show documentation for package or symbol\",\n}\n\nvar (\n\tflag    = &Cmd.Flag\n\twithDoc = flag.Bool(\"all\", false, \"Show all the documentation for the package.\")\n\tdebug   = flag.Bool(\"debug\", false, \"Print debug information.\")\n\tunexp   = flag.Bool(\"u\", false, \"Show documentation for unexported as well as exported symbols, methods, and fields.\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\n\tpattern := flag.Args()\n\tif len(pattern) == 0 {\n\t\tpattern = []string{\".\"}\n\t}\n\n\tproj, _, err := xgoprojs.ParseOne(pattern...)\n\tif err != nil {\n\t\tlog.Panicln(\"xgoprojs.ParseOne:\", err)\n\t}\n\n\tif *debug {\n\t\tgogen.SetDebug(gogen.DbgFlagAll &^ gogen.DbgFlagComments)\n\t\tcl.SetDebug(cl.DbgFlagAll)\n\t\tcl.SetDisableRecover(true)\n\t}\n\n\txgo := xgoenv.Get()\n\tconf := &tool.Config{XGo: xgo}\n\toutlinePkg(proj, conf)\n}\n\nfunc outlinePkg(proj xgoprojs.Proj, conf *tool.Config) {\n\tvar obj string\n\tvar out outline.Package\n\tvar err error\n\tswitch v := proj.(type) {\n\tcase *xgoprojs.DirProj:\n\t\tobj = v.Dir\n\t\tout, err = tool.Outline(obj, conf)\n\tcase *xgoprojs.PkgPathProj:\n\t\tobj = v.Path\n\t\tout, err = tool.OutlinePkgPath(\"\", obj, conf, true)\n\tdefault:\n\t\tlog.Panicln(\"`gop doc` doesn't support\", reflect.TypeOf(v))\n\t}\n\tif tool.NotFound(err) {\n\t\tfmt.Fprintf(os.Stderr, \"gop doc %v: not Go/XGo files found\\n\", obj)\n\t} else if err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t} else {\n\t\toutlineDoc(out.Outline(*unexp), *unexp, *withDoc)\n\t}\n}\n\nconst (\n\tindent = \"    \"\n\tln     = \"\\n\"\n)\n\nfunc outlineDoc(out *outline.All, all, withDoc bool) {\n\tpkg := out.Pkg()\n\tfmt.Printf(\"package %s // import %s\\n\\n\", pkg.Name(), strconv.Quote(pkg.Path()))\n\tif withDoc && len(out.Consts) > 0 {\n\t\tfmt.Print(\"CONSTANTS\\n\\n\")\n\t}\n\tfor _, o := range out.Consts {\n\t\tprintObject(pkg, o, withDoc)\n\t}\n\tif withDoc && len(out.Vars) > 0 {\n\t\tfmt.Print(\"VARIABLES\\n\\n\")\n\t}\n\tfor _, o := range out.Vars {\n\t\tprintObject(pkg, o, withDoc)\n\t}\n\tif withDoc && len(out.Funcs) > 0 {\n\t\tfmt.Print(\"FUNCTIONS\\n\\n\")\n\t}\n\tfor _, fn := range out.Funcs {\n\t\tprintObject(pkg, fn, withDoc)\n\t}\n\tif withDoc && len(out.Types) > 0 {\n\t\tfmt.Print(\"TYPES\\n\\n\")\n\t}\n\tfor _, t := range out.Types {\n\t\tif !(all || t.IsUsed()) {\n\t\t\tcontinue\n\t\t}\n\t\ttypName := t.ObjWith(all)\n\t\tfmt.Print(objectString(pkg, typName), ln)\n\t\tfor _, o := range t.Consts {\n\t\t\tfmt.Print(indent, constShortString(o.Const), ln)\n\t\t}\n\t\tif withDoc {\n\t\t\tprintDoc(t)\n\t\t}\n\t\tprintFuncsForType(pkg, t.Creators, withDoc)\n\t\tprintFuncsForType(pkg, t.GoptFuncs, withDoc)\n\t\tprintFuncsForType(pkg, t.Helpers, withDoc)\n\t\tif !typName.IsAlias() {\n\t\t\ttyp := t.Type()\n\t\t\tif named, ok := typ.CheckNamed(out.Package); ok {\n\t\t\t\tfor _, fn := range named.Methods() {\n\t\t\t\t\tif o := fn.Obj(); all || o.Exported() {\n\t\t\t\t\t\tif withDoc {\n\t\t\t\t\t\t\tfmt.Print(objectString(pkg, o), ln)\n\t\t\t\t\t\t\tprintDoc(fn)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfmt.Print(indent, objectString(pkg, o), ln)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\ntype object interface {\n\tObj() types.Object\n\tDoc() string\n}\n\nfunc printObject(pkg *types.Package, o object, withDoc bool) {\n\tfmt.Print(objectString(pkg, o.Obj()), ln)\n\tif withDoc {\n\t\tprintDoc(o)\n\t}\n}\n\nfunc printDoc(o object) {\n\tif doc := o.Doc(); doc != \"\" {\n\t\tfmt.Print(indent, strings.ReplaceAll(doc, \"\\n\", \"\\n\"+indent), ln)\n\t} else {\n\t\tfmt.Println()\n\t}\n}\n\nfunc printFuncsForType(pkg *types.Package, fns []outline.Func, withDoc bool) {\n\tfor _, fn := range fns {\n\t\tif withDoc {\n\t\t\tprintObject(pkg, fn, true)\n\t\t} else {\n\t\t\tfmt.Print(indent, objectString(pkg, fn.Obj()), ln)\n\t\t}\n\t}\n}\n\nfunc objectString(pkg *types.Package, obj types.Object) string {\n\tif name, fn, ok := outline.CheckOverload(obj); ok {\n\t\tobj = types.NewFunc(fn.Pos(), fn.Pkg(), name, fn.Type().(*types.Signature))\n\t}\n\treturn types.ObjectString(obj, qualifier(pkg))\n}\n\nfunc constShortString(obj *types.Const) string {\n\treturn \"const \" + obj.Name()\n}\n\nfunc qualifier(pkg *types.Package) types.Qualifier {\n\treturn func(other *types.Package) string {\n\t\tif pkg == other {\n\t\t\treturn \"\" // same package; unqualified\n\t\t}\n\t\treturn other.Name()\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/env/env.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"sort\"\n\n\t\"github.com/goplus/mod\"\n\t\"github.com/goplus/mod/modcache\"\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/env\"\n\t\"github.com/goplus/xgo/x/gocmd\"\n)\n\n// Cmd - gop env\nvar Cmd = &base.Command{\n\tUsageLine: \"gop env [-json] [var ...]\",\n\tShort:     \"Prints XGo environment information\",\n}\n\nvar (\n\tflag    = &Cmd.Flag\n\tenvJson = flag.Bool(\"json\", false, \"prints Go environment information.\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(_ *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\n\tvar stdout bytes.Buffer\n\n\tcmd := exec.Command(\"go\", \"env\", \"-json\")\n\tcmd.Env = os.Environ()\n\tcmd.Stdout = &stdout\n\n\terr = cmd.Run()\n\tif err != nil {\n\t\tlog.Fatalln(\"run go env failed:\", err)\n\t}\n\n\tvar xgoEnv map[string]any\n\tif err := json.Unmarshal(stdout.Bytes(), &xgoEnv); err != nil {\n\t\tlog.Fatal(\"decode json of go env failed:\", err)\n\t}\n\n\txgoEnv[\"BUILDDATE\"] = env.BuildDate()\n\txgoEnv[\"XGOVERSION\"] = env.Version()\n\txgoEnv[\"XGOROOT\"] = env.XGOROOT()\n\txgoEnv[\"XGO_GOCMD\"] = gocmd.Name()\n\txgoEnv[\"GOMODCACHE\"] = modcache.GOMODCACHE\n\txgoEnv[\"GOXMOD\"], _ = mod.GOXMOD(\"\")\n\txgoEnv[\"HOME\"] = env.HOME()\n\n\tvars := flag.Args()\n\n\toutputEnvVars(xgoEnv, vars, *envJson)\n}\n\nfunc outputEnvVars(gopEnv map[string]any, vars []string, outputJson bool) {\n\tonlyValues := true\n\n\tif len(vars) == 0 {\n\t\tonlyValues = false\n\n\t\tvars = make([]string, 0, len(gopEnv))\n\t\tfor k := range gopEnv {\n\t\t\tvars = append(vars, k)\n\t\t}\n\t\tsort.Strings(vars)\n\t} else {\n\t\tnewEnv := make(map[string]any)\n\t\tfor _, v := range vars {\n\t\t\tif value, ok := gopEnv[v]; ok {\n\t\t\t\tnewEnv[v] = value\n\t\t\t} else {\n\t\t\t\tnewEnv[v] = \"\"\n\t\t\t}\n\t\t}\n\t\tgopEnv = newEnv\n\t}\n\n\tif outputJson {\n\t\tb, err := json.Marshal(gopEnv)\n\t\tif err != nil {\n\t\t\tlog.Fatal(\"encode json of go env failed:\", err)\n\t\t}\n\n\t\tvar out bytes.Buffer\n\t\tjson.Indent(&out, b, \"\", \"\\t\")\n\t\tfmt.Println(out.String())\n\t} else {\n\t\tfor _, k := range vars {\n\t\t\tv := gopEnv[k]\n\t\t\tif onlyValues {\n\t\t\t\tfmt.Printf(\"%v\\n\", v)\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"%s=\\\"%v\\\"\\n\", k, v)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/internal/gengo/go.go",
    "content": "/*\n * Copyright (c) 2021-2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package gengo implements the “gop go” command.\npackage gengo\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"reflect\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/xgoprojs\"\n\t\"github.com/qiniu/x/errors\"\n)\n\n// gop go\nvar Cmd = &base.Command{\n\tUsageLine: \"gop go [-v] [packages|files]\",\n\tShort:     \"Convert XGo code into Go code\",\n}\n\nvar (\n\tflag                 = &Cmd.Flag\n\tflagVerbose          = flag.Bool(\"v\", false, \"print verbose information\")\n\tflagCheckMode        = flag.Bool(\"t\", false, \"do check syntax only, no generate xgo_autogen.go\")\n\tflagSingleMode       = flag.Bool(\"s\", false, \"run in single file mode for package\")\n\tflagIgnoreNotatedErr = flag.Bool(\n\t\t\"ignore-notated-error\", false, \"ignore notated errors, only available together with -t (check mode)\")\n\tflagTags = flag.String(\"tags\", \"\", \"a comma-separated list of additional build tags to consider satisfied\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Panicln(\"parse input arguments failed:\", err)\n\t}\n\tpattern := flag.Args()\n\tif len(pattern) == 0 {\n\t\tpattern = []string{\".\"}\n\t}\n\n\tprojs, err := xgoprojs.ParseAll(pattern...)\n\tif err != nil {\n\t\tlog.Panicln(\"xgoprojs.ParseAll:\", err)\n\t}\n\n\tif *flagVerbose {\n\t\tgogen.SetDebug(gogen.DbgFlagAll &^ gogen.DbgFlagComments)\n\t\tcl.SetDebug(cl.DbgFlagAll)\n\t\tcl.SetDisableRecover(true)\n\t}\n\n\tconf, err := tool.NewDefaultConf(\".\", 0, *flagTags)\n\tif err != nil {\n\t\tlog.Panicln(\"tool.NewDefaultConf:\", err)\n\t}\n\tdefer conf.UpdateCache()\n\n\tflags := tool.GenFlagPrintError | tool.GenFlagPrompt\n\tif *flagCheckMode {\n\t\tflags |= tool.GenFlagCheckOnly\n\t\tif *flagIgnoreNotatedErr {\n\t\t\tconf.IgnoreNotatedError = true\n\t\t}\n\t}\n\tif *flagSingleMode {\n\t\tflags |= tool.GenFlagSingleFile\n\t}\n\tfor _, proj := range projs {\n\t\tswitch v := proj.(type) {\n\t\tcase *xgoprojs.DirProj:\n\t\t\t_, _, err = tool.GenGoEx(v.Dir, conf, true, flags)\n\t\tcase *xgoprojs.PkgPathProj:\n\t\t\t_, _, err = tool.GenGoPkgPathEx(\"\", v.Path, conf, true, flags)\n\t\tcase *xgoprojs.FilesProj:\n\t\t\t_, err = tool.GenGoFiles(\"\", v.Files, conf)\n\t\tdefault:\n\t\t\tlog.Panicln(\"`gop go` doesn't support\", reflect.TypeOf(v))\n\t\t}\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"GenGo failed: %d errors.\\n\", errorNum(err))\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n}\n\nfunc errorNum(err error) int {\n\tif e, ok := err.(errors.List); ok {\n\t\treturn len(e)\n\t}\n\treturn 1\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/gopfmt/fmt.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package gopfmt implements the “gop fmt” command.\npackage gopfmt\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/format\"\n\t\"github.com/goplus/xgo/tool\"\n\n\tgoformat \"go/format\"\n\t\"go/parser\"\n\t\"go/token\"\n\n\txformat \"github.com/goplus/xgo/x/format\"\n)\n\n// Cmd - gop fmt\nvar Cmd = &base.Command{\n\tUsageLine: \"gop fmt [flags] path ...\",\n\tShort:     \"Format XGo packages\",\n}\n\nvar (\n\tflag        = &Cmd.Flag\n\tflagTest    = flag.Bool(\"t\", false, \"test if XGo files are formatted or not.\")\n\tflagNotExec = flag.Bool(\"n\", false, \"prints commands that would be executed.\")\n\tflagMoveGo  = flag.Bool(\"mvgo\", false, \"move .go files to .xgo files (only available in `--smart` mode).\")\n\tflagSmart   = flag.Bool(\"smart\", false, \"convert Go code style into XGo style.\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nvar (\n\ttestErrCnt = 0\n\tprocCnt    = 0\n\twalkSubDir = false\n\trootDir    = \"\"\n)\n\nfunc gopfmt(path string, class, smart, mvgo bool) (err error) {\n\tsrc, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar target []byte\n\tif smart {\n\t\ttarget, err = xformat.GopstyleSource(src, path)\n\t} else {\n\t\tif !mvgo && filepath.Ext(path) == \".go\" {\n\t\t\tfset := token.NewFileSet()\n\t\t\tf, err := parser.ParseFile(fset, path, src, parser.ParseComments)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tvar buf bytes.Buffer\n\t\t\terr = goformat.Node(&buf, fset, f)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ttarget = buf.Bytes()\n\t\t} else {\n\t\t\ttarget, err = format.Source(src, class, path)\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\tif bytes.Equal(src, target) {\n\t\treturn\n\t}\n\tfmt.Println(path)\n\tif *flagTest {\n\t\ttestErrCnt++\n\t\treturn nil\n\t}\n\tif mvgo {\n\t\tnewPath := strings.TrimSuffix(path, \".go\") + \".xgo\"\n\t\tif err = os.WriteFile(newPath, target, 0666); err != nil {\n\t\t\treturn\n\t\t}\n\t\treturn os.Remove(path)\n\t}\n\treturn writeFileWithBackup(path, target)\n}\n\nfunc writeFileWithBackup(path string, target []byte) (err error) {\n\tdir, file := filepath.Split(path)\n\tf, err := os.CreateTemp(dir, file)\n\tif err != nil {\n\t\treturn\n\t}\n\ttmpfile := f.Name()\n\t_, err = f.Write(target)\n\tf.Close()\n\tif err != nil {\n\t\treturn\n\t}\n\terr = os.Remove(path)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn os.Rename(tmpfile, path)\n}\n\ntype walker struct {\n\tdirMap map[string]func(ext string) (ok, class bool)\n}\n\nfunc newWalker() *walker {\n\treturn &walker{dirMap: make(map[string]func(ext string) (ok, class bool))}\n}\n\nfunc (w *walker) walk(path string, d fs.DirEntry, err error) error {\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t} else if d.IsDir() {\n\t\tif !walkSubDir && path != rootDir {\n\t\t\treturn filepath.SkipDir\n\t\t}\n\t} else {\n\t\tdir, _ := filepath.Split(path)\n\t\tfn, ok := w.dirMap[dir]\n\t\tif !ok {\n\t\t\tif mod, err := tool.LoadMod(path); err == nil {\n\t\t\t\tfn = func(ext string) (ok bool, class bool) {\n\t\t\t\t\tswitch ext {\n\t\t\t\t\tcase \".go\", \".xgo\", \".gop\":\n\t\t\t\t\t\tok = true\n\t\t\t\t\tcase \".gox\", \".spx\", \".gmx\":\n\t\t\t\t\t\tok, class = true, true\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tclass = mod.IsClass(ext)\n\t\t\t\t\t\tok = class\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfn = func(ext string) (ok bool, class bool) {\n\t\t\t\t\tswitch ext {\n\t\t\t\t\tcase \".go\", \".xgo\", \".gop\":\n\t\t\t\t\t\tok = true\n\t\t\t\t\tcase \".gox\", \".spx\", \".gmx\":\n\t\t\t\t\t\tok, class = true, true\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tw.dirMap[dir] = fn\n\t\t}\n\t\text := filepath.Ext(path)\n\t\tsmart := *flagSmart\n\t\tmvgo := smart && *flagMoveGo\n\t\tif ok, class := fn(ext); ok && (!mvgo || ext == \".go\") {\n\t\t\tprocCnt++\n\t\t\tif *flagNotExec {\n\t\t\t\tfmt.Println(\"xgo fmt\", path)\n\t\t\t} else {\n\t\t\t\terr = gopfmt(path, class, smart && (mvgo || ext != \".go\"), mvgo)\n\t\t\t\tif err != nil {\n\t\t\t\t\treport(err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn err\n}\n\nfunc report(err error) {\n\tfmt.Println(err)\n\tos.Exit(2)\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\tnarg := flag.NArg()\n\tif narg < 1 {\n\t\tcmd.Usage(os.Stderr)\n\t}\n\tif *flagTest {\n\t\tdefer func() {\n\t\t\tif testErrCnt > 0 {\n\t\t\t\tfmt.Printf(\"total %d files are not formatted.\\n\", testErrCnt)\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t}()\n\t}\n\twalker := newWalker()\n\tfor i := 0; i < narg; i++ {\n\t\tpath := flag.Arg(i)\n\t\twalkSubDir = strings.HasSuffix(path, \"/...\")\n\t\tif walkSubDir {\n\t\t\tpath = path[:len(path)-4]\n\t\t}\n\t\tprocCnt = 0\n\t\trootDir = path\n\t\tfilepath.WalkDir(path, walker.walk)\n\t\tif procCnt == 0 {\n\t\t\tfmt.Println(\"no XGo files in\", path)\n\t\t}\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/gopget/get.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage gopget\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/goplus/mod/modcache\"\n\t\"github.com/goplus/mod/modfetch\"\n\t\"github.com/goplus/mod/modload\"\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/tool\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Cmd - gop get\nvar Cmd = &base.Command{\n\tUsageLine: \"gop get [-v] [packages]\",\n\tShort:     `Add dependencies to current module and install them`,\n}\n\nvar (\n\tflag = &Cmd.Flag\n\t_    = flag.Bool(\"v\", false, \"print verbose information.\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\tnarg := flag.NArg()\n\tif narg < 1 {\n\t\tlog.Fatalln(\"TODO: not impl\")\n\t}\n\tfor i := 0; i < narg; i++ {\n\t\tget(flag.Arg(i))\n\t}\n}\n\nfunc get(pkgPath string) {\n\tmodBase := \"\"\n\tmod, err := modload.Load(\".\")\n\tnoMod := tool.NotFound(err)\n\tif !noMod {\n\t\tcheck(err)\n\t\tmodBase = mod.Path()\n\t}\n\n\tpkgModVer, _, err := modfetch.GetPkg(pkgPath, modBase)\n\tcheck(err)\n\tif noMod {\n\t\treturn\n\t}\n\n\tpkgModRoot, err := modcache.Path(pkgModVer)\n\tcheck(err)\n\n\tpkgMod, err := modload.Load(pkgModRoot)\n\tcheck(err)\n\n\tcheck(mod.AddRequire(pkgModVer.Path, pkgModVer.Version, pkgMod.HasProject()))\n\tfmt.Fprintf(os.Stderr, \"gop get: added %s %s\\n\", pkgModVer.Path, pkgModVer.Version)\n\n\tcheck(mod.Save())\n}\n\nfunc check(err error) {\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/help/help.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package help implements the “gop help” command.\npackage help\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"text/template\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n)\n\n// Help implements the 'help' command.\nfunc Help(w io.Writer, args []string) {\n\tcmd := base.Gop\nArgs:\n\tfor i, arg := range args {\n\t\tfor _, sub := range cmd.Commands {\n\t\t\tif sub.Name() == arg {\n\t\t\t\tcmd = sub\n\t\t\t\tcontinue Args\n\t\t\t}\n\t\t}\n\n\t\t// helpSuccess is the help command using as many args as possible that would succeed.\n\t\thelpSuccess := \"gop help\"\n\t\tif i > 0 {\n\t\t\thelpSuccess += \" \" + strings.Join(args[:i], \" \")\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"gop help %s: unknown help topic. Run '%s'.\\n\", strings.Join(args, \" \"), helpSuccess)\n\t\tos.Exit(2)\n\t}\n\n\tif len(cmd.Commands) > 0 {\n\t\tPrintUsage(w, cmd)\n\t} else {\n\t\tcmd.Usage(w)\n\t}\n\t// not exit 2: succeeded at 'gop help cmd'.\n}\n\nvar usageTemplate = `{{.Short | trim}}\n\nUsage:\n\n\t{{.UsageLine}} <command> [arguments]\n\nThe commands are:\n{{range .Commands}}{{if or (.Runnable) .Commands}}\n\t{{.Name | printf \"%-11s\"}} {{.Short}}{{end}}{{end}}\n\nUse \"gop help{{with .LongName}} {{.}}{{end}} <command>\" for more information about a command.\n\n`\n\n// An errWriter wraps a writer, recording whether a write error occurred.\ntype errWriter struct {\n\tw   io.Writer\n\terr error\n}\n\nfunc (w *errWriter) Write(b []byte) (int, error) {\n\tn, err := w.w.Write(b)\n\tif err != nil {\n\t\tw.err = err\n\t}\n\treturn n, err\n}\n\n// tmpl executes the given template text on data, writing the result to w.\nfunc tmpl(w io.Writer, text string, data any) {\n\tt := template.New(\"top\")\n\tt.Funcs(template.FuncMap{\"trim\": strings.TrimSpace, \"capitalize\": capitalize})\n\ttemplate.Must(t.Parse(text))\n\tew := &errWriter{w: w}\n\terr := t.Execute(ew, data)\n\tif ew.err != nil {\n\t\t// I/O error writing. Ignore write on closed pipe.\n\t\tif strings.Contains(ew.err.Error(), \"pipe\") {\n\t\t\tos.Exit(1)\n\t\t}\n\t\tlog.Fatalf(\"writing output: %v\", ew.err)\n\t}\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc capitalize(s string) string {\n\tif s == \"\" {\n\t\treturn s\n\t}\n\tr, n := utf8.DecodeRuneInString(s)\n\treturn string(unicode.ToTitle(r)) + s[n:]\n}\n\n// PrintUsage prints usage information.\nfunc PrintUsage(w io.Writer, cmd *base.Command) {\n\tbw := bufio.NewWriter(w)\n\ttmpl(bw, usageTemplate, cmd)\n\tbw.Flush()\n}\n"
  },
  {
    "path": "cmd/internal/install/install.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package install implements the “gop install” command.\npackage install\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"reflect\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/mod/modfetch\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/gocmd\"\n\t\"github.com/goplus/xgo/x/xgoprojs\"\n)\n\n// gop install\nvar Cmd = &base.Command{\n\tUsageLine: \"gop install [-debug] [packages]\",\n\tShort:     \"Build XGo files and install target to GOBIN\",\n}\n\nvar (\n\tflag      = &Cmd.Flag\n\tflagDebug = flag.Bool(\"debug\", false, \"print debug information\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\tpass := base.PassBuildFlags(cmd)\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\n\tpattern := flag.Args()\n\tif len(pattern) == 0 {\n\t\tpattern = []string{\".\"}\n\t}\n\n\tprojs, err := xgoprojs.ParseAll(pattern...)\n\tif err != nil {\n\t\tlog.Panicln(\"xgoprojs.ParseAll:\", err)\n\t}\n\n\tif *flagDebug {\n\t\tmodfetch.SetDebug(modfetch.DbgFlagAll)\n\t\tgogen.SetDebug(gogen.DbgFlagAll &^ gogen.DbgFlagComments)\n\t\tcl.SetDebug(cl.DbgFlagAll)\n\t\tcl.SetDisableRecover(true)\n\t}\n\n\tconf, err := tool.NewDefaultConf(\".\", tool.ConfFlagNoTestFiles, pass.Tags())\n\tif err != nil {\n\t\tlog.Panicln(\"tool.NewDefaultConf:\", err)\n\t}\n\tdefer conf.UpdateCache()\n\n\tconfCmd := conf.NewGoCmdConf()\n\tconfCmd.Flags = pass.Args\n\tfor _, proj := range projs {\n\t\tinstall(proj, conf, confCmd)\n\t}\n}\n\nfunc install(proj xgoprojs.Proj, conf *tool.Config, install *gocmd.InstallConfig) {\n\tconst flags = tool.GenFlagPrompt\n\tvar obj string\n\tvar err error\n\tswitch v := proj.(type) {\n\tcase *xgoprojs.DirProj:\n\t\tobj = v.Dir\n\t\terr = tool.InstallDir(obj, conf, install, flags)\n\tcase *xgoprojs.PkgPathProj:\n\t\tobj = v.Path\n\t\terr = tool.InstallPkgPath(\"\", v.Path, conf, install, flags)\n\tcase *xgoprojs.FilesProj:\n\t\terr = tool.InstallFiles(v.Files, conf, install)\n\tdefault:\n\t\tlog.Panicln(\"`gop install` doesn't support\", reflect.TypeOf(v))\n\t}\n\tif tool.NotFound(err) {\n\t\tfmt.Fprintf(os.Stderr, \"gop install %v: not found\\n\", obj)\n\t} else if err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t} else {\n\t\treturn\n\t}\n\tos.Exit(1)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/list/list.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage list\n\n/*\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/x/gopmod\"\n)\n\n// -----------------------------------------------------------------------------\n\n// gop list\nvar Cmd = &base.Command{\n\tUsageLine: \"gop list [-json] [packages]\",\n\tShort:     \"List packages or modules\",\n}\n\nvar (\n\tflag = &Cmd.Flag\n\t_    = flag.Bool(\"json\", false, \"printing in JSON format.\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\n\tpattern := flag.Args()\n\tif len(pattern) == 0 {\n\t\tpattern = []string{\".\"}\n\t}\n\n\tpkgPaths, err := gopmod.List(pattern...)\n\tcheck(err)\n\tfor _, pkgPath := range pkgPaths {\n\t\tfmt.Println(pkgPath)\n\t}\n}\n\nfunc check(err error) {\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n*/\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/mod/download.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage mod\n\nimport (\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/cmd/internal/gopget\"\n)\n\n// gop mod download\nvar CmdDownload = &base.Command{\n\tUsageLine: \"gop mod download [-x -json] [modules]\",\n\tShort:     \"download modules to local cache\",\n}\n\nfunc init() {\n\tCmdDownload.Run = gopget.Cmd.Run\n}\n"
  },
  {
    "path": "cmd/internal/mod/init.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage mod\n\nimport (\n\t\"log\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/goplus/mod/modload\"\n\t\"github.com/goplus/mod/xgomod\"\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/env\"\n)\n\n// gop mod init\nvar CmdInit = &base.Command{\n\tUsageLine: \"gop mod init [-llgo -tinygo] module-path\",\n\tShort:     \"initialize new module in current directory\",\n}\n\nvar (\n\tflagInit   = &CmdInit.Flag\n\tflagLLGo   = flagInit.Bool(\"llgo\", false, \"use llgo as the compiler\")\n\tflagTinyGo = flagInit.Bool(\"tinygo\", false, \"use tinygo as the compiler\")\n)\n\nfunc init() {\n\tCmdInit.Run = runInit\n}\n\nfunc runInit(cmd *base.Command, args []string) {\n\terr := flagInit.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\targs = flagInit.Args()\n\tswitch len(args) {\n\tcase 0:\n\t\tfatal(`Example usage:\n\t'gop mod init example.com/m' to initialize a v0 or v1 module\n\t'gop mod init example.com/m/v2' to initialize a v2 module\n\nRun 'gop help mod init' for more information.`)\n\tcase 1:\n\tdefault:\n\t\tfatal(\"gop mod init: too many arguments\")\n\t}\n\n\tmodPath := args[0]\n\tmod, err := modload.Create(\".\", modPath, goMainVer(), env.MainVersion())\n\tcheck(err)\n\n\tif *flagLLGo {\n\t\tmod.AddCompiler(\"llgo\", \"1.0\")\n\t\tmod.AddRequire(\"github.com/goplus/lib\", llgoLibVer(), false)\n\t} else if *flagTinyGo {\n\t\tmod.AddCompiler(\"tinygo\", \"0.32\")\n\t}\n\n\terr = mod.Save()\n\tcheck(err)\n}\n\nfunc goMainVer() string {\n\tver := strings.TrimPrefix(runtime.Version(), \"go\")\n\tif pos := strings.Index(ver, \".\"); pos > 0 {\n\t\tpos++\n\t\tif pos2 := strings.Index(ver[pos:], \".\"); pos2 > 0 {\n\t\t\tver = ver[:pos+pos2]\n\t\t}\n\t}\n\treturn ver\n}\n\nfunc llgoLibVer() string {\n\tif modGop, e1 := xgomod.LoadFrom(filepath.Join(env.XGOROOT(), \"go.mod\"), \"\"); e1 == nil {\n\t\tif pkg, e2 := modGop.Lookup(\"github.com/goplus/lib\"); e2 == nil {\n\t\t\treturn pkg.Real.Version\n\t\t}\n\t}\n\treturn \"v0.2.0\"\n}\n"
  },
  {
    "path": "cmd/internal/mod/mod.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mod\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n)\n\nvar Cmd = &base.Command{\n\tUsageLine: \"gop mod\",\n\tShort:     \"Module maintenance\",\n\n\tCommands: []*base.Command{\n\t\tCmdInit,\n\t\tCmdDownload,\n\t\tCmdTidy,\n\t},\n}\n\nfunc check(err error) {\n\tif err != nil {\n\t\tlog.Panicln(err)\n\t}\n}\n\nfunc fatal(msg any) {\n\tfmt.Fprintln(os.Stderr, msg)\n\tos.Exit(1)\n}\n"
  },
  {
    "path": "cmd/internal/mod/tidy.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage mod\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/xgoenv\"\n)\n\n// gop mod tidy\nvar CmdTidy = &base.Command{\n\tUsageLine: \"gop mod tidy [-e -v]\",\n\tShort:     \"add missing and remove unused modules\",\n}\n\nfunc init() {\n\tCmdTidy.Run = runTidy\n}\n\nfunc runTidy(cmd *base.Command, args []string) {\n\terr := tool.Tidy(\".\", xgoenv.Get())\n\tif err != nil {\n\t\tif tool.NotFound(err) {\n\t\t\tfmt.Fprintln(os.Stderr, \"go.mod not found\")\n\t\t} else {\n\t\t\tfmt.Fprintln(os.Stderr, err)\n\t\t}\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/internal/run/run.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package run implements the “gop run” command.\npackage run\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/gocmd\"\n\t\"github.com/goplus/xgo/x/xgoprojs\"\n\t\"github.com/qiniu/x/log\"\n)\n\n// gop run\nvar Cmd = &base.Command{\n\tUsageLine: \"gop run [-nc -asm -quiet -debug -prof] package [arguments...]\",\n\tShort:     \"Run an XGo program\",\n}\n\nvar (\n\tflag        = &Cmd.Flag\n\tflagAsm     = flag.Bool(\"asm\", false, \"generates `asm` code of XGo bytecode backend\")\n\tflagDebug   = flag.Bool(\"debug\", false, \"print debug information\")\n\tflagQuiet   = flag.Bool(\"quiet\", false, \"don't generate any compiling stage log\")\n\tflagNoChdir = flag.Bool(\"nc\", false, \"don't change dir (only for `gop run pkgPath`)\")\n\tflagProf    = flag.Bool(\"prof\", false, \"do profile and generate profile report\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\tpass := base.PassBuildFlags(cmd)\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\tif flag.NArg() < 1 {\n\t\tcmd.Usage(os.Stderr)\n\t}\n\n\tproj, args, err := xgoprojs.ParseOne(flag.Args()...)\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\tif *flagQuiet {\n\t\tlog.SetOutputLevel(0x7000)\n\t} else if *flagDebug {\n\t\tgogen.SetDebug(gogen.DbgFlagAll &^ gogen.DbgFlagComments)\n\t\tcl.SetDebug(cl.DbgFlagAll)\n\t\tcl.SetDisableRecover(true)\n\t} else if *flagAsm {\n\t\tgogen.SetDebug(gogen.DbgFlagInstruction)\n\t}\n\n\tif *flagProf {\n\t\tpanic(\"TODO: profile not impl\")\n\t}\n\n\tnoChdir := *flagNoChdir\n\tconf, err := tool.NewDefaultConf(\".\", tool.ConfFlagNoTestFiles, pass.Tags())\n\tif err != nil {\n\t\tlog.Panicln(\"tool.NewDefaultConf:\", err)\n\t}\n\tdefer conf.UpdateCache()\n\n\tif !conf.Mod.HasModfile() { // if no go.mod, check GopDeps\n\t\tconf.XGoDeps = new(int)\n\t}\n\tconfCmd := conf.NewGoCmdConf()\n\tconfCmd.Flags = pass.Args\n\trun(proj, args, !noChdir, conf, confCmd)\n}\n\nfunc run(proj xgoprojs.Proj, args []string, chDir bool, conf *tool.Config, run *gocmd.RunConfig) {\n\tconst flags = 0\n\tvar obj string\n\tvar err error\n\tswitch v := proj.(type) {\n\tcase *xgoprojs.DirProj:\n\t\tobj = v.Dir\n\t\terr = tool.RunDir(obj, args, conf, run, flags)\n\tcase *xgoprojs.PkgPathProj:\n\t\tobj = v.Path\n\t\terr = tool.RunPkgPath(v.Path, args, chDir, conf, run, flags)\n\tcase *xgoprojs.FilesProj:\n\t\terr = tool.RunFiles(\"\", v.Files, args, conf, run)\n\tdefault:\n\t\tlog.Panicln(\"`gop run` doesn't support\", reflect.TypeOf(v))\n\t}\n\tif tool.NotFound(err) {\n\t\tfmt.Fprintf(os.Stderr, \"gop run %v: not found\\n\", obj)\n\t} else if err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t} else {\n\t\treturn\n\t}\n\tos.Exit(1)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/serve/serve.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package serve implements the “gop serve command.\npackage serve\n\nimport (\n\t\"context\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/x/jsonrpc2\"\n\t\"github.com/goplus/xgo/x/jsonrpc2/stdio\"\n\t\"github.com/goplus/xgo/x/langserver\"\n\t\"github.com/qiniu/x/log\"\n)\n\n// gop serve\nvar Cmd = &base.Command{\n\tUsageLine: \"gop serve [flags]\",\n\tShort:     \"Serve as an XGo LangServer\",\n}\n\nvar (\n\tflag        = &Cmd.Flag\n\tflagVerbose = flag.Bool(\"v\", false, \"print verbose information\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\n\tif *flagVerbose {\n\t\tjsonrpc2.SetDebug(jsonrpc2.DbgFlagCall)\n\t}\n\n\tlistener := stdio.Listener(false)\n\tdefer listener.Close()\n\n\tserver := langserver.NewServer(context.Background(), listener, nil)\n\tserver.Wait()\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/test/test.go",
    "content": "/*\n * Copyright (c) 2021-2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package test implements the “gop test” command.\npackage test\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"reflect\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/gocmd\"\n\t\"github.com/goplus/xgo/x/xgoprojs\"\n)\n\n// gop test\nvar Cmd = &base.Command{\n\tUsageLine: \"gop test [-debug] [packages]\",\n\tShort:     \"Test XGo packages\",\n}\n\nvar (\n\tflag      = &Cmd.Flag\n\tflagDebug = flag.Bool(\"debug\", false, \"print debug information\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\tpass := PassTestFlags(cmd)\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\n\tpattern := flag.Args()\n\tif len(pattern) == 0 {\n\t\tpattern = []string{\".\"}\n\t}\n\n\tprojs, err := xgoprojs.ParseAll(pattern...)\n\tif err != nil {\n\t\tlog.Panicln(\"xgoprojs.ParseAll:\", err)\n\t}\n\n\tif *flagDebug {\n\t\tgogen.SetDebug(gogen.DbgFlagAll &^ gogen.DbgFlagComments)\n\t\tcl.SetDebug(cl.DbgFlagAll)\n\t\tcl.SetDisableRecover(true)\n\t}\n\n\tconf, err := tool.NewDefaultConf(\".\", 0, pass.Tags())\n\tif err != nil {\n\t\tlog.Panicln(\"tool.NewDefaultConf:\", err)\n\t}\n\tdefer conf.UpdateCache()\n\n\tconfCmd := conf.NewGoCmdConf()\n\tconfCmd.Flags = pass.Args\n\tfor _, proj := range projs {\n\t\ttest(proj, conf, confCmd)\n\t}\n}\n\nfunc test(proj xgoprojs.Proj, conf *tool.Config, test *gocmd.TestConfig) {\n\tconst flags = tool.GenFlagPrompt\n\tvar obj string\n\tvar err error\n\tswitch v := proj.(type) {\n\tcase *xgoprojs.DirProj:\n\t\tobj = v.Dir\n\t\terr = tool.TestDir(obj, conf, test, flags)\n\tcase *xgoprojs.PkgPathProj:\n\t\tobj = v.Path\n\t\terr = tool.TestPkgPath(\"\", v.Path, conf, test, flags)\n\tcase *xgoprojs.FilesProj:\n\t\terr = tool.TestFiles(v.Files, conf, test)\n\tdefault:\n\t\tlog.Panicln(\"`gop test` doesn't support\", reflect.TypeOf(v))\n\t}\n\tif tool.NotFound(err) {\n\t\tfmt.Fprintf(os.Stderr, \"gop test %v: not found\\n\", obj)\n\t} else if err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t} else {\n\t\treturn\n\t}\n\tos.Exit(1)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/test/testflag.go",
    "content": "package test\n\nimport (\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n)\n\ntype boolFlag interface {\n\tIsBoolFlag() bool\n}\n\nfunc PassTestFlags(cmd *base.Command) *base.PassArgs {\n\tp := base.PassBuildFlags(cmd)\n\tp.Bool(\"c\", \"i\", \"cover\", \"json\", \"benchmem\", \"failfast\", \"short\")\n\tp.Var(\"o\", \"covermode\", \"coverpkg\", \"exec\", \"vet\",\n\t\t\"bench\", \"benchtime\", \"blockprofile\", \"blockprofilerate\",\n\t\t\"count\", \"coverprofile\", \"cpu\", \"cpuprofile\",\n\t\t\"fuzz\", \"list\", \"memprofile\", \"memprofilerate\",\n\t\t\"mutexprofile\", \"mutexprofilefraction\", \"outputdir\", \"parallel\",\n\t\t\"run\", \"timeout\", \"fuzztime\", \"fuzzminimizetime\",\n\t\t\"trace\", \"shuffle\")\n\tfor name := range passFlagToTest {\n\t\tif b, ok := cmd.Flag.Lookup(name).Value.(boolFlag); ok && b.IsBoolFlag() {\n\t\t\tp.Bool(\"test.\" + name)\n\t\t} else {\n\t\t\tp.Var(\"test.\" + name)\n\t\t}\n\t}\n\treturn p\n}\n\nvar passFlagToTest = map[string]bool{\n\t\"bench\":                true,\n\t\"benchmem\":             true,\n\t\"benchtime\":            true,\n\t\"blockprofile\":         true,\n\t\"blockprofilerate\":     true,\n\t\"count\":                true,\n\t\"coverprofile\":         true,\n\t\"cpu\":                  true,\n\t\"cpuprofile\":           true,\n\t\"failfast\":             true,\n\t\"fuzz\":                 true,\n\t\"fuzzminimizetime\":     true,\n\t\"fuzztime\":             true,\n\t\"list\":                 true,\n\t\"memprofile\":           true,\n\t\"memprofilerate\":       true,\n\t\"mutexprofile\":         true,\n\t\"mutexprofilefraction\": true,\n\t\"outputdir\":            true,\n\t\"parallel\":             true,\n\t\"run\":                  true,\n\t\"short\":                true,\n\t\"shuffle\":              true,\n\t\"timeout\":              true,\n\t\"trace\":                true,\n\t\"v\":                    true,\n}\n"
  },
  {
    "path": "cmd/internal/version/ver.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage version\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/env\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Cmd - gop version\nvar Cmd = &base.Command{\n\tUsageLine: \"gop version [-v]\",\n\tShort:     \"Print XGo version\",\n}\n\nvar (\n\tflag = &Cmd.Flag\n\t_    = flag.Bool(\"v\", false, \"print verbose information.\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\tfmt.Printf(\"gop %s %s/%s\\n\", env.Version(), runtime.GOOS, runtime.GOARCH)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/internal/watch/watch.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage watch\n\nimport (\n\t\"log\"\n\t\"path/filepath\"\n\n\t\"github.com/goplus/xgo/cmd/internal/base\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/fsnotify\"\n\t\"github.com/goplus/xgo/x/watcher\"\n)\n\n// -----------------------------------------------------------------------------\n\n// gop watch\nvar Cmd = &base.Command{\n\tUsageLine: \"gop watch [-v -gentest] [dir]\",\n\tShort:     \"Monitor code changes in an XGo workspace to generate Go files\",\n}\n\nvar (\n\tflag       = &Cmd.Flag\n\tverbose    = flag.Bool(\"v\", false, \"print verbose information.\")\n\tdebug      = flag.Bool(\"debug\", false, \"show all debug information.\")\n\tgenTestPkg = flag.Bool(\"gentest\", false, \"generate test package.\")\n)\n\nfunc init() {\n\tCmd.Run = runCmd\n}\n\nfunc runCmd(cmd *base.Command, args []string) {\n\terr := flag.Parse(args)\n\tif err != nil {\n\t\tlog.Fatalln(\"parse input arguments failed:\", err)\n\t}\n\n\tif *debug {\n\t\tfsnotify.SetDebug(fsnotify.DbgFlagAll)\n\t}\n\tif *debug || *verbose {\n\t\twatcher.SetDebug(watcher.DbgFlagAll)\n\t}\n\n\targs = flag.Args()\n\tif len(args) == 0 {\n\t\targs = []string{\".\"}\n\t}\n\n\troot, _ := filepath.Abs(args[0])\n\tlog.Println(\"Watch\", root)\n\tw := watcher.New(root)\n\tgo w.Run()\n\tfor {\n\t\tdir := w.Fetch(true)\n\t\tlog.Println(\"GenGo\", dir)\n\t\t_, _, err := tool.GenGo(dir, nil, *genTestPkg)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t}\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "cmd/make.go",
    "content": "//go:build ignore\n// +build ignore\n\n/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc checkPathExist(path string, isDir bool) bool {\n\tstat, err := os.Lstat(path) // Note: os.Lstat() will not follow the symbolic link.\n\tisExists := !os.IsNotExist(err)\n\tif isDir {\n\t\treturn isExists && stat.IsDir()\n\t}\n\treturn isExists && !stat.IsDir()\n}\n\nfunc trimRight(s string) string {\n\treturn strings.TrimRight(s, \" \\t\\r\\n\")\n}\n\n// Path returns single path to check\ntype Path struct {\n\tpath  string\n\tisDir bool\n}\n\nfunc (p *Path) checkExists(rootDir string) bool {\n\tabsPath := filepath.Join(rootDir, p.path)\n\treturn checkPathExist(absPath, p.isDir)\n}\n\nfunc getXgoRoot() string {\n\tpwd, _ := os.Getwd()\n\n\tpathsToCheck := []Path{\n\t\t{path: \"cmd/xgo\", isDir: true},\n\t\t{path: \"builtin\", isDir: true},\n\t\t{path: \"go.mod\", isDir: false},\n\t\t{path: \"go.sum\", isDir: false},\n\t}\n\n\tfor _, path := range pathsToCheck {\n\t\tif !path.checkExists(pwd) {\n\t\t\tprintln(\"Error: This script should be run at the root directory of xgo repository.\")\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\treturn pwd\n}\n\nvar xgoRoot = getXgoRoot()\nvar initCommandExecuteEnv = os.Environ()\nvar commandExecuteEnv = initCommandExecuteEnv\n\n// Always put `gop` command as the first item and `xgo` command as the second item,\n// as it will be referenced by below code.\nvar xgoBinFiles = []string{\"gop\", \"xgo\"}\n\nconst (\n\tinWindows = (runtime.GOOS == \"windows\")\n)\n\nfunc init() {\n\tif inWindows {\n\t\tfor index := range xgoBinFiles {\n\t\t\txgoBinFiles[index] += \".exe\"\n\t\t}\n\t}\n}\n\ntype ExecCmdError struct {\n\tErr    error\n\tStderr string\n}\n\nfunc (p *ExecCmdError) Error() string {\n\tif e := p.Stderr; e != \"\" {\n\t\treturn e\n\t}\n\treturn p.Err.Error()\n}\n\ntype iGitRemote interface {\n\tCheckRemoteUrl()\n\tCreateBranchFrom(branch, remote string) error\n\tPushCommits(remote, branch string) error\n\tDeleteBranch(branch string) error\n}\n\ntype (\n\tgitRemoteImpl struct{}\n\tgitRemoteNone struct{}\n)\n\nfunc (p *gitRemoteImpl) CheckRemoteUrl() {\n\tif getGitRemoteUrl(\"xgo\") == \"\" {\n\t\tlog.Fatalln(\"Error: git remote xgo not found, please use `git remote add xgo git@github.com:goplus/xgo.git`.\")\n\t}\n}\n\nfunc (p *gitRemoteImpl) CreateBranchFrom(branch, remote string) (err error) {\n\t_, err = execCommand(\"git\", \"fetch\", remote)\n\tif err != nil {\n\t\treturn\n\t}\n\texecCommand(\"git\", \"branch\", \"-D\", branch)\n\t_, err = execCommand(\"git\", \"checkout\", \"-b\", branch, remote+\"/\"+branch)\n\treturn\n}\n\nfunc (p *gitRemoteImpl) PushCommits(remote, branch string) error {\n\t_, err := execCommand(\"git\", \"push\", remote, branch)\n\treturn err\n}\n\nfunc (p *gitRemoteImpl) DeleteBranch(branch string) error {\n\t_, err := execCommand(\"git\", \"branch\", \"-D\", branch)\n\treturn err\n}\n\nfunc (p *gitRemoteNone) CheckRemoteUrl()                                    {}\nfunc (p *gitRemoteNone) CreateBranchFrom(branch, remote string) (err error) { return nil }\nfunc (p *gitRemoteNone) PushCommits(remote, branch string) error            { return nil }\nfunc (p *gitRemoteNone) DeleteBranch(branch string) error                   { return nil }\n\nvar (\n\tgitRemote iGitRemote = &gitRemoteImpl{}\n)\n\nfunc execCommand(command string, arg ...string) (string, error) {\n\tvar stdout, stderr bytes.Buffer\n\tcmd := exec.Command(command, arg...)\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\tcmd.Env = commandExecuteEnv\n\terr := cmd.Run()\n\tif err != nil {\n\t\terr = &ExecCmdError{Err: err, Stderr: stderr.String()}\n\t}\n\treturn stdout.String(), err\n}\n\nfunc getTagRev(tag string) string {\n\tconst commit = \"commit \"\n\tstdout, err := execCommand(\"git\", \"show\", tag)\n\tif err != nil || !strings.HasPrefix(stdout, commit) {\n\t\treturn \"\"\n\t}\n\tdata := stdout[len(commit):]\n\tif pos := strings.IndexByte(data, '\\n'); pos > 0 {\n\t\treturn data[:pos]\n\t}\n\treturn \"\"\n}\n\nfunc getGitRemoteUrl(name string) string {\n\tstdout, err := execCommand(\"git\", \"remote\", \"get-url\", name)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn stdout\n}\n\nfunc getGitBranch() string {\n\tstdout, err := execCommand(\"git\", \"rev-parse\", \"--abbrev-ref\", \"HEAD\")\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn trimRight(stdout)\n}\n\nfunc gitTag(tag string) error {\n\t_, err := execCommand(\"git\", \"tag\", tag)\n\treturn err\n}\n\nfunc gitTagAndPushTo(tag string, remote, branch string) error {\n\tif err := gitRemote.PushCommits(remote, branch); err != nil {\n\t\treturn err\n\t}\n\tif err := gitTag(tag); err != nil {\n\t\treturn err\n\t}\n\treturn gitRemote.PushCommits(remote, tag)\n}\n\nfunc gitAdd(file string) error {\n\t_, err := execCommand(\"git\", \"add\", file)\n\treturn err\n}\n\nfunc gitCommit(msg string) error {\n\tout, err := execCommand(\"git\", \"commit\", \"-a\", \"-m\", msg)\n\tif err != nil {\n\t\tif e := err.(*ExecCmdError); e.Stderr == \"\" {\n\t\t\te.Stderr = out\n\t\t}\n\t}\n\treturn err\n}\n\nfunc gitCheckoutBranch(branch string) error {\n\t_, err := execCommand(\"git\", \"checkout\", branch)\n\treturn err\n}\n\nfunc isGitRepo() bool {\n\tgitDir, err := execCommand(\"git\", \"rev-parse\", \"--git-dir\")\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn checkPathExist(filepath.Join(xgoRoot, trimRight(gitDir)), true)\n}\n\nfunc getBuildDateTime() string {\n\tnow := time.Now()\n\treturn now.Format(\"2006-01-02_15-04-05\")\n}\n\nfunc getBuildVer() string {\n\tlatestTagCommit, err := execCommand(\"git\", \"rev-list\", \"--tags\", \"--max-count=1\")\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\n\tstdout, err := execCommand(\"git\", \"describe\", \"--tags\", trimRight(latestTagCommit))\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\n\treturn fmt.Sprintf(\"%s devel\", trimRight(stdout))\n}\n\nfunc getGopBuildFlags() string {\n\tdefaultXGoRoot := xgoRoot\n\tif gopRootFinal := os.Getenv(\"GOPROOT_FINAL\"); gopRootFinal != \"\" {\n\t\tdefaultXGoRoot = gopRootFinal\n\t}\n\tbuildFlags := fmt.Sprintf(\"-X \\\"github.com/goplus/xgo/env.defaultXGoRoot=%s\\\"\", defaultXGoRoot)\n\tbuildFlags += fmt.Sprintf(\" -X \\\"github.com/goplus/xgo/env.buildDate=%s\\\"\", getBuildDateTime())\n\n\tversion := findXgoVersion()\n\tbuildFlags += fmt.Sprintf(\" -X \\\"github.com/goplus/xgo/env.buildVersion=%s\\\"\", version)\n\n\treturn buildFlags\n}\n\nfunc detectGopBinPath() string {\n\treturn filepath.Join(xgoRoot, \"bin\")\n}\n\nfunc detectGoBinPath() string {\n\tgoBin, ok := os.LookupEnv(\"GOBIN\")\n\tif ok {\n\t\treturn goBin\n\t}\n\n\tgoPath, ok := os.LookupEnv(\"GOPATH\")\n\tif ok {\n\t\tlist := filepath.SplitList(goPath)\n\t\tif len(list) > 0 {\n\t\t\t// Put in first directory of $GOPATH.\n\t\t\treturn filepath.Join(list[0], \"bin\")\n\t\t}\n\t}\n\n\thomeDir, _ := os.UserHomeDir()\n\treturn filepath.Join(homeDir, \"go\", \"bin\")\n}\n\nfunc linkGoplusToLocalBin() string {\n\tprintln(\"Start Linking.\")\n\n\tgopBinPath := detectGopBinPath()\n\tgoBinPath := detectGoBinPath()\n\tif !checkPathExist(gopBinPath, true) {\n\t\tlog.Fatalf(\"Error: %s is not existed, you should build XGo before linking.\\n\", gopBinPath)\n\t}\n\tif !checkPathExist(goBinPath, true) {\n\t\tif err := os.MkdirAll(goBinPath, 0755); err != nil {\n\t\t\tfmt.Printf(\"Error: target directory %s is not existed and we can't create one.\\n\", goBinPath)\n\t\t\tlog.Fatalln(err)\n\t\t}\n\t}\n\n\tfor i, file := range xgoBinFiles {\n\t\ttargetLink := filepath.Join(goBinPath, file)\n\t\tif i == 0 { // `gop` is the first file, we will link it to `xgo`.\n\t\t\tfile = xgoBinFiles[1]\n\t\t}\n\t\tsourceFile := filepath.Join(gopBinPath, file)\n\t\tif !checkPathExist(sourceFile, false) {\n\t\t\tlog.Fatalf(\"Error: %s is not existed, you should build XGo before linking.\\n\", sourceFile)\n\t\t}\n\t\tif checkPathExist(targetLink, false) {\n\t\t\t// Delete existed one\n\t\t\tif err := os.Remove(targetLink); err != nil {\n\t\t\t\tlog.Fatalln(err)\n\t\t\t}\n\t\t}\n\t\tif err := os.Symlink(sourceFile, targetLink); err != nil {\n\t\t\tlog.Fatalln(err)\n\t\t}\n\t\tfmt.Printf(\"Link %s to %s successfully.\\n\", sourceFile, targetLink)\n\t}\n\n\tprintln(\"End linking.\")\n\treturn goBinPath\n}\n\nfunc buildGoplusTools(useGoProxy bool) {\n\tcommandsDir := filepath.Join(xgoRoot, \"cmd\")\n\tbuildFlags := getGopBuildFlags()\n\n\tif useGoProxy {\n\t\tprintln(\"Info: we will use goproxy.cn as a Go proxy to accelerate installing process.\")\n\t\tcommandExecuteEnv = append(commandExecuteEnv,\n\t\t\t\"GOPROXY=https://goproxy.cn,direct\",\n\t\t)\n\t}\n\n\t// Install XGo binary files under current ./bin directory.\n\tgopBinPath := detectGopBinPath()\n\tif err := os.Mkdir(gopBinPath, 0755); err != nil && !os.IsExist(err) {\n\t\tprintln(\"Error: XGo can't create ./bin directory to put build assets.\")\n\t\tlog.Fatalln(err)\n\t}\n\n\tprintln(\"Building XGo tools...\\n\")\n\tos.Chdir(commandsDir)\n\n\tbuildOutput, err := execCommand(\"go\", \"build\", \"-o\", gopBinPath, \"-v\", \"-trimpath\", \"-ldflags\", buildFlags, \"./...\")\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\tprint(buildOutput)\n\n\t// Clear xgo run cache\n\tcleanGopRunCache()\n\n\tprintln(\"\\nXGo tools built successfully!\")\n}\n\nfunc showHelpPostInstall(installPath string) {\n\tprintln(\"\\nNEXT STEP:\")\n\tprintln(\"\\nWe just installed XGo into the directory: \", installPath)\n\tmessage := `\nTo setup a better XGo development environment,\nwe recommend you add the above install directory into your PATH environment variable.\n\t`\n\tprintln(message)\n}\n\n// Install XGo tools\nfunc install() {\n\tinstallPath := linkGoplusToLocalBin()\n\n\tprintln(\"\\nXGo tools installed successfully!\")\n\n\tif _, err := execCommand(\"xgo\", \"version\"); err != nil {\n\t\tshowHelpPostInstall(installPath)\n\t}\n}\n\nfunc runTestcases() {\n\tprintln(\"Start running testcases.\")\n\tos.Chdir(xgoRoot)\n\n\tcoverage := \"-coverprofile=coverage.txt\"\n\tgopCommand := filepath.Join(detectGopBinPath(), xgoBinFiles[1])\n\tif !checkPathExist(gopCommand, false) {\n\t\tprintln(\"Error: XGo must be installed before running testcases.\")\n\t\tos.Exit(1)\n\t}\n\n\ttestOutput, err := execCommand(gopCommand, \"test\", coverage, \"-covermode=atomic\", \"./...\")\n\tprintln(testOutput)\n\tif err != nil {\n\t\tprintln(err.Error())\n\t}\n\n\tprintln(\"End running testcases.\")\n}\n\nfunc clean() {\n\tgopBinPath := detectGopBinPath()\n\tgoBinPath := detectGoBinPath()\n\n\t// Clean links\n\tfor _, file := range xgoBinFiles {\n\t\ttargetLink := filepath.Join(goBinPath, file)\n\t\tif checkPathExist(targetLink, false) {\n\t\t\tif err := os.Remove(targetLink); err != nil {\n\t\t\t\tlog.Fatalln(err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Clean build binary files\n\tif checkPathExist(gopBinPath, true) {\n\t\tif err := os.RemoveAll(gopBinPath); err != nil {\n\t\t\tlog.Fatalln(err)\n\t\t}\n\t}\n\n\tcleanGopRunCache()\n}\n\nfunc cleanGopRunCache() {\n\thomeDir, _ := os.UserHomeDir()\n\trunCacheDir := filepath.Join(homeDir, \".xgo\", \"run\")\n\tfiles := []string{\"go.mod\", \"go.sum\"}\n\tfor _, file := range files {\n\t\tfullPath := filepath.Join(runCacheDir, file)\n\t\tif checkPathExist(fullPath, false) {\n\t\t\tif err := os.Remove(fullPath); err != nil {\n\t\t\t\tlog.Fatalln(err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc uninstall() {\n\tprintln(\"Uninstalling XGo and related tools.\")\n\tclean()\n\tprintln(\"XGo and related tools uninstalled successfully.\")\n}\n\nfunc isInChinaWindows() bool {\n\t// Run `systeminfo` command on windows to check locale.\n\tout, err := execCommand(\"systeminfo\")\n\tif err != nil {\n\t\tfmt.Println(\"Run [systeminfo] command failed with error: \", err)\n\t\treturn false\n\t}\n\t// Check if output contains `zh-cn;`\n\treturn strings.Contains(out, \"zh-cn;\")\n}\n\nfunc isInChina() bool {\n\tif inWindows {\n\t\treturn isInChinaWindows()\n\t}\n\tconst prefix = \"LANG=\\\"\"\n\tout, err := execCommand(\"locale\")\n\tif err != nil {\n\t\treturn false\n\t}\n\tif strings.HasPrefix(out, prefix) {\n\t\tout = out[len(prefix):]\n\t\treturn strings.HasPrefix(out, \"zh_CN\")\n\t}\n\treturn false\n}\n\n// findXgoVersion returns current version of xgo\nfunc findXgoVersion() string {\n\tversionFile := filepath.Join(xgoRoot, \"VERSION\")\n\t// Read version from VERSION file\n\tdata, err := os.ReadFile(versionFile)\n\tif err == nil {\n\t\tversion := trimRight(string(data))\n\t\treturn version\n\t}\n\n\t// Read version from git repo\n\tif !isGitRepo() {\n\t\tlog.Fatal(\"Error: must be a git repo or a VERSION file existed.\")\n\t}\n\tversion := getBuildVer() // Closet tag on git log\n\treturn version\n}\n\n// releaseNewVersion tags the repo with provided new tag, and writes new tag into VERSION file.\nfunc releaseNewVersion(tag string) {\n\tif !isGitRepo() {\n\t\tlog.Fatalln(\"Error: Releasing a new version could only be operated under a git repo.\")\n\t}\n\tgitRemote.CheckRemoteUrl()\n\tif getTagRev(tag) != \"\" {\n\t\tlog.Fatalln(\"Error: tag already exists -\", tag)\n\t}\n\n\tversion := tag\n\tre := regexp.MustCompile(`^v\\d+?\\.\\d+?`)\n\treleaseBranch := re.FindString(version)\n\tif releaseBranch == \"\" {\n\t\tlog.Fatal(\"Error: A valid version should be has form: vX.Y.Z\")\n\t}\n\n\tsourceBranch := getGitBranch()\n\n\t// Checkout to release breanch\n\tif sourceBranch != releaseBranch {\n\t\tif err := gitCheckoutBranch(releaseBranch); err != nil {\n\t\t\tlog.Fatalf(\"Error: checkout to release branch: %s failed with error: %v.\", releaseBranch, err)\n\t\t}\n\t\tdefer func() {\n\t\t\t// Checkout back to source branch\n\t\t\tif err := gitCheckoutBranch(sourceBranch); err != nil {\n\t\t\t\tlog.Fatalf(\"Error: checkout to source branch: %s failed with error: %v.\", sourceBranch, err)\n\t\t\t}\n\t\t\tgitRemote.DeleteBranch(releaseBranch)\n\t\t}()\n\t}\n\n\t// Cache new version\n\tversionFile := filepath.Join(xgoRoot, \"VERSION\")\n\tif err := os.WriteFile(versionFile, []byte(version), 0644); err != nil {\n\t\tlog.Fatalf(\"Error: cache new version with error: %v\\n\", err)\n\t}\n\n\t// Commit changes\n\tgitAdd(versionFile)\n\tif err := gitCommit(\"release version \" + version); err != nil {\n\t\tlog.Fatalf(\"Error: git commit with error: %v\\n\", err)\n\t}\n\n\t// Tag the source code\n\tif err := gitTagAndPushTo(tag, \"xgo\", releaseBranch); err != nil {\n\t\tlog.Fatalf(\"Error: gitTagAndPushTo with error: %v\\n\", err)\n\t}\n\n\tprintln(\"Released new version:\", version)\n}\n\nfunc runRegtests() {\n\tprintln(\"\\nStart running regtests.\")\n\n\tcmd := exec.Command(filepath.Join(xgoRoot, \"bin/\"+xgoBinFiles[1]), \"go\", \"./...\")\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\tcmd.Dir = filepath.Join(xgoRoot, \"demo\")\n\terr := cmd.Run()\n\tif err != nil {\n\t\tcode := cmd.ProcessState.ExitCode()\n\t\tif code == 0 {\n\t\t\tcode = 1\n\t\t}\n\t\tos.Exit(code)\n\t}\n}\n\nfunc main() {\n\tisInstall := flag.Bool(\"install\", false, \"Install XGo\")\n\tisBuild := flag.Bool(\"build\", false, \"Build XGo tools\")\n\tisTest := flag.Bool(\"test\", false, \"Run testcases\")\n\tisRegtest := flag.Bool(\"regtest\", false, \"Run regtests\")\n\tisUninstall := flag.Bool(\"uninstall\", false, \"Uninstall XGo\")\n\tisGoProxy := flag.Bool(\"proxy\", false, \"Set GOPROXY for people in China\")\n\tisAutoProxy := flag.Bool(\"autoproxy\", false, \"Check to set GOPROXY automatically\")\n\tnoPush := flag.Bool(\"nopush\", false, \"Don't push to remote repo\")\n\ttag := flag.String(\"tag\", \"\", \"Release an new version with specified tag\")\n\n\tflag.Parse()\n\n\tuseGoProxy := *isGoProxy\n\tif !useGoProxy && *isAutoProxy {\n\t\tuseGoProxy = isInChina()\n\t}\n\tflagActionMap := map[*bool]func(){\n\t\tisBuild: func() { buildGoplusTools(useGoProxy) },\n\t\tisInstall: func() {\n\t\t\tbuildGoplusTools(useGoProxy)\n\t\t\tinstall()\n\t\t},\n\t\tisUninstall: uninstall,\n\t\tisTest:      runTestcases,\n\t\tisRegtest:   runRegtests,\n\t}\n\n\t// Sort flags, for example: install flag should be checked earlier than test flag.\n\tflags := []*bool{isBuild, isInstall, isTest, isRegtest, isUninstall}\n\thasActionDone := false\n\n\tif *tag != \"\" {\n\t\tif *noPush {\n\t\t\tgitRemote = &gitRemoteNone{}\n\t\t}\n\t\treleaseNewVersion(*tag)\n\t\thasActionDone = true\n\t}\n\n\tfor _, flag := range flags {\n\t\tif *flag {\n\t\t\tflagActionMap[flag]()\n\t\t\thasActionDone = true\n\t\t}\n\t}\n\n\tif !hasActionDone {\n\t\tprintln(\"Usage:\\n\")\n\t\tflag.PrintDefaults()\n\t}\n}\n"
  },
  {
    "path": "cmd/make_test.go",
    "content": "//go:build ignore\n// +build ignore\n\npackage make_test\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n)\n\nconst (\n\tinWindows = (runtime.GOOS == \"windows\")\n)\n\nvar script = \"all.bash\"\nvar gopRoot = \"\"\nvar xgoBinFiles = []string{\"gop\", \"xgo\"}\nvar installer = \"cmd/make.go\"\nvar versionFile = \"VERSION\"\nvar mainVersionFile = \"env/version.go\"\n\nfunc checkPathExist(path string, isDir bool) bool {\n\tstat, err := os.Stat(path)\n\tisExists := !os.IsNotExist(err)\n\tif isDir {\n\t\treturn isExists && stat.IsDir()\n\t}\n\treturn isExists && !stat.IsDir()\n}\n\nfunc trimRight(s string) string {\n\treturn strings.TrimRight(s, \" \\t\\r\\n\")\n}\n\nfunc execCommand(command string, arg ...string) (string, error) {\n\tvar stdout, stderr bytes.Buffer\n\tcmd := exec.Command(command, arg...)\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\terr := cmd.Run()\n\tif err != nil {\n\t\tif stderr.Len() > 0 {\n\t\t\terr = errors.New(string(stderr.String()))\n\t\t}\n\t}\n\treturn stdout.String(), err\n}\n\nfunc getBranch() string {\n\tstdout, err := execCommand(\"git\", \"rev-parse\", \"--abbrev-ref\", \"HEAD\")\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn trimRight(stdout)\n}\n\nfunc detectGoBinPath() string {\n\tgoBin, ok := os.LookupEnv(\"GOBIN\")\n\tif ok {\n\t\treturn goBin\n\t}\n\tgoPath, ok := os.LookupEnv(\"GOPATH\")\n\tif ok {\n\t\tlist := filepath.SplitList(goPath)\n\t\tif len(list) > 0 {\n\t\t\t// Put in first directory of $GOPATH.\n\t\t\treturn filepath.Join(list[0], \"bin\")\n\t\t}\n\t}\n\thomeDir, _ := os.UserHomeDir()\n\treturn filepath.Join(homeDir, \"go\", \"bin\")\n}\n\nfunc init() {\n\tpwd, _ := os.Getwd()\n\tgopRoot = filepath.Join(pwd, \"..\")\n\tinstaller = filepath.Join(gopRoot, installer)\n\tversionFile = filepath.Join(gopRoot, versionFile)\n\tmainVersionFile = filepath.Join(gopRoot, mainVersionFile)\n\n\tif inWindows {\n\t\tscript = \"all.bat\"\n\t\tfor index := range xgoBinFiles {\n\t\t\txgoBinFiles[index] += \".exe\"\n\t\t}\n\t}\n}\n\nfunc cleanGopRunCacheFiles(t *testing.T) {\n\thomeDir, _ := os.UserHomeDir()\n\trunCacheDir := filepath.Join(homeDir, \".xgo\", \"run\")\n\tfiles := []string{\"go.mod\", \"go.sum\"}\n\tfor _, file := range files {\n\t\tfullPath := filepath.Join(runCacheDir, file)\n\t\tif checkPathExist(fullPath, false) {\n\t\t\tt.Fatalf(\"Failed: %s found in %s directory\\n\", file, runCacheDir)\n\t\t}\n\t}\n}\n\nfunc TestAllScript(t *testing.T) {\n\tos.Chdir(gopRoot)\n\tcmd := exec.Command(filepath.Join(gopRoot, script))\n\tif output, err := cmd.CombinedOutput(); err != nil {\n\t\tt.Fatalf(\"Failed: %v:\\nOut: %s\\n\", err, output)\n\t}\n\n\tgoBinPath := detectGoBinPath()\n\n\tfor i, file := range xgoBinFiles {\n\t\tif i == 0 { // skip gop\n\t\t\tcontinue\n\t\t}\n\t\tif !checkPathExist(filepath.Join(gopRoot, \"bin\", file), false) {\n\t\t\tt.Fatalf(\"Failed: %s not found in ./bin directory\\n\", file)\n\t\t}\n\t\tif !checkPathExist(filepath.Join(goBinPath, file), false) {\n\t\t\tt.Fatalf(\"Failed: %s not found in %s/bin directory\\n\", file, goBinPath)\n\t\t}\n\t}\n\n\tcleanGopRunCacheFiles(t)\n\n\tcmd = exec.Command(filepath.Join(gopRoot, \"bin\", xgoBinFiles[1]), \"version\")\n\tif output, err := cmd.CombinedOutput(); err != nil {\n\t\tt.Fatalf(\"Failed: %v:\\nOut: %s\\n\", err, output)\n\t}\n}\n\nfunc TestTagFlagInGitRepo(t *testing.T) {\n\tos.Chdir(gopRoot)\n\n\t// Setup\n\ttag := \"v1.0.90\"\n\ttag2 := \"v1.0.91\"\n\tbigtag := \"v1.999.12\"\n\treleaseBranch := \"v1.0\"\n\tnonExistBranch := \"non-exist-branch\"\n\tsourceBranch := getBranch()\n\n\t// Teardown\n\tt.Cleanup(func() {\n\t\tgitCmd := exec.Command(\"git\", \"tag\", \"-d\", tag)\n\t\tgitCmd.CombinedOutput()\n\t\tgitCmd = exec.Command(\"git\", \"tag\", \"-d\", tag2)\n\t\tgitCmd.CombinedOutput()\n\t\texecCommand(\"git\", \"checkout\", sourceBranch)\n\t\texecCommand(\"git\", \"branch\", \"-D\", nonExistBranch)\n\t\texecCommand(\"git\", \"branch\", \"-D\", releaseBranch)\n\t})\n\n\tt.Run(\"release new version with bad tag\", func(t *testing.T) {\n\t\tcmd := exec.Command(\"go\", \"run\", installer, \"--nopush\", \"--tag\", \"xyz\")\n\t\tif _, err := cmd.CombinedOutput(); err == nil {\n\t\t\tt.Fatal(\"Failed: release a bad tag should be failed.\")\n\t\t}\n\t})\n\n\tt.Run(\"empty tag should failed\", func(t *testing.T) {\n\t\tcmd := exec.Command(\"go\", \"run\", installer, \"--nopush\", \"--tag\", \"\")\n\t\tif out, err := cmd.CombinedOutput(); err != nil || !strings.Contains(string(out), \"Usage\") {\n\t\t\tt.Fatalf(\"Failed: %v, out: %s\\n\", err, out)\n\t\t}\n\t})\n\n\tt.Run(\"failed when release branch corresponding to tag does not exists\", func(t *testing.T) {\n\t\tcmd := exec.Command(\"go\", \"run\", installer, \"--nopush\", \"--tag\", bigtag)\n\t\tif _, err := cmd.CombinedOutput(); err == nil {\n\t\t\tt.Fatal(\"Failed: a corresponding release branch does not exists should be failed.\")\n\t\t}\n\t})\n\n\tt.Run(\"release new version on release branch\", func(t *testing.T) {\n\t\texecCommand(\"git\", \"checkout\", sourceBranch)\n\t\texecCommand(\"git\", \"branch\", \"-D\", releaseBranch)\n\t\t_, err := execCommand(\"git\", \"checkout\", \"-b\", releaseBranch)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tcmd := exec.Command(\"go\", \"run\", installer, \"--nopush\", \"--tag\", tag)\n\t\tif out, err := cmd.CombinedOutput(); err != nil {\n\t\t\tt.Log(string(out))\n\t\t\tt.Fatalf(\"Failed: release tag: %s on branch: %s should not be failed\", tag, releaseBranch)\n\t\t}\n\n\t\tif !checkPathExist(versionFile, false) {\n\t\t\tt.Fatal(\"Failed: a VERSION file not found.\")\n\t\t}\n\n\t\tif data, _ := os.ReadFile(versionFile); string(data) != tag {\n\t\t\tt.Fatalf(\"Failed: content of VERSION file: '%s' not match tag: %s\", data, tag)\n\t\t}\n\n\t\t// Make sure tag exists.\n\t\tgitCmd := exec.Command(\"git\", \"tag\")\n\t\tif allTags, _ := gitCmd.CombinedOutput(); !strings.Contains(string(allTags), tag) {\n\t\t\tt.Fatalf(\"Failed: %s tag not found in tag list of this git repo.\\n\", tag)\n\t\t}\n\t})\n\n\tt.Run(\"release new version on non-release branch\", func(t *testing.T) {\n\t\t_, err := execCommand(\"git\", \"checkout\", \"-b\", nonExistBranch)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\texecCommand(\"git\", \"branch\", \"-D\", releaseBranch)\n\t\t_, err = execCommand(\"git\", \"checkout\", \"-b\", releaseBranch)\n\t\tif err != nil {\n\t\t\tt.Log(\"current branch:\", getBranch())\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\texecCommand(\"git\", \"checkout\", nonExistBranch)\n\n\t\tcmd := exec.Command(\"go\", \"run\", installer, \"--nopush\", \"--tag\", tag2)\n\t\tif out, err := cmd.CombinedOutput(); err != nil {\n\t\t\tt.Log(string(out))\n\t\t\tt.Fatalf(\"Failed: release tag: %s on branch: %s should not be failed\", tag2, nonExistBranch)\n\t\t}\n\n\t\tif getBranch() != nonExistBranch {\n\t\t\tt.Fatal(\"Failed: getBranch() != nonExistBranch\")\n\t\t}\n\t\texecCommand(\"git\", \"checkout\", releaseBranch)\n\n\t\tif !checkPathExist(versionFile, false) {\n\t\t\tt.Fatal(\"Failed: a VERSION file not found.\")\n\t\t}\n\n\t\tif data, _ := os.ReadFile(versionFile); string(data) != tag2 {\n\t\t\tt.Fatalf(\"Failed: content of VERSION file: '%s' not match tag: %s\", data, tag2)\n\t\t}\n\n\t\tgitCmd := exec.Command(\"git\", \"tag\")\n\t\tif allTags, _ := gitCmd.CombinedOutput(); !strings.Contains(string(allTags), tag2) {\n\t\t\tt.Fatalf(\"Failed: %s tag not found in tag list of this git repo.\\n\", tag)\n\t\t}\n\t})\n}\n\nfunc TestTagFlagInNonGitRepo(t *testing.T) {\n\tos.Chdir(gopRoot)\n\n\t// Setup\n\tgitDir := filepath.Join(gopRoot, \".git\")\n\tgitBackupDir := filepath.Join(gopRoot, \".gitBackup\")\n\t// Rename .git dir\n\tif checkPathExist(gitDir, true) {\n\t\tos.Rename(gitDir, gitBackupDir)\n\t}\n\n\t// Teardown\n\tt.Cleanup(func() {\n\t\tif checkPathExist(gitBackupDir, true) {\n\t\t\tos.Rename(gitBackupDir, gitDir)\n\t\t}\n\t})\n\n\tt.Run(\"specify new tag\", func(t *testing.T) {\n\t\tcmd := exec.Command(\"go\", \"run\", installer, \"--tag\", \"v1.0.98\")\n\t\toutput, err := cmd.CombinedOutput()\n\t\tif err == nil || !strings.Contains(string(output), \"Error\") {\n\t\t\tt.Fatal(\"Failed: release on a non-git repo should be failed.\")\n\t\t}\n\t})\n}\n\nfunc TestInstallInNonGitRepo(t *testing.T) {\n\tos.Chdir(gopRoot)\n\n\t// Setup\n\tgitDir := filepath.Join(gopRoot, \".git\")\n\tgitBackupDir := filepath.Join(gopRoot, \".gitBackup\")\n\t// Rename .git dir\n\tif checkPathExist(gitDir, true) {\n\t\tos.Rename(gitDir, gitBackupDir)\n\t}\n\n\t// Teardown\n\tt.Cleanup(func() {\n\t\tif checkPathExist(gitBackupDir, true) {\n\t\t\tos.Rename(gitBackupDir, gitDir)\n\t\t}\n\t\tif checkPathExist(versionFile, false) {\n\t\t\tos.Remove(versionFile)\n\t\t}\n\t})\n\n\tinstallCmd := func() *exec.Cmd {\n\t\treturn exec.Command(\"go\", \"run\", installer, \"--install\")\n\t}\n\n\tt.Run(\"failed build operation\", func(t *testing.T) {\n\t\tos.Remove(versionFile)\n\t\tcmd := installCmd()\n\t\toutput, err := cmd.CombinedOutput()\n\t\tif err == nil || !strings.Contains(string(output), \"Error\") {\n\t\t\tt.Log(string(output))\n\t\t\tt.Fatal(\"Failed: build XGo in a non-git repo and no VERSION file should be failed.\")\n\t\t}\n\t})\n\n\tt.Run(\"install with VERSION file\", func(t *testing.T) {\n\t\tversion := \"v1.5.65535\" // Use a test version\n\t\t// Create VERSION file\n\t\tif err := os.WriteFile(versionFile, []byte(version), 0644); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tcmd := installCmd()\n\t\tif output, err := cmd.CombinedOutput(); err != nil {\n\t\t\tt.Fatalf(\"Failed: %v, output: %s\\n\", err, output)\n\t\t}\n\n\t\tcmd = exec.Command(filepath.Join(gopRoot, \"bin\", xgoBinFiles[1]), \"version\")\n\t\toutput, err := cmd.CombinedOutput()\n\t\tif err != nil || !strings.Contains(string(output), version) {\n\t\t\tt.Fatalf(\"Failed: %v, output: %s\\n\", err, output)\n\t\t}\n\t})\n}\n\nfunc TestHandleMultiFlags(t *testing.T) {\n\tos.Chdir(gopRoot)\n\n\tcmd := exec.Command(\"go\", \"run\", installer, \"--install\", \"--test\", \"--uninstall\")\n\tif output, err := cmd.CombinedOutput(); err != nil {\n\t\tt.Fatalf(\"Failed: %v:\\nOut: %s\\n\", err, output)\n\t}\n\n\tgoBinPath := detectGoBinPath()\n\n\t// Uninstall will be the last action to run, test if all build assets being cleared.\n\tfor _, file := range xgoBinFiles {\n\t\tif checkPathExist(filepath.Join(gopRoot, \"bin\", file), false) {\n\t\t\tt.Fatalf(\"Failed: %s found in ./bin directory\\n\", file)\n\t\t}\n\t\tif checkPathExist(filepath.Join(goBinPath, file), false) {\n\t\t\tt.Fatalf(\"Failed: %s found in %s/bin directory\\n\", file, goBinPath)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/xgo/bug_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/bug\"\n)\n\nuse \"bug\"\n\nshort \"Start a bug report\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/build_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/build\"\n)\n\nuse \"build [flags] [packages]\"\n\nshort \"Build XGo files\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/clean_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/clean\"\n)\n\nuse \"clean [flags] <gopSrcDir>\"\n\nshort \"Clean all XGo auto generated files\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/doc_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/doc\"\n)\n\nuse \"doc [flags] [pkgPath]\"\n\nshort \"Show documentation for package or symbol\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/env_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/env\"\n)\n\nuse \"env [flags] [var ...]\"\n\nshort \"Prints XGo environment information\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/fmt_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/gopfmt\"\n)\n\nuse \"fmt [flags] path ...\"\n\nshort \"Format XGo packages\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/get_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/gopget\"\n)\n\nuse \"get [flags] [packages]\"\n\nshort \"Add dependencies to current module and install them\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/go_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/gengo\"\n)\n\nuse \"go [flags] [packages|files]\"\n\nshort \"Convert XGo code into Go code\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/install_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/install\"\n)\n\nuse \"install [flags] [packages]\"\n\nshort \"Build XGo files and install target to GOBIN\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/main_app.gox",
    "content": "import (\n\t\"github.com/goplus/gogen\"\n\t\"github.com/qiniu/x/log\"\n)\n\nshort \"xgo is a tool for managing XGo source code.\"\nlog.setFlags log.Ldefault&^log.LstdFlags\n\ngogen.GeneratedHeader = \"// Code generated by xgo (XGo); DO NOT EDIT.\\n\\n\"\n"
  },
  {
    "path": "cmd/xgo/mod_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/mod\"\n)\n\nuse \"mod\"\n\nshort \"Module maintenance\"\n\nrun => {\n\thelp\n}\n"
  },
  {
    "path": "cmd/xgo/mod_download_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/mod\"\n)\n\nuse \"download [flags] [modules]\"\n\nshort \"download modules to local cache\"\n\nflagOff\n\nrun args => {\n\tif args.len < 1 {\n\t\thelp\n\t\treturn\n\t}\n\tself.CmdDownload.Run self.CmdDownload, args\n}\n"
  },
  {
    "path": "cmd/xgo/mod_init_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/mod\"\n)\n\nuse \"init [flags] module-path\"\n\nshort \"initialize new module in current directory\"\n\nflagOff\n\nrun args => {\n\tif args.len < 1 {\n\t\thelp\n\t\treturn\n\t}\n\tself.CmdInit.Run self.CmdInit, args\n}\n"
  },
  {
    "path": "cmd/xgo/mod_tidy_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/mod\"\n)\n\nuse \"tidy [flags]\"\n\nshort \"add missing and remove unused modules\"\n\nflagOff\n\nrun args => {\n\tself.CmdTidy.Run self.CmdTidy, args\n}\n"
  },
  {
    "path": "cmd/xgo/run_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/run\"\n)\n\nuse \"run [flags] package [arguments...]\"\n\nshort \"Compile and run an XGo program\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/serve_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/serve\"\n)\n\nuse \"serve [flags]\"\n\nshort \"Serve as an XGo LangServer\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/test_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/test\"\n)\n\nuse \"test [flags] [packages]\"\n\nshort \"Test XGo packages\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/version_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\t\"runtime\"\n\tself \"xgo/env\"\n)\n\nuse \"version [flags]\"\n\nshort \"Print XGo version\"\n\nrun => {\n\techo \"xgo ${self.version} ${runtime.GOOS}/${runtime.GOARCH}\"\n}\n"
  },
  {
    "path": "cmd/xgo/watch_cmd.gox",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport (\n\tself \"github.com/goplus/xgo/cmd/internal/watch\"\n)\n\nuse \"watch [flags] [dir]\"\n\nshort \"Monitor code changes in an XGo workspace to generate Go files\"\n\nflagOff\n\nrun args => {\n\tself.Cmd.Run self.Cmd, args\n}\n"
  },
  {
    "path": "cmd/xgo/xgo_autogen.go",
    "content": "// Code generated by xgo (XGo); DO NOT EDIT.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/cobra/xcmd\"\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/cmd/internal/bug\"\n\t\"github.com/goplus/xgo/cmd/internal/build\"\n\t\"github.com/goplus/xgo/cmd/internal/clean\"\n\t\"github.com/goplus/xgo/cmd/internal/doc\"\n\t\"github.com/goplus/xgo/cmd/internal/env\"\n\t\"github.com/goplus/xgo/cmd/internal/gengo\"\n\t\"github.com/goplus/xgo/cmd/internal/gopfmt\"\n\t\"github.com/goplus/xgo/cmd/internal/gopget\"\n\t\"github.com/goplus/xgo/cmd/internal/install\"\n\t\"github.com/goplus/xgo/cmd/internal/mod\"\n\t\"github.com/goplus/xgo/cmd/internal/run\"\n\t\"github.com/goplus/xgo/cmd/internal/serve\"\n\t\"github.com/goplus/xgo/cmd/internal/test\"\n\t\"github.com/goplus/xgo/cmd/internal/watch\"\n\tenv1 \"github.com/goplus/xgo/env\"\n\t\"github.com/qiniu/x/log\"\n\t\"github.com/qiniu/x/stringutil\"\n\t\"runtime\"\n)\n\nconst _ = true\n\ntype Cmd_bug struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_build struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_clean struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_doc struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_env struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_fmt struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_get struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_go struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_install struct {\n\txcmd.Command\n\t*App\n}\ntype App struct {\n\txcmd.App\n}\ntype Cmd_mod struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_mod_download struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_mod_init struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_mod_tidy struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_run struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_serve struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_test struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_version struct {\n\txcmd.Command\n\t*App\n}\ntype Cmd_watch struct {\n\txcmd.Command\n\t*App\n}\n//line cmd/xgo/main_app.gox:6\nfunc (this *App) MainEntry() {\n//line cmd/xgo/main_app.gox:6:1\n\tthis.Short(\"xgo is a tool for managing XGo source code.\")\n//line cmd/xgo/main_app.gox:7:1\n\tlog.SetFlags(log.Ldefault &^ log.LstdFlags)\n//line cmd/xgo/main_app.gox:9:1\n\tgogen.GeneratedHeader = \"// Code generated by xgo (XGo); DO NOT EDIT.\\n\\n\"\n}\nfunc (this *App) Main() {\n\t_xgo_obj0 := &Cmd_bug{App: this}\n\t_xgo_obj1 := &Cmd_build{App: this}\n\t_xgo_obj2 := &Cmd_clean{App: this}\n\t_xgo_obj3 := &Cmd_doc{App: this}\n\t_xgo_obj4 := &Cmd_env{App: this}\n\t_xgo_obj5 := &Cmd_fmt{App: this}\n\t_xgo_obj6 := &Cmd_get{App: this}\n\t_xgo_obj7 := &Cmd_go{App: this}\n\t_xgo_obj8 := &Cmd_install{App: this}\n\t_xgo_obj9 := &Cmd_mod{App: this}\n\t_xgo_obj10 := &Cmd_mod_download{App: this}\n\t_xgo_obj11 := &Cmd_mod_init{App: this}\n\t_xgo_obj12 := &Cmd_mod_tidy{App: this}\n\t_xgo_obj13 := &Cmd_run{App: this}\n\t_xgo_obj14 := &Cmd_serve{App: this}\n\t_xgo_obj15 := &Cmd_test{App: this}\n\t_xgo_obj16 := &Cmd_version{App: this}\n\t_xgo_obj17 := &Cmd_watch{App: this}\n\txcmd.XGot_App_Main(this, _xgo_obj0, _xgo_obj1, _xgo_obj2, _xgo_obj3, _xgo_obj4, _xgo_obj5, _xgo_obj6, _xgo_obj7, _xgo_obj8, _xgo_obj9, _xgo_obj10, _xgo_obj11, _xgo_obj12, _xgo_obj13, _xgo_obj14, _xgo_obj15, _xgo_obj16, _xgo_obj17)\n}\n//line cmd/xgo/bug_cmd.gox:20\nfunc (this *Cmd_bug) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/bug_cmd.gox:20:1\n\tthis.Use(\"bug\")\n//line cmd/xgo/bug_cmd.gox:22:1\n\tthis.Short(\"Start a bug report\")\n//line cmd/xgo/bug_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/bug_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/bug_cmd.gox:27:1\n\t\tbug.Cmd.Run(bug.Cmd, args)\n\t})\n}\nfunc (this *Cmd_bug) Classfname() string {\n\treturn \"bug\"\n}\n//line cmd/xgo/build_cmd.gox:20\nfunc (this *Cmd_build) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/build_cmd.gox:20:1\n\tthis.Use(\"build [flags] [packages]\")\n//line cmd/xgo/build_cmd.gox:22:1\n\tthis.Short(\"Build XGo files\")\n//line cmd/xgo/build_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/build_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/build_cmd.gox:27:1\n\t\tbuild.Cmd.Run(build.Cmd, args)\n\t})\n}\nfunc (this *Cmd_build) Classfname() string {\n\treturn \"build\"\n}\n//line cmd/xgo/clean_cmd.gox:20\nfunc (this *Cmd_clean) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/clean_cmd.gox:20:1\n\tthis.Use(\"clean [flags] <gopSrcDir>\")\n//line cmd/xgo/clean_cmd.gox:22:1\n\tthis.Short(\"Clean all XGo auto generated files\")\n//line cmd/xgo/clean_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/clean_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/clean_cmd.gox:27:1\n\t\tclean.Cmd.Run(clean.Cmd, args)\n\t})\n}\nfunc (this *Cmd_clean) Classfname() string {\n\treturn \"clean\"\n}\n//line cmd/xgo/doc_cmd.gox:20\nfunc (this *Cmd_doc) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/doc_cmd.gox:20:1\n\tthis.Use(\"doc [flags] [pkgPath]\")\n//line cmd/xgo/doc_cmd.gox:22:1\n\tthis.Short(\"Show documentation for package or symbol\")\n//line cmd/xgo/doc_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/doc_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/doc_cmd.gox:27:1\n\t\tdoc.Cmd.Run(doc.Cmd, args)\n\t})\n}\nfunc (this *Cmd_doc) Classfname() string {\n\treturn \"doc\"\n}\n//line cmd/xgo/env_cmd.gox:20\nfunc (this *Cmd_env) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/env_cmd.gox:20:1\n\tthis.Use(\"env [flags] [var ...]\")\n//line cmd/xgo/env_cmd.gox:22:1\n\tthis.Short(\"Prints XGo environment information\")\n//line cmd/xgo/env_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/env_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/env_cmd.gox:27:1\n\t\tenv.Cmd.Run(env.Cmd, args)\n\t})\n}\nfunc (this *Cmd_env) Classfname() string {\n\treturn \"env\"\n}\n//line cmd/xgo/fmt_cmd.gox:20\nfunc (this *Cmd_fmt) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/fmt_cmd.gox:20:1\n\tthis.Use(\"fmt [flags] path ...\")\n//line cmd/xgo/fmt_cmd.gox:22:1\n\tthis.Short(\"Format XGo packages\")\n//line cmd/xgo/fmt_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/fmt_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/fmt_cmd.gox:27:1\n\t\tgopfmt.Cmd.Run(gopfmt.Cmd, args)\n\t})\n}\nfunc (this *Cmd_fmt) Classfname() string {\n\treturn \"fmt\"\n}\n//line cmd/xgo/get_cmd.gox:20\nfunc (this *Cmd_get) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/get_cmd.gox:20:1\n\tthis.Use(\"get [flags] [packages]\")\n//line cmd/xgo/get_cmd.gox:22:1\n\tthis.Short(\"Add dependencies to current module and install them\")\n//line cmd/xgo/get_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/get_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/get_cmd.gox:27:1\n\t\tgopget.Cmd.Run(gopget.Cmd, args)\n\t})\n}\nfunc (this *Cmd_get) Classfname() string {\n\treturn \"get\"\n}\n//line cmd/xgo/go_cmd.gox:20\nfunc (this *Cmd_go) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/go_cmd.gox:20:1\n\tthis.Use(\"go [flags] [packages|files]\")\n//line cmd/xgo/go_cmd.gox:22:1\n\tthis.Short(\"Convert XGo code into Go code\")\n//line cmd/xgo/go_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/go_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/go_cmd.gox:27:1\n\t\tgengo.Cmd.Run(gengo.Cmd, args)\n\t})\n}\nfunc (this *Cmd_go) Classfname() string {\n\treturn \"go\"\n}\n//line cmd/xgo/install_cmd.gox:20\nfunc (this *Cmd_install) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/install_cmd.gox:20:1\n\tthis.Use(\"install [flags] [packages]\")\n//line cmd/xgo/install_cmd.gox:22:1\n\tthis.Short(\"Build XGo files and install target to GOBIN\")\n//line cmd/xgo/install_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/install_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/install_cmd.gox:27:1\n\t\tinstall.Cmd.Run(install.Cmd, args)\n\t})\n}\nfunc (this *Cmd_install) Classfname() string {\n\treturn \"install\"\n}\n//line cmd/xgo/mod_cmd.gox:20\nfunc (this *Cmd_mod) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/mod_cmd.gox:20:1\n\tthis.Use(\"mod\")\n//line cmd/xgo/mod_cmd.gox:22:1\n\tthis.Short(\"Module maintenance\")\n//line cmd/xgo/mod_cmd.gox:24:1\n\tthis.Run__0(func() {\n//line cmd/xgo/mod_cmd.gox:25:1\n\t\tthis.Help()\n\t})\n}\nfunc (this *Cmd_mod) Classfname() string {\n\treturn \"mod\"\n}\n//line cmd/xgo/mod_download_cmd.gox:20\nfunc (this *Cmd_mod_download) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/mod_download_cmd.gox:20:1\n\tthis.Use(\"download [flags] [modules]\")\n//line cmd/xgo/mod_download_cmd.gox:22:1\n\tthis.Short(\"download modules to local cache\")\n//line cmd/xgo/mod_download_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/mod_download_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/mod_download_cmd.gox:27:1\n\t\tif len(args) < 1 {\n//line cmd/xgo/mod_download_cmd.gox:28:1\n\t\t\tthis.Help()\n//line cmd/xgo/mod_download_cmd.gox:29:1\n\t\t\treturn\n\t\t}\n//line cmd/xgo/mod_download_cmd.gox:31:1\n\t\tmod.CmdDownload.Run(mod.CmdDownload, args)\n\t})\n}\nfunc (this *Cmd_mod_download) Classfname() string {\n\treturn \"mod_download\"\n}\n//line cmd/xgo/mod_init_cmd.gox:20\nfunc (this *Cmd_mod_init) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/mod_init_cmd.gox:20:1\n\tthis.Use(\"init [flags] module-path\")\n//line cmd/xgo/mod_init_cmd.gox:22:1\n\tthis.Short(\"initialize new module in current directory\")\n//line cmd/xgo/mod_init_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/mod_init_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/mod_init_cmd.gox:27:1\n\t\tif len(args) < 1 {\n//line cmd/xgo/mod_init_cmd.gox:28:1\n\t\t\tthis.Help()\n//line cmd/xgo/mod_init_cmd.gox:29:1\n\t\t\treturn\n\t\t}\n//line cmd/xgo/mod_init_cmd.gox:31:1\n\t\tmod.CmdInit.Run(mod.CmdInit, args)\n\t})\n}\nfunc (this *Cmd_mod_init) Classfname() string {\n\treturn \"mod_init\"\n}\n//line cmd/xgo/mod_tidy_cmd.gox:20\nfunc (this *Cmd_mod_tidy) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/mod_tidy_cmd.gox:20:1\n\tthis.Use(\"tidy [flags]\")\n//line cmd/xgo/mod_tidy_cmd.gox:22:1\n\tthis.Short(\"add missing and remove unused modules\")\n//line cmd/xgo/mod_tidy_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/mod_tidy_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/mod_tidy_cmd.gox:27:1\n\t\tmod.CmdTidy.Run(mod.CmdTidy, args)\n\t})\n}\nfunc (this *Cmd_mod_tidy) Classfname() string {\n\treturn \"mod_tidy\"\n}\n//line cmd/xgo/run_cmd.gox:20\nfunc (this *Cmd_run) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/run_cmd.gox:20:1\n\tthis.Use(\"run [flags] package [arguments...]\")\n//line cmd/xgo/run_cmd.gox:22:1\n\tthis.Short(\"Compile and run an XGo program\")\n//line cmd/xgo/run_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/run_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/run_cmd.gox:27:1\n\t\trun.Cmd.Run(run.Cmd, args)\n\t})\n}\nfunc (this *Cmd_run) Classfname() string {\n\treturn \"run\"\n}\n//line cmd/xgo/serve_cmd.gox:20\nfunc (this *Cmd_serve) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/serve_cmd.gox:20:1\n\tthis.Use(\"serve [flags]\")\n//line cmd/xgo/serve_cmd.gox:22:1\n\tthis.Short(\"Serve as an XGo LangServer\")\n//line cmd/xgo/serve_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/serve_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/serve_cmd.gox:27:1\n\t\tserve.Cmd.Run(serve.Cmd, args)\n\t})\n}\nfunc (this *Cmd_serve) Classfname() string {\n\treturn \"serve\"\n}\n//line cmd/xgo/test_cmd.gox:20\nfunc (this *Cmd_test) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/test_cmd.gox:20:1\n\tthis.Use(\"test [flags] [packages]\")\n//line cmd/xgo/test_cmd.gox:22:1\n\tthis.Short(\"Test XGo packages\")\n//line cmd/xgo/test_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/test_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/test_cmd.gox:27:1\n\t\ttest.Cmd.Run(test.Cmd, args)\n\t})\n}\nfunc (this *Cmd_test) Classfname() string {\n\treturn \"test\"\n}\n//line cmd/xgo/version_cmd.gox:21\nfunc (this *Cmd_version) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/version_cmd.gox:21:1\n\tthis.Use(\"version [flags]\")\n//line cmd/xgo/version_cmd.gox:23:1\n\tthis.Short(\"Print XGo version\")\n//line cmd/xgo/version_cmd.gox:25:1\n\tthis.Run__0(func() {\n//line cmd/xgo/version_cmd.gox:26:1\n\t\tfmt.Println(stringutil.Concat(\"xgo \", env1.Version(), \" \", runtime.GOOS, \"/\", runtime.GOARCH))\n\t})\n}\nfunc (this *Cmd_version) Classfname() string {\n\treturn \"version\"\n}\n//line cmd/xgo/watch_cmd.gox:20\nfunc (this *Cmd_watch) Main(_xgo_arg0 string) {\n\tthis.Command.Main(_xgo_arg0)\n//line cmd/xgo/watch_cmd.gox:20:1\n\tthis.Use(\"watch [flags] [dir]\")\n//line cmd/xgo/watch_cmd.gox:22:1\n\tthis.Short(\"Monitor code changes in an XGo workspace to generate Go files\")\n//line cmd/xgo/watch_cmd.gox:24:1\n\tthis.FlagOff()\n//line cmd/xgo/watch_cmd.gox:26:1\n\tthis.Run__1(func(args []string) {\n//line cmd/xgo/watch_cmd.gox:27:1\n\t\twatch.Cmd.Run(watch.Cmd, args)\n\t})\n}\nfunc (this *Cmd_watch) Classfname() string {\n\treturn \"watch\"\n}\nfunc main() {\n\tnew(App).Main()\n}\n"
  },
  {
    "path": "demo/_llgo/callpy/callpy.xgo",
    "content": "import (\n\t\"c\"\n\t\"py\"\n\t\"py/math\"\n)\n\nx := math.sqrt(py.float(2))\nc.printf c\"sqrt(2) = %f\\n\", x.float64\n"
  },
  {
    "path": "demo/_llgo/chello/hello.xgo",
    "content": "import \"c\"\n\nc.printf c\"Hello world\\n\"\n"
  },
  {
    "path": "demo/_llgo/cpphello/cpphello.xgo",
    "content": "import \"cpp/std\"\n\nprintln std.str(\"Hello world\").str\n"
  },
  {
    "path": "demo/_llgo/defer/defer.xgo",
    "content": "import \"c\"\n\nfunc f(s string) bool {\n\treturn len(s) > 2\n}\n\ndefer func() {\n\tc.printf c\"hi\\n\"\n}()\nif s := \"hello\"; f(s) {\n\tdefer c.printf(c\"%s\\n\", c.allocaCStr(s))\n} else {\n\tdefer c.printf(c\"world\\n\")\n}\ndefer c.printf(c\"bye\\n\")\n"
  },
  {
    "path": "demo/_llgo/errors/errors.xgo",
    "content": "import (\n\t\"c\"\n)\n\n// New returns an error that formats as the given text.\n// Each call to New returns a distinct error value even if the text is identical.\nfunc New(text string) error {\n\treturn &errorString{text}\n}\n\n// errorString is a trivial implementation of error.\ntype errorString struct {\n\ts string\n}\n\nfunc (e *errorString) Error() string {\n\treturn e.s\n}\n\ne := new(\"an error\")\nc.printf c.allocaCStr(e.error+\"\\n\")\n"
  },
  {
    "path": "demo/_llgo/go.mod",
    "content": "module llgoexample\n\ngo 1.18 // llgo 1.0\n\nrequire github.com/goplus/lib v0.2.0\n"
  },
  {
    "path": "demo/_llgo/go.sum",
    "content": "github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=\ngithub.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=\n"
  },
  {
    "path": "demo/_llgo/goroutine/goroutine.xgo",
    "content": "import \"c\"\n\ndone := false\ngo func() {\n\tc.printf c\"Hello, goroutine\\n\"\n\tdone = true\n}()\nfor !done {\n\tc.printf c\".\"\n}\n"
  },
  {
    "path": "demo/_llgo/hello/hello.xgo",
    "content": "echo \"Hello world\"\n"
  },
  {
    "path": "demo/_llgo/hellollgo/README.md",
    "content": "This is an example to show how XGo interacts with C.\n\n```go\nimport \"c\"\n\nc.printf c\"Hello, llgo!\\n\"\nc.fprintf c.Stderr, c\"Hi, %6.1f\\n\", 3.14\n```\n\nHere we use `import \"c\"` to import libc. It's an abbreviation for `import \"github.com/goplus/lib/c\"`. It is equivalent to the following code:\n\n```go\nimport \"github.com/goplus/lib/c\"\n\nc.printf c\"Hello, llgo!\\n\"\nc.fprintf c.Stderr, c\"Hi, %7.1f\\n\", 3.14\n```\n\nIn this example we call two C standard functions `printf` and `fprintf`, pass a C variable `stderr` and two C strings in the form of `c\"xxx\"`.\n\nTo run this demo, you need to set the `XGO_GOCMD` environment variable first.\n\n```sh\nexport XGO_GOCMD=llgo  # default is `go`\n```\n\nThen execute `xgo run .` to see the output of this example:\n\n```\nHello, llgo!\nHi,    3.1\n```\n\n### Give a Star! ⭐\n\nIf you like or are using XGo to learn or start your projects, please give it a star. Thanks!\n"
  },
  {
    "path": "demo/_llgo/hellollgo/hello.xgo",
    "content": "import \"c\"\n\nc.printf c\"Hello, llgo!\\n\"\nc.fprintf c.Stderr, c\"Hi, %6.1f\\n\", 3.14\n"
  },
  {
    "path": "demo/_llgo/matrix/matrix.xgo",
    "content": "import (\n\t\"c\"\n\t\"py\"\n\t\"py/numpy\"\n)\n\na := py.list(\n\tpy.list(1.0, 2.0, 3.0),\n\tpy.list(4.0, 5.0, 6.0),\n\tpy.list(7.0, 8.0, 9.0),\n)\nb := py.list(\n\tpy.list(9.0, 8.0, 7.0),\n\tpy.list(6.0, 5.0, 4.0),\n\tpy.list(3.0, 2.0, 1.0),\n)\nx := numpy.add(a, b)\nc.printf c\"a+b = %s\\n\", x.str.cStr\n"
  },
  {
    "path": "demo/_llgo/pyhello/hello.xgo",
    "content": "import \"py/std\"\n\nstd.print py\"Hello world\"\n"
  },
  {
    "path": "demo/_llgo/pymax/pymax.xgo",
    "content": "import (\n\t\"py\"\n\t\"py/std\"\n)\n\nx := std.max(py.float(3.0), py.float(9.0), py.float(23.0), py.float(100.0))\nstd.print(x)\n\nlist := py.list(3.0, 9.0, 23.0, 100.0)\ny := std.max(std.iter(list))\nstd.print(y)\n"
  },
  {
    "path": "demo/_llgo/pyprint/print.xgo",
    "content": "import (\n\t\"py\"\n\t\"py/std\"\n)\n\nx := py.float(3.14)\nstd.print(x)\n"
  },
  {
    "path": "demo/_llgo/pytensor/tensor.xgo",
    "content": "import (\n\t\"py\"\n\t\"py/std\"\n\t\"py/torch\"\n)\n\ndata := py.list(\n\tpy.list(1.0, 2.0),\n\tpy.list(3.0, 4.0),\n)\nx := torch.tensor(data)\nstd.print(x)\n"
  },
  {
    "path": "demo/_llgo/qsort/qsort.xgo",
    "content": "import (\n\t\"c\"\n\t\"unsafe\"\n)\n\na := [100, 8, 23, 2, 7]\nc.qsort unsafe.Pointer(&a[0]), 5, unsafe.Sizeof(0), (a, b) => {\n\treturn c.Int(*(*int)(a) - *(*int)(b))\n}\nfor v in a {\n\tc.printf c\"%d\\n\", v\n}\n"
  },
  {
    "path": "demo/_llgo/reflect/reflect.xgo",
    "content": "import (\n\t\"c\"\n\t\"reflect\"\n)\n\ntyIntSlice := reflect.sliceOf(reflect.typeOf(0))\nv := reflect.zero(tyIntSlice)\nv = reflect.append(v, reflect.valueOf(1), reflect.valueOf(2), reflect.valueOf(3))\nfor i, n := 0, v.len; i < n; i++ {\n\titem := v.index(i)\n\tc.Printf c\"%d\\n\", item.int\n}\n"
  },
  {
    "path": "demo/_llgo/sqlitedemo/sqlitedemo.xgo",
    "content": "import (\n\t\"c\"\n\t\"c/os\"\n\t\"c/sqlite\"\n)\n\nfunc check(err sqlite.Errno, db *sqlite.Sqlite3, at string) {\n\tif err != sqlite.OK {\n\t\tc.printf c\"==> %s Error: (%d) %s\\n\", c.allocaCStr(at), err, db.errmsg\n\t\tc.exit 1\n\t}\n}\n\nfunc checkDone(err sqlite.Errno, db *sqlite.Sqlite3, at string) {\n\tif err != sqlite.Done {\n\t\tcheck err, db, at\n\t}\n}\n\nos.remove c\"test.db\"\n\ndb, err := sqlite.open(c\"test.db\")\ncheck err, db, \"sqlite: Open\"\n\nerr = db.exec(c\"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)\", nil, nil, nil)\ncheck err, db, \"sqlite: Exec CREATE TABLE\"\n\nstmt, err := db.prepareV3(\"INSERT INTO users (id, name) VALUES (?, ?)\", 0, nil)\ncheck err, db, \"sqlite: PrepareV3 INSERT\"\n\nstmt.bindInt 1, 100\nstmt.bindText 2, c\"Hello World\", -1, nil\n\nerr = stmt.step\ncheckDone err, db, \"sqlite: Step INSERT 1\"\n\nstmt.reset\nstmt.bindInt 1, 200\nstmt.bindText 2, c\"This is llgo\", -1, nil\n\nerr = stmt.step\ncheckDone err, db, \"sqlite: Step INSERT 2\"\n\nstmt.close\n\nstmt, err = db.prepareV3(\"SELECT * FROM users\", 0, nil)\ncheck err, db, \"sqlite: PrepareV3 SELECT\"\n\nfor {\n\tif err = stmt.step; err != sqlite.HasRow {\n\t\tbreak\n\t}\n\tc.printf c\"==> id=%d, name=%s\\n\", stmt.columnInt(0), stmt.columnText(1)\n}\ncheckDone err, db, \"sqlite: Step done\"\n\nstmt.close\ndb.close\n"
  },
  {
    "path": "demo/_llgo/statistics/statistics.xgo",
    "content": "import (\n\t\"c\"\n\t\"py\"\n\t\"py/statistics\"\n)\n\nlist := py.list(1.0, 2.0, 3.0, 4.0, 4.0)\nmean := statistics.mean(list)\nc.printf c\"mean(1, 2, 3, 4, 4) = %f\\n\", mean.float64\n"
  },
  {
    "path": "demo/_llgo/tetris/tetris.xgo",
    "content": "import (\n\t\"c\"\n\t\"c/raylib\"\n)\n\nconst (\n\tBOARD_WIDTH  = 10\n\tBOARD_HEIGHT = 20\n\tBLOCK_SIZE   = 30\n\n\tSCREENWIDTH  = 300\n\tSCREENHEIGHT = 600\n)\n\nconst MAX_BLOCKS = 4\n\ntype Shape struct {\n\tBlocks [MAX_BLOCKS]raylib.Vector2\n\tColor  raylib.Color\n}\n\nvar SHAPES = []Shape{\n\t{Blocks: [MAX_BLOCKS]raylib.Vector2{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 2, Y: 0}, {X: 3, Y: 0}}, Color: raylib.SKYBLUE},\n\t{Blocks: [MAX_BLOCKS]raylib.Vector2{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}}, Color: raylib.YELLOW},\n\t{Blocks: [MAX_BLOCKS]raylib.Vector2{{X: 1, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 2, Y: 1}}, Color: raylib.PURPLE},\n\t{Blocks: [MAX_BLOCKS]raylib.Vector2{{X: 1, Y: 0}, {X: 2, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}}, Color: raylib.GREEN},\n\t{Blocks: [MAX_BLOCKS]raylib.Vector2{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 2, Y: 1}}, Color: raylib.RED},\n\t{Blocks: [MAX_BLOCKS]raylib.Vector2{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 2, Y: 1}}, Color: raylib.BLUE},\n\t{Blocks: [MAX_BLOCKS]raylib.Vector2{{X: 2, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 2, Y: 1}}, Color: raylib.ORANGE},\n}\n\nvar board [BOARD_HEIGHT][BOARD_WIDTH]raylib.Color\n\nvar curShape Shape\nvar curPos raylib.Vector2\n\nvar fallTime = c.Float(0)\nvar fallSpeed = c.Float(0.2)\nvar score = 0\nvar scoreText = (*c.Char)(c.malloc(20))\nvar gameOver = false\n\nfunc genShape() {\n\tcurShape = SHAPES[raylib.getRandomValue(0, 6)]\n\tcurPos = raylib.Vector2{BOARD_WIDTH/2 - 1, 0}\n}\n\nfunc checkCollision() bool {\n\tfor i := 0; i < MAX_BLOCKS; i++ {\n\t\tx := int(curPos.X + curShape.Blocks[i].X)\n\t\ty := int(curPos.Y + curShape.Blocks[i].Y)\n\t\tif x < 0 || x >= BOARD_WIDTH || y >= BOARD_HEIGHT || (y >= 0 && board[y][x] != raylib.BLANK) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc lockShape() {\n\tfor i := 0; i < MAX_BLOCKS; i++ {\n\t\tx := int(curPos.X + curShape.Blocks[i].X)\n\t\ty := int(curPos.Y + curShape.Blocks[i].Y)\n\t\tif y >= 0 {\n\t\t\tboard[y][x] = curShape.Color\n\t\t}\n\t}\n}\n\nfunc rotateShape() {\n\trotated := curShape\n\tfor i := 0; i < MAX_BLOCKS; i++ {\n\t\tx := rotated.Blocks[i].X\n\t\trotated.Blocks[i].X = -rotated.Blocks[i].Y\n\t\trotated.Blocks[i].Y = x\n\t}\n\n\ttemp := curShape\n\tcurShape = rotated\n\tif checkCollision() {\n\t\tcurShape = temp\n\t}\n}\n\nfunc clearLines() int {\n\tlinesCleared := 0\n\tfor y := BOARD_HEIGHT - 1; y >= 0; y-- {\n\t\tlineFull := true\n\t\tfor x := 0; x < BOARD_WIDTH; x++ {\n\t\t\tif board[y][x] == raylib.BLANK {\n\t\t\t\tlineFull = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif lineFull {\n\t\t\tfor yy := y; yy > 0; yy-- {\n\t\t\t\tfor x := 0; x < BOARD_WIDTH; x++ {\n\t\t\t\t\tboard[yy][x] = board[yy-1][x]\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor x := 0; x < BOARD_WIDTH; x++ {\n\t\t\t\tboard[0][x] = raylib.BLANK\n\t\t\t}\n\t\t\ty += 1\n\t\t\tlinesCleared += 1\n\t\t}\n\t}\n\treturn linesCleared\n}\n\nfunc keyPressed(key c.Int) bool {\n\treturn raylib.isKeyPressed(key) || raylib.isKeyPressedRepeat(key)\n}\n\nraylib.initWindow SCREENWIDTH, SCREENHEIGHT, c\"tetris (powered by raylib and XGo)\"\nraylib.setTargetFPS 60\ngenShape\n\nfor !raylib.windowShouldClose && !gameOver {\n\tfallTime += raylib.getFrameTime\n\tif fallTime >= fallSpeed {\n\t\tfallTime = 0\n\t\tcurPos.Y += 1\n\t\tif checkCollision() {\n\t\t\tcurPos.Y -= 1\n\t\t\tlockShape\n\t\t\tlinesCleared := clearLines()\n\t\t\tscore += linesCleared * 100\n\t\t\tgenShape\n\t\t\tif checkCollision() {\n\t\t\t\tgameOver = true\n\t\t\t}\n\t\t}\n\t}\n\n\tif keyPressed(raylib.KEY_LEFT) {\n\t\tcurPos.X--\n\t\tif checkCollision() {\n\t\t\tcurPos.X++\n\t\t}\n\t}\n\tif keyPressed(raylib.KEY_RIGHT) {\n\t\tcurPos.X++\n\t\tif checkCollision() {\n\t\t\tcurPos.X--\n\t\t}\n\t}\n\tif keyPressed(raylib.KEY_SPACE) || keyPressed(raylib.KEY_UP) || keyPressed(raylib.KEY_DOWN) {\n\t\trotateShape\n\t}\n\n\traylib.beginDrawing\n\traylib.clearBackground raylib.RAYWHITE\n\tfor y := 0; y < BOARD_HEIGHT; y++ {\n\t\tfor x := 0; x < BOARD_WIDTH; x++ {\n\t\t\traylib.drawRectangle c.Int(x*BLOCK_SIZE), c.Int(y*BLOCK_SIZE), c.Int(BLOCK_SIZE-1), c.Int(BLOCK_SIZE-1), board[y][x]\n\t\t}\n\t}\n\n\tfor i := 0; i < MAX_BLOCKS; i++ {\n\t\traylib.drawRectangle c.Int((curPos.X+curShape.Blocks[i].X)*BLOCK_SIZE), c.Int((curPos.Y+curShape.Blocks[i].Y)*BLOCK_SIZE),\n\t\t\tBLOCK_SIZE-1, BLOCK_SIZE-1, curShape.Color\n\t}\n\n\tc.sprintf scoreText, c\"Score:%d\", score\n\traylib.drawText scoreText, 10, 10, 20, raylib.BLACK\n\traylib.endDrawing\n}\n\nfor !raylib.windowShouldClose {\n\traylib.beginDrawing\n\traylib.clearBackground raylib.RAYWHITE\n\traylib.drawText c\"Game Over\", SCREENWIDTH/2-50, SCREENHEIGHT/2-10, 20, raylib.RED\n\traylib.drawText scoreText, SCREENWIDTH/2-50, SCREENHEIGHT/2+10, 20, raylib.BLACK\n\traylib.endDrawing\n}\n"
  },
  {
    "path": "demo/_tinygo/blink/blink.xgo",
    "content": "import (\n\t\"machine\"\n\t\"time\"\n)\n\nled := machine.LED\nled.configure {Mode: machine.PinOutput}\nfor {\n\tled.low\n\ttime.sleep 1s\n\n\tled.high\n\ttime.sleep 1s\n}\n"
  },
  {
    "path": "demo/_tinygo/go.mod",
    "content": "module tinygoexample\n\ngo 1.18 // tinygo 0.32\n"
  },
  {
    "path": "demo/_tinygo/sortdemo/sort.xgo",
    "content": "import \"sort\"\n\nvals := [32, 58, 25, 92, 45, 78]\nsort.ints vals\nfor v in vals {\n\tprintln v\n}\n\ntexts := [\"apple\", \"banana\", \"cherry\", \"date\", \"elderberry\", \"fig\"]\nsort.slice texts, (i, j) => {\n\tleni, lenj := len(texts[i]), len(texts[j])\n\tif leni != lenj {\n\t\treturn leni < lenj\n\t}\n\treturn texts[i] < texts[j]\n}\nfor v in texts {\n\tprintln v\n}\n"
  },
  {
    "path": "demo/clsinit/Rect.gox",
    "content": "var (\n    w, h = 10, 20\n    color int\n    border float64 = 1.2\n)\n\necho *this\n"
  },
  {
    "path": "demo/domaintext/domaintext.xgo",
    "content": "config := json`{\n\t\"server\": \"localhost\",\n\t\"port\": 8080,\n\t\"features\": [\"auth\", \"logging\"]\n}`!\n\necho config.port\n"
  },
  {
    "path": "demo/dql-fs/fsq.xgo",
    "content": "for e in fs`.`.**.file.match(\"*.xgo\") {\n    echo e.path\n}\n"
  },
  {
    "path": "demo/dql-json/jq.xgo",
    "content": "doc := json`{\n\t\"animals\": [\n\t\t{\"class\": \"gopher\", \"at\": \"Line 1\"},\n\t\t{\"class\": \"armadillo\", \"at\": \"Line 2\"},\n\t\t{\"class\": \"zebra\", \"at\": \"Line 3\"},\n\t\t{\"class\": \"unknown\", \"at\": \"Line 4\"},\n\t\t{\"class\": \"gopher\", \"at\": \"Line 5\"},\n\t\t{\"class\": \"bee\", \"at\": \"Line 6\"},\n\t\t{\"class\": \"gopher\", \"at\": \"Line 7\"},\n\t\t{\"class\": \"zebra\", \"at\": \"Line 8\"}\n\t]\n}\n`!\n\nfor animal in doc.animals.*@($class == \"zebra\") {\n\techo animal.$at\n}\n"
  },
  {
    "path": "demo/dql-links/links.xgo",
    "content": "import \"os\"\nimport \"github.com/goplus/xgo/dql/html\"\n\ndoc := html.source(os.Args[1])\n\nfor a in doc.**.a {\n\tif url := a.$href; url != \"\" {\n\t\techo url\n\t}\n}\n"
  },
  {
    "path": "demo/dql-xgo/xgoq.xgo",
    "content": "doc := xgo`\nx, y := \"Hi\", 123\necho x\nprint y\n`!\n\nstmts := doc.shadowEntry.body.list.*@(self.class == \"ExprStmt\")\nfor fn in stmts.x@(self.class == \"CallExpr\").fun@(self.class == \"Ident\") {\n\techo fn.$name\n}\n"
  },
  {
    "path": "demo/dql-yaml/yq.xgo",
    "content": "config := yaml`server: localhost\nport: 8080\nfeatures:\n  - auth\n  - logging\n`!\n\necho config.port\n\nfor feature in config.features.* {\n\techo feature._value\n}\n"
  },
  {
    "path": "demo/fullspec/mixgo-complex/bar.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"testing\"\n)\n\ntype ift interface {\n\tio.Closer\n\tf(int) string\n\tg()\n}\n\ntype impl struct {\n\ta T\n}\n\nfunc Bar(t *testing.T) int {\n\tlog.Println(\"Hello\")\n\tt.Log(\"Hello\",\n\t\t\"world\")\n\treturn 0\n}\n"
  },
  {
    "path": "demo/fullspec/mixgo-complex/foo.xgo",
    "content": "// Pkg doc\n\nimport (\n\t\"io\"\n\t\"log\"\n)\n\ntype ift2 interface {\n\tio.Closer\n\tf(int) string\n\tg()\n}\n\n// T doc\ntype T struct {\n\tio.Closer\n}\n\nfunc (T) Close() (err error) {\n\tlog.Println(\"Hi!\")\n\treturn\n}\n\nfunc (t T) g() {}\n\nfunc (t T) f(a int) (b string) {\n\t_ = t.Closer\n\treturn\n}\n\nfunc Foo(i *impl) string {\n\ti.a.f(0)\n\ti.a.g()\n\treturn \"\"\n}\n\n// foo golang/go#61561: interface instances aren't concurrency-safe\n// as they are not completed by the type checker.\nfunc foo(a, b int) string {\n\treturn \"\"\n}\n\nprintln \"Hello, world\", T{}.f(0)\n"
  },
  {
    "path": "demo/fullspec/mixgo-complex/xgo_autogen.go",
    "content": "// Code generated by xgo (XGo); DO NOT EDIT.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n)\n\nconst _ = true\n\ntype ift2 interface {\n\tio.Closer\n\tf(int) string\n\tg()\n}\n// T doc\ntype T struct {\n\tio.Closer\n}\n//line demo/fullspec/mixgo-complex/foo.xgo:19:1\nfunc (T) Close() (err error) {\n//line demo/fullspec/mixgo-complex/foo.xgo:20:1\n\tlog.Println(\"Hi!\")\n//line demo/fullspec/mixgo-complex/foo.xgo:21:1\n\treturn\n}\n//line demo/fullspec/mixgo-complex/foo.xgo:24:1\nfunc (t T) g() {\n}\n//line demo/fullspec/mixgo-complex/foo.xgo:26:1\nfunc (t T) f(a int) (b string) {\n//line demo/fullspec/mixgo-complex/foo.xgo:27:1\n\t_ = t.Closer\n//line demo/fullspec/mixgo-complex/foo.xgo:28:1\n\treturn\n}\n//line demo/fullspec/mixgo-complex/foo.xgo:31:1\nfunc Foo(i *impl) string {\n//line demo/fullspec/mixgo-complex/foo.xgo:32:1\n\ti.a.f(0)\n//line demo/fullspec/mixgo-complex/foo.xgo:33:1\n\ti.a.g()\n//line demo/fullspec/mixgo-complex/foo.xgo:34:1\n\treturn \"\"\n}\n//line demo/fullspec/mixgo-complex/foo.xgo:37:1\n// foo golang/go#61561: interface instances aren't concurrency-safe\n// as they are not completed by the type checker.\nfunc foo(a int, b int) string {\n//line demo/fullspec/mixgo-complex/foo.xgo:40:1\n\treturn \"\"\n}\n//line demo/fullspec/mixgo-complex/foo.xgo:43\nfunc main() {\n//line demo/fullspec/mixgo-complex/foo.xgo:43:1\n\tfmt.Println(\"Hello, world\", T{}.f(0))\n}\n"
  },
  {
    "path": "demo/fullspec/overloadfunc1/add.xgo",
    "content": "func add = (\n\tfunc(a, b int) int {\n\t\treturn a + b\n\t}\n\tfunc(a, b string) string {\n\t\treturn a + b\n\t}\n)\n\nprintln add(100, 7)\nprintln add(\"Hello\", \"World\")\n"
  },
  {
    "path": "demo/fullspec/overloadfunc2/mul.xgo",
    "content": "func mulInt(a, b int) int {\n\treturn a * b\n}\n\nfunc mulFloat(a, b float64) float64 {\n\treturn a * b\n}\n\nfunc mul = (\n\tmulInt\n\tmulFloat\n)\n\nprintln mul(100, 7)\nprintln mul(1.2, 3.14)\n"
  },
  {
    "path": "demo/fullspec/overloadmethod/method.xgo",
    "content": "type foo struct {\n}\n\nfunc (a *foo) mulInt(b int) *foo {\n\tprintln \"mulInt\"\n\treturn a\n}\n\nfunc (a *foo) mulFoo(b *foo) *foo {\n\tprintln \"mulFoo\"\n\treturn a\n}\n\nfunc (foo).mul = (\n\t(foo).mulInt\n\t(foo).mulFoo\n)\n\nvar a, b *foo\nvar c = a.mul(100)\nvar d = a.mul(c)\n"
  },
  {
    "path": "demo/fullspec/overloadop1/overloadop.xgo",
    "content": "type foo struct {\n}\n\nfunc (a foo) + (b foo) (ret foo) {\n\tprintln \"a + b\"\n\treturn\n}\n\nfunc (a foo) - (b foo) (ret foo) {\n\tprintln \"a - b\"\n\treturn\n}\n\nfunc (a foo) -> (b foo) {\n\tprintln \"a -> b\"\n}\n\nfunc (a foo) <> (b foo) {\n\tprintln \"a <> b\"\n}\n\nfunc -(a foo) (ret foo) {\n\tprintln \"-a\"\n\treturn\n}\n\nfunc ++(a foo) {\n\tprintln \"a++\"\n}\n\nfunc (a foo) != (b foo) bool {\n\tprintln \"a != b\"\n\treturn true\n}\n\nvar a, b foo\nvar c = a + b\nvar d = a - b\nvar e = -a\nvar f = a != b\n\nprintln f\na++\na -> b\na <> b\n"
  },
  {
    "path": "demo/fullspec/overloadop2/overloadop.xgo",
    "content": "type foo struct {\n}\n\nfunc (a foo) mulInt(b int) (ret foo) {\n\tprintln \"a * int\"\n\treturn\n}\n\nfunc (a foo) mulFoo(b foo) (ret foo) {\n\tprintln \"a * b\"\n\treturn\n}\n\nfunc intMulFoo(a int, b foo) (ret foo) {\n\tprintln \"int * b\"\n\treturn\n}\n\nfunc (foo).* = (\n\t(foo).mulInt\n\t(foo).mulFoo\n\tintMulFoo\n)\n\nvar a, b foo\nvar c = a * 10\nvar d = a * b\nvar e = 10 * a\n"
  },
  {
    "path": "demo/fullspec/tpl-gen-ast/gen_calc_ast.xgo",
    "content": "import (\n\t\"encoding/json\"\n\t\"os\"\n\t\"xgo/tpl\"\n\t\"xgo/tpl/token\"\n)\n\ntype Expr any\n\ntype UnaryExpr struct {\n\tOpPos token.Pos\n\tOp    token.Token\n\tX     Expr\n}\n\ntype BinaryExpr struct {\n\tX     Expr\n\tOpPos token.Pos\n\tOp    token.Token\n\tY     Expr\n}\n\ntype BasicLit struct {\n\tValuePos token.Pos\n\tKind     token.Token\n\tValue    string\n}\n\ncl := tpl`\n\nexpr = operand % (\"*\" | \"/\") % (\"+\" | \"-\") => {\n\treturn tpl.binaryOp(true, self, (op, x, y) => {\n\t\treturn &BinaryExpr{\n\t\t\tX:     x,\n\t\t\tOpPos: op.Pos,\n\t\t\tOp:    op.Tok,\n\t\t\tY:     y,\n\t\t}\n\t})\n}\n\noperand = basicLit | unaryExpr\n\nunaryExpr = \"-\" operand => {\n\top := self[0].(*tpl.Token)\n\treturn &UnaryExpr{\n\t\tOpPos: op.Pos,\n\t\tOp:    op.Tok,\n\t\tX:     self[1],\n\t}\n}\n\nbasicLit = INT | FLOAT => {\n\top := self.(*tpl.Token)\n\treturn &BasicLit{\n\t\tValuePos: op.Pos,\n\t\tKind:     op.Tok,\n\t\tValue:    op.Lit,\n\t}\n}\n`!\n\nprint \"> \"\nfor line in os.Stdin {\n\te, err := cl.parseExpr(line, nil)\n\tif err != nil {\n\t\tprint err, \"\\n> \"\n\t} else {\n\t\tprint string(json.marshalIndent(e, \"\", \"  \")!), \"\\n> \"\n\t}\n}\n"
  },
  {
    "path": "demo/gsh-exec/exec.gsh",
    "content": "xgo \"run\", \"./foo\"\nexec \"xgo run ./foo\"\nexec \"FOO=100 xgo run ./foo\"\nexec {\"FOO\": \"101\"}, \"xgo\", \"run\", \"./foo\"\nexec \"xgo\", \"run\", \"./foo\"\nexec \"ls $HOME\"\nls ${HOME}\n"
  },
  {
    "path": "demo/gsh-exec/foo/foo.xgo",
    "content": "import \"os\"\n\nfoo := os.getenv(\"FOO\")\nif foo != \"\" {\n\techo foo\n} else {\n\techo \"FOO not found\"\n}\n"
  },
  {
    "path": "demo/kwargs/run.xgo",
    "content": "type Config (timeout, maxRetries int, debug bool)\n\nfunc run(task int, cfg Config?) {\n\tif cfg.timeout == 0 {\n\t\tcfg.timeout = 30\n\t}\n\tif cfg.maxRetries == 0 {\n\t\tcfg.maxRetries = 3\n\t}\n    echo \"timeout:\", cfg.timeout, \"maxRetries:\", cfg.maxRetries, \"debug:\", cfg.debug\n\techo \"task:\", task\n}\n\nrun 100, timeout = 60, maxRetries = 5\nrun 200\n"
  },
  {
    "path": "demo/lambda1/lambda.xgo",
    "content": "func f(x float64, t func(float64) float64) float64 {\n\treturn t(x)\n}\n\necho f(1.0, x => 2 * x)\necho f(5.0, (x) => {\n\treturn 2 * x\n})\n"
  },
  {
    "path": "demo/mapliteral/mapliteral.xgo",
    "content": "func echoS2f32(vals map[string]float32) {\n\techo vals\n}\n\necho {\"Monday\": 1, \"Sunday\": 7}\nechoS2f32 {\"Monday\": 1, \"Sunday\": 7}\n\nvar a map[string]any = {\"Monday\": 1, \"Sunday\": 7}\necho a\n"
  },
  {
    "path": "demo/mixgo/README.md",
    "content": "This is an example to show how to mix Go/XGo code in the same package.\n\nIn this example, we have a Go source file named `a.go`:\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc p(a interface{}) {\n\tsayMix()\n\tfmt.Println(\"Hello,\", a)\n}\n```\n\nAnd we have an XGo source file named `b.xgo`:\n\n```go\nfunc sayMix() {\n\tprintln \"Mix Go and XGo\"\n}\n\np \"world\"\n```\n\nYou can see that Go calls an XGo function named `sayMix`, and XGo calls a Go function named `p`. As you are used to in Go programming, this kind of circular reference is allowed.\n\nRun `xgo run .` to see the output of this example:\n\n```\nMix Go and XGo\nHello, world\n```\n\n### Give a Star! ⭐\n\nIf you like or are using XGo to learn or start your projects, please give it a star. Thanks!\n"
  },
  {
    "path": "demo/mixgo/a.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc p(a any) {\n\tsayMix()\n\tfmt.Println(\"Hello,\", a)\n}\n"
  },
  {
    "path": "demo/mixgo/b.xgo",
    "content": "func sayMix() {\n\tprintln \"Mix Go and XGo\"\n}\n\np \"world\"\n"
  },
  {
    "path": "demo/mixgo/xgo_autogen.go",
    "content": "// Code generated by xgo (XGo); DO NOT EDIT.\n\npackage main\n\nimport \"fmt\"\n\nconst _ = true\n//line demo/mixgo/b.xgo:1:1\nfunc sayMix() {\n//line demo/mixgo/b.xgo:2:1\n\tfmt.Println(\"Mix Go and XGo\")\n}\n//line demo/mixgo/b.xgo:5\nfunc main() {\n//line demo/mixgo/b.xgo:5:1\n\tp(\"world\")\n}\n"
  },
  {
    "path": "demo/sliceliteral/sliceliteral.xgo",
    "content": "func echoF32s(vals []float32) {\n\techo vals\n}\n\nfunc anyslice() []any {\n\treturn [10, 3.14, 200]\n}\n\necho [10, 3.14, 200]\nechoF32s [10, 3.14, 200]\n\nvar a []any = [10, 3.14, 200]\na = [10, 3.14, 200]\necho a\necho anyslice()\n"
  },
  {
    "path": "demo/stringtrans/transform.xgo",
    "content": "t := \"u_int32_t\"\necho t.split(\"_\").capitalize.join(\"\")\necho t.split(\"_\").repeat(3).join(\" \")\n"
  },
  {
    "path": "demo/tpl-calc/calc.xgo",
    "content": "import (\n\t\"os\"\n\t\"xgo/tpl\"\n)\n\ncl := tpl`\n\nexpr = operand % (\"*\" | \"/\") % (\"+\" | \"-\") => {\n\treturn tpl.binaryOp(true, self, (op, x, y) => {\n\t\tswitch op.Tok {\n\t\tcase '+': return x.(float64) + y.(float64)\n\t\tcase '-': return x.(float64) - y.(float64)\n\t\tcase '*': return x.(float64) * y.(float64)\n\t\tcase '/': return x.(float64) / y.(float64)\n\t\t}\n\t\tpanic(\"unexpected\")\n\t})\n}\n\noperand = basicLit | unaryExpr\n\nunaryExpr = \"-\" operand => {\n\treturn -(self[1].(float64))\n}\n\nbasicLit = INT | FLOAT => {\n\treturn self.(*tpl.Token).Lit.float!\n}\n`!\n\nprint \"> \"\nfor line in os.Stdin {\n\te, err := cl.parseExpr(line, nil)\n\tif err != nil {\n\t\tprint err, \"\\n> \"\n\t} else {\n\t\tprint e, \"\\n> \"\n\t}\n}\n"
  },
  {
    "path": "demo/tpl-calc-dump/calc_dump.xgo",
    "content": "import (\n\t\"os\"\n\t\"xgo/tpl\"\n)\n\ncl := tpl`\nexpr = termExpr % (\"+\" | \"-\")\n\ntermExpr = unaryExpr % (\"*\" | \"/\")\n\nunaryExpr = operand | \"-\" unaryExpr\n\noperand = INT | FLOAT | \"(\" expr \")\"\n`!\n\nprint \"> \"\nfor line in os.Stdin {\n\te, err := cl.parseExpr(line, nil)\n\tif err != nil {\n\t\tprint \"${err}\\n> \"\n\t} else {\n\t\ttpl.dump e\n\t\tprint \"> \"\n\t}\n}\n"
  },
  {
    "path": "demo/tpl-intlist/ints.xgo",
    "content": "import \"xgo/tpl\"\n\ncl := tpl`\nexpr = INT % \",\" => {\n\treturn tpl.ListOp[int](self, v => {\n\t\treturn v.(*tpl.Token).Lit.int!\n\t})\n}\n`!\n\necho cl.parseExpr(\"1, 2, 3\", nil)!\n"
  },
  {
    "path": "demo/tpl-natural-lang/nlang.xgo",
    "content": "import \"os\"\n\ncl := tpl`\nexpr = subject verb object\nsubject = \"I\" | \"You\" | \"He\" | \"She\" | \"It\" | \"Dog\" | \"Cat\"\nobject = \"me\" | \"you\" | \"him\" | \"her\" | \"it\" | \"fish\" | \"apple\" | \"banana\" | \"dog\" | \"cat\"\nverb = \"attack\" | \"love\" | \"eat\" | \"hate\"\n`!\n\nprint \"> \"\nfor line in os.Stdin {\n\te, err := cl.parseExpr(line, nil)\n\tif err != nil {\n\t\tprint \"${err}\\n> \"\n\t} else {\n\t\tprint e, \"\\n > \"\n\t}\n}\n"
  },
  {
    "path": "demo/tpl-parser-demo/demo.xgo",
    "content": "cl := tpl`expr = *INT`!\necho cl.parseExpr(\"1 2 3\", nil)!\n\ncl = tpl`expr = INT % \",\"`!\necho cl.parseExpr(\"1, 2, 3\", nil)!\n\ncl = tpl`expr = INT % (\"+\" | \"-\")`!\necho cl.parseExpr(\"1 + 2 - 3\", nil)!\n"
  },
  {
    "path": "demo/tpl-pseudo/gauss.pseudo",
    "content": "DECLARE n, i, sum : INTEGER\nOUTPUT \"please input n:\"\nINPUT n\ni <- 1\nsum <- 0\nWHILE i <= n DO\n    sum <- sum + i\n    i <- i + 1\nENDWHILE\nOUTPUT sum\n"
  },
  {
    "path": "demo/tpl-pseudo/pseudo.gox",
    "content": "import (\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"xgo/tpl\"\n\t\"xgo/tpl/token\"\n\t\"xgo/tpl/variant/delay\"\n\n\t_ \"xgo/tpl/variant/builtin\"\n\t_ \"xgo/tpl/variant/math\"\n\t_ \"xgo/tpl/variant/time\"\n)\n\nvar (\n\tvars   map[string]any\n\tconsts map[string]any\n)\n\nfunc exists(name string) bool {\n\t_, ok := vars[name]\n\tif !ok {\n\t\t_, ok = consts[name]\n\t}\n\treturn ok\n}\n\nfunc value(name string) (v any, ok bool) {\n\tv, ok = vars[name]\n\tif !ok {\n\t\tv, ok = consts[name]\n\t}\n\treturn\n}\n\nfunc setValue(name string, v any) {\n\toldv, ok := vars[name]\n\tif !ok {\n\t\tpanic \"variable ${name} is undefined\"\n\t}\n\tif reflect.typeOf(oldv) != reflect.typeOf(v) {\n\t\tpanic \"assignment of ${name}: type mismatch\"\n\t}\n\tvars[name] = v\n}\n\nfunc chgValue(name string, chg func(oldv any) any) {\n\toldv, ok := vars[name]\n\tif !ok {\n\t\tpanic \"variable ${name} is undefined\"\n\t}\n\tvars[name] = chg(oldv)\n}\n\nif len(os.Args) < 2 {\n\techo \"Usage: tpl-pseudo <file>\"\n\treturn\n}\n\nvars = {}\nconsts = {}\n\ncl := tpl`\n\nstmts = *stmtEOS => {\n\treturn delay.stmtList(self)\n}\n\nstmtEOS = stmt \";\" => {\n\treturn self[0]\n}\n\nstmt = varStmt | constStmt | outputStmt | inputStmt | ifStmt | whileStmt | untilStmt | assignStmt\n\nvarStmt = \"DECLARE\" (IDENT % \",\") \":\" typeExpr => {\n\tnamelist := self[1].([]any)\n\ttypeVal := self[3]\n\treturn delay.rangeOp(namelist, v => {\n\t\tt := v.(*tpl.Token)\n\t\tname := t.Lit\n\t\tif exists(name) {\n\t\t\ttpl.panic t.Pos, \"${name} exists\"\n\t\t}\n\t\tvars[name] = typeVal\n\t})\n}\n\nconstStmt = \"CONSTANT\" IDENT \"<-\" expr => {\n\tt := self[1].(*tpl.Token)\n\treturn delay.evalOp(self[3], v => {\n\t\tname := t.Lit\n\t\tif exists(name) {\n\t\t\ttpl.panic t.Pos, \"${name} exists\"\n\t\t}\n\t\tconsts[name] = v\n\t})\n}\n\nassignStmt = IDENT \"<-\" expr => {\n\tt := self[0].(*tpl.Token)\n\treturn delay.setValue(t.Lit, setValue, self[2])\n}\n\ninputStmt = \"INPUT\" IDENT => {\n\tt := self[1].(*tpl.Token)\n\treturn delay.chgValue(t.Lit, chgValue, oldv => {\n\t\tv := reflect.new(type(oldv))\n\t\tfmt.scanln(v.Interface())!\n\t\treturn v.elem.Interface()\n\t})\n}\n\noutputStmt = \"OUTPUT\" (expr % \",\") => {\n\texprlist := self[1].([]any)\n\treturn delay.list(exprlist, vals => {\n\t\techo vals...\n\t})\n}\n\nifStmt = \"IF\" expr \"THEN\" \";\" stmts ?(\"ELSE\" \";\" stmts) \"ENDIF\" => {\n\treturn delay.ifElse(self[1], self[4], self[5], 2)\n}\n\nwhileStmt = \"WHILE\" expr \"DO\" \";\" stmts \"ENDWHILE\" => {\n\treturn delay.while(self[1], self[4])\n}\n\nuntilStmt = \"REPEAT\" \";\" stmts \"UNTIL\" expr => {\n\treturn delay.repeatUntil(self[2], self[4])\n}\n\ntypeExpr = integer | real | string | boolean\n\ninteger = \"INTEGER\" => {\n\treturn 0\n}\n\nreal = \"REAL\" => {\n\treturn 0.0\n}\n\nstring = \"STRING\" => {\n\treturn \"\"\n}\n\nboolean = \"BOOLEAN\" => {\n\treturn false\n}\n\nexpr = cmpExpr % and % or => {\n\treturn tpl.binaryOp(true, self, (op, x, y) => {\n\t\treturn delay.logicOp(op.Tok, x, y)\n\t})\n}\n\nand = \"AND\" => {\n\treturn &tpl.Token{Tok: token.LAND}\n}\n\nor = \"OR\" => {\n\treturn &tpl.Token{Tok: token.LOR}\n}\n\ncmpExpr = mathExpr % (\"<\" | \"<=\" | \">\" | \">=\" | \"=\" | \"<>\") => {\n\treturn tpl.binaryOp(false, self, (op, x, y) => {\n\t\treturn delay.compare(op.Tok, x, y)\n\t})\n}\n\nmathExpr = operand % (\"*\" | \"/\" | \"%\") % (\"+\" | \"-\") => {\n\treturn tpl.binaryOp(true, self, (op, x, y) => {\n\t\treturn delay.mathOp(op.Tok, x, y)\n\t})\n}\n\noperand = basicLit | parenExpr | unaryExpr | identOrCall\n\nidentOrCall = IDENT ?(\"(\" ?(expr % \",\") \")\") => {\n\tt := self[0].(*tpl.Token)\n\tif params := self[1]; params != nil {\n\t\treturn delay.call(true, t.Lit, params.([]any)[1])\n\t}\n\treturn delay.valueOf(t.Lit, value)\n}\n\nparenExpr = \"(\" expr \")\" => {\n\treturn self[1]\n}\n\nunaryExpr = (\"-\" | \"+\" | \"!\") operand => {\n\top := self[0].(*tpl.Token)\n\treturn delay.unaryOp(op.Tok, self[1])\n}\n\nbasicLit = intVal | floatVal | stringVal | true | false\n\ntrue = \"true\" => {\n\treturn true\n}\n\nfalse = \"false\" => {\n\treturn false\n}\n\nstringVal = STRING => {\n\treturn self.(*tpl.Token).Lit.unquote!\n}\n\nfloatVal = FLOAT => {\n\treturn self.(*tpl.Token).Lit.float!\n}\n\nintVal = INT => {\n\treturn self.(*tpl.Token).Lit.int!\n}\n`!\n\ndelay.initUniverse \"builtin\", \"math\", \"time\"\n\ne, err := cl.parse(os.Args[1], nil, nil)\nif err != nil {\n\tfprintln os.Stderr, err\n} else {\n\tdelay.eval e\n}\n"
  },
  {
    "path": "demo/tpl-vcalc/variant_calc.xgo",
    "content": "import (\n\t\"os\"\n\t\"xgo/tpl\"\n\t\"xgo/tpl/token\"\n\t\"xgo/tpl/variant\"\n\n\t_ \"xgo/tpl/variant/builtin\"\n\t_ \"xgo/tpl/variant/math\"\n\t_ \"xgo/tpl/variant/time\"\n)\n\ncl := tpl`\n\nexpr = cmpExpr % and % or => {\n\treturn tpl.binaryOp(true, self, (op, x, y) => {\n\t\treturn variant.logicOp(op.Tok, x, y)\n\t})\n}\n\nand = \"and\" | \"AND\" | \"&&\" => {\n\treturn &tpl.Token{Tok: token.LAND}\n}\n\nor = \"or\" | \"OR\" | \"||\" => {\n\treturn &tpl.Token{Tok: token.LOR}\n}\n\ncmpExpr = mathExpr % (\"==\" | \"=\" | \"!=\" | \"<>\" | \"<\" | \"<=\" | \">\" | \">=\") => {\n\treturn tpl.binaryOp(false, self, (op, x, y) => {\n\t\treturn variant.compare(op.Tok, x, y)\n\t})\n}\n\nmathExpr = operand % (\"*\" | \"/\" | \"%\") % (\"+\" | \"-\") => {\n\treturn tpl.binaryOp(true, self, (op, x, y) => {\n\t\treturn variant.mathOp(op.Tok, x, y)\n\t})\n}\n\noperand = basicLit | parenExpr | unaryExpr | callExpr\n\nparenExpr = \"(\" expr \")\" => {\n\treturn self[1]\n}\n\nunaryExpr = (\"-\" | \"+\" | \"!\") operand => {\n\top := self[0].(*tpl.Token)\n\treturn variant.unaryOp(op.Tok, self[1])\n}\n\ncallExpr = IDENT \"(\" ?(expr % \",\") \")\" => {\n\tfn := self[0].(*tpl.Token).Lit\n\treturn variant.call(true, fn, self[2])\n}\n\nbasicLit = intVal | floatVal | stringVal | true | false\n\ntrue = \"true\" => {\n\treturn true\n}\n\nfalse = \"false\" => {\n\treturn false\n}\n\nstringVal = STRING => {\n\treturn self.(*tpl.Token).Lit.unquote!\n}\n\nfloatVal = FLOAT => {\n\treturn self.(*tpl.Token).Lit.float!\n}\n\nintVal = INT => {\n\treturn self.(*tpl.Token).Lit.int!\n}\n`!\n\nvariant.initUniverse \"builtin\", \"math\", \"time\"\n\nprint \"> \"\nfor line in os.Stdin {\n\te, err := cl.parseExpr(line, nil)\n\tif err != nil {\n\t\tprint err, \"\\n> \"\n\t} else {\n\t\tprint e, \"\\n> \"\n\t}\n}\n"
  },
  {
    "path": "demo/tupletype/tuple.xgo",
    "content": "type Point (x, y int)\n\npt := Point(2, 3)\necho pt.x, pt.y\n\npt = (100, 200)\necho pt\n\npt2 := Point(pt)\necho pt2\n\npt3 := Point(y = 5, x = 3)\necho pt3.x, pt3.y\n\npts := make(map[Point]string)\npts[(0, 0)] = \"origin\"\necho pts\n"
  },
  {
    "path": "demo/typeasparamsfunc/col.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\nfunc XGox_Col[T any](name string) {\n\tfmt.Printf(\"%v: %s\\n\", reflect.TypeOf((*T)(nil)).Elem(), name)\n}\n"
  },
  {
    "path": "demo/typeasparamsfunc/typeAsParamsFunc.xgo",
    "content": "col string, \"name\"\ncol int, \"age\"\n"
  },
  {
    "path": "demo/typeasparamsmethod/col.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype basetype interface {\n\tint | string\n}\n\ntype Table struct {\n}\n\nfunc XGot_Table_XGox_Col__0[T basetype](p *Table, name string) {\n\tfmt.Printf(\"XGot_Table_XGox_Col__0 %v: %s\\n\", reflect.TypeOf((*T)(nil)).Elem(), name)\n}\n\nfunc XGot_Table_XGox_Col__1[Array any](p *Table, name string) {\n\tfmt.Printf(\"XGot_Table_XGox_Col__1 %v: %s\\n\", reflect.TypeOf((*Array)(nil)).Elem(), name)\n}\n"
  },
  {
    "path": "demo/typeasparamsmethod/typeAsParamsMethod.xgo",
    "content": "var tbl Table\n\ntbl.col int, \"age\"\ntbl.col bool, \"male\"\n"
  },
  {
    "path": "demo/typeparamscast/foo.go",
    "content": "package main\n\ntype M = map[string]any\n\ntype basetype interface {\n\tstring | int | bool | float64\n}\n\ntype Var__0[T basetype] struct {\n\tVal T\n}\n\ntype Var__1[T map[string]any] struct {\n\tVal T\n}\n\nfunc XGox_Var_Cast__0[T basetype]() *Var__0[T] {\n\treturn new(Var__0[T])\n}\n\nfunc XGox_Var_Cast__1[T map[string]any]() *Var__1[T] {\n\treturn new(Var__1[T])\n}\n"
  },
  {
    "path": "demo/typeparamscast/typecast.xgo",
    "content": "println Var(int)\n"
  },
  {
    "path": "demo/unit-test/foo.xgo",
    "content": "func foo(v int) int {\n\treturn v * 2\n}\n"
  },
  {
    "path": "demo/unit-test/foo_test.gox",
    "content": "if v := foo(50); v != 100 {\n\tt.error \"foo(50) ret: ${v}\"\n}\n\nt.run \"foo -10\", t => {\n\tif foo(-10) != -20 {\n\t\tt.fatal \"foo(-10) != -20\"\n\t}\n}\n\nt.run \"foo 0\", t => {\n\tif foo(0) != 0 {\n\t\tt.fatal \"foo(0) != 0\"\n\t}\n}\n"
  },
  {
    "path": "demo/unitliteral/unitlit.xgo",
    "content": "import \"time\"\n\necho \"Let's wait for a second\"\ntime.sleep 1s\necho \"Hello, world\"\n"
  },
  {
    "path": "demo/xgo-calc/calc.xgo",
    "content": "import (\n\t\"math\"\n\t\"os\"\n\t\"xgo/ast\"\n\t\"xgo/parser\"\n\t\"xgo/token\"\n)\n\nfunc calc(e ast.Expr) float64 {\n\tswitch e := e.(type) {\n\tcase *ast.BasicLit:\n\t\treturn e.Value.float!\n\tcase *ast.BinaryExpr:\n\t\tswitch e.Op {\n\t\tcase token.ADD:\n\t\t\treturn calc(e.X) + calc(e.Y)\n\t\tcase token.SUB:\n\t\t\treturn calc(e.X) - calc(e.Y)\n\t\tcase token.MUL:\n\t\t\treturn calc(e.X) * calc(e.Y)\n\t\tcase token.QUO:\n\t\t\treturn calc(e.X) / calc(e.Y)\n\t\t}\n\t\tpanic(\"unknown binary operator\")\n\tcase *ast.CallExpr:\n\t\tswitch e.Fun.(*ast.Ident).Name {\n\t\tcase \"sin\":\n\t\t\treturn math.Sin(calc(e.Args[0]))\n\t\tcase \"cos\":\n\t\t\treturn math.Cos(calc(e.Args[0]))\n\t\tcase \"pow\":\n\t\t\treturn math.Pow(calc(e.Args[0]), calc(e.Args[1]))\n\t\t}\n\t\tpanic(\"unknown function\")\n\tcase *ast.ParenExpr:\n\t\treturn calc(e.X)\n\tcase *ast.UnaryExpr:\n\t\tswitch e.Op {\n\t\tcase token.SUB:\n\t\t\treturn -calc(e.X)\n\t\t}\n\t\tpanic(\"unknown unary operator\")\n\t}\n\tpanic(\"unknown expression\")\n}\n\nprint \"> \"\nfor line in os.Stdin {\n\te, err := parser.parseExpr(line)\n\tif err != nil {\n\t\tprint \"error: ${err}\\n> \"\n\t} else {\n\t\tprint \"${calc(e)}\\n> \"\n\t}\n}\n"
  },
  {
    "path": "demo/xgo-parser/parser.xgo",
    "content": "import (\n\t\"xgo/ast\"\n\t\"xgo/parser\"\n)\n\ne := parser.parseExpr(\"10 + 3.2\")!.(*ast.BinaryExpr)\necho e.X, e.Op, e.Y\n"
  },
  {
    "path": "demo/xgo-sample/a.xgo",
    "content": "package main\n\nfunc a() {\n\tprintln(\"a\")\n}\n\nfunc Ab() {\n\tprintln(\"ab\")\n}\n"
  },
  {
    "path": "demo/xgo-sample/b.xgo",
    "content": "package main\n\nimport (\n\tab \"github.com/goplus/xgo/demo/xgo-sample/cpkag/b\"\n)\n\na()\nAb()\nab.Ab()\n"
  },
  {
    "path": "demo/xgo-sample/cpkag/b/ab.go",
    "content": "package ab\n\nfunc Ab() {\n\tprintln(\"ab\")\n}\n"
  },
  {
    "path": "demo/xgo-scanner/rpncalc/rpncalc.xgo",
    "content": "import (\n\t\"os\"\n\t\"xgo/scanner\"\n\t\"xgo/token\"\n)\n\nfunc calc(expr string) string {\n\tvar vals []float64\n\ts := scanner.new(expr, nil, 0)\n\tfor {\n\t\tpos, tok, lit := s.scan()\n\t\tswitch tok {\n\t\tcase token.INT, token.FLOAT:\n\t\t\tvals <- lit.float!\n\t\tcase token.ADD, token.SUB, token.MUL, token.QUO:\n\t\t\tx := len(vals) - 1\n\t\t\tswitch tok {\n\t\t\tcase token.ADD:\n\t\t\t\tvals[x-1] += vals[x]\n\t\t\tcase token.SUB:\n\t\t\t\tvals[x-1] -= vals[x]\n\t\t\tcase token.MUL:\n\t\t\t\tvals[x-1] *= vals[x]\n\t\t\tcase token.QUO:\n\t\t\t\tvals[x-1] /= vals[x]\n\t\t\t}\n\t\t\tvals = vals[:x]\n\t\tcase token.EOF, token.SEMICOLON:\n\t\t\treturn vals[0].string\n\t\tdefault:\n\t\t\treturn \"${pos}: invalid token ${tok}\"\n\t\t}\n\t}\n}\n\nprint \"> \"\nfor line in os.Stdin {\n\tprint \"${calc(line)}\\n> \"\n}\n"
  },
  {
    "path": "demo/xgo-scanner/scanner.xgo",
    "content": "import \"xgo/scanner\"\n\ns := scanner.new(\"10 + 3.2\", nil, 0)\n_, _, lit1 := s.scan()\n_, tok2, _ := s.scan()\n_, _, lit3 := s.scan()\necho lit1, tok2, lit3\n"
  },
  {
    "path": "demo/xgo-scanner/simplecalc/calc.xgo",
    "content": "import (\n\t\"os\"\n\t\"xgo/scanner\"\n\t\"xgo/token\"\n)\n\nfunc calc(expr string) string {\n\tvar (\n\t\tops  []token.Token\n\t\tvals []float64\n\t)\n\tprec := {\n\t\ttoken.ADD: 1,\n\t\ttoken.SUB: 1,\n\t\ttoken.MUL: 2,\n\t\ttoken.QUO: 2,\n\t}\n\ts := scanner.new(expr, nil, 0)\n\tn := 0       // op count\n\trv := true   // require value\n\tneg := false // negative number\n\tfor {\n\t\tpos, tok, lit := s.scan()\n\t\tswitch tok {\n\t\tcase token.INT, token.FLOAT:\n\t\t\tif !rv {\n\t\t\t\treturn \"${pos}: require operator\"\n\t\t\t}\n\t\t\tv := lit.float!\n\t\t\tif neg {\n\t\t\t\tv = -v\n\t\t\t\tneg = false\n\t\t\t}\n\t\t\tvals <- v\n\t\t\trv = false\n\t\tcase token.ADD, token.SUB, token.MUL, token.QUO, token.SEMICOLON:\n\t\t\tif rv {\n\t\t\t\tif tok == token.SUB {\n\t\t\t\t\tneg = !neg // toggle negative flag\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn \"${pos}: require value\"\n\t\t\t}\n\t\t\tfor n > 0 && prec[ops[n-1]] >= prec[tok] {\n\t\t\t\tx := len(vals) - 1\n\t\t\t\tswitch ops[n-1] {\n\t\t\t\tcase token.ADD:\n\t\t\t\t\tvals[x-1] += vals[x]\n\t\t\t\tcase token.SUB:\n\t\t\t\t\tvals[x-1] -= vals[x]\n\t\t\t\tcase token.MUL:\n\t\t\t\t\tvals[x-1] *= vals[x]\n\t\t\t\tcase token.QUO:\n\t\t\t\t\tvals[x-1] /= vals[x]\n\t\t\t\t}\n\t\t\t\tvals = vals[:x]\n\t\t\t\tn--\n\t\t\t}\n\t\t\tif tok == token.SEMICOLON {\n\t\t\t\treturn vals[0].string\n\t\t\t}\n\t\t\tops = append(ops[:n], tok)\n\t\t\trv = true\n\t\t\tn++\n\t\tdefault:\n\t\t\treturn \"${pos}: invalid token ${tok}\"\n\t\t}\n\t}\n}\n\nprint \"> \"\nfor line in os.Stdin {\n\tprint \"${calc(line)}\\n> \"\n}\n"
  },
  {
    "path": "demo/xgo-typeof/typeof.xgo",
    "content": "echo type([1]), type(\"Hi\")\n"
  },
  {
    "path": "doc/_testdata/gopoFn/in.go",
    "content": "package foo\n\nconst GopPackage = true\n\nconst Gopo_Add = \"AddInt,,AddString\"\n\n// Add doc\nfunc Add__1(a, b float64) float64 {\n\treturn 0\n}\n\n// AddInt doc\nfunc AddInt(a, b int) int {\n\treturn 0\n}\n\n// AddString doc\nfunc AddString(a, b string) string {\n\treturn \"\"\n}\n"
  },
  {
    "path": "doc/_testdata/gopoFn/out.expect",
    "content": "== Func Add ==\nDoc: AddInt doc\n\nfunc Add(a, b int) int\n== Func Add ==\nDoc: Add doc\n\nfunc Add(a, b float64) float64\n== Func Add ==\nDoc: AddString doc\n\nfunc Add(a, b string) string\n== Func AddInt ==\nDoc: AddInt doc\n\nfunc AddInt(a, b int) int\n== Func AddString ==\nDoc: AddString doc\n\nfunc AddString(a, b string) string\n"
  },
  {
    "path": "doc/_testdata/gopoMethod/in.go",
    "content": "package foo\n\nconst GopPackage = true\n\nconst Gopo__T__Gop_Add = \".AddInt,AddString\"\n\ntype T int\n\n// AddInt doc\nfunc (p T) AddInt(b int) *T {\n\treturn nil\n}\n\n// AddString doc\nfunc AddString(this *T, b string) *T {\n\treturn nil\n}\n"
  },
  {
    "path": "doc/_testdata/gopoMethod/out.expect",
    "content": "== Type T ==\n- Func AddString -\nDoc: AddString doc\n\nfunc AddString(this *T, b string) *T\n- Method AddInt -\nRecv: T\nDoc: AddInt doc\n\nfunc (p T) AddInt(b int) *T\n- Method Gop_Add -\nRecv: T\nDoc: AddInt doc\n\nfunc (p T) Gop_Add(b int) *T\n- Method Gop_Add -\nRecv: *T\nDoc: AddString doc\n\nfunc (this *T) Gop_Add(b string) *T\n"
  },
  {
    "path": "doc/_testdata/overloadFn/in.go",
    "content": "package foo\n\nconst GopPackage = true\n\n// Bar doc\nfunc Bar__0() {\n}\n"
  },
  {
    "path": "doc/_testdata/overloadFn/out.expect",
    "content": "== Func Bar ==\nDoc: Bar doc\n\nfunc Bar()\n"
  },
  {
    "path": "doc/_testdata/overloadMethod/in.go",
    "content": "package foo\n\nconst GopPackage = true\n\ntype T int\n\n// Bar doc 1\nfunc (p *T) Bar__0() {\n}\n\n// Bar doc 2\nfunc (t T) Bar__1() {\n}\n"
  },
  {
    "path": "doc/_testdata/overloadMethod/out.expect",
    "content": "== Type T ==\n- Method Bar -\nRecv: *T\nDoc: Bar doc 1\n\nfunc (p *T) Bar()\n- Method Bar -\nRecv: T\nDoc: Bar doc 2\n\nfunc (t T) Bar()\n"
  },
  {
    "path": "doc/_testdata/xgoOverloadFn/in.go",
    "content": "package foo\n\nconst XGoPackage = true\n\n// Bar doc\nfunc Bar__0() {\n}\n"
  },
  {
    "path": "doc/_testdata/xgoOverloadFn/out.expect",
    "content": "== Func Bar ==\nDoc: Bar doc\n\nfunc Bar()\n"
  },
  {
    "path": "doc/_testdata/xgoOverloadMethod/in.go",
    "content": "package foo\n\nconst XGoPackage = true\n\ntype T int\n\n// Bar doc 1\nfunc (p *T) Bar__0() {\n}\n\n// Bar doc 2\nfunc (t T) Bar__1() {\n}\n"
  },
  {
    "path": "doc/_testdata/xgoOverloadMethod/out.expect",
    "content": "== Type T ==\n- Method Bar -\nRecv: *T\nDoc: Bar doc 1\n\nfunc (p *T) Bar()\n- Method Bar -\nRecv: T\nDoc: Bar doc 2\n\nfunc (t T) Bar()\n"
  },
  {
    "path": "doc/builtin.md",
    "content": "# Built‐in Functions\n\nXGo extends Go's standard built-in functions with additional capabilities for common operations. This document provides a comprehensive reference for all built-in functions available in XGo.\n\n## Standard Go Built-in Functions\n\nXGo supports all standard Go built-in functions:\n\n### Memory and Data Structure Operations\n\n**`append(slice []Type, elems ...Type) []Type`**\n\nAppends elements to the end of a slice. Returns the updated slice.\n\n```go\nnumbers := []int{1, 2, 3}\nnumbers = append(numbers, 4, 5)\necho numbers  // Output: [1 2 3 4 5]\n```\n\n**`len(v Type) int`**\n\nReturns the length of arrays, slices, maps, strings, or channels.\n\n```go\necho len(\"Hello\")      // Output: 5\necho len([]int{1,2,3}) // Output: 3\n```\n\n**`cap(v Type) int`**\n\nReturns the capacity of arrays, slices, or channels.\n\n```go\ns := make([]int, 3, 5)\necho len(s)  // Output: 3\necho cap(s)  // Output: 5\n```\n\n**`make(t Type, size ...IntegerType) Type`**\n\nAllocates and initializes slices, maps, or channels.\n\n```go\nslice := make([]int, 5, 10)  // length 5, capacity 10\nm := make(map[string]int)     // empty map\nch := make(chan int, 10)      // buffered channel\n```\n\n**`copy(dst, src []Type) int`**\n\nCopies elements from source to destination slice. Returns the number of elements copied.\n\n```go\nsrc := []int{1, 2, 3}\ndst := make([]int, 3)\nn := copy(dst, src)\necho n    // Output: 3\necho dst  // Output: [1 2 3]\n```\n\n**`delete(m map[Type]Type1, key Type)`**\n\nDeletes a key from a map.\n\n```go\nm := map[string]int{\"a\": 1, \"b\": 2}\ndelete(m, \"a\")\necho m  // Output: map[b:2]\n```\n\n**`clear[T ~[]Type | ~map[Type]Type1](t T)`**\n\nClears all entries in maps or sets slice elements to zero values.\n\n```go\nm := map[string]int{\"a\": 1, \"b\": 2}\nclear(m)\necho len(m)  // Output: 0\n```\n\n**`new(Type) *Type`**\n\nAllocates memory and returns a pointer to zero value.\n\n```go\np := new(int)\necho *p  // Output: 0\n```\n\n### Numeric Operations\n\n**`max[T cmp.Ordered](x T, y ...T) T`**\n\nReturns the largest value among arguments.\n\n```go\necho max(1, 5, 3, 9, 2)  // Output: 9\necho max(3.14, 2.71)     // Output: 3.14\n```\n\n**`min[T cmp.Ordered](x T, y ...T) T`**\n\nReturns the smallest value among arguments.\n\n```go\necho min(1, 5, 3, 9, 2)  // Output: 1\necho min(3.14, 2.71)     // Output: 2.71\n```\n\n**`complex(r, i FloatType) ComplexType`**\n\nConstructs a complex number from real and imaginary parts.\n\n```go\nc := complex(3.0, 4.0)\necho c  // Output: (3+4i)\n```\n\n**`real(c ComplexType) FloatType`**\n\nReturns the real part of a complex number.\n\n```go\nc := 3 + 4i\necho real(c)  // Output: 3\n```\n\n**`imag(c ComplexType) FloatType`**\n\nReturns the imaginary part of a complex number.\n\n```go\nc := 3 + 4i\necho imag(c)  // Output: 4\n```\n\n### Control Flow\n\n**`panic(v any)`**\n\nStops normal execution and begins panicking.\n\n```go\nif value < 0 {\n    panic(\"negative value not allowed\")\n}\n```\n\n**`recover() any`**\n\nRecovers from a panic in deferred functions.\n\n```go\ndefer func() {\n    if r := recover(); r != nil {\n        println(\"Recovered from:\", r)\n    }\n}()\n```\n\n### Channel Operations\n\n**`close(c chan<- Type)`**\n\nCloses a channel.\n\n```go\nch := make(chan int)\nclose(ch)\n```\n\n## I/O and Formatting Functions\n\nXGo provides enhanced I/O functions with a cleaner API:\n\n### Standard Output\n\n**`Echo(a ...any) (n int, err error)`**\n\nFormats and writes to standard output with spaces between operands and a newline appended.\n\n```go\necho \"Hello\", \"World\"  // Output: Hello World\necho 42, true, 3.14    // Output: 42 true 3.14\n```\n\n**`Print(a ...any) (n int, err error)`**\n\nWrites to standard output, adding spaces only when neither operand is a string.\n\n```go\nprint(\"Hello\", \"World\")  // Output: HelloWorld\nprint(1, 2, 3)           // Output: 1 2 3\n```\n\n**`Println(a ...any) (n int, err error)`**\n\nWrites to standard output with spaces between operands and a newline appended.\n\n```go\nprintln(\"Hello\", \"World\")  // Output: Hello World\n```\n\n**`Printf(format string, a ...any) (n int, err error)`**\n\nFormats according to format specifier and writes to standard output.\n\n```go\nprintf(\"Name: %s, Age: %d\\n\", \"Alice\", 30)\n// Output: Name: Alice, Age: 30\n```\n\n### String Formatting\n\n**`Sprint(a ...any) string`**\n\nReturns formatted string, adding spaces when neither operand is a string.\n\n```go\ns := sprint(\"Hello\", \"World\")\necho s  // Output: HelloWorld\n```\n\n**`Sprintln(a ...any) string`**\n\nReturns formatted string with spaces between operands and a newline.\n\n```go\ns := sprintln(\"Hello\", \"World\")\necho s  // Output: Hello World\n```\n\n**`Sprintf(format string, a ...any) string`**\n\nReturns string formatted according to format specifier.\n\n```go\ns := sprintf(\"Age: %d\", 25)\necho s  // Output: Age: 25\n```\n\n### Writer Operations\n\n**`Fprint(w io.Writer, a ...any) (n int, err error)`**\n\nWrites to specified writer.\n\n```go\nfile, _ := create(\"output.txt\")\nfprint(file, \"Hello\", \"World\")\n```\n\n**`Fprintln(w io.Writer, a ...any) (n int, err error)`**\n\nWrites to specified writer with newline.\n\n```go\nfile, _ := create(\"output.txt\")\nfprintln(file, \"Hello\", \"World\")\n```\n\n**`Fprintf(w io.Writer, format string, a ...any) (n int, err error)`**\n\nWrites formatted output to specified writer.\n\n```go\nfile, _ := create(\"output.txt\")\nfprintf(file, \"Count: %d\\n\", 42)\n```\n\n### Error Operations\n\n**`Errorf(format string, a ...any) error`**\n\nCreates formatted error. Supports `%w` verb for error wrapping.\n\n```go\nerr := errorf(\"failed to process: %w\", originalError)\n```\n\n**`Errorln(args ...any)`**\n\nFormats and prints to standard error.\n\n```go\nerrorln(\"Warning:\", \"Something went wrong\")\n```\n\n**`Fatal(args ...any)`**\n\nFormats and prints to standard error (typically exits program).\n\n```go\nfatal(\"Critical error occurred\")\n```\n\n## File Operations\n\n**`Open(name string) (*os.File, error)`**\n\nOpens file for reading with O_RDONLY mode.\n\n```go\nfile, err := open(\"data.txt\")\nif err != nil {\n    errorln(err)\n}\ndefer file.close\n```\n\n**`Create(name string) (*os.File, error)`**\n\nCreates or truncates file with mode 0o666.\n\n```go\nfile, err := create(\"output.txt\")\nif err != nil {\n    errorln(err)\n}\ndefer file.close\n```\n\n## Type Reflection\n\n**`Type(i any) reflect.Type`**\n\nReturns the reflection Type representing the dynamic type of i.\n\n```go\nt := type(42)\necho t.name  // Output: int\n\ns := \"hello\"\necho type(s).name  // Output: string\n```\n\n## Line Reading\n\nXGo provides convenient line reading utilities:\n\n**`Lines(r io.Reader) osx.LineReader`**\n\nReturns a LineReader for reading lines.\n\n```go\nfile, _ := open(\"data.txt\")\nfor line in lines(file) {\n    echo line\n}\n```\n\n**`Blines(r io.Reader) osx.BLineReader`**\n\nReturns a BLineReader for reading lines as byte slices.\n\n```go\nfile, _ := open(\"data.txt\")\nfor line in blines(file) {\n    echo string(line)\n}\n```\n\n**`(r io.Reader).XGo_Enum() osx.LineIter`**\n\nReturns a LineIter for iterating over lines (supports `for in` syntax).\n\n```go\nfile, _ := open(\"data.txt\")\nfor line in file {\n    echo line\n}\n```\n\n## String Methods\n\nWhen working with strings, XGo provides convenient method syntax for common operations.\n\n### Length and Counting\n\n**`(s string).Len() int`**\n\nReturns the number of bytes in the string.\n\n```go\necho \"Hello\".len  // Output: 5\n```\n\n**`(s string).Count(substr string) int`**\n\nCounts non-overlapping instances of substring.\n\n```go\necho \"hello world\".count(\"l\")  // Output: 3\necho \"aaaa\".count(\"aa\")        // Output: 2\n```\n\n### Case Conversion\n\n**`(s string).ToUpper() string`**\n\nConverts all letters to uppercase.\n\n```go\necho \"Hello\".toUpper  // Output: HELLO\n```\n\n**`(s string).ToLower() string`**\n\nConverts all letters to lowercase.\n\n```go\necho \"Hello\".toLower  // Output: hello\n```\n\n**`(s string).ToTitle() string`**\n\nConverts all letters to title case.\n\n```go\necho \"hello world\".toTitle  // Output: HELLO WORLD\n```\n\n**`(s string).Capitalize() string`**\n\nCapitalizes the first letter only.\n\n```go\necho \"hello world\".capitalize  // Output: Hello world\n```\n\n### String Manipulation\n\n**`(s string).Repeat(count int) string`**\n\nReturns string repeated count times.\n\n```go\necho \"Ha\".repeat(3)  // Output: HaHaHa\n```\n\n**`(s string).ReplaceAll(old, new string) string`**\n\nReplaces all non-overlapping instances of old with new.\n\n```go\necho \"Hello\".replaceAll(\"l\", \"L\")  // Output: HeLLo\n```\n\n**`(s string).Replace(old, new string, n int) string`**\n\nReturns a copy of the string with the first `n` non-overlapping instances of `old` replaced by `new`. If `n < 0`, there is no limit on the number of replacements.\n\n```go\ns := \"hello world\"\nresult := s.replace(\"world\", \"XGo\", -1)\necho result  // Output: hello XGo\n```\n\n### Trimming\n\n**`(s string).Trim(cutset string) string`**\n\nRemoves leading and trailing characters from cutset.\n\n```go\necho \"  hello  \".trim(\" \")  // Output: hello\necho \"!!hello!!\".trim(\"!\")  // Output: hello\n```\n\n**`(s string).TrimSpace() string`**\n\nRemoves leading and trailing whitespace.\n\n```go\necho \"  hello  \".trimSpace  // Output: hello\n```\n\n**`(s string).TrimLeft(cutset string) string`**\n\nRemoves leading characters from cutset.\n\n```go\necho \"###hello\".trimLeft(\"#\")  // Output: hello\n```\n\n**`(s string).TrimRight(cutset string) string`**\n\nRemoves trailing characters from cutset.\n\n```go\necho \"hello###\".trimRight(\"#\")  // Output: hello\n```\n\n**`(s string).TrimPrefix(prefix string) string`**\n\nRemoves leading prefix if present.\n\n```go\necho \"Hello World\".trimPrefix(\"Hello \")  // Output: World\n```\n\n**`(s string).TrimSuffix(suffix string) string`**\n\nRemoves trailing suffix if present.\n\n```go\necho \"file.txt\".trimSuffix(\".txt\")  // Output: file\n```\n\n### Splitting\n\n**`(s string).Fields() []string`**\n\nSplits string around whitespace.\n\n```go\necho \"hello world xgo\".fields  // Output: [hello world xgo]\n```\n\n**`(s string).Split(sep string) []string`**\n\nSplits string around separator.\n\n```go\necho \"a,b,c\".split(\",\")  // Output: [a b c]\n```\n\n**`(s string).SplitN(sep string, n int) []string`**\n\nSplits string with count limit.\n\n```go\necho \"a-b-c-d\".splitN(\"-\", 2)  // Output: [a b-c-d]\n```\n\n**`(s string).SplitAfter(sep string) []string`**\n\nSplits after each separator.\n\n```go\necho \"a,b,c\".splitAfter(\",\")  // Output: [a, b, c]\n```\n\n**`(s string).SplitAfterN(sep string, n int) []string`**\n\nSplits after separator with count limit.\n\n```go\necho \"a,b,c,d\".splitAfterN(\",\", 2)  // Output: [a, b,c,d]\n```\n\n### Searching\n\n**`(s string).Index(substr string) int`**\n\nReturns index of first instance of substring, or -1 if not found.\n\n```go\necho \"hello\".index(\"ll\")  // Output: 2\necho \"hello\".index(\"x\")   // Output: -1\n```\n\n**`(s string).IndexByte(c byte) int`**\n\nReturns index of first instance of byte.\n\n```go\necho \"hello\".indexByte('l')  // Output: 2\n```\n\n**`(s string).IndexRune(r rune) int`**\n\nReturns index of first instance of rune.\n\n```go\necho \"hello\".indexRune('o')  // Output: 4\n```\n\n**`(s string).IndexAny(chars string) int`**\n\nReturns index of first instance of any character from chars.\n\n```go\necho \"hello\".indexAny(\"aeiou\")  // Output: 1 (finds 'e')\n```\n\n**`(s string).LastIndex(substr string) int`**\n\nReturns index of last instance of substring.\n\n```go\necho \"hello\".lastIndex(\"l\")  // Output: 3\n```\n\n**`(s string).LastIndexByte(c byte) int`**\n\nReturns index of last instance of byte.\n\n```go\necho \"hello\".lastIndexByte('l')  // Output: 3\n```\n\n**`(s string).LastIndexAny(chars string) int`**\n\nReturns index of last instance of any character.\n\n```go\necho \"hello\".lastIndexAny(\"aeiou\")  // Output: 4 (finds 'o')\n```\n\n### Testing\n\n**`(s string).Contains(substr string) bool`**\n\nReports whether substring is present.\n\n```go\necho \"hello\".contains(\"ll\")   // Output: true\necho \"hello\".contains(\"xyz\")  // Output: false\n```\n\n**`(s string).ContainsAny(chars string) bool`**\n\nReports whether any character from chars is present.\n\n```go\necho \"hello\".containsAny(\"aeiou\")  // Output: true\n```\n\n**`(s string).ContainsRune(r rune) bool`**\n\nReports whether rune is present.\n\n```go\necho \"hello\".containsRune('e')  // Output: true\n```\n\n**`(s string).HasPrefix(prefix string) bool`**\n\nReports whether string begins with prefix.\n\n```go\necho \"hello\".hasPrefix(\"hel\")  // Output: true\necho \"hello\".hasPrefix(\"bye\")  // Output: false\n```\n\n**`(s string).HasSuffix(suffix string) bool`**\n\nReports whether string ends with suffix.\n\n```go\necho \"hello.txt\".hasSuffix(\".txt\")  // Output: true\n```\n\n**`(s string).EqualFold(t string) bool`**\n\nReports case-insensitive equality.\n\n```go\necho \"Hello\".equalFold(\"hello\")  // Output: true\n```\n\n### Comparison\n\n**`(s string).Compare(b string) int`**\n\nReturns 0 if equal, -1 if less, +1 if greater.\n\n```go\necho \"abc\".compare(\"abc\")  // Output: 0\necho \"abc\".compare(\"xyz\")  // Output: -1\necho \"xyz\".compare(\"abc\")  // Output: 1\n```\n\n### Quoting\n\n**`(s string).Quote() string`**\n\nReturns double-quoted Go string literal.\n\n```go\necho \"hello\\nworld\".quote  // Output: \"hello\\nworld\"\n```\n\n**`(s string).Unquote() (string, error)`**\n\nInterprets string as a quoted Go string literal.\n\n```go\ns, err := `\"hello\\nworld\"`.unquote\necho s  // Output: hello\n        //         world\n```\n\n### Type Conversion\n\n**`(s string).Int() (int, error)`**\n\nParses string as base-10 integer.\n\n```go\nn, err := \"42\".int\nif err == nil {\n    echo n  // Output: 42\n}\n```\n\n**`(s string).Int64() (int64, error)`**\n\nParses string as 64-bit signed integer.\n\n```go\nn, err := \"9223372036854775807\".int64\nif err == nil {\n    echo n  // Output: 9223372036854775807\n}\n```\n\n**`(s string).Uint64() (uint64, error)`**\n\nParses string as 64-bit unsigned integer.\n\n```go\nn, err := \"18446744073709551615\".uint64\nif err == nil {\n    echo n  // Output: 18446744073709551615\n}\n```\n\n**`(s string).Float() (float64, error)`**\n\nParses string as 64-bit floating-point number.\n\n```go\nf, err := \"3.14159\".float\nif err == nil {\n    echo f  // Output: 3.14159\n}\n```\n\n## Numeric Type String Methods\n\nXGo provides String() methods for numeric types to easily convert numbers to strings.\n\n**`(i int).String() string`**\n\nConverts int to base-10 string.\n\n```go\necho (42).string  // Output: 42\n```\n\n**`(i int64).String() string`**\n\nConverts int64 to base-10 string.\n\n```go\nn := int64(123456789)\necho n.string  // Output: 123456789\n```\n\n**`(u uint64).String() string`**\n\nConverts uint64 to base-10 string.\n\n```go\nn := uint64(18446744073709551615)\necho n.string  // Output: 18446744073709551615\n```\n\n**`(f float64).String() string`**\n\nConverts float64 to string using format 'g' with precision -1.\n\n```go\necho (3.14159).string  // Output: 3.14159\n```\n\n## String Slice Methods\n\nXGo provides method syntax for operations on string slices, making batch operations more convenient.\n\n### Information\n\n**`(v []string).Len() int`**\n\nReturns the number of elements.\n\n```go\nwords := []string{\"hello\", \"world\"}\necho words.len  // Output: 2\n```\n\n**`(v []string).Cap() int`**\n\nReturns the capacity.\n\n```go\nwords := make([]string, 2, 5)\necho words.cap  // Output: 5\n```\n\n### Joining\n\n**`(v []string).Join(sep string) string`**\n\nConcatenates elements with separator.\n\n```go\nwords := []string{\"hello\", \"world\", \"xgo\"}\necho words.join(\" \")   // Output: hello world xgo\necho words.join(\", \")  // Output: hello, world, xgo\n```\n\n### Batch Case Conversions\n\n**`(v []string).Capitalize() []string`**\n\nCapitalizes first letter of each string.\n\n```go\nwords := []string{\"hello\", \"world\"}\necho words.capitalize  // Output: [Hello World]\n```\n\n**`(v []string).ToTitle() []string`**\n\nTitle-cases all strings.\n\n```go\nwords := []string{\"hello\", \"world\"}\necho words.toTitle  // Output: [HELLO WORLD]\n```\n\n**`(v []string).ToUpper() []string`**\n\nUpper-cases all strings.\n\n```go\nwords := []string{\"hello\", \"world\"}\necho words.toUpper  // Output: [HELLO WORLD]\n```\n\n**`(v []string).ToLower() []string`**\n\nLower-cases all strings.\n\n```go\nwords := []string{\"HELLO\", \"WORLD\"}\necho words.toLower  // Output: [hello world]\n```\n\n### Batch Manipulation\n\n**`(v []string).Repeat(count int) []string`**\n\nRepeats each string count times.\n\n```go\nwords := []string{\"ha\", \"ho\"}\necho words.repeat(3)  // Output: [hahaha hohoho]\n```\n\n**`(v []string).Replace(old, new string, n int) []string`**\n\nReplaces occurrences in each string.\n\n```go\nwords := []string{\"hello\", \"yellow\"}\necho words.replace(\"ll\", \"LL\", -1)  // Output: [heLLo yeLLow]\n```\n\n**`(v []string).ReplaceAll(old, new string) []string`**\n\nReplaces all occurrences in each string.\n\n```go\nwords := []string{\"hello\", \"yellow\"}\necho words.replaceAll(\"l\", \"L\")  // Output: [heLLo yeLLow]\n```\n\n### Batch Trimming\n\n**`(v []string).Trim(cutset string) []string`**\n\nTrims each string.\n\n```go\nwords := []string{\"  hello  \", \"  world  \"}\necho words.trim(\" \")  // Output: [hello world]\n```\n\n**`(v []string).TrimSpace() []string`**\n\nRemoves whitespace from each string.\n\n```go\nwords := []string{\"  hello  \", \"  world  \"}\necho words.trimSpace  // Output: [hello world]\n```\n\n**`(v []string).TrimLeft(cutset string) []string`**\n\nRemoves leading characters from each string.\n\n```go\nwords := []string{\"###hello\", \"###world\"}\necho words.trimLeft(\"#\")  // Output: [hello world]\n```\n\n**`(v []string).TrimRight(cutset string) []string`**\n\nRemoves trailing characters from each string.\n\n```go\nwords := []string{\"hello###\", \"world###\"}\necho words.trimRight(\"#\")  // Output: [hello world]\n```\n\n**`(v []string).TrimPrefix(prefix string) []string`**\n\nRemoves prefix from each string.\n\n```go\nwords := []string{\"Mr. John\", \"Mr. Smith\"}\necho words.trimPrefix(\"Mr. \")  // Output: [John Smith]\n```\n\n**`(v []string).TrimSuffix(suffix string) []string`**\n\nRemoves suffix from each string.\n\n```go\nfiles := []string{\"file1.txt\", \"file2.txt\"}\necho files.trimSuffix(\".txt\")  // Output: [file1 file2]\n```\n\n## Notes\n\n### Method Syntax\n\nXGo allows calling many standard library functions as methods on their first argument. This provides a more fluent API while maintaining compatibility with Go's standard library. For example:\n\n```go\n// Traditional Go style\ns := strings.ToUpper(\"hello\")\n\n// XGo method style\ns := \"hello\".toUpper\n```\n\nBoth styles work in XGo, giving you flexibility in how you write your code.\n\n### Performance Considerations\n\nWhen performing batch operations on string slices, the method syntax creates a new slice with transformed values:\n\n```go\n// Creates a new slice with all strings uppercased\nupper := words.toUpper\n\n// Original slice unchanged\necho words  // Original values\necho upper  // Uppercased values\n```\n\n### For In Loops\n\nXGo uses `for in` syntax for iterating over collections, which is more intuitive than Go's traditional `for range`:\n\n```go\n// Iterate over slice\nwords := []string{\"hello\", \"world\", \"xgo\"}\nfor word in words {\n    echo word\n}\n\n// Iterate over file lines\nfile, _ := open(\"data.txt\")\nfor line in file {\n    echo line\n}\n```\n"
  },
  {
    "path": "doc/classfile.md",
    "content": "XGo Classfiles\n=====\n\n```\nOne language can change the world.\nXGo is a \"DSL\" for all domains.\n```\n\nRob Pike once said that if he could only introduce one feature to Go, he would choose `interface` instead of `goroutine`. `classfile` (and `class framework`) is as important to XGo as `interface` is to Go.\n\nIn the design philosophy of XGo, we do not recommend `DSL` (Domain Specific Language). But `SDF` (Specific Domain Friendliness) is very important. The XGo philosophy about `SDF` is:\n\n```\nDon't define a language for specific domain.\nAbstract domain knowledge for it.\n```\n\nXGo introduces `classfile` and `class framework` to abstract domain knowledge.\n\n* STEM Education: [spx: An XGo 2D Game Engine](https://github.com/goplus/spx)\n* AI Programming: [mcp: An XGo implementation of the Model Context Protocol (MCP)](https://github.com/goplus/mcp)\n* AI Programming: [mcptest: An XGo MCP Test Framework](https://github.com/goplus/mcp/tree/main/mtest)\n* Web Programming: [yap: Yet Another HTTP Web Framework](https://github.com/goplus/yap)\n* Web Programming: [yaptest: An XGo HTTP Test Framework](https://github.com/goplus/yap/tree/main/ytest)\n* Web Programming: [ydb: An XGo Database Framework](https://github.com/goplus/yap/tree/main/ydb)\n* CLI Programming: [cobra: A Commander for modern XGo CLI interactions](https://github.com/goplus/cobra)\n* CLI Programming: [gsh: An alternative to write shell scripts](https://github.com/qiniu/x/tree/main/gsh)\n* Unit Test: [class framework: Unit Test](#class-framework-unit-test)\n* Mechanism: [What's Classfile](#whats-classfile)\n\nSound a bit abstract? Let's take web programming as an example. First let us create a file named [get.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_hello/get.yap) with the following content:\n\n```go\nhtml `<html><body>Hello, YAP!</body></html>`\n```\n\nExecute the following commands:\n\n```sh\ngop mod init hello\ngop get github.com/goplus/yap@latest\ngop mod tidy\ngop run .\n```\n\nA simplest web program is running now. At this time, if you visit http://localhost:8080, you will get:\n\n```\nHello, YAP!\n```\n\nYAP uses filenames to define routes. `get.yap`'s route is `get \"/\"` (GET homepage), and `get_p_#id.yap`'s route is `get \"/p/:id\"` (In fact, the filename can also be `get_p_:id.yap`, but it is not recommended because `:` is not allowed to exist in filenames under Windows).\n\nLet's create a file named [get_p_#id.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_hello/get_p_%23id.yap) with the following content:\n\n```coffee\njson {\n\t\"id\": ${id},\n}\n```\n\nExecute `gop run .` and visit http://localhost:8080/p/123, you will get:\n\n```\n{\"id\": \"123\"}\n```\n\nWhy is `yap` so easy to use? How does it do it? Let us analyze the principles one by one.\n\n\n### What's classfile\n\nWhat's a classfile? And why it is called `classfile`?\n\nFirst let's create a file called `Rect.gox`:\n\n```go\nvar (\n\tWidth, Height int\n)\n\nfunc Area() int {\n\treturn Width * Height\n}\n```\n\nThen we create `hello.xgo` file in the same directory:\n\n```go\nrect := &Rect{10, 20}\necho rect.area\n```\n\nThen we execute `gop run .` to run it and get the result:\n\n```sh\n200\n```\n\nThis shows that the `Rect.gox` file actually defines a class named `Rect`. If we express it in Go syntax, it looks like this:\n\n```go\ntype Rect struct {\n\tWidth, Height int\n}\n\nfunc (this *Rect) Area() int {\n\treturn this.Width * this.Height\n}\n```\n\nSo the name `classfile` comes from the fact that it actually defines a class.\n\nYou may ask: What is the value of doing this?\n\nThe value lies in its ease of use, especially for children and non-expert programmers. Let's look at this syntax:\n\n```go\nvar (\n\tWidth, Height int\n)\n\nfunc Area() int {\n\treturn Width * Height\n}\n```\n\nDefining variables and defining functions are all familiar to them while learning sequential programming. They can define new types using syntax they already know by heart. This will be valuable in getting a wider community to learn XGo.\n\n### What's class framework\n\nOf course, this is not enough to make classfiles an exciting feature. What's more important is its ability to abstract domain knowledge. It is accomplished by defining `base class` for a class and defining `relationships between multiple classes`.\n\nWhat is a `class framework`? Usually it consists of a `project class` and multiple `work classes`. The class framework not only specifies the `base class` of all `project class` and `work classes`, but also organizes all these classes together by the base class of project class. There can be no work classes, that is, the entire classfile consists of only one project class.\n\nThis is a bit abstract. Let's take the [2D Game Engine spx](https://github.com/goplus/spx) as an example. The base class of project class of `spx class framework` is called `Game`. The base class of work class is called `Sprite`. Obviously, there will only be one Game instance in a game, but there are many types of sprites, so many types of work classes are needed, but they all have the same base class called `Sprite`. XGo's class framework allows you to specify different base classes for different work classes. Although this is rare, it can be done.\n\nHow does XGo identify various class files of a class framework? by its filename. By convention, if we define a class framework called `foo`, then its project class is usually called `main_foo.gox`, and the work class is usually called `xxx_foo.gox`. If this class framework does not have a work class, then the project class only needs to ensure that the suffix is `_foo.gox`, and the class name can be freely chosen.\n\nThe earliest version of XGo allows classfiles to be identified through custom file extensions. For example, the project class of the `spx class framework` is called `main.spx`, and the work class is called `xxx.spx`. Although this ability to customize extensions is still retained for now, we do not recommend its use and there is no guarantee that it will continue to be available in the future.\n\n\n### class framework: Unit Test\n\nXGo has a built-in class framework to simplify unit testing. This class framework has the file suffix `_test.gox`.\n\nSuppose you have a function named `foo`:\n\n```go\nfunc foo(v int) int {\n\treturn v * 2\n}\n```\n\nThen you can create a `foo_test.gox` file to test it (see [unit-test/foo_test.gox](../demo/unit-test/foo_test.gox)):\n\n```go\nif v := foo(50); v != 100 {\n\tt.error \"foo(50) ret: ${v}\"\n}\n\nt.run \"foo -10\", t => {\n\tif foo(-10) != -20 {\n\t\tt.fatal \"foo(-10) != -20\"\n\t}\n}\n\nt.run \"foo 0\", t => {\n\tif foo(0) != 0 {\n\t\tt.fatal \"foo(0) != 0\"\n\t}\n}\n```\n\nYou don't need to define a series of `TestXXX` functions like Go, just write your test code directly.\n\nIf you want to run a subtest case, use `t.run`.\n\n\n### yap: Yet Another Go/XGo HTTP Web Framework\n\nThis class framework has the file suffix `.yap`. See [yap: Yet Another HTTP Web Framework](https://github.com/goplus/yap) for more details.\n\n#### Router and Parameters\n\nYAP uses filenames to define routes. `get.yap`'s route is `get \"/\"` (GET homepage), and `get_p_#id.yap`'s route is `get \"/p/:id\"` (In fact, the filename can also be `get_p_:id.yap`, but it is not recommended because `:` is not allowed to exist in filenames under Windows).\n\nLet's create a file named [get_p_#id.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_hello/get_p_%23id.yap) with the following content:\n\n```coffee\njson {\n\t\"id\": ${id},\n}\n```\n\nExecute `gop run .` and visit http://localhost:8080/p/123, you will get:\n\n```\n{\"id\": \"123\"}\n```\n\n\n#### YAP Template\n\nIn most cases, we don't use the `html` directive to generate html pages, but use the `yap` template engine. See [get_p_#id.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_blog/get_p_%23id.yap):\n\n```coffee\nyap \"article\", {\n\t\"id\": ${id},\n}\n```\n\nIt means finding a template called `article` to render. See [yap/article_yap.html](https://github.com/goplus/yap/blob/main/demo/classfile2_blog/yap/article_yap.html):\n\n```html\n<html>\n<head><meta charset=\"utf-8\"/></head>\n<body>Article {{.id}}</body>\n</html>\n```\n\n#### Run at specified address\n\nBy default the YAP server runs on `localhost:8080`, but you can change it in [main.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_blog/main.yap) file:\n\n```coffee\nrun \":8888\"\n```\n\n\n#### Static files\n\nStatic files server demo ([main.yap](https://github.com/goplus/yap/blob/main/demo/classfile2_static/main.yap)):\n\n```coffee\nstatic \"/foo\", FS(\"public\")\nstatic \"/\"\n\nrun \":8080\"\n```\n\n\n### yaptest: HTTP Test Framework\n\nThis class framework has the file suffix `_ytest.gox`.\n\nSuppose we have a web server ([foo/get_p_#id.yap](https://github.com/goplus/yap/blob/main/ytest/demo/foo/get_p_%23id.yap)):\n\n```go\njson {\n\t\"id\": ${id},\n}\n```\n\nThen we create a yaptest file ([foo/foo_ytest.gox](https://github.com/goplus/yap/blob/main/ytest/demo/foo/bar_ytest.gox)):\n\n```go\nmock \"foo.com\", new(AppV2)  // name of any YAP v2 web server is `AppV2`\n\nid := \"123\"\nget \"http://foo.com/p/${id}\"\nret 200\njson {\n\t\"id\": id,\n}\n```\n\nThe directive `mock` creates the web server by [mockhttp](https://pkg.go.dev/github.com/qiniu/x/mockhttp). Then we write test code directly.\n\nYou can change the directive `mock` to `testServer` (see [foo/bar_ytest.gox](https://github.com/goplus/yap/blob/main/ytest/demo/foo/bar_ytest.gox)), and keep everything else unchanged:\n\n```go\ntestServer \"foo.com\", new(AppV2)\n\nid := \"123\"\nget \"http://foo.com/p/${id}\"\nret 200\njson {\n\t\"id\": id,\n}\n```\n\nThe directive `testServer` creates the web server by [net/http/httptest](https://pkg.go.dev/net/http/httptest#NewServer) and obtained a random port as the service address. Then it calls the directive [host](https://pkg.go.dev/github.com/goplus/yap/ytest#App.Host) to map the random service address to `foo.com`. This makes all other code no need to changed.\n\nFor more details, see [yaptest - XGo HTTP Test Framework](https://github.com/goplus/yap/blob/main/ytest).\n\n\n### spx: An XGo 2D Game Engine for STEM education\n\nThis class framework has the file suffix `.spx`. It is the earliest class framework in the world.\n\n#### tutorial/01-Weather\n\n![Screen Shot1](https://github.com/goplus/spx/blob/main/tutorial/01-Weather/1.jpg) ![Screen Shot2](https://github.com/goplus/spx/blob/main/tutorial/01-Weather/2.jpg)\n\nThrough this example you can learn how to implement dialogues between multiple actors.\n\nHere are some codes in [Kai.spx](https://github.com/goplus/spx/blob/main/tutorial/01-Weather/Kai.spx):\n\n```coffee\nonStart => {\n\tsay \"Where do you come from?\", 2\n\tbroadcast \"1\"\n}\n\nonMsg \"2\", => {\n\tsay \"What's the climate like in your country?\", 3\n\tbroadcast \"3\"\n}\n\nonMsg \"4\", => {\n\tsay \"Which seasons do you like best?\", 3\n\tbroadcast \"5\"\n}\n```\n\nWe call `onStart` and `onMsg` to listen events. `onStart` is called when the program is started. And `onMsg` is called when someone calls `broadcast` to broadcast a message.\n\nWhen the program starts, Kai says `Where do you come from?`, and then broadcasts the message `1`. Who will recieve this message? Let's see codes in [Jaime.spx](https://github.com/goplus/spx/blob/main/tutorial/01-Weather/Jaime.spx):\n\n```coffee\nonMsg \"1\", => {\n\tsay \"I come from England.\", 2\n\tbroadcast \"2\"\n}\n\nonMsg \"3\", => {\n\tsay \"It's mild, but it's not always pleasant.\", 4\n\t# ...\n\tbroadcast \"4\"\n}\n```\n\nYes, Jaime recieves the message `1` and says `I come from England.`. Then he broadcasts the message `2`. Kai recieves it and says `What's the climate like in your country?`.\n\nThe following procedures are very similar. In this way you can implement dialogues between multiple actors.\n\n#### tutorial/02-Dragon\n\n![Screen Shot1](https://github.com/goplus/spx/blob/main/tutorial/02-Dragon/1.jpg)\n\nThrough this example you can learn how to define variables and show them on the stage.\n\nHere are all the codes of [Dragon](https://github.com/goplus/spx/blob/main/tutorial/02-Dragon/Dragon.spx):\n\n```coffee\nvar (\n\tscore int\n)\n\nonStart => {\n\tscore = 0\n\tfor {\n\t\tturn rand(-30, 30)\n\t\tstep 5\n\t\tif touching(\"Shark\") {\n\t\t\tscore++\n\t\t\tplay chomp, true\n\t\t\tstep -100\n\t\t}\n\t}\n}\n```\n\nWe define a variable named `score` for `Dragon`. After the program starts, it moves randomly. And every time it touches `Shark`, it gains one score.\n\nHow to show the `score` on the stage? You don't need write code, just add a `stageMonitor` object into [assets/index.json](https://github.com/goplus/spx/blob/main/tutorial/02-Dragon/assets/index.json):\n\n```json\n{\n  \"zorder\": [\n    {\n      \"type\": \"stageMonitor\",\n      \"target\": \"Dragon\",\n      \"val\": \"getVar:score\",\n      \"color\": 15629590,\n      \"label\": \"score\",\n      \"mode\": 1,\n      \"x\": 5,\n      \"y\": 5,\n      \"visible\": true\n    }\n  ]\n}\n```\n\n#### tutorial/03-Clone\n\n![Screen Shot1](https://github.com/goplus/spx/blob/main/tutorial/03-Clone/1.png)\n\nThrough this example you can learn:\n* Clone sprites and destory them.\n* Distinguish between sprite variables and shared variables that can access by all sprites.\n\nHere are some codes in [Calf.spx](https://github.com/goplus/spx/blob/main/tutorial/03-Clone/Calf.spx):\n\n```coffee\nvar (\n\tid int\n)\n\nonClick => {\n\tclone\n}\n\nonCloned => {\n\tgid++\n\t...\n}\n```\n\nWhen we click the sprite `Calf`, it receives an `onClick` event. Then it calls `clone` to clone itself. And after cloning, the new `Calf` sprite will receive an `onCloned` event.\n\nIn `onCloned` event, the new `Calf` sprite uses a variable named `gid`. It doesn't define in [Calf.spx](https://github.com/goplus/spx/blob/main/tutorial/03-Clone/Calf.spx), but in [main.spx](https://github.com/goplus/spx/blob/main/tutorial/03-Clone/main.spx).\n\n\nHere are all the codes of [main.spx](https://github.com/goplus/spx/blob/main/tutorial/03-Clone/main.spx):\n\n```coffee\nvar (\n\tArrow Arrow\n\tCalf  Calf\n\tgid   int\n)\n\nrun \"res\", {Title: \"Clone and Destory (by XGo)\"}\n```\n\nAll these three variables in [main.spx](https://github.com/goplus/spx/blob/main/tutorial/03-Clone/main.spx) are shared by all sprites. `Arrow` and `Calf` are sprites that exist in this project. `gid` means `global id`. It is used to allocate id for all cloned `Calf` sprites.\n\nLet's back to [Calf.spx](https://github.com/goplus/spx/blob/main/tutorial/03-Clone/Calf.spx) to see the full codes of `onCloned`:\n\n```coffee\nonCloned => {\n\tgid++\n\tid = gid\n\tstep 50\n\tsay id, 0.5\n}\n```\n\nIt increases `gid` value and assigns it to sprite `id`. This makes all these `Calf` sprites have different `id`. Then the cloned `Calf` moves forward 50 steps and says `id` of itself.\n\nWhy these `Calf` sprites need different `id`? Because we want destory one of them by its `id`.\n\nHere are all the codes in [Arrow.spx](https://github.com/goplus/spx/blob/main/tutorial/03-Clone/Arrow.spx):\n\n```coffee\nonClick => {\n\tbroadcast \"undo\", true\n\tgid--\n}\n```\n\nWhen we click `Arrow`, it broadcasts an \"undo\" message (NOTE: We pass the second parameter `true` to broadcast to indicate we wait all sprites to finish processing this message).\n\nAll `Calf` sprites receive this message, but only the last cloned sprite finds its `id` is equal to `gid` then destroys itself. Here are the related codes in [Calf.spx](https://github.com/goplus/spx/blob/main/tutorial/03-Clone/Calf.spx):\n\n```coffee\nonMsg \"undo\", => {\n\tif id == gid {\n\t\tdestroy\n\t}\n}\n```\n\n#### tutorial/04-Bullet\n\n![Screen Shot1](https://github.com/goplus/spx/blob/main/tutorial/04-Bullet/1.jpg)\n\nThrough this example you can learn:\n* How to keep a sprite following mouse position.\n* How to fire bullets.\n\nIt's simple to keep a sprite following mouse position. Here are some related codes in [MyAircraft.spx](https://github.com/goplus/spx/blob/main/tutorial/04-Bullet/MyAircraft.spx):\n\n\n```coffee\nonStart => {\n\tfor {\n\t\t# ...\n\t\tsetXYpos mouseX, mouseY\n\t}\n}\n```\n\nYes, we just need to call `setXYpos mouseX, mouseY` to follow mouse position.\n\nBut how to fire bullets? Let's see all codes of [MyAircraft.spx](https://github.com/goplus/spx/blob/main/tutorial/04-Bullet/MyAircraft.spx):\n\n```coffee\nonStart => {\n\tfor {\n\t\twait 0.1\n\t\tBullet.clone\n\t\tsetXYpos mouseX, mouseY\n\t}\n}\n```\n\nIn this example, `MyAircraft` fires bullets every 0.1 seconds. It just calls `Bullet.clone` to create a new bullet. All the rest things are the responsibility of `Bullet`.\n\nHere are all the codes in [Bullet.spx](https://github.com/goplus/spx/blob/main/tutorial/04-Bullet/Bullet.spx):\n\n```coffee\nonCloned => {\n\tsetXYpos MyAircraft.xpos, MyAircraft.ypos+5\n\tshow\n\tfor {\n\t\twait 0.04\n\t\tchangeYpos 10\n\t\tif touching(Edge) {\n\t\t\tdestroy\n\t\t}\n\t}\n}\n```\n\nWhen a `Bullet` is cloned, it calls `setXYpos MyAircraft.xpos, MyAircraft.ypos+5` to follow `MyAircraft`'s position and shows itself (the default state of a `Bullet` is hidden). Then the `Bullet` moves forward every 0.04 seconds and this is why we see the `Bullet` is flying.\n\nAt last, when the `Bullet` touches screen `Edge` or any enemy (in this example we don't have enemies), it destroys itself.\n\nThese are all things about firing bullets.\n"
  },
  {
    "path": "doc/code-coverage.md",
    "content": "XGo Unit Test: Code Coverage\n=====\n\nTODO\n"
  },
  {
    "path": "doc/contributing.md",
    "content": "# Contributing Guidelines\n\nThank you for your interest in contributing to the XGo project! Your efforts\nhelp us build and improve this powerful language.\n\nPlease be aware that participation in the XGo project requires adherence to our\nestablished [code of conduct](https://github.com/goplus/xgo/blob/main/CODE_OF_CONDUCT.md). Your involvement signifies your agreement to comply\nwith the guidelines set forth within these terms. We appreciate your cooperation\nin fostering a respectful and inclusive environment for all contributors.\n\nTo ensure a smooth contribution process, this guide outlines the steps and best\npractices for contributing to XGo. It's assumed that you have a foundational\nknowledge of XGo and are comfortable using Git and GitHub for version control\nand collaboration.\n\n## Before contributing code\n\nThe XGo project welcomes code contributions. However, to ensure coordination,\nplease discuss any significant changes beforehand. It's recommended to signal\nyour intent to contribute via the issue tracker, either by filing a new issue or\nclaiming an existing one.\n\nThe issue tracker is your go-to, whether you've got a contribution in mind or\nare looking for inspiration. Issues are organized to streamline the workflow.\n\nExcept for very trivial changes, all contributions should relate to an existing\nissue. Feel free to open one and discuss your plans. This process allows\nvalidation of design, prevents duplicate efforts, ensures alignment with\nlanguage and tool goals, and confirms design validity before coding.\n\n## Making a code contribution\n\nLet's say you want to fix a typo in the `README.md` file of the\n`https://github.com/goplus/xgo.git` repository, changing \"Github\" to \"GitHub\",\nand you wish to merge this contribution into the `main` branch of the upstream\nrepository.\n\n### Step 1: Fork the upstream repository\n\nFirst, you need to fork the upstream repository to your own username (or an\norganization you own) by following [this guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo). Then you can start making your\ncontributions.\n\nLet's say you've forked `https://github.com/goplus/xgo.git` to\n`https://github.com/YOUR_USERNAME/gop.git`.\n\n### Step 2: Clone the forked repository\n\nOpen your terminal, clone the repository you just forked, and change into the\ncloned directory:\n\n```\ngit clone https://github.com/YOUR_USERNAME/gop.git\ncd gop\n```\n\nThis directory will serve as the working directory in the following steps.\n\n### Step 3: Create a new branch\n\nWe recommend that every contribution should have its own branch, and this branch\nshould be deleted after the contribution is merged into the target branch of the\nupstream repository.\n\nFirst, you need to sync your fork's `main` branch to keep it up-to-date with the\nupstream's `main` branch by following [this guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork).\n\nThen, you can create and switch to a branch for your contribution:\n\n```\ngit checkout -b typo main\n```\n\n### Step 4: Make the changes\n\nThis step is where you'll spend most of your effort when contributing. It\ntypically takes up the majority of the time needed to make a contribution.\n\nHowever, in our case, we just want to correct a typo in the `README.md` file,\nchanging \"Github\" to \"GitHub\". So, go ahead and make that change.\n\n### Step 5: Commit and push\n\nAfter making your changes, you need to commit them:\n\n```\ngit add README.md\ngit commit -s -m 'README.md: correct typo \"Github\" to \"GitHub\"'\n```\n\nThe commit message here is very straightforward, but it's recommended to read\nthe [Commit Messages](#commit-messages) section for crafting better commit messages.\n\nOnce you've committed your changes, you also need to push your commit to your\nfork on GitHub:\n\n```\ngit push --set-upstream origin typo\n```\n\n### Step 6: Open a pull request\n\nHead to the upstream repository and open a pull request by following\n[this guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). Your pull request should clearly describe the change you're\nproposing and why it's beneficial.\n\nTypically, the title and description of the pull request you open will come from\nthe first commit message in the branch of your fork. You can modify them if\nnecessary.\n\n### Step 7: Engage with feedback\n\nBe open to feedback from upstream repository maintainers. They might request\nfurther changes or provide insights that could enhance your contribution.\n\nAfter making any further changes, simply repeat [Step 5](#step-5-commit-and-push). However, when\ncommitting, it's recommended to use the `--amend` option. This ensures that all\nyour changes are contained within a single commit, maintaining a linear commit\nhistory. For example:\n\n```\ngit add README.md\ngit commit --amend\n```\n\nThis isn't a strict requirement, though. You can create as many commits as you\nlike.\n\n### Step 8: Do post-merge cleanup\n\nAs mentioned in [Step 3](#step-3-create-a-new-branch), we recommend dedicating a branch to each contribution.\nOnce a contribution's pull request is [squash and merged](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits), the branch should be\ndeleted:\n\n```\ngit checkout main\ngit branch -d typo\ngit push origin -d typo\n```\n\n## Commit messages\n\nCommit messages in XGo adhere to specific conventions outlined in this section.\n\nA well-crafted commit message looks like this:\n\n```\ndocs: revise contribution guidelines for clarity and inclusivity\n\nAs our project grows, we want to ensure that our community understands\nhow to contribute effectively and feels welcomed to do so. This update\nto our contribution guidelines aims to clarify the process and encourage\nmore developers to get involved.\n\nKey changes include:\n  - Simplified the language to make the guidelines more accessible to\n    non-native English speakers and newcomers to open source.\n  - Included a step-by-step guide on setting up the development\n    environment, submitting a pull request, and what to expect during\n    the review process.\n  - Added a new section on community standards and code of conduct to\n    foster a respectful and inclusive community atmosphere.\n  - Provided clear examples of desirable contributions, such as bug\n    fixes, feature proposals, and documentation improvements.\n\nFixes #123\n\nSigned-off-by: John Doe <john@example.com>\n```\n\n### First line\n\nThe first line of a commit message typically serves as a concise one-line\nsummary, beginning with the primary component it impacts (e.g., a specific\npackage). This summary should ideally be brief, with many Git interfaces\nfavoring a limit of approximately 72 characters. While XGo is not strict on the\nlength limit, it's a good guideline to follow.\n\nThink of the summary as finishing the sentence \"This commit modifies XGo to\n_____.\" As such, it does not begin with a capital letter, form a complete\nsentence, or diverge from succinctly stating the commit's effect.\n\nAfter the summary, ensure to separate the main content (if any) with a blank\nline.\n\n### Main content\n\nThe rest of the commit message should elaborate, offering context and a clear\nexplanation of the commit. Employ complete sentences and proper punctuation.\nRefrain from using HTML, Markdown, or any form of markup language.\n\nInclude relevant information, like benchmark data, if the commit impacts\nperformance.\n\nAdhere to a line width of approximately 72 characters to accommodate Git viewing\ntools, except when longer lines are necessary (such as for ASCII art, tables, or\nextended URLs).\n\n### Referencing issues\n\nThe special notation `Fixes #123` links the commit directly to issue 123 in the\nissue tracker. Upon merging this commit, the issue tracker will automatically\nmark the issue as resolved.\n\nFor commits that partially address an issue, use `Updates #123`. This notation\nwill not close the issue upon the commit's integration.\n\nTo reference multiple issues within a single commit, list them by appending each\nissue number with a comma and a space, such as `Fixes #123, #456`. This indicates\nthat the commit resolves both referenced issues.\n\nFor cross-repository references, use the full `user/repo#number` syntax, such as\n`Fixes goplus/xgo#123`.\n\n### Signed-off-by\n\nFor contributions to be accepted, we mandate that a `Signed-off-by` line, in\naccordance with the [Developer Certificate of Origin](https://developercertificate.org/), be included at the\nconclusion of each commit message.\n"
  },
  {
    "path": "doc/docs.md",
    "content": "XGo Quick Start\n======\n\nXGo is the first AI-native programming language that integrates software engineering into a unified whole.\n\n```\nXGo := C * Go * Python * JavaScript + Scratch\n```\n\nOur vision is to **enable everyone to become a builder of the world**.\n\n#### Easy to learn\n\n* Simple and easy to understand\n* Smaller syntax set than Go and Python in best practices\n\n#### Ready for large projects\n\n* Integrate C/C++, Go, Python, and JavaScript into a unified ecosystem\n* Derived from Go and easy to build large projects from its good engineering foundation\n\nThe XGo programming language is designed for engineering, STEM education, and data science.\n\n* **For engineering**: working in the simplest language that can be mastered by children.\n* **For STEM education**: studying an engineering language that can be used for work in the future.\n* **For data science**: communicating with engineers in the same language.\n\n## How to install\n\nNote: Requires go1.19 or later\n\n### on Windows\n\n```sh\nwinget install goplus.xgo\n```\n\n### on Debian/Ubuntu\n\n```sh\nsudo bash -c ' echo \"deb [trusted=yes] https://pkgs.xgo.dev/apt/ /\" > /etc/apt/sources.list.d/goplus.list'\nsudo apt update\nsudo apt install xgo\n```\n\n### on RedHat/CentOS/Fedora\n\n```sh\nsudo bash -c 'echo -e \"[goplus]\\nname=XGo Repo\\nbaseurl=https://pkgs.xgo.dev/yum/\\nenabled=1\\ngpgcheck=0\" > /etc/yum.repos.d/goplus.repo'\nsudo yum install xgo\n```\n\n### on macOS/Linux (Homebrew)\n\nInstall via [brew](https://brew.sh/)\n\n```sh\n$ brew install xgo\n```\n\n### from source code\n\n```bash\ngit clone https://github.com/goplus/xgo.git\ncd xgo\n\n# On mac/linux run:\n./all.bash\n# On Windows run:\nall.bat\n```\n\nActually, `all.bash` and `all.bat` will use `go run cmd/make.go` underneath.\n\n\n## Running in XGo playground\n\nIf you don't want install XGo, you can write your XGo programs in XGo playground. This is the fastest way to experience XGo.\n\n* XGo playground: https://play.xgo.dev/\n\nAnd you can share your XGo code with your friends.\nHere is my `Hello world` program:\n* https://play.xgo.dev/?p=AAh_gQAKAZR.\n\n\n## Table of Contents\n\n<table>\n    <tr><td width=33% valign=top>\n\n* [Hello world](#hello-world)\n* [Running a project folder](#running-a-project-folder-with-several-files)\n* [Comments](#comments)\n* [Variables](#variables)\n* [XGo types](#xgo-types)\n    * [Strings](#strings)\n    * [Numbers](#numbers)\n    * [Slices](#slices)\n    * [Maps](#maps)\n* [Module imports](#module-imports)\n* [Statements & expressions](#statements--expressions)\n    * [If..else](#ifelse)\n    * [For loop](#for-loop)\n    * [Error handling](#error-handling)\n\n</td><td width=33% valign=top>\n\n* [Functions](#functions)\n    * [Returning multiple values](#returning-multiple-values)\n    * [Optional parameters](#optional-parameters)\n    * [Variadic parameters](#variadic-parameters)\n    * [Keyword arguments](#keyword-arguments)\n    * [Higher order functions](#higher-order-functions)\n    * [Lambda expressions](#lambda-expressions)\n* [Structs](#structs)\n    * [Struct tags](#struct-tags)\n    * [Custom iterators](#custom-iterators)\n    * [Deduce struct type](#deduce-struct-type)\n    * [Overload operators](#overload-operators)\n    * [Auto property](#auto-property)\n\n</td><td valign=top>\n\n* [Go/XGo hybrid programming](#goxgo-hybrid-programming)\n    * [Run XGo in watch mode](#run-xgo-in-watch-mode)\n* [Calling C from XGo](#calling-c-from-xgo)\n* [Data processing](#data-processing)\n    * [Rational numbers](#rational-numbers)\n    * [List comprehension](#list-comprehension)\n    * [Select data from a collection](#select-data-from-a-collection)\n    * [Check if data exists in a collection](#check-if-data-exists-in-a-collection)\n* [Domain-specific text literals](#domain-specific-text-literals)\n* [Unix shebang](#unix-shebang)\n* [Compatibility with Go](#compatibility-with-go)\n\n</td></tr>\n</table>\n\n\n## Hello World\n\nDifferent from the function call style of most languages, XGo recommends command style code:\n\n```go\nprintln \"Hello world\"\n```\n\nSave this snippet into a file named `hello.xgo`. Now do: `xgo run hello.xgo`.\n\nCongratulations - you just wrote and executed your first XGo program!\n\nYou can compile a program without execution with `xgo build hello.xgo`.\nSee `xgo help` for all supported commands.\n\n[`println`](#println) is one of the few [built-in functions](#builtin-functions).\nIt prints the value passed to it to standard output.\n\nTo emphasize our preference for command style, we introduce `echo` as an alias for `println`:\n\n```coffee\necho \"Hello world\"\n```\n\nSee https://tutorial.xgo.dev/hello-world for more details.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Running a project folder with several files\n\nSuppose you have a folder with several .xgo files in it, and you want \nto compile them all into one program. Just do: `xgo run .`.\n\nPassing parameters also works, so you can do:\n`xgo run . --yourparams some_other_stuff`.\n\nYour program can then use the CLI parameters like this:\n\n```go\nimport \"os\"\n\necho os.Args\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Comments\n\n```go\n# This is a single line comment.\n\n// This is a single line comment.\n\n/*\nThis is a multiline comment.\n*/\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Variables\n\n```go\nname := \"Bob\"\nage := 20\nlargeNumber := int128(1 << 65)\necho name, age\necho largeNumber\n```\n\nVariables are declared and initialized with `:=`.\n\nThe variable's type is inferred from the value on the right hand side.\nTo choose a different type, use type conversion:\nthe expression `T(v)` converts the value `v` to the\ntype `T`.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n### Initialization vs. assignment\n\nNote the (important) difference between `:=` and `=`.\n`:=` is used for declaring and initializing, `=` is used for assigning.\n\n```go failcompile\nage = 21\n```\n\nThis code will not compile, because the variable `age` is not declared.\nAll variables need to be declared in XGo.\n\n```go\nage := 21\n```\n\nThe values of multiple variables can be changed in one line.\nIn this way, their values can be swapped without an intermediary variable.\n\n```go\na, b := 0, 1\na, b = b, a\necho a, b // 1, 0\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## XGo Types\n\n### Primitive types\n\n```go ignore\nbool\n\nint8    int16   int32   int    int64    int128\nuint8   uint16  uint32  uint   uint64   uint128\n\nuintptr // similar to C's size_t\n\nbyte // alias for uint8\nrune // alias for int32, represents a Unicode code point\n\nstring\n\nfloat32 float64\n\ncomplex64 complex128\n\nbigint bigrat\n\nunsafe.Pointer // similar to C's void*\n\nany // alias for Go's interface{}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n### Strings\n\n```go\nname := \"Bob\"\necho name.len  // 3\necho name[0]   // 66\necho name[1:3] // ob\necho name[:2]  // Bo\necho name[2:]  // b\n\n// or using octal escape `\\###` notation where `#` is an octal digit\necho \"\\141a\"   // aa\n\n// Unicode can be specified directly as `\\u####` where # is a hex digit\n// and will be converted internally to its UTF-8 representation\necho \"\\u2605\"  // ★\n```\n\nString values are immutable. You cannot mutate elements:\n\n```go failcompile\ns := \"hello 🌎\"\ns[0] = `H` // not allowed\n```\n\nNote that indexing a string will produce a `byte`, not a `rune` nor another `string`.\n\nStrings can be easily converted to integers:\n\n```go\ns := \"12\"\na, err := s.int\nb := s.int! // will panic if s isn't a valid integer\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n#### String operators\n\n```go\nname := \"Bob\"\nbobby := name + \"by\" // + is used to concatenate strings\necho bobby // Bobby\n\ns := \"Hello \"\ns += \"world\"\necho s // Hello world\n```\n\nMost XGo operators must have values of the same type on both sides. You cannot concatenate an\ninteger to a string:\n\n```go failcompile\nage := 10\necho \"age = \" + age // not allowed\n```\n\nWe have to either convert `age` to a `string`:\n\n```go\nage := 10\necho \"age = \" + age.string\n```\n\nHowever, you can replace `age.string` to `\"${age}\"`:\n\n```go\nage := 10\necho \"age = ${age}\"\n```\n\nHere is a more complex example of `${expr}`:\n\n```go\nhost := \"example.com\"\npage := 0\nlimit := 20\necho \"https://${host}/items?page=${page+1}&limit=${limit}\" // https://example.com/items?page=1&limit=20\necho \"$$\" // $\n```\n\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Runes\n\nA `rune` represents a single Unicode character and is an alias for `int32`.\n\n```go\nrocket := '🚀'\necho rocket         // 128640\necho string(rocket) // 🚀\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Numbers\n\n```go\na := 123\n```\n\nThis will assign the value of 123 to `a`. By default `a` will have the\ntype `int`.\n\nYou can also use hexadecimal, binary or octal notation for integer literals:\n\n```go\na := 0x7B\nb := 0b01111011\nc := 0o173\n```\n\nAll of these will be assigned the same value, 123. They will all have type\n`int`, no matter what notation you used.\n\nXGo also supports writing numbers with `_` as separator:\n\n```go\nnum := 1_000_000 // same as 1000000\n```\n\nIf you want a different type of integer, you can use casting:\n\n```go\na := int64(123)\nb := uint8(12)\nc := int128(12345)\n```\n\nAssigning floating point numbers works the same way:\n\n```go\nf1 := 1.0\nf2 := float32(3.14)\n```\n\nIf you do not specify the type explicitly, by default float literals will have the type of `float64`.\n\nFloat literals can also be declared as a power of ten:\n\n```go\nf0 := 42e1   // 420\nf1 := 123e-2 // 1.23\nf2 := 456e+2 // 45600\n```\n\nXGo has built-in support for [rational numbers](#rational-numbers):\n\n```go\na := 1r << 200  // suffix `r` means `rational`\nb := bigint(1 << 200)\n```\n\nAnd you can cast bool to number types (this is NOT supported in Go):\n\n```go\necho int(true)       // 1\necho float64(true)   // 1\necho complex64(true) // (1+0i)\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Slices\n\nA slice is a collection of data elements of the same type. A slice literal is a\nlist of expressions surrounded by square brackets. An individual element can be\naccessed using an *index* expression. Indexes start from `0`:\n\n```go\nnums := [1, 2, 3]\necho nums      // [1 2 3]\necho nums.len  // 3\necho nums[0]   // 1\necho nums[1:3] // [2 3]\necho nums[:2]  // [1 2]\necho nums[2:]  // [3]\n\nnums[1] = 5\necho nums // [1 5 3]\n```\n\nType of a slice literal is infered automatically.\n\n```go\na := [1, 2, 3]   // []int\nb := [1, 2, 3.4] // []float64\nc := [\"Hi\"]      // []string\nd := [\"Hi\", 10]  // []any\nd := []          // []any\n```\n\nAnd casting slice literals also works.\n\n```go\na := []float64([1, 2, 3]) // []float64\n```\n\n#### Appending to slices\n\nXGo provides a convenient `<-` operator for appending elements to slices, which is more intuitive than Go's `append` function:\n\n```go\na := [1, 2, 3]\na <- 4           // append single element\na <- 5, 6, 7     // append multiple elements\nb := [8, 9]\na <- b...        // append another slice\n\necho a // [1 2 3 4 5 6 7 8 9]\n```\n\nThis is equivalent to Go's append operations:\n- `a <- v` is the same as `a = append(a, v)`\n- `a <- v1, v2, v3` is the same as `a = append(a, v1, v2, v3)`\n- `a <- b...` is the same as `a = append(a, b...)`\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Maps\n\nA map literal is a list of expressions surrounded by curly braces.\n\n```go\na := {\"Hello\": 1, \"xsw\": 3}     // map[string]int\nb := {\"Hello\": 1, \"xsw\": 3.4}   // map[string]float64\nc := {\"Hello\": 1, \"xsw\": \"XGo\"} // map[string]any\ne := {1: \"one\", 2: \"two\"}       // map[int]string\nd := {}                         // map[string]any\n```\n\nUse `make` for empty maps or to **pre-allocate capacity** for better performance.\n\n```go\nm := make(map[string]int)          // Basic creation\nlarge := make(map[string]int, 100) // Pre-allocated for ~100 elements\n```\n\nBefore manipulating maps, it is important to understand that XGo supports two notations for referencing keys:\n\n- **Bracket Notation** (`m[\"key\"]`): The universal syntax. It works for all key types and allows using variables as keys.\n- **Field Access Notation** (`m.key`): A convenient shorthand for string-keyed maps when the key is a valid identifier (no spaces or special characters).\n\n**Field access is pure syntax sugar** - `m.field` and `m[\"field\"]` behave identically in all contexts.\n\nBoth notations are used for both **assigning** values and **retrieving** them.\n\n#### Adding and Updating Elements\n\n```go\na := {\"a\": 1, \"b\": 0}\n\n// Using bracket notation\na[\"c\"] = 100\n\n// Using field notation\na.d = 200\n\necho a  // Output: map[a:1 b:0 c:100 d:200]\n\n// Works with maps created by make too\nm := make(map[string]int)\nm[\"x\"] = 10\nm.y = 20\necho m  // Output: map[x:10 y:20]\n```\n\n#### Deleting Elements\n\nUse the `delete` function to remove elements from a map:\n\n```go\na := {\"a\": 1, \"b\": 0, \"c\": 100}\ndelete(a, \"b\")\necho a  // Output: map[a:1 c:100]\n```\n\n#### Getting Map Length\n\nYou can get the number of elements in a map using the `len` function:\n\n```go\na := {\"a\": 1, \"b\": 2, \"c\": 3}\necho len(a)  // Output: 3\n```\n\n#### Accessing Elements\n\n```go\nconfig := {\"host\": \"localhost\", \"port\": 8080}\necho config.host  // Output: localhost\necho config.port  // Output: 8080\n\n// Equivalent to:\necho config[\"host\"]\necho config[\"port\"]\n```\n\n##### Working with `any` Type\n\nEither notation also works with variables of type `any`, automatically treating them as `map[string]any`:\n\n```go\nvar response any = {\"status\": \"ok\", \"code\": 200}\necho response.status  // Output: ok\necho response.code    // Output: 200\n```\n\n##### Safe Access with Comma-ok\n\nWhen accessing uncertain data (such as from JSON or external APIs), use the comma-ok form to safely check if a path exists. The comma-ok form returns two values:\n- The value itself (or zero value if path doesn't exist)\n- A boolean indicating whether the access succeeded\n\nWith comma-ok, accessing non-existent paths **never panics** - it simply returns `false`:\n\n```go\nvar data any = fetchFromAPI()\n\n// Without comma-ok - may panic if structure is wrong\n// name := data.user.profile.name.(string)\n\n// With comma-ok - safe, never panics\nname, ok := data.user.profile.name.(string)\nif ok {\n    // ...\n}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Module imports\n\nFor information about creating a module, see [Modules](#modules).\n\nModules can be imported using the `import` keyword:\n\n```go\nimport \"strings\"\n\nx := strings.NewReplacer(\"?\", \"!\").Replace(\"Hello, world???\")\necho x // Hello, world!!!\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Module import aliasing\n\nAny imported module name can be aliased:\n\n```go\nimport strop \"strings\"\n\nx := strop.NewReplacer(\"?\", \"!\").Replace(\"Hello, world???\")\necho x // Hello, world!!!\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Statements & expressions\n\n\n### If..else\n\nIn XGo, `if` statements are pretty straightforward and similar to most other languages.\nUnlike other C-like languages,\nthere are no parentheses surrounding the condition and the braces are always required.\n\n```go\na := 10\nb := 20\nif a < b {\n    echo \"a < b\"\n} else if a > b {\n    echo \"a > b\"\n} else {\n    echo \"a == b\"\n}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### For loop\n\nXGo has only one looping keyword: `for`, with several forms.\n\n#### `for..in`\n\nThis is the most common form. You can use it with a slice, map, numeric range or custom iterators.\n\nFor information about creating a custom iterators, see [Custom iterators](#custom-iterators).\n\n##### Slice `for`\n\nThe `for value in arr` form is used for going through elements of a slice.\n\n```go\nnumbers := [1, 3, 5, 7, 11, 13, 17]\nsum := 0\nfor x in numbers {\n    sum += x\n}\necho sum // 57\n```\n\nIf an index is required, an alternative form `for index, value in arr` can be used.\n\n```go\nnames := [\"Sam\", \"Peter\"]\nfor i, name in names {\n    echo i, name\n    // 0 Sam\n    // 1 Peter\n}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n##### Map `for`\n\n```go\nm := {\"one\": 1, \"two\": 2}\nfor key, val in m {\n    echo key, val\n    // one 1\n    // two 2\n}\nfor key, _ in m {\n    echo key\n    // one\n    // two\n}\nfor val in m {\n    echo val\n    // 1\n    // 2\n}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n##### Range `for`\n\nYou can use `range expression` (`start:end:step`) in for loop.\n\n```go\nfor i in :5 {\n    echo i\n    // 0\n    // 1\n    // 2\n    // 3\n    // 4\n}\nfor i in 1:5 {\n    echo i\n    // 1\n    // 2\n    // 3\n    // 4\n}\nfor i in 1:5:2 {\n    echo i\n    // 1\n    // 3\n}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n##### `for`/`in`/`if`\n\nAll loops of `for`/`in` form can have an optional `if` condition.\n\n```go\nnumbers := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\nfor num in numbers if num%3 == 0 {\n    echo num\n    // 0\n    // 3\n    // 6\n    // 9\n}\n\nfor num in :10 if num%3 == 0 {\n    echo num\n    // 0\n    // 3\n    // 6\n    // 9\n}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n#### Condition `for`\n\n```go\nsum := 0\ni := 1\nfor i <= 100 {\n    sum += i\n    i++\n}\necho sum // 5050\n```\n\nThis form of the loop is similar to `while` loops in other languages.\nThe loop will stop iterating once the boolean condition evaluates to false.\nAgain, there are no parentheses surrounding the condition, and the braces are always required.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n#### C `for`\n\n```go\nfor i := 0; i < 10; i += 2 {\n    // Don't print 6\n    if i == 6 {\n        continue\n    }\n    echo i\n    // 0\n    // 2\n    // 4\n    // 8\n}\n```\n\nFinally, there's the traditional C style `for` loop. It's safer than the `while` form\nbecause with the latter it's easy to forget to update the counter and get\nstuck in an infinite loop.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n#### Bare `for`\n\n```go\nfor {\n    // ...\n}\n```\n\nThe condition can be omitted, resulting in an infinite loop. You can use `break` or `return` to end the loop.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Error handling\n\nWe reinvent the error handling specification in XGo. We call them `ErrWrap expressions`:\n\n```go\nexpr! // panic if err\nexpr? // return if err\nexpr?:defval // use defval if err\n```\n\nHow to use them? Here is an example:\n\n```go\nimport (\n    \"strconv\"\n)\n\nfunc add(x, y string) (int, error) {\n    return strconv.Atoi(x)? + strconv.Atoi(y)?, nil\n}\n\nfunc addSafe(x, y string) int {\n    return strconv.Atoi(x)?:0 + strconv.Atoi(y)?:0\n}\n\necho `add(\"100\", \"23\"):`, add(\"100\", \"23\")!\n\nsum, err := add(\"10\", \"abc\")\necho `add(\"10\", \"abc\"):`, sum, err\n\necho `addSafe(\"10\", \"abc\"):`, addSafe(\"10\", \"abc\")\n```\n\nThe output of this example is:\n\n```\nadd(\"100\", \"23\"): 123\nadd(\"10\", \"abc\"): 0 strconv.Atoi: parsing \"abc\": invalid syntax\n\n===> errors stack:\nmain.add(\"10\", \"abc\")\n    /Users/xsw/tutorial/15-ErrWrap/err_wrap.xgo:6 strconv.Atoi(y)?\n\naddSafe(\"10\", \"abc\"): 10\n```\n\nCompared to corresponding Go code, It is clear and more readable.\n\nAnd the most interesting thing is, the return error contains the full error stack. When we got an error, it is very easy to position what the root cause is.\n\nHow these `ErrWrap expressions` work? See [Error Handling](https://github.com/goplus/xgo/wiki/Error-Handling) for more information.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Functions\n\n```go\nfunc add(x int, y int) int {\n    return x + y\n}\n\necho add(2, 3) // 5\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Returning multiple values\n\n```go\nfunc foo() (int, int) {\n    return 2, 3\n}\n\na, b := foo()\necho a // 2\necho b // 3\nc, _ := foo() // ignore values using `_`\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Optional parameters\n\nXGo supports optional parameters using the `T?` syntax. Optional parameters must have zero values as their defaults.\n\n```go\nfunc greet(name string, count int?) {\n    if count == 0 {\n        count = 1\n    }\n    for i := 0; i < count; i++ {\n        echo \"Hello,\", name\n    }\n}\n\ngreet \"Alice\", 3  // prints \"Hello, Alice\" three times\ngreet \"Bob\"       // prints \"Hello, Bob\" once (default behavior)\n```\n\nOptional parameters are denoted by adding `?` after the parameter type. The default value is always the zero value of that type (e.g., `0` for integers, `\"\"` for strings, `false` for booleans).\n\n```go\nfunc connect(host string, port int?, secure bool?) {\n    if port == 0 {\n        port = 80\n    }\n    echo \"Connecting to\", host, \"on port\", port, \"secure:\", secure\n}\n\nconnect \"example.com\", 443, true  // Connecting to example.com on port 443 secure: true\nconnect \"example.com\"             // Connecting to example.com on port 80 secure: false\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Variadic parameters\n\n```go\nfunc sum(a ...int) int {\n    total := 0\n    for x in a {\n        total += x\n    }\n    return total\n}\n\necho sum(2, 3, 5) // 10\n```\n\nOutput parameters can have names.\n\n```go\nfunc sum(a ...int) (total int) {\n    for x in a {\n        total += x\n    }\n    return // don't need return values if they are assigned\n}\n\necho sum(2, 3, 5) // 10\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Keyword arguments\n\nXGo supports Python-like keyword arguments (kwargs) syntax for improved code readability. When calling functions with many parameters, you can use `key=value` syntax to make your code more expressive and command-line-style.\n\n#### Using kwargs with maps\n\n```go\nfunc process(opts map[string]any?, args ...any) {\n    if name, ok := opts[\"name\"]; ok {\n        echo \"Name:\", name\n    }\n    if age, ok := opts[\"age\"]; ok {\n        echo \"Age:\", age\n    }\n    echo \"Args:\", args\n}\n\nprocess name = \"Ken\", age = 17              // keyword parameters only\nprocess \"extra\", 1, name = \"Ken\", age = 17  // variadic parameters first, then keyword parameters\nprocess                                     // all parameters optional\n```\n\n#### Using kwargs with structs\n\nYou can also use structs or struct pointers for keyword parameters, which provides type safety:\n\n```go\ntype Config struct {\n    Timeout    int\n    MaxRetries int\n    Debug      bool\n}\n\nfunc run(cfg *Config?) {\n    timeout := 30\n    maxRetries := 3\n    debug := false\n    if cfg != nil {\n        if cfg.Timeout > 0 {\n            timeout = cfg.Timeout\n        }\n        if cfg.MaxRetries > 0 {\n            maxRetries = cfg.MaxRetries\n        }\n        debug = cfg.Debug\n    }\n    echo \"Timeout:\", timeout, \"MaxRetries:\", maxRetries, \"Debug:\", debug\n}\n\nrun timeout = 60, maxRetries = 5           // lowercase field names work\nrun Timeout = 10, Debug = true             // uppercase field names work too\nrun                                        // uses default values\n```\n\n**Key rules:**\n- The keyword parameter must be an optional parameter.\n- The keyword parameter must be the last parameter (without variadic) or second-to-last (with variadic).\n- When calling a function, keyword arguments must be placed after all normal parameters (including variadic parameters). This might seem inconsistent with the order of keyword and variadic parameters in a function declaration, but that's the rule.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Higher order functions\n\nFunctions can also be parameters.\n\n```go\nfunc square(x float64) float64 {\n    return x*x\n}\n\nfunc abs(x float64) float64 {\n    if x < 0 {\n        return -x\n    }\n    return x\n}\n\nfunc transform(a []float64, f func(float64) float64) []float64 {\n    return [f(x) for x in a]\n}\n\ny := transform([1, 2, 3], square)\necho y // [1 4 9]\n\nz := transform([-3, 1, -5], abs)\necho z // [3 1 5]\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Lambda expressions\n\nYou also can use `lambda expression` to define a anonymous function.\n\n```go\nfunc transform(a []float64, f func(float64) float64) []float64 {\n    return [f(x) for x in a]\n}\n\ny := transform([1, 2, 3], x => x*x)\necho y // [1 4 9]\n\nz := transform([-3, 1, -5], x => {\n    if x < 0 {\n        return -x\n    }\n    return x\n})\necho z // [3 1 5]\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Structs\n\n### Struct tags\n\nGo does not provide a way to add reflection information to a struct type. XGo uses Go's built-in struct field tags to implement struct type tags. For example:\n\n```go\ntype Start struct {\n    _ \"Start recording meeting minutes\"\n}\n```\n\nIt is equivalent to\n\n```go\ntype Start struct {\n    _ struct{} `_:\"Start recording meeting minutes\"`\n}\n```\n\n### Custom iterators\n\n#### For range of UDT\n\n```go\ntype Foo struct {\n}\n\n// Gop_Enum(proc func(val ValType)) or:\n// Gop_Enum(proc func(key KeyType, val ValType))\nfunc (p *Foo) Gop_Enum(proc func(key int, val string)) {\n    // ...\n}\n\nfoo := &Foo{}\nfor k, v := range foo {\n    echo k, v\n}\n\nfor k, v in foo {\n    echo k, v\n}\n\necho {v: k for k, v in foo}\n```\n\n**Note: you can't use break/continue or return statements in for range of udt.Gop_Enum(callback).**\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n#### For range of UDT2\n\n```go\ntype FooIter struct {\n}\n\n// (Iterator) Next() (val ValType, ok bool) or:\n// (Iterator) Next() (key KeyType, val ValType, ok bool)\nfunc (p *FooIter) Next() (key int, val string, ok bool) {\n    // ...\n}\n\ntype Foo struct {\n}\n\n// Gop_Enum() Iterator\nfunc (p *Foo) Gop_Enum() *FooIter {\n    // ...\n}\n\nfoo := &Foo{}\nfor k, v := range foo {\n    echo k, v\n}\n\nfor k, v in foo {\n    echo k, v\n}\n\necho {v: k for k, v in foo}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Deduce struct type\n\n```go\ntype Config struct {\n    Dir   string\n    Level int\n}\n\nfunc foo(conf *Config) {\n    // ...\n}\n\nfoo {Dir: \"/foo/bar\", Level: 1}\n```\n\nHere `foo {Dir: \"/foo/bar\", Level: 1}` is equivalent to `foo(&Config{Dir: \"/foo/bar\", Level: 1})`. However, you can't replace `foo(&Config{\"/foo/bar\", 1})` with `foo {\"/foo/bar\", 1}`, because it is confusing to consider `{\"/foo/bar\", 1}` as a struct literal.\n\nYou also can omit struct types in a return statement. For example:\n\n```go\ntype Result struct {\n    Text string\n}\n\nfunc foo() *Result {\n    return {Text: \"Hi, XGo\"} // return &Result{Text: \"Hi, XGo\"}\n}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Overload operators\n\n```go\nimport \"math/big\"\n\ntype MyBigInt struct {\n    *big.Int\n}\n\nfunc Int(v *big.Int) MyBigInt {\n    return MyBigInt{v}\n}\n\nfunc (a MyBigInt) + (b MyBigInt) MyBigInt { // binary operator\n    return MyBigInt{new(big.Int).Add(a.Int, b.Int)}\n}\n\nfunc (a MyBigInt) += (b MyBigInt) {\n    a.Int.Add(a.Int, b.Int)\n}\n\nfunc -(a MyBigInt) MyBigInt { // unary operator\n    return MyBigInt{new(big.Int).Neg(a.Int)}\n}\n\na := Int(1r)\na += Int(2r)\necho a + Int(3r)\necho -a\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Auto property\n\nLet's see an example written in XGo:\n\n```go\nimport \"xgo/ast/goptest\"\n\ndoc := goptest.New(`... XGo code ...`)!\n\necho doc.Any().FuncDecl().Name()\n```\n\nIn many languages, there is a concept named `property` who has `get` and `set` methods.\n\nSuppose we have `get property`, the above example will be:\n\n```go\nimport \"xgo/ast/goptest\"\n\ndoc := goptest.New(`... XGo code ...`)!\n\necho doc.any.funcDecl.name\n```\n\nIn XGo, we introduce a concept named `auto property`. It is a `get property`, but is implemented automatically. If we have a method named `Bar()`, then we will have a `get property` named `bar` at the same time.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Go/XGo hybrid programming\n\nThis is an example to show how to mix Go/XGo code in the same package.\n\nIn this example, we have a Go source file named `a.go`:\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc p(a interface{}) {\n    sayMix()\n    fmt.Println(\"Hello,\", a)\n}\n```\n\nAnd we have an XGo source file named `b.xgo`:\n\n```go\nfunc sayMix() {\n    echo \"Mix Go and XGo\"\n}\n\np \"world\"\n```\n\nYou can see that Go calls an XGo function named `sayMix`, and XGo calls a Go function named `p`. As you are used to in Go programming, this kind of circular reference is allowed.\n\nRun `xgo run .` to see the output of this example:\n\n```\nMix Go and XGo\nHello, world\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Run XGo in watch mode\n\nThe `xgo` command can run in watch mode so that everytime an XGo file is changed it is transpiled to a Go file:\n\n```\nxgo watch [-gentest] [dir]\n```\n\nBy default `xgo watch` does not convert test files (normally ending with `_test.xgo`). You can specify `-gentest` flag to force converting all XGo files.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Calling C from XGo\n\nHere is [an example to show how XGo interacts with C](https://github.com/goplus/xgo/tree/main/demo/_llgo/hellollgo).\n\n```go\nimport \"c\"\n\nc.printf c\"Hello, llgo!\\n\"\nc.fprintf c.Stderr, c\"Hi, %6.1f\\n\", 3.14\n```\n\nHere `import \"c\"` is used to import libc. In this example we call two C standard functions `printf` and `fprintf`, passing a C variable `stderr` and two C strings in the form of `c\"xxx\"` (an XGo syntax to represent C-style strings).\n\nTo run this demo, you need to set the `XGO_GOCMD` environment variable first.\n\n```sh\nexport XGO_GOCMD=llgo  # default is `go`\n```\n\nThen execute `xgo run .` to see the output of this example:\n\n```\nHello, llgo!\nHi,    3.1\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Data processing\n\n### Rational numbers\n\nWe introduce rational numbers as primitive XGo types. We use suffix `r` to denote rational literals. For example, `1r << 200` means a big int whose value is equal to 2<sup>200</sup>.\n\n```go\na := 1r << 200\nb := bigint(1 << 200)\n```\n\nBy default, `1r` will have the type of `bigint`.\n\nAnd `4/5r` means the rational constant `4/5`.\nIt will have the type of `bigrat`.\n\n```go\na := 4/5r\nb := a - 1/3r + 3 * 1/2r\necho a, b // 4/5 59/30\n```\n\nCasting rational numbers works like other [primitive types](#primitive-types):\n\n```go\na := 1r\nb := bigrat(1r)\nc := bigrat(1)\necho a/3 // 0\necho b/3 // 1/3\necho c/3 // 1/3\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### List comprehension\n\n```go\na := [x*x for x in [1, 3, 5, 7, 11]]\nb := [x*x for x in [1, 3, 5, 7, 11] if x > 3]\nc := [i+v for i, v in [1, 3, 5, 7, 11] if i%2 == 1]\n\narr := [1, 2, 3, 4, 5, 6]\nd := [[a, b] for a in arr if a < b for b in arr if b > 2]\n\nx := {x: i for i, x in [1, 3, 5, 7, 11]}\ny := {x: i for i, x in [1, 3, 5, 7, 11] if i%2 == 1}\nz := {v: k for k, v in {1: \"Hello\", 3: \"Hi\", 5: \"xsw\", 7: \"XGo\"} if k > 3}\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Select data from a collection\n\n```go\ntype student struct {\n    name  string\n    score int\n}\n\nstudents := [student{\"Ken\", 90}, student{\"Jason\", 80}, student{\"Lily\", 85}]\n\nunknownScore, ok := {x.score for x in students if x.name == \"Unknown\"}\njasonScore := {x.score for x in students if x.name == \"Jason\"}\n\necho unknownScore, ok // 0 false\necho jasonScore // 80\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n### Check if data exists in a collection\n\n```go\ntype student struct {\n    name  string\n    score int\n}\n\nstudents := [student{\"Ken\", 90}, student{\"Jason\", 80}, student{\"Lily\", 85}]\n\nhasJason := {for x in students if x.name == \"Jason\"} // is any student named Jason?\nhasFailed := {for x in students if x.score < 60}     // is any student failed?\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Domain-Specific Text Literals\n\nDomain-specific text literals allow you to write inline code in specialized formats—such as JSON, XML, regular expressions, or custom DSLs—without sacrificing the benefits of compile-time checking and editor support.\n\n**Basic syntax:**\n\n```go\nresult := domainTag`content`\n```\n\n**With parameters:**\n\n```go\nresult := domainTag`> param1, param2\ncontent\n`\n```\n\nThe `!` suffix forces error handling, causing a panic if parsing fails—useful for literals you expect to always be valid.\n\n## Built-in Formats\n\nXGo currently supports several domain text literals natively:\n\n### Text Processing Language (tpl)\n\nA grammar-based alternative to regular expressions that emphasizes clarity and composability. Ideal for defining parsers and text processors.\n\n```go\ngrammar := tpl`\nexpr = term % (\"+\" | \"-\")\nterm = INT % (\"*\" | \"/\")\n`!\n\nresult := grammar.parseExpr(\"10+5*2\", nil)\necho result\n```\n\nLearn more in the [TPL documentation](../tpl/README.md).\n\n### JSON\n\nParse and validate JSON structures inline:\n\n```go\nconfig := json`{\n\t\"server\": \"localhost\",\n\t\"port\": 8080,\n\t\"features\": [\"auth\", \"logging\"]\n}`!\n\necho config.port\n```\n\n### XML\n\nWork with XML documents directly:\n\n```go\ndoc := xml`\n<configuration>\n\t<database>\n\t\t<host>localhost</host>\n\t\t<port>5432</port>\n\t</database>\n</configuration>\n`!\n```\n\n### CSV\n\nDefine tabular data inline:\n\n```go\ndata := csv`\nname,age,city\nAlice,30,NYC\nBob,25,SF\n`!\n```\n\n### HTML\n\nEmbed HTML with proper parsing (requires `golang.org/x/net/html`):\n\n```go\nimport \"golang.org/x/net/html\"\n\npage := html`\n<html>\n\t<body>\n\t\t<h1>Welcome</h1>\n\t\t<p>Domain-specific literals in action</p>\n\t</body>\n</html>\n`!\n```\n\n### Regular Expressions\n\nDefine regex patterns with improved readability. XGo supports both standard and POSIX regex:\n\n```go\npattern := regexp`^[a-z]+\\[[0-9]+\\]$`!\n\nif pattern.matchString(\"item[42]\") {\n\techo \"Match found\"\n}\n\n// POSIX variant\nposixPattern := regexposix`[[:alpha:]]+`!\n```\n\n## Implementation Details\n\nDomain text literals compile to function calls to the corresponding package's `New()` function. For example:\n\n```go\njson`{\"key\": \"value\"}`\n// Compiles to:\njson.New(`{\"key\": \"value\"}`)\n```\n\nThis design keeps the feature simple while allowing seamless integration with existing Go packages. The `domainTag` represents a package that must have a global `func New(string)` function with any return type.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Unix shebang\n\nYou can use XGo programs as shell scripts now. For example:\n\n```go\n#!/usr/bin/env -S xgo run\n\necho \"Hello, XGo\"\n\necho 1r << 129\necho 1/3r + 2/7r*2\n\narr := [1, 3, 5, 7, 11, 13, 17, 19]\necho arr\necho [x*x for x in arr, x > 3]\n\nm := {\"Hi\": 1, \"XGo\": 2}\necho m\necho {v: k for k, v in m}\necho [k for k, _ in m]\necho [v for v in m]\n```\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Compatibility with Go\n\nAll Go features will be supported (including partially support `cgo`, see [below](#bytecode-vs-go-code)).\n\n**All Go packages (even these packages use `cgo`) can be imported by XGo.**\n\n```coffee\nimport (\n    \"fmt\"\n    \"strings\"\n)\n\nx := strings.NewReplacer(\"?\", \"!\").Replace(\"hello, world???\")\nfmt.Println \"x:\", x\n```\n\n**And all XGo packages can also be imported in Go programs. What you need to do is just using `xgo` command instead of `go`.**\n\nFirst, let's make a directory named `14-Using-goplus-in-Go`.\n\nThen write an XGo package named [foo](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/foo) in it:\n\n```go\npackage foo\n\nfunc ReverseMap(m map[string]int) map[int]string {\n    return {v: k for k, v in m}\n}\n```\n\nThen use it in a Go package [14-Using-goplus-in-Go/gomain](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/gomain):\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/goplus/tutorial/14-Using-goplus-in-Go/foo\"\n)\n\nfunc main() {\n    rmap := foo.ReverseMap(map[string]int{\"Hi\": 1, \"Hello\": 2})\n    fmt.Println(rmap)\n}\n```\n\nHow to build this example? You can use:\n\n```bash\nxgo install -v ./...\n```\n\nGo [github.com/goplus/tutorial/14-Using-goplus-in-Go](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go) to get the source code.\n\n<h5 align=\"right\"><a href=\"#table-of-contents\">⬆ back to toc</a></h5>\n\n\n## Bytecode vs. Go code\n\nXGo supports bytecode backend and Go code generation.\n\nWhen we use `xgo` command, it generates Go code to covert XGo package into Go packages.\n\n```bash\nxgo run     # Run an XGo program\nxgo install # Build XGo files and install target to GOBIN\nxgo build   # Build XGo files\nxgo test    # Test XGo packages\nxgo fmt     # Format XGo packages\nxgo clean   # Clean all XGo auto generated files\nxgo go      # Convert XGo packages into Go packages\n```\n\nWhen we use [`ixgo`](https://github.com/goplus/ixgo) command, it interprets and executes the program.\n\n```bash\nixgo # Run an XGo program\n```\n\nIn bytecode mode, XGo doesn't support `cgo`. However, in Go-code-generation mode, XGo fully supports `cgo`.\n"
  },
  {
    "path": "doc/domian-text-lit.md",
    "content": "Domain Text Literals\n=====\n\nXGo's domain-specific text literals provide a powerful way to embed specialized languages directly into your code with full syntax highlighting and type safety. This feature bridges the gap between general-purpose programming and domain-specific needs, making your code more expressive and maintainable.\n\n## Overview\n\nDomain-specific text literals allow you to write inline code in specialized formats—such as JSON, XML, regular expressions, or custom DSLs—without sacrificing the benefits of compile-time checking and editor support.\n\n**Basic syntax:**\n\n```go\nresult := domainTag`content`\n```\n\n**With parameters:**\n\n```go\nresult := domainTag`> param1, param2\ncontent\n`\n```\n\nThe `!` suffix forces error handling, causing a panic if parsing fails—useful for literals you expect to always be valid.\n\n## Design Inspiration\n\nThis syntax is inspired by **Markdown's code blocks**. Just as Markdown uses triple backticks with a language identifier (` ```json`) to denote code blocks in a specific language, XGo's domain-specific literals use a similar pattern—a tag followed by backticks—to embed domain-specific content directly in your code. This familiar syntax makes the feature intuitive for developers already comfortable with Markdown while bringing the same clarity and language-specific semantics to your programming workflow.\n\n## Core Benefits\n\n- **Type Safety**: Catch errors at compile time rather than runtime\n- **Syntax Highlighting**: Full editor support for embedded languages\n- **Readability**: Keep domain-specific code inline where it's used\n- **Maintainability**: Easier to update and refactor than string concatenation\n- **Tooling Support**: Enables semantic understanding by XGo tools like formatters and IDEs\n\n## Built-in Formats\n\nXGo currently supports several domain text literals natively:\n\n### Text Processing Language (tpl)\n\nA grammar-based alternative to regular expressions that emphasizes clarity and composability. Ideal for defining parsers and text processors.\n\n```go\ngrammar := tpl`\nexpr = term % (\"+\" | \"-\")\nterm = INT % (\"*\" | \"/\")\n`!\n\nresult := grammar.parseExpr(\"10+5*2\", nil)\necho result\n```\n\nLearn more in the [TPL documentation](../tpl/README.md).\n\n### JSON\n\nParse and validate JSON structures inline:\n\n```go\nconfig := json`{\n\t\"server\": \"localhost\",\n\t\"port\": 8080,\n\t\"features\": [\"auth\", \"logging\"]\n}`!\n\necho config.port\n```\n\n### XML\n\nWork with XML documents directly:\n\n```go\ndoc := xml`\n<configuration>\n\t<database>\n\t\t<host>localhost</host>\n\t\t<port>5432</port>\n\t</database>\n</configuration>\n`!\n```\n\n### CSV\n\nDefine tabular data inline:\n\n```go\ndata := csv`\nname,age,city\nAlice,30,NYC\nBob,25,SF\n`!\n```\n\n### HTML\n\nEmbed HTML with proper parsing (requires `golang.org/x/net/html`):\n\n```go\nimport \"golang.org/x/net/html\"\n\npage := html`\n<html>\n\t<body>\n\t\t<h1>Welcome</h1>\n\t\t<p>Domain-specific literals in action</p>\n\t</body>\n</html>\n`!\n```\n\n### Regular Expressions\n\nDefine regex patterns with improved readability. XGo supports both standard and POSIX regex:\n\n```go\npattern := regexp`^[a-z]+\\[[0-9]+\\]$`!\n\nif pattern.matchString(\"item[42]\") {\n\techo \"Match found\"\n}\n\n// POSIX variant\nposixPattern := regexposix`[[:alpha:]]+`!\n```\n\n## Implementation Details\n\nDomain text literals compile to function calls to the corresponding package's `New()` function. For example:\n\n```go\njson`{\"key\": \"value\"}`\n// Compiles to:\njson.New(`{\"key\": \"value\"}`)\n```\n\nThis design keeps the feature simple while allowing seamless integration with existing Go packages. The `domainTag` represents a package that must have a global `func New(string)` function with any return type.\n\n## Creating Custom Formats\n\nExtend XGo with your own domain-specific languages by implementing a package with a global `New(string)` function:\n\n```go\n// Package sql provides SQL query literals\npackage sql\n\ntype Query struct {\n\ttext string\n}\n\nfunc New(query string) (*Query, error) {\n\t// Validate and parse SQL\n\tif err := validateSQL(query); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Query{text: query}, nil\n}\n```\n\n**Usage:**\n\n```go\nimport \"myproject/sql\"\n\nquery := sql`\nSELECT id, name, email \nFROM users \nWHERE active = true\n`!\n```\n\n## Beyond Syntactic Sugar\n\nDomain text literals offer more than just convenient syntax. They enable XGo tooling to understand the semantics of these embedded texts rather than treating them as ordinary strings. This semantic understanding enables:\n\n- **Code formatters** like `xgo fmt` to format both XGo code and supported domain texts simultaneously\n- **IDE plugins** to provide syntax highlighting and advanced features for recognized domain texts\n- **Static analysis tools** to validate domain-specific content at build time\n- **Documentation generators** to extract and document embedded domain content\n\n## Best Practices\n\n1. **Use the `!` suffix for static literals** that should always be valid—this catches errors early\n2. **Handle errors explicitly for dynamic content** that might fail validation\n3. **Keep literals focused** on their domain—avoid mixing concerns\n4. **Leverage syntax highlighting** by configuring your editor for the embedded languages\n5. **Document custom formats** clearly to help other developers understand their usage\n\n## Error Handling\n\nWithout the `!` suffix, domain literals return an error that you can handle:\n\n```go\nquery, err := sql`SELECT * FROM ${table}`\nif err != nil {\n\treturn fmt.Errorf(\"invalid query: %w\", err)\n}\n```\n\nWith the `!` suffix, invalid literals cause a panic:\n\n```go\n// This panics if the JSON is malformed\ndata := json`{\"invalid\": }`!\n```\n\n---\n\n## Historical Background\n\nThe journey of domain text literals in XGo began with a [community proposal in early 2024](https://github.com/goplus/xgo/issues/1770) suggesting adding JSX syntax support to XGo. While JSX has gained widespread adoption in frontend development, particularly in React-based applications, the immediate benefits of building JSX syntax directly into XGo weren't immediately clear, causing the proposal to be temporarily shelved.\n\nThe turning point came when XGo needed to support [TPL (Text Processing Language)](../tpl/README.md) syntax for the [XGo Mini Spec](spec-mini.md) project. This necessity prompted a reconsideration of how XGo should handle domain-specific notations more broadly.\n\n### The Philosophy Behind Domain Text Literals\n\nA common understanding in programming language design suggests that **Domain-Specific Languages (DSLs)** often struggle to compete with general-purpose languages. However, this perspective overlooks the fact that numerous domain languages exist and thrive in specialized contexts:\n\n- **Interface description**: HTML, JSX\n- **Configuration and data representation**: JSON, YAML, CSV\n- **Text syntax representation**: EBNF-like grammar (including TPL syntax), regular expressions\n- **Document formats**: Markdown, DOCX, HTML\n\nWhat distinguishes these domain languages is that they aren't Turing-complete. They lack the full capabilities of general-purpose languages, such as I/O operations, function definitions, and comprehensive flow control structures.\n\nRather than competing with general-purpose languages, these domain languages typically complement them. Most mainstream programming languages either officially support or have community-built libraries to interact with these domain languages.\n\nThis complementary relationship led to the term \"**Domain Text Literals**\" rather than \"**Domain-Specific Languages**\", emphasizing their role as specialized text formats that can be embedded within general-purpose code.\n\n### Syntax Evolution\n\nAfter considerable deliberation on how XGo should support domain text literals, inspiration came from Markdown's code block syntax. Initially, there was consideration to make XGo's domain text syntax identical to Markdown's. However, this would have prevented XGo code from being embedded as a domain text within Markdown documents, potentially reducing interoperability between XGo and Markdown. After careful consideration, the current syntax was chosen to ensure optimal compatibility while maintaining the familiar, intuitive pattern that developers already know from Markdown.\n\n---\n\nDomain-specific text literals make XGo uniquely suited for projects that need to work with multiple specialized formats. By treating domain-specific languages as first-class citizens, XGo helps you write cleaner, safer, and more maintainable code.\n"
  },
  {
    "path": "doc/fncall.md",
    "content": "# Commands and Function Calls\n\nIn XGo, there's a fundamental unity beneath seemingly different syntactic forms: **commands, function calls, and operators are all essentially function invocations**. This unified model makes the language both intuitive for beginners and consistent for experienced programmers.\n\n## The Unified Function Model\n\nConsider these three ways of invoking functions:\n\n```go\necho \"Hello\"           // Command style\necho(\"Hello\")          // Function call style\n3 + 4                  // Operator style\n```\n\nWhile they look different, all three represent function invocations. The different syntaxes simply provide flexibility in how you express intent.\n\n### Command Style: Natural and Intuitive\n\nCommands look like natural language instructions:\n\n```go\necho \"Hello world\"\nprintln \"Temperature:\", 25.5\ntime.sleep 2*time.Second\n```\n\n**Key characteristic:** Parentheses are optional. Arguments follow the command naturally, making code read like sentences.\n\n### Function Call Style: Explicit and Familiar\n\nFunction calls use traditional syntax with mandatory parentheses:\n\n```go\necho(\"Hello world\")\nprintln(\"Temperature:\", 25.5)\ntime.sleep(2*time.Second)\n```\n\n**Key characteristic:** Explicit parentheses make nesting and composition clearer in complex expressions.\n\n### Operators: Mathematical Notation for Functions\n\nOperators use familiar mathematical notation:\n\n```go\n3 + 4        // Addition\nx * y        // Multiplication\na == b       // Equality comparison\n```\n\nWhile operators look like special symbols, they're actually function calls in disguise. The `+` operator calls an addition function, `*` calls a multiplication function, and so on. This syntax matches mathematical conventions, making numeric code natural to read and write.\n\n**Note:** XGo allows you to define your own operators (covered in advanced topics), reinforcing that operators are truly functions at their core.\n\n## Categories of Functions\n\nFunctions in XGo come from three sources, each accessed slightly differently:\n\n### 1. Built-in Functions\n\nBuilt-in functions are always available without any imports. They're part of the language core.\n\n#### Input/Output Functions\n\n```go\necho \"Hello\", \"World\"     // Output with spaces and newline\nprint \"Hello\", \"World\"    // Output without spaces, no newline\n```\n\nBoth forms work identically:\n```go\necho \"Result:\", 42         // Command style\necho(\"Result:\", 42)        // Function call style\n```\n\n**Difference between `echo` and `print`:**\n- `echo` adds spaces between arguments and ends with a newline\n- `print` concatenates arguments directly without spaces or newline\n\n```go\necho \"A\", \"B\", \"C\"    // Output: A B C\\n\nprint \"A\", \"B\", \"C\"   // Output: ABC\n```\n\n#### Error Handling\n\n```go\npanic \"Something went wrong!\"\npanic(\"Fatal error: division by zero\")\n```\n\nThe `panic` function stops program execution immediately—use it for unrecoverable errors.\n\n#### Operators as Built-in Functions\n\nArithmetic and comparison operators are also built-in functions:\n\n```go\nsum := 3 + 4           // Addition operator\nproduct := 5 * 6       // Multiplication operator\nequal := (x == y)      // Equality operator\n```\n\nThe operator syntax is designed to match mathematical notation, but conceptually these are function invocations. This is why you can define custom operators in XGo—they're not special language primitives, just functions with infix notation.\n\n### 2. Package Functions\n\nFunctions from packages are accessed through import and qualified names.\n\n#### Importing Packages\n\nPlace all imports at the beginning of your file:\n\n```go\nimport \"math\"\nimport \"time\"\n```\n\nOr use the grouped form:\n\n```go\nimport (\n    \"math\"\n    \"time\"\n)\n```\n\n#### Using Package Functions\n\nAccess package functions with dot notation: `packageName.functionName`\n\n```go\nimport \"math\"\n\necho math.sqrt(16)        // Square root: 4\necho math.pow(2, 3)       // Power: 8\necho math.abs(-5)         // Absolute value: 5\n```\n\n**Lowercase calling convention:** XGo provides a convenient feature—exported functions (which start with uppercase letters in Go convention) can be called with lowercase names:\n\n```go\n// In the math package, the actual function is Sqrt (uppercase)\nmath.sqrt(16)    // ✓ Lowercase call (recommended in XGo)\nmath.Sqrt(16)    // ✓ Original uppercase name (also works)\n\n// But you cannot call a lowercase function with uppercase\n// somePackage.DoSomething()  // ✗ Won't work if function is actually doSomething\n```\n\nThis feature is specifically designed to make code more readable while maintaining compatibility with Go's export rules. The convention is:\n- Exported functions start with uppercase (Go requirement)\n- You can call them with lowercase for convenience (XGo feature)\n- The reverse is not true—lowercase functions must be called with lowercase\n\n**Omitting parentheses:** For zero-parameter functions, parentheses are optional when using lowercase names:\n\n```go\nimport \"time\"\n\necho time.now        // Current time (no parentheses needed)\necho time.now()      // Same thing with explicit call\necho time.Now()      // Also works with uppercase\n```\n\n#### Common Package Examples\n\n**Math operations:**\n```go\nimport \"math\"\n\nmath.sqrt(16)              // 4\nmath.pow(2, 8)             // 256\nmath.max(10, 20)           // 20\nmath.min(10, 20)           // 10\nmath.Pi                    // 3.141592653589793 (constant, not a function)\n```\n\n**Time operations:**\n```go\nimport \"time\"\n\ntime.now                   // Current timestamp\ntime.sleep 2*time.Second   // Pause for 2 seconds\n```\n\n**Note on constants:** Packages also provide constants like `math.Pi` and `time.Second`. These are accessed the same way as functions but represent fixed values rather than executable code.\n\n### 3. Methods: Functions Belonging to Objects\n\nMethods are functions that operate on specific objects. They're called using dot notation: `object.method()`\n\nThink of methods as actions an object can perform. A string can be converted to uppercase, a time can tell you what day of the week it is.\n\n#### String Methods\n\nStrings have built-in methods for common operations:\n\n```go\n\"Hello\".len              // 5 (length of string)\n\"Hello\".toUpper          // \"HELLO\"\n\"Hello\".toLower          // \"hello\"\n\"Go\".repeat(3)           // \"GoGoGo\"\n\"Hello\".replaceAll(\"l\", \"L\")  // \"HeLLo\"\n```\n\n**Zero-parameter methods:** Like package functions, methods without parameters can omit parentheses:\n\n```go\n\"Hello\".len        // Parentheses optional\n\"Hello\".len()      // Explicit call—same result\n```\n\n**Methods with parameters:** Require parentheses:\n\n```go\n\"Go\".repeat(3)                    // Must use parentheses\n\"Hello\".replaceAll(\"l\", \"L\")      // Must use parentheses\n```\n\n#### Time Methods\n\nTime objects returned from `time.now` have methods to extract components:\n\n```go\nimport \"time\"\n\nnow := time.now\n\necho now.weekday      // e.g., \"Wednesday\"\necho now.year         // e.g., 2025\necho now.month        // e.g., \"February\"\necho now.day          // e.g., 15\necho now.hour         // e.g., 14 (24-hour format, UTC)\necho now.minute       // e.g., 30\necho now.second       // e.g., 45\n```\n\nAll these methods work without parentheses since they take no parameters.\n\n**Chaining method calls:**\n\n```go\nimport \"time\"\n\necho time.now.weekday         // Current day of week\necho time.now.year            // Current year\n```\n\n## Understanding the Dot Notation\n\nThe dot (`.`) connects an object to its method or a package to its function:\n\n```go\n// Package.function\nmath.sqrt(16)\ntime.now\n\n// Object.method\n\"Hello\".toUpper\ntime.now.weekday\n```\n\nBoth follow the same pattern, making the language consistent and predictable.\n\n## Practical Examples\n\n### Combining Different Function Types\n\n```go\nimport \"math\"\nimport \"time\"\n\n// Built-in + Package function\necho \"Square root of 25 is\", math.sqrt(25)\n\n// Package function + Method\necho \"Today is\", time.now.weekday\n\n// Operator + Built-in + Method\nresult := 3 + 4\nmessage := \"Result: \" + result.string\necho message.toUpper\n```\n\n### Command vs. Function Call Style\n\nUse command style for simple, top-level statements:\n\n```go\necho \"Starting calculation...\"\nresult := math.sqrt(144)\necho \"Result:\", result\n```\n\nUse function call style for nested expressions:\n\n```go\n// Clear nesting with explicit parentheses\necho math.sqrt(math.pow(3, 2) + math.pow(4, 2))  // Pythagorean theorem\n\n// String method in expression\nname := \"alice\"\necho \"Hello, \" + name.toUpper()\n```\n\n### Real-World Example\n\n```go\nimport \"time\"\nimport \"math\"\n\n// Get current time details\nnow := time.now\necho \"Current time:\", now\necho \"Day:\", now.weekday\necho \"Date:\", now.year, \"-\", now.month, \"-\", now.day\n\n// Wait a bit\ntime.sleep 2*time.Second\n\n// Do some calculations\nvalue := math.pow(2, 10)\necho \"2^10 =\", value\necho \"Square root:\", math.sqrt(value)\n\n// String manipulation\nmessage := \"processing complete\"\necho message.toUpper()\n```\n\n## Key Takeaways\n\n1. **Everything is a function call** at the conceptual level—commands, function calls, and operators all invoke functions\n2. **Operators use mathematical notation** but represent function calls underneath, allowing for custom operator definitions\n3. **Built-in functions** like `echo`, `print`, and operators are always available\n4. **Package functions** require imports and use `package.function` syntax\n5. **Methods** are functions that belong to objects, using `object.method` syntax\n6. **Parentheses are optional** for commands, and for zero-parameter functions and methods when using lowercase names\n7. **Lowercase calling convention:** Uppercase-exported functions can be called with lowercase (e.g., `math.sqrt` for `math.Sqrt`), but not vice versa\n\nThis unified model means once you understand one form, you understand them all. Whether you write `echo \"Hello\"` or `echo(\"Hello\")`, whether you use `3 + 4` or call `time.now`, you're invoking functions—just with different syntactic styles suited to different situations.\n"
  },
  {
    "path": "doc/func-closure.md",
    "content": "# Functions and Closures\n\n## Basic Function Definition\n\nFunctions in XGo are defined with clear type specifications for parameters and return values:\n\n```go\nfunc add(x int, y int) int {\n    return x + y\n}\n\necho add(2, 3) // 5\n```\n\n### Parameter List Rules\n\nWhen multiple consecutive parameters share the same type, you can combine them for more concise syntax:\n\n```go\n// Verbose form: each parameter has its own type\nfunc add(x int, y int) int {\n    return x + y\n}\n\n// Concise form: parameters of the same type can be grouped\nfunc add(x, y int) int {\n    return x + y\n}\n\n// Mixed types require separate declarations\nfunc greet(firstName, lastName string, age int) {\n    echo firstName, lastName, \"is\", age, \"years old\"\n}\n```\n\nThe same grouping rule applies to return values:\n\n```go\n// Multiple return values of the same type can be grouped\nfunc minMax(a, b int) (min, max int) {\n    if a < b {\n        return a, b\n    }\n    return b, a\n}\n\n// Mixed return types require separate declarations\nfunc divide(a, b float64) (result float64, err error) {\n    if b == 0 {\n        return 0, errors.New(\"division by zero\")\n    }\n    return a / b, nil\n}\n```\n\n**Key points:**\n- Parameters or return values of the same type can be grouped: `x, y int` instead of `x int, y int`\n- Different types must be declared separately\n- This rule applies to both parameter lists and return value lists\n- Grouping improves readability without changing functionality\n\n### Functions Without Return Values\n\nFunctions don't always need to return values. When a function performs an action without producing a result, you can omit the return type:\n\n```go\nfunc greet(name string) {\n    echo \"Hello,\", name\n}\n\ngreet(\"Alice\") // Hello, Alice\n```\n\nYou can also explicitly return early from such functions using a bare `return` statement:\n\n```go\nfunc printPositive(x int) {\n    if x <= 0 {\n        return // exit early if condition not met\n    }\n    echo \"Positive number:\", x\n}\n\nprintPositive(5)  // Positive number: 5\nprintPositive(-3) // (prints nothing)\n```\n\n### Multiple Return Values\n\nFunctions can return multiple values simultaneously:\n\n```go\nfunc foo() (int, int) {\n    return 2, 3\n}\n\na, b := foo()\necho a // 2\necho b // 3\nc, _ := foo() // ignore values using `_`\n```\n\n### Named Return Values\n\nReturn values can be named to simplify return statements:\n\n```go\nfunc sum(a ...int) (total int) {\n    for x in a {\n        total += x\n    }\n    return // no need to explicitly return named values that are already assigned\n}\n\necho sum(2, 3, 5) // 10\n```\n\n## Optional Parameters\n\nXGo supports optional parameters using the `T?` syntax. Optional parameters default to their type's zero value:\n\n```go\nfunc greet(name string, count int?) {\n    if count == 0 {\n        count = 1\n    }\n    for i := 0; i < count; i++ {\n        echo \"Hello,\", name\n    }\n}\n\ngreet \"Alice\", 3  // prints \"Hello, Alice\" three times\ngreet \"Bob\"       // prints \"Hello, Bob\" once (uses default value)\n```\n\nOptional parameters are denoted by adding `?` after the parameter type. The default value is always the zero value of that type (e.g., `0` for integers, `\"\"` for strings, `false` for booleans).\n\n```go\nfunc connect(host string, port int?, secure bool?) {\n    if port == 0 {\n        port = 80\n    }\n    echo \"Connecting to\", host, \"on port\", port, \"secure:\", secure\n}\n\nconnect \"example.com\", 443, true  // Connecting to example.com on port 443 secure: true\nconnect \"example.com\"             // Connecting to example.com on port 80 secure: false\n```\n\n## Variadic Parameters\n\nVariadic parameters allow functions to accept a variable number of arguments of the same type using the `...` syntax:\n\n```go\nfunc sum(a ...int) int {\n    total := 0\n    for x in a {\n        total += x\n    }\n    return total\n}\n\necho sum(2, 3, 5)       // 10\necho sum(1, 2, 3, 4, 5) // 15\necho sum()              // 0\n```\n\nInside the function, the variadic parameter behaves as a slice of the specified type.\n\n### Parameter Positioning Rules\n\n**Important:** The variadic parameter must be the last parameter in the function signature, appearing after all regular parameters and optional parameters:\n\n```go\n// ✓ Correct: variadic parameter is last\nfunc log(level string, verbose bool?, messages ...string) {\n    // ...\n}\n\n// ✗ Wrong: variadic parameter must be last\nfunc log(messages ...string, level string) {  // Error!\n    // ...\n}\n\n// ✗ Wrong: variadic parameter must be after optional parameters\nfunc log(messages ...string, verbose bool?) {  // Error!\n    // ...\n}\n```\n\n### Combining Regular and Variadic Parameters\n\n```go\nfunc log(level string, messages ...string) {\n    echo \"[\" + level + \"]\", strings.Join(messages, \" \")\n}\n\nlog(\"INFO\", \"Server\", \"started\", \"successfully\")\n// Output: [INFO] Server started successfully\n```\n\n### Passing Slices to Variadic Parameters\n\nUse the `...` suffix to pass an existing slice to a variadic parameter:\n\n```go\nfunc max(nums ...int) int {\n    if len(nums) == 0 {\n        return 0\n    }\n    maxVal := nums[0]\n    for _, n in nums[1:] {\n        if n > maxVal {\n            maxVal = n\n        }\n    }\n    return maxVal\n}\n\nnumbers := []int{3, 7, 2, 9, 1}\necho max(numbers...)  // 9\necho max(5, 8, 3)     // 8\n```\n\n### Practical Examples\n\n```go\n// String formatting\nfunc format(template string, args ...any) string {\n    result := template\n    for i, arg in args {\n        result = strings.Replace(result, \"{${i}}\", fmt.Sprint(arg), 1)\n    }\n    return result\n}\n\necho format(\"Hello {0}, you have {1} messages\", \"Alice\", 5)\n// Output: Hello Alice, you have 5 messages\n```\n\n## Keyword Arguments\n\nXGo supports keyword arguments syntax for improved code readability and expressiveness. When calling functions with many parameters, you can use `key = value` syntax.\n\n### Using Maps for Keyword Arguments\n\n```go\nfunc process(opts map[string]any?, args ...any) {\n    if name, ok := opts[\"name\"]; ok {\n        echo \"Name:\", name\n    }\n    if age, ok := opts[\"age\"]; ok {\n        echo \"Age:\", age\n    }\n    echo \"Args:\", args\n}\n\nprocess name = \"Ken\", age = 17              // keyword parameters only\nprocess \"extra\", 1, name = \"Ken\", age = 17  // variadic parameters first, then keyword parameters\nprocess                                     // all parameters optional\n```\n\n**Best for:** Dynamic or extensible parameter sets, diverse parameter types, runtime parameter checking.\n\n### Using Tuples for Keyword Arguments\n\n```go\ntype Config (timeout, maxRetries int, debug bool)\n\nfunc run(task int, cfg Config?) {\n    if cfg.timeout == 0 {\n        cfg.timeout = 30\n    }\n    if cfg.maxRetries == 0 {\n        cfg.maxRetries = 3\n    }\n    echo \"timeout:\", cfg.timeout, \"maxRetries:\", cfg.maxRetries, \"debug:\", cfg.debug\n    echo \"task:\", task\n}\n\nrun 100, timeout = 60, maxRetries = 5\nrun 200\n```\n\n**Best for:** Fixed parameter sets with known types, compile-time validation, optimal performance. **Recommended for most use cases.**\n\n**Note:** Tuple field names must match exactly as defined - no automatic case conversion is performed.\n\n### Using Structs for Keyword Arguments\n\nStructs provide type safety and full runtime reflection support for keyword parameters:\n\n```go\ntype Config struct {\n    Timeout    int\n    MaxRetries int\n    Debug      bool\n}\n\nfunc run(cfg *Config?) {\n    timeout := 30\n    maxRetries := 3\n    debug := false\n    if cfg != nil {\n        if cfg.Timeout > 0 {\n            timeout = cfg.Timeout\n        }\n        if cfg.MaxRetries > 0 {\n            maxRetries = cfg.MaxRetries\n        }\n        debug = cfg.Debug\n    }\n    echo \"Timeout:\", timeout, \"MaxRetries:\", maxRetries, \"Debug:\", debug\n}\n\nrun timeout = 60, maxRetries = 5           // lowercase field names work\nrun Timeout = 10, Debug = true             // uppercase field names work too\nrun                                        // uses default values\n```\n\n**Best for:** Go codebase compatibility, struct features (methods, embedding, tags), runtime reflection needs.\n\n### Rules and Best Practices\n\n**Syntax Rules:**\n\n1. **Parameter Position Requirements**\n   - The keyword parameter must be an optional parameter (marked with `?`)\n   - The keyword parameter must be the last parameter (if no variadic parameters), or second-to-last when variadic parameters are present\n   \n2. **Call Order Requirements**\n   - When calling a function, keyword arguments must be placed after all normal parameters (including variadic parameters)\n\n```go\n// ✓ Correct call order\nprocess \"value\", key1 = \"a\", key2 = \"b\"\nprocess \"v1\", \"v2\", key = \"x\"\n\n// ✗ Wrong call order\nprocess key = \"x\", \"value\"  // keyword arguments must come last\n```\n\n**Type Selection:**\n\n- **Use Map** when you need dynamic parameters or runtime flexibility\n- **Use Tuple** for most cases - lightweight, compile-time validated, optimal performance (recommended)\n- **Use Struct** when you need Go compatibility or struct-specific features\n\n## Higher-Order Functions\n\nFunctions can be passed as parameters to other functions:\n\n```go\nfunc square(x float64) float64 {\n    return x*x\n}\n\nfunc abs(x float64) float64 {\n    if x < 0 {\n        return -x\n    }\n    return x\n}\n\nfunc transform(a []float64, f func(float64) float64) []float64 {\n    return [f(x) for x in a]\n}\n\ny := transform([1, 2, 3], square)\necho y // [1 4 9]\n\nz := transform([-3, 1, -5], abs)\necho z // [3 1 5]\n```\n\n## Lambda Expressions\n\nLambda expressions provide a concise way to define anonymous functions inline using the `=>` operator.\n\n### Basic Syntax\n\n```go\n// Single parameter (no parentheses needed)\nx => x * x\n\n// Multiple parameters (parentheses required)\n(x, y) => x + y\n\n// No parameters (no parentheses needed, just start with =>)\n=> someValue\n\n// Multi-line body\nx => {\n    result := x * 2\n    return result\n}\n```\n\n### Common Use Cases\n\n**Transformations:**\n\n```go\nfunc transform(a []float64, f func(float64) float64) []float64 {\n    return [f(x) for x in a]\n}\n\n// The lambda parameter type is inferred from transform's function parameter type\n// which expects func(float64) float64\ny := transform([1, 2, 3], x => x*x)           // [1 4 9]\n\nz := transform([-3, 1, -5], x => {\n    if x < 0 {\n        return -x\n    }\n    return x\n})                                            // [3 1 5]\n```\n\n**Combining values:**\n\n```go\nfunc combine(a, b []int, f func(int, int) int) (result []int) {\n    for i := 0; i < len(a) && i < len(b); i++ {\n        result = append(result, f(a[i], b[i]))\n    }\n    return result\n}\n\nsums := combine([1, 2, 3], [4, 5, 6], (x, y) => x + y)  // [5 7 9]\n```\n\n**Closures (capturing variables):**\n\n```go\nfunc multiplier(factor int) func(int) int {\n    return x => x * factor\n}\n\nfunc counter(start int) func() int {\n    count := start\n    return => {\n        count++\n        return count\n    }\n}\n\ndouble := multiplier(2)\necho double(5)  // 10\n\nc := counter(0)\necho c()  // 1\necho c()  // 2\n```\n\n**Sorting and filtering:**\n\n```go\nnumbers := [1, 2, 3, 4, 5, 6]\nevens := filter(numbers, x => x % 2 == 0)  // [2 4 6]\n\nsort.Slice(products, (i, j) => products[i].Price < products[j].Price)\n```\n\n**Event handling:**\n\n```go\n// Event registration functions\nfunc OnStart(onStart func())\nfunc OnMsg(msg string, onMsg func())\n\n// Register event handlers with lambdas\n// With one argument (the lambda), no comma is needed.\nonStart => {\n    echo \"Game started!\"\n    initializeGame()\n}\n\n// With multiple arguments, use a comma to separate them.\nonMsg \"game over\", => {\n    echo \"Game over!\"\n    cleanup()\n}\n```\n\n**Note:** If a function with the lowercase name doesn't exist, XGo will automatically look for the capitalized version (e.g., if `onStart` is not defined, it tries `OnStart`). This allows for more flexible and natural function calling syntax.\n\n### Type Inference\n\nParameter and return types are automatically inferred from context. XGo lambdas do not support explicit type annotations:\n\n```go\n// Types are inferred from the function signature\ntransform([1, 2, 3], x => x * 2)\n\n// The lambda parameter type is inferred from transform's function parameter type\n// which expects func(float64) float64\n```\n\n### When to Use\n\n**Use lambdas for:**\n- One-off functions used inline\n- Simple transformations and filters\n- Callbacks and event handlers\n- Building processing pipelines\n\n**Use named functions for:**\n- Reusable logic\n- Complex operations needing documentation\n- Public APIs\n\n## Summary\n\nXGo's function system combines powerful features:\n- Standard function definitions with multiple return values\n- Functions without return values for action-oriented operations\n- Optional parameters for flexible function calls\n- Comprehensive variadic parameters for variable-length argument lists\n- Keyword arguments with maps, tuples, or structs for improved readability\n- Higher-order functions for functional programming patterns\n- Elegant and expressive lambda expressions for inline function definitions\n- Closures for capturing and maintaining state\n\nThese features work together to create a versatile and expressive function system that supports both traditional imperative programming and modern functional programming paradigms.\n"
  },
  {
    "path": "doc/goodbye-printf.md",
    "content": "Goodbye printf\n=====\n\nFor professional programmers, `printf` is a very familiar function, and it can be found in basically every language. However, `printf` is one of the most difficult functions for beginners to master.\n\nUnfortunately, formatting a piece of information and displaying it to the user is a very common operation, so one has to remember their usage. While finding its documentation over the Internet can somewhat ease the burden of using it every time, it's far from a pleasant experience.\n\nThe most primitive way to format information is to use `string concat`:\n\n```go\nage := 10\nprintln \"age = \" + age.string\n```\n\nAnd the most classic way of formatting information is to use `printf`:\n\n```go\nage := 10\nprintf \"age = %d\\n\", age\n```\n\nHere `%d` means to format an integer value and `\\n` means a newline.\n\nTo simplify format information in most cases, XGo introduces `${expr}` expressions in string literals. For above example, you can replace `age.string` to `\"${age}\"`:\n\n```go\nage := 10\nprintln \"age = ${age}\"\n```\n\nHere is a more complex example of `${expr}`:\n\n```go\nhost := \"foo.com\"\npage := 0\nlimit := 20\nprintln \"https://${host}/items?page=${page+1}&limit=${limit}\" // https://foo.com/items?page=1&limit=20\nprintln \"$$\" // $\n```\n\nThis is a bit like how you feel at the `*nix` command line, right? To be more like it, we introduced a new builtin `echo` as an alias for `println`:\n\n```go\nage := 10\necho \"age = ${age}\"\n```\n\n"
  },
  {
    "path": "doc/map.md",
    "content": "# Map Type\n\nXGo provides a concise syntax for working with maps. Maps are key-value data structures that allow you to store and retrieve values using keys.\n\n## Creating Maps\n\nXGo provides two ways to create maps: using map literals for quick initialization with data, and using the `make` function for more control over map types and capacity.\n\n### Map Literal Syntax\n\nIn XGo, you can create maps using curly braces `{}`:\n\n```go\na := {\"Hello\": 1, \"xsw\": 3}     // map[string]int\nb := {\"Hello\": 1, \"xsw\": 3.4}   // map[string]float64\nc := {\"Hello\": 1, \"xsw\": \"XGo\"} // map[string]any\ne := {1: \"one\", 2: \"two\"}       // map[int]string\nd := {}                         // map[string]any\n```\n\n#### Automatic Type Inference\n\nWhen using the `:=` syntax without explicit type declaration, XGo automatically infers the complete map type `map[KeyType]ValueType` based on the literal syntax and values provided.\n\n**Type Inference Rules**\n\nBoth `KeyType` and `ValueType` follow the same inference rules:\n\n1. **Uniform Types**: If all elements have the same type, that type is used.\n2. **Mixed Types**: If elements have different types, the type is inferred as `any`.\n3. **Empty Map** `{}`: Inferred as `map[string]any` by default for maximum flexibility.\n\nYou can also explicitly specify the map type to override automatic type inference:\n\n```go\nvar a map[string]float64 = {\"Hello\": 1, \"xsw\": 3}  // Values converted to float64\nvar c map[string]any = {\"x\": 1, \"y\": \"text\"}       // Explicit any type\n```\n\nWhen a type is explicitly declared, the literal values are converted to match the declared type.\n\n### Creating Maps with `make`\n\nUse `make` when you need an empty map or want to optimize performance by pre-allocating capacity.\n\n#### Basic `make` Syntax\n\n```go\n// Basic creation\nm := make(map[string]int)\n\nm[\"count\"] = 42\necho m  // Output: map[count:42]\n\n// Create a map with complex key types\ntype Point (x, y int)\npositions := make(map[Point]string)\npositions[(0, 0)] = \"origin\"\n```\n\n#### Pre-allocating Capacity\n\nFor performance optimization, you can specify an initial capacity hint:\n\n```go\n// Create a map with initial capacity for ~100 elements\n// Pre-allocating capacity (helps performance for large maps)\nlargeMap := make(map[string]int, 100)\n\n// This doesn't limit the map size, but helps reduce allocations\nfor i := 0; i < 150; i++ {\n    largeMap[\"key${i}\"] = i\n}\n```\n\nThe capacity hint doesn't limit the map's size but helps the runtime allocate memory more efficiently when you know approximately how many elements you'll add.\n\n#### When to Use `make` vs Literals\n\n**Use map literals** (`{}`) when:\n- You have initial data to populate\n- You want automatic type inference for convenience\n- You prefer concise, readable code\n\n**Use map literals with explicit type** (`var m map[K]V = {}`) when:\n- You have initial data with a specific type requirement\n- You need type safety while keeping syntax concise\n- You want to ensure value types are converted correctly\n\n**Use `make`** when:\n- You're creating an empty map and plan to add elements later\n- You want to pre-allocate capacity for performance\n- You prefer the traditional Go style\n- Working with codebases that consistently use `make`\n\n## Map Operations\n\nBefore manipulating maps, it is important to understand that XGo supports two notations for referencing keys:\n\n- **Bracket Notation** (`m[\"key\"]`): The universal syntax. It works for all key types and allows using variables as keys.\n- **Field Access Notation** (`m.key`): A convenient shorthand for string-keyed maps when the key is a valid identifier (no spaces or special characters).\n\n**Field access is pure syntax sugar** - `m.field` and `m[\"field\"]` behave identically in all contexts.\n\nBoth notations are used for both **assigning** values and **retrieving** them.\n\n### Adding and Updating Elements\n\nTo add a new key-value pair or update an existing one, assign a value to a key using either notation. If the key exists, its value is updated; otherwise, a new entry is created.\n\n```go\na := {\"a\": 1, \"b\": 0}\n\n// Using bracket notation\na[\"c\"] = 100\n\n// Using field notation\na.d = 200\n\necho a  // Output: map[a:1 b:0 c:100 d:200]\n\n// Works with maps created by make too\nm := make(map[string]int)\nm[\"x\"] = 10\nm.y = 20\necho m  // Output: map[x:10 y:20]\n```\n\n### Deleting Elements\n\nUse the `delete` function to remove elements from a map:\n\n```go\na := {\"a\": 1, \"b\": 0, \"c\": 100}\ndelete(a, \"b\")\necho a  // Output: map[a:1 c:100]\n\n// Works with any key type\nm := make(map[int]string)\nm[1] = \"one\"\nm[2] = \"two\"\ndelete(m, 1)\necho m  // Output: map[2:two]\n```\n\n### Getting Map Length\n\nYou can get the number of elements in a map using the `len` function:\n\n```go\na := {\"a\": 1, \"b\": 2, \"c\": 3}\necho len(a)  // Output: 3\n\nb := make(map[string]int)\nb[\"x\"] = 10\necho len(b)  // Output: 1\n```\n\n### Accessing Elements\n\nXGo provides flexible ways to retrieve map values, including safety checks for missing keys.\n\n#### Bracket Notation\n\nThe traditional way to access map elements is using the `[]` operator with a key:\n\n```go\na := {\"name\": \"Alice\", \"age\": 25}\necho a[\"name\"]  // Output: Alice\n\n// Works with any key type\nm := make(map[int]string)\nm[42] = \"answer\"\necho m[42]  // Output: answer\n```\n\n#### Field Access Notation\n\nFor string-keyed maps, XGo allows you to use dot notation when the key is a valid identifier:\n\n```go\nconfig := {\"host\": \"localhost\", \"port\": 8080}\necho config.host  // Output: localhost\necho config.port  // Output: 8080\n\n// Equivalent to:\necho config[\"host\"]\necho config[\"port\"]\n```\n\n##### When to Use Each Style\n\n**Use field notation** (`m.field`) when:\n- Keys are known at development time\n- Keys are valid identifiers (letters, digits, underscores only)\n- You want more readable code\n\n**Use bracket notation** (`m[\"key\"]`) when:\n- Keys are computed at runtime\n- Keys contain special characters, spaces, or start with digits\n- You need explicit compatibility with standard Go\n- Working with non-string key types\n\n```go\n// Field notation - clean and readable\nuser := {\"name\": \"Alice\", \"age\": 30}\necho user.name\necho user.age\n\n// Bracket notation - necessary for dynamic or special keys\nkeyName := \"name\"\necho user[keyName]           // Dynamic key\necho user[\"first-name\"]      // Key with hyphen\necho user[\"2024-score\"]      // Key starting with digit\n\n// Bracket notation - required for non-string keys\nscores := make(map[int]float64)\nscores[1] = 95.5\necho scores[1]  // Must use bracket notation\n```\n\n##### Nested Access\n\nField notation works seamlessly with nested maps:\n\n```go\ndata := {\n    \"user\": {\n        \"profile\": {\n            \"name\": \"Alice\",\n            \"age\": 30,\n        },\n    },\n}\n\n// Clean nested access\necho data.user.profile.name  // Output: Alice\n\n// Equivalent to:\necho data[\"user\"][\"profile\"][\"name\"]\n```\n\n##### Working with `any` Type\n\nEither notation also works with variables of type `any`, automatically treating them as `map[string]any`:\n\n```go\nvar response any = {\"status\": \"ok\", \"code\": 200}\necho response.status  // Output: ok\necho response.code    // Output: 200\n\necho response[\"status\"]  // Output: ok\necho response[\"code\"]    // Output: 200\n```\n\n#### Safe Access with Comma-ok\n\nWhen accessing uncertain data (such as from JSON or external APIs), use the comma-ok form to safely check if a path exists. The comma-ok form returns two values:\n- The value itself (or zero value if path doesn't exist)\n- A boolean indicating whether the access succeeded\n\n```go\na := {\"a\": 1, \"b\": 0}\n\n// Check if key exists\nv, ok := a[\"c\"]\necho v, ok  // Output: 0 false (key doesn't exist)\n\nv, ok = a[\"b\"]\necho v, ok  // Output: 0 true (key exists with value 0)\n\n// Works with field notation too\nv, ok = a.c\necho v, ok  // Output: 0 false\n\n// Direct conditional check\nif v, ok := a[\"c\"]; ok {\n    echo \"Found:\", v\n} else {\n    echo \"Not found\"  // Output: Not found\n}\n```\n\n**With comma-ok, accessing non-existent paths never panics** - it simply returns `false`:\n\n```go\ndata := {\"user\": {\"name\": \"Alice\"}}\n\n// Safe single-level access\nname, ok := data.user\nif ok {\n    echo \"User found:\", name\n}\n\n// Safe nested access\nprofile, ok := data.user.profile\nif !ok {\n    echo \"Profile not found\"  // This will print\n}\n\n// Safe access with type assertion\nvar response any = {\"status\": \"ok\", \"code\": 200}\ncode, ok := response.code.(int)\nif ok {\n    echo \"Status code:\", code\n}\n```\n\nThis is especially useful when working with dynamic data:\n\n```go\nvar data any = {\"user\": \"Alice\"}\n\n// Without comma-ok - may panic if structure is wrong\n// name := data.user.profile.name  // Would panic!\n\n// With comma-ok - safe, never panics\nname, ok := data.user.profile.name\nif !ok {\n    echo \"Path does not exist\"  // Output: Path does not exist\n    name = \"Unknown\"\n}\n\n// Processing API response\nvar apiResponse any = fetchFromAPI()\n\n// Safely extract nested values\nif userID, ok := apiResponse.data.user.id.(string); ok {\n    processUser(userID)\n} else {\n    echo \"Invalid response structure\"\n}\n\n// With fallback values\ncity := \"Unknown\"\nif c, ok := apiResponse.user.address.city.(string); ok {\n    city = c\n}\necho \"City:\", city\n```\n\n### Iterating Over Maps\n\nXGo provides two forms of `for in` loop for iterating over maps:\n\n#### Iterate Over Keys and Values\n\n```go\nm := {\"x\": 10, \"y\": 20, \"z\": 30}\nfor key, value in m {\n    echo key, value\n}\n\n// Works with any map type\nages := make(map[string]int)\nages[\"Alice\"] = 30\nages[\"Bob\"] = 25\nfor name, age in ages {\n    echo name, \"is\", age, \"years old\"\n}\n```\n\n#### Iterate Over Keys Only\n\nTo iterate over just the keys, you can use the blank identifier `_` for the value part.\n\n```go\nm := {\"x\": 10, \"y\": 20, \"z\": 30}\nfor key, _ in m {\n    echo key\n}\n```\n\n#### Iterate Over Values Only\n\n```go\nm := {\"x\": 10, \"y\": 20, \"z\": 30}\nfor value in m {\n    echo value\n}\n```\n\n## Map Comprehensions\n\nMap comprehensions provide a concise and expressive way to create new maps by transforming or filtering existing sequences. They follow a syntax similar to Python's dictionary comprehensions.\n\n### Basic Syntax\n\nThe general form of a map comprehension is:\n\n```go\n{keyExpr: valueExpr for vars in iterable}\n```\n\nThis creates a new map where each element from the `iterable` is transformed into a key-value pair.\n\n#### Creating Maps from Slices\n\n```go\n// Map slice values to their indices\nnumbers := [10, 20, 30, 40, 50]\nvalueToIndex := {v: i for i, v in numbers}\necho valueToIndex  // Output: map[10:0 20:1 30:2 40:3 50:4]\n```\n\n#### Creating Maps from Strings\n\n```go\n// Character positions in a string\nword := \"hello\"\ncharPositions := {char: i for i, char in word}\necho charPositions  // Output: map[h:0 e:1 l:3 o:4]\n// Note: 'l' appears twice, so the last occurrence (index 3) is kept\n```\n\n### Comprehensions with Conditions\n\nAdd an `if` clause to filter elements:\n\n```go\n{keyExpr: valueExpr for vars in iterable if condition}\n```\n\n#### Filtering Even/Odd Elements\n\n```go\nnumbers := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\n// Only even numbers\nevenSquares := {v: v * v for v in numbers if v%2 == 0}\necho evenSquares  // Output: map[2:4 4:16 6:36 8:64 10:100]\n\n// Only odd indices\noddIndexValues := {i: v for i, v in numbers if i%2 == 1}\necho oddIndexValues  // Output: map[1:2 3:4 5:6 7:8 9:10]\n```\n\n### Best Practices for Comprehensions\n\n1. **Use comprehensions for simple transformations**: They're most readable when the logic is straightforward\n2. **Consider traditional loops for complex logic**: If you need multiple statements or complex conditions, a regular loop may be clearer\n3. **Watch for duplicate keys**: In comprehensions, later values overwrite earlier ones for the same key\n4. **Keep conditions simple**: Complex filtering logic is often better in a traditional loop\n\n## Common Patterns\n\n### Configuration Maps\n\n```go\n// Using literals for initial configuration\nconfig := {\n    \"host\": \"localhost\",\n    \"port\": 8080,\n    \"debug\": true,\n}\n\n// Access with field notation\necho \"Connecting to\", config.host, \"on port\", config.port\n\n// Using explicit type for type safety\nvar settings map[string]int = {\n    \"maxConnections\": 100,\n    \"timeout\": 30,\n}\n\n// Using make for type-safe configuration\noptions := make(map[string]int)\noptions[\"maxConnections\"] = 100\noptions[\"timeout\"] = 30\n```\n\n### Processing JSON Responses\n\n```go\nvar response any = parseJSON(apiData)\n\n// Safe extraction with defaults\nuserID, ok := response.user.id.(string)\nif !ok {\n    userID = \"guest\"\n}\n\nuserName, ok := response.user.name.(string)\nif !ok {\n    userName = \"Anonymous\"\n}\n\necho \"User:\", userName, \"(\", userID, \")\"\n```\n\n### Counting Occurrences\n\n```go\n// Using make with pre-allocated capacity\nwordCounts := make(map[string]int, 1000)\nfor word in words {\n    wordCounts[word]++\n}\n\n// Using comprehension to initialize\nwords := [\"apple\", \"banana\", \"apple\", \"orange\", \"banana\", \"apple\"]\nuniqueWords := {w: 0 for w in words}  // Initialize all to 0\nfor word in words {\n    uniqueWords[word]++\n}\n```\n\n### Lookup Tables\n\n```go\n// Simple lookup table with literals\nstatusCodes := {\n    \"ok\": 200,\n    \"not_found\": 404,\n    \"error\": 500,\n}\n\necho statusCodes.ok  // Output: 200\n\n// Using comprehension to reverse the mapping\ncodeToStatus := {code: status for status, code in statusCodes}\necho codeToStatus[200]  // Output: ok\n```\n\n### Caching\n\n```go\n// Cache with pre-allocated capacity for performance\ncache := make(map[string][]byte, 10000)\n\nfunc getCachedData(key string) []byte {\n    if data, ok := cache[key]; ok {\n        return data\n    }\n\n    data := fetchData(key)\n    cache[key] = data\n    return data\n}\n```\n\n### Grouping Data\n\n```go\n// Group items by category\ngroups := make(map[string][]string)\n\nfor item in items {\n    category := getCategory(item)\n    groups[category] = append(groups[category], item)\n}\n\n// Access grouped data\nfor category, items in groups {\n    echo \"Category:\", category\n    for item in items {\n        echo \"  -\", item\n    }\n}\n```\n\n## Best Practices\n\n1. **Use field notation for readability** when keys are known and are valid identifiers\n2. **Use bracket notation** when keys are dynamic, contain special characters, or are non-string types\n3. **Use comma-ok form** when working with uncertain data structures (APIs, JSON, dynamic data)\n4. **Use map literals** for quick initialization with known data\n5. **Use explicit type declaration** with literals when you need type safety or specific conversions\n6. **Use `make`** when you need specific types, non-string keys, or want to pre-allocate capacity\n7. **Use map comprehensions** for simple transformations and filtering of sequences\n8. Check for key existence before accessing values when the key might not exist\n9. Pre-allocate capacity with `make` for large maps when the approximate size is known\n10. Use consistent value types when possible for type safety\n11. Consider using `make` with explicit types for better code documentation and type safety in larger projects\n\n## Performance Tips\n\n1. **Pre-allocate capacity**: When you know the approximate size, use `make(map[K]V, size)` to reduce allocations\n2. **Avoid frequent reallocations**: Maps grow dynamically, but pre-allocation prevents repeated internal resizing\n3. **Use appropriate key types**: Simple types (int, string) as keys are more efficient than complex structs\n4. **Consider zero values**: Accessing non-existent keys returns zero values, which can be useful for counters\n5. **Comprehensions vs loops**: For large datasets or complex transformations, traditional loops with pre-allocation may be more efficient than comprehensions\n"
  },
  {
    "path": "doc/overload.md",
    "content": "Overload Func/Method/Operator/Types\n=====\n\n### Overload Funcs\n\nDefine `overload func` in `inline func literal` style (see [overloadfunc1/add.xgo](demo/overloadfunc1/add.xgo)):\n\n```go\nfunc add = (\n\tfunc(a, b int) int {\n\t\treturn a + b\n\t}\n\tfunc(a, b string) string {\n\t\treturn a + b\n\t}\n)\n\nprintln add(100, 7)\nprintln add(\"Hello\", \"World\")\n```\n\nDefine `overload func` in `ident` style (see [overloadfunc2/mul.xgo](demo/overloadfunc2/mul.xgo)):\n\n```go\nfunc mulInt(a, b int) int {\n\treturn a * b\n}\n\nfunc mulFloat(a, b float64) float64 {\n\treturn a * b\n}\n\nfunc mul = (\n\tmulInt\n\tmulFloat\n)\n\nprintln mul(100, 7)\nprintln mul(1.2, 3.14)\n```\n\n### Overload Methods\n\nDefine `overload method` (see [overloadmethod/method.xgo](demo/overloadmethod/method.xgo)):\n\n```go\ntype foo struct {\n}\n\nfunc (a *foo) mulInt(b int) *foo {\n\tprintln \"mulInt\"\n\treturn a\n}\n\nfunc (a *foo) mulFoo(b *foo) *foo {\n\tprintln \"mulFoo\"\n\treturn a\n}\n\nfunc (foo).mul = (\n\t(foo).mulInt\n\t(foo).mulFoo\n)\n\nvar a, b *foo\nvar c = a.mul(100)\nvar d = a.mul(c)\n```\n\n### Overload Unary Operators\n\nDefine `overload unary operator` (see [overloadop1/overloadop.xgo](demo/overloadop1/overloadop.xgo)):\n\n```go\ntype foo struct {\n}\n\nfunc -(a foo) (ret foo) {\n\tprintln \"-a\"\n\treturn\n}\n\nfunc ++(a foo) {\n\tprintln \"a++\"\n}\n\nvar a foo\nvar b = -a\na++\n```\n\n### Overload Binary Operators\n\nDefine `overload binary operator` (see [overloadop1/overloadop.xgo](demo/overloadop1/overloadop.xgo)):\n\n```go\ntype foo struct {\n}\n\nfunc (a foo) * (b foo) (ret foo) {\n\tprintln \"a * b\"\n\treturn\n}\n\nfunc (a foo) != (b foo) bool {\n\tprintln \"a != b\"\n\treturn true\n}\n\nvar a, b foo\nvar c = a * b\nvar d = a != b\n```\n\nHowever, `binary operator` usually need to support interoperability between multiple types. In this case it becomes more complex (see [overloadop2/overloadop.xgo](demo/overloadop2/overloadop.xgo)):\n\n```go\ntype foo struct {\n}\n\nfunc (a foo) mulInt(b int) (ret foo) {\n\tprintln \"a * int\"\n\treturn\n}\n\nfunc (a foo) mulFoo(b foo) (ret foo) {\n\tprintln \"a * b\"\n\treturn\n}\n\nfunc intMulFoo(a int, b foo) (ret foo) {\n\tprintln \"int * b\"\n\treturn\n}\n\nfunc (foo).* = (\n\t(foo).mulInt\n\t(foo).mulFoo\n\tintMulFoo\n)\n\nvar a, b foo\nvar c = a * 10\nvar d = a * b\nvar e = 10 * a\n```\n\n### Overload Types\n\nTODO\n\n### Overload Typecast\n\nTODO\n\n"
  },
  {
    "path": "doc/slice.md",
    "content": "# Slice Type\n\nA `slice` (also named `list`) is a dynamically-sized, flexible view into the elements of an array. Slices are one of the most commonly used data structures in XGo, providing efficient and convenient ways to work with sequences of elements.\n\n**Note**: In XGo, the terms `slice` and `list` are identical and refer to the same data structure. The term \"slice\" comes from Go's terminology, while \"list\" aligns with Python's naming convention.\n\n## Understanding Slices\n\nA slice consists of three components:\n\n1. **Pointer** - Points to the first element of the slice in the underlying array\n2. **Length** - The number of elements in the slice\n3. **Capacity** - The number of elements from the beginning of the slice to the end of the underlying array\n\nUnlike arrays which have a fixed size, slices can grow and shrink dynamically, making them ideal for most collection use cases.\n\n## Creating Slices\n\nXGo provides multiple ways to create slices: using slice literals for quick initialization with data, using the `make` function for more control over slice types and capacity, and slicing existing arrays or slices.\n\n### Slice Literal Syntax\n\nIn XGo, you can create slices using square brackets `[]`:\n\n```go\na := [1, 2, 3]    // []int\nb := [1, 2, 3.4]  // []float64\nc := [\"Hi\"]       // []string\nd := [\"Hi\", 10]   // []any - mixed types\ne := []           // []any - empty slice\n```\n\n#### Automatic Type Inference\n\nWhen using the `:=` syntax without explicit type declaration, XGo automatically infers the complete slice type `[]ElementType` based on the literal values provided.\n\n**Type Inference Rules**\n\n1. **Uniform Types**: If all elements have the same type, that type is used.\n2. **Mixed Types**: If elements have incompatible types, the type is inferred as `any`.\n3. **Empty Slice** `[]`: Inferred as `[]any` by default for maximum flexibility.\n\nYou can also explicitly specify the slice type to override automatic type inference:\n\n```go\n// Explicit type declaration\nvar a []float64 = [1, 2, 3]     // Values converted to float64\nvar c []any = [\"x\", 1, true]    // Explicit any type\n```\n\nWhen a type is explicitly declared, the literal values are converted to match the declared type.\n\n### Creating Slices with `make`\n\nUse `make` when you need an empty slice or want to optimize performance by pre-allocating capacity.\n\n#### Basic `make` Syntax\n\n```go\n// Create slice with specified length (initialized to zero values)\ns1 := make([]int, 5)        // [0, 0, 0, 0, 0]\ns2 := make([]string, 3)     // [\"\", \"\", \"\"]\n\n// Access and modify\ns1[0] = 100\ns1[2] = 300\necho s1  // Output: [100 0 300 0 0]\n```\n\n#### Pre-allocating Capacity\n\nFor performance optimization, you can specify both length and capacity:\n\n```go\n// Create slice with length 0 and capacity 100\ns := make([]int, 0, 100)\n\n// This doesn't limit the slice size, but helps reduce allocations\nfor i := 0; i < 150; i++ {\n    s <- i\n}\n\necho len(s)  // Output: 150\necho cap(s)  // Output: likely > 150\n```\n\nThe capacity hint doesn't limit the slice's size but helps the runtime allocate memory more efficiently when you know approximately how many elements you'll add.\n\n#### When to Use `make` vs Literals\n\n**Use slice literals** (`[]`) when:\n- You have initial data to populate\n- You want automatic type inference for convenience\n- You prefer concise, readable code\n\n**Use slice literals with explicit type** (`var s []T = []`) when:\n- You have initial data with a specific type requirement\n- You need type safety while keeping syntax concise\n- You want to ensure value types are converted correctly\n\n**Use `make`** when:\n- You need a slice initialized with zero values\n- You want to pre-allocate capacity for performance\n- You're creating an empty slice and plan to add elements later\n- Working with codebases that consistently use `make`\n\n### Creating Slices from Slices\n\nYou can create new slices by slicing existing arrays or slices using the range syntax `[start:end]`:\n\n```go\narr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\n// Basic slicing\nslice1 := arr[2:5]   // [3, 4, 5] - from index 2 to 5 (exclusive)\nslice2 := arr[:3]    // [1, 2, 3] - from start to index 3\nslice3 := arr[5:]    // [6, 7, 8, 9, 10] - from index 5 to end\nslice4 := arr[:]     // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - full slice (shallow copy)\n```\n\n**Important**: Slices created this way share the same underlying array. Modifying one slice may affect others:\n\n```go\narr := [1, 2, 3, 4, 5]\nslice1 := arr[1:4]  // [2, 3, 4]\nslice2 := arr[2:5]  // [3, 4, 5]\n\nslice1[1] = 100     // Modifies the shared underlying array\necho slice1         // Output: [2 100 4]\necho slice2         // Output: [100 4 5] - also affected!\necho arr            // Output: [1 2 100 4 5] - original array modified\n```\n\n## Slice Operations\n\n### Modifying Elements\n\nYou can directly modify elements at specific indexes:\n\n```go\nnums := [1, 2, 3, 4, 5]\nnums[0] = 100\nnums[2] = 300\n\necho nums  // Output: [100 2 300 4 5]\n```\n\n### Appending Elements\n\nXGo provides two ways to append elements to slices: the `<-` operator and the `append` built-in function.\n\n#### Using the `<-` Operator\n\nThe `<-` operator provides an intuitive way to append elements:\n\n```go\nnums := [1, 2, 3]\nnums <- 4           // Append single element\nnums <- 5, 6, 7     // Append multiple elements\n\nmore := [8, 9, 10]\nnums <- more...     // Append another slice\n\necho nums  // Output: [1 2 3 4 5 6 7 8 9 10]\n```\n\n#### Using the `append` Function\n\nThe `append` function returns a new slice with elements added or removed:\n\n```go\n// Adding elements\nnums := [1, 2, 3]\nnums = append(nums, 4)        // Append single element\nnums = append(nums, 5, 6, 7)  // Append multiple elements\n\nmore := [8, 9, 10]\nnums = append(nums, more...)  // Append another slice\n\necho nums  // Output: [1 2 3 4 5 6 7 8 9 10]\n```\n\n**Important**: The `append` function returns a new slice, so you must assign the result back to a variable.\n\n#### Removing Elements with `append`\n\nThe `append` function can also remove consecutive elements by concatenating slices before and after the range to remove:\n\n```go\nnums := [1, 2, 3, 4, 5]\n\n// Remove element at index 2 (value 3)\nnums = append(nums[:2], nums[3:]...)\necho nums  // Output: [1 2 4 5]\n\n// Remove multiple consecutive elements (indices 1-2)\nnums = [1, 2, 3, 4, 5]\nnums = append(nums[:1], nums[3:]...)\necho nums  // Output: [1 4 5]\n```\n\nThis pattern uses slice notation to select everything before the removal range (`nums[:start]`) and everything after it (`nums[end:]`), then concatenates them together. This effectively removes the elements in the slice `nums[start:end]`.\n\n### Accessing Elements\n\nIndexes start from `0`. Valid indexes range from `0` to `len(slice) - 1`:\n\n```go\nnums := [10, 20, 30, 40, 50]\n\necho nums[0]   // 10 - first element\necho nums[1]   // 20 - second element\necho nums[4]   // 50 - last element\n```\n\n**Note**: Negative indexing is not supported. Using an index outside the valid range will cause a runtime error.\n\n### Getting Slice Length and Capacity\n\nYou can get the length and capacity of a slice using the `len` and `cap` functions:\n\n```go\nnums := [1, 2, 3, 4, 5]\n\necho len(nums)  // 5 - number of elements\necho cap(nums)  // 5 - capacity (may be larger)\n```\n\n### Extracting Sub-slices\n\nYou can extract portions of a slice using the range syntax:\n\n```go\nnums := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n\necho nums[2:5]   // [2 3 4] - from index 2 to 5 (exclusive)\necho nums[:3]    // [0 1 2] - from start to index 3\necho nums[5:]    // [5 6 7 8 9] - from index 5 to end\necho nums[:]     // [0 1 2 3 4 5 6 7 8 9] - full slice (shallow copy)\n```\n\n**Remember**: Sub-slices share the underlying array with the original slice. See \"Creating Slices from Arrays or Slices\" for details on this behavior.\n\n### Iterating Over Slices\n\nXGo provides multiple ways to iterate over slices using `for` loops:\n\n#### Iterate Over Index and Value\n\n```go\nnums := [10, 20, 30, 40, 50]\n\nfor i, v in nums {\n    echo \"Index:\", i, \"Value:\", v\n}\n```\n\n#### Iterate Over Values Only\n\n```go\nnums := [10, 20, 30, 40, 50]\n\nfor v in nums {\n    echo v\n}\n```\n\n#### Iterate Over Indexes Only\n\n```go\nnums := [10, 20, 30, 40, 50]\n\nfor i, _ in nums {\n    echo \"Index:\", i\n}\n```\n\n## List Comprehensions\n\nList comprehensions provide a concise and expressive way to create new lists by transforming or filtering existing sequences. They follow a syntax similar to Python's list comprehensions.\n\n### Basic Syntax\n\nThe general form of a list comprehension is:\n\n```go\n[expression for vars in iterable]\n```\n\nThis creates a new list where each element from the `iterable` is transformed by the `expression`.\n\n#### Transforming Elements\n\n```go\n// Square all numbers\nnumbers := [1, 2, 3, 4, 5]\nsquares := [v * v for v in numbers]\necho squares  // Output: [1 4 9 16 25]\n\n// Convert to strings\nwords := [\"hello\", \"world\"]\nupper := [v.toUpper for v in words]\necho upper  // Output: [\"HELLO\" \"WORLD\"]\n\n// Extract from index-value pairs\ndoubled := [v * 2 for i, v in numbers]\necho doubled  // Output: [2 4 6 8 10]\n```\n\n#### Creating Lists from Ranges\n\n```go\n// Generate sequence\nnums := [i for i in 1:11]\necho nums  // Output: [1 2 3 4 5 6 7 8 9 10]\n\n// With transformation\nevens := [i * 2 for i in :5]\necho evens  // Output: [0 2 4 6 8]\n\n// With step\nodds := [i for i in 1:10:2]\necho odds  // Output: [1 3 5 7 9]\n```\n\n### Comprehensions with Conditions\n\nAdd an `if` clause to filter elements:\n\n```go\n[expression for vars in iterable if condition]\n```\n\n#### Filtering Elements\n\n```go\nnumbers := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\n// Only even numbers\nevens := [v for v in numbers if v % 2 == 0]\necho evens  // Output: [2 4 6 8 10]\n\n// Only numbers greater than 5\nlarge := [v for v in numbers if v > 5]\necho large  // Output: [6 7 8 9 10]\n\n// Filter and transform\nevenSquares := [v * v for v in numbers if v % 2 == 0]\necho evenSquares  // Output: [4 16 36 64 100]\n```\n\n#### Filtering with Index\n\n```go\nnumbers := [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]\n\n// Elements at even indices\nevenIndexValues := [v for i, v in numbers if i % 2 == 0]\necho evenIndexValues  // Output: [10 30 50 70 90]\n```\n\n### Nested Comprehensions\n\nList comprehensions can be nested to work with multi-dimensional data:\n\n```go\n// Flatten a 2D list\nmatrix := [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\nflattened := [num for row in matrix for num in row]\necho flattened  // Output: [1 2 3 4 5 6 7 8 9]\n\n// Create multiplication table\ntable := [[i * j for j in 1:6] for i in 1:6]\necho table\n// Output: [[1 2 3 4 5] [2 4 6 8 10] [3 6 9 12 15] [4 8 12 16 20] [5 10 15 20 25]]\n\n// Extract diagonal elements\ndiagonal := [matrix[i][i] for i in :len(matrix)]\necho diagonal  // Output: [1 5 9]\n```\n\n### Best Practices for Comprehensions\n\n1. **Use comprehensions for simple transformations**: They're most readable when the logic is straightforward\n2. **Consider traditional loops for complex logic**: If you need multiple statements or complex conditions, a regular loop may be clearer\n3. **Avoid excessive nesting**: More than two levels of nesting can be hard to read\n4. **Keep expressions concise**: Long or complex expressions reduce readability\n5. **Use meaningful variable names**: Even in short comprehensions, clarity matters\n\n### When to Use Comprehensions vs Loops\n\n**Use list comprehensions** when:\n- You need a simple transformation of each element\n- You're filtering based on a clear condition\n- The logic fits naturally in a single expression\n- You want concise, functional-style code\n\n**Use traditional loops** when:\n- You need multiple statements per iteration\n- You have complex conditional logic\n- You need to break or continue based on conditions\n- You're modifying external state or have side effects\n- Readability would suffer from cramming logic into a comprehension\n\n```go\n// Good use of comprehension\nsquares := [x * x for x in :10]\n\n// Better as a traditional loop (side effects, complex logic)\nresults := []\nfor x in :10 {\n    result := complexCalculation(x)\n    if result > threshold {\n        results <- result\n        updateGlobalState(result)\n    }\n}\n```\n\n## Common Patterns\n\n### Filtering Slices\n\n```go\nnums := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nevens := []\n\nfor v in nums {\n    if v % 2 == 0 {\n        evens <- v\n    }\n}\n\necho evens  // Output: [2 4 6 8 10]\n```\n\n### Transforming Slices (Map Operation)\n\n```go\nnums := [1, 2, 3, 4, 5]\nsquared := []\n\nfor v in nums {\n    squared <- v * v\n}\n\necho squared  // Output: [1 4 9 16 25]\n```\n\n### Finding Elements\n\n```go\nnums := [10, 20, 30, 40, 50]\ntarget := 30\nfound := false\nindex := -1\n\nfor i, v in nums {\n    if v == target {\n        found = true\n        index = i\n        break\n    }\n}\n\nif found {\n    echo \"Found\", target, \"at index\", index\n} else {\n    echo target, \"not found\"\n}\n```\n\n### Reversing a Slice\n\n```go\nnums := [1, 2, 3, 4, 5]\nreversed := []\n\nfor i := len(nums) - 1; i >= 0; i-- {\n    reversed <- nums[i]\n}\n\necho reversed  // Output: [5 4 3 2 1]\n```\n\n### Removing Duplicates\n\n```go\nnums := [1, 2, 2, 3, 3, 3, 4, 5, 5]\nunique := []\nseen := {}\n\nfor v in nums {\n    if !seen[v] {\n        unique <- v\n        seen[v] = true\n    }\n}\n\necho unique  // Output: [1 2 3 4 5]\n```\n\n### Merging Multiple Slices\n\n```go\na := [1, 2, 3]\nb := [4, 5, 6]\nc := [7, 8, 9]\n\nmerged := []\nmerged <- a...\nmerged <- b...\nmerged <- c...\n\necho merged  // Output: [1 2 3 4 5 6 7 8 9]\n```\n\n### Summing Elements\n\n```go\nnums := [1, 2, 3, 4, 5]\nsum := 0\n\nfor v in nums {\n    sum += v\n}\n\necho sum  // Output: 15\n```\n\n### Finding Maximum and Minimum\n\n```go\nnums := [34, 12, 67, 23, 89, 45]\n\nmax := nums[0]\nmin := nums[0]\n\nfor v in nums {\n    if v > max {\n        max = v\n    }\n    if v < min {\n        min = v\n    }\n}\n\necho \"Max:\", max  // Output: 89\necho \"Min:\", min  // Output: 12\n```\n\n### Checking if Slice Contains Element\n\n```go\nnums := [10, 20, 30, 40, 50]\ntarget := 30\ncontains := false\n\nfor v in nums {\n    if v == target {\n        contains = true\n        break\n    }\n}\n\necho contains  // Output: true\n```\n\n### Partitioning a Slice\n\n```go\nnums := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nevens := []\nodds := []\n\nfor v in nums {\n    if v % 2 == 0 {\n        evens <- v\n    } else {\n        odds <- v\n    }\n}\n\necho evens  // Output: [2 4 6 8 10]\necho odds   // Output: [1 3 5 7 9]\n```\n\n### Flattening Nested Slices\n\n```go\nnested := [[1, 2], [3, 4], [5, 6]]\nflat := []\n\nfor subslice in nested {\n    flat <- subslice...\n}\n\necho flat  // Output: [1 2 3 4 5 6]\n```\n\n### Using Slices as Stacks\n\n```go\nstack := []\n\n// Push elements\nstack <- 1\nstack <- 2\nstack <- 3\n\necho stack  // Output: [1 2 3]\n\n// Pop element\nif len(stack) > 0 {\n    top := stack[len(stack) - 1]\n    stack = stack[:len(stack) - 1]\n    echo \"Popped:\", top  // Output: Popped: 3\n    echo stack           // Output: [1 2]\n}\n```\n\n### Using Slices as Queues\n\n```go\nqueue := []\n\n// Enqueue elements\nqueue <- 1\nqueue <- 2\nqueue <- 3\n\necho queue  // Output: [1 2 3]\n\n// Dequeue element\nif len(queue) > 0 {\n    front := queue[0]\n    queue = queue[1:]\n    echo \"Dequeued:\", front  // Output: Dequeued: 1\n    echo queue               // Output: [2 3]\n}\n```\n\n### Sliding Window Pattern\n\n```go\nnums := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nwindowSize := 3\n\nfor i := 0; i <= len(nums) - windowSize; i++ {\n    window := nums[i:i + windowSize]\n    echo \"Window:\", window\n}\n// Output:\n// Window: [1 2 3]\n// Window: [2 3 4]\n// Window: [3 4 5]\n// ...\n```\n\n### Grouping Data\n\n```go\n// Group items by category\nitems := [\"apple\", \"banana\", \"carrot\", \"date\", \"eggplant\"]\ngroups := make(map[string][]string)\n\nfor item in items {\n    firstLetter := item[0:1]\n    groups[firstLetter] <- item\n}\n\n// Access grouped data\nfor category, itemList in groups {\n    echo \"Category:\", category\n    for item in itemList {\n        echo \"  -\", item\n    }\n}\n```\n\n## Best Practices\n\n1. **Pre-allocate capacity when size is known**: Use `make([]T, 0, size)` to avoid multiple reallocations\n2. **Use `len(slice)` and `cap(slice)`**: These are the recommended ways to get length and capacity\n3. **Check bounds before accessing**: Ensure indexes are within valid range `[0, len(slice)-1]`\n4. **Be aware of slice sharing**: Slices created by slicing share the same underlying array\n5. **Use the `<-` operator for appending**: It's more concise and idiomatic in XGo\n6. **Use meaningful variable names**: Make code self-documenting\n7. **Avoid modifying slices during iteration**: Create a new slice instead\n8. **Document slice modifications**: Make it clear whether functions modify input slices\n9. **Use deep copies when independence is needed**: Use `copy` or manual copying\n10. **Consider slice capacity for performance**: Pre-allocating can significantly improve performance for large slices\n\n## Performance Considerations\n\n### Slice Growth\n\nWhen a slice's capacity is exceeded during append operations, XGo allocates a new underlying array with increased capacity:\n\n```go\ns := []\necho len(s), cap(s)  // Output: 0 0\n\ns <- 1\necho len(s), cap(s)  // Output: 1 1\n\ns <- 2\necho len(s), cap(s)  // Output: 2 2\n\ns <- 3\necho len(s), cap(s)  // Output: 3 4 (capacity doubled)\n\ns <- 4, 5\necho len(s), cap(s)  // Output: 5 8 (capacity doubled again)\n```\n\nThe exact growth strategy may vary, but typically capacity doubles when exceeded.\n\n### Memory Efficiency\n\nPre-allocating capacity avoids multiple reallocations:\n\n```go\n// Inefficient - multiple reallocations\ninefficient := []\nfor i := 0; i < 1000; i++ {\n    inefficient <- i\n}\n\n// Efficient - single allocation\nefficient := make([]int, 0, 1000)\nfor i := 0; i < 1000; i++ {\n    efficient <- i\n}\n```\n\n### Avoiding Memory Leaks\n\nWhen creating a small slice from a large slice, the underlying array is still retained:\n\n```go\n// May cause memory leak\nfunc getFirstThree(data []int) []int {\n    return data[:3]  // Still references the entire underlying array\n}\n\n// Better approach - create independent slice\nfunc getFirstThree(data []int) []int {\n    result := make([]int, 3)\n    copy(result, data[:3])\n    return result\n}\n```\n\n## Common Pitfalls\n\n### 1. Index Out of Bounds\n\n```go\nnums := [1, 2, 3]\n\n// This will cause a runtime error\n// echo nums[10]  // Error: index out of range\n\n// Always check bounds\nindex := 10\nif index >= 0 && index < len(nums) {\n    echo nums[index]\n} else {\n    echo \"Index out of bounds\"\n}\n```\n\n### 2. Negative Indexing Not Supported\n\n```go\nnums := [1, 2, 3, 4, 5]\n\n// This is NOT valid in XGo\n// echo nums[-1]  // Error: invalid slice index\n\n// To access last element, use:\necho nums[len(nums) - 1]  // Output: 5\n```\n\n### 3. Unintended Sharing\n\n```go\na := [1, 2, 3]\nb := a       // b references same underlying array\nb[0] = 100\n\necho a  // Output: [100 2 3] - a is also modified!\n\n// To avoid this, make a copy\nc := make([]int, len(a))\ncopy(c, a)\nc[0] = 200\necho a  // Output: [100 2 3] - a is not affected\n```\n\n### 4. Slice of Slices Sharing\n\n```go\n// Careful with slice of slices\nmatrix := []\nrow := [1, 2, 3]\n\nmatrix <- row\nmatrix <- row  // Both rows reference the same underlying array!\n\nrow[0] = 100\necho matrix  // Output: [[100 2 3] [100 2 3]] - both rows are modified!\n\n// Better approach - create independent rows\nmatrix := []\nmatrix <- [1, 2, 3]\nmatrix <- [1, 2, 3]  // Each row is independent\n```\n\n### 5. Modifying During Iteration\n\n```go\n// Avoid this - may cause unexpected behavior\nnums := [1, 2, 3, 4, 5]\nfor i, v in nums {\n    if v % 2 == 0 {\n        nums <- v * 2  // Modifying during iteration - risky!\n    }\n}\n\n// Better approach - create new slice\nresult := []\nfor v in nums {\n    if v % 2 == 0 {\n        result <- v * 2\n    } else {\n        result <- v\n    }\n}\n```\n\n## Summary\n\nXGo's slices provide a powerful and flexible way to work with sequences of elements. Key features include:\n\n1. **Simple Literal Syntax**: Use `[]` for concise slice creation\n2. **Automatic Type Inference**: No need for explicit type specification in most cases\n3. **Intuitive Append Operations**: Use the `<-` operator or `append` function for adding elements\n4. **Flexible Slicing**: Create sub-slices with simple range syntax\n5. **Multiple Iteration Styles**: Choose the iteration pattern that fits your needs\n\nBy understanding these features and following best practices, you can write efficient and maintainable code that leverages the full power of XGo's slice type.\n"
  },
  {
    "path": "doc/spec/mini/mini.xgo",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage mini\n\nimport (\n\t\"xgo/token\"\n\t\"xgo/tpl\"\n)\n\nvar _off = tpl.showConflict(false)\n\nvar Spec = tpl`\n\nSourceFile = ?PackageClause *(ImportDecl \";\") *(TopLevelDecl \";\") ?MainStmts\n\nPackageClause = \"package\" IDENT \";\"\n\nImportDecl = \"import\" (ImportSpec | \"(\" *(ImportSpec \";\") \")\")\n\nImportSpec = ?(IDENT | \".\") STRING\n\nTopLevelDecl = Declaration | FuncDecl\n\nDeclaration = ConstDecl | VarDecl | TypeDecl\n\nConstDecl = \"const\" (ConstSpec | \"(\" *(ConstSpec \";\") \")\")\n\nConstSpec = IdentifierList ?Type ?(\"=\" ExpressionList)\n\nIdentifierList = IDENT % \",\"\n\nLambdaExprList = LambdaExpr % \",\"\n\nExpressionList = Expression % \",\"\n\nVarDecl = \"var\" (VarSpec | \"(\" *(VarSpec \";\") \")\")\n\nVarSpec = IdentifierList (\"=\" ExpressionList | Type ?(\"=\" LambdaExprList))\n\nTypeDecl = \"type\" (TypeSpec | \"(\" *(TypeSpec \";\") \")\")\n\nTypeSpec = IDENT ?\"=\" Type\n\nFuncDecl = \"func\" IDENT Signature ?Block\n\nSignature = Parameters ?Result\n\nParameters = \"(\" ?(ParameterList ?\",\") \")\"\n\nParameterList = IdentifierList ?(Type *(\",\" IdentifierList Type) | *(\",\" Type))\n\nResult = Parameters | NoParenType\n\nMainStmts = StatementList\n\n// -----------------------------------------------------------------\n\nBlock = \"{\" StatementList \"}\"\n\nStatementList = *(Statement \";\")\n\nStatement =\n\tDeclaration | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |\n\tFallthroughStmt | IfStmt | SwitchStmt | ForStmt | DeferStmt | Block |\n\tLabeledStmt | CommandStmt | SimpleStmt\n\nReturnStmt = \"return\" ?LambdaExprList\n\nBreakStmt = \"break\" ?IDENT\n\nContinueStmt = \"continue\" ?IDENT\n\nGotoStmt = \"goto\" IDENT\n\nFallthroughStmt = \"fallthrough\"\n\nIfStmt = \"if\" ?(SimpleStmt \";\") Expression Block ?(\"else\" (IfStmt | Block))\n\nSwitchStmt = \"switch\" ?(SimpleStmt \";\") SwitchGuard \"{\" *CaseClause \"}\"\n\nSwitchGuard = ?(IDENT \":=\") PrimaryExpr | ?Expression\n\nCaseClause = (\"case\" ExpressionList | \"default\") \":\" StatementList\n\nForStmt = \"for\" (IDENT ?(\",\" IDENT) \"in\" RangeExpr | ?RangeExpr) Block\n\nDeferStmt = \"defer\" Expression\n\nLabeledStmt = IDENT \":\" Statement\n\nSimpleStmt = SendStmt | ShortVarDecl | IncDecStmt | Assignment | ExpressionStmt | EmptyStmt\n\nSendStmt = IDENT ?(\".\" IDENT) \"<-\" LambdaExprList ?\"...\"\n\nShortVarDecl = IdentifierList \":=\" ExpressionList\n\nIncDecStmt = Expression (\"++\" | \"--\")\n\nAssignment = ExpressionList (\n\t\"=\" | \"+=\" | \"-=\" | \"|=\" | \"^=\" |\n\t\"*=\" | \"/=\" | \"%=\" | \"<<=\" | \">>=\" | \"&=\" | \"&^=\") LambdaExprList\n\nExpressionStmt = Expression\n\nCommandStmt = IDENT ?(\".\" IDENT) SPACE LambdaExprList ?\"...\"\n\nEmptyStmt = \"\"\n\n// -----------------------------------------------------------------\n\nNoParenType = TypeLit | TypeName\n\nType = TypeLit | TypeName | \"(\" Type \")\"\n\nTypeName = IDENT ?(\".\" IDENT)\n\nTypeLit = PointerType | ArrayType | MapType | FuncType | TupleType | InterfaceType\n\nPointerType = \"*\" Type\n\nArrayType = \"[\" ?Expression \"]\" Type\n\nMapType = \"map\" \"[\" Type \"]\" Type\n\nFuncType = \"func\" Signature\n\nTupleType = Parameters\n\nInterfaceType = \"interface\" \"{\" *(InterfaceElem \";\") \"}\"\n\nInterfaceElem = MethodElem | TypeName\n\nMethodElem = IDENT Signature\n\n// -----------------------------------------------------------------\n\nLambdaExpr = (\"(\" ?(IDENT % \",\") \")\" | ?IDENT) \"=>\" LambdaBody | Expression\n\nLambdaBody = Block | \"(\" LambdaExpr % \",\" \")\" | LambdaExpr\n\nRangeExpr = rangeExprEnd | Expression ?rangeExprEnd\n\nrangeExprEnd = \":\" Expression ?(\":\" ?Expression)\n\nExpression = cmpExpr % \"&&\" % \"||\"\n\ncmpExpr = mathExpr % (\"==\" | \"!=\" | \"<\" | \"<=\" | \">\" | \">=\")\n\nmathExpr = UnaryExpr % (\"*\" | \"/\" | \"%\" | \"<<\" | \">>\" | \"&\" | \"&^\") % (\"+\" | \"-\" | \"|\" | \"^\")\n\nUnaryExpr = PrimaryExpr | (\"-\" | \"!\" | \"^\" | \"*\" | \"&\" | \"+\") UnaryExpr\n\nPrimaryExpr = Operand *(\n\tCallOrConversion | SelectorOrTypeAssertion | IndexOrSlice | ErrWrap)\n\nOperand =\n\tINT ?UNIT | FLOAT ?UNIT | STRING | CHAR | RAT | IMAG | TupleLit |\n\tLiteralValue | FunctionLit | Env | \"c\" ++ QSTRING | \"py\" ++ QSTRING |\n\tListLit | DomainTextLit | IDENT\n\nEnv = \"$\" (\"{\" IDENT \"}\" | IDENT)\n\nDomainTextLit = IDENT ++ RAWSTRING\n\nTupleLit = \"(\" ExpressionList ?\",\" \")\"\n\nListLit = \"[\" ?(LambdaExpr % \",\") ?\",\" \"]\"\n\nLiteralValue = \"{\" ElementList \"}\"\n\nElementList = ?(KeyedElement % \",\" ?\",\")\n\nKeyedElement = ?(Key \":\") Element\n\nKey = LiteralValue | Expression\n\nElement = LiteralValue | LambdaExpr\n\nFunctionLit = \"func\" Signature Block\n\nCallOrConversion = \"(\" ?(argExpr % \",\") ?\"...\" ?\",\" \")\"\n\nargExpr = KwargExpr | LambdaExpr\n\nKwargExpr = IDENT \"=\" LambdaExpr\n\nSelectorOrTypeAssertion = \".\" (IDENT | \"(\" Type \")\")\n\nIndexOrSlice = \"[\" (\":\" ?Expression | Expression (\":\" ?Expression | ?\",\")) \"]\"\n\nErrWrap = \"!\" | \"?\" ?(\":\" UnaryExpr)\n`!\n\n// -----------------------------------------------------------------\n\nfunc ParseFile(fset *token.FileSet, filename string, src any) (any, error) {\n\treturn Spec.Parse(filename, src, &tpl.Config{\n\t\tFset: fset,\n\t})\n}\n\n// -----------------------------------------------------------------\n"
  },
  {
    "path": "doc/spec-mini.md",
    "content": "The XGo Mini Specification\n=====\n\nXGo has a recommended best practice syntax set, which we call the XGo Mini Specification. It is simple but Turing-complete and can elegantly implement any business requirements.\n\n## Notation\n\nThe syntax is specified using a [variant](https://en.wikipedia.org/wiki/Wirth_syntax_notation) of Extended Backus-Naur Form (EBNF):\n\n```go\nSyntax      = { Production } .\nProduction  = production_name \"=\" [ Expression ] \".\" .\nExpression  = Term { \"|\" Term } .\nTerm        = Factor { Factor } .\nFactor      = production_name | token [ \"…\" token ] | Group | Option | Repetition .\nGroup       = \"(\" Expression \")\" .\nOption      = \"[\" Expression \"]\" .\nRepetition  = \"{\" Expression \"}\" .\n```\n\nProductions are expressions constructed from terms and the following operators, in increasing precedence:\n\n```go\n|   alternation\n()  grouping\n[]  option (0 or 1 times)\n{}  repetition (0 to n times)\n```\n\nLowercase production names are used to identify lexical (terminal) tokens. Non-terminals are in CamelCase. Lexical tokens are enclosed in double quotes `\"\"` or back quotes ``.\n\nThe form `a … b` represents the set of characters from a through b as alternatives. The horizontal ellipsis `…` is also used elsewhere in the spec to informally denote various enumerations or code snippets that are not further specified. The character … (as opposed to the three characters `...`) is not a token of the XGo language.\n\n## Source code representation\n\nSource code is Unicode text encoded in [UTF-8](https://en.wikipedia.org/wiki/UTF-8). The text is not canonicalized, so a single accented code point is distinct from the same character constructed from combining an accent and a letter; those are treated as two code points. For simplicity, this document will use the unqualified term character to refer to a Unicode code point in the source text.\n\nEach code point is distinct; for instance, uppercase and lowercase letters are different characters.\n\nImplementation restriction: For compatibility with other tools, a compiler may disallow the NUL character (U+0000) in the source text.\n\nImplementation restriction: For compatibility with other tools, a compiler may ignore a UTF-8-encoded byte order mark (U+FEFF) if it is the first Unicode code point in the source text. A byte order mark may be disallowed anywhere else in the source.\n\n### Characters\n\nThe following terms are used to denote specific Unicode character categories:\n\n```go\nnewline        = /* the Unicode code point U+000A */ .\nunicode_char   = /* an arbitrary Unicode code point except newline */ .\nunicode_letter = /* a Unicode code point categorized as \"Letter\" */ .\nunicode_digit  = /* a Unicode code point categorized as \"Number, decimal digit\" */ .\n```\n\nIn [The Unicode Standard 8.0](https://www.unicode.org/versions/Unicode8.0.0/), Section 4.5 \"General Category\" defines a set of character categories. Go treats all characters in any of the Letter categories Lu, Ll, Lt, Lm, or Lo as Unicode letters, and those in the Number category Nd as Unicode digits.\n\n### Letters and digits\n\nThe underscore character _ (U+005F) is considered a lowercase letter.\n\n```go\nletter        = unicode_letter | \"_\" .\ndecimal_digit = \"0\" … \"9\" .\nbinary_digit  = \"0\" | \"1\" .\noctal_digit   = \"0\" … \"7\" .\nhex_digit     = \"0\" … \"9\" | \"A\" … \"F\" | \"a\" … \"f\" .\n```\n\n## Lexical elements\n\n### Comments\n\nComments serve as program documentation. There are three forms:\n\n* _Line comments_ start with the character sequence `//` and stop at the end of the line.\n* _Line comments_ start with the character sequence `#` and stop at the end of the line.\n* _General comments_ start with the character sequence `/*` and stop with the first subsequent character sequence `*/`.\n\nA _general comment_ containing no newlines acts like a space. Any other comment acts like a newline.\n\n```\n# this is a line comment\n// this is another line comment\n/* this is a general comment */\n```\n\n### Tokens\n\nTokens form the vocabulary of the XGo language. There are four classes: _identifiers_, _keywords_, _operators_ and _punctuation_, and _literals_. White space, formed from spaces (U+0020), horizontal tabs (U+0009), carriage returns (U+000D), and newlines (U+000A), is ignored except as it separates tokens that would otherwise combine into a single token. Also, a newline or end of file may trigger the insertion of a [semicolon](). While breaking the input into tokens, the next token is the longest sequence of characters that form a valid token.\n\n### Semicolons\n\nThe formal syntax uses semicolons \";\" as terminators in a number of productions. XGo programs may omit most of these semicolons using the following two rules:\n\n* When the input is broken into tokens, a semicolon is automatically inserted into the token stream immediately after a line's final token if that token is\n  * an [identifier](#identifiers)\n  * an [integer](), [floating-point](), [imaginary](), [rune](), or [string]() literal\n  * one of the [keywords]() `break`, `continue`, `fallthrough`, or `return`\n  * one of the [operators and punctuation]() `++`, `--`, `)`, `]`, or `}`\n* To allow complex statements to occupy a single line, a semicolon may be omitted before a closing `\")\"` or `\"}\"`.\n\nTo reflect idiomatic use, code examples in this document elide semicolons using these rules.\n\n### Identifiers\n\nIdentifiers name program entities such as variables and types. An identifier is a sequence of one or more letters and digits. The first character in an identifier must be a letter.\n\n```go\nidentifier = letter { letter | unicode_digit } .\n```\n\n```go\na\n_x9\nThisVariableIsExported\nαβ\n```\n\nSome identifiers are [predeclared](#predeclared-identifiers).\n\n### Keywords\n\nThe following keywords are reserved and may not be used as identifiers (TODO: some keywords are allowed as identifiers).\n\n```go\nbreak        default      func         interface    select\ncase         defer        go           map          type\nchan         else         goto         package      switch\nconst        fallthrough  if           range        var\ncontinue     for          import       return\n```\n\n### Operators and punctuation\n\nThe following character sequences represent [operators](#operators) (including [assignment operators](#assignment-statements)) and punctuation:\n\n```\n+    &     +=    &=     &&    ==    !=    (    )\n-    |     -=    |=     ||    <     <=    [    ]\n*    ^     *=    ^=     <-    >     >=    {    }\n/    <<    /=    <<=    ++    =     :=    ,    ;\n%    >>    %=    >>=    --    !     ...   .    :\n     &^          &^=          ~\n```\n\n### Integer literals\n\nAn integer literal is a sequence of digits representing an [integer constant](#constants). An optional prefix sets a non-decimal base: 0b or 0B for binary, 0, 0o, or 0O for octal, and 0x or 0X for hexadecimal. A single 0 is considered a decimal zero. In hexadecimal literals, letters a through f and A through F represent values 10 through 15.\n\nFor readability, an underscore character _ may appear after a base prefix or between successive digits; such underscores do not change the literal's value.\n\n```go\nint_lit        = decimal_lit | binary_lit | octal_lit | hex_lit .\ndecimal_lit    = \"0\" | ( \"1\" … \"9\" ) [ [ \"_\" ] decimal_digits ] .\nbinary_lit     = \"0\" ( \"b\" | \"B\" ) [ \"_\" ] binary_digits .\noctal_lit      = \"0\" [ \"o\" | \"O\" ] [ \"_\" ] octal_digits .\nhex_lit        = \"0\" ( \"x\" | \"X\" ) [ \"_\" ] hex_digits .\n\ndecimal_digits = decimal_digit { [ \"_\" ] decimal_digit } .\nbinary_digits  = binary_digit { [ \"_\" ] binary_digit } .\noctal_digits   = octal_digit { [ \"_\" ] octal_digit } .\nhex_digits     = hex_digit { [ \"_\" ] hex_digit } .\n```\n\n```go\n42\n4_2\n0600\n0_600\n0o600\n0O600       // second character is capital letter 'O'\n0xBadFace\n0xBad_Face\n0x_67_7a_2f_cc_40_c6\n170141183460469231731687303715884105727\n170_141183_460469_231731_687303_715884_105727\n\n_42         // an identifier, not an integer literal\n42_         // invalid: _ must separate successive digits\n4__2        // invalid: only one _ at a time\n0_xBadFace  // invalid: _ must separate successive digits\n```\n\n### Floating-point literals\n\nA floating-point literal is a decimal or hexadecimal representation of a [floating-point constant](#constants).\n\nA decimal floating-point literal consists of an integer part (decimal digits), a decimal point, a fractional part (decimal digits), and an exponent part (e or E followed by an optional sign and decimal digits). One of the integer part or the fractional part may be elided; one of the decimal point or the exponent part may be elided. An exponent value exp scales the mantissa (integer and fractional part) by 10<sup>exp</sup>.\n\nA hexadecimal floating-point literal consists of a 0x or 0X prefix, an integer part (hexadecimal digits), a radix point, a fractional part (hexadecimal digits), and an exponent part (p or P followed by an optional sign and decimal digits). One of the integer part or the fractional part may be elided; the radix point may be elided as well, but the exponent part is required. (This syntax matches the one given in IEEE 754-2008 §5.12.3.) An exponent value exp scales the mantissa (integer and fractional part) by 2<sup>exp</sup>.\n\nFor readability, an underscore character _ may appear after a base prefix or between successive digits; such underscores do not change the literal value.\n\n```go\nfloat_lit         = decimal_float_lit | hex_float_lit .\n\ndecimal_float_lit = decimal_digits \".\" [ decimal_digits ] [ decimal_exponent ] |\n                    decimal_digits decimal_exponent |\n                    \".\" decimal_digits [ decimal_exponent ] .\ndecimal_exponent  = ( \"e\" | \"E\" ) [ \"+\" | \"-\" ] decimal_digits .\n\nhex_float_lit     = \"0\" ( \"x\" | \"X\" ) hex_mantissa hex_exponent .\nhex_mantissa      = [ \"_\" ] hex_digits \".\" [ hex_digits ] |\n                    [ \"_\" ] hex_digits |\n                    \".\" hex_digits .\nhex_exponent      = ( \"p\" | \"P\" ) [ \"+\" | \"-\" ] decimal_digits .\n```\n\n```go\n0.\n72.40\n072.40       // == 72.40\n2.71828\n1.e+0\n6.67428e-11\n1E6\n.25\n.12345E+5\n1_5.         // == 15.0\n0.15e+0_2    // == 15.0\n\n0x1p-2       // == 0.25\n0x2.p10      // == 2048.0\n0x1.Fp+0     // == 1.9375\n0X.8p-0      // == 0.5\n0X_1FFFP-16  // == 0.1249847412109375\n\n0x15e-2      // == 0x15e - 2 (integer subtraction)\n\n0x.p1        // invalid: mantissa has no digits\n1p-2         // invalid: p exponent requires hexadecimal mantissa\n0x1.5e-2     // invalid: hexadecimal mantissa requires p exponent\n1_.5         // invalid: _ must separate successive digits\n1._5         // invalid: _ must separate successive digits\n1.5_e1       // invalid: _ must separate successive digits\n1.5e_1       // invalid: _ must separate successive digits\n1.5e1_       // invalid: _ must separate successive digits\n```\n\n### Rational literals\n\nRational literals in XGo come in two forms:\n\n* **Rational integers**: An integer followed by the suffix `r`.\n* **Fractions**: A numerator, division operator `/`, denominator, followed by the suffix `r`\n\n```\nrational_lit   = rational_int | rational_frac .\nrational_int   = int_lit \"r\" .\nrational_frac  = int_lit \"/\" int_lit \"r\" .\n```\n\nExamples:\n\n```sh\n1r       # Rational integer 1\n2/3r     # Fraction 2/3\n```\n\n### Imaginary literals\n\nAn imaginary literal represents the imaginary part of a [complex constant](#constants). It consists of an [integer](#integer-literals) or [floating-point](#floating-point-literals) literal followed by the lowercase letter _i_. The value of an imaginary literal is the value of the respective integer or floating-point literal multiplied by the imaginary unit _i_.\n\n```go\nimaginary_lit = (decimal_digits | int_lit | float_lit) \"i\" .\n```\n\nFor backward compatibility, an imaginary literal's integer part consisting entirely of decimal digits (and possibly underscores) is considered a decimal integer, even if it starts with a leading `0`.\n\n```go\n0i\n0123i         // == 123i for backward-compatibility\n0o123i        // == 0o123 * 1i == 83i\n0xabci        // == 0xabc * 1i == 2748i\n0.i\n2.71828i\n1.e+0i\n6.67428e-11i\n1E6i\n.25i\n.12345E+5i\n0x1p-2i       // == 0x1p-2 * 1i == 0.25i\n```\n\n### Boolean literals\n\nThe boolean truth values are represented by the [predeclared constants](#constants) `true` and `false`.\n\n```go\ntrue\nfalse\n```\n\n### Rune literals\n\nA rune literal represents a [rune constant](#constants), an integer value identifying a Unicode code point. A rune literal is expressed as one or more characters enclosed in single quotes, as in `'x'` or `'\\n'`. Within the quotes, any character may appear except newline and unescaped single quote. A single quoted character represents the Unicode value of the character itself, while multi-character sequences beginning with a backslash encode values in various formats.\n\nThe simplest form represents the single character within the quotes; since XGo source text is Unicode characters encoded in UTF-8, multiple UTF-8-encoded bytes may represent a single integer value. For instance, the literal `'a'` holds a single byte representing a literal a, Unicode `U+0061`, value 0x61, while `'ä'` holds two bytes (0xc3 0xa4) representing a literal a-dieresis, `U+00E4`, value 0xe4.\n\nSeveral backslash escapes allow arbitrary values to be encoded as ASCII text. There are four ways to represent the integer value as a numeric constant: `\\x` followed by exactly two hexadecimal digits; `\\u` followed by exactly four hexadecimal digits; `\\U` followed by exactly eight hexadecimal digits, and a plain backslash `\\` followed by exactly three octal digits. In each case the value of the literal is the value represented by the digits in the corresponding base.\n\nAlthough these representations all result in an integer, they have different valid ranges. Octal escapes must represent a value between 0 and 255 inclusive. Hexadecimal escapes satisfy this condition by construction. The escapes `\\u` and `\\U` represent Unicode code points so within them some values are illegal, in particular those above 0x10FFFF and surrogate halves.\n\nAfter a backslash, certain single-character escapes represent special values:\n\n```\n\\a   U+0007 alert or bell\n\\b   U+0008 backspace\n\\f   U+000C form feed\n\\n   U+000A line feed or newline\n\\r   U+000D carriage return\n\\t   U+0009 horizontal tab\n\\v   U+000B vertical tab\n\\\\   U+005C backslash\n\\'   U+0027 single quote  (valid escape only within rune literals)\n\\\"   U+0022 double quote  (valid escape only within string literals)\n```\n\nAn unrecognized character following a backslash in a rune literal is illegal.\n\n```go\nrune_lit         = \"'\" ( unicode_value | byte_value ) \"'\" .\nunicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .\nbyte_value       = octal_byte_value | hex_byte_value .\noctal_byte_value = `\\` octal_digit octal_digit octal_digit .\nhex_byte_value   = `\\` \"x\" hex_digit hex_digit .\nlittle_u_value   = `\\` \"u\" hex_digit hex_digit hex_digit hex_digit .\nbig_u_value      = `\\` \"U\" hex_digit hex_digit hex_digit hex_digit\n                           hex_digit hex_digit hex_digit hex_digit .\nescaped_char     = `\\` ( \"a\" | \"b\" | \"f\" | \"n\" | \"r\" | \"t\" | \"v\" | `\\` | \"'\" | `\"` ) .\n```\n\n```go\n'a'\n'ä'\n'本'\n'\\t'\n'\\000'\n'\\007'\n'\\377'\n'\\x07'\n'\\xff'\n'\\u12e4'\n'\\U00101234'\n'\\''         // rune literal containing single quote character\n'aa'         // illegal: too many characters\n'\\k'         // illegal: k is not recognized after a backslash\n'\\xa'        // illegal: too few hexadecimal digits\n'\\0'         // illegal: too few octal digits\n'\\400'       // illegal: octal value over 255\n'\\uDFFF'     // illegal: surrogate half\n'\\U00110000' // illegal: invalid Unicode code point\n```\n\n### String literals\n\nA string literal represents a [string constant](#constants) obtained from concatenating a sequence of characters. There are two forms: raw string literals and interpreted string literals.\n\nRaw string literals are character sequences between back quotes, as in \\`foo\\`. Within the quotes, any character may appear except back quote. The value of a raw string literal is the string composed of the uninterpreted (implicitly UTF-8-encoded) characters between the quotes; in particular, backslashes have no special meaning and the string may contain newlines. Carriage return characters (`'\\r'`) inside raw string literals are discarded from the raw string value.\n\nInterpreted string literals are character sequences between double quotes, as in `\"bar\"`. Within the quotes, any character may appear except newline and unescaped double quote. The text between the quotes forms the value of the literal, with backslash escapes interpreted as they are in [rune literals](#rune-literals) (except that `\\'` is illegal and `\\\"` is legal), with the same restrictions. The three-digit octal (`\\nnn`) and two-digit hexadecimal (`\\xnn`) escapes represent individual bytes of the resulting string; all other escapes represent the (possibly multi-byte) UTF-8 encoding of individual characters. Thus inside a string literal `\\377` and `\\xFF` represent a single byte of value 0xFF=255, while `ÿ`, `\\u00FF`, `\\U000000FF` and `\\xc3\\xbf` represent the two bytes 0xc3 0xbf of the UTF-8 encoding of character `U+00FF`.\n\n```go\nstring_lit             = raw_string_lit | interpreted_string_lit .\nraw_string_lit         = \"`\" { unicode_char | newline } \"`\" .\ninterpreted_string_lit = `\"` { unicode_value | byte_value } `\"` .\n```\n\n```go\n`abc`                // same as \"abc\"\n`\\n\n\\n`                  // same as \"\\\\n\\n\\\\n\"\n\"\\n\"\n\"\\\"\"                 // same as `\"`\n\"Hello, world!\\n\"\n\"日本語\"\n\"\\u65e5本\\U00008a9e\"\n\"\\xff\\u00FF\"\n\"\\uD800\"             // illegal: surrogate half\n\"\\U00110000\"         // illegal: invalid Unicode code point\n```\n\nThese examples all represent the same string:\n\n```go\n\"日本語\"                                 // UTF-8 input text\n`日本語`                                 // UTF-8 input text as a raw literal\n\"\\u65e5\\u672c\\u8a9e\"                    // the explicit Unicode code points\n\"\\U000065e5\\U0000672c\\U00008a9e\"        // the explicit Unicode code points\n\"\\xe6\\x97\\xa5\\xe6\\x9c\\xac\\xe8\\xaa\\x9e\"  // the explicit UTF-8 bytes\n```\n\nIf the source code represents a character as two code points, such as a combining form involving an accent and a letter, the result will be an error if placed in a rune literal (it is not a single code point), and will appear as two code points if placed in a string literal.\n\n### Special literals\n\nTODO\n\n```go\nnil\niota\n```\n\n## Constants\n\nThere are _boolean constants_, _rune constants_, _integer constants_, _floating-point constants_, _complex constants_, and _string constants_. Rune, integer, floating-point, and complex constants are collectively called _numeric constants_.\n\nA constant value is represented by a [rune](#rune-literals), [integer](#integer-literals), [floating-point](#floating-point-literals), [imaginary](#imaginary-literals), [boolean](#boolean-literals) or [string](#string-literals) literal, an identifier denoting a constant, a [constant expression](#constant-expressions), a [conversion](#conversions) with a result that is a constant, or the result value of some built-in functions such as `min` or `max` applied to constant arguments, `unsafe.Sizeof` applied to [certain values](), `cap` or `len` applied to [some expressions](), `real` and `imag` applied to a complex constant and complex applied to numeric constants. The boolean truth values are represented by the predeclared constants `true` and `false`. The predeclared identifier `iota` denotes an integer constant.\n\nNot all literals are constants. For example:\n\n```sh\nnil\n1r\n2/3r\n```\n\nIn general, complex constants are a form of [constant expression](#constant-expressions) and are discussed in that section.\n\nNumeric constants represent exact values of arbitrary precision and do not overflow. Consequently, there are no constants denoting the IEEE-754 negative zero, infinity, and not-a-number values.\n\nConstants may be [typed](#types) or _untyped_. Literal constants (including `true`, `false`, `iota`), and certain [constant expressions]() containing only untyped constant operands are untyped.\n\nA constant may be given a type explicitly by a [constant declaration]() or [conversion](#conversions), or implicitly when used in a [variable declaration]() or an [assignment statement]() or as an operand in an [expression](#expressions). It is an error if the constant value cannot be [represented]() as a value of the respective type.\n\nAn untyped constant has a _default type_ which is the type to which the constant is implicitly converted in contexts where a typed value is required, for instance, in a [short variable declaration]() such as `i := 0` where there is no explicit type. The default type of an untyped constant is `bool`, `rune`, `int`, `float64`, `complex128`, or `string` respectively, depending on whether it is a boolean, rune, integer, floating-point, complex, or string constant.\n\n\n## Variables\n\nA variable is a storage location for holding a value. The set of permissible values is determined by the variable's [type](#types).\n\nA [variable declaration]() or, for function parameters and results, the signature of a [function declaration]() or [function literal]() reserves storage for a named variable. Calling the built-in function [new]() or taking the address of a [composite literal]() allocates storage for a variable at run time. Such an anonymous variable is referred to via a (possibly implicit) [pointer indirection](#address-operators).\n\n_Structured_ variables of [array](#array-types), [slice](#slice-types), and [class](#classes) types have elements and fields that may be [addressed](#address-operators) individually. Each such element acts like a variable.\n\nThe _static type_ (or just _type_) of a variable is the type given in its declaration, the type provided in the new call or composite literal, or the type of an element of a class variable. Variables of interface type also have a distinct _dynamic type_, which is the (non-interface) type of the value assigned to the variable at run time (unless the value is the predeclared identifier `nil`, which has no type). The dynamic type may vary during execution but values stored in interface variables are always [assignable]() to the static type of the variable.\n\n```go\nvar x any  // x is nil and has static type any\nvar v *T   // v has value nil, static type *T\nx = 42     // x has value 42 and dynamic type int\nx = v      // x has value (*T)(nil) and dynamic type *T\n```\n\nA variable's value is retrieved by referring to the variable in an [expression](#expressions); it is the most recent value [assigned]() to the variable. If a variable has not yet been assigned a value, its value is the [zero value]() for its type.\n\n\n## Types\n\nA type determines a set of values together with operations and methods specific to those values. A type may be denoted by a _type name_. A type may also be specified using a type literal, which composes a type from existing types.\n\n```go\nType      = TypeName [ TypeArgs ] | TypeLit | \"(\" Type \")\" .\nTypeName  = identifier | QualifiedIdent .\nTypeArgs  = \"[\" TypeList [ \",\" ] \"]\" .\nTypeList  = Type { \",\" Type } .\nTypeLit   = ArrayType | TupleType | PointerType | FunctionType | InterfaceType |\n            SliceType | MapType . // TODO: check this\n```\n\nThe language [predeclares]() certain type names. Others are introduced with [type declarations](#type-declarations). _Composite types_—array, tuple, pointer, function, interface, slice, map—may be constructed using type literals.\n\nPredeclared types and defined types are called _named types_. An alias denotes a named type if the type given in the alias declaration is a named type.\n\n### Boolean types\n\nA _boolean type_ represents the set of Boolean truth values denoted by the predeclared constants true and false. The predeclared boolean type is `bool`; it is a defined type.\n\n```go\nbool\n```\n\n### Numeric types\n\nAn _integer_, _floating-point_, _rational_ or _complex_ type represents the set of integer, floating-point, or complex values, respectively. They are collectively called _numeric types_. The predeclared architecture-independent numeric types are:\n\n```go\nuint8       // the set of all unsigned  8-bit integers (0 to 255)\nuint16      // the set of all unsigned 16-bit integers (0 to 65535)\nuint32      // the set of all unsigned 32-bit integers (0 to 4294967295)\nuint64      // the set of all unsigned 64-bit integers (0 to 18446744073709551615)\n\nint8        // the set of all signed  8-bit integers (-128 to 127)\nint16       // the set of all signed 16-bit integers (-32768 to 32767)\nint32       // the set of all signed 32-bit integers (-2147483648 to 2147483647)\nint64       // the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)\n\nfloat32     // the set of all IEEE-754 32-bit floating-point numbers\nfloat64     // the set of all IEEE-754 64-bit floating-point numbers\n\ncomplex64   // the set of all complex numbers with float32 real and imaginary parts\ncomplex128  // the set of all complex numbers with float64 real and imaginary parts\n\nbyte        // alias for uint8\nrune        // alias for int32\n```\n\nThe value of an _n_-bit integer is n bits wide and represented using [two's complement arithmetic](https://en.wikipedia.org/wiki/Two's_complement).\n\nThere is also a set of predeclared integer types with implementation-specific sizes:\n\n```go\nuint     // either 32 or 64 bits\nint      // same size as uint\nuintptr  // an unsigned integer large enough to store the uninterpreted bits of a pointer value\n```\n\nTo avoid portability issues all numeric types are defined types and thus distinct except _byte_, which is an [alias]() for _uint8_, and _rune_, which is an alias for _int32_. Explicit conversions are required when different numeric types are mixed in an expression or assignment. For instance, _int32_ and _int_ are not the same type even though they may have the same size on a particular architecture.\n\nTODO:\n\n```go\nbigint  // TODO\nbigrat  // TODO\n```\n\n### String types\n\nA _string type_ represents the set of string values. A string value is a (possibly empty) sequence of bytes. The number of bytes is called the length of the string and is never negative. Strings are immutable: once created, it is impossible to change the contents of a string. The predeclared string type is `string`; it is a defined type.\n\n```go\nstring\n```\n\nThe length of a string `s` can be discovered using the built-in function [len](). The length is a compile-time constant if the string is a constant. A string's bytes can be accessed by integer [indices]() `0` through `len(s)-1`. It is illegal to take the address of such an element; if `s[i]` is the i'th byte of a string, `&s[i]` is invalid.\n\n\n### Array types\n\nAn array is a numbered sequence of elements of a single type, called the element type. The number of elements is called the length of the array and is never negative.\n\n```go\nArrayType   = \"[\" ArrayLength \"]\" ElementType .\nArrayLength = Expression .\nElementType = Type .\n```\n\nThe length is part of the array's type; it must evaluate to a non-negative [constant](#constants) [representable]() by a value of type int. The length of array `a` can be discovered using the built-in function [len](). The elements can be addressed by integer [indices]() `0` through `len(a)-1`. Array types are always one-dimensional but may be composed to form multi-dimensional types.\n\n```go\n[32]byte\n[2*N](x int32, y int32)\n[1000]*float64\n[3][5]int\n[2][2][2]float64  // same as [2]([2]([2]float64))\n```\n\nAn array type T may not have an element of type T, or of a type containing T as a component, directly or indirectly, if those containing types are only array or tuple types.\n\n```go\n// invalid array types\ntype (\n\tT1 [10]T1                 // element type of T1 is T1\n\tT2 [10](f T2)             // T2 contains T2 as component of a tuple\n\tT3 [10]T4                 // T3 contains T3 as component of a tuple in T4\n\tT4 (f [10]T3)             // T4 contains T4 as component of array T3 in a tuple\n)\n\n// valid array types\ntype (\n\tT5 [10]*T5                // T5 contains T5 as component of a pointer\n\tT6 [10]func() T6          // T6 contains T6 as component of a function type\n\tT7 [10](f []T7)           // T7 contains T7 as component of a slice in a tuple\n)\n```\n\n### Pointer types\n\nA _pointer_ type denotes the set of all pointers to [variables](#variables) of a given type, called the base type of the pointer. The value of an uninitialized pointer is `nil`.\n\n```go\nPointerType = \"*\" BaseType .\nBaseType    = Type .\n```\n\n```go\n*Point\n*[4]int\n```\n\n### Slice types\n\nA _slice_ is a descriptor for a contiguous segment of an _underlying array_ and provides access to a numbered sequence of elements from that array. A slice type denotes the set of all slices of arrays of its element type. The number of elements is called the length of the slice and is never negative. The value of an uninitialized slice is `nil`.\n\n```go\nSliceType = \"[\" \"]\" ElementType .\n```\n\nThe length of a slice `s` can be discovered by the built-in function [len](); unlike with arrays it may change during execution. The elements can be addressed by integer [indices]() `0` through `len(s)-1`. The slice index of a given element may be less than the index of the same element in the underlying array.\n\nA slice, once initialized, is always associated with an underlying array that holds its elements. A slice therefore shares storage with its array and with other slices of the same array; by contrast, distinct arrays always represent distinct storage.\n\nThe array underlying `a` slice may extend past the end of the slice. The capacity is a measure of that extent: it is the sum of the length of the slice and the length of the array beyond the slice; a slice of length up to that capacity can be created by [slicing]() a new one from the original slice. The capacity of a slice a can be discovered using the built-in function `cap(a)`.\n\nA new, initialized slice value for a given element type `T` may be made using the built-in function [make](), which takes a slice type and parameters specifying the length and optionally the capacity. A slice created with make always allocates a new, hidden array to which the returned slice value refers. That is, executing\n\n```go\nmake([]T, length, capacity)\n```\n\nproduces the same slice as allocating an array and [slicing]() it, so these two expressions are equivalent:\n\n```\nmake([]int, 50, 100)\nnew([100]int)[0:50]\n```\n\nLike arrays, slices are always one-dimensional but may be composed to construct higher-dimensional objects. With arrays of arrays, the inner arrays are, by construction, always the same length; however with slices of slices (or arrays of slices), the inner lengths may vary dynamically. Moreover, the inner slices must be initialized individually.\n\n### Tuple types\n\nA _tuple_ type is a lightweight data structure for grouping multiple values together. Tuple types are syntactic sugar for anonymous structs with ordinal field names (`X_0`, `X_1`, etc.). Named fields in tuples serve as compile-time aliases for better code readability but map to ordinal fields at runtime.\n\n```go\nTupleType = Parameters .\n```\n\nA tuple type is denoted by a parenthesized, comma-separated list of types, optionally with names for the elements:\n\n```go\n// Empty tuple (equivalent to struct{})\n()\n\n// Anonymous tuples (elements accessed by ordinal: .0, .1, .2)\n(int, string)\n(int, string, bool)\n\n// Named tuples (compile-time names for readability, runtime uses ordinal fields)\ntype Point (x int, y int)\ntype Person (name string, age int)\n\n// Type shorthand syntax (same as: x int, y int, z int)\ntype Point3D (x, y, z int)\n```\n\n**Single-Element Degeneracy**: A tuple with a single element `(T)` or `(value T)` degenerates to `T` itself, not a tuple.\n\n#### Tuple Construction\n\nTuples are constructed using **function-style syntax**:\n\n```go\n// Positional construction\np := Point(10, 20)\nresult := (42, \"success\", true)\n\n// Keyword argument construction (using = for clarity)\np := Point(x = 10, y = 20)\nperson := Person(name = \"Alice\", age = 30)\n```\n\n#### Field Access\n\nTuple fields can be accessed by:\n- **Named fields**: `p.x`, `p.y` (using declared names)\n- **Ordinal notation**: `p.0`, `p.1` (numeric shorthand)\n\n```go\ntype Point (x int, y int)\np := Point(10, 20)\n\necho p.x      // 10 (using compile-time name)\necho p.0      // 10 (using ordinal index, equivalent to p.x)\necho p.y      // 20\necho p.1      // 20\n```\n\n#### Type Identity\n\nTwo tuple types are identical if they have the same number of elements and corresponding element types are identical. Named fields are not part of type identity:\n\n```go\ntype Point (x int, y int)\ntype Coord (a int, b int)\n\n// Point and Coord have identical underlying types (both map to struct{ X_0 int; X_1 int })\n// but are different named types\n```\n\n#### Tuples in Composite Types\n\nTuples can be used as element types in arrays, slices, maps, and channels:\n\n```go\n// Tuple as map value type\nvar cache map[string](int, bool)\n\n// Tuple as slice element type\nvar pairs [](string, int)\n\n// Tuple as channel element type\nvar ch chan (int, error)\n```\n\n#### Recursive Type Restrictions\n\nA tuple type `T` may not contain an element of type `T`, or of a type containing `T` as a component, directly or indirectly, if those containing types are only array or tuple types.\n\n```go\n// invalid tuple types\ntype (\n\tT1 (T1, int)              // T1 contains an element of T1\n\tT2 ([10]T2, string)       // T2 contains T2 as component of an array\n)\n\n// valid tuple types\ntype (\n\tT3 (*T3, int)             // T3 contains T3 as component of a pointer\n\tT4 (func() T4, string)    // T4 contains T4 as component of a function type\n\tT5 ([]T5, int)            // T5 contains T5 as component of a slice\n)\n```\n\n### Map types\n\nA _map_ is an unordered group of elements of one type, called the element type, indexed by a set of unique _keys_ of another type, called the key type. The value of an uninitialized map is `nil`.\n\n```go\nMapType     = \"map\" \"[\" KeyType \"]\" ElementType .\nKeyType     = Type .\n```\n\nThe comparison operators `==` and `!=` must be fully defined for operands of the key type; thus the key type must not be a function, map, or slice. If the key type is an interface type, these comparison operators must be defined for the dynamic key values; failure will cause a run-time panic.\n\n```go\nmap[string]int\nmap[*T](x float64, y float64)\nmap[string]any\n```\n\nThe number of map elements is called its length. For a map `m`, it can be discovered using the built-in function [len]() and may change during execution. Elements may be added during execution using [assignments]() and retrieved with [index expressions](); they may be removed with the [delete]() and [clear]() built-in function.\n\nA new, empty map value is made using the built-in function [make](), which takes the map type and an optional capacity hint as arguments:\n\n```go\nmake(map[string]int)\nmake(map[string]int, 100)\n```\n\nThe initial capacity does not bound its size: maps grow to accommodate the number of items stored in them, with the exception of nil maps. A nil map is equivalent to an empty map except that no elements may be added.\n\n### Function types\n\nA _function_ type denotes the set of all functions with the same parameter and result types. The value of an uninitialized variable of function type is `nil`.\n\n```go\nFunctionType   = \"func\" Signature .\nSignature      = Parameters [ Result ] .\nResult         = Parameters | Type .\nParameters     = \"(\" [ ParameterList [ \",\" ] ] \")\" .\nParameterList  = ParameterDecl { \",\" ParameterDecl } .\nParameterDecl  = [ IdentifierList ] [ \"...\" ] Type .\n```\n\nWithin a list of parameters or results, the names (IdentifierList) must either all be present or all be absent. If present, each name stands for one item (parameter or result) of the specified type and all non-[blank]() names in the signature must be [unique](). If absent, each type stands for one item of that type. Parameter and result lists are always parenthesized except that if there is exactly one unnamed result it may be written as an unparenthesized type.\n\nThe final incoming parameter in a function signature may have a type prefixed with `...`. A function with such a parameter is called _variadic_ and may be invoked with zero or more arguments for that parameter.\n\n```go\nfunc()\nfunc(x int) int\nfunc(a, _ int, z float32) bool\nfunc(a, b int, z float32) (bool)\nfunc(prefix string, values ...int)\nfunc(a, b int, z float64, opt ...any) (success bool)\nfunc(int, int, float64) (float64, *[]int)\nfunc(n int) func(p *T)\n```\n\n### Interface types\n\nAn interface type specifies a [method set]() called its _interface_. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to _implement the interface_. The value of an uninitialized variable of interface type is `nil`.\n\n```go\nInterfaceType      = \"interface\" \"{\" { ( MethodSpec | InterfaceTypeName ) \";\" } \"}\" .\nMethodSpec         = MethodName Signature .\nMethodName         = identifier .\nInterfaceTypeName  = TypeName .\n```\n\nAn interface type may specify methods _explicitly_ through method specifications, or it may _embed_ methods of other interfaces through interface type names.\n\n```go\n// A simple File interface.\ninterface {\n\tRead([]byte) (int, error)\n\tWrite([]byte) (int, error)\n\tClose() error\n}\n```\n\nThe name of each explicitly specified method must be [unique]() and not [blank]().\n\n```go\ninterface {\n\tString() string\n\tString() string  // illegal: String not unique\n\t_(x int)         // illegal: method must have non-blank name\n}\n```\n\nMore than one type may implement an interface. For instance, if two types `S1` and `S2` have the method set\n\n```go\nfunc Read(p []byte) (n int, err error)\nfunc Write(p []byte) (n int, err error)\nfunc Close() error\n```\n\n(where `T` stands for either `S1` or `S2`) then the `File` interface is implemented by both `S1` and `S2`, regardless of what other methods `S1` and `S2` may have or share.\n\nA type implements any interface comprising any subset of its methods and may therefore implement several distinct interfaces. For instance, all types implement the _empty interface_:\n\n```go\ninterface{}\n```\n\nSimilarly, consider this interface specification, which appears within a [type declaration](#type-declarations) to define an interface called `Locker`:\n\n```go\ntype Locker interface {\n\tLock()\n\tUnlock()\n}\n```\n\nIf `S1` and `S2` also implement\n\n```go\nfunc Lock() { … }\nfunc Unlock() { … }\n```\n\nthey implement the `Locker` interface as well as the `File` interface.\n\nAn interface `T` may use a (possibly qualified) interface type name `E` in place of a method specification. This is called _embedding_ interface `E` in `T`. The method set of `T` is the union of the method sets of T’s explicitly declared methods and of T’s embedded interfaces.\n\n```go\ntype Reader interface {\n\tRead(p []byte) (n int, err error)\n\tClose() error\n}\n\ntype Writer interface {\n\tWrite(p []byte) (n int, err error)\n\tClose() error\n}\n\n// ReadWriter's methods are Read, Write, and Close.\ntype ReadWriter interface {\n\tReader  // includes methods of Reader in ReadWriter's method set\n\tWriter  // includes methods of Writer in ReadWriter's method set\n}\n```\n\nA union of method sets contains the (exported and non-exported) methods of each method set exactly once, and methods with the [same]() names must have [identical]() signatures.\n\n```go\ntype ReadCloser interface {\n\tReader   // includes methods of Reader in ReadCloser's method set\n\tClose()  // illegal: signatures of Reader.Close and Close are different\n}\n```\n\nAn interface type `T` may not embed itself or any interface type that embeds `T`, recursively.\n\n```go\n// illegal: Bad cannot embed itself\ntype Bad interface {\n\tBad\n}\n\n// illegal: Bad1 cannot embed itself using Bad2\ntype Bad1 interface {\n\tBad2\n}\ntype Bad2 interface {\n\tBad1\n}\n```\n\n#### Built-in interfaces\n\nThe following interfaces are predeclared in XGo:\n\n```go\nany    // alias for interface{}, the empty interface\nerror  // interface for error handling\n```\n\n##### The any interface\n\nThe predeclared type `any` is an alias for the empty interface `interface{}`. It represents the set of all types and is useful when you need to work with values of any type:\n\n```go\nvar x any  // x is nil and has static type any\nx = 42     // x has value 42 and dynamic type int\nx = \"hello\" // x has value \"hello\" and dynamic type string\n```\n\n##### The error interface\n\nThe predeclared type `error` is defined as:\n\n```go\ntype error interface {\n\tError() string\n}\n```\n\nIt is the conventional interface for representing an error condition, with the nil value representing no error. For instance, a function to read data from a file might be defined:\n\n```go\nfunc Read(f *File, b []byte) (n int, err error)\n```\n\nAny type that implements an `Error()` method returning a string satisfies the `error` interface.\n\n### Classes\n\nTODO (classfile)\n\n\n## Properties of types and values\n\n### Underlying types\n\nEach type T has an _underlying type_: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its declaration.\n\n```go\ntype (\n\tA1 = string\n\tA2 = A1\n)\n\ntype (\n\tB1 string\n\tB2 B1\n\tB3 []B1\n\tB4 B3\n)\n```\n\nThe underlying type of `string`, `A1`, `A2`, `B1`, and `B2` is `string`. The underlying type of `[]B1`, `B3`, and `B4` is `[]B1`.\n\n### Type identity\n\nTwo types are either _identical_ or _different_.\n\nA [named type](#types) is always different from any other type. Otherwise, two types are identical if their [underlying type](#underlying-types) literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types. In detail:\n\n* Two array types are identical if they have identical element types and the same array length.\n* Two slice types are identical if they have identical element types.\n* Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags. [Non-exported](#exported-identifiers) field names from different packages are always different.\n* Two pointer types are identical if they have identical base types.\n* Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is. Parameter and result names are not required to match.\n* Two interface types are identical if they define the same type set.\n* Two map types are identical if they have identical key and element types.\n\nGiven the declarations\n\n```go\ntype (\n\tA0 = []string\n\tA1 = A0\n\tA2 = struct{ a, b int }\n\tA3 = int\n\tA4 = func(A3, float64) *A0\n\tA5 = func(x int, _ float64) *[]string\n\n\tB0 A0\n\tB1 []string\n\tB2 struct{ a, b int }\n\tB3 struct{ a, c int }\n\tB4 func(int, float64) *B0\n\tB5 func(x int, y float64) *A1\n\n\tC0 = B0\n)\n```\n\nthese types are identical:\n\n```go\nA0, A1, and []string\nA2 and struct{ a, b int }\nA3 and int\nA4, func(int, float64) *[]string, and A5\n\nB0 and C0\n[]int and []int\nstruct{ a, b *B5 } and struct{ a, b *B5 }\nfunc(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5\n```\n\n`B0` and `B1` are different because they are new types created by distinct [type definitions](#type-definitions); `func(int, float64) *B0` and `func(x int, y float64) *[]string` are different because `B0` is different from `[]string`.\n\n### Assignability\n\nA value x of type V is _assignable_ to a [variable](#variables) of type T (\"x is assignable to T\") if one of the following conditions applies:\n\n* V and T are identical.\n* V and T have identical [underlying types](#underlying-types) but are not type parameters and at least one of V or T is not a [named type](#types).\n* T is an interface type, and x [implements]() T.\n* x is the predeclared identifier nil and T is a pointer, function, slice, map, or interface type.\n* x is an untyped [constant](#constants) [representable](#representability) by a value of type T.\n\n### Representability\n\nA [constant](#constants) x is _representable_ by a value of type T if one of the following conditions applies:\n\n* x is in the set of values [determined](#types) by T.\n* T is a floating-point type and x can be rounded to T's precision without overflow. Rounding uses IEEE 754 round-to-even rules but with an IEEE negative zero further simplified to an unsigned zero. Note that constant values never result in an IEEE negative zero, NaN, or infinity.\n* T is a complex type, and x's [components](#manipulating-complex-numbers) `real(x)` and `imag(x)` are representable by values of T's component type (`float32` or `float64`).\n\n```go\nx                   T           x is representable by a value of T because\n\n'a'                 byte        97 is in the set of byte values\n97                  rune        rune is an alias for int32, and 97 is in the set of 32-bit integers\n\"foo\"               string      \"foo\" is in the set of string values\n1024                int16       1024 is in the set of 16-bit integers\n42.0                byte        42 is in the set of unsigned 8-bit integers\n1e10                uint64      10000000000 is in the set of unsigned 64-bit integers\n2.718281828459045   float32     2.718281828459045 rounds to 2.7182817 which is in the set of float32 values\n-1e-1000            float64     -1e-1000 rounds to IEEE -0.0 which is further simplified to 0.0\n0i                  int         0 is an integer value\n(42 + 0i)           float32     42.0 (with zero imaginary part) is in the set of float32 values\n```\n\n```go\nx                   T           x is not representable by a value of T because\n\n0                   bool        0 is not in the set of boolean values\n'a'                 string      'a' is a rune, it is not in the set of string values\n1024                byte        1024 is not in the set of unsigned 8-bit integers\n-1                  uint16      -1 is not in the set of unsigned 16-bit integers\n1.1                 int         1.1 is not an integer value\n42i                 float32     (0 + 42i) is not in the set of float32 values\n1e1000              float64     1e1000 overflows to IEEE +Inf after rounding\n```\n\n### Method sets\n\nThe _method set_ of a type determines the methods that can be [called](#commands-and-calls) on an [operand]() of that type. Every type has a (possibly empty) method set associated with it:\n\n* The method set of a [defined type](#type-definitions) T consists of all [methods]() declared with receiver type T.\n* The method set of a pointer to a defined type T (where T is neither a pointer nor an interface) is the set of all methods declared with receiver *T or T.\n* The method set of an [interface type](#interface-types) is the intersection of the method sets of each type in the interface's [type set](#interface-types) (the resulting method set is usually just the set of declared methods in the interface).\n\nFurther rules apply to structs (and pointer to structs) containing embedded fields, as described in the section on [struct types](#struct-types). Any other type has an empty method set.\n\nIn a method set, each method must have a [unique](#uniqueness-of-identifiers) non-[blank](#blank-identifier) [method name]().\n\n\n## Expressions\n\nAn expression specifies the computation of a value by applying operators and functions to operands.\n\n### Operands\n\nOperands denote the elementary values in an expression. An operand may be a literal, a (possibly [qualified]()) non-[blank](#blank-identifier) identifier denoting a [constant](#constant-declarations), [variable](#variable-declarations), or [function](#function-declarations), or a parenthesized expression.\n\n```go\nOperand     = Literal | OperandName [ TypeArgs ] .\nLiteral     = BasicLit | FunctionLit | TupleLit .\nBasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .\nOperandName = identifier | QualifiedIdent .\n```\n\nThe [blank identifier](#blank-identifier) may appear as an operand only on the left-hand side of an [assignment statement](#assignment-statements).\n\n### Qualified identifiers\n\nA _qualified identifier_ is an identifier qualified with a package name prefix. Both the package name and the identifier must not be [blank](#blank-identifier).\n\n```go\nQualifiedIdent = PackageName \".\" identifier .\n```\n\nA qualified identifier accesses an identifier in a different package, which must be [imported](#import-declarations). The identifier must be [exported](#exported-identifiers) and declared in the [package block](#blocks) of that package.\n\n```go\nmath.Sin // denotes the Sin function in package math\n```\n\n### Tuple literals\n\nTuple literals construct tuple values using function-style syntax. Both anonymous and named tuples are created using parenthesized, comma-separated expressions.\n\n```go\nTupleLit = \"(\" [ ExpressionList [ \",\" ] ] \")\" .\n```\n\n#### Anonymous Tuple Literals\n\nAnonymous tuple literals create values of anonymous tuple types:\n\n```go\nresult := (42, \"success\", true)        // creates a (int, string, bool)\npair := (10, 20)                       // creates a (int, int)\nempty := ()                            // creates an empty tuple ()\n```\n\n#### Named Tuple Construction\n\nNamed tuple types are instantiated using function-style call syntax:\n\n**Positional Arguments**:\n\n```go\ntype Point (x int, y int)\np := Point(10, 20)              // positional construction\n\ntype Person (name string, age int)\nalice := Person(\"Alice\", 30)    // positional construction\n```\n\n**Keyword Arguments**:\n\nKeyword arguments use `=` for assignment and provide clarity when initializing tuples with many fields:\n\n```go\np := Point(x = 10, y = 20)              // keyword argument construction\nperson := Person(name = \"Bob\", age = 25) // keyword argument construction\n```\n\nKeyword arguments can be mixed, but all positional arguments must come before keyword arguments:\n\n```go\n// This is valid\np := Point(10, y = 20)\n\n// This is invalid\n// p := Point(x = 10, 20)  // error: positional arg after keyword arg\n```\n\n#### Zero Values\n\nCalling a tuple type with no arguments returns the zero value for that type:\n\n```go\ntype Point (x int, y int)\nzero := Point()   // equivalent to Point(0, 0)\n```\n\n#### Type Inference\n\nThe type of a tuple literal is inferred from context when possible:\n\n```go\nfunc process(p (int, string)) {\n\t// ...\n}\n\nprocess((42, \"hello\"))  // tuple literal type inferred as (int, string)\n```\n\n#### Tuple Literals in Composite Types\n\nTuple literals can be used as elements in slices, arrays, and maps:\n\n```go\npairs := [](string, int){\n\t(\"a\", 1),\n\t(\"b\", 2),\n\t(\"c\", 3),\n}\n\n// In a map\ncache := map[string](int, bool){\n\t\"key1\": (100, true),\n\t\"key2\": (200, false),\n}\n```\n\n### Function literals\n\nA function literal represents an anonymous [function](#function-declarations).\n\n```go\nFunctionLit                 = [ AnonymousParameters ] \"=>\" AnonymousFunctionBody .\nAnonymousParameters         = SingleAnonymousParameter | MultipleAnonymousParameters .\nSingleAnonymousParameter    = identifier .\nMultipleAnonymousParameters = \"(\" AnonymousParameterList \")\" .\nAnonymousParameterList      = identifier { \",\" identifier } .\nAnonymousFunctionBody       = Expression | FunctionBody .\n```\n\nA anonymous function body can be a single expression.\n\n```go\nx => math.Sin(x)\n(x, y) => x*x + y*y\n```\n\nIt can also be the same as a normal function body. The above examples are equivalent to\n\n```go\nx => {\n\treturn math.Sin(x)\n}\n\n(x, y) => {\n\treturn x*x + y*y\n}\n```\n\nAnonymous functions are typically passed in as parameters for function calls.\n\n```go\nimport \"sort\"\n\na := [32, 100, 50, 6, 92]\nsort.Slice a, (i, j) => {\n\treturn a[i] < b[j]\n}\n```\n\nNote: Both the parameter types and return value types of anonymous functions are omitted. XGo determines the parameter types and return value types of anonymous functions through type inference.\n\nFunction literals are _closures_: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.\n\n\n### Commands and calls\n\nXGo supports two styles for function and method calls: the traditional function-call style with parentheses, and a command-style syntax without parentheses.\n\n#### Function-call style\n\nThe traditional function-call syntax uses parentheses:\n\n**Syntax:**\n\n```go\nCallOrConversion = Operand \"(\" [ ArgList ] [ \"...\" ] [ \",\" ] \")\" .\nArgList          = (KwargExpr | LambdaExpr) { \",\" (KwargExpr | LambdaExpr) } .\nKwargExpr        = IDENT \"=\" LambdaExpr .\n```\n\nExamples:\n\n```go\necho(\"Hello world\")\nfmt.Println(\"Hello, world\")\n```\n\n#### Command-style syntax\n\nXGo recommends command-style code where the function name is followed by a space and then arguments, without parentheses:\n\n```go\nCommandStmt = IDENT [ \".\" IDENT ] [ SPACE ArgList ] [ \"...\" ] .\n```\n\nExamples:\n\n```go\necho \"Hello world\"\nfmt.Println \"Hello, world\"\n```\n\nCommand-style calls can be qualified (with package or receiver name) or unqualified:\n\n```go\necho \"unqualified call\"\nfmt.Println \"qualified call\"\nos.Exit 1\n```\n\nVariadic arguments are supported with the `...` operator:\n\n```go\necho elements...\n```\n\nBoth styles are equivalent and can be used interchangeably. XGo prefers command-style for its cleaner, more natural appearance, similar to shell commands. The built-in function `echo` is provided as an alias for `println` to emphasize this command-oriented approach.\n\n#### Keyword arguments\n\nXGo supports keyword arguments (kwargs) in commands and calls, allowing arguments to be specified by parameter name. When calling functions with many parameters, you can use `key=value` syntax to make your code more expressive and command-line-style.\n\n#### Using kwargs with tuples\n\nYou can use tuples or tuple pointers as keyword parameters, which provides type safety:\n\n```go\ntype Config (timeout, maxRetries int, debug bool)\n\nfunc run(task int, cfg Config?) {\n\tif cfg.timeout == 0 {\n\t\tcfg.timeout = 30\n\t}\n\tif cfg.maxRetries == 0 {\n\t\tcfg.maxRetries = 3\n\t}\n    echo \"timeout:\", cfg.timeout, \"maxRetries:\", cfg.maxRetries, \"debug:\", cfg.debug\n\techo \"task:\", task\n}\n\nrun 100, timeout = 60, maxRetries = 5\nrun 200\n```\n\n#### Using kwargs with maps\n\nYou also can use maps as keyword parameters:\n\n```go\nfunc process(opts map[string]any?, args ...any) {\n    if name, ok := opts[\"name\"]; ok {\n        echo \"name:\", name\n    }\n    if age, ok := opts[\"age\"]; ok {\n        echo \"age:\", age\n    }\n    echo \"args:\", args\n}\n\nprocess name = \"Ken\", age = 17              // keyword parameters only\nprocess \"extra\", 1, name = \"Ken\", age = 17  // variadic parameters first, then keyword parameters\nprocess                                     // all parameters optional\n```\n\n**Key rules:**\n- The keyword parameter must be an optional parameter.\n- The keyword parameter must be the last parameter (without variadic) or second-to-last (with variadic).\n- When calling a function, keyword arguments must be placed after all normal parameters (including variadic parameters). This might seem inconsistent with the order of keyword and variadic parameters in a function declaration, but that's the rule.\n\n\n### Built-in functions\n\nTODO\n\n### Operators\n\nOperators combine operands into expressions.\n\nBinary operators:\n\n```go\n|| && == != < <= > >=\n+ - * / %\n| & ^ &^ << >>\n```\n\nUnary operators:\n\n```go\n+ - ! ^ * &\n```\n\n#### Operator precedence\n\n_Unary operators_ have the highest precedence. As the ++ and -- operators form statements, not expressions, they fall outside the operator hierarchy. As a consequence, statement *p++ is the same as (*p)++.\n\nThere are five precedence levels for _binary operators_. Multiplication operators bind strongest, followed by addition operators, comparison operators, && (logical AND), and finally || (logical OR):\n\n```\nPrecedence    Operator\n    5             *  /  %  <<  >>  &  &^\n    4             +  -  |  ^\n    3             ==  !=  <  <=  >  >=\n    2             &&\n    1             ||\n```\n\nBinary operators of the same precedence associate from left to right. For instance, `x / y * z` is the same as `(x / y) * z`.\n\n```go\n+x                         // x\n42 + a - b                 // (42 + a) - b\n23 + 3*x[i]                // 23 + (3 * x[i])\nx <= f()                   // x <= f()\n^a >> b                    // (^a) >> b\nf() || g()                 // f() || g()\nx == y+1 && <-chanInt > 0  // (x == (y+1)) && ((<-chanInt) > 0)\n```\n\n#### Arithmetic operators\n\n_Arithmetic operators_ apply to numeric values and yield a result of the same type as the first operand. The four standard arithmetic operators (+, -, *, /) apply to [integer](), [floating-point](), [rational]() and [complex]() types; + also applies to [strings](). The bitwise logical and shift operators apply to integers only.\n\n```\n+    sum                    integers (including bigint), floats, bigrat, complex values, strings\n-    difference             integers (including bigint), floats, bigrat, complex values\n*    product                integers (including bigint), floats, bigrat, complex values\n/    quotient               integers (including bigint), floats, bigrat, complex values\n%    remainder              integers (including bigint)\n\n&    bitwise AND            integers (including bigint)\n|    bitwise OR             integers (including bigint)\n^    bitwise XOR            integers (including bigint)\n&^   bit clear (AND NOT)    integers (including bigint)\n\n<<   left shift             integer << integer >= 0\n>>   right shift            integer >> integer >= 0\n```\n\nTODO\n\n#### Comparison operators\n\n_Comparison operators_ compare two operands and yield an untyped boolean value.\n\n```go\n==    equal\n!=    not equal\n<     less\n<=    less or equal\n>     greater\n>=    greater or equal\n```\n\nIn any comparison, the first operand must be [assignable]() to the type of the second operand, or vice versa.\n\nThe equality operators == and != apply to operands of comparable types. The ordering operators <, <=, >, and >= apply to operands of ordered types. These terms and the result of the comparisons are defined as follows:\n\n* Boolean types are comparable. Two boolean values are equal if they are either both true or both false.\n* Integer types are comparable and ordered. Two integer values are compared in the usual way.\n* Floating-point types are comparable and ordered. Two floating-point values are compared as defined by the IEEE-754 standard.\n* Complex types are comparable. Two complex values u and v are equal if both real(u) == real(v) and imag(u) == imag(v).\n* String types are comparable and ordered. Two string values are compared lexically byte-wise.\n* Pointer types are comparable. Two pointer values are equal if they point to the same variable or if both have value `nil`. Pointers to distinct [zero-size]() variables may or may not be equal.\n* Interface types are comparable. Two interface values are equal if they have [identical]() dynamic types and equal dynamic values or if both have value `nil`.\n* A value x of non-interface type X and a value t of interface type T can be compared if type X is comparable and X [implements]() T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x.\n* Array types are comparable if their array element types are comparable. Two array values are equal if their corresponding element values are equal. The elements are compared in ascending index order, and comparison stops as soon as two element values differ (or all elements have been compared).\n\nA comparison of two interface values with identical dynamic types causes a [run-time panic]() if that type is not comparable. This behavior applies not only to direct interface value comparisons but also when comparing arrays of interface values or structs with interface-valued fields.\n\nSlice, map, and function types are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier `nil`. Comparison of pointer, and interface values to `nil` is also allowed and follows from the general rules above.\n\n#### Logical operators\n\nLogical operators apply to [boolean]() values and yield a result of the same type as the operands. The left operand is evaluated, and then the right if the condition requires it.\n\n```\n&&    conditional AND    p && q  is  \"if p then q else false\"\n||    conditional OR     p || q  is  \"if p then true else q\"\n!     NOT                !p      is  \"not p\"\n```\n\n#### Address operators\n\nFor an operand x of type T, the address operation &x generates a pointer of type *T to x. The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x may also be a (possibly parenthesized) [composite literal](). If the evaluation of x would cause a [run-time panic](), then the evaluation of &x does too.\n\nFor an operand x of pointer type *T, the pointer indirection *x denotes the [variable]() of type T pointed to by x. If x is nil, an attempt to evaluate *x will cause a [run-time panic]().\n\n```go\n&x\n&a[f(2)]\n&Point{2, 3}\n*p\n*pf(x)\n\nvar x *int = nil\n*x   // causes a run-time panic\n&*x  // causes a run-time panic\n```\n\n#### Conversions\n\nA _conversion_ changes the [type](#types) of an expression to the type specified by the conversion. A conversion may appear literally in the source, or it may be _implied_ by the context in which an expression appears.\n\nAn _explicit conversion_ is an expression of the form `T(x)` where `T` is a type and `x` is an expression that can be converted to type `T`.\n\n```go\nT(x)\n```\n\nIf the type starts with the operator * or <-, or if the type starts with the keyword func and has no result list, it must be parenthesized when necessary to avoid ambiguity:\n\n```go\n*Point(p)        // same as *(Point(p))\n(*Point)(p)      // p is converted to *Point\nfunc()(x)        // function signature func() x\n(func())(x)      // x is converted to func()\n(func() int)(x)  // x is converted to func() int\nfunc() int(x)    // x is converted to func() int (unambiguous)\n```\n\nA [constant]() value `x` can be converted to type `T` if `x` is [representable]() by a value of `T`. As a special case, an integer constant `x` can be explicitly converted to a [string type]() using the [same rule]() as for non-constant `x`.\n\nConverting a constant to a type yields a typed constant.\n\n```go\nuint(iota)               // iota value of type uint\nfloat32(2.718281828)     // 2.718281828 of type float32\ncomplex128(1)            // 1.0 + 0.0i of type complex128\nfloat32(0.49999999)      // 0.5 of type float32\nfloat64(-1e-1000)        // 0.0 of type float64\nstring('x')              // \"x\" of type string\nstring(0x266c)           // \"♬\" of type string\nmyString(\"foo\" + \"bar\")  // \"foobar\" of type myString\nstring([]byte{'a'})      // not a constant: []byte{'a'} is not a constant\n(*int)(nil)              // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type\nint(1.2)                 // illegal: 1.2 cannot be represented as an int\nstring(65.0)             // illegal: 65.0 is not an integer constant\n```\n\n##### Conversions between numeric types\n\nFor the conversion of non-constant numeric values, the following rules apply:\n\n* When converting between [integer types](#numeric-types), if the value is a signed integer, it is sign extended to implicit infinite precision; otherwise it is zero extended. It is then truncated to fit in the result type's size. For example, if v := uint16(0x10F0), then uint32(int8(v)) == 0xFFFFFFF0. The conversion always yields a valid value; there is no indication of overflow.\n* When converting a [floating-point number](#numeric-types) to an integer, the fraction is discarded (truncation towards zero).\n* When converting an integer or floating-point number to a floating-point type, or a [complex number](#numeric-types) to another complex type, the result value is rounded to the precision specified by the destination type. For instance, the value of a variable x of type float32 may be stored using additional precision beyond that of an IEEE-754 32-bit number, but float32(x) represents the result of rounding x's value to 32-bit precision. Similarly, x + 0.1 may use more than 32 bits of precision, but float32(x + 0.1) does not.\n\nIn all non-constant conversions involving floating-point or complex values, if the result type cannot represent the value the conversion succeeds but the result value is implementation-dependent.\n\n##### Conversions to and from a string type\n\nTODO\n\n##### Conversions from slice to array or array pointer\n\nTODO\n\n### Constant expressions\n\nConstant expressions may contain only [constant](#constants) operands and are evaluated at compile time.\n\nUntyped boolean, numeric, and string constants may be used as operands wherever it is legal to use an operand of boolean, numeric, or string type, respectively.\n\nA constant [comparison](#comparison-operators) always yields an untyped boolean constant. If the left operand of a constant [shift expression](#operators) is an untyped constant, the result is an integer constant; otherwise it is a constant of the same type as the left operand, which must be of [integer type](#numeric-types).\n\nAny other operation on untyped constants results in an untyped constant of the same kind; that is, a boolean, integer, floating-point, complex, or string constant. If the untyped operands of a binary operation (other than a shift) are of different kinds, the result is of the operand's kind that appears later in this list: integer, rune, floating-point, complex. For example, an untyped integer constant divided by an untyped complex constant yields an untyped complex constant.\n\n```go\nconst a = 2 + 3.0          // a == 5.0   (untyped floating-point constant)\nconst b = 15 / 4           // b == 3     (untyped integer constant)\nconst c = 15 / 4.0         // c == 3.75  (untyped floating-point constant)\nconst Θ float64 = 3/2      // Θ == 1.0   (type float64, 3/2 is integer division)\nconst Π float64 = 3/2.     // Π == 1.5   (type float64, 3/2. is float division)\nconst d = 1 << 3.0         // d == 8     (untyped integer constant)\nconst e = 1.0 << 3         // e == 8     (untyped integer constant)\nconst f = int32(1) << 33   // illegal    (constant 8589934592 overflows int32)\nconst g = float64(2) >> 1  // illegal    (float64(2) is a typed floating-point constant)\nconst h = \"foo\" > \"bar\"    // h == true  (untyped boolean constant)\nconst j = true             // j == true  (untyped boolean constant)\nconst k = 'w' + 1          // k == 'x'   (untyped rune constant)\nconst l = \"hi\"             // l == \"hi\"  (untyped string constant)\nconst m = string(k)        // m == \"x\"   (type string)\nconst Σ = 1 - 0.707i       //            (untyped complex constant)\nconst Δ = Σ + 2.0e-4       //            (untyped complex constant)\nconst Φ = iota*1i - 1/1i   //            (untyped complex constant)\n```\n\nApplying the built-in function `complex` to untyped integer, rune, or floating-point constants yields an untyped complex constant.\n\n```go\nconst ic = complex(0, c)   // ic == 3.75i  (untyped complex constant)\nconst iΘ = complex(0, Θ)   // iΘ == 1i     (type complex128)\n```\n\nConstant expressions are always evaluated exactly; intermediate values and the constants themselves may require precision significantly larger than supported by any predeclared type in the language. The following are legal declarations:\n\n```go\nconst Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)\nconst Four int8 = Huge >> 98  // Four == 4                                (type int8)\n```\n\nThe divisor of a constant division or remainder operation must not be zero:\n\n```go\n3.14 / 0.0   // illegal: division by zero\n```\n\nThe values of typed constants must always be accurately [representable]() by values of the constant type. The following constant expressions are illegal:\n\n```go\nuint(-1)     // -1 cannot be represented as a uint\nint(3.14)    // 3.14 cannot be represented as an int\nint64(Huge)  // 1267650600228229401496703205376 cannot be represented as an int64\nFour * 300   // operand 300 cannot be represented as an int8 (type of Four)\nFour * 100   // product 400 cannot be represented as an int8 (type of Four)\n```\n\nThe mask used by the unary bitwise complement operator ^ matches the rule for non-constants: the mask is all 1s for unsigned constants and -1 for signed and untyped constants.\n\n```go\n^1         // untyped integer constant, equal to -2\nuint8(^1)  // illegal: same as uint8(-2), -2 cannot be represented as a uint8\n^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)\nint8(^1)   // same as int8(-2)\n^int8(1)   // same as -1 ^ int8(1) = -2\n```\n\n### Short variable declarations\n\nA short variable declaration uses the syntax:\n\n```go\nShortVarDecl = IdentifierList \":=\" ExpressionList .\n```\n\nIt is shorthand for a regular [variable declaration](#variable-declarations) with initializer expressions but no types:\n\n```go\n\"var\" IdentifierList \"=\" ExpressionList .\n```\n\n```go\ni, j := 0, 10\nf := func() int { return 7 }\nints := make([]int)\nr, w, _ := os.Pipe()  // os.Pipe() returns a connected pair of Files and an error, if any\n_, y, _ := coord(p)   // coord() returns three values; only interested in y coordinate\n```\n\nUnlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-[blank]() variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original. The non-blank variable names on the left side of := must be [unique]().\n\n```go\nfield1, offset := nextField(str, 0)\nfield2, offset := nextField(str, offset)  // redeclares offset\nx, y, x := 1, 2, 3                        // illegal: x repeated on left side of :=\n```\n\nShort variable declarations may appear only inside functions. In some contexts such as the initializers for \"[if]()\", \"[for]()\", or \"[switch]()\" statements, they can be used to declare local temporary variables.\n\n\n### Slice literals\n\nTODO\n\n```go\n[expression1, ...]\n```\n\nFor example:\n\n```go\n[]                   // []any\n[1, 2, 3]            // []int\n[10, 3.14, 200]      // []float64\n[\"Hello\", \"world\"]   // []string\n[\"Hello\", 100, true] // []any\n```\n\nThe type of slice literals can be inferred from the context:\n\n```go\nfunc echoF32s(vals []float32) {\n\techo vals\n}\n\necho [10, 3.14, 200]           // []float64\nechoF32s [10, 3.14, 200]       // []float32\n\nvar a []any = [10, 3.14, 200]  // []any\necho a\n```\n\n### Map literals\n\nTODO\n\n```go\n{key1: value1, ...}\n```\n\nFor example:\n\n```go\n{}                           // map[string]any\n{\"Monday\": 1, \"Sunday\": 7}   // map[string]int\n{1: 100, 3: 3.14, 5: 10}     // map[int]float64\n```\n\nThe type of map literals can be inferred from the context:\n\n```go\nfunc echoS2f32(vals map[string]float32) {\n\techo vals\n}\n\necho {\"Monday\": 1, \"Sunday\": 7}\nechoS2f32 {\"Monday\": 1, \"Sunday\": 7}\n\nvar a map[string]any = {\"Monday\": 1, \"Sunday\": 7}\necho a\n```\n\n### Order of evaluation\n\nAt package level, [initialization dependencies]() determine the evaluation order of individual initialization expressions in [variable declarations](). Otherwise, when evaluating the [operands]() of an expression, assignment, or [return statement](#return-statements), all function calls, method calls, [receive operations](), and [binary logical operations](#logical-operators) are evaluated in lexical left-to-right order.\n\nFor example, in the (function-local) assignment\n\n```go\ny[f()], ok = g(z || h(), i()+x[j()], <-c), k()\n```\n\nthe function calls and communication happen in the order `f()`, `h()` (if z evaluates to false), `i()`, `j()`, `<-c`, `g()`, and `k()`. However, the order of those events compared to the evaluation and indexing of `x` and the evaluation of `y` and `z` is not specified, except as required lexically. For instance, `g` cannot be called before its arguments are evaluated.\n\n```go\na := 1\nf := func() int { a++; return a }\nx := [a, f()]      // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified\nm := {a: 1, a: 2}  // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified\nn := {a: f()}      // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified\n```\n\nAt package level, initialization dependencies override the left-to-right rule for individual initialization expressions, but not for operands within each expression:\n\n```go\nvar a, b, c = f() + v(), g(), sqr(u()) + v()\n\nfunc f() int        { return c }\nfunc g() int        { return a }\nfunc sqr(x int) int { return x*x }\n\n// functions u and v are independent of all other variables and functions\n```\n\nThe function calls happen in the order `u()`, `sqr()`, `v()`, `f()`, `v()`, and `g()`.\n\nFloating-point operations within a single expression are evaluated according to the associativity of the operators. Explicit parentheses affect the evaluation by overriding the default associativity. In the expression `x + (y + z)` the addition `y + z` is performed before adding `x`.\n\n\n## Statements\n\nStatements control execution.\n\n```go\nStatement =\n\tDeclaration | SimpleStmt | IfStmt | ForStmt | SwitchStmt |\n    LabeledStmt | BreakStmt | ContinueStmt | FallthroughStmt | GotoStmt |\n\tReturnStmt | DeferStmt | Block .\n\nSimpleStmt = EmptyStmt | ExpressionStmt | IncDecStmt | Assignment | ShortVarDecl .\n```\n\n### Empty statements\n\nThe empty statement does nothing.\n\n```go\nEmptyStmt = .\n```\n\n### Expression statements\n\nWith the exception of specific built-in functions, function and method [calls](#commands-and-calls) and [receive operations]() can appear in statement context. Such statements may be parenthesized.\n\n```go\nExpressionStmt = Expression .\n```\n\nThe following built-in functions are not permitted in statement context:\n\n```go\nappend cap complex imag len make new real\nunsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice unsafe.SliceData unsafe.String unsafe.StringData\n```\n\n```go\nh(x+y)\nf.Close()\n<-ch\n(<-ch)\nlen(\"foo\")  // illegal if len is the built-in function\n```\n\n### IncDec statements\n\nThe \"++\" and \"--\" statements increment or decrement their operands by the untyped constant 1. As with an assignment, the operand must be addressable or a map index expression.\n\n```go\nIncDecStmt = Expression ( \"++\" | \"--\" ) .\n```\n\nThe following [assignment statements]() are semantically equivalent:\n\n```go\nIncDec statement    Assignment\nx++                 x += 1\nx--                 x -= 1\n```\n\n### Assignment statements\n\nAn assignment replaces the current value stored in a [variable](#variables) with a new value specified by an [expression](#expressions). An assignment statement may assign a single value to a single variable, or multiple values to a matching number of variables.\n\n```go\nAssignment = ExpressionList assign_op ExpressionList .\nExpressionList = Expression { \",\" Expression } .\n```\n\nHere `assign_op` can be:\n\n```go\n= += -= |= ^= *= /= %= <<= >>= &= &^=\n```\n\nEach left-hand side operand must be [addressable](#address-operators), a map index expression, or (for = assignments only) the [blank identifier](). Operands may be parenthesized.\n\n```go\nx = 1\n*p = f()\na[i] = 23\n(k) = <-ch  // same as: k = <-ch\n```\n\nAn _assignment operation_ x _op=_ y where op is a binary [arithmetic operator](#arithmetic-operators) is equivalent to x = x op (y) but evaluates x only once. The op= construct is a single token. In assignment operations, both the left- and right-hand expression lists must contain exactly one single-valued expression, and the left-hand expression must not be the blank identifier.\n\n```go\na[i] <<= 2\ni &^= 1<<n\n```\n\nA tuple assignment assigns the individual elements of a multi-valued operation to a list of variables. There are two forms. In the first, the right hand operand is a single multi-valued expression such as a function call or [map](#map-types) operation, or a [type assertion](). The number of operands on the left hand side must match the number of values. For instance, if f is a function returning two values,\n\n```go\nx, y = f()\n```\n\nassigns the first value to x and the second to y. In the second form, the number of operands on the left must equal the number of expressions on the right, each of which must be single-valued, and the nth expression on the right is assigned to the nth operand on the left:\n\n```go\none, two, three = '一', '二', '三'\n```\n\nThe [blank identifier]() provides a way to ignore right-hand side values in an assignment:\n\n```go\n_ = x       // evaluate x but ignore it\nx, _ = f()  // evaluate f() but ignore second result value\n```\n\nThe assignment proceeds in two phases. First, the operands of [index expressions]() and [pointer indirections]() (including implicit pointer indirections in selectors) on the left and the expressions on the right are all [evaluated in the usual order](). Second, the assignments are carried out in left-to-right order.\n\n```go\na, b = b, a  // exchange a and b\n\nx := [1, 2, 3]\ni := 0\ni, x[i] = 1, 2  // set i = 1, x[0] = 2\n\ni = 0\nx[i], i = 2, 1  // set x[0] = 2, i = 1\n\nx[0], x[0] = 1, 2  // set x[0] = 1, then x[0] = 2 (so x[0] == 2 at end)\n\nx[1], x[3] = 4, 5  // set x[1] = 4, then panic setting x[3] = 5.\n\ni = 2\nx = [3, 5, 7]\nfor i, x[i] <- x {  // set i, x[2] = 0, x[0]\n\tbreak\n}\n// after this loop, i == 0 and x is [3, 5, 3]\n```\n\nIn assignments, each value must be [assignable]() to the type of the operand to which it is assigned, with the following special cases:\n\n* Any typed value may be assigned to the blank identifier.\n* If an untyped constant is assigned to a variable of interface type or the blank identifier, the constant is first implicitly [converted](#conversions) to its [default type](#constants).\n* If an untyped boolean value is assigned to a variable of interface type or the blank identifier, it is first implicitly converted to type bool.\n\n\n### If statements\n\n\"If\" statements specify the conditional execution of two branches according to the value of a boolean expression. If the expression evaluates to true, the \"if\" branch is executed, otherwise, if present, the \"else\" branch is executed.\n\n```go\nIfStmt = \"if\" [ SimpleStmt \";\" ] Expression Block [ \"else\" ( IfStmt | Block ) ] .\n```\n\n```go\nif x > 1 {\n\tx = 1\n}\n```\n\nThe expression may be preceded by a simple statement, which executes before the expression is evaluated.\n\n```go\nif x := f(); x < y {\n\treturn x\n} else if x > z {\n\treturn z\n} else {\n\treturn y\n}\n```\n\n### For statements\n\nA \"for\" statement specifies repeated execution of a block. There are three forms: The iteration may be controlled by a single condition, a \"for\" clause, or a \"range\" clause.\n\n```go\nForStmt = \"for\" [ Condition | ForClause | RangeClause ] Block .\nCondition = Expression .\n```\n\n#### For statements with single condition\n\nIn its simplest form, a \"for\" statement specifies the repeated execution of a block as long as a boolean condition evaluates to true. The condition is evaluated before each iteration. If the condition is absent, it is equivalent to the boolean value true.\n\n```go\nfor a < b {\n\ta *= 2\n}\n```\n\n#### For statements with for clause\n\nA \"for\" statement with a ForClause is also controlled by its condition, but additionally it may specify an init and a post statement, such as an assignment, an increment or decrement statement. The init statement may be a [short variable declaration](#short-variable-declarations), but the post statement must not.\n\n```go\nForClause = [ InitStmt ] \";\" [ Condition ] \";\" [ PostStmt ] .\nInitStmt = SimpleStmt .\nPostStmt = SimpleStmt .\n```\n\n```go\nfor i := 0; i < 10; i++ {\n\tf(i)\n}\n```\n\nIf non-empty, the init statement is executed once before evaluating the condition for the first iteration; the post statement is executed after each execution of the block (and only if the block was executed). Any element of the ForClause may be empty but the [semicolons]() are required unless there is only a condition. If the condition is absent, it is equivalent to the boolean value true.\n\n```go\nfor cond { S() }    is the same as    for ; cond ; { S() }\nfor      { S() }    is the same as    for true     { S() }\n```\n\nEach iteration has its own separate declared variable (or variables) [Go 1.22](). The variable used by the first iteration is declared by the init statement. The variable used by each subsequent iteration is declared implicitly before executing the post statement and initialized to the value of the previous iteration's variable at that moment.\n\n```go\nvar prints []func()\nfor i := 0; i < 5; i++ {\n\tprints = append(prints, func() { println(i) })\n\ti++\n}\nfor _, p := range prints {\n\tp()\n}\n```\n\nprints\n\n```\n1\n3\n5\n```\n\nPrior to [Go 1.22], iterations share one set of variables instead of having their own separate variables. In that case, the example above prints\n\n```\n6\n6\n6\n```\n\n#### For statements with range clause\n\nTODO\n\n### Switch statements\n\n\"Switch\" statements provide multi-way execution. An expression or type is compared to the \"cases\" inside the \"switch\" to determine which branch to execute.\n\n```go\nSwitchStmt = ExprSwitchStmt | TypeSwitchStmt .\n```\n\nThere are two forms: expression switches and type switches. In an expression switch, the cases contain expressions that are compared against the value of the switch expression. In a type switch, the cases contain types that are compared against the type of a specially annotated switch expression. The switch expression is evaluated exactly once in a switch statement.\n\n#### Expression switches\n\nIn an expression switch, the switch expression is evaluated and the case expressions, which need not be constants, are evaluated left-to-right and top-to-bottom; the first one that equals the switch expression triggers execution of the statements of the associated case; the other cases are skipped. If no case matches and there is a \"default\" case, its statements are executed. There can be at most one default case and it may appear anywhere in the \"switch\" statement. A missing switch expression is equivalent to the boolean value true.\n\n```go\nExprSwitchStmt = \"switch\" [ SimpleStmt \";\" ] [ Expression ] \"{\" { ExprCaseClause } \"}\" .\nExprCaseClause = ExprSwitchCase \":\" StatementList .\nExprSwitchCase = \"case\" ExpressionList | \"default\" .\n```\n\nIf the switch expression evaluates to an untyped constant, it is first implicitly [converted](#conversions) to its [default type](#constants). The predeclared untyped value nil cannot be used as a switch expression. The switch expression type must be [comparable](#comparison-operators).\n\nIf a case expression is untyped, it is first implicitly [converted](#conversions) to the type of the switch expression. For each (possibly converted) case expression x and the value t of the switch expression, x == t must be a valid [comparison](#comparison-operators).\n\nIn other words, the switch expression is treated as if it were used to declare and initialize a temporary variable t without explicit type; it is that value of t against which each case expression x is tested for equality.\n\nIn a case or default clause, the last non-empty statement may be a (possibly [labeled](#labeled-statements)) [\"fallthrough\" statement]() to indicate that control should flow from the end of this clause to the first statement of the next clause. Otherwise control flows to the end of the \"switch\" statement. A \"fallthrough\" statement may appear as the last statement of all but the last clause of an expression switch.\n\nThe switch expression may be preceded by a simple statement, which executes before the expression is evaluated.\n\n```go\nswitch tag {\ndefault: s3()\ncase 0, 1, 2, 3: s1()\ncase 4, 5, 6, 7: s2()\n}\n\nswitch x := f(); {  // missing switch expression means \"true\"\ncase x < 0: return -x\ndefault: return x\n}\n\nswitch {\ncase x < y: f1()\ncase x < z: f2()\ncase x == 4: f3()\n}\n```\n\nImplementation restriction: A compiler may disallow multiple case expressions evaluating to the same constant. For instance, the current compilers disallow duplicate integer, floating point, or string constants in case expressions.\n\n#### Type switches\n\nA type switch compares types rather than values. It is otherwise similar to an expression switch. It is marked by a special switch expression that has the form of a [type assertion]() using the keyword type rather than an actual type:\n\n```go\nswitch x.(type) {\n// cases\n}\n```\n\nCases then match actual types T against the dynamic type of the expression x. As with type assertions, x must be of interface type, but not a type parameter, and each non-interface type T listed in a case must implement the type of x. The types listed in the cases of a type switch must all be different.\n\n```go\nTypeSwitchStmt  = \"switch\" [ SimpleStmt \";\" ] TypeSwitchGuard \"{\" { TypeCaseClause } \"}\" .\nTypeSwitchGuard = [ identifier \":=\" ] PrimaryExpr \".\" \"(\" \"type\" \")\" .\nTypeCaseClause  = TypeSwitchCase \":\" StatementList .\nTypeSwitchCase  = \"case\" TypeList | \"default\" .\n```\n\nThe TypeSwitchGuard may include a [short variable declaration](#short-variable-declarations). When that form is used, the variable is declared at the end of the TypeSwitchCase in the [implicit block]() of each clause. In clauses with a case listing exactly one type, the variable has that type; otherwise, the variable has the type of the expression in the TypeSwitchGuard.\n\nInstead of a type, a case may use the predeclared identifier [nil](); that case is selected when the expression in the TypeSwitchGuard is a `nil` interface value. There may be at most one `nil` case.\n\nGiven an expression x of type `any`, the following type switch:\n\n```\nswitch i := x.(type) {\ncase nil:\n\tprintString(\"x is nil\")                // type of i is type of x (any)\ncase int:\n\tprintInt(i)                            // type of i is int\ncase float64:\n\tprintFloat64(i)                        // type of i is float64\ncase func(int) float64:\n\tprintFunction(i)                       // type of i is func(int) float64\ncase bool, string:\n\tprintString(\"type is bool or string\")  // type of i is type of x (any)\ndefault:\n\tprintString(\"don't know the type\")     // type of i is type of x (any)\n}\n```\n\ncould be rewritten:\n\n```go\nv := x  // x is evaluated exactly once\nif v == nil {\n\ti := v                                 // type of i is type of x (any)\n\tprintString(\"x is nil\")\n} else if i, isInt := v.(int); isInt {\n\tprintInt(i)                            // type of i is int\n} else if i, isFloat64 := v.(float64); isFloat64 {\n\tprintFloat64(i)                        // type of i is float64\n} else if i, isFunc := v.(func(int) float64); isFunc {\n\tprintFunction(i)                       // type of i is func(int) float64\n} else {\n\t_, isBool := v.(bool)\n\t_, isString := v.(string)\n\tif isBool || isString {\n\t\ti := v                         // type of i is type of x (any)\n\t\tprintString(\"type is bool or string\")\n\t} else {\n\t\ti := v                         // type of i is type of x (any)\n\t\tprintString(\"don't know the type\")\n\t}\n}\n```\n\nThe type switch guard may be preceded by a simple statement, which executes before the guard is evaluated.\n\nThe \"fallthrough\" statement is not permitted in a type switch.\n\n\n### Labeled statements\n\nA labeled statement may be the target of a goto, break or continue statement.\n\n```go\nLabeledStmt = Label \":\" Statement .\nLabel       = identifier .\n```\n\n```go\nError:\n\tlog.Panic(\"error encountered\")\n```\n\n### Break statements\n\nA \"break\" statement terminates execution of the innermost \"[for](#for-statements)\" or \"[switch](#switch-statements)\" statement within the same function.\n\n```go\nBreakStmt = \"break\" [ Label ] .\n```\n\nIf there is a label, it must be that of an enclosing \"for\" or \"switch\" statement, and that is the one whose execution terminates.\n\n```go\nOuterLoop:\n\tfor i = 0; i < n; i++ {\n\t\tfor j = 0; j < m; j++ {\n\t\t\tswitch a[i][j] {\n\t\t\tcase nil:\n\t\t\t\tstate = Error\n\t\t\t\tbreak OuterLoop\n\t\t\tcase item:\n\t\t\t\tstate = Found\n\t\t\t\tbreak OuterLoop\n\t\t\t}\n\t\t}\n\t}\n```\n\n### Continue statements\n\nA \"continue\" statement begins the next iteration of the innermost enclosing \"[for](#for-statements)\" loop by advancing control to the end of the loop block. The \"for\" loop must be within the same function.\n\n```go\nContinueStmt = \"continue\" [ Label ] .\n```\n\nIf there is a label, it must be that of an enclosing \"for\" statement, and that is the one whose execution advances.\n\n```go\nRowLoop:\n\tfor y, row := range rows {\n\t\tfor x, data := range row {\n\t\t\tif data == endOfRow {\n\t\t\t\tcontinue RowLoop\n\t\t\t}\n\t\t\trow[x] = data + bias(x, y)\n\t\t}\n\t}\n```\n\n### Fallthrough statements\n\nA \"fallthrough\" statement transfers control to the first statement of the next case clause in an expression \"[switch](#switch-statements)\" statement. It may be used only as the final non-empty statement in such a clause.\n\n```go\nFallthroughStmt = \"fallthrough\" .\n```\n\n### Goto statements\n\nA \"goto\" statement transfers control to the statement with the corresponding label within the same function.\n\n```go\nGotoStmt = \"goto\" Label .\n```\n\n```go\ngoto Error\n```\n\nExecuting the \"goto\" statement must not cause any variables to come into [scope]() that were not already in scope at the point of the goto. For instance, this example:\n\n```go\n\tgoto L  // BAD\n\tv := 3\nL:\n```\n\nis erroneous because the jump to label L skips the creation of v.\n\nA \"goto\" statement outside a [block]() cannot jump to a label inside that block. For instance, this example:\n\n```go\nif n%2 == 1 {\n\tgoto L1  // BAD\n}\nfor n > 0 {\n\tf()\n\tn--\nL1:\n\tf()\n\tn--\n}\n```\n\nis erroneous because the label L1 is inside the \"for\" statement's block but the goto is not.\n\n### Return statements\n\nA \"return\" statement in a function F terminates the execution of F, and optionally provides one or more result values. Any functions [deferred](#defer-statements) by F are executed before F returns to its caller.\n\n```go\nReturnStmt = \"return\" [ ExpressionList ] .\n```\n\nIn a function without a result type, a \"return\" statement must not specify any result values.\n\n```go\nfunc noResult() {\n\treturn\n}\n```\n\nThere are three ways to return values from a function with a result type:\n\n* The return value or values may be explicitly listed in the \"return\" statement. Each expression must be single-valued and [assignable]() to the corresponding element of the function's result type.\n\n```go\nfunc simpleF() int {\n\treturn 2\n}\n\nfunc complexF1() (re float64, im float64) {\n\treturn -7.0, -4.0\n}\n```\n\n* The expression list in the \"return\" statement may be a single call to a multi-valued function. The effect is as if each value returned from that function were assigned to a temporary variable with the type of the respective value, followed by a \"return\" statement listing these variables, at which point the rules of the previous case apply.\n\n```go\nfunc complexF2() (re float64, im float64) {\n\treturn complexF1()\n}\n```\n\n* The expression list may be empty if the function's result type specifies names for its [result parameters](). The result parameters act as ordinary local variables and the function may assign values to them as necessary. The \"return\" statement returns the values of these variables.\n\n```go\nfunc complexF3() (re float64, im float64) {\n\tre = 7.0\n\tim = 4.0\n\treturn\n}\n```\n\nRegardless of how they are declared, all the result values are initialized to the [zero values]() for their type upon entry to the function. A \"return\" statement that specifies results sets the result parameters before any deferred functions are executed.\n\nImplementation restriction: A compiler may disallow an empty expression list in a \"return\" statement if a different entity (constant, type, or variable) with the same name as a result parameter is in [scope]() at the place of the return.\n\n```go\nfunc f(n int) (res int, err error) {\n\tif _, err := f(n-1); err != nil {\n\t\treturn  // invalid return statement: err is shadowed\n\t}\n\treturn\n}\n```\n\n### Defer statements\n\nA \"defer\" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a [return statement](#return-statements), reached the end of its [function body](), or because the corresponding goroutine is [panicking]().\n\n```go\nDeferStmt = \"defer\" Expression .\n```\n\nThe expression must be a function or method call; it cannot be parenthesized. Calls of built-in functions are restricted as for [expression statements](#expression-statements).\n\nEach time a \"defer\" statement executes, the function value and parameters to the call are [evaluated as usual]() and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. That is, if the surrounding function returns through an explicit [return statement](#return-statements), deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller. If a deferred function value evaluates to nil, execution [panics]() when the function is invoked, not when the \"defer\" statement is executed.\n\nFor instance, if the deferred function is a [function literal]() and the surrounding function has [named result parameters]() that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned. If the deferred function has any return values, they are discarded when the function completes. (See also the section on [handling panics]().)\n\n```go\nlock(l)\ndefer unlock(l)  // unlocking happens before surrounding function returns\n\n// f returns 42\nfunc f() (result int) {\n\tdefer func() {\n\t\t// result is accessed after it was set to 6 by the return statement\n\t\tresult *= 7\n\t}()\n\treturn 6\n}\n```\n\n### Terminating statements\n\nTODO\n\n\n## Built-in functions\n\nBuilt-in functions are [predeclared](). They are called like any other function but some of them accept a type instead of an expression as the first argument.\n\nThe built-in functions do not have standard Go types, so they can only appear in [call expressions](#commands-and-calls); they cannot be used as function values.\n\n### Appending to and copying slices\n\nThe built-in functions `append` and `copy` assist in common slice operations. For both functions, the result is independent of whether the memory referenced by the arguments overlaps.\n\nThe [variadic](#function-types) function append appends zero or more values x to a slice s and returns the resulting slice of the same type as s. The [underlying type](#underlying-types) of s must be a slice of type `[]E`. The values x are passed to a parameter of type `...E` and the respective [parameter passing rules]() apply. As a special case, if the underlying type of s is []byte, append also accepts a second argument with underlying type [bytestring]() followed by `...`. This form appends the bytes of the byte slice or string.\n\n```go\nappend(s S, x ...E) S  // underlying type of S is []E\n```\n\nIf the capacity of s is not large enough to fit the additional values, append [allocates]() a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.\n\n```go\ns0 := [0, 0]\ns1 := append(s0, 2)                // append a single element     s1 is [0, 0, 2]\ns2 := append(s1, 3, 5, 7)          // append multiple elements    s2 is [0, 0, 2, 3, 5, 7]\ns3 := append(s2, s0...)            // append a slice              s3 is [0, 0, 2, 3, 5, 7, 0, 0]\ns4 := append(s3[3:6], s3[2:]...)   // append overlapping slice    s4 is [3, 5, 7, 2, 3, 5, 7, 0, 0]\n\nvar t []any\nt = append(t, 42, 3.1415, \"foo\")   //                             t is [42, 3.1415, \"foo\"]\n\nvar b []byte\nb = append(b, \"bar\"...)            // append string contents      b is []byte(\"bar\")\n```\n\nThe function copy copies slice elements from a source src to a destination dst and returns the number of elements copied. The [underlying types](#underlying-types) of both arguments must be slices with [identical]() element type. The number of elements copied is the minimum of `len(src)` and `len(dst)`. As a special case, if the destination's underlying type is `[]byte`, copy also accepts a source argument with underlying type [bytestring](). This form copies the bytes from the byte slice or string into the byte slice.\n\n```go\ncopy(dst, src []T) int\ncopy(dst []byte, src string) int\n```\n\nExamples:\n\n```go\na := [0, 1, 2, 3, 4, 5, 6, 7]\ns := make([]int, 6)\nb := make([]byte, 5)\nn1 := copy(s, a)                // n1 == 6, s is []int{0, 1, 2, 3, 4, 5}\nn2 := copy(s, s[2:])            // n2 == 4, s is []int{2, 3, 4, 5, 4, 5}\nn3 := copy(b, \"Hello, World!\")  // n3 == 5, b is []byte(\"Hello\")\n```\n\n### Clear\n\nThe built-in function `clear` takes an argument of `map` or `slice` and deletes or zeroes out all elements.\n\n```\nCall        Argument type     Result\n\nclear(m)    map[K]T           deletes all entries, resulting in an\n                              empty map (len(m) == 0)\n\nclear(s)    []T               sets all elements up to the length of\n                              s to the zero value of T\n```\n\nIf the map or slice is `nil`, `clear` is a no-op.\n\n### Manipulating complex numbers\n\nThree functions assemble and disassemble complex numbers. The built-in function complex constructs a complex value from a floating-point real and imaginary part, while real and imag extract the real and imaginary parts of a complex value.\n\n```go\ncomplex(realPart, imaginaryPart floatT) complexT\nreal(complexT) floatT\nimag(complexT) floatT\n```\n\nThe type of the arguments and return value correspond. For `complex`, the two arguments must be of the same [floating-point type](#numeric-types) and the return type is the [complex type](#numeric-types) with the corresponding floating-point constituents: `complex64` for `float32` arguments, and `complex128` for `float64` arguments. If one of the arguments evaluates to an untyped constant, it is first implicitly [converted](#conversions) to the type of the other argument. If both arguments evaluate to untyped constants, they must be non-complex numbers or their imaginary parts must be zero, and the return value of the function is an untyped complex constant.\n\nFor `real` and `imag`, the argument must be of complex type, and the return type is the corresponding floating-point type: `float32` for a `complex64` argument, and float64 for a complex128 argument. If the argument evaluates to an untyped constant, it must be a number, and the return value of the function is an untyped floating-point constant.\n\nThe `real` and `imag` functions together form the inverse of `complex`, so for a value `z` of a complex type `Z`, `z == Z(complex(real(z), imag(z)))`.\n\nIf the operands of these functions are all constants, the return value is a constant.\n\n```go\nvar a = complex(2, -2)             // complex128\nconst b = complex(1.0, -1.4)       // untyped complex constant 1 - 1.4i\nx := float32(math.Cos(math.Pi/2))  // float32\nvar c64 = complex(5, -x)           // complex64\nvar s int = complex(1, 0)          // untyped complex constant 1 + 0i can be converted to int\n_ = complex(1, 2<<s)               // illegal: 2 assumes floating-point type, cannot shift\nvar rl = real(c64)                 // float32\nvar im = imag(a)                   // float64\nconst c = imag(b)                  // untyped constant -1.4\n_ = imag(3 << s)                   // illegal: 3 assumes complex type, cannot shift\n```\n\n### Deletion of map elements\n\nThe built-in function `delete` removes the element with key `k` from a [map](#map-types) `m`. The value `k` must be [assignable]() to the key type of `m`.\n\n```go\ndelete(m, k)  // remove element m[k] from map m\n```\n\nIf the map m is nil or the element m[k] does not exist, delete is a no-op.\n\n### Length and capacity\n\nThe built-in functions `len` and `cap` take arguments of various types and return a result of type `int`. The implementation guarantees that the result always fits into an `int`.\n\n```go\nCall      Argument type    Result\n\nlen(s)    string type      string length in bytes\n          [n]T, *[n]T      array length (== n)\n          []T              slice length\n          map[K]T          map length (number of defined keys)\n          type parameter   see below\n\ncap(s)    [n]T, *[n]T      array length (== n)\n          []T              slice capacity\n          type parameter   see below\n```\n\nThe capacity of a slice is the number of elements for which there is space allocated in the underlying array. At any time the following relationship holds:\n\n```go\n0 <= len(s) <= cap(s)\n```\n\nThe length of a `nil` slice, map is `0`. The capacity of a `nil` slice is `0`.\n\nThe expression `len(s)` is [constant](#constants) if s is a string constant. The expressions `len(s)` and `cap(s)` are constants if the type of `s` is an array or pointer to an array and the expression `s` does not contain (non-constant) [function calls](#commands-and-calls); in this case `s` is not evaluated. Otherwise, invocations of `len` and `cap` are not constant and `s` is evaluated.\n\n```go\nconst (\n\tc1 = imag(2i)                    // imag(2i) = 2.0 is a constant\n\tc2 = len([10]float64{2})         // [10]float64{2} contains no function calls\n\tc3 = len([10]float64{c1})        // [10]float64{c1} contains no function calls\n\tc4 = len([10]float64{imag(2i)})  // imag(2i) is a constant and no function call is issued\n\tc5 = len([10]float64{imag(z)})   // invalid: imag(z) is a (non-constant) function call\n)\nvar z complex128\n```\n\nMaking slices and maps\n\nThe built-in function `make` takes a type `T`, optionally followed by a type-specific list of expressions. The [underlying type]() of `T` must be a slice or map. It returns a value of type `T` (not `*T`). The memory is initialized as described in the section on [initial values]().\n\n```go\nCall             Underlying type    Result\n\nmake(T, n)       slice        slice of type T with length n and capacity n\nmake(T, n, m)    slice        slice of type T with length n and capacity m\n\nmake(T)          map          map of type T\nmake(T, n)       map          map of type T with initial space for approximately n elements\n```\n\nEach of the size arguments `n` and `m` must be of [integer type](#numeric-types), have a [type set](#interface-types) containing only integer types, or be an untyped constant. A constant size argument must be non-negative and [representable]() by a value of type `int`; if it is an untyped constant it is given type `int`. If both `n` and `m` are provided and are constant, then `n` must be no larger than `m`. For slices, if `n` is negative or larger than `m` at run time, a [run-time panic]() occurs.\n\n```go\ns := make([]int, 10, 100)       // slice with len(s) == 10, cap(s) == 100\ns := make([]int, 1e3)           // slice with len(s) == cap(s) == 1000\ns := make([]int, 1<<63)         // illegal: len(s) is not representable by a value of type int\ns := make([]int, 10, 0)         // illegal: len(s) > cap(s)\nm := make(map[string]int, 100)  // map with initial space for approximately 100 elements\n```\n\nCalling make with a map type and size hint `n` will create a map with initial space to hold `n` map elements. The precise behavior is implementation-dependent.\n\n\n### Allocation\n\nThe built-in function `new` takes a type `T`, allocates storage for a [variable](#variables) of that type at run time, and returns a value of type `*T` [pointing](#pointer-types) to it. The variable is initialized as described in the section on [initial values]().\n\n```go\nnew(T)\n```\n\nFor instance\n\n```go\nnew(int)\n```\n\nallocates storage for a variable of type `int`, initializes it `0`, and returns a value of type `*int` containing the address of the location.\n\n\n### Min and max\n\nThe built-in functions `min` and `max` compute the smallest—or largest, respectively—value of a fixed number of arguments of [ordered types](). There must be at least one argument.\n\nThe same type rules as for [operators](#operators) apply: for [ordered]() arguments `x` and `y`, `min(x, y)` is valid if `x + y` is valid, and the type of `min(x, y)` is the type of `x + y` (and similarly for `max`). If all arguments are constant, the result is constant.\n\n```go\nvar x, y int\nm := min(x)                 // m == x\nm := min(x, y)              // m is the smaller of x and y\nm := max(x, y, 10)          // m is the larger of x and y but at least 10\nc := max(1, 2.0, 10)        // c == 10.0 (floating-point kind)\nf := max(0, float32(x))     // type of f is float32\nvar s []string\n_ = min(s...)               // invalid: slice arguments are not permitted\nt := max(\"\", \"foo\", \"bar\")  // t == \"foo\" (string kind)\n```\n\nFor numeric arguments, assuming all `NaN`s are equal, `min` and `max` are commutative and associative:\n\n```\nmin(x, y)    == min(y, x)\nmin(x, y, z) == min(min(x, y), z) == min(x, min(y, z))\n```\n\nFor floating-point arguments negative zero, `NaN`, and infinity the following rules apply:\n\n```go\nx        y    min(x, y)    max(x, y)\n\n-0.0    0.0         -0.0          0.0    // negative zero is smaller than (non-negative) zero\n-Inf      y         -Inf            y    // negative infinity is smaller than any other number\n+Inf      y            y         +Inf    // positive infinity is larger than any other number\n NaN      y          NaN          NaN    // if any argument is a NaN, the result is a NaN\n```\n\nFor string arguments the result for min is the first argument with the smallest (or for max, largest) value, compared lexically byte-wise:\n\n```go\nmin(x, y)    == if x <= y then x else y\nmin(x, y, z) == min(min(x, y), z)\n```\n\n### Handling panics\n\nTwo built-in functions, `panic` and `recover`, assist in reporting and handling [run-time panics]() and program-defined error conditions.\n\n```go\nfunc panic(any)\nfunc recover() any\n```\n\nWhile executing a function `F`, an explicit call to `panic` or a [run-time panic]() terminates the execution of `F`. Any functions [deferred]() by `F` are then executed as usual. Next, any deferred functions run by `F`'s caller are run, and so on up to any deferred by the top-level function in the executing goroutine. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called _panicking_.\n\n```go\npanic(42)\npanic(\"unreachable\")\npanic(Error(\"cannot parse\"))\n```\n\nThe `recover` function allows a program to manage behavior of a panicking goroutine. Suppose a function `G` defers a function `D` that calls `recover` and a panic occurs in a function on the same goroutine in which `G` is executing. When the running of deferred functions reaches `D`, the return value of `D`'s call to recover will be the value passed to the call of panic. If `D` returns normally, without starting a new panic, the panicking sequence stops. In that case, the state of functions called between `G` and the call to panic is discarded, and normal execution resumes. Any functions deferred by `G` before `D` are then run and `G`'s execution terminates by returning to its caller.\n\nThe return value of `recover` is `nil` when the goroutine is not panicking or `recover` was not called directly by a deferred function. Conversely, if a goroutine is panicking and recover was called directly by a deferred function, the return value of recover is guaranteed not to be `nil`. To ensure this, calling panic with a `nil` interface value (or an untyped nil) causes a [run-time panic]().\n\nThe `protect` function in the example below invokes the function argument `g` and protects callers from run-time panics raised by `g`.\n\n```go\nfunc protect(g func()) {\n\tdefer func() {\n\t\tlog.Println(\"done\")  // Println executes normally even if there is a panic\n\t\tif x := recover(); x != nil {\n\t\t\tlog.Printf(\"run time panic: %v\", x)\n\t\t}\n\t}()\n\tlog.Println(\"start\")\n\tg()\n}\n```\n\n### TODO\n\n```go\nprint\nprintf\nprintln\n...\n```\n\n## Blocks\n\nA _block_ is a possibly empty sequence of declarations and statements within matching brace brackets.\n\n```go\nBlock = \"{\" StatementList \"}\" .\nStatementList = { Statement \";\" } .\n```\n\nIn addition to explicit blocks in the source code, there are implicit blocks:\n\n* The _universe block_ encompasses all XGo source text.\n* Each [package](#packages) has a _package block_ containing all XGo source text for that package.\n* Each file has a _file block_ containing all XGo source text in that file.\n* Each \"[if](#if-statements)\", \"[for](#for-statements)\", and \"[switch](#switch-statements)\" statement is considered to be in its own implicit block.\n* Each clause in a \"[switch](#switch-statements)\" statement acts as an implicit block.\n\nBlocks nest and influence [scoping]().\n\n\n## Declarations and scope\n\nA _declaration_ binds a non-[blank]() identifier to a [constant](), [type](), [variable](), [function](), [label](), or [package](). Every identifier in a program must be declared. No identifier may be declared twice in the same block, and no identifier may be declared in both the file and package block.\n\nThe [blank identifier]() may be used like any other identifier in a declaration, but it does not introduce a binding and thus is not declared. In the package block, the identifier `init` may only be used for [init function]() declarations, and like the blank identifier it does not introduce a new binding.\n\n```go\nDeclaration   = ConstDecl | TypeDecl | VarDecl .\nTopLevelDecl  = Declaration | FunctionDecl .\n```\n\nThe scope of a declared identifier is the extent of source text in which the identifier denotes the specified constant, type, variable, function, label, or package.\n\nXGo is lexically scoped using blocks:\n\n* The scope of a [predeclared identifier]() is the universe block.\n* The scope of an identifier denoting a constant, type, variable, or function declared at top level (outside any function) is the package block.\n* The scope of the package name of an imported package is the file block of the file containing the import declaration.\n* The scope of an identifier denoting function parameter, or result variable is the function body.\n* The scope of a constant or variable identifier declared inside a function begins at the end of the `ConstSpec` or `VarSpec` (`ShortVarDecl` for short variable declarations) and ends at the end of the innermost containing block.\n\nAn identifier declared in a block may be redeclared in an inner block. While the identifier of the inner declaration is in scope, it denotes the entity declared by the inner declaration.\n\nThe [package clause]() is not a declaration; the package name does not appear in any scope. Its purpose is to identify the files belonging to the same [package](#packages) and to specify the default package name for import declarations.\n\n### Label scopes\n\nLabels are declared by [labeled statements](#labeled-statements) and are used in the \"[break](#break-statements)\", \"[continue](#continue-statements)\", and \"[goto](#goto-statements)\" statements. It is illegal to define a label that is never used. In contrast to other identifiers, labels are not block scoped and do not conflict with identifiers that are not labels. The scope of a label is the body of the function in which it is declared and excludes the body of any nested function.\n\n\n### Blank identifier\n\nThe _blank identifier_ is represented by the underscore character `_`. It serves as an anonymous placeholder instead of a regular (non-blank) identifier and has special meaning in [declarations](#declarations-and-scope), as an [operand](), and in [assignment statements](#assignment-statements).\n\n### Predeclared identifiers\n\nThe following identifiers are implicitly declared in the [universe block]():\n\n```go\nTypes:\n\tany bigint bigrat bool byte comparable\n\tcomplex64 complex128 error float32 float64\n\tint int8 int16 int32 int64 rune string\n\tuint uint8 uint16 uint32 uint64 uintptr\n\nConstants:\n\ttrue false iota\n\nZero value:\n\tnil\n\nFunctions:\n\tappend cap clear close complex copy delete echo imag len\n\tmake max min new panic print printf println real recover // TODO(xsw): more\n```\n\n### Exported identifiers\n\nAn identifier may be _exported_ to permit access to it from another package. An identifier is exported if both:\n\n* the first character of the identifier's name is a Unicode uppercase letter (Unicode character category Lu); and\n* the identifier is declared in the [package block]() or it is a [field name]() or [method name]().\n\nAll other identifiers are not exported.\n\n### Uniqueness of identifiers\n\nGiven a set of identifiers, an identifier is called _unique_ if it is _different_ from every other in the set. Two identifiers are different if they are spelled differently, or if they appear in different [packages](#packages) and are not [exported](#exported-identifiers). Otherwise, they are the same.\n\n### Constant declarations\n\nA constant declaration binds a list of identifiers (the names of the constants) to the values of a list of [constant expressions](#constant-expressions). The number of identifiers must be equal to the number of expressions, and the nth identifier on the left is bound to the value of the nth expression on the right.\n\n```go\nConstDecl      = \"const\" ( ConstSpec | \"(\" { ConstSpec \";\" } \")\" ) .\nConstSpec      = IdentifierList [ [ Type ] \"=\" ExpressionList ] .\n\nIdentifierList = identifier { \",\" identifier } .\nExpressionList = Expression { \",\" Expression } .\n```\n\nIf the type is present, all constants take the type specified, and the expressions must be [assignable]() to that type, which must not be a type parameter. If the type is omitted, the constants take the individual types of the corresponding expressions. If the expression values are untyped [constants](#constants), the declared constants remain untyped and the constant identifiers denote the constant values. For instance, if the expression is a floating-point literal, the constant identifier denotes a floating-point constant, even if the literal's fractional part is zero.\n\n```go\nconst Pi float64 = 3.14159265358979323846\nconst zero = 0.0         // untyped floating-point constant\nconst (\n\tsize int64 = 1024\n\teof        = -1  // untyped integer constant\n)\nconst a, b, c = 3, 4, \"foo\"  // a = 3, b = 4, c = \"foo\", untyped integer and string constants\nconst u, v float32 = 0, 3    // u = 0.0, v = 3.0\n```\n\nWithin a parenthesized const declaration list the expression list may be omitted from any but the first ConstSpec. Such an empty list is equivalent to the textual substitution of the first preceding non-empty expression list and its type if any. Omitting the list of expressions is therefore equivalent to repeating the previous list. The number of identifiers must be equal to the number of expressions in the previous list. Together with the [iota constant generator](#iota) this mechanism permits light-weight declaration of sequential values:\n\n```go\nconst (\n\tSunday = iota\n\tMonday\n\tTuesday\n\tWednesday\n\tThursday\n\tFriday\n\tPartyday\n\tnumberOfDays  // this constant is not exported\n)\n```\n\n### Iota\n\nWithin a [constant declaration](#constant-declarations), the predeclared identifier iota represents successive untyped integer [constants](#constants). Its value is the index of the respective [ConstSpec]() in that constant declaration, starting at zero. It can be used to construct a set of related constants:\n\n```go\nconst (\n\tc0 = iota  // c0 == 0\n\tc1 = iota  // c1 == 1\n\tc2 = iota  // c2 == 2\n)\n\nconst (\n\ta = 1 << iota  // a == 1  (iota == 0)\n\tb = 1 << iota  // b == 2  (iota == 1)\n\tc = 3          // c == 3  (iota == 2, unused)\n\td = 1 << iota  // d == 8  (iota == 3)\n)\n\nconst (\n\tu         = iota * 42  // u == 0     (untyped integer constant)\n\tv float64 = iota * 42  // v == 42.0  (float64 constant)\n\tw         = iota * 42  // w == 84    (untyped integer constant)\n)\n\nconst x = iota  // x == 0\nconst y = iota  // y == 0\n```\n\nBy definition, multiple uses of iota in the same ConstSpec all have the same value:\n\n```go\nconst (\n\tbit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)\n\tbit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)\n\t_, _                                  //                        (iota == 2, unused)\n\tbit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)\n)\n```\n\nThis last example exploits the [implicit repetition](#constant-declarations) of the last non-empty expression list.\n\n### Type declarations\n\nA type declaration binds an identifier, the _type name_, to a [type](#types). Type declarations come in two forms: alias declarations and type definitions.\n\n```go\nTypeDecl = \"type\" ( TypeSpec | \"(\" { TypeSpec \";\" } \")\" ) .\nTypeSpec = AliasDecl | TypeDef .\n```\n\n#### Alias declarations\n\nAn alias declaration binds an identifier to the given type.\n\n```go\nAliasDecl = identifier \"=\" Type .\n```\n\nWithin the [scope]() of the identifier, it serves as an alias for the type.\n\n```go\ntype (\n\tnodeList = []*Node  // nodeList and []*Node are identical types\n\tPolar    = polar    // Polar and polar denote identical types\n)\n```\n\n#### Type definitions\n\nA type definition creates a new, distinct type with the same [underlying type](#underlying-types) and operations as the given type and binds an identifier, the _type name_, to it.\n\n```go\nTypeDef = identifier Type .\n```\n\nThe new type is called a _defined type_. It is [different]() from any other type, including the type it is created from.\n\n```go\ntype (\n\tPoint struct{ x, y float64 }  // Point and struct{ x, y float64 } are different types\n\tpolar Point                   // polar and Point denote different types\n)\n\ntype TreeNode struct {\n\tleft, right *TreeNode\n\tvalue any\n}\n\ntype Block interface {\n\tBlockSize() int\n\tEncrypt(src, dst []byte)\n\tDecrypt(src, dst []byte)\n}\n```\n\nType definitions may be used to define different boolean, numeric, or string types:\n\n```go\ntype TimeZone int\n\nconst (\n\tEST TimeZone = -(5 + iota)\n\tCST\n\tMST\n\tPST\n)\n```\n\n### Variable declarations\n\nA variable declaration creates one or more [variables](#variables), binds corresponding identifiers to them, and gives each a type and an initial value.\n\n```go\nVarDecl     = \"var\" ( VarSpec | \"(\" { VarSpec \";\" } \")\" ) .\nVarSpec     = IdentifierList ( Type [ \"=\" ExpressionList ] | \"=\" ExpressionList ) .\n```\n\n```go\nvar i int\nvar U, V, W float64\nvar k = 0\nvar x, y float32 = -1, -2\nvar (\n\ti       int\n\tu, v, s = 2.0, 3.0, \"bar\"\n)\nvar re, im = complexSqrt(-1)\nvar _, found = entries[name]  // map lookup; only interested in \"found\"\n```\n\nIf a list of expressions is given, the variables are initialized with the expressions following the rules for [assignment statements](#assignment-statements). Otherwise, each variable is initialized to its [zero value](#the-zero-value).\n\nIf a type is present, each variable is given that type. Otherwise, each variable is given the type of the corresponding initialization value in the assignment. If that value is an untyped constant, it is first implicitly [converted](#conversions) to its [default type](#constants); if it is an untyped boolean value, it is first implicitly converted to type `bool`. The predeclared value `nil` cannot be used to initialize a variable with no explicit type.\n\n```go\nvar d = math.Sin(0.5)  // d is float64\nvar i = 42             // i is int\nvar t, ok = x.(T)      // t is T, ok is bool\nvar n = nil            // illegal\n```\n\nImplementation restriction: A compiler may make it illegal to declare a variable inside a [function body](#function-declarations) if the variable is never used.\n\nIn a function body, variables do not need to be explicitly defined.\n\n```go\nd := math.Sin(0.5)  // d is float64\ni := 42             // i is int\nt, ok := x.(T)      // t is T, ok is bool\n```\n\nSee [short variable declarations](#short-variable-declarations).\n\n### Function declarations\n\nA function declaration binds an identifier, the function name, to a function.\n\n```go\nFunctionDecl = \"func\" FunctionName Signature [ FunctionBody ] .\nFunctionName = identifier .\nFunctionBody = Block .\n```\n\nIf the function's [signature](#function-types) declares result parameters, the function body's statement list must end in a [terminating statement](#terminating-statements).\n\n```go\nfunc IndexRune(s string, r rune) int {\n\tfor i, c := range s {\n\t\tif c == r {\n\t\t\treturn i\n\t\t}\n\t}\n\t// invalid: missing return statement\n}\n```\n\n\n## Packages\n\nXGo programs are constructed by linking together packages. A package in turn is constructed from one or more source files that together declare constants, types, variables and functions belonging to the package and which are accessible in all files of the same package. Those elements may be [exported]() and used in another package.\n\n### Source file organization\n\nEach source file consists of a package clause defining the package to which it belongs, followed by a possibly empty set of import declarations that declare packages whose contents it wishes to use, followed by a possibly empty set of declarations of functions, types, variables, and constants.\n\n```go\nSourceFile       = [ PackageClause \";\" ] { ImportDecl \";\" } { TopLevelDecl \";\" } .\n```\n\n### Package clause\n\nA package clause begins each source file and defines the package to which the file belongs.\n\n```go\nPackageClause  = \"package\" PackageName .\nPackageName    = identifier .\n```\n\nThe PackageName must not be the [blank identifier]().\n\n```go\npackage math\n```\n\n### Import declarations\n\nAn import declaration states that the source file containing the declaration depends on functionality of the `imported` package ([Program initialization and execution]()) and enables access to [exported]() identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported.\n\n```go\nImportDecl       = \"import\" ( ImportSpec | \"(\" { ImportSpec \";\" } \")\" ) .\nImportSpec       = [ \".\" | PackageName ] ImportPath .\nImportPath       = string_lit .\n```\n\nThe PackageName is used in [qualified identifiers]() to access exported identifiers of the package within the importing source file. It is declared in the [file block](). If the PackageName is omitted, it defaults to the identifier specified in the [package clause](#package-clause) of the imported package. If an explicit period (.) appears instead of a name, all the package's exported identifiers declared in that package's [package block]() will be declared in the importing source file's file block and must be accessed without a qualifier.\n\nThe interpretation of the ImportPath is implementation-dependent but it is typically a substring of the full file name of the compiled package and may be relative to a repository of installed packages.\n\nImplementation restriction: A compiler may restrict ImportPaths to non-empty strings using only characters belonging to [Unicode's]() L, M, N, P, and S general categories (the Graphic characters without spaces) and may also exclude the characters !\"#$%&'()*,:;<=>?[\\]^`{|} and the Unicode replacement character U+FFFD.\n\nConsider a compiled a package containing the package clause package math, which exports function `Sin`, and installed the compiled package in the file identified by \"lib/math\". This table illustrates how `Sin` is accessed in files that import the package after the various types of import declaration.\n\n```go\nImport declaration          Local name of Sin\n\nimport   \"lib/math\"         math.Sin\nimport m \"lib/math\"         m.Sin\nimport . \"lib/math\"         Sin\n```\n\nAn import declaration declares a dependency relation between the importing and imported package. It is illegal for a package to import itself, directly or indirectly, or to directly import a package without referring to any of its exported identifiers. To import a package solely for its side-effects (initialization), use the [blank]() identifier as explicit package name:\n\n```go\nimport _ \"lib/math\"\n```\n\n### An example package\n\nHere is a complete XGo package that implements XXX.\n\n```go\nTODO\n```\n\n## Program initialization and execution\n\n### The zero value\n\nWhen storage is allocated for a [variable](#variables), either through a declaration or a call of `new`, or when a new value is created, either through a composite literal or a call of `make`, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the _zero_ value for its type: `false` for booleans, `0` for numeric types, `\"\"` for strings, and `nil` for pointers, functions, interfaces, slices, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.\n\nThese two simple declarations are equivalent:\n\n```go\nvar i int\nvar i int = 0\n```\n\nTODO\n\n### Package initialization\n\nWithin a package, package-level variable initialization proceeds stepwise, with each step selecting the variable earliest in `declaration order` which has no dependencies on uninitialized variables.\n\nMore precisely, a package-level variable is considered ready for `initialization` if it is not yet initialized and either has no [initialization expression]() or its initialization expression has no dependencies on uninitialized variables. Initialization proceeds by repeatedly initializing the next package-level variable that is earliest in declaration order and ready for initialization, until there are no variables ready for initialization.\n\nIf any variables are still uninitialized when this process ends, those variables are part of one or more initialization cycles, and the program is not valid.\n\nMultiple variables on the left-hand side of a variable declaration initialized by single (multi-valued) expression on the right-hand side are initialized together: If any of the variables on the left-hand side is initialized, all those variables are initialized in the same step.\n\n```go\nvar x = a\nvar a, b = f() // a and b are initialized together, before x is initialized\n```\n\nFor the purpose of package initialization, [blank]() variables are treated like any other variables in declarations.\n\nThe declaration order of variables declared in multiple files is determined by the order in which the files are presented to the compiler: Variables declared in the first file are declared before any of the variables declared in the second file, and so on. To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.\n\nDependency analysis does not rely on the actual values of the variables, only on lexical `references` to them in the source, analyzed transitively. For instance, if a variable x's initialization expression refers to a function whose body refers to variable y then x depends on y. Specifically:\n\n* A reference to a variable or function is an identifier denoting that variable or function.\n* A reference to a method m is a [method value]() or [method expression]() of the form `t.m`, where the (static) type of t is not an interface type, and the method `m` is in the method set of `t`. It is immaterial whether the resulting function value `t.m` is invoked.\n* A variable, function, or method x depends on a variable y if x's initialization expression or body (for functions and methods) contains a reference to y or to a function or method that depends on y.\n\nFor example, given the declarations\n\n```go\nvar (\n\ta = c + b  // == 9\n\tb = f()    // == 4\n\tc = f()    // == 5\n\td = 3      // == 5 after initialization has finished\n)\n\nfunc f() int {\n\td++\n\treturn d\n}\n```\n\nthe initialization order is `d`, `b`, `c`, `a`. Note that the order of subexpressions in initialization expressions is irrelevant: `a = c + b` and `a = b + c` result in the same initialization order in this example.\n\nDependency analysis is performed per package; only references referring to variables, functions, and (non-interface) methods declared in the current package are considered. If other, hidden, data dependencies exists between variables, the initialization order between those variables is unspecified.\n\nFor instance, given the declarations (TODO: use classfile instead of method)\n\n```go\nvar x = I(T{}).ab()   // x has an undetected, hidden dependency on a and b\nvar _ = sideEffect()  // unrelated to x, a, or b\nvar a = b\nvar b = 42\n\ntype I interface      { ab() []int }\ntype T struct{}\nfunc (T) ab() []int   { return []int{a, b} }\n```\n\nthe variable `a` will be initialized after `b` but whether `x` is initialized before `b`, between `b` and `a`, or after `a`, and thus also the moment at which `sideEffect()` is called (before or after `x` is initialized) is not specified.\n\nVariables may also be initialized using functions named init declared in the package block, with no arguments and no result parameters.\n\n```go\nfunc init() { … }\n```\n\nMultiple such functions may be defined per package, even within a single source file. In the package block, the `init` identifier can be used only to declare `init` functions, yet the identifier itself is not [declared](). Thus init functions cannot be referred to from anywhere in a program.\n\nThe entire package is initialized by assigning initial values to all its package-level variables followed by calling all `init` functions in the order they appear in the source, possibly in multiple files, as presented to the compiler.\n\n### Program initialization\n\nThe packages of a complete program are initialized stepwise, one package at a time. If a package has imports, the imported packages are initialized before initializing the package itself. If multiple packages import a package, the imported package will be initialized only once. The importing of packages, by construction, guarantees that there can be no cyclic initialization dependencies. More precisely:\n\nGiven the list of all packages, sorted by import path, in each step the first uninitialized package in the list for which all imported packages (if any) are already initialized is [initialized](#package-initialization). This step is repeated until all packages are initialized.\n\nPackage initialization—variable initialization and the invocation of `init` functions—happens in a single goroutine, sequentially, one package at a time. An `init` function may launch other goroutines, which can run concurrently with the initialization code. However, initialization always sequences the `init` functions: it will not invoke the next one until the previous one has returned.\n\n### Program execution\n\nA complete program is created by linking a single, unimported package called the main package with all the packages it imports, transitively. The main package must have package name main and declare a function main that takes no arguments and returns no value.\n\n```go\nfunc main() { … }\n```\n\nProgram execution begins by [initializing the program](#program-initialization) and then invoking the function `main` in package `main`. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.\n\n\n### Run-time panics\n\nExecution errors such as attempting to index an array out of bounds trigger a _run-time panic_ equivalent to a call of the built-in function [panic]() with a value of the implementation-defined interface type `runtime.Error`. That type satisfies the predeclared interface type [error](#errors). The exact error values that represent distinct run-time error conditions are unspecified.\n\n```go\npackage runtime\n\ntype Error interface {\n\terror\n\t// and perhaps other methods\n}\n```\n\n\n## System considerations\n\n### Package unsafe\n\nThe built-in package `unsafe`, known to the compiler and accessible through the [import path]() `\"unsafe\"`, provides facilities for low-level programming including operations that violate the type system. A package using unsafe must be vetted manually for type safety and may not be portable. The package provides the following interface:\n\n```go\npackage unsafe\n\ntype ArbitraryType int  // shorthand for an arbitrary Go type; it is not a real type\ntype Pointer *ArbitraryType\n\nfunc Alignof(variable ArbitraryType) uintptr\nfunc Offsetof(selector ArbitraryType) uintptr\nfunc Sizeof(variable ArbitraryType) uintptr\n\ntype IntegerType int  // shorthand for an integer type; it is not a real type\nfunc Add(ptr Pointer, len IntegerType) Pointer\nfunc Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType\nfunc SliceData(slice []ArbitraryType) *ArbitraryType\nfunc String(ptr *byte, len IntegerType) string\nfunc StringData(str string) *byte\n```\n\nA Pointer is a [pointer type](#pointer-types) but a Pointer value may not be [dereferenced](#address-operators). Any pointer or value of [underlying type](#underlying-types) `uintptr` can be [converted](#conversions) to a type of underlying type Pointer and vice versa. The effect of converting between Pointer and `uintptr` is implementation-defined.\n\n```go\nvar f float64\nbits = *(*uint64)(unsafe.Pointer(&f))\n\ntype ptr unsafe.Pointer\nbits = *(*uint64)(ptr(&f))\n\nfunc f[P ~*B, B any](p P) uintptr {\n\treturn uintptr(unsafe.Pointer(p))\n}\n\nvar p ptr = nil\n```\n\nThe functions `Alignof` and `Sizeof` take an expression `x` of any type and return the alignment or size, respectively, of a hypothetical variable `v` as if `v` was declared via `var v = x`.\n\nThe function `Offsetof` takes a (possibly parenthesized) [selector]() `s.f`, denoting a field `f` of the struct denoted by `s` or `*s`, and returns the field offset in bytes relative to the struct's address. If `f` is an [embedded field](), it must be reachable without pointer indirections through fields of the struct. For a struct `s` with field `f`:\n\n```go\nuintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))\n```\n\nComputer architectures may require memory addresses to be _aligned_; that is, for addresses of a variable to be a multiple of a factor, the variable's type's _alignment_. The function `Alignof` takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes. For a variable `x`:\n\n```go\nuintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0\n```\n\nA (variable of) type T has _variable size_ if T is a [type parameter](), or if it is an array or struct type containing elements or fields of variable size. Otherwise the size is constant. Calls to `Alignof`, `Offsetof`, and `Sizeof` are compile-time [constant expressions](#constant-expressions) of type `uintptr` if their arguments (or the struct `s` in the selector expression `s.f` for `Offsetof`) are types of constant size.\n\nThe function `Add` adds `len` to `ptr` and returns the updated pointer `unsafe.Pointer(uintptr(ptr) + uintptr(len))`. The `len` argument must be of [integer type](#numeric-types) or an untyped [constant](#constants). A constant `len` argument must be [representable]() by a value of type `int`; if it is an untyped constant it is given type `int`. The rules for [valid uses]() of Pointer still apply.\n\nThe function `Slice` returns a slice whose underlying array starts at `ptr` and whose length and capacity are `len`. `Slice(ptr, len)` is equivalent to\n\n```go\n(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]\n```\n\nexcept that, as a special case, if `ptr` is `nil` and `len` is zero, `Slice` returns `nil`.\n\nThe `len` argument must be of [integer type](#numeric-types) or an untyped [constant](#constants). A constant `len` argument must be non-negative and [representable]() by a value of type `int`; if it is an untyped constant it is given type `int`. At run time, if `len` is negative, or if `ptr` is nil and `len` is not zero, a [run-time panic](#run-time-panics) occurs.\n\nThe function `SliceData` returns a pointer to the underlying array of the `slice` argument. If the slice's capacity `cap(slice)` is not zero, that pointer is `&slice[:1][0]`. If slice is `nil`, the result is `nil`. Otherwise it is a non-nil pointer to an unspecified memory address.\n\nThe function `String` returns a `string` value whose underlying bytes start at `ptr` and whose length is `len`. The same requirements apply to the `ptr` and `len` argument as in the function `Slice`. If `len` is zero, the result is the empty string `\"\"`. Since XGo strings are immutable, the bytes passed to `String` must not be modified afterwards.\n\nThe function `StringData` returns a pointer to the underlying bytes of the `str` argument. For an empty string the return value is unspecified, and may be `nil`. Since XGo strings are immutable, the bytes returned by `StringData` must not be modified.\n\n### Size and alignment guarantees\n\nFor the [numeric types](#numeric-types), the following sizes are guaranteed:\n\n```go\ntype                                 size in bytes\n\nbyte, uint8, int8                     1\nuint16, int16                         2\nuint32, int32, float32                4\nuint64, int64, float64, complex64     8\ncomplex128                           16\n```\n\nThe following minimal alignment properties are guaranteed:\n\n* For a variable `x` of any type: `unsafe.Alignof(x)` is at least 1.\n* For a variable `x` of struct type: `unsafe.Alignof(x)` is the largest of all the values `unsafe.Alignof(x.f)` for each field `f` of `x`, but at least 1.\n* For a variable `x` of array type: `unsafe.Alignof(x)` is the same as the alignment of a variable of the array's element type.\n\nA struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.\n\n## Appendix\n\n### Language versions\n\nTODO\n\n### Type unification rules\n\nThe type unification rules describe if and how two types unify. The precise details are relevant for XGo implementations, affect the specifics of error messages (such as whether a compiler reports a type inference or other error), and may explain why type inference fails in unusual code situations. But by and large these rules can be ignored when writing Go code: type inference is designed to mostly \"work as expected\", and the unification rules are fine-tuned accordingly.\n\nType unification is controlled by a matching mode, which may be `exact` or `loose`. As unification recursively descends a composite type structure, the matching mode used for elements of the type, the element matching mode, remains the same as the matching mode except when two types are unified for [assignability]() (≡A): in this case, the matching mode is loose at the top level but then changes to exact for element types, reflecting the fact that types don't have to be identical to be assignable.\n\nTwo types that are not bound type parameters unify exactly if any of following conditions is true:\n\n* Both types are [identical]().\n* Both types have identical structure and their element types unify exactly.\n* Exactly one type is an [unbound]() type parameter with a [underlying type](#underlying-types), and that underlying type unifies with the other type per the unification rules for ≡A (loose unification at the top level and exact unification for element types).\n\nIf both types are bound type parameters, they unify per the given matching modes if:\n\n* Both type parameters are identical.\n* At most one of the type parameters has a known type argument. In this case, the type parameters are `joined`: they both stand for the same type argument. If neither type parameter has a known type argument yet, a future type argument inferred for one the type parameters is simultaneously inferred for both of them.\n* Both type parameters have a known type argument and the type arguments unify per the given matching modes.\n\nA single bound type parameter P and another type T unify per the given matching modes if:\n\n* P doesn't have a known type argument. In this case, T is inferred as the type argument for P.\n* P does have a known type argument A, A and T unify per the given matching modes, and one of the following conditions is true:\n  * Both A and T are interface types: In this case, if both A and T are also [defined]() types, they must be [identical](). Otherwise, if neither of them is a defined type, they must have the same number of methods (unification of A and T already established that the methods match).\n  * Neither A nor T are interface types: In this case, if T is a defined type, T replaces A as the inferred type argument for P.\n\nFinally, two types that are not bound type parameters unify loosely (and per the element matching mode) if:\n\n* Both types unify exactly.\n* One type is a [defined type](), the other type is a type literal, but not an interface, and their underlying types unify per the element matching mode.\n* Both types are interfaces (but not type parameters) with identical [type terms](), both or neither embed the predeclared type [comparable](), corresponding method types unify exactly, and the method set of one of the interfaces is a subset of the method set of the other interface.\n* Only one type is an interface (but not a type parameter), corresponding methods of the two types unify per the element matching mode, and the method set of the interface is a subset of the method set of the other type.\n* Both types have the same structure and their element types unify per the element matching mode.\n"
  },
  {
    "path": "doc/spec.md",
    "content": "The XGo Full Specification\n=====\n\nTODO\n\n## Comments\n\nSee [Comments](spec-mini.md#comments).\n\n\n## Literals\n\nSee [Literals](spec-mini.md#literals).\n\n### String literals\n\n### Composite literals\n\nComposite literals construct new composite values each time they are evaluated. They consist of the type of the literal followed by a brace-bound list of elements. Each element may optionally be preceded by a corresponding key.\n\n```go\nCompositeLit  = LiteralType LiteralValue .\nLiteralType   = TypeName [ TypeArgs ] .\nLiteralValue  = \"{\" [ ElementList [ \",\" ] ] \"}\" .\nElementList   = KeyedElement { \",\" KeyedElement } .\nKeyedElement  = [ Key \":\" ] Element .\nKey           = FieldName | Expression | LiteralValue .\nFieldName     = identifier .\nElement       = Expression | LiteralValue .\n```\n\nThe LiteralType's [underlying type](#underlying-types) `T` must be a [struct](#struct-types) or a [classfile]() type. The types of the elements and keys must be [assignable](#assignability) to the respective field; there is no additional conversion. It is an error to specify multiple elements with the same field name.\n\n* A key must be a field name declared in the struct type.\n* An element list that does not contain any keys must list an element for each struct field in the order in which the fields are declared.\n* If any element has a key, every element must have a key.\n* An element list that contains keys does not need to have an element for each struct field. Omitted fields get the zero value for that field.\n* A literal may omit the element list; such a literal evaluates to the zero value for its type.\n* It is an error to specify an element for a non-exported field of a struct belonging to a different package.\n\nGiven the declarations\n\n```go\ntype Point3D struct { x, y, z float64 }\ntype Line struct { p, q Point3D }\n```\n\none may write\n\n```go\norigin := Point3D{}                            // zero value for Point3D\nline := Line{origin, Point3D{y: -4, z: 12.3}}  // zero value for line.q.x\n```\n\n[Taking the address](#address-operators) of a composite literal generates a pointer to a unique [variable](#variables) initialized with the literal's value.\n\n```go\nvar pointer *Point3D = &Point3D{y: 1000}\n```\n\nA parsing ambiguity arises when a composite literal using the TypeName form of the LiteralType appears as an operand between the [keyword](#keywords) and the opening brace of the block of an \"if\", \"for\", or \"switch\" statement, and the composite literal is not enclosed in parentheses, square brackets, or curly braces. In this rare case, the opening brace of the literal is erroneously parsed as the one introducing the block of statements. To resolve the ambiguity, the composite literal must appear within parentheses.\n\n```go\nif x == (T{a,b,c}[i]) { … }\nif (x == T{a,b,c}[i]) { … }\n```\n\n#### C style string literals\n\nTODO\n\n```go\nc\"Hello, world!\\n\"\n```\n\n#### Python string literals\n\nTODO\n\n```go\npy\"Hello, world!\\n\"\n```\n\n\n## Types\n\n### Boolean types\n\nSee [Boolean types](spec-mini.md#boolean-types).\n\n### Numeric types\n\nSee [Numeric types](spec-mini.md#numeric-types).\n\n### String types\n\nSee [String types](spec-mini.md#string-types).\n\n#### C style string types\n\n```go\nimport \"c\"\n\n*c.Char  // alias for *int8\n```\n\n#### Python string types\n\n```go\nimport \"py\"\n\n*py.Object  // TODO: *py.String?\n```\n\n### Array types\n\nSee [Array types](spec-mini.md#array-types).\n\nAn array type T may not have an element of type T, or of a type containing T as a component, directly or indirectly, if those containing types are only array or struct types.\n\n```go\n// invalid array types\ntype (\n\tT1 [10]T1                 // element type of T1 is T1\n\tT2 [10]struct{ f T2 }     // T2 contains T2 as component of a struct\n\tT3 [10]T4                 // T3 contains T3 as component of a struct in T4\n\tT4 struct{ f T3 }         // T4 contains T4 as component of array T3 in a struct\n)\n\n// valid array types\ntype (\n\tT5 [10]*T5                // T5 contains T5 as component of a pointer\n\tT6 [10]func() T6          // T6 contains T6 as component of a function type\n\tT7 [10]struct{ f []T7 }   // T7 contains T7 as component of a slice in a struct\n)\n```\n\n### Pointer types\n\nSee [Pointer types](spec-mini.md#pointer-types).\n\n### Slice types\n\nSee [Slice types](spec-mini.md#slice-types).\n\n### Map types\n\nSee [Map types](spec-mini.md#map-types).\n\n### Struct types\n\nA struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or implicitly (EmbeddedField). Within a struct, non-[blank](#blank-identifier) field names must be [unique]().\n\n```go\nStructType    = \"struct\" \"{\" { FieldDecl \";\" } \"}\" .\nFieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .\nEmbeddedField = [ \"*\" ] TypeName [ TypeArgs ] .\nTag           = string_lit .\n```\n\n```go\n// An empty struct.\nstruct {}\n\n// A struct with 6 fields.\nstruct {\n\tx, y int\n\tu float32\n\t_ float32  // padding\n\tA *[]int\n\tF func()\n}\n```\n\nA field declared with a type but no explicit field name is called an _embedded field_. An embedded field must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type. The unqualified type name acts as the field name.\n\n```go\n// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4\nstruct {\n\tT1        // field name is T1\n\t*T2       // field name is T2\n\tP.T3      // field name is T3\n\t*P.T4     // field name is T4\n\tx, y int  // field names are x and y\n}\n```\n\nThe following declaration is illegal because field names must be unique in a struct type:\n\n```go\nstruct {\n\tT     // conflicts with embedded field *T and *P.T\n\t*T    // conflicts with embedded field T and *P.T\n\t*P.T  // conflicts with embedded field T and *T\n}\n```\n\nA field `f` or [method]() of an embedded field in a struct `x` is called promoted if `x.f` is a legal [selector]() that denotes that field or method `f`.\n\nPromoted fields act like ordinary fields of a struct except that they cannot be used as field names in [composite literals]() of the struct.\n\nGiven a struct type `S` and a [named type](#types) `T`, promoted methods are included in the method set of the struct as follows:\n\n* If `S` contains an embedded field `T`, the [method sets]() of `S` and `*S` both include promoted methods with receiver `T`. The method set of `*S` also includes promoted methods with receiver `*T`.\n* If `S` contains an embedded field `*T`, the method sets of `S` and `*S` both include promoted methods with receiver `T` or `*T`.\n\nA field declaration may be followed by an optional string literal _tag_, which becomes an attribute for all the fields in the corresponding field declaration. An empty tag string is equivalent to an absent tag. The tags are made visible through a [reflection interface]() and take part in [type identity]() for structs but are otherwise ignored.\n\n```go\nstruct {\n\tx, y float64 \"\"  // an empty tag string is like an absent tag\n\tname string  \"any string is permitted as a tag\"\n\t_    [4]byte \"ceci n'est pas un champ de structure\"\n}\n\n// A struct corresponding to a TimeStamp protocol buffer.\n// The tag strings define the protocol buffer field numbers;\n// they follow the convention outlined by the reflect package.\nstruct {\n\tmicrosec  uint64 `protobuf:\"1\"`\n\tserverIP6 uint64 `protobuf:\"2\"`\n}\n```\n\nA struct type `T` may not contain a field of type T, or of a type containing T as a component, directly or indirectly, if those containing types are only array or struct types.\n\n```go\n// invalid struct types\ntype (\n\tT1 struct{ T1 }            // T1 contains a field of T1\n\tT2 struct{ f [10]T2 }      // T2 contains T2 as component of an array\n\tT3 struct{ T4 }            // T3 contains T3 as component of an array in struct T4\n\tT4 struct{ f [10]T3 }      // T4 contains T4 as component of struct T3 in an array\n)\n\n// valid struct types\ntype (\n\tT5 struct{ f *T5 }         // T5 contains T5 as component of a pointer\n\tT6 struct{ f func() T6 }   // T6 contains T6 as component of a function type\n\tT7 struct{ f [10][]T7 }    // T7 contains T7 as component of a slice in an array\n)\n```\n\n### Tuple types\n\nSee [Tuple types](spec-mini.md#tuple-types).\n\n#### Brace-Style Construction\n\nIn addition to function-style construction, tuple supports brace-based initialization using `:` for field assignment:\n\n```go\ntype Point (x int, y int)\n\np1 := Point{x: 10, y: 20}\np2 := Point{10, 20}\n```\n\n#### Anonymous Tuple Literals with Braces\n\nIt allows using tuple literals within brace-based composite literals:\n\n```go\n// Using tuples in struct fields\ntype Record struct {\n\tcoords (int, int)\n\tdata   (string, bool)\n}\n\nr := Record{\n\tcoords: (10, 20),\n\tdata:   (\"test\", true),\n}\n```\n\n#### Type Compatibility and Reflection\n\nAt runtime, tuples are implemented as structs with ordinal field names `X_0`, `X_1`, `X_2`, etc.:\n\n```go\ntype Point (x int, y int)\n\n// At runtime, Point is equivalent to:\n// struct {\n//     X_0 int  // accessible as .x or .0 at compile time, .X_0 at runtime\n//     X_1 int  // accessible as .y or .1 at compile time, .X_1 at runtime\n// }\n```\n\nTuple types with the same element types (in the same order) have identical underlying structures but are distinct named types:\n\n```go\ntype Point (x int, y int)\ntype Coord (a int, b int)\n\n// Point and Coord have identical underlying types but are different types\n// Conversion is required: c := Coord(p)\n```\n\n### Function types\n\nSee [Function types](spec-mini.md#function-types).\n\n### Interface types\n\nTODO\n\n#### Builtin interfaces\n\nSee [Builtin interfaces](spec-mini.md#builtin-interfaces).\n\n##### The comparable interface\n\nThe predeclared interface type `comparable` denotes the set of all non-interface types that are strictly comparable. A type is strictly comparable if values of that type can be compared using the `==` and `!=` operators.\n\nThe `comparable` interface is primarily used as a type constraint in generic code and cannot be used as the type of a variable or struct field:\n\n```go\n// Example: using comparable as a type constraint\nfunc Find[T comparable](slice []T, value T) int {\n\tfor i, v := range slice {\n\t\tif v == value {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n```\n\nTypes that are strictly comparable include:\n- Boolean, numeric, and string types\n- Pointer types\n- Channel types\n- Array types (if their element type is strictly comparable)\n- Struct types (if all their field types are strictly comparable)\n\nSlice, map, and function types are not comparable and cannot be used with `comparable`.\n\n### Channel types\n\nTODO\n\n\n## Expressions\n\n### Commands and calls\n\nSee [Commands and calls](spec-mini.md#commands-and-calls).\n\n### Operators\n\nSee [Operators](spec-mini.md#operators).\n\n#### Operator precedence\n\nSee [Operator precedence](spec-mini.md#operator-precedence).\n\n#### Arithmetic operators\n\nSee [Arithmetic operators](spec-mini.md#arithmetic-operators).\n\n#### Comparison operators\n\nSee [Comparison operators](spec-mini.md#comparison-operators).\n\nThe equality operators == and != apply to operands of comparable types. The ordering operators <, <=, >, and >= apply to operands of ordered types. These terms and the result of the comparisons are defined as follows:\n\n* Channel types are comparable. Two channel values are equal if they were created by the same call to [make]() or if both have value `nil`.\n* Struct types are comparable if all their field types are comparable. Two struct values are equal if their corresponding non-[blank]() field values are equal. The fields are compared in source order, and comparison stops as soon as two field values differ (or all fields have been compared).\n* Type parameters are comparable if they are strictly comparable (see below).\n\n```go\nconst c = 3 < 4            // c is the untyped boolean constant true\n\ntype MyBool bool\nvar x, y int\nvar (\n\t// The result of a comparison is an untyped boolean.\n\t// The usual assignment rules apply.\n\tb3        = x == y // b3 has type bool\n\tb4 bool   = x == y // b4 has type bool\n\tb5 MyBool = x == y // b5 has type MyBool\n)\n```\n\nA type is _strictly comparable_ if it is comparable and not an interface type nor composed of interface types. Specifically:\n\n* Boolean, numeric, string, pointer, and channel types are strictly comparable.\n* Struct types are strictly comparable if all their field types are strictly comparable.\n* Array types are strictly comparable if their array element types are strictly comparable.\n* Type parameters are strictly comparable if all types in their type set are strictly comparable.\n\n#### Logical operators\n\nSee [Logical operators](spec-mini.md#logical-operators).\n\n### Address operators\n\nSee [Address operators](spec-mini.md#address-operators).\n\n### Send/Receive operator\n\nTODO\n\n### Conversions\n\nSee [Conversions](spec-mini.md#conversions).\n\nTODO\n\n\n## Statements\n\nTODO\n\n## Built-in functions\n\n### Appending to and copying slices\n\nSee [Appending to and copying slices](spec-mini.md#appending-to-and-copying-slices).\n\n### Clear\n\nTODO\n\n### Close\n\nTODO\n\n### Manipulating complex numbers\n"
  },
  {
    "path": "doc/string.md",
    "content": "# String Type\n\nXGo provides powerful and flexible string handling capabilities. Strings are sequences of characters used to represent text, and they are one of the most commonly used data types in programming.\n\n## String Literals\n\nXGo provides multiple ways to represent strings, from simple literals to complex Unicode characters.\n\n### Basic String Literals\n\nIn XGo, you can create strings using double quotes:\n\n```go\nname := \"Bob\"\nmessage := \"Hello, World!\"\nempty := \"\"\n```\n\n### Raw String Literals\n\nXGo also supports raw string literals using backticks (`` ` ``). Raw strings treat backslashes and other special characters literally, making them ideal for regular expressions, file paths, and multi-line text:\n\n```go\n// Raw strings ignore escape sequences\npath := `C:\\Users\\Bob\\Documents`  // Backslashes are literal\nregex := `\\d+\\.\\d+`               // No need to escape backslashes\n\n// Multi-line raw strings\nmultiline := `Line 1\nLine 2\nLine 3`\n\n// JSON or code snippets\njson := `{\n    \"name\": \"Alice\",\n    \"age\": 30\n}`\n\n// SQL queries\nquery := `SELECT * FROM users\n          WHERE age > 18\n          ORDER BY name`\n```\n\n**Key differences between double-quoted and raw strings:**\n\n| Feature | Double-quoted `\"...\"` | Raw (backtick) `` `...` `` |\n|---------|----------------------|---------------------------|\n| Escape sequences | Processed (`\\n`, `\\t`, etc.) | Literal (ignored) |\n| Multi-line | Requires `\\n` | Natural line breaks |\n| Backslashes | Must escape `\\\\` | Literal `\\` |\n| Interpolation | Supported `${...}` | Not supported |\n| Use case | General strings, interpolation | Paths, regex, multi-line text |\n\n```go\n// Comparison example\nescaped := \"Line 1\\nLine 2\"    // Two lines when printed\nraw := `Line 1\\nLine 2`        // Literal \\n characters\n\necho escaped\n// Output:\n// Line 1\n// Line 2\n\necho raw\n// Output:\n// Line 1\\nLine 2\n```\n\n### Escape Sequences\n\nXGo supports various escape sequences for special characters:\n\n```go\n// Common escape sequences\nnewline := \"Line 1\\nLine 2\"     // Newline\ntab := \"Column1\\tColumn2\"       // Tab\nquote := \"She said \\\"Hello\\\"\"   // Double quote\nbackslash := \"Path: C:\\\\files\"  // Backslash\n\n// Octal escape notation \\### where # is an octal digit\noctalChar := \"\\141a\"            // aa\n\n// Unicode can be specified as \\u#### where # is a hex digit\n// It will be converted internally to its UTF-8 representation\nstar := \"\\u2605\"                // ★\nheart := \"\\u2665\"               // ♥\n```\n\n#### Common Escape Sequences\n\n| Sequence | Description | Example |\n|----------|-------------|---------|\n| `\\n` | Newline | `\"Line 1\\nLine 2\"` |\n| `\\t` | Tab | `\"Name:\\tAlice\"` |\n| `\\\\` | Backslash | `\"C:\\\\path\"` |\n| `\\\"` | Double quote | `\"He said \\\"Hi\\\"\"` |\n| `\\###` | Octal character | `\"\\141\"` (a) |\n| `\\u####` | Unicode character | `\"\\u2605\"` (★) |\n\n## String Immutability\n\nString values are immutable in XGo. Once created, you cannot modify individual characters:\n\n```go failcompile\ns := \"hello 🌎\"\ns[0] = `H` // Error: not allowed\n```\n\nTo modify a string, you must create a new one:\n\n```go\ns := \"hello\"\ns = \"Hello\"  // OK: assigning a new string\ns = s + \" world\"  // OK: creating a new concatenated string\n```\n\n## String Operations\n\n### Indexing and Slicing\n\n#### Indexing\n\nIndexing a string returns a `byte` value (not a `rune` or another `string`):\n\n```go\nname := \"Bob\"\necho name[0]   // 66 (ASCII value of 'B')\necho name[1]   // 111 (ASCII value of 'o')\necho name[2]   // 98 (ASCII value of 'b')\n```\n\n**Warning**: Indexing into multi-byte characters (like Chinese characters or emojis) will return individual bytes, which may not represent a complete character.\n\n#### Slicing\n\nYou can extract substrings using slice notation:\n\n```go\nname := \"Bob\"\necho name[1:3]  // ob (from index 1 to 3, exclusive)\necho name[:2]   // Bo (from start to index 2)\necho name[2:]   // b (from index 2 to end)\n\ns := \"Hello, World!\"\necho s[0:5]     // Hello\necho s[:5]      // Hello (start defaults to 0)\necho s[7:]      // World! (end defaults to string length)\n```\n\nSlicing syntax:\n- `s[start:end]` - from index `start` to `end` (exclusive)\n- `s[:end]` - from beginning to `end`\n- `s[start:]` - from `start` to end of string\n\n### String Conversion\n\n#### Converting Strings to Integers\n\nStrings can be easily converted to integers:\n\n```go\ns := \"12\"\na, err := s.int    // Returns value and error (safe conversion)\nb := s.int!        // Panics if s isn't a valid integer (unsafe conversion)\n\n// Example with error handling\nif num, err := s.int; err == nil {\n    echo \"Valid number:\", num\n} else {\n    echo \"Invalid number\"\n}\n```\n\n#### Converting Other Types to Strings\n\nTo convert other types to strings, use the `.string` property:\n\n```go\nage := 10\nageStr := age.string\necho \"age = \" + ageStr  // age = 10\n\npi := 3.14159\npiStr := pi.string\necho \"π = \" + piStr  // π = 3.14159\n```\n\n## String Operators\n\n### Concatenation\n\nUse the `+` operator to concatenate strings:\n\n```go\nname := \"Bob\"\nbobby := name + \"by\"\necho bobby // Bobby\n\ns := \"Hello \"\ns += \"world\"\necho s // Hello world\n\n// Multiple concatenations\ngreeting := \"Hello\" + \" \" + \"World\" + \"!\"\necho greeting  // Hello World!\n```\n\n### Type Consistency\n\nXGo operators require values of the same type on both sides. You cannot concatenate an integer directly to a string:\n\n```go failcompile\nage := 10\necho \"age = \" + age // Error: not allowed\n```\n\nYou must convert `age` to a string first:\n\n```go\nage := 10\necho \"age = \" + age.string  // age = 10\n```\n\n## String Interpolation\n\nXGo supports string interpolation using `${expression}` syntax, which provides a cleaner alternative to concatenation:\n\n```go\nage := 10\necho \"age = ${age}\"  // age = 10\n\nname := \"Alice\"\ngreeting := \"Hello, ${name}!\"\necho greeting  // Hello, Alice!\n```\n\n### Complex Interpolation\n\nYou can use any expression inside `${...}`:\n\n```go\n// Arithmetic expressions\nx := 5\ny := 3\necho \"${x} + ${y} = ${x + y}\"  // 5 + 3 = 8\n\n// Function calls and method calls\nname := \"bob\"\necho \"Hello, ${name.toUpper}!\"  // Hello, BOB!\n\n// Complex example\nhost := \"example.com\"\npage := 0\nlimit := 20\nurl := \"https://${host}/items?page=${page+1}&limit=${limit}\"\necho url  // https://example.com/items?page=1&limit=20\n```\n\n### Escaping the Dollar Sign\n\nTo include a literal `$` in a string, use `$$`:\n\n```go\necho \"Price: $$50\"  // Price: $50\necho \"Total: $$${100 + 50}\"  // Total: $150\n```\n\n## String Methods\n\nXGo provides built-in methods for common string operations. These methods do not modify the original string (strings are immutable) but return new strings.\n\n### String Length\n\nYou can get the length of a string using the `len` method:\n\n```go\nname := \"Bob\"\necho name.len  // 3\n\nchinese := \"你好\"\necho chinese.len  // 6 (bytes, not characters - each Chinese character is 3 bytes in UTF-8)\n```\n\n**Important**: `len` returns the number of bytes, not the number of characters. For strings containing non-ASCII characters (like Chinese, emojis), the byte length will be larger than the character count.\n\n### Case Conversion\n\n```go\n// Convert to uppercase\necho \"Hello\".toUpper  // HELLO\necho \"hello world\".toUpper  // HELLO WORLD\n\n// Convert to lowercase\necho \"Hello\".toLower  // hello\necho \"HELLO WORLD\".toLower  // hello world\n\n// Capitalize first letter of each word\necho \"hello world\".capitalize  // Hello World\necho \"the quick brown fox\".capitalize  // The Quick Brown Fox\n```\n\n### String Repetition\n\n```go\n// Repeat a string n times\necho \"XGo\".repeat(3)  // XGoXGoXGo\necho \"Ha\".repeat(5)  // HaHaHaHaHa\necho \"-\".repeat(10)  // ----------\n\n// Useful for formatting\nseparator := \"=\".repeat(40)\necho separator\necho \"Title\"\necho separator\n```\n\n### String Replacement\n\n```go\n// Replace all occurrences\necho \"Hello\".replaceAll(\"l\", \"L\")  // HeLLo\necho \"banana\".replaceAll(\"a\", \"o\")  // bonono\n\n// Practical example\ntext := \"The quick brown fox\"\ncensored := text.replaceAll(\"fox\", \"***\")\necho censored  // The quick brown ***\n```\n\n### Joining Strings\n\nJoin a list of strings into a single string with a separator:\n\n```go\n// Join with comma\nfruits := [\"apple\", \"banana\", \"cherry\"]\necho fruits.join(\",\")  // apple,banana,cherry\n\n// Join with space\nwords := [\"Hello\", \"World\"]\necho words.join(\" \")  // Hello World\n\n// Join without separator\nletters := [\"H\", \"e\", \"l\", \"l\", \"o\"]\necho letters.join(\"\")  // Hello\n\n// Practical example with newlines\nlines := [\"Line 1\", \"Line 2\", \"Line 3\"]\ntext := lines.join(\"\\n\")\necho text\n// Output:\n// Line 1\n// Line 2\n// Line 3\n```\n\n### Splitting Strings\n\nSplit a string into a list of substrings using a separator:\n\n```go\n// Split by delimiter\nsubjects := \"Math-English-Science-History\"\nsubjectList := subjects.split(\"-\")\necho subjectList  // [Math English Science History]\n\n// Split by space\nsentence := \"The quick brown fox\"\nwords := sentence.split(\" \")\necho words  // [The quick brown fox]\n\n// Split CSV data\ncsv := \"Alice,30,Engineer\"\nfields := csv.split(\",\")\necho fields  // [Alice 30 Engineer]\n\n// Process split results\nfor field in fields {\n    echo \"Field:\", field\n}\n```\n\n## Characters and Bytes\n\nIn XGo, strings can be traversed by character (`rune`) or by byte. Understanding the difference is crucial when working with non-ASCII characters.\n\n### Character Encoding Basics\n\n- **ASCII characters** (like English letters, digits): 1 byte per character\n- **Non-ASCII characters** (like Chinese, emojis): 2-4 bytes per character (UTF-8 encoding)\n- **`len`** returns byte count, not character count\n- **Indexing** returns bytes, not complete characters\n\n### Iterating by Character (Rune)\n\nUse `for in` loop to iterate over characters (runes):\n\n```go\n// English text (1 byte per character)\ns := \"Hello\"\nfor c in s {\n    echo c\n}\n// Output:\n// H\n// e\n// l\n// l\n// o\n\n// Mixed text with Chinese characters\ns := \"你好XGo\"\nfor c in s {\n    echo c\n}\n// Output:\n// 你\n// 好\n// X\n// G\n// o\n```\n\n### Iterating by Byte\n\nUse traditional index-based loop to iterate over bytes:\n\n```go\ns := \"Hello\"\nfor i := 0; i < len(s); i++ {\n    echo s[i]  // Prints byte values: 72, 101, 108, 108, 111\n}\n\n// With non-ASCII characters\ns := \"你好XGo\"\nfor i := 0; i < len(s); i++ {\n    echo s[i]\n}\n// Outputs byte values (each Chinese character is 3 bytes)\n// For '你': 228, 189, 160\n// For '好': 229, 165, 189\n// For 'X': 88\n// For 'G': 71\n// For 'o': 111\n```\n\n### Working with Non-ASCII Characters\n\n**⚠️ Important Warnings**:\n\n1. **Length discrepancy**: `len()` returns bytes, not character count\n2. **Indexing multi-byte characters**: Accessing individual bytes of multi-byte characters yields incomplete data\n3. **Use character iteration**: When processing text with non-ASCII characters, use `for c in s` instead of index-based loops\n\n```go\n// Example: Chinese characters\ns := \"你好\"\n\n// WRONG: This returns byte count, not character count\necho s.len  // 6 (bytes)\n\n// WRONG: This returns part of a character\necho s[0]  // 228 (first byte of '你')\n\n// CORRECT: Count characters\ncount := 0\nfor _ in s {\n    count++\n}\necho count  // 2 (characters)\n\n// CORRECT: Process characters\nfor char in s {\n    echo char  // Prints: 你, then 好\n}\n```\n\n### Practical Example: Character vs Byte Processing\n\n```go\n// Process mixed text (need character iteration)\nmixed := \"Hello你好\"\necho \"Byte length:\", mixed.len  // 11 (5 ASCII + 6 for Chinese)\n\ncharCount := 0\nfor _ in mixed {\n    charCount++\n}\necho \"Character count:\", charCount  // 7\n\n// Extract characters correctly\nfor i, char in mixed {\n    echo \"Character ${i}: ${char}\"\n}\n// Output:\n// Character 0: H\n// Character 1: e\n// Character 2: l\n// Character 3: l\n// Character 4: o\n// Character 5: 你\n// Character 8: 好\n```\n\n## Common Patterns\n\n### String Validation\n\n```go\n// Check if string is a valid integer\ninput := \"12345\"\nif num, err := input.int; err == nil {\n    echo \"Valid number:\", num\n} else {\n    echo \"Invalid number\"\n}\n\n// Check string length\nusername := \"alice\"\nif username.len < 3 {\n    echo \"Username too short\"\n} else if username.len > 20 {\n    echo \"Username too long\"\n} else {\n    echo \"Username OK\"\n}\n```\n\n### String Formatting\n\n```go\n// Build formatted strings\nname := \"Alice\"\nage := 30\ncity := \"New York\"\n\n// Using interpolation\nprofile := \"Name: ${name}, Age: ${age}, City: ${city}\"\necho profile\n\n// Building multi-line strings\nheader := \"=\".repeat(40)\ntitle := \"User Profile\"\ncontent := \"${header}\\n${title}\\n${header}\\nName: ${name}\\nAge: ${age}\\nCity: ${city}\"\necho content\n```\n\n### String Parsing\n\n```go\n// Parse CSV data\ncsv := \"Alice,30,Engineer,New York\"\nfields := csv.split(\",\")\nname := fields[0]\nage := fields[1].int!\njob := fields[2]\ncity := fields[3]\n\necho \"Name: ${name}, Age: ${age}, Job: ${job}, City: ${city}\"\n\n// Parse key-value pairs\nconfig := \"host=localhost;port=8080;debug=true\"\npairs := config.split(\";\")\nsettings := {}\nfor pair in pairs {\n    parts := pair.split(\"=\")\n    key := parts[0]\n    value := parts[1]\n    settings[key] = value\n}\necho settings  // map[host:localhost port:8080 debug:true]\n```\n\n### String Templates\n\n```go\n// Email template\nfunc generateEmail(name, action, link string) string {\n    return \"Hello ${name},\\n\\nPlease click the link below to ${action}:\\n${link}\\n\\nBest regards,\\nThe Team\"\n}\n\nemail := generateEmail(\"Alice\", \"verify your email\", \"https://example.com/verify\")\necho email\n```\n\n### Text Processing\n\n```go\n// Word count\ntext := \"The quick brown fox jumps over the lazy dog\"\nwords := text.split(\" \")\necho \"Word count:\", words.len\n\n// Capitalize each word\ncapitalized := []\nfor word in words {\n    capitalized = append(capitalized, word.capitalize)\n}\nresult := capitalized.join(\" \")\necho result  // The Quick Brown Fox Jumps Over The Lazy Dog\n\n// Remove extra spaces\nmessyText := \"  Too   many    spaces   \"\ncleaned := [s for s in messyText.split(\" \") if s != \"\"].join(\" \")\necho cleaned  // Too many spaces\n```\n\n### Building Complex Strings\n\n```go\n// Building a URL with query parameters\nfunc buildURL(base string, params map[string]any) string {\n    if params.len == 0 {\n        return base\n    }\n    \n    queryParts := []\n    for key, value in params {\n        queryParts = append(queryParts, \"${key}=${value}\")\n    }\n    \n    return \"${base}?${queryParts.join(\"&\")}\"\n}\n\nurl := buildURL(\"https://api.example.com/search\", {\n    \"q\": \"xgo\",\n    \"page\": 1,\n    \"limit\": 20,\n})\necho url  // https://api.example.com/search?q=xgo&page=1&limit=20\n\n// Building a report\nfunc buildReport(title string, items []string) string {\n    separator := \"=\".repeat(50)\n    header := \"${separator}\\n${title}\\n${separator}\"\n    \n    itemList := []\n    for i, item in items {\n        itemList = append(itemList, \"${i+1}. ${item}\")\n    }\n    \n    return \"${header}\\n${itemList.join(\"\\n\")}\"\n}\n\nreport := buildReport(\"Task List\", [\"Review code\", \"Write tests\", \"Update docs\"])\necho report\n```\n\n## Best Practices\n\n1. **Use string interpolation** (`\"${expr}\"`) instead of concatenation for better readability\n2. **Use `.string` method** to convert other types to strings\n3. **Check string length** before accessing indices to avoid runtime errors\n4. **Use character iteration** (`for c in s`) when processing text with non-ASCII characters\n5. **Prefer string methods** over manual manipulation for common operations\n6. **Handle conversion errors** when converting strings to numbers using the comma-ok form\n7. **Remember strings are immutable** - methods return new strings rather than modifying originals\n8. **Use escape sequences** for special characters rather than trying to insert them literally\n9. **Be aware of byte vs. character distinction** when working with internationalized text\n10. **Use appropriate string methods** (`.toUpper`, `.toLower`, etc.) for case-insensitive operations\n11. **Use raw strings** (backticks) for paths, regular expressions, and multi-line text to avoid escape sequence hassles\n12. **Choose the right string literal type**: double quotes for interpolation and escape sequences, backticks for literal text\n\n## Performance Tips\n\n1. **Avoid excessive concatenation in loops**: Build string slices and join them instead\n   ```go\n   // Less efficient\n   result := \"\"\n   for i := 0; i < 1000; i++ {\n       result += \"item${i},\"\n   }\n   \n   // More efficient\n   parts := []\n   for i := 0; i < 1000; i++ {\n       parts = append(parts, \"item${i}\")\n   }\n   result := parts.join(\",\")\n   ```\n\n2. **Use string interpolation**: It's more efficient than multiple concatenations\n   ```go\n   // Less efficient\n   message := \"Hello, \" + name + \"! You are \" + age.string + \" years old.\"\n   \n   // More efficient\n   message := \"Hello, ${name}! You are ${age} years old.\"\n   ```\n\n3. **Reuse string slices**: When splitting strings multiple times, consider reusing slices\n\n4. **Consider byte operations**: For performance-critical ASCII-only operations, byte-level processing can be faster\n\n5. **Preallocate when building large strings**: If you know the approximate size, preallocate capacity\n"
  },
  {
    "path": "doc/struct-vs-tuple.md",
    "content": "# Tuples vs. Structs\n\nIn the XGo programming language, tuple and struct are two distinct ways of organizing data. While both can combine multiple values together, they differ significantly in their type system, visibility rules, runtime characteristics, and other aspects. Understanding these differences is crucial for choosing the right data structure.\n\n## Type Identity and Instantiation\n\nTuple and struct behave very differently in both the type system and how instances are created.\n\n**Type identity:** Tuple types with the same element structure have identical underlying representations, meaning `(a int, b string)` and `(c int, d string)` are **identical** types. However, named tuple types are distinct. For example, `type Point (x int, y int)` and `type Coord (a int, b int)` define two different types, even though they have the same underlying structure. In this context, tuple field names are compile-time aliases, but named tuple types provide nominal typing.\n\nIn contrast, struct type checking is much stricter. Even if two structs have exactly the same field types and order, they are considered different types if their definitions differ or their field names are different. This characteristic makes struct more suitable for expressing data structures with clear semantics.\n\n**Initialization syntax:** A key advantage of tuple is its **unified syntax philosophy**—both tuple type definitions and tuple initialization use **function-like syntax**. Defining a tuple type resembles a function signature, and creating a tuple instance resembles a function call. This consistency makes tuples intuitive and reduces cognitive overhead.\n\nFurthermore, tuple initialization syntax is **identical to type conversion syntax**. Whether you're creating a new tuple or converting between compatible tuple types, you use the same `TypeName(values)` pattern. This means developers only need to learn one syntax pattern that works for function calls, tuple creation, and type conversions—a remarkable level of consistency.\n\nStruct, however, requires learning multiple distinct syntaxes: curly braces with field-value pairs for initialization (`Point{3, 4}`), and a different pattern for type conversion. This adds conceptual complexity that doesn't align with the rest of the language's function-centric design.\n\n### Tuple Example\n\n```go\n// Anonymous tuples: structural equivalence\nvar t1 (int, string) = (42, \"hello\")\nvar t2 (int, string) = t1  // ✓ OK: same structure\n\n// Named tuples: nominal typing but structurally convertible\ntype Point (x, y int)      // Definition: function-like syntax\ntype Coord (a, b int)\n\n// Tuple initialization has two equivalent forms:\n// Form 1: positional arguments (like function calls)\nvar pt1 = Point(3, 4)\n\n// Form 2: named arguments (explicit field names)\nvar pt2 = Point(x = 3, y = 4)\n\n// Both forms use function-call syntax\n// Type conversion: same function-like syntax!\nvar cd = Coord(pt1) // Type conversion (structurally compatible)\n\n// Notice: Point(3, 4) creates a tuple, Coord(pt1) converts types\n// Both use identical syntax pattern - no new syntax to learn\n```\n\n### Struct Example\n\n```go\n// Structs: always require exact type match\ntype Point struct {        // Definition: requires 'struct' keyword\n    X, Y int\n}\n\ntype Coord struct {\n    X, Y int\n}\n\n// Struct initialization also has two equivalent forms:\n// Form 1: positional arguments\nvar pt1 = Point{3, 4}\n\n// Form 2: named fields (explicit field names)\nvar pt2 = Point{X: 3, Y: 4}\n\n// Type conversion still requires explicit field access\nvar cd = Coord(pt1)          // ✗ Error: different types\nvar cd = Coord{pt1.X, pt1.Y} // Positional conversion\n\n// Struct requires learning the {field: value} syntax separately\n// No unified pattern across initialization and conversion\n```\n\n## Differences in Visibility Rules\n\nRegarding visibility control, tuple adopts a simpler strategy: all fields in a tuple are **always public**, with no concept of lowercase letters indicating private access. This design reflects tuple's positioning as a lightweight data container—it's primarily used for temporarily combining data rather than encapsulating complex object state.\n\nStruct, on the other hand, fully supports Go-style visibility control, using uppercase and lowercase initial letters to distinguish between public and private fields, providing necessary support for modular design and encapsulation.\n\n## Runtime Reflection Differences\n\nWhen it comes to runtime reflection, the differences become even more pronounced. After performing reflect operations on a tuple, its field names become `X_0`, `X_1`, and so on. This means that the friendly field names used at compile time **only exist during compilation** and are erased at runtime.\n\nIn contrast, struct field names are fully preserved at runtime, which enables struct to support various reflection-based functionalities such as serialization, ORM mapping, configuration parsing, and more. This is a significant advantage of struct over tuple.\n\n## Methods and Object-Orientation\n\nIn XGo's design philosophy, **tuple does not encourage objectification**, meaning it's not recommended to add methods to tuples. This aligns with tuple's positioning as a simple data container—it should remain lightweight and simple, avoiding the burden of excessive behavioral logic.\n\nIf methods are genuinely needed for a data structure, XGo recommends using [classfile](classfile.md) to achieve more complete object-oriented features.\n\n## Practical Application Limitations\n\nIn practical applications, these differences lead to obvious usage limitations. For example, in scenarios like **reading configuration files**, tuple cannot replace struct. Configuration parsing typically relies on reflection mechanisms to map configuration items to data structure fields, and since tuple loses field name information at runtime, it cannot support this kind of mapping.\n\nMore broadly, almost all **functionalities that depend on reflect must use struct**. Common scenarios including JSON/XML serialization, database ORM, dependency injection, and struct tag parsing all require the complete runtime type information that struct provides.\n\n## XGo MiniSpec Recommendation\n\nAn important consideration when choosing between tuple and struct is their position in **XGo's recommended syntax set (XGo MiniSpec)**. Tuple is included in the XGo MiniSpec, while struct is not.\n\nThis design choice reflects XGo's philosophy that **tuple combined with [classfile](classfile.md) can completely replace all scenarios where struct is used in Go**. The combination provides:\n- Tuple for lightweight data containers and function return values\n- Classfile for complex types requiring encapsulation, methods, and object-oriented features\n\nBy promoting this tuple + classfile approach, XGo MiniSpec aims to provide a more streamlined and consistent way of organizing data and behavior, reducing the conceptual overhead of having multiple overlapping constructs.\n\n## Summary\n\nTuple and struct each have their appropriate use cases in XGo. Tuple is suitable as a lightweight, temporary data container for returning multiple values from functions or simple data combinations. Struct, however, is more appropriate for defining data types with clear semantics, especially in scenarios requiring encapsulation, reflection support, or method binding.\n\nFor developers following XGo MiniSpec, the recommended approach is to use tuple for simple data combinations and classfile when object-oriented features are needed, avoiding struct altogether. This provides a cleaner conceptual model while maintaining full functionality.\n\nUnderstanding these differences helps developers choose the appropriate data structure for the right scenarios, leading to clearer and more efficient code.\n"
  },
  {
    "path": "doc/xgo-vs-go.md",
    "content": "XGo vs. Go\n======\n\nWhat's the difference between XGo and Go?\n\nThe goal of Go is to provide a way to easily build simple, reliable, and efficient software. It wants to be a better C.\n\nThe goal of XGo is to achieve a natural fusion of engineering and low-code. It wants to be a better Python.\n\nThe essence of low-code is non-professionals oriented. Today's mainstream programming language designers still take non-professionals as a niche group. But XGo take this group as vital, and they are a vital breakthrough in today's programming language revolution.\n\nThe Python language appeared very early (1991), but its design idea is very far-sighted, and it is a rare non-professionals oriented programming language so far. There are quite a few languages that look relatively concise, such as Ruby and CoffeeScript, but they have many language magics so that people feel they are flexible and powerful, but not easy to master. They cannot be regarded as non-professionals oriented.\n\nPython is very popular today, but it's hard to call it an engineering language. Most people just take it as a tool for rapid prototyping.\n\nIs it possible to minimize the complexity of engineering and present it in a low-code form? XGo hopes to explore a new path at this point.\n\n## Fundamental Differences in Design Philosophy\n\n### XGo: A Programming Language for Everyone\n\nXGo's core design philosophy is to \"enable everyone to become a builder of the world\". This is reflected in three key design goals:\n\n- **For engineering**: working in the simplest language that can be mastered by children\n- **For STEM education**: studying an engineering language that can be used for work in the future  \n- **For data science**: communicating with engineers in the same language\n\n### Go: An Engineering Language for Systems Programming\n\nGo's design focus is on engineering practices for systems programming, emphasizing performance, concurrency, and maintainability for large projects.\n\n## Differences in Abstraction Levels\n\n### XGo: Specific Domain Friendliness (SDF)\n\nXGo adopts a unique design philosophy: instead of creating Domain Specific Languages (DSL), it provides domain-friendly support. \n\nThe core of this design philosophy is:\n- Don't define a language for specific domain\n- Abstract domain knowledge for it\n\n### Go: General-Purpose Systems Programming\n\nGo focuses on general-purpose systems programming, providing abstraction capabilities through interfaces and composition, but doesn't specifically optimize for particular application domains.\n\n## Differences in Learning Curve Design\n\n### XGo: Progressive Complexity\n\nXGo's design allows starting from the simplest scripts and gradually scaling to large projects:\n- Simple and easy to understand\n- Smaller syntax set than Python in best practices\n- Easy to build large projects from Go's good engineering foundation\n\n### Go: Consistent Complexity\n\nGo was designed from the beginning for systems programming, with a relatively flat learning curve but a higher starting point.\n\n## Differences in Ecosystem Integration Philosophy\n\n### XGo: Multi-Language Ecosystem Fusion\n\nXGo's design formula `XGo := C * Go * Python * JavaScript + Scratch` reflects its philosophy of integrating multiple programming paradigms, aiming to break down language barriers.\n\n### Go: Focus on Go Ecosystem\n\nGo focuses on building and maintaining its own ecosystem. While it can integrate with C, this is not a core design goal.\n\n## Notes\n\nXGo's design philosophy is essentially about \"lowering the programming barrier while maintaining engineering capabilities,\" which fundamentally differs from Go's approach of \"providing simple and efficient tools for systems programming.\" XGo is more like an attempt at \"programming democratization,\" while Go is a product of \"engineering efficiency optimization.\"\n\n### Give a Star! ⭐\n\nIf you like or are using XGo to learn or start your projects, please give it a star. Thanks!\n"
  },
  {
    "path": "doc/z_gop.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage doc\n\nimport (\n\t\"go/doc\"\n\t\"strings\"\n)\n\nconst (\n\tgoptPrefix = \"Gopt_\" // template method\n\tgopoPrefix = \"Gopo_\" // overload function/method\n\tgopxPrefix = \"Gopx_\" // type as parameters function/method\n\tgopPackage = \"GopPackage\"\n\txgoPackage = \"XGoPackage\"\n)\n\nfunc isXGoPackage(in *doc.Package) bool {\n\tfor _, v := range in.Consts {\n\t\tfor _, name := range v.Names {\n\t\t\tif name == gopPackage || name == xgoPackage {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc isGopoConst(name string) bool {\n\treturn strings.HasPrefix(name, gopoPrefix)\n}\n\nfunc hasGopoConst(in *doc.Value) bool {\n\tfor _, name := range in.Names {\n\t\tif isGopoConst(name) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc isOverload(name string) bool {\n\tn := len(name)\n\treturn n > 3 && name[n-3:n-1] == \"__\"\n}\n\n// Func (no _ func name)\n// _Func (with _ func name)\n// TypeName_Method (no _ method name)\n// _TypeName__Method (with _ method name)\nfunc checkTypeMethod(name string) mthd {\n\tif pos := strings.IndexByte(name, '_'); pos >= 0 {\n\t\tif pos == 0 {\n\t\t\tt := name[1:]\n\t\t\tif pos = strings.Index(t, \"__\"); pos <= 0 {\n\t\t\t\treturn mthd{\"\", t} // _Func\n\t\t\t}\n\t\t\treturn mthd{t[:pos], t[pos+2:]} // _TypeName__Method\n\t\t}\n\t\treturn mthd{name[:pos], name[pos+1:]} // TypeName_Method\n\t}\n\treturn mthd{\"\", name} // Func\n}\n"
  },
  {
    "path": "doc/z_test.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage doc\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/doc\"\n\t\"go/format\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestToIndex(t *testing.T) {\n\tif ret := toIndex('a'); ret != 10 {\n\t\tt.Fatal(\"toIndex:\", ret)\n\t}\n\tdefer func() {\n\t\tif e := recover(); e != \"invalid character out of [0-9,a-z]\" {\n\t\t\tt.Fatal(\"panic:\", e)\n\t\t}\n\t}()\n\ttoIndex('A')\n}\n\nfunc TestCheckTypeMethod(t *testing.T) {\n\tif ret := checkTypeMethod(\"_Foo_a\"); ret.typ != \"\" || ret.name != \"Foo_a\" {\n\t\tt.Fatal(\"checkTypeMethod:\", ret)\n\t}\n\tif ret := checkTypeMethod(\"Foo_a\"); ret.typ != \"Foo\" || ret.name != \"a\" {\n\t\tt.Fatal(\"checkTypeMethod:\", ret)\n\t}\n}\n\nfunc TestIsXGoPackage(t *testing.T) {\n\tif isXGoPackage(&doc.Package{}) {\n\t\tt.Fatal(\"isXGoPackage: true?\")\n\t}\n}\n\nfunc TestDocRecv(t *testing.T) {\n\tif _, ok := docRecv(&ast.Field{}); ok {\n\t\tt.Fatal(\"docRecv: ok?\")\n\t}\n}\n\nfunc printVal(parts []string, format string, val any) []string {\n\treturn append(parts, fmt.Sprintf(format, val))\n}\n\nfunc printFuncDecl(parts []string, fset *token.FileSet, decl *ast.FuncDecl) []string {\n\tvar b bytes.Buffer\n\tif e := format.Node(&b, fset, decl); e != nil {\n\t\tpanic(e)\n\t}\n\treturn append(parts, b.String())\n}\n\nfunc printFunc(parts []string, fset *token.FileSet, format string, fn *doc.Func) []string {\n\tparts = printVal(parts, format, fn.Name)\n\tif fn.Recv != \"\" {\n\t\tparts = printVal(parts, \"Recv: %s\", fn.Recv)\n\t}\n\tparts = printVal(parts, \"Doc: %s\", fn.Doc)\n\tparts = printFuncDecl(parts, fset, fn.Decl)\n\treturn parts\n}\n\nfunc printFuncs(parts []string, fset *token.FileSet, fns []*doc.Func) []string {\n\tfor _, fn := range fns {\n\t\tparts = printFunc(parts, fset, \"== Func %s ==\", fn)\n\t}\n\treturn parts\n}\n\nfunc printType(parts []string, fset *token.FileSet, typ *doc.Type) []string {\n\tparts = append(parts, fmt.Sprintf(\"== Type %s ==\", typ.Name))\n\tfor _, fn := range typ.Funcs {\n\t\tparts = printFunc(parts, fset, \"- Func %s -\", fn)\n\t}\n\tfor _, fn := range typ.Methods {\n\t\tparts = printFunc(parts, fset, \"- Method %s -\", fn)\n\t}\n\treturn parts\n}\n\nfunc printTypes(parts []string, fset *token.FileSet, types []*doc.Type) []string {\n\tfor _, typ := range types {\n\t\tparts = printType(parts, fset, typ)\n\t}\n\treturn parts\n}\n\nfunc printPkg(fset *token.FileSet, in *doc.Package) string {\n\tvar parts []string\n\tparts = printFuncs(parts, fset, in.Funcs)\n\tparts = printTypes(parts, fset, in.Types)\n\treturn strings.Join(append(parts, \"\"), \"\\n\")\n}\n\nfunc testPkg(t *testing.T, filename string, src any, expected string) {\n\tt.Helper()\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, filename, src, parser.ParseComments)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tpkg, err := doc.NewFromFiles(fset, []*ast.File{f}, \"foo\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tpkg = Transform(pkg)\n\tif ret := printPkg(fset, pkg); ret != expected {\n\t\tt.Fatalf(\"got:\\n%s\\nexpected:\\n%s\\n\", ret, expected)\n\t}\n}\n\nfunc testFromDir(t *testing.T, sel, relDir string) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = path.Join(dir, relDir)\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"ReadDir failed:\", err)\n\t}\n\tfor _, fi := range fis {\n\t\tname := fi.Name()\n\t\tif strings.HasPrefix(name, \"_\") || !fi.IsDir() || (sel != \"\" && !strings.Contains(name, sel)) {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttestDir := dir + \"/\" + name\n\t\t\tout, e := os.ReadFile(testDir + \"/out.expect\")\n\t\t\tif e != nil {\n\t\t\t\tt.Fatal(e)\n\t\t\t}\n\t\t\ttestPkg(t, testDir+\"/in.go\", nil, string(out))\n\t\t})\n\t}\n}\n\nfunc TestFromTestdata(t *testing.T) {\n\ttestFromDir(t, \"\", \"./_testdata\")\n}\n"
  },
  {
    "path": "doc/z_transform.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage doc\n\nimport (\n\t\"go/ast\"\n\t\"go/doc\"\n\t\"go/token\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype mthd struct {\n\ttyp  string\n\tname string\n}\n\ntype omthd struct {\n\tmthd\n\tidx int\n}\n\ntype typExtra struct {\n\tt       *doc.Type\n\tfuncs   []*doc.Func\n\tmethods []*doc.Func\n}\n\ntype transformCtx struct {\n\toverloadFuncs map[mthd][]omthd // realName => []overloadName\n\ttyps          map[string]*typExtra\n\torders        map[*doc.Func]int\n}\n\nfunc (p *transformCtx) finish(in *doc.Package) {\n\tfor _, ex := range p.typs {\n\t\tif t := ex.t; t != nil {\n\t\t\tt.Funcs = p.mergeFuncs(t.Funcs, ex.funcs)\n\t\t\tt.Methods = p.mergeFuncs(t.Methods, ex.methods)\n\t\t} else {\n\t\t\tin.Funcs = p.mergeFuncs(in.Funcs, ex.funcs)\n\t\t}\n\t}\n}\n\nfunc (p *transformCtx) mergeFuncs(a, b []*doc.Func) []*doc.Func {\n\tif len(b) == 0 {\n\t\treturn a\n\t}\n\ta = append(a, b...)\n\tsort.Slice(a, func(i, j int) bool {\n\t\tfa, fb := a[i], a[j]\n\t\taName, bName := fa.Name, fb.Name\n\t\tif aName == bName {\n\t\t\treturn p.orders[fa] < p.orders[fb]\n\t\t}\n\t\treturn aName < bName\n\t})\n\treturn a\n}\n\nfunc newCtx(in *doc.Package) *transformCtx {\n\ttyps := make(map[string]*typExtra, len(in.Types)+1)\n\ttyps[\"\"] = &typExtra{} // global functions\n\tfor _, t := range in.Types {\n\t\ttyps[t.Name] = &typExtra{t: t}\n\t}\n\treturn &transformCtx{\n\t\toverloadFuncs: make(map[mthd][]omthd),\n\t\ttyps:          typs,\n\t\torders:        make(map[*doc.Func]int),\n\t}\n}\n\nfunc newIdent(name string, in *ast.Ident) *ast.Ident {\n\tret := *in\n\tret.Name = name\n\treturn &ret\n}\n\nfunc newFuncDecl(name string, in *ast.FuncDecl) *ast.FuncDecl {\n\tret := *in\n\tret.Name = newIdent(name, ret.Name)\n\treturn &ret\n}\n\nfunc newMethodDecl(name string, in *ast.FuncDecl) *ast.FuncDecl {\n\tret := *in\n\tif ret.Recv == nil {\n\t\tft := *ret.Type\n\t\tparams := *ft.Params\n\t\tret.Recv = &ast.FieldList{List: params.List[:1]}\n\t\tparams.List = params.List[1:]\n\t\tft.Params = &params\n\t\tret.Type = &ft\n\t}\n\tret.Name = newIdent(name, ret.Name)\n\treturn &ret\n}\n\nfunc docRecv(recv *ast.Field) (_ string, ok bool) {\n\tswitch v := recv.Type.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name, true\n\tcase *ast.StarExpr:\n\t\tif t, ok := v.X.(*ast.Ident); ok {\n\t\t\treturn \"*\" + t.Name, true\n\t\t}\n\t}\n\treturn\n}\n\nfunc newMethod(name string, in *doc.Func) *doc.Func {\n\tret := *in\n\tret.Name = name\n\tret.Decl = newMethodDecl(name, in.Decl)\n\tif recv, ok := docRecv(ret.Decl.Recv.List[0]); ok {\n\t\tret.Recv = recv\n\t}\n\t// TODO(xsw): alias doc - ret.Doc\n\treturn &ret\n}\n\nfunc newFunc(name string, in *doc.Func) *doc.Func {\n\tret := *in\n\tret.Name = name\n\tret.Decl = newFuncDecl(name, in.Decl)\n\t// TODO(xsw): alias doc - ret.Doc\n\treturn &ret\n}\n\nfunc setOrder(ctx *transformCtx, in *doc.Func, order int) *doc.Func {\n\tctx.orders[in] = order\n\treturn in\n}\n\nfunc buildFunc(ctx *transformCtx, overload omthd, in *doc.Func) {\n\tif ex, ok := ctx.typs[overload.typ]; ok {\n\t\tif ex.t != nil { // method\n\t\t\tex.methods = append(ex.methods, setOrder(ctx, newMethod(overload.name, in), overload.idx))\n\t\t} else {\n\t\t\tex.funcs = append(ex.funcs, setOrder(ctx, newFunc(overload.name, in), overload.idx))\n\t\t}\n\t}\n}\n\nfunc toIndex(c byte) int {\n\tif c >= '0' && c <= '9' {\n\t\treturn int(c - '0')\n\t}\n\tif c >= 'a' && c <= 'z' {\n\t\treturn int(c - ('a' - 10))\n\t}\n\tpanic(\"invalid character out of [0-9,a-z]\")\n}\n\nfunc transformFunc(ctx *transformCtx, t *doc.Type, in *doc.Func, method bool) {\n\tvar m mthd\n\tif method {\n\t\tm.typ = t.Name\n\t}\n\tm.name = in.Name\n\tif overloads, ok := ctx.overloadFuncs[m]; ok {\n\t\tfor _, overload := range overloads {\n\t\t\tbuildFunc(ctx, overload, in)\n\t\t}\n\t}\n\tif isOverload(in.Name) {\n\t\torder := toIndex(in.Name[len(in.Name)-1])\n\t\tin.Name = in.Name[:len(in.Name)-3]\n\t\tin.Decl.Name.Name = in.Name\n\t\tctx.orders[in] = order\n\t}\n}\n\nfunc transformFuncs(ctx *transformCtx, t *doc.Type, in []*doc.Func, method bool) {\n\tfor _, f := range in {\n\t\ttransformFunc(ctx, t, f, method)\n\t}\n}\n\nfunc transformTypes(ctx *transformCtx, in []*doc.Type) {\n\tfor _, t := range in {\n\t\ttransformFuncs(ctx, t, t.Funcs, false)\n\t\ttransformFuncs(ctx, t, t.Methods, true)\n\t}\n}\n\nfunc transformGopo(ctx *transformCtx, name, val string) {\n\toverload := checkTypeMethod(name[len(gopoPrefix):])\n\tparts := strings.Split(val, \",\")\n\tfor idx, part := range parts {\n\t\tif part == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tvar real mthd\n\t\tif part[0] == '.' {\n\t\t\treal = mthd{overload.typ, part[1:]}\n\t\t} else {\n\t\t\treal = mthd{\"\", part}\n\t\t}\n\t\tctx.overloadFuncs[real] = append(ctx.overloadFuncs[real], omthd{overload, idx})\n\t}\n}\n\nfunc transformConstSpec(ctx *transformCtx, vspec *ast.ValueSpec) {\n\tname := vspec.Names[0].Name\n\tif isGopoConst(name) {\n\t\tif lit, ok := vspec.Values[0].(*ast.BasicLit); ok {\n\t\t\tif lit.Kind == token.STRING {\n\t\t\t\tif val, e := strconv.Unquote(lit.Value); e == nil {\n\t\t\t\t\ttransformGopo(ctx, name, val)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc transformConst(ctx *transformCtx, in *doc.Value) {\n\tif hasGopoConst(in) {\n\t\tfor _, spec := range in.Decl.Specs {\n\t\t\tvspec := spec.(*ast.ValueSpec)\n\t\t\ttransformConstSpec(ctx, vspec)\n\t\t}\n\t}\n}\n\nfunc transformConsts(ctx *transformCtx, in []*doc.Value) {\n\tfor _, v := range in {\n\t\ttransformConst(ctx, v)\n\t}\n}\n\n// Transform converts a Go doc package to an XGo doc package.\nfunc Transform(in *doc.Package) *doc.Package {\n\tif isXGoPackage(in) {\n\t\tctx := newCtx(in)\n\t\ttransformConsts(ctx, in.Consts)\n\t\ttransformFuncs(ctx, nil, in.Funcs, false)\n\t\ttransformTypes(ctx, in.Types)\n\t\tctx.finish(in)\n\t}\n\treturn in\n}\n"
  },
  {
    "path": "dql/README.md",
    "content": "DQL - DOM Query Language\n=====\n\nDQL is a universal, expressive query language for structured and tree-shaped data, built for [XGo](https://github.com/goplus/xgo). It provides a unified query interface across JSON, XML, HTML, ASTs, file systems, and any other domain that can be modeled as a tree.\n\n---\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Supported Domains](#supported-domains)\n- [Core Concepts](#core-concepts)\n- [Syntax Reference](#syntax-reference)\n- [Query Examples](#query-examples)\n  - [JSON](#json)\n  - [HTML](#html)\n  - [XGo AST](#xgo-ast)\n  - [File System](#file-system)\n- [Error Handling](#error-handling)\n- [Performance and Caching](#performance-and-caching)\n- [Implementing a NodeSet](#implementing-a-nodeset)\n- [Standard Errors](#standard-errors)\n\n---\n\n## Overview\n\nDQL brings a concise, chainable query syntax to XGo. Think of it as XPath or CSS selectors — but designed for XGo's type system, with first-class support for lazy evaluation, error propagation, and domain-specific adaptations.\n\n```go\n// Find all hyperlinks in an HTML document\nfor a in doc.**.a {\n    if url := a.$href; url != \"\" {\n        echo url\n    }\n}\n\n// Filter JSON records\nfor animal in doc.animals.*@($class == \"zebra\") {\n    echo animal.$at\n}\n\n// Walk the file system\nfor e in fs`.`.**.file.match(\"*.xgo\") {\n    echo e.path\n}\n```\n\n---\n\n## Supported Domains\n\nDQL is not tied to any single format. Any data that can be represented as a tree can be queried with DQL:\n\n- **JSON** — query fields, arrays, and nested objects\n- **YAML** — same model as JSON\n- **HTML / XML** — traverse elements, attributes, and text\n- **Go / XGo AST** — inspect and filter syntax trees\n- **File System** — walk directories, match files by name or extension\n- **Any custom tree structure** — implement the NodeSet interface\n\n---\n\n## Core Concepts\n\n### NodeSet\n\nEverything in DQL is a `NodeSet`. Queries consume a NodeSet and produce a new NodeSet. The entry point is typically the root document node, called `doc`.\n\n```go\ndoc NodeSet  // root of the query\n```\n\nNodeSets are **lazily evaluated** — they describe a query plan that is executed only when you actually iterate or read data from them. This keeps memory usage low and allows complex query composition without overhead.\n\n### Child Access\n\nNavigate the tree with dot notation:\n\n```\nns.name          // direct child named \"name\"\nns.\"elem-name\"   // child with a hyphenated name\nns.*             // all direct children (wildcard)\nns.**.name       // all descendants named \"name\" (deep query)\nns.**.*          // all descendants\nns[0]            // first element of an array/list node (0-based)\n```\n\n### Filtering (Select)\n\nFilter nodes using `@`:\n\n```\nns@name          // keep only nodes named \"name\"\nns@(condExpr)    // keep nodes matching a boolean expression\nns@fn(args)      // keep nodes where fn(...) returns true\n```\n\nExamples:\n\n```go\ndoc.users.*@($role == \"admin\")         // users whose role attribute is \"admin\"\ndoc.users.*@($age.int?:100 < 18)       // users under 18\ndoc.**.div@hasClass(\"widget\")          // all divs with a given CSS class\n```\n\n### Attributes\n\nRead node-level data with `$`:\n\n```go\nval := ns.$name              // single-value: zero value on error\nval, err := ns.$name         // dual-value: explicit error handling\nval := ns.$name!             // panic on error\nval := ns.$name?:\"default\"   // use a fallback value on error\nval := ns.$\"attr-name\"       // hyphenated attribute names\n```\n\nAttribute access always operates on the **first node** in the NodeSet. To collect an attribute from every node, use a list comprehension:\n\n```go\nnames := [user.$name for user in doc.users.*]\n```\n\n### Methods\n\nMethods provide access to computed or typed data:\n\n```go\ntext := div.text             // call Text() method (single-value)\ntext, err := div.text        // dual-value\ncount := div.count?:0        // explicit default for numeric methods\nage, err := node.$age.int    // numeric methods require dual-value\n```\n\nMethods prefixed with `_` map to `XGo_`-prefixed implementations:\n\n```go\nnode._text    // → node.XGo_text()\nnode._count   // → node.XGo_count()\nnode._first   // → node.XGo_first()\n```\n\n> **Note on numeric methods**: Methods that return numeric types (like `int`, `float`, `count`) do **not** have a single-value form, to prevent silent bugs where a zero default masks a real error. Always use the dual-value form or an explicit `?:` default.\n\n---\n\n## Syntax Reference\n\n| Syntax | Meaning |\n|---|---|\n| `ns.name` | Direct child named `name` |\n| `ns.\"elem-name\"` | Direct child with special characters |\n| `ns.*` | All direct children |\n| `ns[n]` | Child at index `n` (0-based) |\n| `ns.**.name` | Deep search for `name` |\n| `ns.**.*` | All descendants |\n| `ns@name` | Filter: keep nodes named `name` |\n| `ns@(expr)` | Filter: keep nodes matching expression |\n| `ns.$name` | Attribute (single-value, zero on error) |\n| `val, err := ns.$name` | Attribute (dual-value) |\n| `ns.$name!` | Attribute, panic on error |\n| `ns.$name?:def` | Attribute, custom default on error |\n| `ns.method(args)` | Method call |\n| `ns._method` | Method call via `XGo_method()` |\n| `ns.all` / `ns._all` | Materialize and cache all results |\n| `ns.one` / `ns._one` | First match, early termination |\n| `ns.single` / `ns._single` | Exactly one match (validates uniqueness) |\n\n---\n\n## Query Examples\n\n### JSON\n\n```go\ndoc := json`{\n    \"animals\": [\n        {\"class\": \"gopher\", \"at\": \"Line 1\"},\n        {\"class\": \"armadillo\", \"at\": \"Line 2\"},\n        {\"class\": \"zebra\", \"at\": \"Line 3\"},\n        {\"class\": \"unknown\", \"at\": \"Line 4\"},\n        {\"class\": \"gopher\", \"at\": \"Line 5\"},\n        {\"class\": \"bee\", \"at\": \"Line 6\"},\n        {\"class\": \"gopher\", \"at\": \"Line 7\"},\n        {\"class\": \"zebra\", \"at\": \"Line 8\"}\n    ]\n}`!\n\n// Iterate filtered records\nfor animal in doc.animals.*@($class == \"zebra\") {\n    echo animal.$at\n}\n\n// Collect all names into a list\nnames := [a.$class for a in doc.animals.*]\n\n// Access by index\nfirst := doc.animals[0].$class\n\n// Find unique admin (validate exactly one exists)\nuser := doc.users.*@($role == \"admin\")._single\nname, err := user.$name\n```\n\n### HTML\n\n```go\nimport \"os\"\nimport \"github.com/goplus/xgo/dql/html\"\n\ndoc := html.source(os.Args[1])\n\n// Print all hyperlink URLs\nfor a in doc.**.a {\n    if url := a.$href; url != \"\" {\n        echo url\n    }\n}\n\n// Collect text from all paragraphs\ntexts := [p.text for p in doc.**.p]\n\n// Find the first element with a specific class (early termination)\nwidget := doc.**.*@($class == \"widget\").one\nid := widget.$id\n```\n\n### XGo AST\n\n```go\ndoc := xgo`\nx, y := \"Hi\", 123\necho x\nprint y\n`!\n\n// Find all expression statements\nstmts := doc.shadowEntry.body.list.*@(self.class == \"ExprStmt\")\n\n// Extract function names from call expressions\nfor fn in stmts.x@(self.class == \"CallExpr\").fun@(self.class == \"Ident\") {\n    echo fn.$name\n}\n```\n\n### File System\n\n```go\n// Walk current directory and print all .xgo files\nfor e in fs`.`.**.file.match(\"*.xgo\") {\n    echo e.path\n}\n\n// Collect all Go source file names\nnames := [f.$name for f in root.**.file@match(\"*.go\", $name)]\n```\n\n---\n\n## Error Handling\n\nDQL integrates with XGo's error handling operators and follows a consistent two-version design for attribute and method access.\n\n### Two-Version Design\n\nMost accessors come in two forms:\n\n```go\n// Single-value: convenient, returns zero on error\nname := node.$name\n\n// Dual-value: explicit, lets you inspect the error\nname, err := node.$name\n```\n\nAdditionally, XGo's error operators work directly on DQL expressions:\n\n```go\nname := node.$name!           // panic if error\nname := node.$name?:\"N/A\"     // custom fallback\n```\n\n### NodeSet Error State\n\nSome operations return a NodeSet that internally carries an error (e.g., `_one` when no match is found, `_single` when zero or multiple matches are found). This error propagates automatically to any subsequent attribute or method access:\n\n```go\nadmin := doc.users.*@($role == \"admin\")._one\n\n// All of these reflect the internal ErrNotFound:\nname := admin.$name              // returns zero value\nname, err := admin.$name         // err == dql.ErrNotFound\nname := admin.$name!             // panics\n```\n\nAn empty NodeSet is still a valid NodeSet — loops simply don't execute:\n\n```go\nfor user in admin {\n    // Not reached if admin has ErrNotFound\n}\n```\n\n---\n\n## Performance and Caching\n\nDQL uses **lazy evaluation** by default. Queries are not executed until you iterate or read from the NodeSet. This is memory-efficient and enables query composition, but means a NodeSet re-executes its query each time it is accessed.\n\nUse cache control methods to avoid repeated execution:\n\n### `_all` / `all` — Materialize Everything\n\nExecutes the query and caches all results. Use when you need to access the same NodeSet multiple times.\n\n```go\nusers := doc.users.*@($active == true)._all\n\nnames  := [u.$name  for u in users]   // uses cache\nemails := [u.$email for u in users]   // uses cache, no re-query\n```\n\n### `_one` / `one` — First Match, Early Exit\n\nStops after finding the first matching node. Use when you know (or assume) there is at most one result.\n\n```go\nadmin := doc.users.*@($role == \"admin\")._one\nname := admin.$name   // ErrNotFound if no match\n```\n\n### `_single` / `single` — Uniqueness Validation\n\nValidates that exactly one node matches. Returns `ErrMultipleEntities` if more than one is found.\n\n```go\nuser := doc.users.*@($id == 12345)._single\nname, err := user.$name\n// err may be ErrNotFound or ErrMultipleEntities\n```\n\n### Choosing the Right Method\n\n| Need | Use |\n|---|---|\n| Multiple accesses to same results | `_all` |\n| Expect one result, want fast exit | `_one` |\n| Require exactly one result | `_single` |\n| Single-pass iteration | no cache (default lazy) |\n\n---\n\n## Implementing a NodeSet\n\nTo make a custom data source queryable with DQL, implement the following interface on your NodeSet type.\n\n### Required Methods\n\n```go\n// Iteration\nfunc (ns NodeSet) XGo_Enum() iter.Seq[NodeSet]\n\n// Extract first node\nfunc (ns NodeSet) XGo_first() (NodeType, error)\n\n// Type conversion from raw sequence\nfunc NodeSet_Cast(seq func(func(NodeType) bool)) NodeSet\n```\n\n### Child Navigation\n\n```go\nfunc (ns NodeSet) XGo_Elem(name string) NodeSet    // ns.name\nfunc (ns NodeSet) XGo_Child() NodeSet              // ns.*\nfunc (ns NodeSet) XGo_Index(index int) NodeSet     // ns[n]\nfunc (ns NodeSet) XGo_Any(name string) NodeSet     // ns.**.name  (\"\" = all)\n```\n\n### Filtering\n\n```go\nfunc (ns NodeSet) XGo_Select(name string) NodeSet  // ns@name\n// Conditional filters are compiler-generated using NodeSet_Cast\n```\n\n### Attribute Access\n\n```go\nfunc (ns NodeSet) XGo_Attr__0(name string) ValueType          // single-value\nfunc (ns NodeSet) XGo_Attr__1(name string) (ValueType, error) // dual-value\n```\n\n### Cache Control\n\nProvide either general-form (`XGo_all`, `XGo_one`, `XGo_single`) or domain-specific form (`All`, `One`, `Single`) methods:\n\n```go\nfunc (ns NodeSet) XGo_all()    NodeSet  // or All()\nfunc (ns NodeSet) XGo_one()    NodeSet  // or One()\nfunc (ns NodeSet) XGo_single() NodeSet  // or Single()\n```\n\nDomain types (`NodeType`, `ValueType`, `IndexType`) are customized per implementation.\n\n---\n\n## Standard Errors\n\nThe `dql` package defines two standard sentinel errors:\n\n```go\npackage dql\n\nvar (\n    ErrNotFound         = errors.New(\"node not found\")\n    ErrMultipleEntities = errors.New(\"multiple entities found, expected single\")\n)\n```\n\nThese are returned by `_one` (when no match exists) and `_single` (when zero or more than one match exists), and propagate through the NodeSet to any subsequent attribute or method call.\n"
  },
  {
    "path": "dql/dql.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage dql\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nconst (\n\tXGoPackage = true\n)\n\nvar (\n\tErrNotFound      = errors.New(\"entity not found\")\n\tErrMultiEntities = errors.New(\"too many entities found\")\n)\n\n// -----------------------------------------------------------------------------\n\n// NopIter is a no-operation iterator that yields no values.\nfunc NopIter[T any](yield func(T) bool) {}\n\n// -----------------------------------------------------------------------------\n\n// First retrieves the first item from the provided sequence. If the sequence is\n// empty, it returns ErrNotFound.\nfunc First[T any, Seq ~func(func(T) bool)](seq Seq) (ret T, err error) {\n\terr = ErrNotFound\n\tseq(func(item T) bool {\n\t\tret, err = item, nil\n\t\treturn false\n\t})\n\treturn\n}\n\n// Single retrieves a single item from the provided sequence. If the sequence is\n// empty, it returns ErrNotFound. If the sequence contains more than one item, it\n// returns ErrMultiEntities.\nfunc Single[T any, Seq ~func(func(T) bool)](seq Seq) (ret T, err error) {\n\terr = ErrNotFound\n\tfirst := true\n\tseq(func(item T) bool {\n\t\tif first {\n\t\t\tret, err = item, nil\n\t\t\tfirst = false\n\t\t\treturn true\n\t\t}\n\t\terr = ErrMultiEntities\n\t\treturn false\n\t})\n\treturn\n}\n\n// Collect retrieves all items from the provided sequence.\nfunc Collect[T any, Seq ~func(func(T) bool)](seq Seq) []T {\n\tret := make([]T, 0, 8)\n\tseq(func(item T) bool {\n\t\tret = append(ret, item)\n\t\treturn true\n\t})\n\treturn ret\n}\n\n// -----------------------------------------------------------------------------\n\n// Int parses the given string as an integer, removing any commas and trimming\n// whitespace.\nfunc Int(text string) (int, error) {\n\treturn strconv.Atoi(strings.ReplaceAll(strings.TrimSpace(text), \",\", \"\"))\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/fetcher/fetch.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage fetcher\n\nimport (\n\t\"errors\"\n\t\"reflect\"\n\t\"sort\"\n\n\t\"github.com/goplus/xgo/dql/html\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Conv defines a converter function type.\n// func(input any, doc html.NodeSet) <any-object>\n// A converter function converts a html source to an object.\ntype Conv = any\n\n// convert converts a html source to an object.\nfunc convert(conv reflect.Value, input, source any) any {\n\tdoc := reflect.ValueOf(html.Source(source))\n\tout := conv.Call([]reflect.Value{reflect.ValueOf(input), doc})\n\treturn out[0].Interface()\n}\n\n// -----------------------------------------------------------------------------\n\nvar (\n\tErrUnknownFetchType = errors.New(\"unknown fetch type\")\n)\n\n// URL generates a URL from an input by registered converter.\nfunc URL(fetchType string, input any) (string, error) {\n\tfi, ok := convs[fetchType]\n\tif !ok {\n\t\treturn \"\", ErrUnknownFetchType\n\t}\n\treturn fi.URL(input), nil\n}\n\n// Do fetches HTML content from an input and converts it to an object by\n// registered converter.\nfunc Do(fetchType string, input any) (any, error) {\n\tfi, ok := convs[fetchType]\n\tif !ok {\n\t\treturn nil, ErrUnknownFetchType\n\t}\n\turl := fi.URL(input)\n\treturn convert(fi.Conv, input, url), nil\n}\n\n// From reads HTML content from a source and converts it to an object by\n// registered converter. It is used when HTML content is already available.\nfunc From(fetchType string, input, source any) (any, error) {\n\tfi, ok := convs[fetchType]\n\tif !ok {\n\t\treturn nil, ErrUnknownFetchType\n\t}\n\treturn convert(fi.Conv, input, source), nil\n}\n\n// fetchInfo represents a fetch information, including convert function\n// and URL function that generates URL from input.\ntype fetchInfo struct {\n\tConv reflect.Value\n\tURL  func(input any) string\n}\n\nvar (\n\tconvs = map[string]fetchInfo{}\n)\n\n// Register registers a fetchType with a convert function.\n// The urlOf function generates URL from input.\n// func conv(input any, doc html.NodeSet) <any-object>\nfunc Register(fetchType string, conv Conv, urlOf func(input any) string) {\n\tvConv := reflect.ValueOf(conv)\n\tconvs[fetchType] = fetchInfo{vConv, urlOf}\n}\n\n// List returns a list of registered fetch types.\nfunc List() []string {\n\tkeys := make([]string, 0, len(convs))\n\tfor k := range convs {\n\t\tkeys = append(keys, k)\n\t}\n\tsort.Strings(keys)\n\treturn keys\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/fetcher/github.com/issueTask/issueTask.xgo",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage issueTask\n\nimport (\n\t\"github.com/goplus/xgo/dql/html\"\n\t\"github.com/goplus/xgo/dql/fetcher\"\n)\n\n// Task represents a GitHub issue task item.\ntype Task struct {\n\tDesc string `json:\"desc\"`\n\tDone bool   `json:\"done\"`\n}\n\n// Result represents the result of issue task fetcher.\ntype Result struct {\n\tIssue string `json:\"issue\"` // goplus/llgo#642\n\tTasks []Task `json:\"tasks\"`\n}\n\n// New extracts issue tasks from the given document and returns the Result.\nfunc New(input any, doc html.NodeSet) Result {\n\tissue := input.(string)\n\ttaskList := doc.**.ul@($class == \"contains-task-list\").one\n\ttasks := [Task{li.text, li.firstElementChild.hasAttr(\"checked\")} for li in taskList.li]\n\treturn {issue, tasks}\n}\n\n// URL returns the URL from the input.\n// Input can be either a full github issue URL or a shorthand format\n// like \"goplus/llgo#642\".\nfunc URL(input any) string {\n\tissue := input.(string)\n\tif issue.hasPrefix(\"https://github.com/\") {\n\t\treturn issue\n\t}\n\treturn \"https://github.com/\" + issue.replace(\"#\", \"/issues/\", 1)\n}\n\nfunc init() {\n\tfetcher.register(\"github.com/issueTask\", New, URL)\n}\n"
  },
  {
    "path": "dql/fetcher/github.com/issueTask/xgo_autogen.go",
    "content": "// Code generated by xgo (XGo); DO NOT EDIT.\n\npackage issueTask\n\nimport (\n\t\"github.com/goplus/xgo/dql/fetcher\"\n\t\"github.com/goplus/xgo/dql/html\"\n\t\"strings\"\n)\n\nconst XGoPackage = \"github.com/goplus/xgo/dql/html\"\nconst _ = true\n// Task represents a GitHub issue task item.\ntype Task struct {\n\tDesc string `json:\"desc\"`\n\tDone bool   `json:\"done\"`\n}\n// Result represents the result of issue task fetcher.\ntype Result struct {\n\tIssue string `json:\"issue\"`\n\tTasks []Task `json:\"tasks\"`\n}\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:36:1\n// New extracts issue tasks from the given document and returns the Result.\nfunc New(input interface{}, doc html.NodeSet) Result {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:38:1\n\tissue := input.(string)\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:39:1\n\ttaskList := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:39:1\n\t\tdoc.XGo_Any(\"ul\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:39:1\n\t\t\tif self.XGo_Attr__0(\"class\") == \"contains-task-list\" {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:39:1\n\t\t\t\tif\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:39:1\n\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:39:1\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:39:1\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:39:1\n\t\t\treturn true\n\t\t})\n\t}).One()\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:40:1\n\ttasks := func() (_xgo_ret []Task) {\n\t\tfor\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:40:1\n\t\tli := range taskList.XGo_Elem(\"li\").XGo_Enum() {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:40:1\n\t\t\t_xgo_ret = append(_xgo_ret, Task{li.Text__0(), li.FirstElementChild().HasAttr(\"checked\")})\n\t\t}\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:40:1\n\t\treturn\n\t}()\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:41:1\n\treturn Result{issue, tasks}\n}\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:44:1\n// URL returns the URL from the input.\n// Input can be either a full github issue URL or a shorthand format\n// like \"goplus/llgo#642\".\nfunc URL(input interface{}) string {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:48:1\n\tissue := input.(string)\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:49:1\n\tif strings.HasPrefix(issue, \"https://github.com/\") {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:50:1\n\t\treturn issue\n\t}\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:52:1\n\treturn \"https://github.com/\" + strings.Replace(issue, \"#\", \"/issues/\", 1)\n}\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:55:1\nfunc init() {\n//line dql/fetcher/github.com/issueTask/issueTask.xgo:56:1\n\tfetcher.Register(\"github.com/issueTask\", New, URL)\n}\n"
  },
  {
    "path": "dql/fetcher/github.com/repoList/repoList.xgo",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage repoList\n\nimport (\n\t\"github.com/goplus/xgo/dql/html\"\n\t\"github.com/goplus/xgo/dql/fetcher\"\n)\n\n// Repo is the information of a repository.\ntype Repo struct {\n\tRepo       string `json:\"repo\"`\n\tForkedFrom string `json:\"forkedFrom\"`\n\tTitle      string `json:\"title\"`\n\tLanguage   string `json:\"language\"`\n\tUpdateTime string `json:\"updateTime\"`\n\tForks      int    `json:\"forks\"`\n}\n\n// Result is the result of fetching a repository list page.\ntype Result struct {\n\tUser  string `json:\"user\"` // github username\n\tRepos []Repo `json:\"repos\"`\n\tNext  string `json:\"next\"`\n}\n\nfunc newRepo(node html.NodeSet) Repo {\n\taRepo := node.**.a@($itemprop == \"name codeRepository\").one\n\trepo := aRepo.$href\n\troot := aRepo.parentN(3).one\n\tforkedFrom := root.**.span.**.textNode@(self.value.trimSpace == \"Forked from\").nextSibling@a.$href\n\ttitle := root.**.p@($itemprop == \"description\").text\n\tlanguage := root.**.span@($itemprop == \"programmingLanguage\").text\n\tupdateTime := root.**.\"relative-time\".$datetime\n\tforks := root.**.a@($href == repo+\"/network/members\").int?:0\n\treturn {\n\t\tRepo:       repo,\n\t\tForkedFrom: forkedFrom,\n\t\tTitle:      title,\n\t\tLanguage:   language,\n\t\tUpdateTime: updateTime,\n\t\tForks:      forks,\n\t}\n}\n\n// New extracts the repository information from the given HTML document and\n// returns the Result.\nfunc New(input any, doc html.NodeSet) Result {\n\tuser := input.(string)\n\tdivRepos := doc.**.div@($id == \"user-repositories-list\").one\n\trepos := [newRepo(x) for x in divRepos.ul.li]\n\tdivPaginate := doc.**.div@($class == \"paginate-container\").one\n\tnext := divPaginate.**.a@(self.text == \"Next\").$href\n\treturn {user, repos, next}\n}\n\n// URL returns the URL from the input.\n// Input is expected to be a GitHub username, and the URL will be the user's\n// repository list page.\nfunc URL(input any) string {\n\treturn \"https://github.com/\" + input.(string)\n}\n\nfunc init() {\n\tfetcher.register(\"github.com/repoList\", New, URL)\n}\n"
  },
  {
    "path": "dql/fetcher/github.com/repoList/xgo_autogen.go",
    "content": "// Code generated by xgo (XGo); DO NOT EDIT.\n\npackage repoList\n\nimport (\n\t\"github.com/goplus/xgo/dql/fetcher\"\n\t\"github.com/goplus/xgo/dql/html\"\n\t\"strings\"\n)\n\nconst XGoPackage = \"github.com/goplus/xgo/dql/html\"\nconst _ = true\n// Repo is the information of a repository.\ntype Repo struct {\n\tRepo       string `json:\"repo\"`\n\tForkedFrom string `json:\"forkedFrom\"`\n\tTitle      string `json:\"title\"`\n\tLanguage   string `json:\"language\"`\n\tUpdateTime string `json:\"updateTime\"`\n\tForks      int    `json:\"forks\"`\n}\n// Result is the result of fetching a repository list page.\ntype Result struct {\n\tUser  string `json:\"user\"`\n\tRepos []Repo `json:\"repos\"`\n\tNext  string `json:\"next\"`\n}\n//line dql/fetcher/github.com/repoList/repoList.xgo:41:1\nfunc newRepo(node html.NodeSet) Repo {\n//line dql/fetcher/github.com/repoList/repoList.xgo:42:1\n\taRepo := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:42:1\n\t\tnode.XGo_Any(\"a\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/github.com/repoList/repoList.xgo:42:1\n\t\t\tif self.XGo_Attr__0(\"itemprop\") == \"name codeRepository\" {\n//line dql/fetcher/github.com/repoList/repoList.xgo:42:1\n\t\t\t\tif\n//line dql/fetcher/github.com/repoList/repoList.xgo:42:1\n\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/github.com/repoList/repoList.xgo:42:1\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:42:1\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:42:1\n\t\t\treturn true\n\t\t})\n\t}).One()\n//line dql/fetcher/github.com/repoList/repoList.xgo:43:1\n\trepo := aRepo.XGo_Attr__0(\"href\")\n//line dql/fetcher/github.com/repoList/repoList.xgo:44:1\n\troot := aRepo.ParentN(3).One()\n//line dql/fetcher/github.com/repoList/repoList.xgo:45:1\n\tforkedFrom := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:45:1\n\t\troot.XGo_Any(\"span\").XGo_Any(\"textNode\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/github.com/repoList/repoList.xgo:45:1\n\t\t\tif strings.TrimSpace(self.Value__0()) == \"Forked from\" {\n//line dql/fetcher/github.com/repoList/repoList.xgo:45:1\n\t\t\t\tif\n//line dql/fetcher/github.com/repoList/repoList.xgo:45:1\n\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/github.com/repoList/repoList.xgo:45:1\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:45:1\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:45:1\n\t\t\treturn true\n\t\t})\n\t}).NextSibling().XGo_Select(\"a\").XGo_Attr__0(\"href\")\n//line dql/fetcher/github.com/repoList/repoList.xgo:46:1\n\ttitle := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:46:1\n\t\troot.XGo_Any(\"p\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/github.com/repoList/repoList.xgo:46:1\n\t\t\tif self.XGo_Attr__0(\"itemprop\") == \"description\" {\n//line dql/fetcher/github.com/repoList/repoList.xgo:46:1\n\t\t\t\tif\n//line dql/fetcher/github.com/repoList/repoList.xgo:46:1\n\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/github.com/repoList/repoList.xgo:46:1\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:46:1\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:46:1\n\t\t\treturn true\n\t\t})\n\t}).Text__0()\n//line dql/fetcher/github.com/repoList/repoList.xgo:47:1\n\tlanguage := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:47:1\n\t\troot.XGo_Any(\"span\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/github.com/repoList/repoList.xgo:47:1\n\t\t\tif self.XGo_Attr__0(\"itemprop\") == \"programmingLanguage\" {\n//line dql/fetcher/github.com/repoList/repoList.xgo:47:1\n\t\t\t\tif\n//line dql/fetcher/github.com/repoList/repoList.xgo:47:1\n\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/github.com/repoList/repoList.xgo:47:1\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:47:1\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:47:1\n\t\t\treturn true\n\t\t})\n\t}).Text__0()\n//line dql/fetcher/github.com/repoList/repoList.xgo:48:1\n\tupdateTime := root.XGo_Any(\"relative-time\").XGo_Attr__0(\"datetime\")\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\tforks := func() (_xgo_ret int) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\tvar _xgo_err error\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\t_xgo_ret, _xgo_err = html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\t\troot.XGo_Any(\"a\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\t\t\tif self.XGo_Attr__0(\"href\") == repo+\"/network/members\" {\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\t\t\t\tif\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\t\t\treturn true\n\t\t\t})\n\t\t}).Int()\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\tif _xgo_err != nil {\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\t\treturn 0\n\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:49:1\n\t\treturn\n\t}()\n//line dql/fetcher/github.com/repoList/repoList.xgo:50:1\n\treturn Repo{Repo: repo, ForkedFrom: forkedFrom, Title: title, Language: language, UpdateTime: updateTime, Forks: forks}\n}\n//line dql/fetcher/github.com/repoList/repoList.xgo:60:1\n// New extracts the repository information from the given HTML document and\n// returns the Result.\nfunc New(input interface{}, doc html.NodeSet) Result {\n//line dql/fetcher/github.com/repoList/repoList.xgo:63:1\n\tuser := input.(string)\n//line dql/fetcher/github.com/repoList/repoList.xgo:64:1\n\tdivRepos := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:64:1\n\t\tdoc.XGo_Any(\"div\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/github.com/repoList/repoList.xgo:64:1\n\t\t\tif self.XGo_Attr__0(\"id\") == \"user-repositories-list\" {\n//line dql/fetcher/github.com/repoList/repoList.xgo:64:1\n\t\t\t\tif\n//line dql/fetcher/github.com/repoList/repoList.xgo:64:1\n\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/github.com/repoList/repoList.xgo:64:1\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:64:1\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:64:1\n\t\t\treturn true\n\t\t})\n\t}).One()\n//line dql/fetcher/github.com/repoList/repoList.xgo:65:1\n\trepos := func() (_xgo_ret []Repo) {\n\t\tfor\n//line dql/fetcher/github.com/repoList/repoList.xgo:65:1\n\t\tx := range divRepos.XGo_Elem(\"ul\").XGo_Elem(\"li\").XGo_Enum() {\n//line dql/fetcher/github.com/repoList/repoList.xgo:65:1\n\t\t\t_xgo_ret = append(_xgo_ret, newRepo(x))\n\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:65:1\n\t\treturn\n\t}()\n//line dql/fetcher/github.com/repoList/repoList.xgo:66:1\n\tdivPaginate := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:66:1\n\t\tdoc.XGo_Any(\"div\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/github.com/repoList/repoList.xgo:66:1\n\t\t\tif self.XGo_Attr__0(\"class\") == \"paginate-container\" {\n//line dql/fetcher/github.com/repoList/repoList.xgo:66:1\n\t\t\t\tif\n//line dql/fetcher/github.com/repoList/repoList.xgo:66:1\n\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/github.com/repoList/repoList.xgo:66:1\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:66:1\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:66:1\n\t\t\treturn true\n\t\t})\n\t}).One()\n//line dql/fetcher/github.com/repoList/repoList.xgo:67:1\n\tnext := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:67:1\n\t\tdivPaginate.XGo_Any(\"a\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/github.com/repoList/repoList.xgo:67:1\n\t\t\tif self.Text__0() == \"Next\" {\n//line dql/fetcher/github.com/repoList/repoList.xgo:67:1\n\t\t\t\tif\n//line dql/fetcher/github.com/repoList/repoList.xgo:67:1\n\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/github.com/repoList/repoList.xgo:67:1\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/github.com/repoList/repoList.xgo:67:1\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//line dql/fetcher/github.com/repoList/repoList.xgo:67:1\n\t\t\treturn true\n\t\t})\n\t}).XGo_Attr__0(\"href\")\n//line dql/fetcher/github.com/repoList/repoList.xgo:68:1\n\treturn Result{user, repos, next}\n}\n//line dql/fetcher/github.com/repoList/repoList.xgo:71:1\n// URL returns the URL from the input.\n// Input is expected to be a GitHub username, and the URL will be the user's\n// repository list page.\nfunc URL(input interface{}) string {\n//line dql/fetcher/github.com/repoList/repoList.xgo:75:1\n\treturn \"https://github.com/\" + input.(string)\n}\n//line dql/fetcher/github.com/repoList/repoList.xgo:78:1\nfunc init() {\n//line dql/fetcher/github.com/repoList/repoList.xgo:79:1\n\tfetcher.Register(\"github.com/repoList\", New, URL)\n}\n"
  },
  {
    "path": "dql/fetcher/hrefs/hrefs.xgo",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage hrefs\n\nimport (\n\t\"github.com/goplus/xgo/dql/html\"\n\t\"github.com/goplus/xgo/dql/fetcher\"\n)\n\n// Result represents the result of hrefs fetcher.\ntype Result struct {\n\tURL   string   `json:\"url,omitempty\"`\n\tHrefs []string `json:\"hrefs,omitempty\"`\n}\n\n// New extracts hrefs from the given document and returns the Result.\nfunc New(input any, doc html.NodeSet) Result {\n\threfs := [url for a in doc.**.a if url := a.$href; url != \"\"]\n\treturn {input.(string), hrefs}\n}\n\n// URL returns the URL from the input.\n// Input is expected to be a URL, and the fetcher will extract all hrefs\n// from the page at that URL.\nfunc URL(input any) string {\n\treturn input.(string)\n}\n\nfunc init() {\n\tfetcher.register(\"hrefs\", New, URL)\n}\n"
  },
  {
    "path": "dql/fetcher/hrefs/xgo_autogen.go",
    "content": "// Code generated by xgo (XGo); DO NOT EDIT.\n\npackage hrefs\n\nimport (\n\t\"github.com/goplus/xgo/dql/fetcher\"\n\t\"github.com/goplus/xgo/dql/html\"\n)\n\nconst XGoPackage = \"github.com/goplus/xgo/dql/html\"\nconst _ = true\n// Result represents the result of hrefs fetcher.\ntype Result struct {\n\tURL   string   `json:\"url,omitempty\"`\n\tHrefs []string `json:\"hrefs,omitempty\"`\n}\n//line dql/fetcher/hrefs/hrefs.xgo:30:1\n// New extracts hrefs from the given document and returns the Result.\nfunc New(input interface{}, doc html.NodeSet) Result {\n//line dql/fetcher/hrefs/hrefs.xgo:32:1\n\threfs := func() (_xgo_ret []string) {\n\t\tfor\n//line dql/fetcher/hrefs/hrefs.xgo:32:1\n\t\ta := range doc.XGo_Any(\"a\").XGo_Enum() {\n//line dql/fetcher/hrefs/hrefs.xgo:32:1\n\t\t\tif\n//line dql/fetcher/hrefs/hrefs.xgo:32:1\n\t\t\turl := a.XGo_Attr__0(\"href\"); url != \"\" {\n//line dql/fetcher/hrefs/hrefs.xgo:32:1\n\t\t\t\t_xgo_ret = append(_xgo_ret, url)\n\t\t\t}\n\t\t}\n//line dql/fetcher/hrefs/hrefs.xgo:32:1\n\t\treturn\n\t}()\n//line dql/fetcher/hrefs/hrefs.xgo:33:1\n\treturn Result{input.(string), hrefs}\n}\n//line dql/fetcher/hrefs/hrefs.xgo:36:1\n// URL returns the URL from the input.\n// Input is expected to be a URL, and the fetcher will extract all hrefs\n// from the page at that URL.\nfunc URL(input interface{}) string {\n//line dql/fetcher/hrefs/hrefs.xgo:40:1\n\treturn input.(string)\n}\n//line dql/fetcher/hrefs/hrefs.xgo:43:1\nfunc init() {\n//line dql/fetcher/hrefs/hrefs.xgo:44:1\n\tfetcher.Register(\"hrefs\", New, URL)\n}\n"
  },
  {
    "path": "dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage importedBy\n\nimport (\n\t\"github.com/goplus/xgo/dql\"\n\t\"github.com/goplus/xgo/dql/html\"\n\t\"github.com/goplus/xgo/dql/fetcher\"\n)\n\ntype Result struct {\n\tPath       string `json:\"path\"` // package path\n\tImportedBy int    `json:\"importedBy\"`\n}\n\n// New extracts the number of packages that import the given package from\n// the given HTML document and returns the Result.\nfunc New(input any, doc html.NodeSet) Result {\n\tconst importedBy = \"Imported By:\"\n\tpath := input.(string)\n\ta := doc.**.a@(($\"aria-label\"?:\"\").hasPrefix(importedBy)).one\n\tif !a.ok {\n\t\treturn {path, 0}\n\t}\n\tlabel := a.$\"aria-label\"!\n\tnImported := dql.int(label[len(importedBy):])!\n\t// dql.int support comma in number string, e.g. \"1,234\"\n\treturn {path, nImported}\n}\n\n// URL returns the input URL for the given input.\n// Input is expected to be a Go package path.\nfunc URL(input any) string {\n\treturn \"https://pkg.go.dev/\" + input.(string)\n}\n\nfunc init() {\n\tfetcher.register(\"pkg.go.dev/importedBy\", New, URL)\n}\n"
  },
  {
    "path": "dql/fetcher/pkg.go.dev/importedBy/xgo_autogen.go",
    "content": "// Code generated by xgo (XGo); DO NOT EDIT.\n\npackage importedBy\n\nimport (\n\t\"github.com/goplus/xgo/dql\"\n\t\"github.com/goplus/xgo/dql/fetcher\"\n\t\"github.com/goplus/xgo/dql/html\"\n\t\"github.com/qiniu/x/errors\"\n\t\"strings\"\n)\n\nconst XGoPackage = \"github.com/goplus/xgo/dql/html\"\nconst _ = true\n\ntype Result struct {\n\tPath       string `json:\"path\"`\n\tImportedBy int    `json:\"importedBy\"`\n}\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:30:1\n// New extracts the number of packages that import the given package from\n// the given HTML document and returns the Result.\nfunc New(input interface{}, doc html.NodeSet) Result {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:33:1\n\tconst importedBy = \"Imported By:\"\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:34:1\n\tpath := input.(string)\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\ta := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\tdoc.XGo_Any(\"a\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\tif strings.HasPrefix(func() (_xgo_ret string) {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\t\tvar _xgo_err error\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\t\t_xgo_ret, _xgo_err = self.XGo_Attr__1(\"aria-label\")\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\t\tif _xgo_err != nil {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\t\t\treturn \"\"\n\t\t\t\t}\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\t\treturn\n\t\t\t}(), importedBy) {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\t\tif\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:35:1\n\t\t\treturn true\n\t\t})\n\t}).One()\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:36:1\n\tif !a.Ok() {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:37:1\n\t\treturn Result{path, 0}\n\t}\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:39:1\n\tlabel := func() (_xgo_ret string) {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:39:1\n\t\tvar _xgo_err error\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:39:1\n\t\t_xgo_ret, _xgo_err = a.XGo_Attr__1(\"aria-label\")\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:39:1\n\t\tif _xgo_err != nil {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:39:1\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"a.$\\\"aria-label\\\"\", \"dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo\", 39, \"importedBy.New\")\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:39:1\n\t\t\tpanic(_xgo_err)\n\t\t}\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:39:1\n\t\treturn\n\t}()\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:40:1\n\tnImported := func() (_xgo_ret int) {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:40:1\n\t\tvar _xgo_err error\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:40:1\n\t\t_xgo_ret, _xgo_err = dql.Int(label[len(importedBy):])\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:40:1\n\t\tif _xgo_err != nil {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:40:1\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"dql.int(label[len(importedBy):])\", \"dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo\", 40, \"importedBy.New\")\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:40:1\n\t\t\tpanic(_xgo_err)\n\t\t}\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:40:1\n\t\treturn\n\t}()\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:42:1\n\treturn Result{path, nImported}\n}\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:45:1\n// URL returns the input URL for the given input.\n// Input is expected to be a Go package path.\nfunc URL(input interface{}) string {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:48:1\n\treturn \"https://pkg.go.dev/\" + input.(string)\n}\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:51:1\nfunc init() {\n//line dql/fetcher/pkg.go.dev/importedBy/importedBy.xgo:52:1\n\tfetcher.Register(\"pkg.go.dev/importedBy\", New, URL)\n}\n"
  },
  {
    "path": "dql/fetcher/pytorch.org/fndoc/fndoc.xgo",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage fndoc\n\nimport (\n\t\"github.com/goplus/xgo/dql/html\"\n\t\"github.com/goplus/xgo/dql/fetcher\"\n)\n\n// -----------------------------------------------------------------------------\n\nconst (\n\tspaces = \" \\t\\r\\n¶\"\n)\n\ntype Result struct {\n\tName string `json:\"name\"`           // function name, type name, var name, etc.\n\tType string `json:\"type,omitempty\"` // \"function\", \"type\", \"var\", etc.\n\tDoc  string `json:\"doc,omitempty\"`\n\tSig  string `json:\"sig\"`\n\tURL  string `json:\"url,omitempty\"`\n}\n\n// New extracts the function declaration from the given HTML document\n// and returns the Result.\nfunc New(input any, doc html.NodeSet) Result {\n\tname := input.(string)\n\turl := name\n\tif name != \"\" {\n\t\turl = URL(input)\n\t}\n\tif doc.ok {\n\t\tfn := doc.**.dl@($class == \"py function\").one\n\t\tdecl := fn.firstElementChild@dt.text\n\t\tpos := decl.indexByte('(')\n\t\tif pos > 0 {\n\t\t\tsig := decl[pos:]\n\t\t\treturn {name, \"function\", \"\", sig.trimRight(spaces), url}\n\t\t}\n\t}\n\treturn {name, \"\", \"\", \"<NULL>\", url}\n}\n\n// URL returns the input URL for the given input.\n// Input is expected to be a function name, and the URL will be the function's\n// documentation page on pytorch.org.\nfunc URL(input any) string {\n\treturn \"https://pytorch.org/docs/stable/generated/torch.\" + input.(string) + \".html\"\n}\n\nfunc init() {\n\tfetcher.Register(\"pytorch.org/fndoc\", New, URL)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/fetcher/pytorch.org/fndoc/xgo_autogen.go",
    "content": "// Code generated by xgo (XGo); DO NOT EDIT.\n\npackage fndoc\n\nimport (\n\t\"github.com/goplus/xgo/dql/fetcher\"\n\t\"github.com/goplus/xgo/dql/html\"\n\t\"strings\"\n)\n\nconst XGoPackage = \"github.com/goplus/xgo/dql/html\"\nconst _ = true\nconst spaces = \" \\t\\r\\n¶\"\n\ntype Result struct {\n\tName string `json:\"name\"`\n\tType string `json:\"type,omitempty\"`\n\tDoc  string `json:\"doc,omitempty\"`\n\tSig  string `json:\"sig\"`\n\tURL  string `json:\"url,omitempty\"`\n}\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:38:1\n// New extracts the function declaration from the given HTML document\n// and returns the Result.\nfunc New(input interface{}, doc html.NodeSet) Result {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:41:1\n\tname := input.(string)\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:42:1\n\turl := name\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:43:1\n\tif name != \"\" {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:62:1\n\t\turl = URL(input)\n\t}\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:46:1\n\tif doc.Ok() {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:47:1\n\t\tfn := html.NodeSet_Cast(func(_xgo_yield func(*html.Node) bool) {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:47:1\n\t\t\tdoc.XGo_Any(\"dl\").XGo_Enum()(func(self html.NodeSet) bool {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:47:1\n\t\t\t\tif self.XGo_Attr__0(\"class\") == \"py function\" {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:47:1\n\t\t\t\t\tif\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:47:1\n\t\t\t\t\t_xgo_val, _xgo_err := self.XGo_first(); _xgo_err == nil {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:47:1\n\t\t\t\t\t\tif !_xgo_yield(_xgo_val) {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:47:1\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:47:1\n\t\t\t\treturn true\n\t\t\t})\n\t\t}).One()\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:48:1\n\t\tdecl := fn.FirstElementChild().XGo_Select(\"dt\").Text__0()\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:49:1\n\t\tpos := strings.IndexByte(decl, '(')\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:50:1\n\t\tif pos > 0 {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:51:1\n\t\t\tsig := decl[pos:]\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:52:1\n\t\t\treturn Result{name, \"function\", \"\", strings.TrimRight(sig, spaces), url}\n\t\t}\n\t}\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:55:1\n\treturn Result{name, \"\", \"\", \"<NULL>\", url}\n}\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:58:1\n// URL returns the input URL for the given input.\n// Input is expected to be a function name, and the URL will be the function's\n// documentation page on pytorch.org.\nfunc URL(input interface{}) string {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:62:1\n\treturn \"https://pytorch.org/docs/stable/generated/torch.\" + input.(string) + \".html\"\n}\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:65:1\nfunc init() {\n//line dql/fetcher/pytorch.org/fndoc/fndoc.xgo:66:1\n\tfetcher.Register(\"pytorch.org/fndoc\", New, URL)\n}\n"
  },
  {
    "path": "dql/fs/fs.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage fs\n\nimport (\n\t\"errors\"\n\t\"io/fs\"\n\t\"iter\"\n\t\"os\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/goplus/xgo/dql\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Node represents a file or directory in the file system.\ntype Node struct {\n\t// Path is the absolute path to the file or directory (relative to the root\n\t// of the file system).\n\tPath string\n\n\t// directory entry for the file or directory.\n\tde  fs.DirEntry\n\tfi  fs.FileInfo\n\terr error\n}\n\n// Name returns the name of the file (or subdirectory) described by the entry.\n// This name is only the final element of the path (the base name), not the entire path.\n// For example, Name would return \"hello.go\" not \"home/gopher/hello.go\".\nfunc (p *Node) Name() (string, error) {\n\tif p.err != nil {\n\t\treturn \"\", p.err\n\t}\n\treturn p.de.Name(), nil\n}\n\n// IsDir reports whether the entry describes a directory.\nfunc (p *Node) IsDir() (bool, error) {\n\tif p.err != nil {\n\t\treturn false, p.err\n\t}\n\treturn p.de.IsDir(), nil\n}\n\nfunc (p *Node) info() (fs.FileInfo, error) {\n\tif p.fi == nil {\n\t\tif p.err == nil {\n\t\t\tp.fi, p.err = p.de.Info()\n\t\t}\n\t}\n\treturn p.fi, p.err\n}\n\n// Size returns the size of the file in bytes.\n// If the file is a directory, the size is system-dependent and should not be used.\nfunc (p *Node) Size() (int64, error) {\n\tfi, err := p.info()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn fi.Size(), nil\n}\n\n// Type returns the type bits for the entry.\n// The type bits are a subset of the usual FileMode bits, those returned by the FileMode.Type method.\nfunc (p *Node) Type() (fs.FileMode, error) {\n\tif p.err != nil {\n\t\treturn 0, p.err\n\t}\n\treturn p.de.Type(), nil\n}\n\n// Mode returns file mode bits.\nfunc (p *Node) Mode() (fs.FileMode, error) {\n\tfi, err := p.info()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn fi.Mode(), nil\n}\n\n// ModTime returns the modification time of the file.\nfunc (p *Node) ModTime() (time.Time, error) {\n\tfi, err := p.info()\n\tif err != nil {\n\t\treturn time.Time{}, err\n\t}\n\treturn fi.ModTime(), nil\n}\n\n// Sys returns the underlying data source (can return nil).\nfunc (p *Node) Sys() (any, error) {\n\tfi, err := p.info()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fi.Sys(), nil\n}\n\n// -----------------------------------------------------------------------------\n\n// NodeSet represents a set of file system nodes, along with any error that\n// occurred while retrieving them.\ntype NodeSet struct {\n\tData iter.Seq[*Node]\n\tBase fs.FS\n\tErr  error\n}\n\n// Root creates a NodeSet containing the provided root node.\nfunc Root(root fs.FS, doc *Node) NodeSet {\n\treturn NodeSet{\n\t\tBase: root,\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tyield(doc)\n\t\t},\n\t}\n}\n\n// Nodes creates a NodeSet containing the provided nodes.\nfunc Nodes(root fs.FS, nodes ...*Node) NodeSet {\n\treturn NodeSet{\n\t\tBase: root,\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tfor _, node := range nodes {\n\t\t\t\tif !yield(node) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n// Dir returns a NodeSet for the specified directory.\nfunc Dir(dir string) NodeSet {\n\treturn New(os.DirFS(dir))\n}\n\n// New creates a NodeSet for the provided file system, starting with\n// the root node.\nfunc New(root fs.FS) NodeSet {\n\treturn NodeSet{\n\t\tBase: root,\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tyield(rootEntry)\n\t\t},\n\t}\n}\n\nvar (\n\trootEntry = &Node{\n\t\tPath: \"\",\n\t\tde:   rootDirEntry{},\n\t\tfi:   rootDirEntry{},\n\t}\n)\n\ntype rootDirEntry struct {\n}\n\nfunc (p rootDirEntry) Name() string {\n\treturn \"\"\n}\n\nfunc (p rootDirEntry) Size() int64 {\n\treturn 0\n}\n\nfunc (p rootDirEntry) Type() fs.FileMode {\n\treturn fs.ModeDir\n}\n\nfunc (p rootDirEntry) Mode() fs.FileMode {\n\treturn fs.ModeDir\n}\n\nfunc (p rootDirEntry) Info() (fs.FileInfo, error) {\n\treturn p, nil\n}\n\nfunc (p rootDirEntry) ModTime() time.Time {\n\treturn time.Time{}\n}\n\nfunc (p rootDirEntry) IsDir() bool {\n\treturn true\n}\n\nfunc (p rootDirEntry) Sys() any {\n\treturn nil\n}\n\n// -----------------------------------------------------------------------------\n\n// XGo_Enum returns an iterator over the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Enum() iter.Seq[NodeSet] {\n\tif p.Err != nil {\n\t\treturn dql.NopIter[NodeSet]\n\t}\n\treturn func(yield func(NodeSet) bool) {\n\t\tp.Data(func(node *Node) bool {\n\t\t\treturn yield(Root(p.Base, node))\n\t\t})\n\t}\n}\n\n// Dir returns a NodeSet containing all child nodes of the nodes in the NodeSet\n// that are directories.\nfunc (p NodeSet) Dir() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\treturn NodeSet{\n\t\tBase: p.Base,\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldChildNodes(p.Base, node, filterDir, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// File returns a NodeSet containing all child nodes of the nodes in the NodeSet\n// that are files (not directories).\nfunc (p NodeSet) File() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\treturn NodeSet{\n\t\tBase: p.Base,\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldChildNodes(p.Base, node, filterFile, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Child() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\treturn NodeSet{\n\t\tBase: p.Base,\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldChildNodes(p.Base, node, nil, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\ntype filterType = func(fs.DirEntry) bool\n\nfunc filterDir(de fs.DirEntry) bool {\n\treturn de.IsDir()\n}\n\nfunc filterFile(de fs.DirEntry) bool {\n\treturn !de.IsDir()\n}\n\n// yieldChildNodes yields all child nodes of the given node.\nfunc yieldChildNodes(base fs.FS, node *Node, filter filterType, yield func(*Node) bool) bool {\n\tvar items []fs.DirEntry\n\tvar path = node.Path\n\tisDir, err := node.IsDir()\n\tif err == nil {\n\t\tif !isDir {\n\t\t\treturn true\n\t\t}\n\t\tdir := path\n\t\tif dir == \"\" {\n\t\t\t// fs.ReadDir does not accept an empty string as the directory, use \".\"\n\t\t\t// instead to read the root directory.\n\t\t\tdir = \".\"\n\t\t}\n\t\titems, err = fs.ReadDir(base, dir)\n\t}\n\tif err != nil {\n\t\treturn yield(&Node{Path: path, err: err}) // yield the error as a node\n\t}\n\tif path != \"\" {\n\t\tpath += \"/\"\n\t}\n\tfor _, item := range items {\n\t\tif filter == nil || filter(item) {\n\t\t\tchildPath := path + item.Name()\n\t\t\tif !yield(&Node{Path: childPath, de: item}) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\nconst (\n\tkindAny = iota\n\tkindFile\n\tkindDir\n)\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// nodes themselves) with the specified name.\n// If name is \"file\", it returns all file nodes.\n// If name is \"dir\", it returns all directory nodes.\n// If name is \"\", it returns all nodes.\n//   - .**.file\n//   - .**.dir\n//   - .**.*\nfunc (p NodeSet) XGo_Any(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\tkind := kindAny\n\tswitch name {\n\tcase \"file\":\n\t\tkind = kindFile\n\tcase \"dir\":\n\t\tkind = kindDir\n\tcase \"\":\n\tdefault:\n\t\treturn NodeSet{Err: errors.New(\"XGo_Any: invalid name - \" + name)}\n\t}\n\treturn NodeSet{\n\t\tBase: p.Base,\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldAnyNodes(kind, p.Base, node, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldAnyNodes yields all descendant nodes of the given node that match the\n// specified kind. If kind is kindAny, it yields all nodes.\nfunc yieldAnyNodes(kind int, base fs.FS, node *Node, yield func(*Node) bool) bool {\n\tisDir, err := node.IsDir()\n\tif err != nil {\n\t\treturn yield(&Node{Path: node.Path, err: err}) // yield the error as a node\n\t}\n\tswitch kind {\n\tcase kindFile:\n\t\tif isDir {\n\t\t\tgoto checkChildren\n\t\t}\n\tcase kindDir:\n\t\tif !isDir {\n\t\t\treturn true\n\t\t}\n\t}\n\tif !yield(node) {\n\t\treturn false\n\t}\ncheckChildren:\n\tif isDir {\n\t\treturn yieldChildNodes(base, node, nil, func(n *Node) bool {\n\t\t\treturn yieldAnyNodes(kind, base, n, yield)\n\t\t})\n\t}\n\treturn true\n}\n\n// -----------------------------------------------------------------------------\n\n// Match returns a NodeSet containing all child nodes of the nodes in the NodeSet\n// that match the specified pattern. The pattern syntax is the same as in path.Match.\n// For example, \"file*.txt\" matches \"file1.txt\" and \"file2.txt\", but not \"myfile.txt\".\nfunc (p NodeSet) Match(pattern string) NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tif _, err := path.Match(pattern, \"\"); err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn NodeSet{\n\t\tBase: p.Base,\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\tif name, err := node.Name(); err == nil {\n\t\t\t\t\t// The pattern has been validated, so we can ignore the error here.\n\t\t\t\t\tmatched, _ := path.Match(pattern, name)\n\t\t\t\t\tif matched {\n\t\t\t\t\t\treturn yield(node)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t},\n\t}\n}\n\n// OnError calls onErr for any error in the NodeSet and returns a new NodeSet without\n// the nodes that have errors. If onErr returns false, it stops processing and returns\n// a NodeSet without the remaining nodes.\nfunc (p NodeSet) OnError(onErr func(error) bool) NodeSet {\n\tif p.Err != nil {\n\t\tonErr(p.Err)\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\treturn NodeSet{\n\t\tBase: p.Base,\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\tif node.err != nil {\n\t\t\t\t\treturn onErr(node.err)\n\t\t\t\t}\n\t\t\t\treturn yield(node)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// All returns a NodeSet containing all nodes.\n// It's a cache operation for performance optimization when you need to traverse\n// the nodes multiple times.\nfunc (p NodeSet) All() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tnodes := dql.Collect(p.Data)\n\treturn Nodes(p.Base, nodes...)\n}\n\n// One returns a NodeSet containing the first node.\n// It's a performance optimization when you only need the first node (stop early).\nfunc (p NodeSet) One() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.First(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(p.Base, n)\n}\n\n// Single returns a NodeSet containing the single node.\n// If there are zero or more than one nodes, it returns an error.\n// ErrNotFound or ErrMultiEntities is returned accordingly.\nfunc (p NodeSet) Single() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.Single(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(p.Base, n)\n}\n\n// -----------------------------------------------------------------------------\n\n// Ok returns true if there is no error in the NodeSet.\nfunc (p NodeSet) Ok() bool {\n\treturn p.Err == nil\n}\n\n// _first returns the first node in the NodeSet.\n// It's required by XGo compiler.\nfunc (p NodeSet) XGo_first() (ret *Node, err error) {\n\tif p.Err != nil {\n\t\terr = p.Err\n\t\treturn\n\t}\n\treturn dql.First(p.Data)\n}\n\n// First returns the first node in the NodeSet.\nfunc (p NodeSet) First() (*Node, error) {\n\tif p.Err != nil {\n\t\treturn nil, p.Err\n\t}\n\treturn dql.First(p.Data)\n}\n\n// Collect retrieves all nodes from the NodeSet.\nfunc (p NodeSet) Collect() ([]*Node, error) {\n\tif p.Err != nil {\n\t\treturn nil, p.Err\n\t}\n\treturn dql.Collect(p.Data), nil\n}\n\n// -----------------------------------------------------------------------------\n\n// Path returns the path of the first node in the NodeSet.\n// The path is the absolute path to the file or directory (relative to the root\n// of the file system).\n// Note the path is not started with a slash.\n// For example, if the root of the file system is \"/home/gopher\" and the node\n// represents the file \"/home/gopher/a/b.go\", Path would return \"a/b.go\".\n// For the root node, Path would return \"\" (not \"/\").\nfunc (p NodeSet) Path() (name string, err error) {\n\tnode, err := p.First()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn node.Path, nil\n}\n\n// Name returns the name of the first node in the NodeSet.\nfunc (p NodeSet) Name() (name string, err error) {\n\tnode, err := p.First()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn node.Name()\n}\n\n// IsDir reports whether the first node in the NodeSet is a directory.\nfunc (p NodeSet) IsDir() (is bool, err error) {\n\tnode, err := p.First()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn node.IsDir()\n}\n\n// Size returns the size of the first node in the NodeSet.\nfunc (p NodeSet) Size() (size int64, err error) {\n\tnode, err := p.First()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn node.Size()\n}\n\n// Mode returns the file mode of the first node in the NodeSet.\nfunc (p NodeSet) Mode() (mode fs.FileMode, err error) {\n\tnode, err := p.First()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn node.Mode()\n}\n\n// ModTime returns the modification time of the first node in the NodeSet.\nfunc (p NodeSet) ModTime() (modTime time.Time, err error) {\n\tnode, err := p.First()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn node.ModTime()\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/golang/golang.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage golang\n\nimport (\n\t\"bytes\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io\"\n\t\"iter\"\n\t\"reflect\"\n\n\t\"github.com/goplus/xgo/dql\"\n\t\"github.com/goplus/xgo/dql/reflects\"\n\t\"github.com/qiniu/x/stream\"\n)\n\nconst (\n\tXGoPackage = \"github.com/goplus/xgo/dql/reflects\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Node represents a Go AST node.\ntype Node = reflects.Node\n\n// NodeSet represents a set of Go AST nodes.\ntype NodeSet struct {\n\treflects.NodeSet\n}\n\n// NodeSet(seq) casts a NodeSet from a sequence of nodes.\nfunc NodeSet_Cast(seq iter.Seq[Node]) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: reflects.NodeSet{Data: seq},\n\t}\n}\n\n// Root creates a NodeSet containing the provided root node.\nfunc Root(doc Node) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: reflects.Root(doc),\n\t}\n}\n\n// Nodes creates a NodeSet containing the provided nodes.\nfunc Nodes(nodes ...Node) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: reflects.Nodes(nodes...),\n\t}\n}\n\n// New creates a NodeSet from the given *ast.File.\nfunc New(f *ast.File) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: reflects.New(reflect.ValueOf(f)),\n\t}\n}\n\n// Config represents the configuration for parsing Go source code.\ntype Config struct {\n\tMode parser.Mode\n\tFset *token.FileSet\n}\n\nconst (\n\tdefaultMode = parser.ParseComments\n)\n\n// parse parses Go source code from the given URI or source.\nfunc parse(uri string, src any, conf ...Config) (f *ast.File, err error) {\n\tin, err := stream.ReadSourceFromURI(uri, src)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar c Config\n\tif len(conf) > 0 {\n\t\tc = conf[0]\n\t} else {\n\t\tc.Mode = defaultMode\n\t}\n\tif c.Fset == nil {\n\t\tc.Fset = token.NewFileSet()\n\t}\n\treturn parser.ParseFile(c.Fset, uri, in, c.Mode)\n}\n\n// From parses Go source code from the given URI or source, returning a NodeSet.\n// An optional Config can be provided to customize the parsing behavior.\nfunc From(uri string, src any, conf ...Config) NodeSet {\n\tf, err := parse(uri, src, conf...)\n\tif err != nil {\n\t\treturn NodeSet{NodeSet: reflects.NodeSet{Err: err}}\n\t}\n\treturn New(f)\n}\n\n// Source creates a NodeSet from various types of Go sources.\n// It supports the following source types:\n// - string: treats the string as a URI, opens the resource, and reads Go source code from it.\n// - []byte: treated as Go source code.\n// - *bytes.Buffer: treated as Go source code.\n// - io.Reader: treated as Go source code.\n// - *ast.File: creates a NodeSet from the provided *ast.File.\n// - reflect.Value: creates a NodeSet from the provided reflect.Value (expected to be *ast.File).\n// - Node: creates a NodeSet containing the single provided node.\n// - iter.Seq[Node]: returns the provided sequence as a NodeSet.\n// - NodeSet: returns the provided NodeSet as is.\n// If the source type is unsupported, it panics.\nfunc Source(r any, conf ...Config) (ret NodeSet) {\n\tswitch v := r.(type) {\n\tcase string:\n\t\treturn From(v, nil, conf...)\n\tcase []byte:\n\t\treturn From(\"\", v, conf...)\n\tcase *bytes.Buffer:\n\t\treturn From(\"\", v, conf...)\n\tcase io.Reader:\n\t\treturn From(\"\", v, conf...)\n\tcase *ast.File:\n\t\treturn New(v)\n\tcase reflect.Value:\n\t\treturn NodeSet{NodeSet: reflects.New(v)}\n\tcase Node:\n\t\treturn NodeSet{NodeSet: reflects.Root(v)}\n\tcase iter.Seq[Node]:\n\t\treturn NodeSet{NodeSet: reflects.NodeSet{Data: v}}\n\tcase NodeSet:\n\t\treturn v\n\tdefault:\n\t\tpanic(\"dql/golang.Source: unsupported source type\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// XGo_Enum returns an iterator over the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Enum() iter.Seq[NodeSet] {\n\tif p.Err != nil {\n\t\treturn dql.NopIter[NodeSet]\n\t}\n\treturn func(yield func(NodeSet) bool) {\n\t\tp.Data(func(node Node) bool {\n\t\t\treturn yield(Root(node))\n\t\t})\n\t}\n}\n\n// XGo_Select returns a NodeSet containing the nodes with the specified name.\n//   - @name\n//   - @\"element-name\"\nfunc (p NodeSet) XGo_Select(name string) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_Select(name),\n\t}\n}\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (p NodeSet) XGo_Elem(name string) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_Elem(name),\n\t}\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Child() NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_Child(),\n\t}\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// nodes themselves) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (p NodeSet) XGo_Any(name string) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_Any(name),\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// All returns a NodeSet containing all nodes.\n// It's a cache operation for performance optimization when you need to traverse\n// the nodes multiple times.\nfunc (p NodeSet) All() NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_all(),\n\t}\n}\n\n// One returns a NodeSet containing the first node.\n// It's a performance optimization when you only need the first node (stop early).\nfunc (p NodeSet) One() NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_one(),\n\t}\n}\n\n// Single returns a NodeSet containing the single node.\n// If there are zero or more than one nodes, it returns an error.\n// ErrNotFound or ErrMultipleResults is returned accordingly.\nfunc (p NodeSet) Single() NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_single(),\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// Ok returns true if there is no error in the NodeSet.\nfunc (p NodeSet) Ok() bool {\n\treturn p.Err == nil\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__0(name string) any {\n\tval, _ := p.XGo_Attr__1(name)\n\treturn val\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__1(name string) (val any, err error) {\n\tval, err = p.NodeSet.XGo_Attr__1(name)\n\tif err == nil {\n\t\tswitch v := val.(type) {\n\t\tcase *ast.Ident:\n\t\t\tif v != nil {\n\t\t\t\treturn v.Name, nil\n\t\t\t}\n\t\t\treturn \"\", nil\n\t\tcase *ast.BasicLit:\n\t\t\tif v != nil {\n\t\t\t\treturn v.Value, nil\n\t\t\t}\n\t\t\treturn \"\", nil\n\t\t}\n\t}\n\treturn\n}\n\n// Class returns the class name of the first node in the NodeSet.\nfunc (p NodeSet) Class() string {\n\treturn p.XGo_class()\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/golang/parse.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage golang\n\nimport (\n\t\"go/ast\"\n\t\"unsafe\"\n)\n\n// -----------------------------------------------------------------------------\n\n// File represents a Go file.\ntype File struct {\n\tast.File\n\t// File must contain only the embedded ast.File field.\n}\n\n// ParseFile parses Go source code from the given filename or source, returning a File\n// object. An optional Config can be provided to customize the parsing behavior.\nfunc ParseFile(filename string, src any, conf ...Config) (f *File, err error) {\n\tdoc, err := parse(filename, src, conf...)\n\tif err == nil {\n\t\tf = (*File)(unsafe.Pointer(doc))\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (f *File) XGo_Elem(name string) NodeSet {\n\treturn New(&f.File).XGo_Elem(name)\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the node.\n//   - .*\nfunc (f *File) XGo_Child() NodeSet {\n\treturn New(&f.File).XGo_Child()\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// node itself) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (f *File) XGo_Any(name string) NodeSet {\n\treturn New(&f.File).XGo_Any(name)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/html/html.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage html\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"iter\"\n\t\"os\"\n\n\t\"github.com/goplus/xgo/dql\"\n\t\"github.com/qiniu/x/stream\"\n\t\"golang.org/x/net/html\"\n)\n\nconst (\n\tXGoPackage = true\n)\n\n// -----------------------------------------------------------------------------\n\n// NodeSet represents a set of HTML nodes.\ntype NodeSet struct {\n\tData iter.Seq[*Node]\n\tErr  error\n}\n\n// NodeSet(seq) casts a NodeSet from a sequence of nodes.\nfunc NodeSet_Cast(seq iter.Seq[*Node]) NodeSet {\n\treturn NodeSet{Data: seq}\n}\n\n// Root creates a NodeSet containing the provided root node.\nfunc Root(doc *Node) NodeSet {\n\tif doc.Type == html.DocumentNode {\n\t\tif n := doc.FirstChild; n.NextSibling == nil {\n\t\t\tdoc = toNode(n) // skip document node\n\t\t}\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tyield(doc)\n\t\t},\n\t}\n}\n\n// Nodes creates a NodeSet containing the provided nodes.\nfunc Nodes(nodes ...*Node) NodeSet {\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tfor _, node := range nodes {\n\t\t\t\tif !yield(node) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n// New parses the HTML document from the provided reader and returns a NodeSet\n// containing the root node. If there is an error during parsing, the NodeSet's\n// Err field is set.\nfunc New(r io.Reader) NodeSet {\n\tdoc, err := html.Parse(r)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(toNode(doc))\n}\n\n// Source creates a NodeSet from various types of sources:\n// - string: treated as an URL to read HTML content from.\n// - []byte: treated as raw HTML content.\n// - io.Reader: reads HTML content from the reader.\n// - *Node: creates a NodeSet containing the single provided node.\n// - iter.Seq[*Node]: directly uses the provided sequence of nodes.\n// - NodeSet: returns the provided NodeSet as is.\n// If the source type is unsupported, it panics.\nfunc Source(r any) (ret NodeSet) {\n\tswitch v := r.(type) {\n\tcase string:\n\t\tf, err := stream.Open(v)\n\t\tif err != nil {\n\t\t\treturn NodeSet{Err: err}\n\t\t}\n\t\tdefer f.Close()\n\t\treturn New(f)\n\tcase []byte:\n\t\tr := bytes.NewReader(v)\n\t\treturn New(r)\n\tcase io.Reader:\n\t\treturn New(v)\n\tcase *Node:\n\t\treturn Root(v)\n\tcase iter.Seq[*Node]:\n\t\treturn NodeSet{Data: v}\n\tcase NodeSet:\n\t\treturn v\n\tdefault:\n\t\tpanic(\"dql/html.Source: unsupported source type\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// XGo_Enum returns an iterator over the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Enum() iter.Seq[NodeSet] {\n\tif p.Err != nil {\n\t\treturn dql.NopIter[NodeSet]\n\t}\n\treturn func(yield func(NodeSet) bool) {\n\t\tp.Data(func(node *Node) bool {\n\t\t\treturn yield(Root(node))\n\t\t})\n\t}\n}\n\n// XGo_Select returns a NodeSet containing the nodes with the specified name.\n//   - @name\n//   - @\"element-name\"\nfunc (p NodeSet) XGo_Select(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn selectNode(node, name, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// selectNode yields the node if it matches the specified name.\nfunc selectNode(node *Node, name string, yield func(*Node) bool) bool {\n\tif node.Type == html.ElementNode && node.Data == name {\n\t\treturn yield(node)\n\t}\n\treturn true\n}\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (p NodeSet) XGo_Elem(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldNode(node, name, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldNode yields the child node with the specified name if it exists.\nfunc yieldNode(n *Node, name string, yield func(*Node) bool) bool {\n\tfor c := n.FirstChild; c != nil; c = c.NextSibling {\n\t\tif c.Type == html.ElementNode && c.Data == name {\n\t\t\tif !yield(toNode(c)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Child() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(n *Node) bool {\n\t\t\t\treturn yieldChildNodes(n, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldChildNodes yields all child nodes of the given node.\nfunc yieldChildNodes(n *Node, yield func(*Node) bool) bool {\n\tfor c := n.FirstChild; c != nil; c = c.NextSibling {\n\t\tif !yield(toNode(c)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// nodes themselves) with the specified name.\n// If name is \"textNode\", it returns all text nodes.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (p NodeSet) XGo_Any(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldAnyNodes(node, name, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldAnyNodes yields all descendant nodes of the given node that match the\n// specified name. If name is \"textNode\", it yields text nodes. If name is \"\",\n// it yields all nodes.\nfunc yieldAnyNodes(n *Node, name string, yield func(*Node) bool) bool {\n\tswitch name {\n\tcase \"textNode\":\n\t\tif n.Type == html.TextNode {\n\t\t\tif !yield(n) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\tcase \"\": // .**.*\n\t\tif !yield(n) {\n\t\t\treturn false\n\t\t}\n\tdefault:\n\t\tif n.Type == html.ElementNode && n.Data == name {\n\t\t\tif !yield(n) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\tfor c := n.FirstChild; c != nil; c = c.NextSibling {\n\t\tif !yieldAnyNodes(toNode(c), name, yield) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// -----------------------------------------------------------------------------\n\n// All returns a NodeSet containing all nodes.\n// It's a cache operation for performance optimization when you need to traverse\n// the nodes multiple times.\nfunc (p NodeSet) All() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tnodes := dql.Collect(p.Data)\n\treturn Nodes(nodes...)\n}\n\n// One returns a NodeSet containing the first node.\n// It's a performance optimization when you only need the first node (stop early).\nfunc (p NodeSet) One() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.First(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(n)\n}\n\n// Single returns a NodeSet containing the single node.\n// If there are zero or more than one nodes, it returns an error.\n// ErrNotFound or ErrMultiEntities is returned accordingly.\nfunc (p NodeSet) Single() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.Single(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(n)\n}\n\n// ParentN returns a NodeSet containing the N-th parent nodes.\nfunc (p NodeSet) ParentN(n int) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldParentN(node, n, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\nfunc yieldParentN(node *Node, n int, yield func(*Node) bool) bool {\n\tif n > 0 {\n\t\tfor {\n\t\t\tnode = toNode(node.Parent)\n\t\t\tif node == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tn--\n\t\t\tif n == 0 {\n\t\t\t\treturn yield(node)\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// Parent returns a NodeSet containing the parent nodes.\nfunc (p NodeSet) Parent() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\tif next := node.Parent; next != nil {\n\t\t\t\t\treturn yield(toNode(next))\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t},\n\t}\n}\n\n// PrevSibling returns a NodeSet containing the previous sibling nodes.\nfunc (p NodeSet) PrevSibling() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\tif next := node.PrevSibling; next != nil {\n\t\t\t\t\treturn yield(toNode(next))\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t},\n\t}\n}\n\n// NextSibling returns a NodeSet containing the next sibling nodes.\nfunc (p NodeSet) NextSibling() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\tif next := node.NextSibling; next != nil {\n\t\t\t\t\treturn yield(toNode(next))\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t},\n\t}\n}\n\n// FirstElementChild returns a NodeSet containing the first element\n// child of each node.\nfunc (p NodeSet) FirstElementChild() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\tfor c := node.FirstChild; c != nil; c = c.NextSibling {\n\t\t\t\t\tif c.Type == html.ElementNode {\n\t\t\t\t\t\treturn yield(toNode(c))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t},\n\t}\n}\n\n// TextNode returns a NodeSet containing all text nodes.\nfunc (p NodeSet) TextNode() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldNodeType(node, html.TextNode, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\nfunc yieldNodeType(node *Node, typ html.NodeType, yield func(*Node) bool) bool {\n\tfor c := node.FirstChild; c != nil; c = c.NextSibling {\n\t\tif c.Type == typ {\n\t\t\tif !yield(toNode(c)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// -----------------------------------------------------------------------------\n\n// Dump prints the nodes in the NodeSet for debugging purposes.\nfunc (p NodeSet) Dump() NodeSet {\n\tif p.Err == nil {\n\t\tp.Data(func(node *Node) bool {\n\t\t\tswitch node.Type {\n\t\t\tcase html.ElementNode:\n\t\t\t\tfmt.Fprintln(os.Stderr, \"==> element:\", node.Data, node.Attr)\n\t\t\tcase html.TextNode:\n\t\t\t\tfmt.Fprintln(os.Stderr, \"==> text:\", node.Data)\n\t\t\tcase html.DocumentNode:\n\t\t\t\tfmt.Fprintln(os.Stderr, \"==> document\")\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}\n\treturn p\n}\n\n// -----------------------------------------------------------------------------\n\n// Ok returns true if there is no error in the NodeSet.\nfunc (p NodeSet) Ok() bool {\n\treturn p.Err == nil\n}\n\n// _first returns the first node in the NodeSet.\n// It's required by XGo compiler.\nfunc (p NodeSet) XGo_first() (ret *Node, err error) {\n\tif p.Err != nil {\n\t\terr = p.Err\n\t\treturn\n\t}\n\treturn dql.First(p.Data)\n}\n\n// First returns the first node in the NodeSet.\nfunc (p NodeSet) First() (*Node, error) {\n\tif p.Err != nil {\n\t\treturn nil, p.Err\n\t}\n\treturn dql.First(p.Data)\n}\n\n// Collect retrieves all nodes from the NodeSet.\nfunc (p NodeSet) Collect() ([]*Node, error) {\n\tif p.Err != nil {\n\t\treturn nil, p.Err\n\t}\n\treturn dql.Collect(p.Data), nil\n}\n\n// Name returns the name of the first node in the NodeSet.\n// empty string is returned if the NodeSet is empty or the first node is not\n// an element node.\nfunc (p NodeSet) Name() string {\n\tnode, err := p.First()\n\tif err == nil {\n\t\tif node.Type == html.ElementNode {\n\t\t\treturn node.Data\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// Value returns the data content of the first node in the NodeSet.\nfunc (p NodeSet) Value__0() string {\n\tval, _ := p.Value__1()\n\treturn val\n}\n\n// Value returns the data content of the first node in the NodeSet.\nfunc (p NodeSet) Value__1() (val string, err error) {\n\tnode, err := p.First()\n\tif err == nil {\n\t\treturn node.Data, nil\n\t}\n\treturn\n}\n\n// HasAttr returns true if the first node in the NodeSet has the specified attribute.\n// It returns false otherwise.\nfunc (p NodeSet) HasAttr(name string) bool {\n\tnode, err := p.First()\n\tif err == nil {\n\t\treturn node.HasAttr(name)\n\t}\n\treturn false\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__0(name string) string {\n\tval, _ := p.XGo_Attr__1(name)\n\treturn val\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__1(name string) (val string, err error) {\n\tnode, err := p.First()\n\tif err == nil {\n\t\treturn node.XGo_Attr__1(name)\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/html/html_test.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage html\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"golang.org/x/net/html\"\n\t\"golang.org/x/net/html/atom\"\n)\n\nfunc TestTextOf(t *testing.T) {\n\tcases := []struct {\n\t\tname string\n\t\thtml string\n\t\twant string\n\t}{\n\t\t{\"p\", `<p data-v-f3ebc54b=\"\" class=\"param-description\">Aspect ratio of the generated images\n                            (width:height)</p>`, \"Aspect ratio of the generated images (width:height)\\n\"},\n\t\t{\"codeBody\", codeBody, `{\n\"code\": 0, // Error codes; specific definitions see Error codes\n\"message\": \"string\", // Error information\n\"request_id\": \"string\", // Request ID, generated by the system, for tracking and troubleshooting\n\"data\": {\n\"task_id\": \"string\", // Task ID, generated by the system\n\"task_status\": \"string\", // Task status: submitted, processing, succeed, failed\n\"task_info\": { // Task creation parameters\n  \"external_task_id\": \"string\" // Customer-defined task ID\n},\n\"created_at\": 1722769557708, // Task creation time, Unix timestamp, ms\n\"updated_at\": 1722769557708 // Task update time, Unix timestamp, ms\n}\n}\n\n`},\n\t}\n\tfor _, c := range cases {\n\t\tdoc, e := html.Parse(strings.NewReader(c.html))\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"%s error: %v\", c.name, e)\n\t\t}\n\t\tif got := textOf(doc, false, noFilter{}); got != c.want {\n\t\t\tt.Errorf(\"%s: `%s`, want `%s`\", c.name, got, c.want)\n\t\t}\n\t}\n}\n\nconst codeBody = `<div data-v-9ebd45a1=\"\" class=\"code-body\">\n<pre data-v-9ebd45a1=\"\"><code data-v-9ebd45a1=\"\" class=\"hljs language-json\"><span class=\"hljs-punctuation\">{</span>\n<span class=\"hljs-attr\">\"code\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-number\">0</span><span class=\"hljs-punctuation\">,</span> <span class=\"hljs-comment\">// Error codes; specific definitions see Error codes</span>\n<span class=\"hljs-attr\">\"message\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">\"string\"</span><span class=\"hljs-punctuation\">,</span> <span class=\"hljs-comment\">// Error information</span>\n<span class=\"hljs-attr\">\"request_id\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">\"string\"</span><span class=\"hljs-punctuation\">,</span> <span class=\"hljs-comment\">// Request ID, generated by the system, for tracking and troubleshooting</span>\n<span class=\"hljs-attr\">\"data\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-punctuation\">{</span>\n<span class=\"hljs-attr\">\"task_id\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">\"string\"</span><span class=\"hljs-punctuation\">,</span> <span class=\"hljs-comment\">// Task ID, generated by the system</span>\n<span class=\"hljs-attr\">\"task_status\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">\"string\"</span><span class=\"hljs-punctuation\">,</span> <span class=\"hljs-comment\">// Task status: submitted, processing, succeed, failed</span>\n<span class=\"hljs-attr\">\"task_info\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-punctuation\">{</span> <span class=\"hljs-comment\">// Task creation parameters</span>\n  <span class=\"hljs-attr\">\"external_task_id\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">\"string\"</span> <span class=\"hljs-comment\">// Customer-defined task ID</span>\n<span class=\"hljs-punctuation\">}</span><span class=\"hljs-punctuation\">,</span>\n<span class=\"hljs-attr\">\"created_at\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-number\">1722769557708</span><span class=\"hljs-punctuation\">,</span> <span class=\"hljs-comment\">// Task creation time, Unix timestamp, ms</span>\n<span class=\"hljs-attr\">\"updated_at\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-number\">1722769557708</span> <span class=\"hljs-comment\">// Task update time, Unix timestamp, ms</span>\n<span class=\"hljs-punctuation\">}</span>\n<span class=\"hljs-punctuation\">}</span></code></pre>\n</div>`\n\ntype commentRemover struct {\n\tnoFilter\n}\n\nfunc (f commentRemover) Filter(node *html.Node) bool {\n\tif node.DataAtom != atom.Span {\n\t\treturn true\n\t}\n\tfor _, a := range node.Attr {\n\t\tif a.Key == \"class\" && a.Val == \"hljs-comment\" {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc TestTextFilter(t *testing.T) {\n\tcases := []struct {\n\t\tname string\n\t\thtml string\n\t\twant string\n\t}{\n\t\t{\"codeBody\", codeBody, `{\n\"code\": 0, \n\"message\": \"string\", \n\"request_id\": \"string\", \n\"data\": {\n\"task_id\": \"string\", \n\"task_status\": \"string\", \n\"task_info\": { \n  \"external_task_id\": \"string\" \n},\n\"created_at\": 1722769557708, \n\"updated_at\": 1722769557708 \n}\n}\n\n`},\n\t}\n\tfor _, c := range cases {\n\t\tdoc, e := html.Parse(strings.NewReader(c.html))\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"%s error: %v\", c.name, e)\n\t\t}\n\t\tif got := textOf(doc, false, commentRemover{}); got != c.want {\n\t\t\tt.Errorf(\"%s: `%s`, want `%s`\", c.name, got, c.want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "dql/html/node.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage html\n\nimport (\n\t\"io\"\n\t\"unsafe\"\n\n\t\"github.com/goplus/xgo/dql\"\n\t\"golang.org/x/net/html\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Node represents an HTML node.\ntype Node struct {\n\thtml.Node\n\t// Node must contain only the embedded html.Node field.\n}\n\n// Parse returns the parse tree for the HTML from the given Reader.\nfunc Parse(r io.Reader) (n *Node, err error) {\n\tdoc, err := html.Parse(r)\n\tif err == nil {\n\t\tn = toNode(doc)\n\t}\n\treturn\n}\n\nfunc toNode(n *html.Node) *Node {\n\treturn (*Node)(unsafe.Pointer(n))\n}\n\n// -----------------------------------------------------------------------------\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (n *Node) XGo_Elem(name string) NodeSet {\n\treturn Root(n).XGo_Elem(name)\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the node.\n//   - .*\nfunc (n *Node) XGo_Child() NodeSet {\n\treturn Root(n).XGo_Child()\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// node itself) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (n *Node) XGo_Any(name string) NodeSet {\n\treturn Root(n).XGo_Any(name)\n}\n\n// Dump prints the node for debugging purposes.\nfunc (n *Node) Dump() NodeSet {\n\treturn Root(n).Dump()\n}\n\n// -----------------------------------------------------------------------------\n\n// Name returns the name of the node if it's an element node, or an empty string\n// otherwise.\nfunc (n *Node) Name() string {\n\tif n.Type == html.ElementNode {\n\t\treturn n.Data\n\t}\n\treturn \"\"\n}\n\n// Value returns the data of the node.\nfunc (n *Node) Value() string {\n\treturn n.Data\n}\n\n// HasAttr returns true if the node has the specified attribute.\nfunc (n *Node) HasAttr(name string) bool {\n\tfor _, attr := range n.Attr {\n\t\tif attr.Key == name {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// XGo_Attr returns the value of the specified attribute from the node.\n// If the attribute is not found, it returns an empty string.\n//   - $name\n//   - $“attr-name”\nfunc (n *Node) XGo_Attr__0(name string) string {\n\tval, _ := n.XGo_Attr__1(name)\n\treturn val\n}\n\n// XGo_Attr returns the value of the specified attribute from the node.\n// If the attribute is not found, it returns ErrNotFound.\n//   - $name\n//   - $“attr-name”\nfunc (n *Node) XGo_Attr__1(name string) (string, error) {\n\tfor _, attr := range n.Attr {\n\t\tif attr.Key == name {\n\t\t\treturn attr.Val, nil\n\t\t}\n\t}\n\treturn \"\", dql.ErrNotFound\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/html/text.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage html\n\nimport (\n\t\"github.com/goplus/xgo/dql\"\n\t\"golang.org/x/net/html\"\n\t\"golang.org/x/net/html/atom\"\n)\n\n// -----------------------------------------------------------------------------\n\n// NodeFilter is the interface for filtering nodes when retrieving text content.\ntype NodeFilter interface {\n\t// Filter returns true if the node should be included in the text content.\n\tFilter(*html.Node) bool\n\n\t// TextNodeData returns the text data of a text node. It can be used to customize\n\t// the text data of a text node, for example, to trim spaces or to replace certain\n\t// characters.\n\tTextNodeData(*html.Node) string\n}\n\ntype noFilter struct{}\n\nfunc (f noFilter) Filter(*html.Node) bool { return true }\nfunc (f noFilter) TextNodeData(node *html.Node) string {\n\treturn node.Data\n}\n\n// textOf returns text data of all node's children.\nfunc textOf[F NodeFilter](node *html.Node, outer bool, f F) string {\n\tp := textPrinter[F]{\n\t\tnodef: f,\n\t}\n\tp.printNode(node, outer, false)\n\treturn string(p.data)\n}\n\ntype textPrinter[F NodeFilter] struct {\n\tdata         []byte\n\tnodef        F\n\tnotLineStart bool\n\thasSpace     bool\n}\n\nfunc isSpace(c byte) bool {\n\treturn c == ' ' || c == '\\t' || c == '\\r' || c == '\\n'\n}\n\nfunc (p *textPrinter[F]) printCollapsed(v string) {\n\tfor len(v) > 0 {\n\t\tn := len(v)\n\t\ti := 0\n\t\tfor i < n && isSpace(v[i]) {\n\t\t\ti++ // skip leading spaces\n\t\t}\n\t\tif i > 0 {\n\t\t\tp.hasSpace = true\n\t\t}\n\t\tif i >= n {\n\t\t\tbreak\n\t\t}\n\t\tif p.notLineStart && p.hasSpace {\n\t\t\tp.data = append(p.data, ' ')\n\t\t} else {\n\t\t\tp.notLineStart = true\n\t\t}\n\t\tp.hasSpace = false\n\t\tstart := i\n\t\ti++\n\t\tfor i < n && !isSpace(v[i]) {\n\t\t\ti++\n\t\t}\n\t\tp.data = append(p.data, v[start:i]...)\n\t\tv = v[i:]\n\t}\n}\n\nfunc (p *textPrinter[F]) printVerbatim(v string) {\n\tp.data = append(p.data, v...)\n\tif len(v) > 0 {\n\t\tlast := v[len(v)-1]\n\t\tp.notLineStart = last != '\\n'\n\t\tp.hasSpace = p.notLineStart && isSpace(last)\n\t}\n}\n\nfunc (p *textPrinter[F]) printNode(node *html.Node, outer, verbatim bool) {\n\tif node == nil {\n\t\treturn\n\t}\n\tf := p.nodef\n\tif node.Type == html.TextNode {\n\t\tdata := f.TextNodeData(node)\n\t\tif verbatim {\n\t\t\tp.printVerbatim(data)\n\t\t} else {\n\t\t\tp.printCollapsed(data)\n\t\t}\n\t\treturn\n\t}\n\tverbatim = verbatim || node.DataAtom == atom.Pre\n\tfor child := node.FirstChild; child != nil; child = child.NextSibling {\n\t\tif f.Filter(child) {\n\t\t\tp.printNode(child, true, verbatim)\n\t\t}\n\t}\n\tif outer {\n\t\tswitch node.DataAtom {\n\t\tcase atom.P, atom.Div, atom.Br, atom.H1, atom.H2, atom.H3, atom.H4,\n\t\t\tatom.H5, atom.H6, atom.Li, atom.Blockquote, atom.Pre:\n\t\t\tp.data = append(p.data, '\\n')\n\t\t\tp.notLineStart = false\n\t\t}\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// Text retrieves the text content of the NodeSet. It only retrieves from the\n// first node in the NodeSet. It ignores any error and returns an empty string\n// if there is an error.\nfunc (p NodeSet) Text__0() string {\n\tval, _ := p.Text__1()\n\treturn val\n}\n\n// Text retrieves the text content of the NodeSet. It only retrieves from the\n// first node in the NodeSet.\nfunc (p NodeSet) Text__1() (val string, err error) {\n\tnode, err := p.First()\n\tif err == nil {\n\t\tval = textOf(&node.Node, false, noFilter{})\n\t}\n\treturn\n}\n\n// Text retrieves the text content of the NodeSet with a node filter. It only\n// retrieves from the first node in the NodeSet.\n//\n// The node filter is used to filter the nodes when retrieving text content. If\n// the node filter returns false for a node, the node and its children will be\n// ignored when retrieving text content.\n//\n// The outer parameter specifies whether to include the text content of the\n// outer node itself. If outer is true, the text content of the outer node will\n// be included; otherwise, only the text content of the inner nodes will be\n// included.\nfunc Text[F NodeFilter](ns NodeSet, outer bool, f F) (val string, err error) {\n\tnode, err := ns.First()\n\tif err == nil {\n\t\tval = textOf(&node.Node, outer, f)\n\t}\n\treturn\n}\n\n// Int retrieves the integer value from the text content of the first node in\n// the NodeSet.\nfunc (p NodeSet) Int() (int, error) {\n\ttext, err := p.Text__1()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn dql.Int(text)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/json/json.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage json\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"iter\"\n\n\t\"github.com/goplus/xgo/dql/maps\"\n\t\"github.com/qiniu/x/stream\"\n)\n\nconst (\n\tXGoPackage = \"github.com/goplus/xgo/dql/maps\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Node represents a map[string]any or []any node.\ntype Node = maps.Node\n\n// NodeSet represents a set of JSON nodes.\ntype NodeSet = maps.NodeSet\n\n// New creates a JSON NodeSet from JSON data read from r.\nfunc New(r io.Reader) NodeSet {\n\tvar data any\n\terr := json.NewDecoder(r).Decode(&data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn maps.New(data)\n}\n\n// Source creates a JSON NodeSet from various source types:\n// - string: treats the string as a file path, opens the file, and reads JSON data from it.\n// - []byte: reads JSON data from the byte slice.\n// - io.Reader: reads JSON data from the provided reader.\n// - map[string]any: creates a NodeSet from the provided map.\n// - []any: creates a NodeSet from the provided slice.\n// - Node: creates a NodeSet containing the single provided node.\n// - iter.Seq[Node]: directly uses the provided sequence of nodes.\n// - NodeSet: returns the provided NodeSet as is.\n// If the source type is unsupported, it panics.\nfunc Source(r any) (ret NodeSet) {\n\tswitch v := r.(type) {\n\tcase string:\n\t\tf, err := stream.Open(v)\n\t\tif err != nil {\n\t\t\treturn NodeSet{Err: err}\n\t\t}\n\t\tdefer f.Close()\n\t\treturn New(f)\n\tcase []byte:\n\t\tr := bytes.NewReader(v)\n\t\treturn New(r)\n\tcase io.Reader:\n\t\treturn New(v)\n\tcase map[string]any, []any:\n\t\treturn maps.New(v)\n\tcase Node:\n\t\treturn maps.Root(v)\n\tcase iter.Seq[Node]:\n\t\treturn NodeSet{Data: v}\n\tcase NodeSet:\n\t\treturn v\n\tdefault:\n\t\tpanic(\"dql/json.Source: unsupported source type\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/maps/maps.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage maps\n\nimport (\n\t\"iter\"\n\n\t\"github.com/goplus/xgo/dql\"\n)\n\nconst (\n\tXGoPackage = true\n)\n\n// -----------------------------------------------------------------------------\n\n// NodeSet represents a set of nodes.\ntype NodeSet struct {\n\tData iter.Seq[Node]\n\tErr  error\n}\n\n// NodeSet(seq) casts a NodeSet from a sequence of nodes.\nfunc NodeSet_Cast(seq iter.Seq[Node]) NodeSet {\n\treturn NodeSet{Data: seq}\n}\n\n// Root creates a NodeSet containing the provided root node.\nfunc Root(doc Node) NodeSet {\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tyield(doc)\n\t\t},\n\t}\n}\n\n// Nodes creates a NodeSet containing the provided nodes.\nfunc Nodes(nodes ...Node) NodeSet {\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tfor _, node := range nodes {\n\t\t\t\tif !yield(node) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n// New creates a NodeSet containing a single node from the provided document.\n// The document should be of type map[string]any or []any.\n// If the document type is invalid, it panics.\nfunc New(doc any) NodeSet {\n\tswitch doc.(type) {\n\tcase map[string]any, []any:\n\tdefault:\n\t\tpanic(\"dql/maps.New: invalid document type, should be map[string]any or []any\")\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tyield(Node{Name: \"\", Value: doc})\n\t\t},\n\t}\n}\n\n// Source creates a NodeSet from various types of sources:\n// - map[string]any: creates a NodeSet containing the single provided node.\n// - []any: creates a NodeSet containing the single provided node.\n// - Node: creates a NodeSet containing the single provided node.\n// - iter.Seq[Node]: directly uses the provided sequence of nodes.\n// - NodeSet: returns the provided NodeSet as is.\n// If the source type is unsupported, it panics.\nfunc Source(r any) (ret NodeSet) {\n\tswitch v := r.(type) {\n\tcase map[string]any, []any:\n\t\treturn New(v)\n\tcase Node:\n\t\treturn Root(v)\n\tcase iter.Seq[Node]:\n\t\treturn NodeSet{Data: v}\n\tcase NodeSet:\n\t\treturn v\n\tdefault:\n\t\tpanic(\"dql/maps.Source: unsupported source type\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// XGo_Enum returns an iterator over the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Enum() iter.Seq[NodeSet] {\n\tif p.Err != nil {\n\t\treturn dql.NopIter[NodeSet]\n\t}\n\treturn func(yield func(NodeSet) bool) {\n\t\tp.Data(func(node Node) bool {\n\t\t\treturn yield(Root(node))\n\t\t})\n\t}\n}\n\n// XGo_Select returns a NodeSet containing the nodes with the specified name.\n//   - @name\n//   - @\"element-name\"\nfunc (p NodeSet) XGo_Select(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tp.Data(func(node Node) bool {\n\t\t\t\tif node.Name == name {\n\t\t\t\t\treturn yield(node)\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t},\n\t}\n}\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (p NodeSet) XGo_Elem(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tp.Data(func(node Node) bool {\n\t\t\t\treturn yieldElem(node, name, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldElem yields the child node with the specified name if it exists.\nfunc yieldElem(node Node, name string, yield func(Node) bool) bool {\n\tif children, ok := node.Value.(map[string]any); ok {\n\t\tif v, ok := children[name]; ok {\n\t\t\treturn yield(Node{Name: name, Value: v})\n\t\t}\n\t}\n\treturn true\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Child() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tp.Data(func(node Node) bool {\n\t\t\t\treturn yieldChildNodes(node, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldChildNodes yields all child nodes of the given node.\nfunc yieldChildNodes(node Node, yield func(Node) bool) bool {\n\tswitch children := node.Value.(type) {\n\tcase map[string]any:\n\t\tfor k, v := range children {\n\t\t\tif !yield(Node{Name: k, Value: v}) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\tcase []any:\n\t\tfor _, v := range children {\n\t\t\tif !yield(Node{Name: \"\", Value: v}) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// nodes themselves) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (p NodeSet) XGo_Any(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tp.Data(func(node Node) bool {\n\t\t\t\treturn yieldAnyNodes(name, node, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldAnyNodes yields all descendant nodes of the given node that match the\n// specified name. If name is \"\", it yields all nodes.\nfunc yieldAnyNodes(name string, node Node, yield func(Node) bool) bool {\n\tif name == \"\" || node.Name == name {\n\t\tif !yield(node) {\n\t\t\treturn false\n\t\t}\n\t}\n\tswitch children := node.Value.(type) {\n\tcase map[string]any:\n\t\tfor k, v := range children {\n\t\t\tif !yieldAnyNode(name, k, v, yield) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\tcase []any:\n\t\tfor _, v := range children {\n\t\t\tif !yieldAnyNode(name, \"\", v, yield) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// yieldAnyNode recursively traverses into v if it is a map[string]any or []any,\n// looking for descendant nodes matching name.\nfunc yieldAnyNode(name, k string, v any, yield func(Node) bool) bool {\n\tswitch v.(type) {\n\tcase map[string]any, []any:\n\t\treturn yieldAnyNodes(name, Node{Name: k, Value: v}, yield)\n\t}\n\treturn true\n}\n\n// -----------------------------------------------------------------------------\n\n// _all returns a NodeSet containing all nodes.\n// It's a cache operation for performance optimization when you need to traverse\n// the nodes multiple times.\nfunc (p NodeSet) XGo_all() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tnodes := dql.Collect(p.Data)\n\treturn Nodes(nodes...)\n}\n\n// _one returns a NodeSet containing the first node.\n// It's a performance optimization when you only need the first node (stop early).\nfunc (p NodeSet) XGo_one() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.First(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(n)\n}\n\n// _single returns a NodeSet containing the single node.\n// If there are zero or more than one nodes, it returns an error.\n// ErrNotFound or ErrMultipleResults is returned accordingly.\nfunc (p NodeSet) XGo_single() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.Single(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(n)\n}\n\n// -----------------------------------------------------------------------------\n\n// _ok returns true if there is no error in the NodeSet.\nfunc (p NodeSet) XGo_ok() bool {\n\treturn p.Err == nil\n}\n\n// _first returns the first node in the NodeSet.\nfunc (p NodeSet) XGo_first() (Node, error) {\n\tif p.Err != nil {\n\t\treturn Node{}, p.Err\n\t}\n\treturn dql.First(p.Data)\n}\n\n// _name returns the name of the first node in the NodeSet.\n// empty string is returned if the NodeSet is empty or error occurs.\nfunc (p NodeSet) XGo_name__0() string {\n\tval, _ := p.XGo_name__1()\n\treturn val\n}\n\n// _name returns the name of the first node in the NodeSet.\n// If the NodeSet is empty, it returns ErrNotFound.\nfunc (p NodeSet) XGo_name__1() (ret string, err error) {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\tret = node.Name\n\t}\n\treturn\n}\n\n// _value returns the value of the first node in the NodeSet.\n// nil is returned if the NodeSet is empty or error occurs.\nfunc (p NodeSet) XGo_value__0() any {\n\tval, _ := p.XGo_value__1()\n\treturn val\n}\n\n// _value returns the value of the first node in the NodeSet.\n// If the NodeSet is empty, it returns ErrNotFound.\nfunc (p NodeSet) XGo_value__1() (ret any, err error) {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\tret = node.Value\n\t}\n\treturn\n}\n\n// _hasAttr returns true if the first node in the NodeSet has the specified attribute.\n// It returns false otherwise.\nfunc (p NodeSet) XGo_hasAttr(name string) bool {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\treturn node.XGo_hasAttr(name)\n\t}\n\treturn false\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__0(name string) any {\n\tval, _ := p.XGo_Attr__1(name)\n\treturn val\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__1(name string) (val any, err error) {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\treturn node.XGo_Attr__1(name)\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/maps/node.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage maps\n\nimport (\n\t\"github.com/goplus/xgo/dql\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Node represents a named value in a DQL query tree.\ntype Node struct {\n\tName  string\n\tValue any\n}\n\n// XGo_Elem returns the child node with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (n Node) XGo_Elem(name string) (ret Node) {\n\tif children, ok := n.Value.(map[string]any); ok {\n\t\tif v, ok := children[name]; ok {\n\t\t\tret = Node{Name: name, Value: v}\n\t\t}\n\t}\n\treturn\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the node.\n//   - .*\nfunc (n Node) XGo_Child() NodeSet {\n\treturn Root(n).XGo_Child()\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// node itself) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (n Node) XGo_Any(name string) NodeSet {\n\treturn Root(n).XGo_Any(name)\n}\n\n// -----------------------------------------------------------------------------\n\n// _hasAttr returns true if the node has the specified attribute.\nfunc (n Node) XGo_hasAttr(name string) bool {\n\tswitch children := n.Value.(type) {\n\tcase map[string]any:\n\t\t_, ok := children[name]\n\t\treturn ok\n\t}\n\treturn false\n}\n\n// XGo_Attr returns the value of the specified attribute from the node.\n// If the attribute does not exist, it returns nil.\n//   - $name\n//   - $“attr-name”\nfunc (n Node) XGo_Attr__0(name string) any {\n\tval, _ := n.XGo_Attr__1(name)\n\treturn val\n}\n\n// XGo_Attr returns the value of the specified attribute from the node.\n// If the attribute does not exist, it returns ErrNotFound.\n//   - $name\n//   - $“attr-name”\nfunc (n Node) XGo_Attr__1(name string) (any, error) {\n\tswitch children := n.Value.(type) {\n\tcase map[string]any:\n\t\tif v, ok := children[name]; ok {\n\t\t\treturn v, nil\n\t\t}\n\t}\n\treturn nil, dql.ErrNotFound\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/reflects/node.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage reflects\n\nimport (\n\t\"reflect\"\n\n\t\"github.com/goplus/xgo/dql\"\n)\n\n// capitalize capitalizes the first letter of the given name.\nfunc capitalize(name string) string {\n\tif name != \"\" {\n\t\tif c := name[0]; c >= 'a' && c <= 'z' {\n\t\t\treturn string(c-'a'+'A') + name[1:]\n\t\t}\n\t}\n\treturn name\n}\n\n// uncapitalize uncapitalizes the first letter of the given name.\nfunc uncapitalize(name string) string {\n\tif name != \"\" {\n\t\tif c := name[0]; c >= 'A' && c <= 'Z' {\n\t\t\treturn string(c-'A'+'a') + name[1:]\n\t\t}\n\t}\n\treturn name\n}\n\nfunc allowAnyMethod(obj reflect.Value, name string) bool {\n\treturn true\n}\n\nvar (\n\ttyError = reflect.TypeFor[error]()\n)\n\nfunc lookup(obj reflect.Value, name string, allowMthd func(reflect.Value, string) bool) (ret reflect.Value) {\n\tkind, node := deref(obj)\n\tswitch kind {\n\tcase reflect.Struct:\n\t\tname = capitalize(name)\n\t\tret = node.FieldByName(name)\n\t\tif !ret.IsValid() {\n\t\t\tif mth := obj.MethodByName(name); mth.IsValid() {\n\t\t\t\tif t := mth.Type(); t.NumIn() == 0 && t.NumOut() == 1 && t.Out(0) != tyError {\n\t\t\t\t\tif allowMthd == nil {\n\t\t\t\t\t\treturn mth // only find the method, do not call it\n\t\t\t\t\t} else if allowMthd(obj, name) {\n\t\t\t\t\t\t// method call as attribute value\n\t\t\t\t\t\tret = mth.Call(nil)[0]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tcase reflect.Map:\n\t\tret = node.MapIndex(reflect.ValueOf(name))\n\t}\n\treturn\n}\n\nfunc deref(v reflect.Value) (reflect.Kind, reflect.Value) {\n\tkind := v.Kind()\n\tif kind == reflect.Interface {\n\t\tv = v.Elem()\n\t\tkind = v.Kind()\n\t}\n\tif kind == reflect.Pointer {\n\t\tv = v.Elem()\n\t\tkind = v.Kind()\n\t}\n\treturn kind, v\n}\n\n// -----------------------------------------------------------------------------\n\n// Node represents a named value in a DQL query tree.\ntype Node struct {\n\tName  string\n\tValue reflect.Value\n}\n\n// XGo_Elem returns the child node with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (n Node) XGo_Elem(name string) (ret Node) {\n\treturn n.XGo_ElemEx(name, allowAnyMethod)\n}\n\n// XGo_ElemEx returns the child node with the specified name.\n// It allows you to specify a custom function to determine whether to call a method.\n//   - .name\n//   - .“element-name”\nfunc (n Node) XGo_ElemEx(name string, allowMthd func(reflect.Value, string) bool) (ret Node) {\n\tif v := lookup(n.Value, name, allowMthd); v.IsValid() {\n\t\tret = Node{Name: name, Value: v}\n\t}\n\treturn\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the node.\n//   - .*\nfunc (n Node) XGo_Child() NodeSet {\n\treturn Root(n).XGo_Child()\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// node itself) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (n Node) XGo_Any(name string) NodeSet {\n\treturn Root(n).XGo_Any(name)\n}\n\n// -----------------------------------------------------------------------------\n\n// _hasAttr checks if the node has an attribute with the specified name.\nfunc (n Node) XGo_hasAttr(name string) bool {\n\treturn lookup(n.Value, name, nil).IsValid()\n}\n\n// XGo_Attr returns the value of the attribute with the specified name.\n// If the attribute does not exist, it returns nil.\n//   - $name\n//   - $“attr-name”\nfunc (n Node) XGo_Attr__0(name string) any {\n\tval, _ := n.XGo_AttrEx(name, allowAnyMethod)\n\treturn val\n}\n\n// XGo_Attr returns the value of the attribute with the specified name.\n// If the attribute does not exist, it returns ErrNotFound.\n//   - $name\n//   - $“attr-name”\nfunc (n Node) XGo_Attr__1(name string) (any, error) {\n\treturn n.XGo_AttrEx(name, allowAnyMethod)\n}\n\n// XGo_AttrEx returns the value of the attribute with the specified name.\n// If the attribute does not exist, it returns ErrNotFound.\n// It allows you to specify a custom function to determine whether to call a method.\n//   - $name\n//   - $“attr-name”\nfunc (n Node) XGo_AttrEx(name string, allowMthd func(reflect.Value, string) bool) (any, error) {\n\tif v := lookup(n.Value, name, allowMthd); v.IsValid() {\n\t\treturn v.Interface(), nil\n\t}\n\treturn nil, dql.ErrNotFound\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/reflects/reflects.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage reflects\n\nimport (\n\t\"iter\"\n\t\"reflect\"\n\n\t\"github.com/goplus/xgo/dql\"\n)\n\nconst (\n\tXGoPackage = true\n)\n\n// -----------------------------------------------------------------------------\n\n// NodeSet represents a set of nodes.\ntype NodeSet struct {\n\tData iter.Seq[Node]\n\tErr  error\n}\n\n// NodeSet(seq) casts a NodeSet from a sequence of nodes.\nfunc NodeSet_Cast(seq iter.Seq[Node]) NodeSet {\n\treturn NodeSet{Data: seq}\n}\n\n// Root creates a NodeSet containing the provided root node.\nfunc Root(doc Node) NodeSet {\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tyield(doc)\n\t\t},\n\t}\n}\n\n// Nodes creates a NodeSet containing the provided nodes.\nfunc Nodes(nodes ...Node) NodeSet {\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tfor _, node := range nodes {\n\t\t\t\tif !yield(node) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n// New creates a NodeSet containing a single provided node.\nfunc New(doc reflect.Value) NodeSet {\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tyield(Node{Name: \"\", Value: doc})\n\t\t},\n\t}\n}\n\n// Source creates a NodeSet from various types of sources:\n// - reflect.Value: creates a NodeSet containing the single provided node.\n// - Node: creates a NodeSet containing the single provided node.\n// - iter.Seq[Node]: directly uses the provided sequence of nodes.\n// - NodeSet: returns the provided NodeSet as is.\n// - any other type: uses reflect.ValueOf to create a NodeSet.\nfunc Source(r any) (ret NodeSet) {\n\tswitch v := r.(type) {\n\tcase reflect.Value:\n\t\treturn New(v)\n\tcase Node:\n\t\treturn Root(v)\n\tcase iter.Seq[Node]:\n\t\treturn NodeSet{Data: v}\n\tcase NodeSet:\n\t\treturn v\n\tdefault:\n\t\treturn New(reflect.ValueOf(r))\n\t}\n}\n\n// XGo_Enum returns an iterator over the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Enum() iter.Seq[NodeSet] {\n\tif p.Err != nil {\n\t\treturn dql.NopIter[NodeSet]\n\t}\n\treturn func(yield func(NodeSet) bool) {\n\t\tp.Data(func(node Node) bool {\n\t\t\treturn yield(Root(node))\n\t\t})\n\t}\n}\n\n// XGo_Select returns a NodeSet containing the nodes with the specified name.\n//   - @name\n//   - @\"element-name\"\nfunc (p NodeSet) XGo_Select(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tp.Data(func(node Node) bool {\n\t\t\t\tif node.Name == name {\n\t\t\t\t\treturn yield(node)\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t},\n\t}\n}\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (p NodeSet) XGo_Elem(name string) NodeSet {\n\treturn p.XGo_ElemEx(name, allowAnyMethod)\n}\n\n// XGo_ElemEx returns a NodeSet containing the child nodes with the specified name.\n// It allows you to specify a custom function to determine whether to call a method.\n//   - .name\n//   - .“element-name”\nfunc (p NodeSet) XGo_ElemEx(name string, allowMthd func(reflect.Value, string) bool) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tp.Data(func(node Node) bool {\n\t\t\t\treturn yieldElem(node, name, allowMthd, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldElem yields the child node with the specified name if it exists.\nfunc yieldElem(node Node, name string, allowMthd func(reflect.Value, string) bool, yield func(Node) bool) bool {\n\tif v := lookup(node.Value, name, allowMthd); v.IsValid() {\n\t\treturn yield(Node{Name: name, Value: v})\n\t}\n\treturn true\n}\n\nfunc yieldChildNodes(node reflect.Value, yield func(Node) bool) bool {\n\tkind, node := deref(node)\n\tswitch kind {\n\tcase reflect.Struct:\n\t\ttyp := node.Type()\n\t\tfor i, n := 0, typ.NumField(); i < n; i++ {\n\t\t\tif v := node.Field(i); v.CanInterface() { // only yield exported fields\n\t\t\t\tif !yield(Node{Name: uncapitalize(typ.Field(i).Name), Value: v}) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tcase reflect.Map:\n\t\ttyp := node.Type()\n\t\tif typ.Key().Kind() != reflect.String { // Only support map[string]T\n\t\t\tbreak\n\t\t}\n\t\tit := node.MapRange()\n\t\tfor it.Next() {\n\t\t\tif !yield(Node{Name: it.Key().String(), Value: it.Value()}) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\tcase reflect.Slice:\n\t\tfor i := 0; i < node.Len(); i++ {\n\t\t\tif !yield(Node{Name: \"\", Value: node.Index(i)}) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// yieldAnyNodes yields all descendant nodes of the given node that match the\n// specified name. If name is \"\", it yields all nodes.\nfunc yieldAnyNodes(name string, node Node, yield func(Node) bool) bool {\n\tif name == \"\" || node.Name == name {\n\t\tif !yield(node) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn yieldChildNodes(node.Value, func(n Node) bool {\n\t\treturn yieldAnyNodes(name, n, yield)\n\t})\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Child() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tp.Data(func(node Node) bool {\n\t\t\t\treturn yieldChildNodes(node.Value, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// nodes themselves) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (p NodeSet) XGo_Any(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(Node) bool) {\n\t\t\tp.Data(func(node Node) bool {\n\t\t\t\treturn yieldAnyNodes(name, node, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// _all returns a NodeSet containing all nodes.\n// It's a cache operation for performance optimization when you need to traverse\n// the nodes multiple times.\nfunc (p NodeSet) XGo_all() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tnodes := dql.Collect(p.Data)\n\treturn Nodes(nodes...)\n}\n\n// _one returns a NodeSet containing the first node.\n// It's a performance optimization when you only need the first node (stop early).\nfunc (p NodeSet) XGo_one() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.First(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(n)\n}\n\n// _single returns a NodeSet containing the single node.\n// If there are zero or more than one nodes, it returns an error.\n// ErrNotFound or ErrMultipleResults is returned accordingly.\nfunc (p NodeSet) XGo_single() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.Single(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(n)\n}\n\n// -----------------------------------------------------------------------------\n\n// _ok returns true if there is no error in the NodeSet.\nfunc (p NodeSet) XGo_ok() bool {\n\treturn p.Err == nil\n}\n\n// _first returns the first node in the NodeSet.\nfunc (p NodeSet) XGo_first() (Node, error) {\n\tif p.Err != nil {\n\t\treturn Node{}, p.Err\n\t}\n\treturn dql.First(p.Data)\n}\n\n// _name returns the name of the first node in the NodeSet.\n// empty string is returned if the NodeSet is empty or error occurs.\nfunc (p NodeSet) XGo_name__0() string {\n\tval, _ := p.XGo_name__1()\n\treturn val\n}\n\n// _name returns the name of the first node in the NodeSet.\n// If the NodeSet is empty, it returns ErrNotFound.\nfunc (p NodeSet) XGo_name__1() (ret string, err error) {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\tret = node.Name\n\t}\n\treturn\n}\n\n// _value returns the value of the first node in the NodeSet.\n// nil is returned if the NodeSet is empty or error occurs.\nfunc (p NodeSet) XGo_value__0() any {\n\tval, _ := p.XGo_value__1()\n\treturn val\n}\n\n// _value returns the value of the first node in the NodeSet.\n// If the NodeSet is empty, it returns ErrNotFound.\nfunc (p NodeSet) XGo_value__1() (ret any, err error) {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\tret = node.Value.Interface()\n\t}\n\treturn\n}\n\n// XGo_class returns the class name of the first node in the NodeSet.\nfunc (p NodeSet) XGo_class() (class string) {\n\tnode, err := p.XGo_first()\n\tif err != nil {\n\t\treturn\n\t}\n\t_, v := deref(node.Value)\n\tif v.IsValid() {\n\t\treturn v.Type().Name()\n\t}\n\treturn \"\"\n}\n\n// _hasAttr returns true if the first node in the NodeSet has the specified attribute.\n// It returns false otherwise.\nfunc (p NodeSet) XGo_hasAttr(name string) bool {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\treturn node.XGo_hasAttr(name)\n\t}\n\treturn false\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__0(name string) any {\n\tval, _ := p.XGo_AttrEx(name, allowAnyMethod)\n\treturn val\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__1(name string) (any, error) {\n\treturn p.XGo_AttrEx(name, allowAnyMethod)\n}\n\n// XGo_AttrEx returns the value of the specified attribute from the first node in the\n// NodeSet. It allows you to specify a custom function to determine whether to call a\n// method.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_AttrEx(name string, allowMthd func(reflect.Value, string) bool) (any, error) {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\treturn node.XGo_AttrEx(name, allowMthd)\n\t}\n\treturn nil, err\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/xgo/parse.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xgo\n\nimport (\n\t\"unsafe\"\n\n\t\"github.com/goplus/xgo/ast\"\n)\n\n// -----------------------------------------------------------------------------\n\n// File represents a XGo file.\ntype File struct {\n\tast.File\n\t// File must contain only the embedded ast.File field.\n}\n\n// ParseFile parses XGo source code from the given filename or source, returning a File\n// object. An optional Config can be provided to customize the parsing behavior.\nfunc ParseFile(filename string, src any, conf ...Config) (f *File, err error) {\n\tdoc, err := parse(filename, src, conf...)\n\tif err == nil {\n\t\tf = (*File)(unsafe.Pointer(doc))\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (f *File) XGo_Elem(name string) NodeSet {\n\treturn New(&f.File).XGo_Elem(name)\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the node.\n//   - .*\nfunc (f *File) XGo_Child() NodeSet {\n\treturn New(&f.File).XGo_Child()\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// node itself) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (f *File) XGo_Any(name string) NodeSet {\n\treturn New(&f.File).XGo_Any(name)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/xgo/xgo.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xgo\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"iter\"\n\t\"reflect\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/dql\"\n\t\"github.com/goplus/xgo/dql/reflects\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/qiniu/x/stream\"\n)\n\nconst (\n\tXGoPackage = \"github.com/goplus/xgo/dql/reflects\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Node represents a XGo AST node.\ntype Node = reflects.Node\n\n// NodeSet represents a set of XGo AST nodes.\ntype NodeSet struct {\n\treflects.NodeSet\n}\n\n// NodeSet(seq) casts a NodeSet from a sequence of nodes.\nfunc NodeSet_Cast(seq iter.Seq[Node]) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: reflects.NodeSet{Data: seq},\n\t}\n}\n\n// Root creates a NodeSet containing the provided root node.\nfunc Root(doc Node) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: reflects.Root(doc),\n\t}\n}\n\n// Nodes creates a NodeSet containing the provided nodes.\nfunc Nodes(nodes ...Node) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: reflects.Nodes(nodes...),\n\t}\n}\n\n// New creates a NodeSet from the given *ast.File.\nfunc New(f *ast.File) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: reflects.New(reflect.ValueOf(f)),\n\t}\n}\n\n// Config represents the configuration for parsing XGo source code.\ntype Config struct {\n\tMode parser.Mode\n\tFset *token.FileSet\n}\n\nconst (\n\tdefaultMode = parser.ParseComments\n)\n\n// parse parses XGo source code from the given URI or source.\nfunc parse(uri string, src any, conf ...Config) (f *ast.File, err error) {\n\tin, err := stream.ReadSourceFromURI(uri, src)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar c Config\n\tif len(conf) > 0 {\n\t\tc = conf[0]\n\t} else {\n\t\tc.Mode = defaultMode\n\t}\n\tif c.Fset == nil {\n\t\tc.Fset = token.NewFileSet()\n\t}\n\treturn parser.ParseFile(c.Fset, uri, in, c.Mode)\n}\n\n// From parses XGo source code from the given URI or source, returning a NodeSet.\n// An optional Config can be provided to customize the parsing behavior.\nfunc From(uri string, src any, conf ...Config) NodeSet {\n\tf, err := parse(uri, src, conf...)\n\tif err != nil {\n\t\treturn NodeSet{NodeSet: reflects.NodeSet{Err: err}}\n\t}\n\treturn New(f)\n}\n\n// Source creates a NodeSet from various types of XGo sources.\n// It supports the following source types:\n// - string: treats the string as a URI, opens the resource, and reads XGo source code from it.\n// - []byte: treated as XGo source code.\n// - *bytes.Buffer: treated as XGo source code.\n// - io.Reader: treated as XGo source code.\n// - *ast.File: creates a NodeSet from the provided *ast.File.\n// - reflect.Value: creates a NodeSet from the provided reflect.Value (expected to be *ast.File).\n// - Node: creates a NodeSet containing the single provided node.\n// - iter.Seq[Node]: returns the provided sequence as a NodeSet.\n// - NodeSet: returns the provided NodeSet as is.\n// If the source type is unsupported, it panics.\nfunc Source(r any, conf ...Config) (ret NodeSet) {\n\tswitch v := r.(type) {\n\tcase string:\n\t\treturn From(v, nil, conf...)\n\tcase []byte:\n\t\treturn From(\"\", v, conf...)\n\tcase *bytes.Buffer:\n\t\treturn From(\"\", v, conf...)\n\tcase io.Reader:\n\t\treturn From(\"\", v, conf...)\n\tcase *ast.File:\n\t\treturn New(v)\n\tcase reflect.Value:\n\t\treturn NodeSet{NodeSet: reflects.New(v)}\n\tcase Node:\n\t\treturn NodeSet{NodeSet: reflects.Root(v)}\n\tcase iter.Seq[Node]:\n\t\treturn NodeSet{NodeSet: reflects.NodeSet{Data: v}}\n\tcase NodeSet:\n\t\treturn v\n\tdefault:\n\t\tpanic(\"dql/xgo.Source: unsupported source type\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// XGo_Enum returns an iterator over the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Enum() iter.Seq[NodeSet] {\n\tif p.Err != nil {\n\t\treturn dql.NopIter[NodeSet]\n\t}\n\treturn func(yield func(NodeSet) bool) {\n\t\tp.Data(func(node Node) bool {\n\t\t\treturn yield(Root(node))\n\t\t})\n\t}\n}\n\n// XGo_Select returns a NodeSet containing the nodes with the specified name.\n//   - @name\n//   - @\"element-name\"\nfunc (p NodeSet) XGo_Select(name string) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_Select(name),\n\t}\n}\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (p NodeSet) XGo_Elem(name string) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_Elem(name),\n\t}\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Child() NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_Child(),\n\t}\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// nodes themselves) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (p NodeSet) XGo_Any(name string) NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_Any(name),\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// All returns a NodeSet containing all nodes.\n// It's a cache operation for performance optimization when you need to traverse\n// the nodes multiple times.\nfunc (p NodeSet) All() NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_all(),\n\t}\n}\n\n// One returns a NodeSet containing the first node.\n// It's a performance optimization when you only need the first node (stop early).\nfunc (p NodeSet) One() NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_one(),\n\t}\n}\n\n// Single returns a NodeSet containing the single node.\n// If there are zero or more than one nodes, it returns an error.\n// ErrNotFound or ErrMultipleResults is returned accordingly.\nfunc (p NodeSet) Single() NodeSet {\n\treturn NodeSet{\n\t\tNodeSet: p.NodeSet.XGo_single(),\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// Ok returns true if there is no error in the NodeSet.\nfunc (p NodeSet) Ok() bool {\n\treturn p.Err == nil\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__0(name string) any {\n\tval, _ := p.XGo_Attr__1(name)\n\treturn val\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__1(name string) (val any, err error) {\n\tval, err = p.NodeSet.XGo_Attr__1(name)\n\tif err == nil {\n\t\tswitch v := val.(type) {\n\t\tcase *ast.Ident:\n\t\t\tif v != nil {\n\t\t\t\treturn v.Name, nil\n\t\t\t}\n\t\t\treturn \"\", nil\n\t\tcase *ast.BasicLit:\n\t\t\tif v != nil {\n\t\t\t\treturn v.Value, nil\n\t\t\t}\n\t\t\treturn \"\", nil\n\t\t}\n\t}\n\treturn\n}\n\n// Class returns the class name of the first node in the NodeSet.\nfunc (p NodeSet) Class() string {\n\treturn p.XGo_class()\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/xml/node.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xml\n\nimport (\n\t\"encoding/xml\"\n\t\"io\"\n\n\t\"github.com/goplus/xgo/dql\"\n)\n\n// -----------------------------------------------------------------------------\n\n// A CharData represents XML character data (raw text),\n// in which XML escape sequences have been replaced by\n// the characters they represent.\ntype CharData = xml.CharData\n\n// A Name represents an XML name (Local) annotated\n// with a name space identifier (Space).\n// In tokens returned by [Decoder.Token], the Space identifier\n// is given as a canonical URL, not the short prefix used\n// in the document being parsed.\ntype Name = xml.Name\n\n// An Attr represents an attribute in an XML element (Name=Value).\ntype Attr = xml.Attr\n\n// Node represents a generic XML node with its name, attributes, and children.\ntype Node struct {\n\tName     xml.Name\n\tAttr     []xml.Attr\n\tChildren []any // can be *Node or xml.CharData\n}\n\n// Parse returns the parse tree for the XML from the given Reader.\nfunc Parse(r io.Reader) (doc *Node, err error) {\n\tdoc = new(Node)\n\terr = xml.NewDecoder(r).Decode(doc)\n\treturn\n}\n\n// UnmarshalXML implements the xml.Unmarshaler interface for the Node struct.\nfunc (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tn.Name = start.Name\n\t// The start.Attr slice is owned by the xml.Decoder and is only valid\n\t// until the next call to d.Token().\n\t// It must be copied to be stored in the Node struct, otherwise it can\n\t// lead to data corruption.\n\tn.Attr = append([]xml.Attr(nil), start.Attr...)\n\tfor {\n\t\ttoken, err := d.Token()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tswitch t := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\tchild := &Node{}\n\t\t\tif err := d.DecodeElement(child, &t); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tn.Children = append(n.Children, child)\n\n\t\tcase xml.CharData:\n\t\t\t// CharData tokens must be copied before storage\n\t\t\ttext := append(xml.CharData(nil), t...)\n\t\t\tn.Children = append(n.Children, text)\n\n\t\tcase xml.EndElement:\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (n *Node) XGo_Elem(name string) NodeSet {\n\treturn Root(n).XGo_Elem(name)\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the node.\n//   - .*\nfunc (n *Node) XGo_Child() NodeSet {\n\treturn Root(n).XGo_Child()\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// node itself) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (n *Node) XGo_Any(name string) NodeSet {\n\treturn Root(n).XGo_Any(name)\n}\n\n// _dump prints the node for debugging purposes.\nfunc (n *Node) XGo_dump() NodeSet {\n\treturn Root(n).XGo_dump()\n}\n\n// -----------------------------------------------------------------------------\n\n// _hasAttr returns true if the node has the specified attribute.\nfunc (n *Node) XGo_hasAttr(name string) bool {\n\tfor _, attr := range n.Attr {\n\t\tif attr.Name.Local == name {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// XGo_Attr returns the value of the specified attribute from the node.\n// If the attribute is not found, it returns an empty string.\n//   - $name\n//   - $“attr-name”\nfunc (n *Node) XGo_Attr__0(name string) string {\n\tval, _ := n.XGo_Attr__1(name)\n\treturn val\n}\n\n// XGo_Attr returns the value of the specified attribute from the node.\n// If the attribute is not found, it returns ErrNotFound.\n//   - $name\n//   - $“attr-name”\nfunc (n *Node) XGo_Attr__1(name string) (string, error) {\n\tfor _, attr := range n.Attr {\n\t\tif attr.Name.Local == name {\n\t\t\treturn attr.Value, nil\n\t\t}\n\t}\n\treturn \"\", dql.ErrNotFound\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/xml/text.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xml\n\nimport (\n\t\"encoding/xml\"\n\n\t\"github.com/goplus/xgo/dql\"\n)\n\n// -----------------------------------------------------------------------------\n\n// _text retrieves the text content of the first child xml.CharData.\n// It only retrieves from the first node in the NodeSet.\nfunc (p NodeSet) XGo_text__0() string {\n\tval, _ := p.XGo_text__1()\n\treturn val\n}\n\n// _text retrieves the text content of the first child xml.CharData.\n// It only retrieves from the first node in the NodeSet.\nfunc (p NodeSet) XGo_text__1() (val string, err error) {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\tfor _, c := range node.Children {\n\t\t\tif data, ok := c.(xml.CharData); ok {\n\t\t\t\treturn string(data), nil\n\t\t\t}\n\t\t}\n\t\terr = dql.ErrNotFound // text not found on first node\n\t}\n\treturn\n}\n\n// _int retrieves the integer value from the text content of the first child\n// text node. It only retrieves from the first node in the NodeSet.\nfunc (p NodeSet) XGo_int() (int, error) {\n\ttext, err := p.XGo_text__1()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn dql.Int(text)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/xml/xml.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xml\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"iter\"\n\t\"os\"\n\n\t\"github.com/goplus/xgo/dql\"\n\t\"github.com/qiniu/x/stream\"\n)\n\nconst (\n\tXGoPackage = true\n)\n\n// -----------------------------------------------------------------------------\n\n// NodeSet represents a set of XML nodes.\ntype NodeSet struct {\n\tData iter.Seq[*Node]\n\tErr  error\n}\n\n// NodeSet(seq) casts a NodeSet from a sequence of nodes.\nfunc NodeSet_Cast(seq iter.Seq[*Node]) NodeSet {\n\treturn NodeSet{Data: seq}\n}\n\n// Root creates a NodeSet containing the provided root node.\nfunc Root(doc *Node) NodeSet {\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tyield(doc)\n\t\t},\n\t}\n}\n\n// Nodes creates a NodeSet containing the provided nodes.\nfunc Nodes(nodes ...*Node) NodeSet {\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tfor _, node := range nodes {\n\t\t\t\tif !yield(node) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n// New parses the XML document from the provided reader and returns a NodeSet\n// containing the root node. If there is an error during parsing, the NodeSet's\n// Err field is set.\nfunc New(r io.Reader) NodeSet {\n\tdoc, err := Parse(r)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tyield(doc)\n\t\t},\n\t}\n}\n\n// Source creates a NodeSet from various types of sources:\n// - string: treated as an URL to read XML content from.\n// - []byte: treated as raw XML content.\n// - io.Reader: reads XML content from the reader.\n// - *Node: creates a NodeSet containing the single provided node.\n// - iter.Seq[*Node]: directly uses the provided sequence of nodes.\n// - NodeSet: returns the provided NodeSet as is.\n// If the source type is unsupported, it panics.\nfunc Source(r any) (ret NodeSet) {\n\tswitch v := r.(type) {\n\tcase string:\n\t\tf, err := stream.Open(v)\n\t\tif err != nil {\n\t\t\treturn NodeSet{Err: err}\n\t\t}\n\t\tdefer f.Close()\n\t\treturn New(f)\n\tcase []byte:\n\t\tr := bytes.NewReader(v)\n\t\treturn New(r)\n\tcase io.Reader:\n\t\treturn New(v)\n\tcase *Node:\n\t\treturn Root(v)\n\tcase iter.Seq[*Node]:\n\t\treturn NodeSet{Data: v}\n\tcase NodeSet:\n\t\treturn v\n\tdefault:\n\t\tpanic(\"dql/xml.Source: unsupported source type\")\n\t}\n}\n\n// XGo_Enum returns an iterator over the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Enum() iter.Seq[NodeSet] {\n\tif p.Err != nil {\n\t\treturn dql.NopIter[NodeSet]\n\t}\n\treturn func(yield func(NodeSet) bool) {\n\t\tp.Data(func(node *Node) bool {\n\t\t\treturn yield(Root(node))\n\t\t})\n\t}\n}\n\n// XGo_Select returns a NodeSet containing the nodes with the specified name.\n//   - @name\n//   - @\"element-name\"\nfunc (p NodeSet) XGo_Select(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn selectNode(node, name, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// selectNode yields the node if it matches the specified name.\nfunc selectNode(node *Node, name string, yield func(*Node) bool) bool {\n\tif node.Name.Local == name {\n\t\treturn yield(node)\n\t}\n\treturn true\n}\n\n// XGo_Elem returns a NodeSet containing the child nodes with the specified name.\n//   - .name\n//   - .“element-name”\nfunc (p NodeSet) XGo_Elem(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldNode(node, name, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldNode yields the child node with the specified name if it exists.\nfunc yieldNode(n *Node, name string, yield func(*Node) bool) bool {\n\tfor _, c := range n.Children {\n\t\tif child, ok := c.(*Node); ok {\n\t\t\tif child.Name.Local == name {\n\t\t\t\tif !yield(child) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// XGo_Child returns a NodeSet containing all child nodes of the nodes in the NodeSet.\nfunc (p NodeSet) XGo_Child() NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(n *Node) bool {\n\t\t\t\treturn yieldChildNodes(n, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldChildNodes yields all child nodes of the given node.\nfunc yieldChildNodes(n *Node, yield func(*Node) bool) bool {\n\tfor _, c := range n.Children {\n\t\tif child, ok := c.(*Node); ok {\n\t\t\tif !yield(child) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// XGo_Any returns a NodeSet containing all descendant nodes (including the\n// nodes themselves) with the specified name.\n// If name is \"\", it returns all nodes.\n//   - .**.name\n//   - .**.“element-name”\n//   - .**.*\nfunc (p NodeSet) XGo_Any(name string) NodeSet {\n\tif p.Err != nil {\n\t\treturn p\n\t}\n\treturn NodeSet{\n\t\tData: func(yield func(*Node) bool) {\n\t\t\tp.Data(func(node *Node) bool {\n\t\t\t\treturn yieldAnyNodes(node, name, yield)\n\t\t\t})\n\t\t},\n\t}\n}\n\n// yieldAnyNodes yields all descendant nodes of the given node that match the\n// specified name. If name is \"\", it yields all nodes.\nfunc yieldAnyNodes(n *Node, name string, yield func(*Node) bool) bool {\n\tif name == \"\" || n.Name.Local == name {\n\t\tif !yield(n) {\n\t\t\treturn false\n\t\t}\n\t}\n\tfor _, c := range n.Children {\n\t\tif child, ok := c.(*Node); ok {\n\t\t\tif !yieldAnyNodes(child, name, yield) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// -----------------------------------------------------------------------------\n\n// _all returns a NodeSet containing all nodes.\n// It's a cache operation for performance optimization when you need to traverse\n// the nodes multiple times.\nfunc (p NodeSet) XGo_all() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tnodes := dql.Collect(p.Data)\n\treturn Nodes(nodes...)\n}\n\n// _one returns a NodeSet containing the first node.\n// It's a performance optimization when you only need the first node (stop early).\nfunc (p NodeSet) XGo_one() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.First(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(n)\n}\n\n// _single returns a NodeSet containing the single node.\n// If there are zero or more than one nodes, it returns an error.\n// ErrNotFound or ErrMultiEntities is returned accordingly.\nfunc (p NodeSet) XGo_single() NodeSet {\n\tif p.Err != nil {\n\t\treturn NodeSet{Err: p.Err}\n\t}\n\tn, err := dql.Single(p.Data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn Root(n)\n}\n\n// -----------------------------------------------------------------------------\n\n// _dump prints the nodes in the NodeSet for debugging purposes.\nfunc (p NodeSet) XGo_dump() NodeSet {\n\tif p.Err == nil {\n\t\tp.Data(func(node *Node) bool {\n\t\t\tfmt.Fprintln(os.Stderr, \"node:\", node.Name.Local, node.Attr)\n\t\t\treturn true\n\t\t})\n\t}\n\treturn p\n}\n\n// -----------------------------------------------------------------------------\n\n// _ok returns true if there is no error in the NodeSet.\nfunc (p NodeSet) XGo_ok() bool {\n\treturn p.Err == nil\n}\n\n// _first returns the first node in the NodeSet.\nfunc (p NodeSet) XGo_first() (*Node, error) {\n\tif p.Err != nil {\n\t\treturn nil, p.Err\n\t}\n\treturn dql.First(p.Data)\n}\n\n// _hasAttr returns true if the first node in the NodeSet has the specified attribute.\n// It returns false otherwise.\nfunc (p NodeSet) XGo_hasAttr(name string) bool {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\treturn node.XGo_hasAttr(name)\n\t}\n\treturn false\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__0(name string) string {\n\tval, _ := p.XGo_Attr__1(name)\n\treturn val\n}\n\n// XGo_Attr returns the value of the specified attribute from the first node in the\n// NodeSet. It only retrieves the attribute from the first node.\n//   - $name\n//   - $“attr-name”\nfunc (p NodeSet) XGo_Attr__1(name string) (val string, err error) {\n\tnode, err := p.XGo_first()\n\tif err == nil {\n\t\treturn node.XGo_Attr__1(name)\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "dql/yaml/yaml.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage yaml\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"iter\"\n\n\t\"github.com/goccy/go-yaml\"\n\t\"github.com/goplus/xgo/dql/maps\"\n\t\"github.com/qiniu/x/stream\"\n)\n\nconst (\n\tXGoPackage = \"github.com/goplus/xgo/dql/maps\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Node represents a map[string]any or []any node.\ntype Node = maps.Node\n\n// NodeSet represents a set of YAML nodes.\ntype NodeSet = maps.NodeSet\n\n// New creates a YAML NodeSet from YAML data read from r.\nfunc New(r io.Reader, opts ...yaml.DecodeOption) NodeSet {\n\tvar data any\n\terr := yaml.NewDecoder(r, opts...).Decode(&data)\n\tif err != nil {\n\t\treturn NodeSet{Err: err}\n\t}\n\treturn maps.New(data)\n}\n\n// Source creates a YAML NodeSet from various source types:\n// - string: treats the string as a file path, opens the file, and reads YAML data from it.\n// - []byte: reads YAML data from the byte slice.\n// - io.Reader: reads YAML data from the provided reader.\n// - map[string]any: creates a NodeSet from the provided map.\n// - []any: creates a NodeSet from the provided slice.\n// - Node: creates a NodeSet containing the single provided node.\n// - iter.Seq[Node]: directly uses the provided sequence of nodes.\n// - NodeSet: returns the provided NodeSet as is.\n// If the source type is unsupported, it panics.\nfunc Source(r any, opts ...yaml.DecodeOption) (ret NodeSet) {\n\tswitch v := r.(type) {\n\tcase string:\n\t\tf, err := stream.Open(v)\n\t\tif err != nil {\n\t\t\treturn NodeSet{Err: err}\n\t\t}\n\t\tdefer f.Close()\n\t\treturn New(f, opts...)\n\tcase []byte:\n\t\tr := bytes.NewReader(v)\n\t\treturn New(r, opts...)\n\tcase io.Reader:\n\t\treturn New(v, opts...)\n\tcase map[string]any, []any:\n\t\treturn maps.New(v)\n\tcase Node:\n\t\treturn maps.Root(v)\n\tcase iter.Seq[Node]:\n\t\treturn NodeSet{Data: v}\n\tcase NodeSet:\n\t\treturn v\n\tdefault:\n\t\tpanic(\"dql/yaml.Source: unsupported source type\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "encoding/csv/csv.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage csv\n\nimport (\n\t\"encoding/csv\"\n\t\"strings\"\n)\n\n// Object is a type alias for a 2D slice of strings, representing the rows and\n// columns of a CSV file.\ntype Object = [][]string\n\n// New creates a new CSV object from a string.\nfunc New(text string) (records Object, err error) {\n\treturn csv.NewReader(strings.NewReader(text)).ReadAll()\n}\n"
  },
  {
    "path": "encoding/fs/fs.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage fs\n\nimport (\n\t\"github.com/goplus/xgo/dql/fs\"\n)\n\n// New returns a new fs.NodeSet for the specified directory.\nfunc New(dir string) fs.NodeSet {\n\treturn fs.Dir(dir)\n}\n"
  },
  {
    "path": "encoding/golang/golang.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage golang\n\nimport (\n\t\"go/parser\"\n\t\"strings\"\n\n\t\"github.com/goplus/xgo/dql/golang\"\n)\n\nconst (\n\tXGoPackage = \"github.com/goplus/xgo/dql/golang\"\n)\n\n// Object represents a Go File.\ntype Object = *golang.File\n\n// New parses Go source code from the given text, returning a Go File object.\n// An optional parser Mode can be provided to customize the parsing behavior.\nfunc New(text string, mode ...parser.Mode) (f Object, err error) {\n\tvar conf []golang.Config\n\tif len(mode) > 0 {\n\t\tconf = []golang.Config{{Mode: mode[0]}}\n\t}\n\treturn golang.ParseFile(\"\", strings.NewReader(text), conf...)\n}\n"
  },
  {
    "path": "encoding/html/html.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage html\n\nimport (\n\t\"strings\"\n\n\t\"github.com/goplus/xgo/dql/html\"\n)\n\nconst (\n\tXGoPackage = \"github.com/goplus/xgo/dql/html\"\n)\n\n// Object represents an HTML object.\ntype Object = *html.Node\n\n// New creates a new HTML object from a string.\nfunc New(text string) (ret Object, err error) {\n\treturn html.Parse(strings.NewReader(text))\n}\n"
  },
  {
    "path": "encoding/json/json.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage json\n\nimport (\n\t\"encoding/json\"\n\t\"strings\"\n)\n\n// Object is a type alias for any JSON value, which can be a map, slice, string,\n// number, boolean, or null.\ntype Object = any\n\n// New creates a new JSON object from a string.\nfunc New(text string) (ret Object, err error) {\n\terr = json.NewDecoder(strings.NewReader(text)).Decode(&ret)\n\treturn\n}\n"
  },
  {
    "path": "encoding/regexp/regexp.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage regexp\n\nimport (\n\t\"regexp\"\n)\n\n// Object is a type alias for a compiled regular expression.\ntype Object = *regexp.Regexp\n\n// New creates a new regexp object from a string.\nfunc New(text string) (Object, error) {\n\treturn regexp.Compile(text)\n}\n"
  },
  {
    "path": "encoding/regexposix/regexp.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage regexposix\n\nimport (\n\t\"regexp\"\n)\n\n// Object is a type alias for a compiled POSIX regular expression.\ntype Object = *regexp.Regexp\n\n// New creates a new POSIX regexp object from a string.\nfunc New(text string) (Object, error) {\n\treturn regexp.CompilePOSIX(text)\n}\n"
  },
  {
    "path": "encoding/xgo/xgo.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xgo\n\nimport (\n\t\"strings\"\n\n\t\"github.com/goplus/xgo/dql/xgo\"\n\t\"github.com/goplus/xgo/parser\"\n)\n\nconst (\n\tXGoPackage = \"github.com/goplus/xgo/dql/xgo\"\n)\n\n// Object represents a XGo File.\ntype Object = *xgo.File\n\n// New parses XGo source code from the given text, returning a XGo File object.\n// An optional parser Mode can be provided to customize the parsing behavior.\nfunc New(text string, mode ...parser.Mode) (f Object, err error) {\n\tvar conf []xgo.Config\n\tif len(mode) > 0 {\n\t\tconf = []xgo.Config{{Mode: mode[0]}}\n\t}\n\treturn xgo.ParseFile(\"\", strings.NewReader(text), conf...)\n}\n"
  },
  {
    "path": "encoding/xml/xml.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xml\n\nimport (\n\t\"strings\"\n\n\t\"github.com/goplus/xgo/dql/xml\"\n)\n\nconst (\n\tXGoPackage = \"github.com/goplus/xgo/dql/xml\"\n)\n\n// Object represents an XML object.\ntype Object = *xml.Node\n\n// New creates a new XML object from a string.\nfunc New(text string) (ret Object, err error) {\n\treturn xml.Parse(strings.NewReader(text))\n}\n"
  },
  {
    "path": "encoding/yaml/yaml.go",
    "content": "/*\n * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage yaml\n\nimport (\n\t\"strings\"\n\n\t\"github.com/goccy/go-yaml\"\n)\n\n// Object is a type alias for any YAML value, which can be a map, slice, string,\n// number, boolean, or null.\ntype Object = any\n\n// New creates a new YAML object from a string.\nfunc New(text string, opts ...yaml.DecodeOption) (ret Object, err error) {\n\terr = yaml.NewDecoder(strings.NewReader(text), opts...).Decode(&ret)\n\treturn\n}\n"
  },
  {
    "path": "env/build.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\n// The value of variables come form\n// `go build -ldflags '-X \"buildDate=xxxxx\"`\nvar (\n\tbuildDate string\n)\n\n// BuildDate returns build date of the `xgo` command.\nfunc BuildDate() string {\n\treturn buildDate\n}\n"
  },
  {
    "path": "env/goenv.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nimport (\n\t\"os\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc HOME() string {\n\treturn os.Getenv(envHOME)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "env/gop_nonwindows.go",
    "content": "//go:build !windows\n// +build !windows\n\n/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nfunc isXgoCmd(fname string) bool {\n\treturn fname == \"xgo\" || fname == \"gop\"\n}\n"
  },
  {
    "path": "env/path.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"syscall\"\n)\n\nvar (\n\t// This is set by the linker.\n\tdefaultXGoRoot string\n)\n\n// XGOROOT returns the root of the XGo tree. It uses the XGOROOT environment variable,\n// if set at process start, or else the root used during the XGo build.\nfunc XGOROOT() string {\n\txgoRoot, err := findXgoRoot()\n\tif err != nil {\n\t\tlog.Panicln(\"XGOROOT not found:\", err)\n\t}\n\treturn xgoRoot\n}\n\nconst (\n\tenvXGOROOT = \"XGOROOT\"\n)\n\nfunc findXgoRoot() (string, error) {\n\tenvXgoRoot := os.Getenv(envXGOROOT)\n\tif envXgoRoot != \"\" {\n\t\t// XGOROOT must valid\n\t\tif isValidXgoRoot(envXgoRoot) {\n\t\t\treturn envXgoRoot, nil\n\t\t}\n\t\tlog.Panicf(\"\\n%s (%s) is not valid\\n\", envXGOROOT, envXgoRoot)\n\t}\n\n\t// if parent directory is a valid xgo root, use it\n\texePath, err := executableRealPath()\n\tif err == nil {\n\t\tdir := filepath.Dir(exePath)\n\t\tparentDir := filepath.Dir(dir)\n\t\tif parentDir != dir && isValidXgoRoot(parentDir) {\n\t\t\treturn parentDir, nil\n\t\t}\n\t}\n\n\t// check defaultXGoRoot, if it is valid, use it\n\tif defaultXGoRoot != \"\" && isValidXgoRoot(defaultXGoRoot) {\n\t\treturn defaultXGoRoot, nil\n\t}\n\n\t// Compatible with old XGOROOT\n\tif home := HOME(); home != \"\" {\n\t\txgoRoot := filepath.Join(home, \"xgo\")\n\t\tif isValidXgoRoot(xgoRoot) {\n\t\t\treturn xgoRoot, nil\n\t\t}\n\t}\n\treturn \"\", syscall.ENOENT\n}\n\n// Mockable for testing.\nvar executable = func() (string, error) {\n\treturn os.Executable()\n}\n\nfunc executableRealPath() (path string, err error) {\n\tpath, err = executable()\n\tif err == nil {\n\t\tpath, err = filepath.EvalSymlinks(path)\n\t\tif err == nil {\n\t\t\tpath, err = filepath.Abs(path)\n\t\t}\n\t}\n\treturn\n}\n\nfunc isFileExists(path string) bool {\n\t_, err := os.Stat(path)\n\treturn err == nil\n}\n\nfunc isDirExists(path string) bool {\n\tst, err := os.Stat(path)\n\treturn err == nil && st.IsDir()\n}\n\nfunc isValidXgoRoot(path string) bool {\n\treturn isDirExists(filepath.Join(path, \"cmd/xgo\")) && isFileExists(filepath.Join(path, \"go.mod\"))\n}\n"
  },
  {
    "path": "env/path_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/goplus/mod\"\n)\n\nfunc findGoModFile(dir string) (modfile string, noCacheFile bool, err error) {\n\tmodfile, err = mod.GOMOD(dir)\n\tif err != nil {\n\t\txgoRoot, err := findXgoRoot()\n\t\tif err == nil {\n\t\t\tmodfile = filepath.Join(xgoRoot, \"go.mod\")\n\t\t\treturn modfile, true, nil\n\t\t}\n\t}\n\treturn\n}\n\n// Common testing directory structure:\n// testing_root/\n//\n//\t  src/\n//\t\t   subdir/\n//\t  valid_xgoroot/\n//\t\t   go.mod\n//\t\t   go.sum\n//\t    cmd/xgo/\nfunc makeTestDir(t *testing.T) (root string, src string, xgoRoot string) {\n\troot, _ = filepath.EvalSymlinks(t.TempDir())\n\tsrc = filepath.Join(root, \"src\")\n\txgoRoot = filepath.Join(root, \"valid_xgoroot\")\n\tmakeValidXgoRoot(xgoRoot)\n\tos.Mkdir(src, 0755)\n\treturn\n}\n\nfunc makeValidXgoRoot(root string) {\n\tos.Mkdir(root, 0755)\n\tos.MkdirAll(filepath.Join(root, \"cmd/xgo\"), 0755)\n\tos.WriteFile(filepath.Join(root, \"go.mod\"), nil, 0644)\n\tos.WriteFile(filepath.Join(root, \"go.sum\"), nil, 0644)\n}\n\nfunc writeDummyFile(path string) {\n\tos.WriteFile(path, nil, 0644)\n}\n\nfunc cleanup() {\n\tos.Setenv(\"XGOROOT\", \"\")\n\tos.Setenv(envHOME, \"\")\n\tdefaultXGoRoot = \"\"\n}\n\nfunc TestBasic(t *testing.T) {\n\tdefaultXGoRoot = \"..\"\n\tif XGOROOT() == \"\" {\n\t\tt.Fatal(\"TestBasic failed\")\n\t}\n\tdefaultXGoRoot = \"\"\n}\n\nfunc TestFindGoModFileInGoModDir(t *testing.T) {\n\tcleanup()\n\n\tt.Run(\"the src/ is a valid mod dir\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanup)\n\t\t_, src, _ := makeTestDir(tt)\n\t\tsubdir := filepath.Join(src, \"subdir\")\n\t\twriteDummyFile(filepath.Join(src, \"go.mod\"))\n\t\tos.Mkdir(subdir, 0755)\n\n\t\t{\n\t\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\t\tif err != nil || modfile != filepath.Join(src, \"go.mod\") || noCacheFile {\n\t\t\t\ttt.Fatal(\"got:\", modfile, noCacheFile, err)\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\t// Should found go.mod in parent dir\n\t\t\tmodfile, noCacheFile, err := findGoModFile(subdir)\n\n\t\t\tif err != nil || modfile != filepath.Join(src, \"go.mod\") || noCacheFile {\n\t\t\t\ttt.Fatal(\"got:\", modfile, noCacheFile, err)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"the src/ is not a valid mod dir\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanup)\n\t\t_, src, _ := makeTestDir(tt)\n\n\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\tif err == nil {\n\t\t\ttt.Fatal(\"should not found the mod file, but got:\", modfile, noCacheFile)\n\t\t}\n\t})\n}\n\nfunc TestFindGoModFileInGopRoot(t *testing.T) {\n\toriginDir, _ := os.Getwd()\n\torigiExecutable := executable\n\thome := filepath.Join(os.TempDir(), \"test_home\")\n\tos.Mkdir(home, 0755)\n\tt.Cleanup(func() {\n\t\tos.Chdir(originDir)\n\t\tos.RemoveAll(home)\n\t\texecutable = origiExecutable\n\t})\n\n\tbin := filepath.Join(home, \"bin\")\n\n\t// Don't find go.mod in gop source dir when testing\n\tos.Chdir(home)\n\n\tcleanupAll := func() {\n\t\tcleanup()\n\t\texecutable = func() (string, error) {\n\t\t\treturn filepath.Join(bin, \"run\"), nil\n\t\t}\n\t}\n\tcleanupAll()\n\n\tt.Run(\"without xgo root\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanupAll)\n\t\troot, _, _ := makeTestDir(tt)\n\n\t\tmodfile, noCacheFile, err := findGoModFile(root)\n\n\t\tif err == nil || noCacheFile || modfile != \"\" {\n\t\t\ttt.Fatal(\"should not found go.mod without xgo root, got:\", modfile, noCacheFile, err)\n\t\t}\n\t})\n\n\tt.Run(\"set XGOROOT to a valid xgoroot path\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanupAll)\n\t\t_, src, xgoRoot := makeTestDir(tt)\n\n\t\tos.Setenv(\"XGOROOT\", xgoRoot)\n\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\tif err != nil || modfile != filepath.Join(xgoRoot, \"go.mod\") || !noCacheFile {\n\t\t\ttt.Fatal(\"should found mod file in XGOROOT, got:\", modfile, noCacheFile, err)\n\t\t}\n\t})\n\n\tt.Run(\"set XGOROOT to an invalid xgoroot path\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanupAll)\n\t\troot, src, _ := makeTestDir(tt)\n\t\tinvalidXgoRoot := filepath.Join(root, \"invalid_xgoroot\")\n\n\t\tdefer func() {\n\t\t\tr := recover()\n\t\t\tif r == nil {\n\t\t\t\ttt.Fatal(\"should panic, but not\")\n\t\t\t}\n\t\t}()\n\n\t\tos.Setenv(\"XGOROOT\", invalidXgoRoot)\n\t\tfindGoModFile(src)\n\t})\n\n\tt.Run(\"set defaultXGoRoot to a valid xgoroot path\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanupAll)\n\t\t_, src, xgoRoot := makeTestDir(tt)\n\n\t\tdefaultXGoRoot = xgoRoot\n\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\tif err != nil || modfile != filepath.Join(xgoRoot, \"go.mod\") || !noCacheFile {\n\t\t\ttt.Fatal(\"should found go.mod in the dir of defaultXGoRoot, got:\", modfile, noCacheFile, err)\n\t\t}\n\t})\n\n\tt.Run(\"set defaultXGoRoot to an invalid path\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanupAll)\n\t\troot, src, _ := makeTestDir(tt)\n\t\tinvalidXgoRoot := filepath.Join(root, \"invalid_xgoroot\")\n\n\t\tdefaultXGoRoot = invalidXgoRoot\n\t\t{\n\t\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\t\tif err == nil || noCacheFile || modfile != \"\" {\n\t\t\t\ttt.Fatal(\"should not found go.mod when defaultXGoRoot isn't exists, got:\", modfile, noCacheFile, err)\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\tos.Mkdir(invalidXgoRoot, 0755)\n\t\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\t\tif err == nil || noCacheFile || modfile != \"\" {\n\t\t\t\ttt.Fatal(\"should not found go.mod when defaultXGoRoot isn't an valid gop root dir, got:\", modfile, noCacheFile, err)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"use $HOME/xgo\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanupAll)\n\t\troot, src, _ := makeTestDir(tt)\n\t\thome := filepath.Join(root, \"home\")\n\t\tos.Mkdir(home, 0755)\n\t\tos.Setenv(envHOME, home)\n\n\t\t{\n\t\t\txgoRoot := filepath.Join(home, \"xgo\")\n\t\t\tmakeValidXgoRoot(xgoRoot)\n\n\t\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\t\tif err != nil || !noCacheFile || modfile != filepath.Join(xgoRoot, \"go.mod\") {\n\t\t\t\ttt.Fatal(\"should found go.mod in $HOME/gop, but got:\", modfile, noCacheFile, err)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"check if parent dir of the executable is valid gop root\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanupAll)\n\t\t_, src, xgoRoot := makeTestDir(tt)\n\t\tbin := filepath.Join(xgoRoot, \"bin\")\n\t\texePath := filepath.Join(bin, \"run\")\n\t\tos.Mkdir(bin, 0755)\n\t\twriteDummyFile(exePath)\n\n\t\t// Mock executable location\n\t\texecutable = func() (string, error) {\n\t\t\treturn exePath, nil\n\t\t}\n\n\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\tif err != nil || !noCacheFile || modfile != filepath.Join(xgoRoot, \"go.mod\") {\n\t\t\ttt.Fatal(\"should found go.mod in xgoRoot, but got:\", modfile, noCacheFile, err)\n\t\t}\n\t})\n\n\tt.Run(\"test xgo root priority\", func(tt *testing.T) {\n\t\ttt.Cleanup(cleanupAll)\n\t\troot, src, _ := makeTestDir(tt)\n\n\t\ttt.Run(\"without xgo root\", func(tt *testing.T) {\n\t\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\t\tif err == nil || noCacheFile || modfile != \"\" {\n\t\t\t\ttt.Fatal(\"should not found go.mod without xgo root, got:\", modfile, noCacheFile, err)\n\t\t\t}\n\t\t})\n\n\t\ttt.Run(\"set defaultXGoRoot to a valid xgo root dir\", func(tt *testing.T) {\n\t\t\tnewXgoRoot := filepath.Join(root, \"new_xgo_root\")\n\t\t\tmakeValidXgoRoot(newXgoRoot)\n\n\t\t\tdefaultXGoRoot = newXgoRoot\n\t\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\t\tif err != nil || !noCacheFile || modfile != filepath.Join(newXgoRoot, \"go.mod\") {\n\t\t\t\ttt.Fatal(\"should found go.mod in new_xgo_root/, but got:\", modfile, noCacheFile, err)\n\t\t\t}\n\t\t})\n\n\t\ttt.Run(\"the executable's parent dir is a valid xgo root dir\", func(tt *testing.T) {\n\t\t\tnewGopRoot2 := filepath.Join(root, \"new_xgo_root2\")\n\t\t\tmakeValidXgoRoot(newGopRoot2)\n\t\t\tbin := filepath.Join(newGopRoot2, \"bin\")\n\t\t\texePath := filepath.Join(bin, \"run\")\n\t\t\tos.Mkdir(bin, 0755)\n\t\t\twriteDummyFile(exePath)\n\t\t\t// Mock executable location\n\t\t\texecutable = func() (string, error) {\n\t\t\t\treturn exePath, nil\n\t\t\t}\n\n\t\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\t\tif err != nil || !noCacheFile || modfile != filepath.Join(newGopRoot2, \"go.mod\") {\n\t\t\t\ttt.Fatal(\"should found go.mod in new_xgo_root2/, but got:\", modfile, noCacheFile, err)\n\t\t\t}\n\t\t})\n\n\t\ttt.Run(\"set XGOROOT to an invalid xgo root dir\", func(tt *testing.T) {\n\t\t\tnewGopRoot3 := filepath.Join(root, \"new_xgo_root3\")\n\n\t\t\tdefer func() {\n\t\t\t\tr := recover()\n\t\t\t\tif r == nil {\n\t\t\t\t\ttt.Fatal(\"should panic, but not\")\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tos.Setenv(\"XGOROOT\", newGopRoot3)\n\t\t\tfindGoModFile(src)\n\t\t})\n\n\t\ttt.Run(\"set XGOROOT to a valid xgo root dir\", func(tt *testing.T) {\n\t\t\tnewGopRoot4 := filepath.Join(root, \"new_xgo_root4\")\n\t\t\tmakeValidXgoRoot(newGopRoot4)\n\n\t\t\tos.Setenv(\"XGOROOT\", newGopRoot4)\n\t\t\tmodfile, noCacheFile, err := findGoModFile(src)\n\n\t\t\tif err != nil || !noCacheFile || modfile != filepath.Join(newGopRoot4, \"go.mod\") {\n\t\t\t\ttt.Fatal(\"should found go.mod in new_xgo_root3/, but got:\", modfile, noCacheFile, err)\n\t\t\t}\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "env/sys_others.go",
    "content": "//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris\n// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris\n\n/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nconst (\n\tenvHOME = \"HOME\"\n)\n"
  },
  {
    "path": "env/sys_plan9.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nconst (\n\tenvHOME = \"home\"\n)\n"
  },
  {
    "path": "env/sys_windows.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nimport \"strings\"\n\nconst (\n\tenvHOME = \"USERPROFILE\"\n)\n\nfunc isXgoCmd(fname string) bool {\n\tfname = strings.TrimSuffix(fname, \".exe\")\n\treturn fname == \"xgo\" || fname == \"gop\"\n}\n"
  },
  {
    "path": "env/version.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime/debug\"\n\t\"strings\"\n)\n\n// buildVersion is the XGo tree's version string at build time.\n// This is set by the linker via ldflags for official releases.\nvar (\n\tbuildVersion string\n)\n\nfunc init() {\n\tinitEnv()\n}\n\nfunc initEnv() {\n\tif buildVersion == \"\" {\n\t\tinitEnvByXgo()\n\t}\n}\n\nfunc initEnvByXgo() {\n\tif fname := filepath.Base(os.Args[0]); !isXgoCmd(fname) {\n\t\tif ret, err := xgoEnv(); err == nil {\n\t\t\tparts := strings.SplitN(strings.TrimRight(ret, \"\\n\"), \"\\n\", 3)\n\t\t\tif len(parts) == 3 {\n\t\t\t\tbuildVersion, buildDate, defaultXGoRoot = parts[0], parts[1], parts[2]\n\t\t\t}\n\t\t}\n\t}\n}\n\nvar xgoEnv = func() (string, error) {\n\tvar b bytes.Buffer\n\tcmd := exec.Command(\"xgo\", \"env\", \"XGOVERSION\", \"BUILDDATE\", \"XGOROOT\")\n\tcmd.Stdout = &b\n\terr := cmd.Run()\n\treturn b.String(), err\n}\n\n// Installed checks is `xgo` installed or not.\n// If returns false, it means `xgo` is not installed or not in PATH.\nfunc Installed() bool {\n\treturn buildVersion != \"\"\n}\n\n// Version returns the XGo tree's version string.\n// It is either the commit hash and date at the time of the build or,\n// when possible, a release tag like \"v1.0.0-rc1\".\n//\n// Version detection priority:\n// 1. buildVersion from ldflags (for official releases via goreleaser)\n// 2. debug.ReadBuildInfo() - reads embedded Go module version from VCS\n// 3. \"(devel)\" for non-VCS builds\nfunc Version() string {\n\t// Prefer ldflags-injected version (for official releases)\n\tif buildVersion != \"\" {\n\t\treturn buildVersion\n\t}\n\n\t// Fallback to debug.ReadBuildInfo (embedded module version from VCS)\n\tif bi, ok := debug.ReadBuildInfo(); ok {\n\t\tif bi.Main.Version != \"\" {\n\t\t\treturn bi.Main.Version\n\t\t}\n\t}\n\n\t// Return devel for non-VCS builds\n\treturn \"(devel)\"\n}\n\n// MainVersion extracts the major.minor version from the current version.\n// For example, \"v1.5.3\" returns \"1.5\", \"v1.5.0-rc1\" returns \"1.5\".\n// For development versions like \"(devel)\" or pseudo-versions, returns \"0.0\".\nfunc MainVersion() string {\n\tver := Version()\n\n\t// Handle (devel) and other non-version strings\n\tif !strings.HasPrefix(ver, \"v\") {\n\t\treturn \"0.0\"\n\t}\n\n\t// Remove 'v' prefix\n\tver = strings.TrimPrefix(ver, \"v\")\n\n\t// Handle pseudo-versions (e.g., \"v0.0.0-20240101-abcdef\")\n\tif strings.HasPrefix(ver, \"0.0.0-\") {\n\t\treturn \"0.0\"\n\t}\n\n\t// Extract major.minor from semantic version\n\tparts := strings.Split(ver, \".\")\n\tif len(parts) >= 2 {\n\t\treturn parts[0] + \".\" + parts[1]\n\t}\n\n\treturn \"0.0\"\n}\n"
  },
  {
    "path": "env/version_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage env\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestPanic(t *testing.T) {\n\tt.Run(\"XGOROOT panic\", func(t *testing.T) {\n\t\tdefer func() {\n\t\t\tif e := recover(); e == nil {\n\t\t\t\tt.Fatal(\"XGOROOT: no panic?\")\n\t\t\t}\n\t\t}()\n\t\tdefaultXGoRoot = \"\"\n\t\tos.Setenv(envXGOROOT, \"\")\n\t\tXGOROOT()\n\t})\n}\n\nfunc TestEnv(t *testing.T) {\n\txgoEnv = func() (string, error) {\n\t\twd, _ := os.Getwd()\n\t\troot := filepath.Dir(wd)\n\t\treturn \"v1.0.0-beta1\\n2023-10-18_17-45-50\\n\" + root + \"\\n\", nil\n\t}\n\tbuildVersion = \"\"\n\tinitEnv()\n\tif !Installed() {\n\t\tt.Fatal(\"not Installed\")\n\t}\n\tif Version() != \"v1.0.0-beta1\" {\n\t\tt.Fatal(\"TestVersion failed:\", Version())\n\t}\n\tbuildVersion = \"\"\n\t// When no buildVersion is set, Version() should return the module version\n\t// from debug.ReadBuildInfo() or \"(devel)\"\n\tver := Version()\n\tif ver != \"(devel)\" && !strings.HasPrefix(ver, \"v\") {\n\t\tt.Fatal(\"TestVersion failed - expected (devel) or version starting with v, got:\", ver)\n\t}\n\tif BuildDate() != \"2023-10-18_17-45-50\" {\n\t\tt.Fatal(\"BuildInfo failed:\", BuildDate())\n\t}\n}\n\nfunc TestMainVersion(t *testing.T) {\n\ttests := []struct {\n\t\tversion  string\n\t\texpected string\n\t}{\n\t\t{\"v1.5.3\", \"1.5\"},\n\t\t{\"v1.5.0-rc1\", \"1.5\"},\n\t\t{\"v2.0.10\", \"2.0\"},\n\t\t{\"(devel)\", \"0.0\"},\n\t\t{\"v0.0.0-20240101-abcdef\", \"0.0\"},\n\t\t{\"invalid\", \"0.0\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.version, func(t *testing.T) {\n\t\t\t// Temporarily set buildVersion to control Version() output\n\t\t\toldBuildVersion := buildVersion\n\t\t\tbuildVersion = tt.version\n\t\t\tdefer func() { buildVersion = oldBuildVersion }()\n\n\t\t\tresult := MainVersion()\n\t\t\tif result != tt.expected {\n\t\t\t\tt.Fatalf(\"MainVersion() for %s: expected %s, got %s\", tt.version, tt.expected, result)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "format/format.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package format implements standard formatting of XGo source.\n//\n// Note that formatting of Go source code changes over time, so tools relying on\n// consistent formatting should execute a specific version of the gofmt binary\n// instead of using this package. That way, the formatting will be stable, and\n// the tools won't need to be recompiled each time gofmt changes.\n//\n// For example, pre-submit checks that use this package directly would behave\n// differently depending on what Go version each developer uses, causing the\n// check to be inherently fragile.\npackage format\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/printer\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nvar config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}\n\nconst parserMode = parser.ParseComments\n\n// Node formats node in canonical gofmt style and writes the result to dst.\n//\n// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl,\n// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec,\n// or ast.Stmt. Node does not modify node. Imports are not sorted for\n// nodes representing partial source files (for instance, if the node is\n// not an *ast.File or a *printer.CommentedNode not wrapping an *ast.File).\n//\n// The function may return early (before the entire result is written)\n// and return a formatting error, for instance due to an incorrect AST.\nfunc Node(dst io.Writer, fset *token.FileSet, node any) error {\n\t// Determine if we have a complete source file (file != nil).\n\tvar file *ast.File\n\tvar cnode *printer.CommentedNode\n\tswitch n := node.(type) {\n\tcase *ast.File:\n\t\tfile = n\n\tcase *printer.CommentedNode:\n\t\tif f, ok := n.Node.(*ast.File); ok {\n\t\t\tfile = f\n\t\t\tcnode = n\n\t\t}\n\t}\n\n\t// Sort imports if necessary.\n\tif file != nil && hasUnsortedImports(file) {\n\t\t// Make a copy of the AST because ast.SortImports is destructive.\n\t\t// TODO(gri) Do this more efficiently.\n\t\tvar buf bytes.Buffer\n\t\terr := config.Fprint(&buf, fset, file)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfile, err = parser.ParseFile(fset, \"\", buf.Bytes(), parserMode)\n\t\tif err != nil {\n\t\t\t_, err = dst.Write(buf.Bytes())\n\t\t\treturn err\n\t\t\t// We should never get here. If we do, provide good diagnostic.\n\t\t\t// return fmt.Errorf(\"format.Node internal error (%s)\", err)\n\t\t}\n\t\tast.SortImports(fset, file)\n\n\t\t// Use new file with sorted imports.\n\t\tnode = file\n\t\tif cnode != nil {\n\t\t\tnode = &printer.CommentedNode{Node: file, Comments: cnode.Comments}\n\t\t}\n\t}\n\n\treturn config.Fprint(dst, fset, node)\n}\n\n// Source formats src in canonical gofmt style and returns the result\n// or an (I/O or syntax) error. src is expected to be a syntactically\n// correct Go source file, or a list of Go declarations or statements.\n//\n// If src is a partial source file, the leading and trailing space of src\n// is applied to the result (such that it has the same leading and trailing\n// space as src), and the result is indented by the same amount as the first\n// line of src containing code. Imports are not sorted for partial source files.\nfunc Source(src []byte, class bool, filename ...string) ([]byte, error) {\n\tvar fname string\n\tif filename != nil {\n\t\tfname = filename[0]\n\t}\n\tfset := token.NewFileSet()\n\tfile, sourceAdj, indentAdj, err := parse(fset, fname, src, class, true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif sourceAdj == nil {\n\t\t// Complete source file.\n\t\t// TODO(gri) consider doing this always.\n\t\tast.SortImports(fset, file)\n\t}\n\n\treturn format(fset, file, sourceAdj, indentAdj, src, config)\n}\n\nfunc hasUnsortedImports(file *ast.File) bool {\n\tfor _, d := range file.Decls {\n\t\td, ok := d.(*ast.GenDecl)\n\t\tif !ok || d.Tok != token.IMPORT {\n\t\t\t// Not an import declaration, so we're done.\n\t\t\t// Imports are always first.\n\t\t\treturn false\n\t\t}\n\t\tif d.Lparen.IsValid() {\n\t\t\t// For now assume all grouped imports are unsorted.\n\t\t\t// TODO(gri) Should check if they are sorted already.\n\t\t\treturn true\n\t\t}\n\t\t// Ungrouped imports are sorted by default.\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "format/formatutil/_testdata/format/basic/in.data",
    "content": "goto \"a\"\n\n// this is a comment\n\n// x comment 1\n// x comment 2\n// x comment 3\nfunc x() {\n}\n\n// call\nfunc(a func()) {}(nil)\n\n/*\n y comment\n*/\nfunc y() {\n}\n\necho \"Hi\"\n"
  },
  {
    "path": "format/formatutil/_testdata/format/basic/out.expect",
    "content": "// this is a comment\n\n// x comment 1\n// x comment 2\n// x comment 3\nfunc x() {\n}\n\n/*\n y comment\n*/\nfunc y() {\n}\n\ngoto \"a\"\n\n// call\nfunc(a func()) {}(nil)\n\necho \"Hi\"\n"
  },
  {
    "path": "format/formatutil/_testdata/format/nondecl/in.data",
    "content": "const a = 0\n"
  },
  {
    "path": "format/formatutil/_testdata/format/nondecl/out.expect",
    "content": "const a = 0\n"
  },
  {
    "path": "format/formatutil/_testdata/rearrange/noeol/in.data",
    "content": "var a int"
  },
  {
    "path": "format/formatutil/_testdata/rearrange/noeol/out.expect",
    "content": "var a int"
  },
  {
    "path": "format/formatutil/_testdata/rearrange/nondecl/in.data",
    "content": "// this is a comment\n\n// x comment 1\n// x comment 2\n// x comment 3\nfunc x() {\n}\n\n/*\n y comment\n*/\nvar y int\n"
  },
  {
    "path": "format/formatutil/_testdata/rearrange/nondecl/out.expect",
    "content": "// this is a comment\n\n// x comment 1\n// x comment 2\n// x comment 3\nfunc x() {\n}\n\n/*\n y comment\n*/\nvar y int\n"
  },
  {
    "path": "format/formatutil/_testdata/splitstmts/basic/in.data",
    "content": "goto \"a\"\n\n// this is a comment\n\n// x comment 1\n// x comment 2\n// x comment 3\nfunc x() {\n}\n\n// call\nfunc(a func()) {}(nil)\n\n/*\n y comment\n*/\nfunc y() {\n}\n\necho \"Hi\"\n"
  },
  {
    "path": "format/formatutil/_testdata/splitstmts/basic/out.expect",
    "content": "goto\nFUNC\nFNCALL\nFUNC\nIDENT\n"
  },
  {
    "path": "format/formatutil/format_gop.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage formatutil\n\nimport (\n\t\"github.com/goplus/xgo/format\"\n\t\"github.com/goplus/xgo/scanner\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// RearrangeFuncs rearranges functions in src.\nfunc RearrangeFuncs(src []byte, filename ...string) ([]byte, error) {\n\tvar fname string\n\tif filename != nil {\n\t\tfname = filename[0]\n\t}\n\n\tfset := token.NewFileSet()\n\tbase := fset.Base()\n\tf := fset.AddFile(fname, base, len(src))\n\n\tvar s scanner.Scanner\n\ts.Init(f, src, nil, scanner.ScanComments)\n\tstmts := splitStmts(&s)\n\tfirst := firstNonDecl(stmts)\n\tif first < 0 { // no non-decl stmt\n\t\treturn src, nil\n\t}\n\n\tret := make([]byte, 0, len(src))\n\toff := int(stmts[first].words[0].pos) - base\n\tret = append(ret, src[:off]...)\n\n\trest := stmts[first:]\n\tfor i, s := range rest {\n\t\tif s.isFuncDecl() {\n\t\t\tret = append(ret, codeOf(src, base, i, rest)...)\n\t\t}\n\t}\n\tfor i, s := range rest {\n\t\tif !s.isFuncDecl() {\n\t\t\tret = append(ret, codeOf(src, base, i, rest)...)\n\t\t}\n\t}\n\treturn ret, nil\n}\n\nfunc codeOf(src []byte, base, i int, rest []aStmt) []byte {\n\tfrom := int(rest[i].words[0].pos) - base\n\tto := 0\n\tif i == len(rest)-1 {\n\t\tto = len(src)\n\t} else {\n\t\tto = int(rest[i+1].words[0].pos) - base\n\t}\n\treturn src[from:to]\n}\n\nfunc firstNonDecl(stmts []aStmt) int {\n\tfor i, stmt := range stmts {\n\t\tif !stmt.isDecl() {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc splitStmts(s *scanner.Scanner) (stmts []aStmt) {\n\tvar level int\n\tvar stmt aStmt\n\tfor {\n\t\tpos, tok, _ := s.Scan()\n\t\tif tok == token.EOF {\n\t\t\treturn\n\t\t}\n\t\tstmt.words = append(stmt.words, aWord{pos, tok})\n\t\tswitch tok {\n\t\tcase token.LBRACE:\n\t\t\tlevel++\n\t\tcase token.RBRACE:\n\t\t\tlevel--\n\t\t}\n\t\tif tok == token.SEMICOLON && level == 0 {\n\t\t\tstmt.tok, stmt.at = tokOf(stmt.words)\n\t\t\tstmts = append(stmts, stmt)\n\t\t\tstmt = aStmt{}\n\t\t\tcontinue\n\t\t}\n\t}\n}\n\ntype aWord struct {\n\tpos token.Pos\n\ttok token.Token\n}\n\ntype aStmt struct {\n\twords []aWord\n\ttok   token.Token\n\tat    int\n}\n\nfunc (s aStmt) isFuncDecl() bool {\n\treturn s.tok == token.FUNC && isFuncDecl(s.words[s.at+1:])\n}\n\nfunc (s aStmt) isDecl() bool {\n\tswitch s.tok {\n\tcase token.CONST, token.TYPE, token.VAR:\n\t\treturn true\n\tcase token.FUNC:\n\t\treturn isFuncDecl(s.words[s.at+1:])\n\t}\n\treturn false\n}\n\nfunc tokOf(words []aWord) (tok token.Token, at int) {\n\tfor i, w := range words {\n\t\tif w.tok != token.COMMENT {\n\t\t\treturn w.tok, i\n\t\t}\n\t}\n\treturn words[0].tok, 0\n}\n\nfunc isFuncDecl(words []aWord) bool {\n\tif startWith(words, token.LPAREN) { // func (\n\t\twords = seekAfter(words[1:], token.RPAREN, token.LPAREN) // func (...)\n\t\tif startWith(words, token.LBRACE) {                      // func (...) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc seekAfter(words []aWord, tokR, tokL token.Token) []aWord {\n\tlevel := 0\n\tfor i, w := range words {\n\t\tswitch w.tok {\n\t\tcase tokR:\n\t\t\tif level == 0 {\n\t\t\t\treturn words[i+1:]\n\t\t\t}\n\t\t\tlevel--\n\t\tcase tokL:\n\t\t\tlevel++\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc startWith(words []aWord, tok token.Token) bool {\n\tfor _, w := range words {\n\t\tswitch w.tok {\n\t\tcase token.COMMENT:\n\t\t\tcontinue\n\t\tcase tok:\n\t\t\treturn true\n\t\t}\n\t\tbreak\n\t}\n\treturn false\n}\n\n// SourceEx formats src in canonical xgofmt style and returns the result\n// or an (I/O or syntax) error. src is expected to be a syntactically\n// correct XGo source file, or a list of XGo declarations or statements.\n//\n// If src is a partial source file, the leading and trailing space of src\n// is applied to the result (such that it has the same leading and trailing\n// space as src), and the result is indented by the same amount as the first\n// line of src containing code. Imports are not sorted for partial source files.\nfunc SourceEx(src []byte, class bool, filename ...string) (formatted []byte, err error) {\n\tformatted, err = format.Source(src, class, filename...)\n\tif err == nil {\n\t\treturn\n\t}\n\tsrc, err = RearrangeFuncs(src, filename...)\n\tif err == nil {\n\t\tformatted, err = format.Source(src, class, filename...)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "format/formatutil/format_test.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage formatutil\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/scanner\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nfunc TestSeekAfter(t *testing.T) {\n\tif seekAfter(nil, 0, 0) != nil {\n\t\tt.Fatal(\"seekAfter failed\")\n\t}\n}\n\nfunc TestTokOf(t *testing.T) {\n\twords := []aWord{{tok: token.COMMENT}}\n\tif tok, _ := tokOf(words); token.COMMENT != tok {\n\t\tt.Fatal(\"tokOf failed:\", tok)\n\t}\n\tif startWith(words, token.VAR) {\n\t\tt.Fatal(\"startWith failed\")\n\t}\n}\n\nfunc doSplitStmts(src []byte) (ret []string) {\n\tfset := token.NewFileSet()\n\tbase := fset.Base()\n\tf := fset.AddFile(\"\", base, len(src))\n\n\tvar s scanner.Scanner\n\ts.Init(f, src, nil, scanner.ScanComments)\n\tstmts := splitStmts(&s)\n\n\tret = make([]string, len(stmts))\n\tfor i, stmt := range stmts {\n\t\tret[i] = stmtKind(stmt)\n\t}\n\treturn\n}\n\nfunc stmtKind(s aStmt) string {\n\ttok := s.tok\n\tif tok == token.FUNC {\n\t\tif isFuncDecl(s.words[s.at+1:]) {\n\t\t\treturn \"FUNC\"\n\t\t}\n\t\treturn \"FNCALL\"\n\t}\n\treturn tok.String()\n}\n\nfunc testFrom(t *testing.T, pkgDir, sel string, doIt func(in []byte) ([]byte, error)) {\n\tif sel != \"\" && !strings.Contains(pkgDir, sel) {\n\t\treturn\n\t}\n\tt.Helper()\n\tlog.Println(\"Parsing\", pkgDir)\n\tin, err := os.ReadFile(pkgDir + \"/in.data\")\n\tif err != nil {\n\t\tt.Fatal(\"Parsing\", pkgDir, \"-\", err)\n\t}\n\tout, err := os.ReadFile(pkgDir + \"/out.expect\")\n\tif err != nil {\n\t\tt.Fatal(\"Parsing\", pkgDir, \"-\", err)\n\t}\n\tret, err := doIt(in)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Equal(ret, out) {\n\t\tt.Fatal(\"Parsing\", pkgDir, \"- failed:\\n\"+string(ret))\n\t}\n}\n\nfunc testFromDir(t *testing.T, sel, relDir string, doIt func(in []byte) ([]byte, error)) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = path.Join(dir, relDir)\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"ReadDir failed:\", err)\n\t}\n\tfor _, fi := range fis {\n\t\tname := fi.Name()\n\t\tif strings.HasPrefix(name, \"_\") {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttestFrom(t, dir+\"/\"+name, sel, doIt)\n\t\t})\n\t}\n}\n\nfunc TestSplitStmts(t *testing.T) {\n\ttestFromDir(t, \"\", \"./_testdata/splitstmts\", func(in []byte) ([]byte, error) {\n\t\tret := strings.Join(doSplitStmts(in), \"\\n\") + \"\\n\"\n\t\treturn []byte(ret), nil\n\t})\n}\n\nfunc TestRearrangeFuncs(t *testing.T) {\n\tif runtime.GOOS == \"windows\" { // skip temporarily\n\t\treturn\n\t}\n\ttestFromDir(t, \"\", \"./_testdata/rearrange\", func(in []byte) ([]byte, error) {\n\t\treturn RearrangeFuncs(in)\n\t})\n}\n\nfunc TestFormat(t *testing.T) {\n\ttestFromDir(t, \"\", \"./_testdata/format\", func(in []byte) ([]byte, error) {\n\t\treturn SourceEx(in, false, \"\")\n\t})\n}\n"
  },
  {
    "path": "format/internal.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// TODO(gri): This file and the file src/cmd/gofmt/internal.go are\n// the same (but for this comment and the package name). Do not modify\n// one without the other. Determine if we can factor out functionality\n// in a public API. See also #11844 for context.\n\npackage format\n\nimport (\n\t\"bytes\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/printer\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// parse parses src, which was read from the named file,\n// as a Go source file, declaration, or statement list.\nfunc parse(fset *token.FileSet, filename string, src []byte, class, _ /* fragmentOk */ bool) (\n\tfile *ast.File,\n\tsourceAdj func(src []byte, indent int) []byte,\n\tindentAdj int,\n\terr error,\n) {\n\tmode := parserMode\n\tif class {\n\t\tmode |= parser.ParseXGoClass\n\t}\n\tfile, err = parser.ParseFile(fset, filename, src, mode)\n\treturn\n}\n\n// format formats the given package file originally obtained from src\n// and adjusts the result based on the original source via sourceAdj\n// and indentAdj.\nfunc format(\n\tfset *token.FileSet,\n\tfile *ast.File,\n\tsourceAdj func(src []byte, indent int) []byte,\n\tindentAdj int,\n\tsrc []byte,\n\tcfg printer.Config,\n) ([]byte, error) {\n\tif sourceAdj == nil {\n\t\t// Complete source file.\n\t\tvar buf bytes.Buffer\n\t\terr := cfg.Fprint(&buf, fset, file)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn buf.Bytes(), nil\n\t}\n\n\t// Partial source file.\n\t// Determine and prepend leading space.\n\ti, j := 0, 0\n\tfor j < len(src) && isSpace(src[j]) {\n\t\tif src[j] == '\\n' {\n\t\t\ti = j + 1 // byte offset of last line in leading space\n\t\t}\n\t\tj++\n\t}\n\tvar res []byte\n\tres = append(res, src[:i]...)\n\n\t// Determine and prepend indentation of first code line.\n\t// Spaces are ignored unless there are no tabs,\n\t// in which case spaces count as one tab.\n\tindent := 0\n\thasSpace := false\n\tfor _, b := range src[i:j] {\n\t\tswitch b {\n\t\tcase ' ':\n\t\t\thasSpace = true\n\t\tcase '\\t':\n\t\t\tindent++\n\t\t}\n\t}\n\tif indent == 0 && hasSpace {\n\t\tindent = 1\n\t}\n\tfor i := 0; i < indent; i++ {\n\t\tres = append(res, '\\t')\n\t}\n\n\t// Format the source.\n\t// Write it without any leading and trailing space.\n\tcfg.Indent = indent + indentAdj\n\tvar buf bytes.Buffer\n\n\terr := cfg.Fprint(&buf, fset, file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tout := sourceAdj(buf.Bytes(), cfg.Indent)\n\n\t// If the adjusted output is empty, the source\n\t// was empty but (possibly) for white space.\n\t// The result is the incoming source.\n\tif len(out) == 0 {\n\t\treturn src, nil\n\t}\n\n\t// Otherwise, append output to leading space.\n\tres = append(res, out...)\n\treturn append(res, '\\n'), nil\n}\n\n// isSpace reports whether the byte is a space character.\n// isSpace defines a space as being among the following bytes: ' ', '\\t', '\\n' and '\\r'.\nfunc isSpace(b byte) bool {\n\treturn b == ' ' || b == '\\t' || b == '\\n' || b == '\\r'\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/goplus/xgo\n\ngo 1.24.0\n\ntoolchain go1.24.2\n\nrequire (\n\tgithub.com/fsnotify/fsnotify v1.9.0\n\tgithub.com/goccy/go-yaml v1.19.2\n\tgithub.com/goplus/cobra v1.9.13 //xgo:class\n\tgithub.com/goplus/gogen v1.21.2\n\tgithub.com/goplus/lib v0.3.1\n\tgithub.com/goplus/mod v0.19.6\n\tgithub.com/qiniu/x v1.16.5\n\tgolang.org/x/net v0.50.0\n)\n\nrequire (\n\tgolang.org/x/mod v0.20.0 // indirect\n\tgolang.org/x/sys v0.41.0 // indirect\n)\n\nretract v1.1.12\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=\ngithub.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=\ngithub.com/goplus/cobra v1.9.13 h1:onu/65rXILVMc7RBvdY1mGdOIZ/F4C/6o7DWgglwFaw=\ngithub.com/goplus/cobra v1.9.13/go.mod h1:p4LhfNJDKEpiGjGiNn0crUXL5dUPA5DX2ztYpEJR34E=\ngithub.com/goplus/gogen v1.21.2 h1:xbXPgZOZiQx/WBM0nZxVSxFbtdCMZCRB+lguDnh8pfs=\ngithub.com/goplus/gogen v1.21.2/go.mod h1:Y7ulYW3wonQ3d9er00b0uGFEV/IUZa6okWJZh892ACQ=\ngithub.com/goplus/lib v0.3.1 h1:Xws4DBVvgOMu58awqB972wtvTacDbk3nqcbHjdx9KSg=\ngithub.com/goplus/lib v0.3.1/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=\ngithub.com/goplus/mod v0.19.6 h1:o00M6mN/rQ06cGDdH8xSHdL3xiZJXom6AUmwpOTBZ84=\ngithub.com/goplus/mod v0.19.6/go.mod h1:UnoI3xX5LbUu5TFwOhVRpwOBHSH//s7yqWNIbXpzpRA=\ngithub.com/qiniu/x v1.16.5 h1:+/cSm9m8F8sx6zJ4ylmsuhux8xVpxMHP/pzL8xv1Y9w=\ngithub.com/qiniu/x v1.16.5/go.mod h1:AiovSOCaRijaf3fj+0CBOpR1457pn24b0Vdb1JpwhII=\ngolang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=\ngolang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=\ngolang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=\ngolang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=\ngolang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\n"
  },
  {
    "path": "make.bash",
    "content": "#! /usr/bin/env bash\n\n#\n# Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nset -ex\n\ngo run cmd/make.go --install --autoproxy\n"
  },
  {
    "path": "make.bat",
    "content": "go run cmd/make.go --install --autoproxy\n"
  },
  {
    "path": "parser/_instance/instance1/cmd.xgo",
    "content": "type T1 P1[int]\n\ntype T2 P2[int, string]\n\ntype T3 *P1[P2[int, P1[int]]]\n\ntype S1 struct {\n\tv1 P1[int]\n\tv2 P2[int, string]\n}\n\ntype S2 struct {\n\tP1[int]\n}\n\ntype A1 []P1[int]\n\ntype A2 [2]P2[int, string]\n\ntype M1 map[P1[int]]P2[int, string]\n\ntype C1 chan P1[int]\n"
  },
  {
    "path": "parser/_instance/instance1/parser.expect",
    "content": "package main\n\nfile cmd.xgo\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: T1\n      Type:\n        ast.IndexExpr:\n          X:\n            ast.Ident:\n              Name: P1\n          Index:\n            ast.Ident:\n              Name: int\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: T2\n      Type:\n        ast.IndexListExpr:\n          X:\n            ast.Ident:\n              Name: P2\n          Indices:\n            ast.Ident:\n              Name: int\n            ast.Ident:\n              Name: string\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: T3\n      Type:\n        ast.StarExpr:\n          X:\n            ast.IndexExpr:\n              X:\n                ast.Ident:\n                  Name: P1\n              Index:\n                ast.IndexListExpr:\n                  X:\n                    ast.Ident:\n                      Name: P2\n                  Indices:\n                    ast.Ident:\n                      Name: int\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: S1\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: v1\n                  Type:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: v2\n                  Type:\n                    ast.IndexListExpr:\n                      X:\n                        ast.Ident:\n                          Name: P2\n                      Indices:\n                        ast.Ident:\n                          Name: int\n                        ast.Ident:\n                          Name: string\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: S2\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: A1\n      Type:\n        ast.ArrayType:\n          Elt:\n            ast.IndexExpr:\n              X:\n                ast.Ident:\n                  Name: P1\n              Index:\n                ast.Ident:\n                  Name: int\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: A2\n      Type:\n        ast.ArrayType:\n          Len:\n            ast.BasicLit:\n              Kind: INT\n              Value: 2\n          Elt:\n            ast.IndexListExpr:\n              X:\n                ast.Ident:\n                  Name: P2\n              Indices:\n                ast.Ident:\n                  Name: int\n                ast.Ident:\n                  Name: string\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: M1\n      Type:\n        ast.MapType:\n          Key:\n            ast.IndexExpr:\n              X:\n                ast.Ident:\n                  Name: P1\n              Index:\n                ast.Ident:\n                  Name: int\n          Value:\n            ast.IndexListExpr:\n              X:\n                ast.Ident:\n                  Name: P2\n              Indices:\n                ast.Ident:\n                  Name: int\n                ast.Ident:\n                  Name: string\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: C1\n      Type:\n        ast.ChanType:\n          Value:\n            ast.IndexExpr:\n              X:\n                ast.Ident:\n                  Name: P1\n              Index:\n                ast.Ident:\n                  Name: int\n"
  },
  {
    "path": "parser/_instance/instance2/cmd.xgo",
    "content": "func fn1(P1[int], P2[int, string])\n\nfunc fn2(p1 P1[int], p2 P2[int, string])\n\nfunc fn3(p1 P1[int], p2 ...P2[int, string])\n\nfunc fn4(p1 []P1[int], p3 []P2[int, string]) *P1[P2[int, P1[int]]]\n\nfunc fn5(p1 []P1[int], p2 [2]P2[int, string])\n\nfunc fn6(p1 chan P1[int], p2 map[P1[int]]P2[int, string])\n\nfunc fn7(p1 struct {\n\tv1 P1[int]\n\tv2 P2[int, string]\n})\n"
  },
  {
    "path": "parser/_instance/instance2/parser.expect",
    "content": "package main\n\nfile cmd.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: fn1\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: P1\n                  Index:\n                    ast.Ident:\n                      Name: int\n            ast.Field:\n              Type:\n                ast.IndexListExpr:\n                  X:\n                    ast.Ident:\n                      Name: P2\n                  Indices:\n                    ast.Ident:\n                      Name: int\n                    ast.Ident:\n                      Name: string\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: fn2\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p1\n              Type:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: P1\n                  Index:\n                    ast.Ident:\n                      Name: int\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p2\n              Type:\n                ast.IndexListExpr:\n                  X:\n                    ast.Ident:\n                      Name: P2\n                  Indices:\n                    ast.Ident:\n                      Name: int\n                    ast.Ident:\n                      Name: string\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: fn3\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p1\n              Type:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: P1\n                  Index:\n                    ast.Ident:\n                      Name: int\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p2\n              Type:\n                ast.Ellipsis:\n                  Elt:\n                    ast.IndexListExpr:\n                      X:\n                        ast.Ident:\n                          Name: P2\n                      Indices:\n                        ast.Ident:\n                          Name: int\n                        ast.Ident:\n                          Name: string\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: fn4\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p1\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p3\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.IndexListExpr:\n                      X:\n                        ast.Ident:\n                          Name: P2\n                      Indices:\n                        ast.Ident:\n                          Name: int\n                        ast.Ident:\n                          Name: string\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.IndexListExpr:\n                          X:\n                            ast.Ident:\n                              Name: P2\n                          Indices:\n                            ast.Ident:\n                              Name: int\n                            ast.IndexExpr:\n                              X:\n                                ast.Ident:\n                                  Name: P1\n                              Index:\n                                ast.Ident:\n                                  Name: int\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: fn5\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p1\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p2\n              Type:\n                ast.ArrayType:\n                  Len:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 2\n                  Elt:\n                    ast.IndexListExpr:\n                      X:\n                        ast.Ident:\n                          Name: P2\n                      Indices:\n                        ast.Ident:\n                          Name: int\n                        ast.Ident:\n                          Name: string\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: fn6\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p1\n              Type:\n                ast.ChanType:\n                  Value:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p2\n              Type:\n                ast.MapType:\n                  Key:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\n                  Value:\n                    ast.IndexListExpr:\n                      X:\n                        ast.Ident:\n                          Name: P2\n                      Indices:\n                        ast.Ident:\n                          Name: int\n                        ast.Ident:\n                          Name: string\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: fn7\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p1\n              Type:\n                ast.StructType:\n                  Fields:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Names:\n                            ast.Ident:\n                              Name: v1\n                          Type:\n                            ast.IndexExpr:\n                              X:\n                                ast.Ident:\n                                  Name: P1\n                              Index:\n                                ast.Ident:\n                                  Name: int\n                        ast.Field:\n                          Names:\n                            ast.Ident:\n                              Name: v2\n                          Type:\n                            ast.IndexListExpr:\n                              X:\n                                ast.Ident:\n                                  Name: P2\n                              Indices:\n                                ast.Ident:\n                                  Name: int\n                                ast.Ident:\n                                  Name: string\n"
  },
  {
    "path": "parser/_instance/instance3/cmd.xgo",
    "content": "println(P1[int]{}, P2[int, string]{})\nprintln((*P1[int])(nil), (*P2[int, string])(nil))\n\nprintln P1[int]{}, P2[int, string]{}\nprintln (*P1[int])(nil), (*P2[int, string])(nil)\n\nfunc(x P1[int], y *P2[int, string]) *int {\n\treturn nil\n}(P1[int]{1}, &P2[int, string]{})\n\nfoo(=> P1[int]{1})\nfoo(=> {\n\tprintln(&P2[int, string]{})\n})\n\nfn1[int]([]P1[int]{P1[int]{0}, P1[int]{1}}, &P2[int, string]{})\nfn2[int, string](&P1[int]{}, &P2[int, string]{})\n\nfoo1[int](=> P1[int]{1})\nfoo2[int, string](=> {\n\tprintln(&P2[int, string]{})\n})\n"
  },
  {
    "path": "parser/_instance/instance3/parser.expect",
    "content": "package main\n\nfile cmd.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.CompositeLit:\n                  Type:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\n                ast.CompositeLit:\n                  Type:\n                    ast.IndexListExpr:\n                      X:\n                        ast.Ident:\n                          Name: P2\n                      Indices:\n                        ast.Ident:\n                          Name: int\n                        ast.Ident:\n                          Name: string\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.ParenExpr:\n                      X:\n                        ast.StarExpr:\n                          X:\n                            ast.IndexExpr:\n                              X:\n                                ast.Ident:\n                                  Name: P1\n                              Index:\n                                ast.Ident:\n                                  Name: int\n                  Args:\n                    ast.Ident:\n                      Name: nil\n                ast.CallExpr:\n                  Fun:\n                    ast.ParenExpr:\n                      X:\n                        ast.StarExpr:\n                          X:\n                            ast.IndexListExpr:\n                              X:\n                                ast.Ident:\n                                  Name: P2\n                              Indices:\n                                ast.Ident:\n                                  Name: int\n                                ast.Ident:\n                                  Name: string\n                  Args:\n                    ast.Ident:\n                      Name: nil\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.CompositeLit:\n                  Type:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\n                ast.CompositeLit:\n                  Type:\n                    ast.IndexListExpr:\n                      X:\n                        ast.Ident:\n                          Name: P2\n                      Indices:\n                        ast.Ident:\n                          Name: int\n                        ast.Ident:\n                          Name: string\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.ParenExpr:\n                      X:\n                        ast.StarExpr:\n                          X:\n                            ast.IndexExpr:\n                              X:\n                                ast.Ident:\n                                  Name: P1\n                              Index:\n                                ast.Ident:\n                                  Name: int\n                  Args:\n                    ast.Ident:\n                      Name: nil\n                ast.CallExpr:\n                  Fun:\n                    ast.ParenExpr:\n                      X:\n                        ast.StarExpr:\n                          X:\n                            ast.IndexListExpr:\n                              X:\n                                ast.Ident:\n                                  Name: P2\n                              Indices:\n                                ast.Ident:\n                                  Name: int\n                                ast.Ident:\n                                  Name: string\n                  Args:\n                    ast.Ident:\n                      Name: nil\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.FuncLit:\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: x\n                              Type:\n                                ast.IndexExpr:\n                                  X:\n                                    ast.Ident:\n                                      Name: P1\n                                  Index:\n                                    ast.Ident:\n                                      Name: int\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: y\n                              Type:\n                                ast.StarExpr:\n                                  X:\n                                    ast.IndexListExpr:\n                                      X:\n                                        ast.Ident:\n                                          Name: P2\n                                      Indices:\n                                        ast.Ident:\n                                          Name: int\n                                        ast.Ident:\n                                          Name: string\n                      Results:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.StarExpr:\n                                  X:\n                                    ast.Ident:\n                                      Name: int\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ReturnStmt:\n                          Results:\n                            ast.Ident:\n                              Name: nil\n              Args:\n                ast.CompositeLit:\n                  Type:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: P1\n                      Index:\n                        ast.Ident:\n                          Name: int\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                ast.UnaryExpr:\n                  Op: &\n                  X:\n                    ast.CompositeLit:\n                      Type:\n                        ast.IndexListExpr:\n                          X:\n                            ast.Ident:\n                              Name: P2\n                          Indices:\n                            ast.Ident:\n                              Name: int\n                            ast.Ident:\n                              Name: string\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr:\n                  Rhs:\n                    ast.CompositeLit:\n                      Type:\n                        ast.IndexExpr:\n                          X:\n                            ast.Ident:\n                              Name: P1\n                          Index:\n                            ast.Ident:\n                              Name: int\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr2:\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: println\n                              Args:\n                                ast.UnaryExpr:\n                                  Op: &\n                                  X:\n                                    ast.CompositeLit:\n                                      Type:\n                                        ast.IndexListExpr:\n                                          X:\n                                            ast.Ident:\n                                              Name: P2\n                                          Indices:\n                                            ast.Ident:\n                                              Name: int\n                                            ast.Ident:\n                                              Name: string\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn1\n                  Index:\n                    ast.Ident:\n                      Name: int\n              Args:\n                ast.CompositeLit:\n                  Type:\n                    ast.ArrayType:\n                      Elt:\n                        ast.IndexExpr:\n                          X:\n                            ast.Ident:\n                              Name: P1\n                          Index:\n                            ast.Ident:\n                              Name: int\n                  Elts:\n                    ast.CompositeLit:\n                      Type:\n                        ast.IndexExpr:\n                          X:\n                            ast.Ident:\n                              Name: P1\n                          Index:\n                            ast.Ident:\n                              Name: int\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 0\n                    ast.CompositeLit:\n                      Type:\n                        ast.IndexExpr:\n                          X:\n                            ast.Ident:\n                              Name: P1\n                          Index:\n                            ast.Ident:\n                              Name: int\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                ast.UnaryExpr:\n                  Op: &\n                  X:\n                    ast.CompositeLit:\n                      Type:\n                        ast.IndexListExpr:\n                          X:\n                            ast.Ident:\n                              Name: P2\n                          Indices:\n                            ast.Ident:\n                              Name: int\n                            ast.Ident:\n                              Name: string\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexListExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn2\n                  Indices:\n                    ast.Ident:\n                      Name: int\n                    ast.Ident:\n                      Name: string\n              Args:\n                ast.UnaryExpr:\n                  Op: &\n                  X:\n                    ast.CompositeLit:\n                      Type:\n                        ast.IndexExpr:\n                          X:\n                            ast.Ident:\n                              Name: P1\n                          Index:\n                            ast.Ident:\n                              Name: int\n                ast.UnaryExpr:\n                  Op: &\n                  X:\n                    ast.CompositeLit:\n                      Type:\n                        ast.IndexListExpr:\n                          X:\n                            ast.Ident:\n                              Name: P2\n                          Indices:\n                            ast.Ident:\n                              Name: int\n                            ast.Ident:\n                              Name: string\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: foo1\n                  Index:\n                    ast.Ident:\n                      Name: int\n              Args:\n                ast.LambdaExpr:\n                  Rhs:\n                    ast.CompositeLit:\n                      Type:\n                        ast.IndexExpr:\n                          X:\n                            ast.Ident:\n                              Name: P1\n                          Index:\n                            ast.Ident:\n                              Name: int\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexListExpr:\n                  X:\n                    ast.Ident:\n                      Name: foo2\n                  Indices:\n                    ast.Ident:\n                      Name: int\n                    ast.Ident:\n                      Name: string\n              Args:\n                ast.LambdaExpr2:\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: println\n                              Args:\n                                ast.UnaryExpr:\n                                  Op: &\n                                  X:\n                                    ast.CompositeLit:\n                                      Type:\n                                        ast.IndexListExpr:\n                                          X:\n                                            ast.Ident:\n                                              Name: P2\n                                          Indices:\n                                            ast.Ident:\n                                              Name: int\n                                            ast.Ident:\n                                              Name: string\n"
  },
  {
    "path": "parser/_instance/instance4/cmd.xgo",
    "content": "fn[int]()\nfn[[]int]()\nfn[struct {\n\tX int\n\tY int\n}]()\nfn[map[int]string]()\nfn[chan int]()\nfn[chan struct{}]()\nfn[interface{}]()\nfn[interface{ Method() }]()\nfn[[]struct {\n\tX int\n\tY int\n}, map[int]string]()\n"
  },
  {
    "path": "parser/_instance/instance4/parser.expect",
    "content": "package main\n\nfile cmd.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn\n                  Index:\n                    ast.Ident:\n                      Name: int\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn\n                  Index:\n                    ast.ArrayType:\n                      Elt:\n                        ast.Ident:\n                          Name: int\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn\n                  Index:\n                    ast.StructType:\n                      Fields:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: X\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: Y\n                              Type:\n                                ast.Ident:\n                                  Name: int\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn\n                  Index:\n                    ast.MapType:\n                      Key:\n                        ast.Ident:\n                          Name: int\n                      Value:\n                        ast.Ident:\n                          Name: string\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn\n                  Index:\n                    ast.ChanType:\n                      Value:\n                        ast.Ident:\n                          Name: int\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn\n                  Index:\n                    ast.ChanType:\n                      Value:\n                        ast.StructType:\n                          Fields:\n                            ast.FieldList:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn\n                  Index:\n                    ast.InterfaceType:\n                      Methods:\n                        ast.FieldList:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn\n                  Index:\n                    ast.InterfaceType:\n                      Methods:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: Method\n                              Type:\n                                ast.FuncType:\n                                  Params:\n                                    ast.FieldList:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.IndexListExpr:\n                  X:\n                    ast.Ident:\n                      Name: fn\n                  Indices:\n                    ast.ArrayType:\n                      Elt:\n                        ast.StructType:\n                          Fields:\n                            ast.FieldList:\n                              List:\n                                ast.Field:\n                                  Names:\n                                    ast.Ident:\n                                      Name: X\n                                  Type:\n                                    ast.Ident:\n                                      Name: int\n                                ast.Field:\n                                  Names:\n                                    ast.Ident:\n                                      Name: Y\n                                  Type:\n                                    ast.Ident:\n                                      Name: int\n                    ast.MapType:\n                      Key:\n                        ast.Ident:\n                          Name: int\n                      Value:\n                        ast.Ident:\n                          Name: string\n"
  },
  {
    "path": "parser/_instance/instance5/cmd.xgo",
    "content": "var a [2]int\n\nif 0 < a[0] {\n\tprintln a\n\tprintln M[int]{1}\n\tprintln T[int]{a: 1, b: 2}\n}\n\nfor _, i := range []int{7, 42} {\n\tprintln i\n}\n"
  },
  {
    "path": "parser/_instance/instance5/parser.expect",
    "content": "package main\n\nfile cmd.xgo\nnoEntrypoint\nast.GenDecl:\n  Tok: var\n  Specs:\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: a\n      Type:\n        ast.ArrayType:\n          Len:\n            ast.BasicLit:\n              Kind: INT\n              Value: 2\n          Elt:\n            ast.Ident:\n              Name: int\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.IfStmt:\n          Cond:\n            ast.BinaryExpr:\n              X:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 0\n              Op: <\n              Y:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Index:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: a\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.CompositeLit:\n                          Type:\n                            ast.IndexExpr:\n                              X:\n                                ast.Ident:\n                                  Name: M\n                              Index:\n                                ast.Ident:\n                                  Name: int\n                          Elts:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 1\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.CompositeLit:\n                          Type:\n                            ast.IndexExpr:\n                              X:\n                                ast.Ident:\n                                  Name: T\n                              Index:\n                                ast.Ident:\n                                  Name: int\n                          Elts:\n                            ast.KeyValueExpr:\n                              Key:\n                                ast.Ident:\n                                  Name: a\n                              Value:\n                                ast.BasicLit:\n                                  Kind: INT\n                                  Value: 1\n                            ast.KeyValueExpr:\n                              Key:\n                                ast.Ident:\n                                  Name: b\n                              Value:\n                                ast.BasicLit:\n                                  Kind: INT\n                                  Value: 2\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: _\n          Value:\n            ast.Ident:\n              Name: i\n          Tok: :=\n          X:\n            ast.CompositeLit:\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: int\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 42\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: i\n"
  },
  {
    "path": "parser/_nofmt/cmdlinestyle1/cmd.xgo",
    "content": "package main\n\nfunc main() {\n\tchangeYpos -0.7, 8\n\tchangeYpos -0.7\n\tchangeYpos - 0.7\n\tchangeYpos-0.7\n\tx[1]\n\tx []...\n}\n"
  },
  {
    "path": "parser/_nofmt/cmdlinestyle1/parser.expect",
    "content": "package main\n\nfile cmd.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: changeYpos\n              Args:\n                ast.UnaryExpr:\n                  Op: -\n                  X:\n                    ast.BasicLit:\n                      Kind: FLOAT\n                      Value: 0.7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 8\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: changeYpos\n              Args:\n                ast.UnaryExpr:\n                  Op: -\n                  X:\n                    ast.BasicLit:\n                      Kind: FLOAT\n                      Value: 0.7\n        ast.ExprStmt:\n          X:\n            ast.BinaryExpr:\n              X:\n                ast.Ident:\n                  Name: changeYpos\n              Op: -\n              Y:\n                ast.BasicLit:\n                  Kind: FLOAT\n                  Value: 0.7\n        ast.ExprStmt:\n          X:\n            ast.BinaryExpr:\n              X:\n                ast.Ident:\n                  Name: changeYpos\n              Op: -\n              Y:\n                ast.BasicLit:\n                  Kind: FLOAT\n                  Value: 0.7\n        ast.ExprStmt:\n          X:\n            ast.IndexExpr:\n              X:\n                ast.Ident:\n                  Name: x\n              Index:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: x\n              Args:\n                ast.SliceLit:\n"
  },
  {
    "path": "parser/_nofmt/cmdlinestyle2/cmd2.xgo",
    "content": "add(100, 200) (0).Test()\n"
  },
  {
    "path": "parser/_nofmt/cmdlinestyle2/parser.expect",
    "content": "package main\n\nfile cmd2.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: add\n                          Args:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 100\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 200\n                      Args:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 0\n                  Sel:\n                    ast.Ident:\n                      Name: Test\n"
  },
  {
    "path": "parser/_nofmt/cmdlinestyle3/cmd.xgo",
    "content": "println (1+2i, 2)\nprintln (1, a...)\nprintln (a...)\n"
  },
  {
    "path": "parser/_nofmt/cmdlinestyle3/parser.expect",
    "content": "package main\n\nfile cmd.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.TupleLit:\n                  Elts:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                      Op: +\n                      Y:\n                        ast.BasicLit:\n                          Kind: IMAG\n                          Value: 2i\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 2\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.TupleLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                    ast.Ident:\n                      Name: a\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.TupleLit:\n                  Elts:\n                    ast.Ident:\n                      Name: a\n"
  },
  {
    "path": "parser/_nofmt/exists/exists.xgo",
    "content": "a := [1, 3, 5, 7, 8, 19]\nhasEven := {for x <- a if x%2 == 0}\n"
  },
  {
    "path": "parser/_nofmt/exists/parser.expect",
    "content": "package main\n\nfile exists.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 8\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 19\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: hasEven\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: {\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: x\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BinaryExpr:\n                          X:\n                            ast.Ident:\n                              Name: x\n                          Op: %\n                          Y:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 2\n                      Op: ==\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 0\n"
  },
  {
    "path": "parser/_nofmt/forloop/forloop.xgo",
    "content": "n := 0\nfor range [1, 3, 5, 7, 11] {\n\tn++\n}\nprintln(\"n:\", n)\n\nfor x := range [1] {\n}\n\nsum := 0\nfor _, x := range [1, 3, 5, 7, 11] {\n\tif x > 3 {\n\t\tsum += x\n\t}\n}\nprintln(\"sum(1,3,5,7,11):\", sum)\n\nsum = 0\nfor i := 1; i < 100; i++ {\n\tsum += i\n}\nprintln(\"sum(1-100):\", sum)\n\nfor x <- [1] {\n\tprintln(x)\n}\n\nfor i, x <- [1] if i%2 == 0 {\n\tprintln(i, x)\n}\n"
  },
  {
    "path": "parser/_nofmt/forloop/parser.expect",
    "content": "package main\n\nfile forloop.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: n\n          Tok: :=\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 0\n        ast.RangeStmt:\n          Tok: ILLEGAL\n          X:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 11\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.IncDecStmt:\n                  X:\n                    ast.Ident:\n                      Name: n\n                  Tok: ++\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"n:\"\n                ast.Ident:\n                  Name: n\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: x\n          Tok: :=\n          X:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n          Body:\n            ast.BlockStmt:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: sum\n          Tok: :=\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 0\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: _\n          Value:\n            ast.Ident:\n              Name: x\n          Tok: :=\n          X:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 11\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.IfStmt:\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: x\n                      Op: >\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.AssignStmt:\n                          Lhs:\n                            ast.Ident:\n                              Name: sum\n                          Tok: +=\n                          Rhs:\n                            ast.Ident:\n                              Name: x\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"sum(1,3,5,7,11):\"\n                ast.Ident:\n                  Name: sum\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: sum\n          Tok: =\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 0\n        ast.ForStmt:\n          Init:\n            ast.AssignStmt:\n              Lhs:\n                ast.Ident:\n                  Name: i\n              Tok: :=\n              Rhs:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n          Cond:\n            ast.BinaryExpr:\n              X:\n                ast.Ident:\n                  Name: i\n              Op: <\n              Y:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 100\n          Post:\n            ast.IncDecStmt:\n              X:\n                ast.Ident:\n                  Name: i\n              Tok: ++\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.AssignStmt:\n                  Lhs:\n                    ast.Ident:\n                      Name: sum\n                  Tok: +=\n                  Rhs:\n                    ast.Ident:\n                      Name: i\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"sum(1-100):\"\n                ast.Ident:\n                  Name: sum\n        ast.ForPhraseStmt:\n          ForPhrase:\n            ast.ForPhrase:\n              Value:\n                ast.Ident:\n                  Name: x\n              X:\n                ast.SliceLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: x\n        ast.ForPhraseStmt:\n          ForPhrase:\n            ast.ForPhrase:\n              Key:\n                ast.Ident:\n                  Name: i\n              Value:\n                ast.Ident:\n                  Name: x\n              X:\n                ast.SliceLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n              Cond:\n                ast.BinaryExpr:\n                  X:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: i\n                      Op: %\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 2\n                  Op: ==\n                  Y:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: i\n                        ast.Ident:\n                          Name: x\n"
  },
  {
    "path": "parser/_nofmt/listcompr/listcompr.xgo",
    "content": "y := [x*x for x <- [1]]\nprintln(y)\n\ny = [x*x for x <- [1, 3, 5, 7, 11] if x > 3]\nprintln(y)\n\nz := [i+v for i, v <- [1, 3, 5, 7, 11] if t := i % 2; t == 1]\nprintln(z)\n\nprintln([k+\",\"+s for k, s <- {\"Hello\": \"xsw\", \"Hi\": \"XGo\"}])\n\narr := [1, 2, 3, 4, 5, 6]\nx := [[a, b] for a <- arr if a < b for b <- arr if b > 2]\nprintln(\"x:\", x)\n"
  },
  {
    "path": "parser/_nofmt/listcompr/parser.expect",
    "content": "package main\n\nfile listcompr.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: y\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: [\n              Elt:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: x\n                  Op: *\n                  Y:\n                    ast.Ident:\n                      Name: x\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: x\n                  X:\n                    ast.SliceLit:\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: y\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: y\n          Tok: =\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: [\n              Elt:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: x\n                  Op: *\n                  Y:\n                    ast.Ident:\n                      Name: x\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: x\n                  X:\n                    ast.SliceLit:\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 5\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 7\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 11\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: x\n                      Op: >\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: y\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: z\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: [\n              Elt:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: i\n                  Op: +\n                  Y:\n                    ast.Ident:\n                      Name: v\n              Fors:\n                ast.ForPhrase:\n                  Key:\n                    ast.Ident:\n                      Name: i\n                  Value:\n                    ast.Ident:\n                      Name: v\n                  X:\n                    ast.SliceLit:\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 5\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 7\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 11\n                  Init:\n                    ast.AssignStmt:\n                      Lhs:\n                        ast.Ident:\n                          Name: t\n                      Tok: :=\n                      Rhs:\n                        ast.BinaryExpr:\n                          X:\n                            ast.Ident:\n                              Name: i\n                          Op: %\n                          Y:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 2\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: t\n                      Op: ==\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: z\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.ComprehensionExpr:\n                  Tok: [\n                  Elt:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BinaryExpr:\n                          X:\n                            ast.Ident:\n                              Name: k\n                          Op: +\n                          Y:\n                            ast.BasicLit:\n                              Kind: STRING\n                              Value: \",\"\n                      Op: +\n                      Y:\n                        ast.Ident:\n                          Name: s\n                  Fors:\n                    ast.ForPhrase:\n                      Key:\n                        ast.Ident:\n                          Name: k\n                      Value:\n                        ast.Ident:\n                          Name: s\n                      X:\n                        ast.CompositeLit:\n                          Elts:\n                            ast.KeyValueExpr:\n                              Key:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"Hello\"\n                              Value:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"xsw\"\n                            ast.KeyValueExpr:\n                              Key:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"Hi\"\n                              Value:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"XGo\"\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: arr\n          Tok: :=\n          Rhs:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 2\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 4\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 6\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: x\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: [\n              Elt:\n                ast.SliceLit:\n                  Elts:\n                    ast.Ident:\n                      Name: a\n                    ast.Ident:\n                      Name: b\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: a\n                  X:\n                    ast.Ident:\n                      Name: arr\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: a\n                      Op: <\n                      Y:\n                        ast.Ident:\n                          Name: b\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: b\n                  X:\n                    ast.Ident:\n                      Name: arr\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: b\n                      Op: >\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 2\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"x:\"\n                ast.Ident:\n                  Name: x\n"
  },
  {
    "path": "parser/_nofmt/matrix1/matrix.xgo",
    "content": "echo [\n\t1, 2, 3\n\t4, 5, 6 ]\n"
  },
  {
    "path": "parser/_nofmt/matrix1/parser.expect",
    "content": "package main\n\nfile matrix.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.MatrixLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 2\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 3\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 4\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 5\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 6\n                  NElt: 2\n"
  },
  {
    "path": "parser/_nofmt/printvariadic/parser.expect",
    "content": "package main\n\nfile printv.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: x\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: y\n"
  },
  {
    "path": "parser/_nofmt/printvariadic/printv.xgo",
    "content": "println(\n\tx...\n)\nprintln(\n\ty...,\n)\n"
  },
  {
    "path": "parser/_nofmt/rangeexpr1/parser.expect",
    "content": "package main\n\nfile rangeexpr.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: i\n          Tok: :=\n          X:\n            ast.RangeExpr:\n              Last:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 10\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: i\n        ast.ForPhraseStmt:\n          ForPhrase:\n            ast.ForPhrase:\n              Value:\n                ast.Ident:\n                  Name: i\n              X:\n                ast.RangeExpr:\n                  First:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                  Last:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 10\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: i\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: i\n          Tok: :=\n          X:\n            ast.RangeExpr:\n              Last:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 10\n              Expr3:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 2\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: i\n        ast.RangeStmt:\n          Tok: ILLEGAL\n          X:\n            ast.RangeExpr:\n              Last:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 10\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.BasicLit:\n                          Kind: STRING\n                          Value: \"Range expression\"\n"
  },
  {
    "path": "parser/_nofmt/rangeexpr1/rangeexpr.xgo",
    "content": "package main\n\nfunc main() {\n\tfor i := range :10 {\n\t\tprintln(i)\n\t}\n\n\tfor i <- 1:10 {\n\t\tprintln(i)\n\t}\n\n\tfor i := range :10:2 {\n\t\tprintln(i)\n\t}\n\n\tfor range :10 {\n\t\tprintln(\"Range expression\")\n\t}\n}\n"
  },
  {
    "path": "parser/_nofmt/selectdata/parser.expect",
    "content": "package main\n\nfile select.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 8\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 19\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: y\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: {\n              Elt:\n                ast.Ident:\n                  Name: x\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: x\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BinaryExpr:\n                          X:\n                            ast.Ident:\n                              Name: x\n                          Op: %\n                          Y:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 2\n                      Op: ==\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 0\n"
  },
  {
    "path": "parser/_nofmt/selectdata/select.xgo",
    "content": "a := [1, 3, 5, 7, 8, 19]\ny := {x for x <- a if x%2 == 0}\n"
  },
  {
    "path": "parser/_nofmt/structtag/parser.expect",
    "content": "package main\n\nfile tag.xgo\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Start\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: _\n                  Tag:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"Start program\"\n"
  },
  {
    "path": "parser/_nofmt/structtag/tag.xgo",
    "content": "type Start struct {\n\t_ \"Start program\"\n}\n"
  },
  {
    "path": "parser/_nofmt/tupletype/parser.expect",
    "content": "package main\n\nfile tuple.xgo\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with parenthesized types (covers token.LPAREN case)\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: WithParen\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.ParenExpr:\n                      X:\n                        ast.Ident:\n                          Name: int\n                ast.Field:\n                  Type:\n                    ast.ParenExpr:\n                      X:\n                        ast.Ident:\n                          Name: string\n"
  },
  {
    "path": "parser/_nofmt/tupletype/tuple.xgo",
    "content": "// Tuple with parenthesized types (covers token.LPAREN case)\ntype WithParen ((int), (string))\n"
  },
  {
    "path": "parser/_testdata/append1/append.xgo",
    "content": "a <- 1, 2, 3\n"
  },
  {
    "path": "parser/_testdata/append1/parser.expect",
    "content": "package main\n\nfile append.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.SendStmt:\n          Chan:\n            ast.Ident:\n              Name: a\n          Values:\n            ast.BasicLit:\n              Kind: INT\n              Value: 1\n            ast.BasicLit:\n              Kind: INT\n              Value: 2\n            ast.BasicLit:\n              Kind: INT\n              Value: 3\n"
  },
  {
    "path": "parser/_testdata/append2/append.xgo",
    "content": "a <- b...\n"
  },
  {
    "path": "parser/_testdata/append2/parser.expect",
    "content": "package main\n\nfile append.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.SendStmt:\n          Chan:\n            ast.Ident:\n              Name: a\n          Values:\n            ast.Ident:\n              Name: b\n"
  },
  {
    "path": "parser/_testdata/arrowop/arrowop.xgo",
    "content": "echo 1+a -> b\necho a <> b+1\necho a -> b\necho a <> b, \"Hi\"\n"
  },
  {
    "path": "parser/_testdata/arrowop/parser.expect",
    "content": "package main\n\nfile arrowop.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.BinaryExpr:\n                  X:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                      Op: +\n                      Y:\n                        ast.Ident:\n                          Name: a\n                  Op: ->\n                  Y:\n                    ast.Ident:\n                      Name: b\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Op: <>\n                  Y:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: b\n                      Op: +\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Op: ->\n                  Y:\n                    ast.Ident:\n                      Name: b\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Op: <>\n                  Y:\n                    ast.Ident:\n                      Name: b\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"Hi\"\n"
  },
  {
    "path": "parser/_testdata/autoprop/goto.xgo",
    "content": "L:\n\tgoto(1, 2) + break(3, 4) + a.goto(6)\n\tgoto L\n"
  },
  {
    "path": "parser/_testdata/autoprop/parser.expect",
    "content": "package main\n\nfile goto.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.LabeledStmt:\n          Label:\n            ast.Ident:\n              Name: L\n          Stmt:\n            ast.ExprStmt:\n              X:\n                ast.BinaryExpr:\n                  X:\n                    ast.BinaryExpr:\n                      X:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: goto\n                          Args:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 1\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 2\n                      Op: +\n                      Y:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: break\n                          Args:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 3\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 4\n                  Op: +\n                  Y:\n                    ast.CallExpr:\n                      Fun:\n                        ast.SelectorExpr:\n                          X:\n                            ast.Ident:\n                              Name: a\n                          Sel:\n                            ast.Ident:\n                              Name: goto\n                      Args:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 6\n        ast.BranchStmt:\n          Tok: goto\n          Label:\n            ast.Ident:\n              Name: L\n"
  },
  {
    "path": "parser/_testdata/build/build.xgo",
    "content": "type cstring string\n\ntitle := \"Hello,world!2020-05-27\"\ns := (*cstring)(&title)\nprintln(title[0 : len(title)-len(\"2006-01-02\")])\n"
  },
  {
    "path": "parser/_testdata/build/parser.expect",
    "content": "package main\n\nfile build.xgo\nnoEntrypoint\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: cstring\n      Type:\n        ast.Ident:\n          Name: string\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: title\n          Tok: :=\n          Rhs:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"Hello,world!2020-05-27\"\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: s\n          Tok: :=\n          Rhs:\n            ast.CallExpr:\n              Fun:\n                ast.ParenExpr:\n                  X:\n                    ast.StarExpr:\n                      X:\n                        ast.Ident:\n                          Name: cstring\n              Args:\n                ast.UnaryExpr:\n                  Op: &\n                  X:\n                    ast.Ident:\n                      Name: title\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.SliceExpr:\n                  X:\n                    ast.Ident:\n                      Name: title\n                  Low:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\n                  High:\n                    ast.BinaryExpr:\n                      X:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: len\n                          Args:\n                            ast.Ident:\n                              Name: title\n                      Op: -\n                      Y:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: len\n                          Args:\n                            ast.BasicLit:\n                              Kind: STRING\n                              Value: \"2006-01-02\"\n"
  },
  {
    "path": "parser/_testdata/c2gohello/hello.xgo",
    "content": "import \"C\"\n\nC.printf c\"Hello, world!\\n\"\n"
  },
  {
    "path": "parser/_testdata/c2gohello/parser.expect",
    "content": "package main\n\nfile hello.xgo\nnoEntrypoint\nast.GenDecl:\n  Tok: import\n  Specs:\n    ast.ImportSpec:\n      Path:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"C\"\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: C\n                  Sel:\n                    ast.Ident:\n                      Name: printf\n              Args:\n                ast.BasicLit:\n                  Kind: CSTRING\n                  Value: \"Hello, world!\\n\"\n"
  },
  {
    "path": "parser/_testdata/classfile_init1/Rect.gox",
    "content": "var (\n\tWidth  float64 = 100.0\n\tHeight float64 = 200.0\n\tName   string  = \"Rectangle\"\n\tCount  int     = 0\n)\n\nfunc Area() float64 {\n\treturn Width * Height\n}\n"
  },
  {
    "path": "parser/_testdata/classfile_init1/parser.expect",
    "content": "package main\n\nfile Rect.gox\nast.GenDecl:\n  Tok: var\n  Specs:\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: Width\n      Type:\n        ast.Ident:\n          Name: float64\n      Values:\n        ast.BasicLit:\n          Kind: FLOAT\n          Value: 100.0\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: Height\n      Type:\n        ast.Ident:\n          Name: float64\n      Values:\n        ast.BasicLit:\n          Kind: FLOAT\n          Value: 200.0\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: Name\n      Type:\n        ast.Ident:\n          Name: string\n      Values:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"Rectangle\"\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: Count\n      Type:\n        ast.Ident:\n          Name: int\n      Values:\n        ast.BasicLit:\n          Kind: INT\n          Value: 0\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: Area\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: float64\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ReturnStmt:\n          Results:\n            ast.BinaryExpr:\n              X:\n                ast.Ident:\n                  Name: Width\n              Op: *\n              Y:\n                ast.Ident:\n                  Name: Height\n"
  },
  {
    "path": "parser/_testdata/classfile_init2/Rect.gox",
    "content": "var (\n\tx = 1\n)\n"
  },
  {
    "path": "parser/_testdata/classfile_init2/parser.expect",
    "content": "package main\n\nfile Rect.gox\nast.GenDecl:\n  Tok: var\n  Specs:\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: x\n      Values:\n        ast.BasicLit:\n          Kind: INT\n          Value: 1\n"
  },
  {
    "path": "parser/_testdata/cmdlinestyle1/cmd.xgo",
    "content": "println (1+2i)*2\n"
  },
  {
    "path": "parser/_testdata/cmdlinestyle1/parser.expect",
    "content": "package main\n\nfile cmd.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BinaryExpr:\n                  X:\n                    ast.ParenExpr:\n                      X:\n                        ast.BinaryExpr:\n                          X:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 1\n                          Op: +\n                          Y:\n                            ast.BasicLit:\n                              Kind: IMAG\n                              Value: 2i\n                  Op: *\n                  Y:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 2\n"
  },
  {
    "path": "parser/_testdata/cmdlinestyle2/cmd2.xgo",
    "content": "x {}\nx{}\n"
  },
  {
    "path": "parser/_testdata/cmdlinestyle2/parser.expect",
    "content": "package main\n\nfile cmd2.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: x\n              Args:\n                ast.CompositeLit:\n        ast.ExprStmt:\n          X:\n            ast.CompositeLit:\n              Type:\n                ast.Ident:\n                  Name: x\n"
  },
  {
    "path": "parser/_testdata/cmdlinestyle3/cmd3.xgo",
    "content": "println &x\nprintln !x\n"
  },
  {
    "path": "parser/_testdata/cmdlinestyle3/parser.expect",
    "content": "package main\n\nfile cmd3.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.UnaryExpr:\n                  Op: &\n                  X:\n                    ast.Ident:\n                      Name: x\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.UnaryExpr:\n                  Op: !\n                  X:\n                    ast.Ident:\n                      Name: x\n"
  },
  {
    "path": "parser/_testdata/cmdlinestyle4/cmd4.xgo",
    "content": "func call(fn func(x int)) { fn(100) }\n\ncall(func(x int) { println })\ncall(func(x int) { println x })\ncall(func(x int) { println {\"x\": 100, \"y\": 200}, x })\n"
  },
  {
    "path": "parser/_testdata/cmdlinestyle4/parser.expect",
    "content": "package main\n\nfile cmd4.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: call\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: fn\n              Type:\n                ast.FuncType:\n                  Params:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Names:\n                            ast.Ident:\n                              Name: x\n                          Type:\n                            ast.Ident:\n                              Name: int\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: fn\n              Args:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 100\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: call\n              Args:\n                ast.FuncLit:\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: x\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.Ident:\n                              Name: println\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: call\n              Args:\n                ast.FuncLit:\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: x\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: println\n                              Args:\n                                ast.Ident:\n                                  Name: x\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: call\n              Args:\n                ast.FuncLit:\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: x\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: println\n                              Args:\n                                ast.CompositeLit:\n                                  Elts:\n                                    ast.KeyValueExpr:\n                                      Key:\n                                        ast.BasicLit:\n                                          Kind: STRING\n                                          Value: \"x\"\n                                      Value:\n                                        ast.BasicLit:\n                                          Kind: INT\n                                          Value: 100\n                                    ast.KeyValueExpr:\n                                      Key:\n                                        ast.BasicLit:\n                                          Kind: STRING\n                                          Value: \"y\"\n                                      Value:\n                                        ast.BasicLit:\n                                          Kind: INT\n                                          Value: 200\n                                ast.Ident:\n                                  Name: x\n"
  },
  {
    "path": "parser/_testdata/collection/collection.xgo",
    "content": "// We often need our programs to perform operations on\n// collections of data, like selecting all items that\n// satisfy a given predicate or mapping all items to a new\n// collection with a custom function.\n\n// In some languages it's idiomatic to use [generic](http://en.wikipedia.org/wiki/Generic_programming)\n// data structures and algorithms. Go does not support\n// generics; in Go it's common to provide collection\n// functions if and when they are specifically needed for\n// your program and data types.\n\n// Here are some example collection functions for slices\n// of `strings`. You can use these examples to build your\n// own functions. Note that in some cases it may be\n// clearest to just inline the collection-manipulating\n// code directly, instead of creating and calling a\n// helper function.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// Index returns the first index of the target string `t`, or\n// -1 if no match is found.\nfunc Index(vs []string, t string) int {\n\tfor i, v := range vs {\n\t\tif v == t {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// Include returns `true` if the target string t is in the\n// slice.\nfunc Include(vs []string, t string) bool {\n\treturn Index(vs, t) >= 0\n}\n\n// Any returns `true` if one of the strings in the slice\n// satisfies the predicate `f`.\nfunc Any(vs []string, f func(string) bool) bool {\n\tfor _, v := range vs {\n\t\tif f(v) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// All returns `true` if all of the strings in the slice\n// satisfy the predicate `f`.\nfunc All(vs []string, f func(string) bool) bool {\n\tfor _, v := range vs {\n\t\tif !f(v) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Filter returns a new slice containing all strings in the\n// slice that satisfy the predicate `f`.\nfunc Filter(vs []string, f func(string) bool) []string {\n\tvsf := make([]string, 0)\n\tfor _, v := range vs {\n\t\tif f(v) {\n\t\t\tvsf = append(vsf, v)\n\t\t}\n\t}\n\treturn vsf\n}\n\n// Map returns a new slice containing the results of applying\n// the function `f` to each string in the original slice.\nfunc Map(vs []string, f func(string) string) []string {\n\tvsm := make([]string, len(vs))\n\tfor i, v := range vs {\n\t\tvsm[i] = f(v)\n\t}\n\treturn vsm\n}\n\nfunc main() {\n\n\t// Here we try out our various collection functions.\n\tvar strs = []string{\"peach\", \"apple\", \"pear\", \"plum\"}\n\n\tfmt.Println(Index(strs, \"pear\"))\n\n\tfmt.Println(Include(strs, \"grape\"))\n\n\tfmt.Println(Any(strs, func(v string) bool {\n\t\treturn strings.HasPrefix(v, \"p\")\n\t}))\n\n\tfmt.Println(All(strs, func(v string) bool {\n\t\treturn strings.HasPrefix(v, \"p\")\n\t}))\n\n\tfmt.Println(Filter(strs, func(v string) bool {\n\t\treturn strings.Contains(v, \"e\")\n\t}))\n\n\t// The above examples all used anonymous functions,\n\t// but you can also use named functions of the correct\n\t// type.\n\tfmt.Println(Map(strs, strings.ToUpper))\n\n}\n"
  },
  {
    "path": "parser/_testdata/collection/parser.expect",
    "content": "package main\n\nfile collection.xgo\nast.GenDecl:\n  Tok: import\n  Specs:\n    ast.ImportSpec:\n      Path:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"fmt\"\n    ast.ImportSpec:\n      Path:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"strings\"\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Index returns the first index of the target string `t`, or\n        ast.Comment:\n          Text: // -1 if no match is found.\n  Name:\n    ast.Ident:\n      Name: Index\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: vs\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: t\n              Type:\n                ast.Ident:\n                  Name: string\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: int\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: i\n          Value:\n            ast.Ident:\n              Name: v\n          Tok: :=\n          X:\n            ast.Ident:\n              Name: vs\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.IfStmt:\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: v\n                      Op: ==\n                      Y:\n                        ast.Ident:\n                          Name: t\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ReturnStmt:\n                          Results:\n                            ast.Ident:\n                              Name: i\n        ast.ReturnStmt:\n          Results:\n            ast.UnaryExpr:\n              Op: -\n              X:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Include returns `true` if the target string t is in the\n        ast.Comment:\n          Text: // slice.\n  Name:\n    ast.Ident:\n      Name: Include\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: vs\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: t\n              Type:\n                ast.Ident:\n                  Name: string\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: bool\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ReturnStmt:\n          Results:\n            ast.BinaryExpr:\n              X:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: Index\n                  Args:\n                    ast.Ident:\n                      Name: vs\n                    ast.Ident:\n                      Name: t\n              Op: >=\n              Y:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 0\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Any returns `true` if one of the strings in the slice\n        ast.Comment:\n          Text: // satisfies the predicate `f`.\n  Name:\n    ast.Ident:\n      Name: Any\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: vs\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: f\n              Type:\n                ast.FuncType:\n                  Params:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: string\n                  Results:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: bool\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: bool\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: _\n          Value:\n            ast.Ident:\n              Name: v\n          Tok: :=\n          X:\n            ast.Ident:\n              Name: vs\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.IfStmt:\n                  Cond:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: f\n                      Args:\n                        ast.Ident:\n                          Name: v\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ReturnStmt:\n                          Results:\n                            ast.Ident:\n                              Name: true\n        ast.ReturnStmt:\n          Results:\n            ast.Ident:\n              Name: false\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // All returns `true` if all of the strings in the slice\n        ast.Comment:\n          Text: // satisfy the predicate `f`.\n  Name:\n    ast.Ident:\n      Name: All\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: vs\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: f\n              Type:\n                ast.FuncType:\n                  Params:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: string\n                  Results:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: bool\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: bool\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: _\n          Value:\n            ast.Ident:\n              Name: v\n          Tok: :=\n          X:\n            ast.Ident:\n              Name: vs\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.IfStmt:\n                  Cond:\n                    ast.UnaryExpr:\n                      Op: !\n                      X:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: f\n                          Args:\n                            ast.Ident:\n                              Name: v\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ReturnStmt:\n                          Results:\n                            ast.Ident:\n                              Name: false\n        ast.ReturnStmt:\n          Results:\n            ast.Ident:\n              Name: true\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Filter returns a new slice containing all strings in the\n        ast.Comment:\n          Text: // slice that satisfy the predicate `f`.\n  Name:\n    ast.Ident:\n      Name: Filter\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: vs\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: f\n              Type:\n                ast.FuncType:\n                  Params:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: string\n                  Results:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: bool\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: vsf\n          Tok: :=\n          Rhs:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: make\n              Args:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 0\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: _\n          Value:\n            ast.Ident:\n              Name: v\n          Tok: :=\n          X:\n            ast.Ident:\n              Name: vs\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.IfStmt:\n                  Cond:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: f\n                      Args:\n                        ast.Ident:\n                          Name: v\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.AssignStmt:\n                          Lhs:\n                            ast.Ident:\n                              Name: vsf\n                          Tok: =\n                          Rhs:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: append\n                              Args:\n                                ast.Ident:\n                                  Name: vsf\n                                ast.Ident:\n                                  Name: v\n        ast.ReturnStmt:\n          Results:\n            ast.Ident:\n              Name: vsf\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Map returns a new slice containing the results of applying\n        ast.Comment:\n          Text: // the function `f` to each string in the original slice.\n  Name:\n    ast.Ident:\n      Name: Map\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: vs\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: f\n              Type:\n                ast.FuncType:\n                  Params:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: string\n                  Results:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: string\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: vsm\n          Tok: :=\n          Rhs:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: make\n              Args:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: string\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: len\n                  Args:\n                    ast.Ident:\n                      Name: vs\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: i\n          Value:\n            ast.Ident:\n              Name: v\n          Tok: :=\n          X:\n            ast.Ident:\n              Name: vs\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.AssignStmt:\n                  Lhs:\n                    ast.IndexExpr:\n                      X:\n                        ast.Ident:\n                          Name: vsm\n                      Index:\n                        ast.Ident:\n                          Name: i\n                  Tok: =\n                  Rhs:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: f\n                      Args:\n                        ast.Ident:\n                          Name: v\n        ast.ReturnStmt:\n          Results:\n            ast.Ident:\n              Name: vsm\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.DeclStmt:\n          Decl:\n            ast.GenDecl:\n              Doc:\n                ast.CommentGroup:\n                  List:\n                    ast.Comment:\n                      Text: // Here we try out our various collection functions.\n              Tok: var\n              Specs:\n                ast.ValueSpec:\n                  Names:\n                    ast.Ident:\n                      Name: strs\n                  Values:\n                    ast.CompositeLit:\n                      Type:\n                        ast.ArrayType:\n                          Elt:\n                            ast.Ident:\n                              Name: string\n                      Elts:\n                        ast.BasicLit:\n                          Kind: STRING\n                          Value: \"peach\"\n                        ast.BasicLit:\n                          Kind: STRING\n                          Value: \"apple\"\n                        ast.BasicLit:\n                          Kind: STRING\n                          Value: \"pear\"\n                        ast.BasicLit:\n                          Kind: STRING\n                          Value: \"plum\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: fmt\n                  Sel:\n                    ast.Ident:\n                      Name: Println\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: Index\n                  Args:\n                    ast.Ident:\n                      Name: strs\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"pear\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: fmt\n                  Sel:\n                    ast.Ident:\n                      Name: Println\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: Include\n                  Args:\n                    ast.Ident:\n                      Name: strs\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"grape\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: fmt\n                  Sel:\n                    ast.Ident:\n                      Name: Println\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: Any\n                  Args:\n                    ast.Ident:\n                      Name: strs\n                    ast.FuncLit:\n                      Type:\n                        ast.FuncType:\n                          Params:\n                            ast.FieldList:\n                              List:\n                                ast.Field:\n                                  Names:\n                                    ast.Ident:\n                                      Name: v\n                                  Type:\n                                    ast.Ident:\n                                      Name: string\n                          Results:\n                            ast.FieldList:\n                              List:\n                                ast.Field:\n                                  Type:\n                                    ast.Ident:\n                                      Name: bool\n                      Body:\n                        ast.BlockStmt:\n                          List:\n                            ast.ReturnStmt:\n                              Results:\n                                ast.CallExpr:\n                                  Fun:\n                                    ast.SelectorExpr:\n                                      X:\n                                        ast.Ident:\n                                          Name: strings\n                                      Sel:\n                                        ast.Ident:\n                                          Name: HasPrefix\n                                  Args:\n                                    ast.Ident:\n                                      Name: v\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"p\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: fmt\n                  Sel:\n                    ast.Ident:\n                      Name: Println\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: All\n                  Args:\n                    ast.Ident:\n                      Name: strs\n                    ast.FuncLit:\n                      Type:\n                        ast.FuncType:\n                          Params:\n                            ast.FieldList:\n                              List:\n                                ast.Field:\n                                  Names:\n                                    ast.Ident:\n                                      Name: v\n                                  Type:\n                                    ast.Ident:\n                                      Name: string\n                          Results:\n                            ast.FieldList:\n                              List:\n                                ast.Field:\n                                  Type:\n                                    ast.Ident:\n                                      Name: bool\n                      Body:\n                        ast.BlockStmt:\n                          List:\n                            ast.ReturnStmt:\n                              Results:\n                                ast.CallExpr:\n                                  Fun:\n                                    ast.SelectorExpr:\n                                      X:\n                                        ast.Ident:\n                                          Name: strings\n                                      Sel:\n                                        ast.Ident:\n                                          Name: HasPrefix\n                                  Args:\n                                    ast.Ident:\n                                      Name: v\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"p\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: fmt\n                  Sel:\n                    ast.Ident:\n                      Name: Println\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: Filter\n                  Args:\n                    ast.Ident:\n                      Name: strs\n                    ast.FuncLit:\n                      Type:\n                        ast.FuncType:\n                          Params:\n                            ast.FieldList:\n                              List:\n                                ast.Field:\n                                  Names:\n                                    ast.Ident:\n                                      Name: v\n                                  Type:\n                                    ast.Ident:\n                                      Name: string\n                          Results:\n                            ast.FieldList:\n                              List:\n                                ast.Field:\n                                  Type:\n                                    ast.Ident:\n                                      Name: bool\n                      Body:\n                        ast.BlockStmt:\n                          List:\n                            ast.ReturnStmt:\n                              Results:\n                                ast.CallExpr:\n                                  Fun:\n                                    ast.SelectorExpr:\n                                      X:\n                                        ast.Ident:\n                                          Name: strings\n                                      Sel:\n                                        ast.Ident:\n                                          Name: Contains\n                                  Args:\n                                    ast.Ident:\n                                      Name: v\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"e\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: fmt\n                  Sel:\n                    ast.Ident:\n                      Name: Println\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: Map\n                  Args:\n                    ast.Ident:\n                      Name: strs\n                    ast.SelectorExpr:\n                      X:\n                        ast.Ident:\n                          Name: strings\n                      Sel:\n                        ast.Ident:\n                          Name: ToUpper\n"
  },
  {
    "path": "parser/_testdata/complit/complit.xgo",
    "content": "a := [][]int{}\nprintln(a)\n"
  },
  {
    "path": "parser/_testdata/complit/parser.expect",
    "content": "package main\n\nfile complit.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.CompositeLit:\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.ArrayType:\n                      Elt:\n                        ast.Ident:\n                          Name: int\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: a\n"
  },
  {
    "path": "parser/_testdata/domainhuh/huh.xgo",
    "content": "form := huh`> &ret, 10\n<form>\n\t<group>\n\t\t<select id=\"Burger\" title=\"Choose your burger\">\n\t\t\t<option value=\"classic\" title=\"Charmburger Classic\"/>\n\t\t\t<option value=\"chickwich\" title=\"Chickwich\"/>\n\t\t\t<option value=\"fishburger\" title=\"Fishburger\"/>\n\t\t\t<option value=\"charmpossible\" title=\"Charmpossible™ Burger\"/>\n\t\t</select>\n\t</group>\n</form>\n`\n"
  },
  {
    "path": "parser/_testdata/domainhuh/parser.expect",
    "content": "package main\n\nfile huh.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: form\n          Tok: :=\n          Rhs:\n            ast.DomainTextLit:\n              Domain:\n                ast.Ident:\n                  Name: huh\n              Value: `> &ret, 10\n<form>\n\t<group>\n\t\t<select id=\"Burger\" title=\"Choose your burger\">\n\t\t\t<option value=\"classic\" title=\"Charmburger Classic\"/>\n\t\t\t<option value=\"chickwich\" title=\"Chickwich\"/>\n\t\t\t<option value=\"fishburger\" title=\"Fishburger\"/>\n\t\t\t<option value=\"charmpossible\" title=\"Charmpossible™ Burger\"/>\n\t\t</select>\n\t</group>\n</form>\n`\n                Extra: args=2\n                  ast.UnaryExpr:\n                    Op: &\n                    X:\n                      ast.Ident:\n                        Name: ret\n                  ast.BasicLit:\n                    Kind: INT\n                    Value: 10\n                  <form>\n\t<group>\n\t\t<select id=\"Burger\" title=\"Choose your burger\">\n\t\t\t<option value=\"classic\" title=\"Charmburger Classic\"/>\n\t\t\t<option value=\"chickwich\" title=\"Chickwich\"/>\n\t\t\t<option value=\"fishburger\" title=\"Fishburger\"/>\n\t\t\t<option value=\"charmpossible\" title=\"Charmpossible™ Burger\"/>\n\t\t</select>\n\t</group>\n</form>\n\n"
  },
  {
    "path": "parser/_testdata/domaintext/parser.expect",
    "content": "package main\n\nfile tpl.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: cl\n          Tok: :=\n          Rhs:\n            ast.ErrWrapExpr:\n              X:\n                ast.DomainTextLit:\n                  Domain:\n                    ast.Ident:\n                      Name: tpl\n                  Value: `\nexpr = termExpr % (\"+\" | \"-\")\n\ntermExpr = unaryExpr % (\"*\" | \"/\")\n\nunaryExpr = operand | \"-\" unaryExpr\n\noperand = INT | FLOAT | \"(\" expr \")\"\n`\n                  Extra:\n                    ast.File:\n                      Decls:\n                        ast.Rule:\n                          Name:\n                            ast.Ident:\n                              Name: expr\n                          Expr:\n                            ast.BinaryExpr:\n                              X:\n                                ast.Ident:\n                                  Name: termExpr\n                              Op: %\n                              Y:\n                                ast.Choice:\n                                  Options:\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"+\"\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"-\"\n                        ast.Rule:\n                          Name:\n                            ast.Ident:\n                              Name: termExpr\n                          Expr:\n                            ast.BinaryExpr:\n                              X:\n                                ast.Ident:\n                                  Name: unaryExpr\n                              Op: %\n                              Y:\n                                ast.Choice:\n                                  Options:\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"*\"\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"/\"\n                        ast.Rule:\n                          Name:\n                            ast.Ident:\n                              Name: unaryExpr\n                          Expr:\n                            ast.Choice:\n                              Options:\n                                ast.Ident:\n                                  Name: operand\n                                ast.Sequence:\n                                  Items:\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"-\"\n                                    ast.Ident:\n                                      Name: unaryExpr\n                        ast.Rule:\n                          Name:\n                            ast.Ident:\n                              Name: operand\n                          Expr:\n                            ast.Choice:\n                              Options:\n                                ast.Ident:\n                                  Name: INT\n                                ast.Ident:\n                                  Name: FLOAT\n                                ast.Sequence:\n                                  Items:\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"(\"\n                                    ast.Ident:\n                                      Name: expr\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \")\"\n              Tok: !\n"
  },
  {
    "path": "parser/_testdata/domaintext/tpl.xgo",
    "content": "cl := tpl`\nexpr = termExpr % (\"+\" | \"-\")\n\ntermExpr = unaryExpr % (\"*\" | \"/\")\n\nunaryExpr = operand | \"-\" unaryExpr\n\noperand = INT | FLOAT | \"(\" expr \")\"\n`!\n"
  },
  {
    "path": "parser/_testdata/domaintpl/parser.expect",
    "content": "package main\n\nfile tpl.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: cl\n          Tok: :=\n          Rhs:\n            ast.ErrWrapExpr:\n              X:\n                ast.DomainTextLit:\n                  Domain:\n                    ast.Ident:\n                      Name: tpl\n                  Value: `\nfile = stmts => {\n\treturn &ast.File{\n\t\tStmts: this.([]ast.Stmt),\n\t}\n}\n\nstmts = *(stmt \";\") => {\n\treturn [n.([]any)[0].(ast.Stmt) for n in this]\n}\n`\n                  Extra:\n                    ast.File:\n                      Decls:\n                        ast.Rule:\n                          Name:\n                            ast.Ident:\n                              Name: file\n                          Expr:\n                            ast.Ident:\n                              Name: stmts\n                          RetProc:\n                            ast.LambdaExpr2:\n                              Body:\n                                ast.BlockStmt:\n                                  List:\n                                    ast.ReturnStmt:\n                                      Results:\n                                        ast.UnaryExpr:\n                                          Op: &\n                                          X:\n                                            ast.CompositeLit:\n                                              Type:\n                                                ast.SelectorExpr:\n                                                  X:\n                                                    ast.Ident:\n                                                      Name: ast\n                                                  Sel:\n                                                    ast.Ident:\n                                                      Name: File\n                                              Elts:\n                                                ast.KeyValueExpr:\n                                                  Key:\n                                                    ast.Ident:\n                                                      Name: Stmts\n                                                  Value:\n                                                    ast.TypeAssertExpr:\n                                                      X:\n                                                        ast.Ident:\n                                                          Name: this\n                                                      Type:\n                                                        ast.ArrayType:\n                                                          Elt:\n                                                            ast.SelectorExpr:\n                                                              X:\n                                                                ast.Ident:\n                                                                  Name: ast\n                                                              Sel:\n                                                                ast.Ident:\n                                                                  Name: Stmt\n                        ast.Rule:\n                          Name:\n                            ast.Ident:\n                              Name: stmts\n                          Expr:\n                            ast.UnaryExpr:\n                              Op: *\n                              X:\n                                ast.Sequence:\n                                  Items:\n                                    ast.Ident:\n                                      Name: stmt\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \";\"\n                          RetProc:\n                            ast.LambdaExpr2:\n                              Body:\n                                ast.BlockStmt:\n                                  List:\n                                    ast.ReturnStmt:\n                                      Results:\n                                        ast.ComprehensionExpr:\n                                          Tok: [\n                                          Elt:\n                                            ast.TypeAssertExpr:\n                                              X:\n                                                ast.IndexExpr:\n                                                  X:\n                                                    ast.TypeAssertExpr:\n                                                      X:\n                                                        ast.Ident:\n                                                          Name: n\n                                                      Type:\n                                                        ast.ArrayType:\n                                                          Elt:\n                                                            ast.Ident:\n                                                              Name: any\n                                                  Index:\n                                                    ast.BasicLit:\n                                                      Kind: INT\n                                                      Value: 0\n                                              Type:\n                                                ast.SelectorExpr:\n                                                  X:\n                                                    ast.Ident:\n                                                      Name: ast\n                                                  Sel:\n                                                    ast.Ident:\n                                                      Name: Stmt\n                                          Fors:\n                                            ast.ForPhrase:\n                                              Value:\n                                                ast.Ident:\n                                                  Name: n\n                                              X:\n                                                ast.Ident:\n                                                  Name: this\n              Tok: !\n"
  },
  {
    "path": "parser/_testdata/domaintpl/tpl.xgo",
    "content": "cl := tpl`\nfile = stmts => {\n\treturn &ast.File{\n\t\tStmts: this.([]ast.Stmt),\n\t}\n}\n\nstmts = *(stmt \";\") => {\n\treturn [n.([]any)[0].(ast.Stmt) for n in this]\n}\n`!\n"
  },
  {
    "path": "parser/_testdata/dql1/dql.xgo",
    "content": "echo doc.**.*@($class == \"red\")\necho doc.**.users@($name == \"ken\").$age\necho doc.*.\"elem-name\"@isTotal(self).**.\n\t\"a\".$\"attr-name\"\n"
  },
  {
    "path": "parser/_testdata/dql1/parser.expect",
    "content": "package main\n\nfile dql.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.CondExpr:\n                  X:\n                    ast.AnySelectorExpr:\n                      X:\n                        ast.Ident:\n                          Name: doc\n                      Sel:\n                        ast.Ident:\n                          Name: *\n                  Cond:\n                    ast.ParenExpr:\n                      X:\n                        ast.BinaryExpr:\n                          X:\n                            ast.EnvExpr:\n                              Name:\n                                ast.Ident:\n                                  Name: class\n                          Op: ==\n                          Y:\n                            ast.BasicLit:\n                              Kind: STRING\n                              Value: \"red\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.SelectorExpr:\n                  X:\n                    ast.CondExpr:\n                      X:\n                        ast.AnySelectorExpr:\n                          X:\n                            ast.Ident:\n                              Name: doc\n                          Sel:\n                            ast.Ident:\n                              Name: users\n                      Cond:\n                        ast.ParenExpr:\n                          X:\n                            ast.BinaryExpr:\n                              X:\n                                ast.EnvExpr:\n                                  Name:\n                                    ast.Ident:\n                                      Name: name\n                              Op: ==\n                              Y:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"ken\"\n                  Sel:\n                    ast.Ident:\n                      Name: $age\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.SelectorExpr:\n                  X:\n                    ast.AnySelectorExpr:\n                      X:\n                        ast.CondExpr:\n                          X:\n                            ast.SelectorExpr:\n                              X:\n                                ast.SelectorExpr:\n                                  X:\n                                    ast.Ident:\n                                      Name: doc\n                                  Sel:\n                                    ast.Ident:\n                                      Name: *\n                              Sel:\n                                ast.Ident:\n                                  Name: \"elem-name\"\n                          Cond:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: isTotal\n                              Args:\n                                ast.Ident:\n                                  Name: self\n                      Sel:\n                        ast.Ident:\n                          Name: \"a\"\n                  Sel:\n                    ast.Ident:\n                      Name: $\"attr-name\"\n"
  },
  {
    "path": "parser/_testdata/dql2/dql.xgo",
    "content": "echo doc.*@users@\"users\".$name\n"
  },
  {
    "path": "parser/_testdata/dql2/parser.expect",
    "content": "package main\n\nfile dql.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.SelectorExpr:\n                  X:\n                    ast.CondExpr:\n                      X:\n                        ast.CondExpr:\n                          X:\n                            ast.SelectorExpr:\n                              X:\n                                ast.Ident:\n                                  Name: doc\n                              Sel:\n                                ast.Ident:\n                                  Name: *\n                          Cond:\n                            ast.Ident:\n                              Name: users\n                      Cond:\n                        ast.Ident:\n                          Name: \"users\"\n                  Sel:\n                    ast.Ident:\n                      Name: $name\n"
  },
  {
    "path": "parser/_testdata/dql3/dql.xgo",
    "content": "a := doc.**.a@(($\"aria-label\"?:\"\").hasPrefix(importedBy))\n"
  },
  {
    "path": "parser/_testdata/dql3/parser.expect",
    "content": "package main\n\nfile dql.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.CondExpr:\n              X:\n                ast.AnySelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: doc\n                  Sel:\n                    ast.Ident:\n                      Name: a\n              Cond:\n                ast.ParenExpr:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.SelectorExpr:\n                          X:\n                            ast.ParenExpr:\n                              X:\n                                ast.ErrWrapExpr:\n                                  X:\n                                    ast.EnvExpr:\n                                      Name:\n                                        ast.Ident:\n                                          Name: \"aria-label\"\n                                  Tok: ?\n                                  Default:\n                                    ast.BasicLit:\n                                      Kind: STRING\n                                      Value: \"\"\n                          Sel:\n                            ast.Ident:\n                              Name: hasPrefix\n                      Args:\n                        ast.Ident:\n                          Name: importedBy\n"
  },
  {
    "path": "parser/_testdata/embedded1/embtype.xgo",
    "content": "type T struct {\n\tabc.E\n}\n"
  },
  {
    "path": "parser/_testdata/embedded1/parser.expect",
    "content": "package main\n\nfile embtype.xgo\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: T\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.SelectorExpr:\n                      X:\n                        ast.Ident:\n                          Name: abc\n                      Sel:\n                        ast.Ident:\n                          Name: E\n"
  },
  {
    "path": "parser/_testdata/envop1/envop.xgo",
    "content": "echo ${name}, $id\n"
  },
  {
    "path": "parser/_testdata/envop1/parser.expect",
    "content": "package main\n\nfile envop.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.EnvExpr:\n                  Name:\n                    ast.Ident:\n                      Name: name\n                ast.EnvExpr:\n                  Name:\n                    ast.Ident:\n                      Name: id\n"
  },
  {
    "path": "parser/_testdata/envop2/envop.xgo",
    "content": "${name}\necho {\"id\": $id}\n"
  },
  {
    "path": "parser/_testdata/envop2/parser.expect",
    "content": "package main\n\nfile envop.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.EnvExpr:\n              Name:\n                ast.Ident:\n                  Name: name\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.CompositeLit:\n                  Elts:\n                    ast.KeyValueExpr:\n                      Key:\n                        ast.BasicLit:\n                          Kind: STRING\n                          Value: \"id\"\n                      Value:\n                        ast.EnvExpr:\n                          Name:\n                            ast.Ident:\n                              Name: id\n"
  },
  {
    "path": "parser/_testdata/errwrap1/errwrap.xgo",
    "content": "import (\n\t\"strconv\"\n)\n\nfunc add(x, y string) (int, error) {\n\treturn strconv.Atoi(x)? + strconv.Atoi(y)?, nil\n}\n\nfunc addSafe(x, y string) int {\n\treturn strconv.Atoi(x)?:0 + strconv.Atoi(y)?:0\n}\n\nprintln(`add(\"100\", \"23\"):`, add(\"100\", \"23\")!)\n\nsum, err := add(\"10\", \"abc\")\nprintln(`add(\"10\", \"abc\"):`, sum, err)\n\nprintln(`addSafe(\"10\", \"abc\"):`, addSafe(\"10\", \"abc\"))\n"
  },
  {
    "path": "parser/_testdata/errwrap1/parser.expect",
    "content": "package main\n\nfile errwrap.xgo\nnoEntrypoint\nast.GenDecl:\n  Tok: import\n  Specs:\n    ast.ImportSpec:\n      Path:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"strconv\"\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: add\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: x\n                ast.Ident:\n                  Name: y\n              Type:\n                ast.Ident:\n                  Name: string\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: int\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: error\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ReturnStmt:\n          Results:\n            ast.BinaryExpr:\n              X:\n                ast.ErrWrapExpr:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.SelectorExpr:\n                          X:\n                            ast.Ident:\n                              Name: strconv\n                          Sel:\n                            ast.Ident:\n                              Name: Atoi\n                      Args:\n                        ast.Ident:\n                          Name: x\n                  Tok: ?\n              Op: +\n              Y:\n                ast.ErrWrapExpr:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.SelectorExpr:\n                          X:\n                            ast.Ident:\n                              Name: strconv\n                          Sel:\n                            ast.Ident:\n                              Name: Atoi\n                      Args:\n                        ast.Ident:\n                          Name: y\n                  Tok: ?\n            ast.Ident:\n              Name: nil\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: addSafe\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: x\n                ast.Ident:\n                  Name: y\n              Type:\n                ast.Ident:\n                  Name: string\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: int\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ReturnStmt:\n          Results:\n            ast.BinaryExpr:\n              X:\n                ast.ErrWrapExpr:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.SelectorExpr:\n                          X:\n                            ast.Ident:\n                              Name: strconv\n                          Sel:\n                            ast.Ident:\n                              Name: Atoi\n                      Args:\n                        ast.Ident:\n                          Name: x\n                  Tok: ?\n                  Default:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\n              Op: +\n              Y:\n                ast.ErrWrapExpr:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.SelectorExpr:\n                          X:\n                            ast.Ident:\n                              Name: strconv\n                          Sel:\n                            ast.Ident:\n                              Name: Atoi\n                      Args:\n                        ast.Ident:\n                          Name: y\n                  Tok: ?\n                  Default:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: `add(\"100\", \"23\"):`\n                ast.ErrWrapExpr:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: add\n                      Args:\n                        ast.BasicLit:\n                          Kind: STRING\n                          Value: \"100\"\n                        ast.BasicLit:\n                          Kind: STRING\n                          Value: \"23\"\n                  Tok: !\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: sum\n            ast.Ident:\n              Name: err\n          Tok: :=\n          Rhs:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: add\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"10\"\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"abc\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: `add(\"10\", \"abc\"):`\n                ast.Ident:\n                  Name: sum\n                ast.Ident:\n                  Name: err\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: `addSafe(\"10\", \"abc\"):`\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: addSafe\n                  Args:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"10\"\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"abc\"\n"
  },
  {
    "path": "parser/_testdata/errwrap2/errwrap2.xgo",
    "content": "func neg(x string) (int, error) {\n\treturn -atoi(x)?, nil\n}\n"
  },
  {
    "path": "parser/_testdata/errwrap2/parser.expect",
    "content": "package main\n\nfile errwrap2.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: neg\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: x\n              Type:\n                ast.Ident:\n                  Name: string\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: int\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: error\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ReturnStmt:\n          Results:\n            ast.UnaryExpr:\n              Op: -\n              X:\n                ast.ErrWrapExpr:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: atoi\n                      Args:\n                        ast.Ident:\n                          Name: x\n                  Tok: ?\n            ast.Ident:\n              Name: nil\n"
  },
  {
    "path": "parser/_testdata/errwrap3/errwrap3.xgo",
    "content": "mkdir! \"foo\"\nprintln foo()!.fields\n"
  },
  {
    "path": "parser/_testdata/errwrap3/parser.expect",
    "content": "package main\n\nfile errwrap3.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.ErrWrapExpr:\n                  X:\n                    ast.Ident:\n                      Name: mkdir\n                  Tok: !\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"foo\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.SelectorExpr:\n                  X:\n                    ast.ErrWrapExpr:\n                      X:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: foo\n                      Tok: !\n                  Sel:\n                    ast.Ident:\n                      Name: fields\n"
  },
  {
    "path": "parser/_testdata/exists/exists.xgo",
    "content": "a := [1, 3, 5, 7, 8, 19]\nhasEven := {for x in a if x%2 == 0}\n"
  },
  {
    "path": "parser/_testdata/exists/parser.expect",
    "content": "package main\n\nfile exists.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 8\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 19\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: hasEven\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: {\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: x\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BinaryExpr:\n                          X:\n                            ast.Ident:\n                              Name: x\n                          Op: %\n                          Y:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 2\n                      Op: ==\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 0\n"
  },
  {
    "path": "parser/_testdata/fnbody/fnbody.xgo",
    "content": "a := 1\n{\n\ttype T = int\n\tvar b = 2\n\tvar c T = 3\n}\n"
  },
  {
    "path": "parser/_testdata/fnbody/parser.expect",
    "content": "package main\n\nfile fnbody.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 1\n        ast.BlockStmt:\n          List:\n            ast.DeclStmt:\n              Decl:\n                ast.GenDecl:\n                  Tok: type\n                  Specs:\n                    ast.TypeSpec:\n                      Name:\n                        ast.Ident:\n                          Name: T\n                      Type:\n                        ast.Ident:\n                          Name: int\n            ast.DeclStmt:\n              Decl:\n                ast.GenDecl:\n                  Tok: var\n                  Specs:\n                    ast.ValueSpec:\n                      Names:\n                        ast.Ident:\n                          Name: b\n                      Values:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 2\n            ast.DeclStmt:\n              Decl:\n                ast.GenDecl:\n                  Tok: var\n                  Specs:\n                    ast.ValueSpec:\n                      Names:\n                        ast.Ident:\n                          Name: c\n                      Type:\n                        ast.Ident:\n                          Name: T\n                      Values:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n"
  },
  {
    "path": "parser/_testdata/fncall/fncall.xgo",
    "content": "fn(1)(x)\n"
  },
  {
    "path": "parser/_testdata/fncall/parser.expect",
    "content": "package main\n\nfile fncall.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: fn\n                  Args:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n              Args:\n                ast.Ident:\n                  Name: x\n"
  },
  {
    "path": "parser/_testdata/forloop/forloop.xgo",
    "content": "n := 0\nfor range [1, 3, 5, 7, 11] {\n\tn++\n}\nprintln(\"n:\", n)\n\nfor x := range [1] {\n}\n\nsum := 0\nfor _, x := range [1, 3, 5, 7, 11] {\n\tif x > 3 {\n\t\tsum += x\n\t}\n}\nprintln(\"sum(1,3,5,7,11):\", sum)\n\nsum = 0\nfor i := 1; i < 100; i++ {\n\tsum += i\n}\nprintln(\"sum(1-100):\", sum)\n\nfor x in [1] {\n\tprintln(x)\n}\n\nfor i, x in [1] if i%2 == 0 {\n\tprintln(i, x)\n}\n"
  },
  {
    "path": "parser/_testdata/forloop/parser.expect",
    "content": "package main\n\nfile forloop.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: n\n          Tok: :=\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 0\n        ast.RangeStmt:\n          Tok: ILLEGAL\n          X:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 11\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.IncDecStmt:\n                  X:\n                    ast.Ident:\n                      Name: n\n                  Tok: ++\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"n:\"\n                ast.Ident:\n                  Name: n\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: x\n          Tok: :=\n          X:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n          Body:\n            ast.BlockStmt:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: sum\n          Tok: :=\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 0\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: _\n          Value:\n            ast.Ident:\n              Name: x\n          Tok: :=\n          X:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 11\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.IfStmt:\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: x\n                      Op: >\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.AssignStmt:\n                          Lhs:\n                            ast.Ident:\n                              Name: sum\n                          Tok: +=\n                          Rhs:\n                            ast.Ident:\n                              Name: x\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"sum(1,3,5,7,11):\"\n                ast.Ident:\n                  Name: sum\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: sum\n          Tok: =\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 0\n        ast.ForStmt:\n          Init:\n            ast.AssignStmt:\n              Lhs:\n                ast.Ident:\n                  Name: i\n              Tok: :=\n              Rhs:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n          Cond:\n            ast.BinaryExpr:\n              X:\n                ast.Ident:\n                  Name: i\n              Op: <\n              Y:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 100\n          Post:\n            ast.IncDecStmt:\n              X:\n                ast.Ident:\n                  Name: i\n              Tok: ++\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.AssignStmt:\n                  Lhs:\n                    ast.Ident:\n                      Name: sum\n                  Tok: +=\n                  Rhs:\n                    ast.Ident:\n                      Name: i\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"sum(1-100):\"\n                ast.Ident:\n                  Name: sum\n        ast.ForPhraseStmt:\n          ForPhrase:\n            ast.ForPhrase:\n              Value:\n                ast.Ident:\n                  Name: x\n              X:\n                ast.SliceLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: x\n        ast.ForPhraseStmt:\n          ForPhrase:\n            ast.ForPhrase:\n              Key:\n                ast.Ident:\n                  Name: i\n              Value:\n                ast.Ident:\n                  Name: x\n              X:\n                ast.SliceLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n              Cond:\n                ast.BinaryExpr:\n                  X:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: i\n                      Op: %\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 2\n                  Op: ==\n                  Y:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: i\n                        ast.Ident:\n                          Name: x\n"
  },
  {
    "path": "parser/_testdata/funcdecl1/fndecl.xgo",
    "content": "func() (int, int) {\n\treturn 1, 1\n}()\n"
  },
  {
    "path": "parser/_testdata/funcdecl1/parser.expect",
    "content": "package main\n\nfile fndecl.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.FuncLit:\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                      Results:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ReturnStmt:\n                          Results:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 1\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 1\n"
  },
  {
    "path": "parser/_testdata/funcdecl2/fndecl.xgo",
    "content": "func() {\n}()\n"
  },
  {
    "path": "parser/_testdata/funcdecl2/parser.expect",
    "content": "package main\n\nfile fndecl.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.FuncLit:\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                  Body:\n                    ast.BlockStmt:\n"
  },
  {
    "path": "parser/_testdata/funcdecl3/fndecl.xgo",
    "content": "func() int {\n\treturn 1\n}()\n"
  },
  {
    "path": "parser/_testdata/funcdecl3/parser.expect",
    "content": "package main\n\nfile fndecl.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.FuncLit:\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                      Results:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ReturnStmt:\n                          Results:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 1\n"
  },
  {
    "path": "parser/_testdata/funcdoc/funcdoc.xgo",
    "content": "package foo\n\n//go:noinline\n//go:uintptrescapes\nfunc test(s string, p, q uintptr, rest ...uintptr) int {\n}\n"
  },
  {
    "path": "parser/_testdata/funcdoc/parser.expect",
    "content": "package foo\n\nfile funcdoc.xgo\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: //go:noinline\n        ast.Comment:\n          Text: //go:uintptrescapes\n  Name:\n    ast.Ident:\n      Name: test\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: s\n              Type:\n                ast.Ident:\n                  Name: string\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p\n                ast.Ident:\n                  Name: q\n              Type:\n                ast.Ident:\n                  Name: uintptr\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: rest\n              Type:\n                ast.Ellipsis:\n                  Elt:\n                    ast.Ident:\n                      Name: uintptr\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: int\n  Body:\n    ast.BlockStmt:\n"
  },
  {
    "path": "parser/_testdata/funclit/funclit.xgo",
    "content": "func(x, y int) *int {\n\treturn nil\n}(100, 200)\n\nprintln \"hello\"\n"
  },
  {
    "path": "parser/_testdata/funclit/parser.expect",
    "content": "package main\n\nfile funclit.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.FuncLit:\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: x\n                                ast.Ident:\n                                  Name: y\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                      Results:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.StarExpr:\n                                  X:\n                                    ast.Ident:\n                                      Name: int\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ReturnStmt:\n                          Results:\n                            ast.Ident:\n                              Name: nil\n              Args:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 100\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 200\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"hello\"\n"
  },
  {
    "path": "parser/_testdata/functype/dummy/dummy.md",
    "content": "dummy file\n"
  },
  {
    "path": "parser/_testdata/functype/functype.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ntype T struct {\n\t*T\n\tA int `json:\"a\"`\n}\n\nfunc bar(v chan bool) (int, <-chan error) {\n\tv <- true\n\t<-v\n\treturn 0, (<-chan error)(nil)\n}\n\nfunc foo(f func([]byte, *string, ...T) chan<- int) (v int, err error) {\n\treturn\n}\n"
  },
  {
    "path": "parser/_testdata/functype/parser.expect",
    "content": "package main\n\nfile functype.go\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: T\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.StarExpr:\n                      X:\n                        ast.Ident:\n                          Name: T\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: A\n                  Type:\n                    ast.Ident:\n                      Name: int\n                  Tag:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: `json:\"a\"`\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: bar\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: v\n              Type:\n                ast.ChanType:\n                  Value:\n                    ast.Ident:\n                      Name: bool\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: int\n            ast.Field:\n              Type:\n                ast.ChanType:\n                  Value:\n                    ast.Ident:\n                      Name: error\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.SendStmt:\n          Chan:\n            ast.Ident:\n              Name: v\n          Values:\n            ast.Ident:\n              Name: true\n        ast.ExprStmt:\n          X:\n            ast.UnaryExpr:\n              Op: <-\n              X:\n                ast.Ident:\n                  Name: v\n        ast.ReturnStmt:\n          Results:\n            ast.BasicLit:\n              Kind: INT\n              Value: 0\n            ast.CallExpr:\n              Fun:\n                ast.ParenExpr:\n                  X:\n                    ast.ChanType:\n                      Value:\n                        ast.Ident:\n                          Name: error\n              Args:\n                ast.Ident:\n                  Name: nil\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: foo\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: f\n              Type:\n                ast.FuncType:\n                  Params:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.ArrayType:\n                              Elt:\n                                ast.Ident:\n                                  Name: byte\n                        ast.Field:\n                          Type:\n                            ast.StarExpr:\n                              X:\n                                ast.Ident:\n                                  Name: string\n                        ast.Field:\n                          Type:\n                            ast.Ellipsis:\n                              Elt:\n                                ast.Ident:\n                                  Name: T\n                  Results:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.ChanType:\n                              Value:\n                                ast.Ident:\n                                  Name: int\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: v\n              Type:\n                ast.Ident:\n                  Name: int\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: err\n              Type:\n                ast.Ident:\n                  Name: error\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ReturnStmt:\n"
  },
  {
    "path": "parser/_testdata/gmxtest/foo.gmx",
    "content": "a := 1\n"
  },
  {
    "path": "parser/_testdata/gmxtest/parser.expect",
    "content": "package main\n\nfile foo.gmx\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 1\n"
  },
  {
    "path": "parser/_testdata/goto1/goto.xgo",
    "content": "goto \"a\"\ngoto 1, 2\ngoto (1+2)*3\n"
  },
  {
    "path": "parser/_testdata/goto1/parser.expect",
    "content": "package main\n\nfile goto.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: goto\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: goto\n              Args:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 2\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: goto\n              Args:\n                ast.BinaryExpr:\n                  X:\n                    ast.ParenExpr:\n                      X:\n                        ast.BinaryExpr:\n                          X:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 1\n                          Op: +\n                          Y:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 2\n                  Op: *\n                  Y:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 3\n"
  },
  {
    "path": "parser/_testdata/goto2/goto.xgo",
    "content": "goto x+y\ngoto x, y\n"
  },
  {
    "path": "parser/_testdata/goto2/parser.expect",
    "content": "package main\n\nfile goto.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: goto\n              Args:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: x\n                  Op: +\n                  Y:\n                    ast.Ident:\n                      Name: y\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: goto\n              Args:\n                ast.Ident:\n                  Name: x\n                ast.Ident:\n                  Name: y\n"
  },
  {
    "path": "parser/_testdata/goxtest1/bar.gox",
    "content": "var (\n\tA `json:\"a\"`\n\t*B\n\tx, y string\n\tC.A  `json:\"ca\"`\n\t*C.B `json:\"b\"`\n\tv    int `json:\"v\"`\n)\n\ntype A struct{}\n\ntype B struct{}\n"
  },
  {
    "path": "parser/_testdata/goxtest1/parser.expect",
    "content": "package main\n\nfile bar.gox\nast.GenDecl:\n  Tok: var\n  Specs:\n    ast.ValueSpec:\n      Type:\n        ast.Ident:\n          Name: A\n      Tag:\n        ast.BasicLit:\n          Kind: STRING\n          Value: `json:\"a\"`\n    ast.ValueSpec:\n      Type:\n        ast.StarExpr:\n          X:\n            ast.Ident:\n              Name: B\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: x\n        ast.Ident:\n          Name: y\n      Type:\n        ast.Ident:\n          Name: string\n    ast.ValueSpec:\n      Type:\n        ast.SelectorExpr:\n          X:\n            ast.Ident:\n              Name: C\n          Sel:\n            ast.Ident:\n              Name: A\n      Tag:\n        ast.BasicLit:\n          Kind: STRING\n          Value: `json:\"ca\"`\n    ast.ValueSpec:\n      Type:\n        ast.StarExpr:\n          X:\n            ast.SelectorExpr:\n              X:\n                ast.Ident:\n                  Name: C\n              Sel:\n                ast.Ident:\n                  Name: B\n      Tag:\n        ast.BasicLit:\n          Kind: STRING\n          Value: `json:\"b\"`\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: v\n      Type:\n        ast.Ident:\n          Name: int\n      Tag:\n        ast.BasicLit:\n          Kind: STRING\n          Value: `json:\"v\"`\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: A\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: B\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\n"
  },
  {
    "path": "parser/_testdata/goxtest2/bar.gox",
    "content": "var (\n\tx.App\n)\n"
  },
  {
    "path": "parser/_testdata/goxtest2/parser.expect",
    "content": "package main\n\nfile bar.gox\nast.GenDecl:\n  Tok: var\n  Specs:\n    ast.ValueSpec:\n      Type:\n        ast.SelectorExpr:\n          X:\n            ast.Ident:\n              Name: x\n          Sel:\n            ast.Ident:\n              Name: App\n"
  },
  {
    "path": "parser/_testdata/kwargs1/kwargs.xgo",
    "content": "playSound getUrl(\"1.mp3\", cache = false), loop = true\nlistDir withHidden = true, recursive = false\n"
  },
  {
    "path": "parser/_testdata/kwargs1/parser.expect",
    "content": "package main\n\nfile kwargs.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: playSound\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: getUrl\n                  Args:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"1.mp3\"\n                  Kwargs:\n                    ast.KwargExpr:\n                      Name:\n                        ast.Ident:\n                          Name: cache\n                      Value:\n                        ast.Ident:\n                          Name: false\n              Kwargs:\n                ast.KwargExpr:\n                  Name:\n                    ast.Ident:\n                      Name: loop\n                  Value:\n                    ast.Ident:\n                      Name: true\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: listDir\n              Kwargs:\n                ast.KwargExpr:\n                  Name:\n                    ast.Ident:\n                      Name: withHidden\n                  Value:\n                    ast.Ident:\n                      Name: true\n                ast.KwargExpr:\n                  Name:\n                    ast.Ident:\n                      Name: recursive\n                  Value:\n                    ast.Ident:\n                      Name: false\n"
  },
  {
    "path": "parser/_testdata/lambda1/lambda.xgo",
    "content": "package main\n\nfunc main() {\n\tfoo(=> \"Hi\")\n\tfoo(x => x * x)\n\tfoo((x, y) => x + y)\n\tfoo((x) => (x, x * 2))\n\tfoo(() => \"Hi\")\n}\n"
  },
  {
    "path": "parser/_testdata/lambda1/parser.expect",
    "content": "package main\n\nfile lambda.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr:\n                  Rhs:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"Hi\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr:\n                  Lhs:\n                    ast.Ident:\n                      Name: x\n                  Rhs:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: x\n                      Op: *\n                      Y:\n                        ast.Ident:\n                          Name: x\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr:\n                  Lhs:\n                    ast.Ident:\n                      Name: x\n                    ast.Ident:\n                      Name: y\n                  Rhs:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: x\n                      Op: +\n                      Y:\n                        ast.Ident:\n                          Name: y\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr:\n                  Lhs:\n                    ast.Ident:\n                      Name: x\n                  Rhs:\n                    ast.Ident:\n                      Name: x\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: x\n                      Op: *\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 2\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr:\n                  Rhs:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"Hi\"\n"
  },
  {
    "path": "parser/_testdata/lambda2/lambda2.xgo",
    "content": "package main\n\nfunc main() {\n\tfoo(=> {\n\t\tprintln(\"Hi\")\n\t})\n\tfoo(x => {\n\t\tprintln(x)\n\t})\n\tfoo((x, y) => {\n\t\tprintln(x, y)\n\t})\n}\n"
  },
  {
    "path": "parser/_testdata/lambda2/parser.expect",
    "content": "package main\n\nfile lambda2.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr2:\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: println\n                              Args:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"Hi\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr2:\n                  Lhs:\n                    ast.Ident:\n                      Name: x\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: println\n                              Args:\n                                ast.Ident:\n                                  Name: x\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr2:\n                  Lhs:\n                    ast.Ident:\n                      Name: x\n                    ast.Ident:\n                      Name: y\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: println\n                              Args:\n                                ast.Ident:\n                                  Name: x\n                                ast.Ident:\n                                  Name: y\n"
  },
  {
    "path": "parser/_testdata/lambda3/lambda3.xgo",
    "content": "package main\n\nfunc main() {\n\tfoo => {\n\t\tprintln \"Hi\"\n\t}\n\tfoo x => {\n\t\tprintln x\n\t}\n}\n"
  },
  {
    "path": "parser/_testdata/lambda3/parser.expect",
    "content": "package main\n\nfile lambda3.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr2:\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: println\n                              Args:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"Hi\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: foo\n              Args:\n                ast.LambdaExpr2:\n                  Lhs:\n                    ast.Ident:\n                      Name: x\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.ExprStmt:\n                          X:\n                            ast.CallExpr:\n                              Fun:\n                                ast.Ident:\n                                  Name: println\n                              Args:\n                                ast.Ident:\n                                  Name: x\n"
  },
  {
    "path": "parser/_testdata/lambda4/lambda4.xgo",
    "content": "type Foo struct {\n\tPlot func(x float64) (float64, float64)\n}\n\n&Foo{\n\tPlot: x => (x * 2, x * x),\n}\n\n&Foo{\n\tPlot: x => {\n\t\treturn x * 2, x * x\n\t},\n}\n"
  },
  {
    "path": "parser/_testdata/lambda4/parser.expect",
    "content": "package main\n\nfile lambda4.xgo\nnoEntrypoint\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Foo\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: Plot\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: x\n                              Type:\n                                ast.Ident:\n                                  Name: float64\n                      Results:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: float64\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: float64\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.UnaryExpr:\n              Op: &\n              X:\n                ast.CompositeLit:\n                  Type:\n                    ast.Ident:\n                      Name: Foo\n                  Elts:\n                    ast.KeyValueExpr:\n                      Key:\n                        ast.Ident:\n                          Name: Plot\n                      Value:\n                        ast.LambdaExpr:\n                          Lhs:\n                            ast.Ident:\n                              Name: x\n                          Rhs:\n                            ast.BinaryExpr:\n                              X:\n                                ast.Ident:\n                                  Name: x\n                              Op: *\n                              Y:\n                                ast.BasicLit:\n                                  Kind: INT\n                                  Value: 2\n                            ast.BinaryExpr:\n                              X:\n                                ast.Ident:\n                                  Name: x\n                              Op: *\n                              Y:\n                                ast.Ident:\n                                  Name: x\n        ast.ExprStmt:\n          X:\n            ast.UnaryExpr:\n              Op: &\n              X:\n                ast.CompositeLit:\n                  Type:\n                    ast.Ident:\n                      Name: Foo\n                  Elts:\n                    ast.KeyValueExpr:\n                      Key:\n                        ast.Ident:\n                          Name: Plot\n                      Value:\n                        ast.LambdaExpr2:\n                          Lhs:\n                            ast.Ident:\n                              Name: x\n                          Body:\n                            ast.BlockStmt:\n                              List:\n                                ast.ReturnStmt:\n                                  Results:\n                                    ast.BinaryExpr:\n                                      X:\n                                        ast.Ident:\n                                          Name: x\n                                      Op: *\n                                      Y:\n                                        ast.BasicLit:\n                                          Kind: INT\n                                          Value: 2\n                                    ast.BinaryExpr:\n                                      X:\n                                        ast.Ident:\n                                          Name: x\n                                      Op: *\n                                      Y:\n                                        ast.Ident:\n                                          Name: x\n"
  },
  {
    "path": "parser/_testdata/listcompr/listcompr.xgo",
    "content": "y := [x*x for x in [1]]\nprintln(y)\n\ny = [x*x for x in [1, 3, 5, 7, 11] if x > 3]\nprintln(y)\n\nz := [i+v for i, v in [1, 3, 5, 7, 11] if t := i % 2; t == 1]\nprintln(z)\n\nprintln([k+\",\"+s for k, s in {\"Hello\": \"xsw\", \"Hi\": \"XGo\"}])\n\narr := [1, 2, 3, 4, 5, 6]\nx := [[a, b] for a in arr if a < b for b in arr if b > 2]\nprintln(\"x:\", x)\n"
  },
  {
    "path": "parser/_testdata/listcompr/parser.expect",
    "content": "package main\n\nfile listcompr.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: y\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: [\n              Elt:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: x\n                  Op: *\n                  Y:\n                    ast.Ident:\n                      Name: x\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: x\n                  X:\n                    ast.SliceLit:\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: y\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: y\n          Tok: =\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: [\n              Elt:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: x\n                  Op: *\n                  Y:\n                    ast.Ident:\n                      Name: x\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: x\n                  X:\n                    ast.SliceLit:\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 5\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 7\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 11\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: x\n                      Op: >\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: y\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: z\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: [\n              Elt:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: i\n                  Op: +\n                  Y:\n                    ast.Ident:\n                      Name: v\n              Fors:\n                ast.ForPhrase:\n                  Key:\n                    ast.Ident:\n                      Name: i\n                  Value:\n                    ast.Ident:\n                      Name: v\n                  X:\n                    ast.SliceLit:\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 5\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 7\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 11\n                  Init:\n                    ast.AssignStmt:\n                      Lhs:\n                        ast.Ident:\n                          Name: t\n                      Tok: :=\n                      Rhs:\n                        ast.BinaryExpr:\n                          X:\n                            ast.Ident:\n                              Name: i\n                          Op: %\n                          Y:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 2\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: t\n                      Op: ==\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: z\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.ComprehensionExpr:\n                  Tok: [\n                  Elt:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BinaryExpr:\n                          X:\n                            ast.Ident:\n                              Name: k\n                          Op: +\n                          Y:\n                            ast.BasicLit:\n                              Kind: STRING\n                              Value: \",\"\n                      Op: +\n                      Y:\n                        ast.Ident:\n                          Name: s\n                  Fors:\n                    ast.ForPhrase:\n                      Key:\n                        ast.Ident:\n                          Name: k\n                      Value:\n                        ast.Ident:\n                          Name: s\n                      X:\n                        ast.CompositeLit:\n                          Elts:\n                            ast.KeyValueExpr:\n                              Key:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"Hello\"\n                              Value:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"xsw\"\n                            ast.KeyValueExpr:\n                              Key:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"Hi\"\n                              Value:\n                                ast.BasicLit:\n                                  Kind: STRING\n                                  Value: \"XGo\"\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: arr\n          Tok: :=\n          Rhs:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 2\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 4\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 6\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: x\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: [\n              Elt:\n                ast.SliceLit:\n                  Elts:\n                    ast.Ident:\n                      Name: a\n                    ast.Ident:\n                      Name: b\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: a\n                  X:\n                    ast.Ident:\n                      Name: arr\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: a\n                      Op: <\n                      Y:\n                        ast.Ident:\n                          Name: b\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: b\n                  X:\n                    ast.Ident:\n                      Name: arr\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.Ident:\n                          Name: b\n                      Op: >\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 2\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"x:\"\n                ast.Ident:\n                  Name: x\n"
  },
  {
    "path": "parser/_testdata/mapfunc/map.xgo",
    "content": "map strs, toUpper\nmap [\"hello\", \"world\"], toUpper\nmap[string]int{\"Hi\": 1}\nprintln map(strs, strings.ToUpper)\n"
  },
  {
    "path": "parser/_testdata/mapfunc/parser.expect",
    "content": "package main\n\nfile map.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: map\n              Args:\n                ast.Ident:\n                  Name: strs\n                ast.Ident:\n                  Name: toUpper\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: map\n              Args:\n                ast.SliceLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"hello\"\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"world\"\n                ast.Ident:\n                  Name: toUpper\n        ast.ExprStmt:\n          X:\n            ast.CompositeLit:\n              Type:\n                ast.MapType:\n                  Key:\n                    ast.Ident:\n                      Name: string\n                  Value:\n                    ast.Ident:\n                      Name: int\n              Elts:\n                ast.KeyValueExpr:\n                  Key:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"Hi\"\n                  Value:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: map\n                  Args:\n                    ast.Ident:\n                      Name: strs\n                    ast.SelectorExpr:\n                      X:\n                        ast.Ident:\n                          Name: strings\n                      Sel:\n                        ast.Ident:\n                          Name: ToUpper\n"
  },
  {
    "path": "parser/_testdata/matrix1/matrix.xgo",
    "content": "echo [\n\t1, 2, 3\n\t4, 5, 6\n]\n"
  },
  {
    "path": "parser/_testdata/matrix1/parser.expect",
    "content": "package main\n\nfile matrix.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.MatrixLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 2\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 3\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 4\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 5\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 6\n                  NElt: 2\n"
  },
  {
    "path": "parser/_testdata/matrix2/matrix.xgo",
    "content": "echo [\n\t1, 2, 3\n\trow...\n\t7, 8, 9\n]\n"
  },
  {
    "path": "parser/_testdata/matrix2/parser.expect",
    "content": "package main\n\nfile matrix.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.MatrixLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 2\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 3\n                    ast.ElemEllipsis:\n                      Elt:\n                        ast.Ident:\n                          Name: row\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 7\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 8\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 9\n                  NElt: 3\n"
  },
  {
    "path": "parser/_testdata/mytest/mytest.xgo",
    "content": "package demo\n\nimport (\n\t\"go/token\"\n\t\"os\"\n)\n\nvar stmtStart = map[token.Token]bool{\n\ttoken.BREAK:       true,\n\ttoken.CONST:       true,\n\ttoken.CONTINUE:    true,\n\ttoken.DEFER:       true,\n\ttoken.FALLTHROUGH: true,\n\ttoken.FOR:         true,\n\ttoken.GO:          true,\n\ttoken.GOTO:        true,\n\ttoken.IF:          true,\n\ttoken.RETURN:      true,\n\ttoken.SELECT:      true,\n\ttoken.SWITCH:      true,\n\ttoken.TYPE:        true,\n\ttoken.VAR:         true,\n}\n\ntype Mode uint\n\nconst (\n\t// PackageClauseOnly - stop parsing after package clause\n\tPackageClauseOnly Mode = 1 << iota\n\t// ImportsOnly - stop parsing after import declarations\n\tImportsOnly\n\t// ParseComments - parse comments and add them to AST\n\tParseComments\n\t// Trace - print a trace of parsed productions\n\tTrace\n\t// DeclarationErrors - report declaration errors\n\tDeclarationErrors\n\t// AllErrors - report all errors (not just the first 10 on different lines)\n\tAllErrors\n)\n\n// FileSystem represents a file system.\ntype FileSystem interface {\n\tReadDir(dirname string) ([]os.\n\t\tFileInfo, error)\n\tReadFile(filename string) ([]byte, error)\n\tJoin(elem ...string) string\n}\n\ntype IF = FileSystem\n\ntype Foo struct {\n\ta, b map[string]struct{}\n}\n\nfunc (p *Foo) bar() {\n}\n\nfunc init() {\n\tf, err := os.\n\t\tOpen(\"a\")\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer f.Close()\n\n\tch := make(chan bool, 100)\n\tselect {\n\tcase <-ch:\n\t\tprintln(\"1\")\n\tcase ch <- true:\n\t\tprintln(\"2\")\n\t}\n\n\tgo func(fs FileSystem) {\n\t\tif foo, ok := fs.(*Foo); ok {\n\t\t\tprintln(foo)\n\t\t}\n\t}(nil)\n}\n"
  },
  {
    "path": "parser/_testdata/mytest/parser.expect",
    "content": "package demo\n\nfile mytest.xgo\nast.GenDecl:\n  Tok: import\n  Specs:\n    ast.ImportSpec:\n      Path:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"go/token\"\n    ast.ImportSpec:\n      Path:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"os\"\nast.GenDecl:\n  Tok: var\n  Specs:\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: stmtStart\n      Values:\n        ast.CompositeLit:\n          Type:\n            ast.MapType:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: Token\n              Value:\n                ast.Ident:\n                  Name: bool\n          Elts:\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: BREAK\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: CONST\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: CONTINUE\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: DEFER\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: FALLTHROUGH\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: FOR\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: GO\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: GOTO\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: IF\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: RETURN\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: SELECT\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: SWITCH\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: TYPE\n              Value:\n                ast.Ident:\n                  Name: true\n            ast.KeyValueExpr:\n              Key:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: token\n                  Sel:\n                    ast.Ident:\n                      Name: VAR\n              Value:\n                ast.Ident:\n                  Name: true\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Mode\n      Type:\n        ast.Ident:\n          Name: uint\nast.GenDecl:\n  Tok: const\n  Specs:\n    ast.ValueSpec:\n      Doc:\n        ast.CommentGroup:\n          List:\n            ast.Comment:\n              Text: // PackageClauseOnly - stop parsing after package clause\n      Names:\n        ast.Ident:\n          Name: PackageClauseOnly\n      Type:\n        ast.Ident:\n          Name: Mode\n      Values:\n        ast.BinaryExpr:\n          X:\n            ast.BasicLit:\n              Kind: INT\n              Value: 1\n          Op: <<\n          Y:\n            ast.Ident:\n              Name: iota\n    ast.ValueSpec:\n      Doc:\n        ast.CommentGroup:\n          List:\n            ast.Comment:\n              Text: // ImportsOnly - stop parsing after import declarations\n      Names:\n        ast.Ident:\n          Name: ImportsOnly\n    ast.ValueSpec:\n      Doc:\n        ast.CommentGroup:\n          List:\n            ast.Comment:\n              Text: // ParseComments - parse comments and add them to AST\n      Names:\n        ast.Ident:\n          Name: ParseComments\n    ast.ValueSpec:\n      Doc:\n        ast.CommentGroup:\n          List:\n            ast.Comment:\n              Text: // Trace - print a trace of parsed productions\n      Names:\n        ast.Ident:\n          Name: Trace\n    ast.ValueSpec:\n      Doc:\n        ast.CommentGroup:\n          List:\n            ast.Comment:\n              Text: // DeclarationErrors - report declaration errors\n      Names:\n        ast.Ident:\n          Name: DeclarationErrors\n    ast.ValueSpec:\n      Doc:\n        ast.CommentGroup:\n          List:\n            ast.Comment:\n              Text: // AllErrors - report all errors (not just the first 10 on different lines)\n      Names:\n        ast.Ident:\n          Name: AllErrors\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // FileSystem represents a file system.\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: FileSystem\n      Type:\n        ast.InterfaceType:\n          Methods:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: ReadDir\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: dirname\n                              Type:\n                                ast.Ident:\n                                  Name: string\n                      Results:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.ArrayType:\n                                  Elt:\n                                    ast.SelectorExpr:\n                                      X:\n                                        ast.Ident:\n                                          Name: os\n                                      Sel:\n                                        ast.Ident:\n                                          Name: FileInfo\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: error\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: ReadFile\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: filename\n                              Type:\n                                ast.Ident:\n                                  Name: string\n                      Results:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.ArrayType:\n                                  Elt:\n                                    ast.Ident:\n                                      Name: byte\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: error\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: Join\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: elem\n                              Type:\n                                ast.Ellipsis:\n                                  Elt:\n                                    ast.Ident:\n                                      Name: string\n                      Results:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: string\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: IF\n      Type:\n        ast.Ident:\n          Name: FileSystem\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Foo\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: a\n                    ast.Ident:\n                      Name: b\n                  Type:\n                    ast.MapType:\n                      Key:\n                        ast.Ident:\n                          Name: string\n                      Value:\n                        ast.StructType:\n                          Fields:\n                            ast.FieldList:\nast.FuncDecl:\n  Recv:\n    ast.FieldList:\n      List:\n        ast.Field:\n          Names:\n            ast.Ident:\n              Name: p\n          Type:\n            ast.StarExpr:\n              X:\n                ast.Ident:\n                  Name: Foo\n  Name:\n    ast.Ident:\n      Name: bar\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: init\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: f\n            ast.Ident:\n              Name: err\n          Tok: :=\n          Rhs:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: os\n                  Sel:\n                    ast.Ident:\n                      Name: Open\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a\"\n        ast.IfStmt:\n          Cond:\n            ast.BinaryExpr:\n              X:\n                ast.Ident:\n                  Name: err\n              Op: !=\n              Y:\n                ast.Ident:\n                  Name: nil\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ReturnStmt:\n        ast.DeferStmt:\n          Call:\n            ast.CallExpr:\n              Fun:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: f\n                  Sel:\n                    ast.Ident:\n                      Name: Close\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: ch\n          Tok: :=\n          Rhs:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: make\n              Args:\n                ast.ChanType:\n                  Value:\n                    ast.Ident:\n                      Name: bool\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 100\n        ast.SelectStmt:\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.CommClause:\n                  Comm:\n                    ast.ExprStmt:\n                      X:\n                        ast.UnaryExpr:\n                          Op: <-\n                          X:\n                            ast.Ident:\n                              Name: ch\n                  Body:\n                    ast.ExprStmt:\n                      X:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: println\n                          Args:\n                            ast.BasicLit:\n                              Kind: STRING\n                              Value: \"1\"\n                ast.CommClause:\n                  Comm:\n                    ast.SendStmt:\n                      Chan:\n                        ast.Ident:\n                          Name: ch\n                      Values:\n                        ast.Ident:\n                          Name: true\n                  Body:\n                    ast.ExprStmt:\n                      X:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: println\n                          Args:\n                            ast.BasicLit:\n                              Kind: STRING\n                              Value: \"2\"\n        ast.GoStmt:\n          Call:\n            ast.CallExpr:\n              Fun:\n                ast.FuncLit:\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: fs\n                              Type:\n                                ast.Ident:\n                                  Name: FileSystem\n                  Body:\n                    ast.BlockStmt:\n                      List:\n                        ast.IfStmt:\n                          Init:\n                            ast.AssignStmt:\n                              Lhs:\n                                ast.Ident:\n                                  Name: foo\n                                ast.Ident:\n                                  Name: ok\n                              Tok: :=\n                              Rhs:\n                                ast.TypeAssertExpr:\n                                  X:\n                                    ast.Ident:\n                                      Name: fs\n                                  Type:\n                                    ast.StarExpr:\n                                      X:\n                                        ast.Ident:\n                                          Name: Foo\n                          Cond:\n                            ast.Ident:\n                              Name: ok\n                          Body:\n                            ast.BlockStmt:\n                              List:\n                                ast.ExprStmt:\n                                  X:\n                                    ast.CallExpr:\n                                      Fun:\n                                        ast.Ident:\n                                          Name: println\n                                      Args:\n                                        ast.Ident:\n                                          Name: foo\n              Args:\n                ast.Ident:\n                  Name: nil\n"
  },
  {
    "path": "parser/_testdata/optparam/optparam.xgo",
    "content": "// Basic optional parameters\nfunc single(a int?)\n\nfunc multiple(a int, b string?, c bool?)\n\nfunc mixed(name string, age int?, active bool?, data []byte)\n\n// Pointer types with optional\nfunc pointer(a *int?, b **string?)\n\n// Complex types with optional\nfunc complex(m map[string]int?, s []int?, ch chan int?)\n\n// Array types with optional\nfunc arrays(a [10]int?, b [5]string?)\n\n// Interface types with optional\nfunc interfaces(io.Reader?, io.Writer?)\n\n// Struct types with optional\nfunc structs(p struct{ X int }?, q struct{ Y string }?)\n\n// Function types with optional\nfunc funcs(f func(int) string?, g func(string) error?)\n\n// Qualified identifiers with optional\nfunc qualified(t time.Time?, d time.Duration?)\n\n// Unnamed (anonymous) parameters with optional\nfunc unnamed(int?, string?, bool)\n\n// All optional parameters\nfunc allOptional(a int?, b string?, c bool?)\n\n// Optional with variadic (variadic cannot be optional)\nfunc withVariadic(a int?, b ...string)\n\n// Type declarations with optional\ntype Handler func(req *Request?, resp *Response?) error\n\ntype Callback func(int?, string?) bool\n\n// Method with optional parameters\ntype Server struct{}\n\nfunc (s *Server) Handle(req *Request?, opts *Options?) error {\n\treturn nil\n}\n"
  },
  {
    "path": "parser/_testdata/optparam/parser.expect",
    "content": "package main\n\nfile optparam.xgo\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Basic optional parameters\n  Name:\n    ast.Ident:\n      Name: single\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: a\n              Type:\n                ast.Ident:\n                  Name: int\n              Optional: true\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: multiple\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: a\n              Type:\n                ast.Ident:\n                  Name: int\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: b\n              Type:\n                ast.Ident:\n                  Name: string\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: c\n              Type:\n                ast.Ident:\n                  Name: bool\n              Optional: true\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: mixed\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: name\n              Type:\n                ast.Ident:\n                  Name: string\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: age\n              Type:\n                ast.Ident:\n                  Name: int\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: active\n              Type:\n                ast.Ident:\n                  Name: bool\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: data\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: byte\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Pointer types with optional\n  Name:\n    ast.Ident:\n      Name: pointer\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: a\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.Ident:\n                      Name: int\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: b\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.StarExpr:\n                      X:\n                        ast.Ident:\n                          Name: string\n              Optional: true\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Complex types with optional\n  Name:\n    ast.Ident:\n      Name: complex\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: m\n              Type:\n                ast.MapType:\n                  Key:\n                    ast.Ident:\n                      Name: string\n                  Value:\n                    ast.Ident:\n                      Name: int\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: s\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: int\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: ch\n              Type:\n                ast.ChanType:\n                  Value:\n                    ast.Ident:\n                      Name: int\n              Optional: true\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Array types with optional\n  Name:\n    ast.Ident:\n      Name: arrays\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: a\n              Type:\n                ast.ArrayType:\n                  Len:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 10\n                  Elt:\n                    ast.Ident:\n                      Name: int\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: b\n              Type:\n                ast.ArrayType:\n                  Len:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 5\n                  Elt:\n                    ast.Ident:\n                      Name: string\n              Optional: true\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Interface types with optional\n  Name:\n    ast.Ident:\n      Name: interfaces\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: io\n                  Sel:\n                    ast.Ident:\n                      Name: Reader\n              Optional: true\n            ast.Field:\n              Type:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: io\n                  Sel:\n                    ast.Ident:\n                      Name: Writer\n              Optional: true\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Struct types with optional\n  Name:\n    ast.Ident:\n      Name: structs\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: p\n              Type:\n                ast.StructType:\n                  Fields:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Names:\n                            ast.Ident:\n                              Name: X\n                          Type:\n                            ast.Ident:\n                              Name: int\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: q\n              Type:\n                ast.StructType:\n                  Fields:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Names:\n                            ast.Ident:\n                              Name: Y\n                          Type:\n                            ast.Ident:\n                              Name: string\n              Optional: true\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Function types with optional\n  Name:\n    ast.Ident:\n      Name: funcs\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: f\n              Type:\n                ast.FuncType:\n                  Params:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: int\n                  Results:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: string\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: g\n              Type:\n                ast.FuncType:\n                  Params:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: string\n                  Results:\n                    ast.FieldList:\n                      List:\n                        ast.Field:\n                          Type:\n                            ast.Ident:\n                              Name: error\n              Optional: true\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Qualified identifiers with optional\n  Name:\n    ast.Ident:\n      Name: qualified\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: t\n              Type:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: time\n                  Sel:\n                    ast.Ident:\n                      Name: Time\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: d\n              Type:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: time\n                  Sel:\n                    ast.Ident:\n                      Name: Duration\n              Optional: true\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Unnamed (anonymous) parameters with optional\n  Name:\n    ast.Ident:\n      Name: unnamed\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: int\n              Optional: true\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: string\n              Optional: true\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: bool\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // All optional parameters\n  Name:\n    ast.Ident:\n      Name: allOptional\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: a\n              Type:\n                ast.Ident:\n                  Name: int\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: b\n              Type:\n                ast.Ident:\n                  Name: string\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: c\n              Type:\n                ast.Ident:\n                  Name: bool\n              Optional: true\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Optional with variadic (variadic cannot be optional)\n  Name:\n    ast.Ident:\n      Name: withVariadic\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: a\n              Type:\n                ast.Ident:\n                  Name: int\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: b\n              Type:\n                ast.Ellipsis:\n                  Elt:\n                    ast.Ident:\n                      Name: string\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Type declarations with optional\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Handler\n      Type:\n        ast.FuncType:\n          Params:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: req\n                  Type:\n                    ast.StarExpr:\n                      X:\n                        ast.Ident:\n                          Name: Request\n                  Optional: true\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: resp\n                  Type:\n                    ast.StarExpr:\n                      X:\n                        ast.Ident:\n                          Name: Response\n                  Optional: true\n          Results:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: error\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Callback\n      Type:\n        ast.FuncType:\n          Params:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: int\n                  Optional: true\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: string\n                  Optional: true\n          Results:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: bool\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Method with optional parameters\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Server\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\nast.FuncDecl:\n  Recv:\n    ast.FieldList:\n      List:\n        ast.Field:\n          Names:\n            ast.Ident:\n              Name: s\n          Type:\n            ast.StarExpr:\n              X:\n                ast.Ident:\n                  Name: Server\n  Name:\n    ast.Ident:\n      Name: Handle\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: req\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.Ident:\n                      Name: Request\n              Optional: true\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: opts\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.Ident:\n                      Name: Options\n              Optional: true\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: error\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ReturnStmt:\n          Results:\n            ast.Ident:\n              Name: nil\n"
  },
  {
    "path": "parser/_testdata/overload1/overload.xgo",
    "content": "func foo = (\n\tfunc(a, b float64) float64 {\n\t\treturn a + b\n\t}\n\tfunc(a, b string) string {\n\t\treturn a + b\n\t}\n)\n\nfunc bar = (\n\taddComplex\n\t(T).add\n)\n"
  },
  {
    "path": "parser/_testdata/overload1/parser.expect",
    "content": "package main\n\nfile overload.xgo\nast.OverloadFuncDecl:\n  Name:\n    ast.Ident:\n      Name: foo\n  Funcs:\n    ast.FuncLit:\n      Type:\n        ast.FuncType:\n          Params:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: a\n                    ast.Ident:\n                      Name: b\n                  Type:\n                    ast.Ident:\n                      Name: float64\n          Results:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: float64\n      Body:\n        ast.BlockStmt:\n          List:\n            ast.ReturnStmt:\n              Results:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Op: +\n                  Y:\n                    ast.Ident:\n                      Name: b\n    ast.FuncLit:\n      Type:\n        ast.FuncType:\n          Params:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: a\n                    ast.Ident:\n                      Name: b\n                  Type:\n                    ast.Ident:\n                      Name: string\n          Results:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: string\n      Body:\n        ast.BlockStmt:\n          List:\n            ast.ReturnStmt:\n              Results:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Op: +\n                  Y:\n                    ast.Ident:\n                      Name: b\nast.OverloadFuncDecl:\n  Name:\n    ast.Ident:\n      Name: bar\n  Funcs:\n    ast.Ident:\n      Name: addComplex\n    ast.SelectorExpr:\n      X:\n        ast.ParenExpr:\n          X:\n            ast.Ident:\n              Name: T\n      Sel:\n        ast.Ident:\n          Name: add\n"
  },
  {
    "path": "parser/_testdata/overload2/overload2.xgo",
    "content": "func (T).* = (\n\tmul1\n\tmul2\n)\n\nfunc (T).add = (\n\tadd1\n\tfunc(a, b T) T {\n\t\treturn a + b\n\t}\n)\n"
  },
  {
    "path": "parser/_testdata/overload2/parser.expect",
    "content": "package main\n\nfile overload2.xgo\nast.OverloadFuncDecl:\n  Recv:\n    ast.FieldList:\n      List:\n        ast.Field:\n          Type:\n            ast.Ident:\n              Name: T\n  Name:\n    ast.Ident:\n      Name: *\n  Funcs:\n    ast.Ident:\n      Name: mul1\n    ast.Ident:\n      Name: mul2\nast.OverloadFuncDecl:\n  Recv:\n    ast.FieldList:\n      List:\n        ast.Field:\n          Type:\n            ast.Ident:\n              Name: T\n  Name:\n    ast.Ident:\n      Name: add\n  Funcs:\n    ast.Ident:\n      Name: add1\n    ast.FuncLit:\n      Type:\n        ast.FuncType:\n          Params:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: a\n                    ast.Ident:\n                      Name: b\n                  Type:\n                    ast.Ident:\n                      Name: T\n          Results:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: T\n      Body:\n        ast.BlockStmt:\n          List:\n            ast.ReturnStmt:\n              Results:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Op: +\n                  Y:\n                    ast.Ident:\n                      Name: b\n"
  },
  {
    "path": "parser/_testdata/overloadop/op_overload.xgo",
    "content": "type foo struct {\n}\n\nfunc (a *foo) * (b *foo) *foo\n\nfunc (a *foo) + (b *foo) *foo {\n\tprintln(\"a + b\")\n\treturn &foo{}\n}\n\nfunc (a foo) / (b foo) foo {\n\tprintln(\"a / b\")\n\treturn foo{}\n}\n\nfunc -(a foo) {\n\tprintln(\"-a\")\n}\n\nfunc ++(a foo) {\n\tprintln(\"a++\")\n}\n"
  },
  {
    "path": "parser/_testdata/overloadop/parser.expect",
    "content": "package main\n\nfile op_overload.xgo\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: foo\n      Type:\n        ast.StructType:\n          Fields:\n            ast.FieldList:\nast.FuncDecl:\n  Recv:\n    ast.FieldList:\n      List:\n        ast.Field:\n          Names:\n            ast.Ident:\n              Name: a\n          Type:\n            ast.StarExpr:\n              X:\n                ast.Ident:\n                  Name: foo\n  Name:\n    ast.Ident:\n      Name: *\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: b\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.Ident:\n                      Name: foo\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.Ident:\n                      Name: foo\nast.FuncDecl:\n  Recv:\n    ast.FieldList:\n      List:\n        ast.Field:\n          Names:\n            ast.Ident:\n              Name: a\n          Type:\n            ast.StarExpr:\n              X:\n                ast.Ident:\n                  Name: foo\n  Name:\n    ast.Ident:\n      Name: +\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: b\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.Ident:\n                      Name: foo\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.Ident:\n                      Name: foo\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a + b\"\n        ast.ReturnStmt:\n          Results:\n            ast.UnaryExpr:\n              Op: &\n              X:\n                ast.CompositeLit:\n                  Type:\n                    ast.Ident:\n                      Name: foo\nast.FuncDecl:\n  Recv:\n    ast.FieldList:\n      List:\n        ast.Field:\n          Names:\n            ast.Ident:\n              Name: a\n          Type:\n            ast.Ident:\n              Name: foo\n  Name:\n    ast.Ident:\n      Name: /\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: b\n              Type:\n                ast.Ident:\n                  Name: foo\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: foo\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a / b\"\n        ast.ReturnStmt:\n          Results:\n            ast.CompositeLit:\n              Type:\n                ast.Ident:\n                  Name: foo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: -\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: a\n              Type:\n                ast.Ident:\n                  Name: foo\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"-a\"\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: ++\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: a\n              Type:\n                ast.Ident:\n                  Name: foo\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a++\"\n"
  },
  {
    "path": "parser/_testdata/printvariadic/parser.expect",
    "content": "package main\n\nfile printv.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: x\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: y\n"
  },
  {
    "path": "parser/_testdata/printvariadic/printv.xgo",
    "content": "println x...\nprintln y...\n"
  },
  {
    "path": "parser/_testdata/pystr/parser.expect",
    "content": "package main\n\nfile pystr.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: print\n              Args:\n                ast.BasicLit:\n                  Kind: PYSTRING\n                  Value: \"Hello\"\n"
  },
  {
    "path": "parser/_testdata/pystr/pystr.xgo",
    "content": "print py\"Hello\"\n"
  },
  {
    "path": "parser/_testdata/rangeexpr1/parser.expect",
    "content": "package main\n\nfile rangeexpr.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: i\n          Tok: :=\n          X:\n            ast.RangeExpr:\n              Last:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 10\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: i\n        ast.ForPhraseStmt:\n          ForPhrase:\n            ast.ForPhrase:\n              Value:\n                ast.Ident:\n                  Name: i\n              X:\n                ast.RangeExpr:\n                  First:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                  Last:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 10\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: i\n        ast.RangeStmt:\n          Key:\n            ast.Ident:\n              Name: i\n          Tok: :=\n          X:\n            ast.RangeExpr:\n              Last:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 10\n              Expr3:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 2\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.Ident:\n                          Name: i\n        ast.RangeStmt:\n          Tok: ILLEGAL\n          X:\n            ast.RangeExpr:\n              Last:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 10\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.ExprStmt:\n                  X:\n                    ast.CallExpr:\n                      Fun:\n                        ast.Ident:\n                          Name: println\n                      Args:\n                        ast.BasicLit:\n                          Kind: STRING\n                          Value: \"Range expression\"\n"
  },
  {
    "path": "parser/_testdata/rangeexpr1/rangeexpr.xgo",
    "content": "package main\n\nfunc main() {\n\tfor i := range :10 {\n\t\tprintln(i)\n\t}\n\n\tfor i in 1:10 {\n\t\tprintln(i)\n\t}\n\n\tfor i := range :10:2 {\n\t\tprintln(i)\n\t}\n\n\tfor range :10 {\n\t\tprintln(\"Range expression\")\n\t}\n}\n"
  },
  {
    "path": "parser/_testdata/rangeexpr2/parser.expect",
    "content": "package main\n\nfile rangeexpr.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.RangeStmt:\n          Tok: ILLEGAL\n          X:\n            ast.RangeExpr:\n              Last:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 10\n          Body:\n            ast.BlockStmt:\n"
  },
  {
    "path": "parser/_testdata/rangeexpr2/rangeexpr.xgo",
    "content": "for :10 {\n}\n"
  },
  {
    "path": "parser/_testdata/rangeexpr3/parser.expect",
    "content": "package main\n\nfile rangeexpr.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.RangeStmt:\n          Tok: ILLEGAL\n          X:\n            ast.RangeExpr:\n              First:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n              Last:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 10\n          Body:\n            ast.BlockStmt:\n"
  },
  {
    "path": "parser/_testdata/rangeexpr3/rangeexpr.xgo",
    "content": "for 1:10 {\n}\n"
  },
  {
    "path": "parser/_testdata/rational/parser.expect",
    "content": "package main\n\nfile rational.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.BinaryExpr:\n              X:\n                ast.BasicLit:\n                  Kind: RAT\n                  Value: 1r\n              Op: <<\n              Y:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 65\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: b\n          Tok: :=\n          Rhs:\n            ast.BinaryExpr:\n              X:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 4\n              Op: /\n              Y:\n                ast.BasicLit:\n                  Kind: RAT\n                  Value: 5r\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: c\n          Tok: :=\n          Rhs:\n            ast.BinaryExpr:\n              X:\n                ast.BinaryExpr:\n                  X:\n                    ast.Ident:\n                      Name: b\n                  Op: -\n                  Y:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                      Op: /\n                      Y:\n                        ast.BasicLit:\n                          Kind: RAT\n                          Value: 3r\n              Op: +\n              Y:\n                ast.BinaryExpr:\n                  X:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n                      Op: *\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                  Op: /\n                  Y:\n                    ast.BasicLit:\n                      Kind: RAT\n                      Value: 2r\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: a\n                ast.Ident:\n                  Name: b\n                ast.Ident:\n                  Name: c\n"
  },
  {
    "path": "parser/_testdata/rational/rational.xgo",
    "content": "a := 1r << 65 // bigint, large than int64\nb := 4/5r     // bigrat\nc := b - 1/3r + 3*1/2r\nprintln(a, b, c)\n"
  },
  {
    "path": "parser/_testdata/selectdata/parser.expect",
    "content": "package main\n\nfile select.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 8\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 19\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: y\n          Tok: :=\n          Rhs:\n            ast.ComprehensionExpr:\n              Tok: {\n              Elt:\n                ast.Ident:\n                  Name: x\n              Fors:\n                ast.ForPhrase:\n                  Value:\n                    ast.Ident:\n                      Name: x\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Cond:\n                    ast.BinaryExpr:\n                      X:\n                        ast.BinaryExpr:\n                          X:\n                            ast.Ident:\n                              Name: x\n                          Op: %\n                          Y:\n                            ast.BasicLit:\n                              Kind: INT\n                              Value: 2\n                      Op: ==\n                      Y:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 0\n"
  },
  {
    "path": "parser/_testdata/selectdata/select.xgo",
    "content": "a := [1, 3, 5, 7, 8, 19]\ny := {x for x in a if x%2 == 0}\n"
  },
  {
    "path": "parser/_testdata/slice1/parser.expect",
    "content": "package main\n\nfile slice.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.IndexExpr:\n                  X:\n                    ast.SliceLit:\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                  Index:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.SliceExpr:\n                  X:\n                    ast.SliceLit:\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                  Low:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\n                  High:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.SliceExpr:\n                  X:\n                    ast.SliceLit:\n                      Elts:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 1\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 2\n                  Low:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\n                  High:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                  Max:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 5\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.IndexExpr:\n                  X:\n                    ast.Ident:\n                      Name: a\n                  Index:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 0\n"
  },
  {
    "path": "parser/_testdata/slice1/slice.xgo",
    "content": "println([1][0])\nprintln([1][0:1])\nprintln([1, 2][0:1:5])\n\na := [1]\nprintln(a[0])\n"
  },
  {
    "path": "parser/_testdata/slice2/parser.expect",
    "content": "package main\n\nfile slice2.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.SliceLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 2\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.Ident:\n                  Name: a\n"
  },
  {
    "path": "parser/_testdata/slice2/slice2.xgo",
    "content": "package main\n\nfunc main() {\n\ta := [\n\t\t1,\n\t\t2,\n\t\t3,\n\t]\n\tprintln(a)\n}\n"
  },
  {
    "path": "parser/_testdata/spxtest/foo.spx",
    "content": "a := 1\n"
  },
  {
    "path": "parser/_testdata/spxtest/parser.expect",
    "content": "package main\n\nfile foo.spx\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 1\n"
  },
  {
    "path": "parser/_testdata/staticmthd1/parser.expect",
    "content": "package main\n\nfile static_method.xgo\nast.FuncDecl:\n  Recv:\n    ast.FieldList:\n      List:\n        ast.Field:\n          Type:\n            ast.Ident:\n              Name: T\n  Name:\n    ast.Ident:\n      Name: foo\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: a\n                ast.Ident:\n                  Name: b\n              Type:\n                ast.Ident:\n                  Name: int\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.Ident:\n                  Name: string\n  Body:\n    ast.BlockStmt:\n"
  },
  {
    "path": "parser/_testdata/staticmthd1/static_method.xgo",
    "content": "func T.foo(a, b int) string {\n}\n"
  },
  {
    "path": "parser/_testdata/staticmthd2/a.gox",
    "content": "func .New() *T {\n}\n"
  },
  {
    "path": "parser/_testdata/staticmthd2/parser.expect",
    "content": "package main\n\nfile a.gox\nast.FuncDecl:\n  Recv:\n    ast.FieldList:\n  Name:\n    ast.Ident:\n      Name: New\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n      Results:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Type:\n                ast.StarExpr:\n                  X:\n                    ast.Ident:\n                      Name: T\n  Body:\n    ast.BlockStmt:\n"
  },
  {
    "path": "parser/_testdata/stdtype/parser.expect",
    "content": "package bar\n\nfile stdtype.xgo\nnoEntrypoint\nast.GenDecl:\n  Tok: import\n  Specs:\n    ast.ImportSpec:\n      Path:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"io\"\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: x\n          Tok: :=\n          Rhs:\n            ast.CompositeLit:\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: float64\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: FLOAT\n                  Value: 3.4\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: y\n          Tok: :=\n          Rhs:\n            ast.CompositeLit:\n              Type:\n                ast.MapType:\n                  Key:\n                    ast.Ident:\n                      Name: string\n                  Value:\n                    ast.Ident:\n                      Name: float64\n              Elts:\n                ast.KeyValueExpr:\n                  Key:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"Hello\"\n                  Value:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                ast.KeyValueExpr:\n                  Key:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"xsw\"\n                  Value:\n                    ast.BasicLit:\n                      Kind: FLOAT\n                      Value: 3.4\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"x:\"\n                ast.Ident:\n                  Name: x\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"y:\"\n                ast.Ident:\n                  Name: y\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: a\n          Tok: :=\n          Rhs:\n            ast.CompositeLit:\n              Type:\n                ast.ArrayType:\n                  Len:\n                    ast.Ellipsis:\n                  Elt:\n                    ast.Ident:\n                      Name: float64\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.BasicLit:\n                  Kind: FLOAT\n                  Value: 3.4\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: b\n          Tok: :=\n          Rhs:\n            ast.CompositeLit:\n              Type:\n                ast.ArrayType:\n                  Len:\n                    ast.Ellipsis:\n                  Elt:\n                    ast.Ident:\n                      Name: float64\n              Elts:\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 1\n                ast.KeyValueExpr:\n                  Key:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 3\n                  Value:\n                    ast.BasicLit:\n                      Kind: FLOAT\n                      Value: 3.4\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 5\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: c\n          Tok: :=\n          Rhs:\n            ast.CompositeLit:\n              Type:\n                ast.ArrayType:\n                  Elt:\n                    ast.Ident:\n                      Name: float64\n              Elts:\n                ast.KeyValueExpr:\n                  Key:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 2\n                  Value:\n                    ast.BasicLit:\n                      Kind: FLOAT\n                      Value: 1.2\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 3\n                ast.KeyValueExpr:\n                  Key:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 6\n                  Value:\n                    ast.BasicLit:\n                      Kind: FLOAT\n                      Value: 4.5\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a:\"\n                ast.Ident:\n                  Name: a\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"b:\"\n                ast.Ident:\n                  Name: b\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"c:\"\n                ast.Ident:\n                  Name: c\n"
  },
  {
    "path": "parser/_testdata/stdtype/stdtype.xgo",
    "content": "package bar\n\nimport \"io\"\n\nx := []float64{1, 3.4, 5}\ny := map[string]float64{\"Hello\": 1, \"xsw\": 3.4}\nprintln(\"x:\", x, \"y:\", y)\n\na := [...]float64{1, 3.4, 5}\nb := [...]float64{1, 3: 3.4, 5}\nc := []float64{2: 1.2, 3, 6: 4.5}\nprintln(\"a:\", a, \"b:\", b, \"c:\", c)\n"
  },
  {
    "path": "parser/_testdata/stringex1/parser.expect",
    "content": "package main\n\nfile string_lit.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"$\"\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"$$\"\n                    Extra:\n                      $$\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a$$b$\"\n                    Extra:\n                      a$$\n                      b$\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a$$b$$\"\n                    Extra:\n                      a$$\n                      b$$\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a$b$%\"\n"
  },
  {
    "path": "parser/_testdata/stringex1/string_lit.xgo",
    "content": "println \"$\"\nprintln \"$$\"\nprintln \"a$$b$\"\nprintln \"a$$b$$\"\nprintln \"a$b$%\"\n"
  },
  {
    "path": "parser/_testdata/stringex2/parser.expect",
    "content": "package main\n\nfile string_lit.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"${\"\n                    Extra:\n                      ${\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"${ b }\"\n                    Extra:\n                      ast.Ident:\n                        Name: b\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a${\"\n                    Extra:\n                      a${\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"a${b}c\"\n                    Extra:\n                      a\n                      ast.Ident:\n                        Name: b\n                      c\n"
  },
  {
    "path": "parser/_testdata/stringex2/string_lit.xgo",
    "content": "println \"${\"\nprintln \"${ b }\"\nprintln \"a${\"\nprintln \"a${b}c\"\n"
  },
  {
    "path": "parser/_testdata/stringex3/parser.expect",
    "content": "package main\n\nfile string_lit.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"file:${args[0]}?${query}\"\n                    Extra:\n                      file:\n                      ast.IndexExpr:\n                        X:\n                          ast.Ident:\n                            Name: args\n                        Index:\n                          ast.BasicLit:\n                            Kind: INT\n                            Value: 0\n                      ?\n                      ast.Ident:\n                        Name: query\n"
  },
  {
    "path": "parser/_testdata/stringex3/string_lit.xgo",
    "content": "println \"file:${args[0]}?${query}\"\n"
  },
  {
    "path": "parser/_testdata/tuplelit/parser.expect",
    "content": "package main\n\nfile tuplelit.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: ken\n          Tok: :=\n          Rhs:\n            ast.TupleLit:\n              Elts:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"Ken\"\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"ken@abc.com\"\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 7\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.SelectorExpr:\n                  X:\n                    ast.Ident:\n                      Name: ken\n                  Sel:\n                    ast.Ident:\n                      Name: 0\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: dump\n              Args:\n                ast.TupleLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                    ast.BasicLit:\n                      Kind: FLOAT\n                      Value: 3.14\n                ast.Ident:\n                  Name: true\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: dump\n              Args:\n                ast.TupleLit:\n                  Elts:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                    ast.BasicLit:\n                      Kind: FLOAT\n                      Value: 3.14\n"
  },
  {
    "path": "parser/_testdata/tuplelit/tuplelit.xgo",
    "content": "ken := (\"Ken\", \"ken@abc.com\", 7)\necho ken.0\n\ndump (1, 3.14), true\ndump (1, 3.14)\n"
  },
  {
    "path": "parser/_testdata/tupletype/parser.expect",
    "content": "package main\n\nfile tupletype.xgo\nast.GenDecl:\n  Tok: import\n  Specs:\n    ast.ImportSpec:\n      Path:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"io\"\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Empty tuple\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Empty\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Anonymous tuple types\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Pair\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: int\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: string\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Triple\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: int\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: string\n                ast.Field:\n                  Type:\n                    ast.Ident:\n                      Name: bool\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Named tuple types\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Point\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: x\n                  Type:\n                    ast.Ident:\n                      Name: int\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: y\n                  Type:\n                    ast.Ident:\n                      Name: int\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Person\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: name\n                  Type:\n                    ast.Ident:\n                      Name: string\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: age\n                  Type:\n                    ast.Ident:\n                      Name: int\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Type shorthand syntax\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Point3D\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: x\n                    ast.Ident:\n                      Name: y\n                    ast.Ident:\n                      Name: z\n                  Type:\n                    ast.Ident:\n                      Name: int\nast.GenDecl:\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: Mixed\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: a\n                    ast.Ident:\n                      Name: b\n                  Type:\n                    ast.Ident:\n                      Name: string\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: c\n                  Type:\n                    ast.Ident:\n                      Name: int\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple as channel element type\n  Tok: var\n  Specs:\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: ch\n      Type:\n        ast.ChanType:\n          Value:\n            ast.TupleType:\n              Fields:\n                ast.FieldList:\n                  List:\n                    ast.Field:\n                      Type:\n                        ast.Ident:\n                          Name: int\n                    ast.Field:\n                      Type:\n                        ast.Ident:\n                          Name: error\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple as map value type\n  Tok: var\n  Specs:\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: cache\n      Type:\n        ast.MapType:\n          Key:\n            ast.Ident:\n              Name: string\n          Value:\n            ast.TupleType:\n              Fields:\n                ast.FieldList:\n                  List:\n                    ast.Field:\n                      Type:\n                        ast.Ident:\n                          Name: int\n                    ast.Field:\n                      Type:\n                        ast.Ident:\n                          Name: bool\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple as slice element type\n  Tok: var\n  Specs:\n    ast.ValueSpec:\n      Names:\n        ast.Ident:\n          Name: pairs\n      Type:\n        ast.ArrayType:\n          Elt:\n            ast.TupleType:\n              Fields:\n                ast.FieldList:\n                  List:\n                    ast.Field:\n                      Type:\n                        ast.Ident:\n                          Name: string\n                    ast.Field:\n                      Type:\n                        ast.Ident:\n                          Name: int\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with array types (covers token.LBRACK case)\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: WithArray\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: arr\n                  Type:\n                    ast.ArrayType:\n                      Elt:\n                        ast.Ident:\n                          Name: int\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: data\n                  Type:\n                    ast.ArrayType:\n                      Len:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 5\n                      Elt:\n                        ast.Ident:\n                          Name: string\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with pointer types (covers token.MUL case)\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: WithPointers\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.StarExpr:\n                      X:\n                        ast.Ident:\n                          Name: int\n                ast.Field:\n                  Type:\n                    ast.StarExpr:\n                      X:\n                        ast.Ident:\n                          Name: string\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with function types (covers token.FUNC case)\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: WithFunc\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: fn\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                      Results:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Type:\n                                ast.Ident:\n                                  Name: string\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: callback\n                  Type:\n                    ast.FuncType:\n                      Params:\n                        ast.FieldList:\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with channel types (covers token.CHAN case)\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: WithChan\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: ch\n                  Type:\n                    ast.ChanType:\n                      Value:\n                        ast.Ident:\n                          Name: int\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: recv\n                  Type:\n                    ast.ChanType:\n                      Value:\n                        ast.Ident:\n                          Name: string\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with map types (covers token.MAP case)\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: WithMap\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: m\n                  Type:\n                    ast.MapType:\n                      Key:\n                        ast.Ident:\n                          Name: string\n                      Value:\n                        ast.Ident:\n                          Name: int\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: lookup\n                  Type:\n                    ast.MapType:\n                      Key:\n                        ast.Ident:\n                          Name: int\n                      Value:\n                        ast.Ident:\n                          Name: bool\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with struct types (covers token.STRUCT case)\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: WithStruct\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: s\n                  Type:\n                    ast.StructType:\n                      Fields:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: x\n                              Type:\n                                ast.Ident:\n                                  Name: int\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: data\n                  Type:\n                    ast.StructType:\n                      Fields:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: name\n                              Type:\n                                ast.Ident:\n                                  Name: string\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with interface types (covers token.INTERFACE case)\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: WithInterface\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: i\n                  Type:\n                    ast.InterfaceType:\n                      Methods:\n                        ast.FieldList:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: reader\n                  Type:\n                    ast.InterfaceType:\n                      Methods:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: Read\n                              Type:\n                                ast.FuncType:\n                                  Params:\n                                    ast.FieldList:\n                                      List:\n                                        ast.Field:\n                                          Type:\n                                            ast.ArrayType:\n                                              Elt:\n                                                ast.Ident:\n                                                  Name: byte\n                                  Results:\n                                    ast.FieldList:\n                                      List:\n                                        ast.Field:\n                                          Type:\n                                            ast.Ident:\n                                              Name: int\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with qualified type names (covers token.PERIOD case)\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: WithQualified\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Type:\n                    ast.SelectorExpr:\n                      X:\n                        ast.Ident:\n                          Name: io\n                      Sel:\n                        ast.Ident:\n                          Name: Reader\n                ast.Field:\n                  Type:\n                    ast.SelectorExpr:\n                      X:\n                        ast.Ident:\n                          Name: io\n                      Sel:\n                        ast.Ident:\n                          Name: Writer\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Tuple with mixed named and array types\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: MixedArray\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: items\n                  Type:\n                    ast.ArrayType:\n                      Elt:\n                        ast.Ident:\n                          Name: int\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: count\n                  Type:\n                    ast.Ident:\n                      Name: int\nast.GenDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // Single named field tuple\n  Tok: type\n  Specs:\n    ast.TypeSpec:\n      Name:\n        ast.Ident:\n          Name: SingleNamed\n      Type:\n        ast.TupleType:\n          Fields:\n            ast.FieldList:\n              List:\n                ast.Field:\n                  Names:\n                    ast.Ident:\n                      Name: value\n                  Type:\n                    ast.Ident:\n                      Name: int\n"
  },
  {
    "path": "parser/_testdata/tupletype/tupletype.xgo",
    "content": "// Test cases for tuple types\n\nimport \"io\"\n\n// Empty tuple\ntype Empty ()\n\n// Anonymous tuple types\ntype Pair (int, string)\n\ntype Triple (int, string, bool)\n\n// Named tuple types\ntype Point (x int, y int)\n\ntype Person (name string, age int)\n\n// Type shorthand syntax\ntype Point3D (x, y, z int)\n\ntype Mixed (a, b string, c int)\n\n// Tuple as channel element type\nvar ch chan (int, error)\n\n// Tuple as map value type\nvar cache map[string](int, bool)\n\n// Tuple as slice element type\nvar pairs [](string, int)\n\n// Tuple with array types (covers token.LBRACK case)\ntype WithArray (arr []int, data [5]string)\n\n// Tuple with pointer types (covers token.MUL case)\ntype WithPointers (*int, *string)\n\n// Tuple with function types (covers token.FUNC case)\ntype WithFunc (fn func(int) string, callback func())\n\n// Tuple with channel types (covers token.CHAN case)\ntype WithChan (ch chan int, recv <-chan string)\n\n// Tuple with map types (covers token.MAP case)\ntype WithMap (m map[string]int, lookup map[int]bool)\n\n// Tuple with struct types (covers token.STRUCT case)\ntype WithStruct (s struct{ x int }, data struct{ name string })\n\n// Tuple with interface types (covers token.INTERFACE case)\ntype WithInterface (i interface{}, reader interface{ Read([]byte) int })\n\n// Tuple with qualified type names (covers token.PERIOD case)\ntype WithQualified (io.Reader, io.Writer)\n\n// Tuple with mixed named and array types\ntype MixedArray (items []int, count int)\n\n// Single named field tuple\ntype SingleNamed (value int)\n"
  },
  {
    "path": "parser/_testdata/typeof/parser.expect",
    "content": "package main\n\nfile typeof.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: echo\n              Args:\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: type\n                  Args:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 1\n                ast.CallExpr:\n                  Fun:\n                    ast.Ident:\n                      Name: type\n                  Args:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"Hi\"\n"
  },
  {
    "path": "parser/_testdata/typeof/typeof.xgo",
    "content": "echo type(1), type(\"Hi\")\n"
  },
  {
    "path": "parser/_testdata/typeswitch/parser.expect",
    "content": "package main\n\nfile typeswitch.xgo\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: add\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n          List:\n            ast.Field:\n              Names:\n                ast.Ident:\n                  Name: v\n              Type:\n                ast.InterfaceType:\n                  Methods:\n                    ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.TypeSwitchStmt:\n          Assign:\n            ast.AssignStmt:\n              Lhs:\n                ast.Ident:\n                  Name: a\n              Tok: :=\n              Rhs:\n                ast.TypeAssertExpr:\n                  X:\n                    ast.Ident:\n                      Name: v\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.CaseClause:\n                  List:\n                    ast.Ident:\n                      Name: int\n                    ast.InterfaceType:\n                      Methods:\n                        ast.FieldList:\n                          List:\n                            ast.Field:\n                              Names:\n                                ast.Ident:\n                                  Name: Foo\n                              Type:\n                                ast.FuncType:\n                                  Params:\n                                    ast.FieldList:\n                ast.CaseClause:\n                  List:\n                    ast.StarExpr:\n                      X:\n                        ast.Ident:\n                          Name: string\n                ast.CaseClause:\n"
  },
  {
    "path": "parser/_testdata/typeswitch/typeswitch.xgo",
    "content": "func add(v interface{}) {\n\tswitch a := v.(type) {\n\tcase int, interface{ Foo() }:\n\tcase *string:\n\tdefault:\n\t}\n}\n"
  },
  {
    "path": "parser/_testdata/unit/parser.expect",
    "content": "package main\n\nfile step.xgo\nnoEntrypoint\nast.FuncDecl:\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: wait\n              Args:\n                ast.NumberUnitLit:\n                  Kind: INT\n                  Value: 1\n                  Unit: µs\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: wait\n              Args:\n                ast.NumberUnitLit:\n                  Kind: FLOAT\n                  Value: 0.5\n                  Unit: µs\n"
  },
  {
    "path": "parser/_testdata/unit/step.xgo",
    "content": "wait 1µs\nwait 0.5µs\n"
  },
  {
    "path": "parser/_testexpr/lambda/in.xgo",
    "content": "=> {\n    return this\n}\n"
  },
  {
    "path": "parser/_testexpr/lambda/out.expect",
    "content": "ast.LambdaExpr2:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.ReturnStmt:\n          Results:\n            ast.Ident:\n              Name: this\n"
  },
  {
    "path": "parser/fsx/fsys.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage fsx\n\nimport (\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\n// -----------------------------------------------------------------------------\n\n// FileSystem represents a file system.\ntype FileSystem interface {\n\tReadDir(dirname string) ([]fs.DirEntry, error)\n\tReadFile(filename string) ([]byte, error)\n\tJoin(elem ...string) string\n\n\t// Base returns the last element of path.\n\t// Trailing path separators are removed before extracting the last element.\n\t// If the path is empty, Base returns \".\".\n\t// If the path consists entirely of separators, Base returns a single separator.\n\tBase(filename string) string\n\n\t// Abs returns an absolute representation of path.\n\tAbs(path string) (string, error)\n}\n\n// -----------------------------------------------------------------------------\n\ntype localFS struct{}\n\nfunc (p localFS) ReadDir(dirname string) ([]fs.DirEntry, error) {\n\treturn os.ReadDir(dirname)\n}\n\nfunc (p localFS) ReadFile(filename string) ([]byte, error) {\n\treturn os.ReadFile(filename)\n}\n\nfunc (p localFS) Join(elem ...string) string {\n\treturn filepath.Join(elem...)\n}\n\n// Base returns the last element of path.\n// Trailing path separators are removed before extracting the last element.\n// If the path is empty, Base returns \".\".\n// If the path consists entirely of separators, Base returns a single separator.\nfunc (p localFS) Base(filename string) string {\n\treturn filepath.Base(filename)\n}\n\n// Abs returns an absolute representation of path.\nfunc (p localFS) Abs(path string) (string, error) {\n\treturn filepath.Abs(path)\n}\n\nvar Local FileSystem = localFS{}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "parser/fsx/memfs/fs.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage memfs\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\ntype FileFS struct {\n\tdata []byte\n\tinfo fs.DirEntry\n}\n\ntype dirEntry struct {\n\tfs.FileInfo\n}\n\nfunc (p *dirEntry) Type() fs.FileMode {\n\treturn p.FileInfo.Mode().Type()\n}\n\nfunc (p *dirEntry) Info() (fs.FileInfo, error) {\n\treturn p.FileInfo, nil\n}\n\nfunc File(filename string, src any) (f *FileFS, err error) {\n\tvar data []byte\n\tvar info fs.DirEntry\n\tif src != nil {\n\t\tdata, err = readSource(src)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tinfo = &memFileInfo{name: filename, size: len(data)}\n\t} else {\n\t\tfi, e := os.Stat(filename)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tdata, err = os.ReadFile(filename)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tinfo = &dirEntry{fi}\n\t}\n\treturn &FileFS{data: data, info: info}, nil\n}\n\nfunc (p *FileFS) ReadDir(dirname string) ([]fs.DirEntry, error) {\n\treturn []fs.DirEntry{p.info}, nil\n}\n\nfunc (p *FileFS) ReadFile(filename string) ([]byte, error) {\n\treturn p.data, nil\n}\n\nfunc (p *FileFS) Join(elem ...string) string {\n\treturn filepath.Join(elem...)\n}\n\nfunc (p *FileFS) Base(filename string) string {\n\treturn filepath.Base(filename)\n}\n\nfunc (p *FileFS) Abs(path string) (string, error) {\n\treturn path, nil\n}\n\nfunc readSource(src any) ([]byte, error) {\n\tswitch s := src.(type) {\n\tcase string:\n\t\treturn []byte(s), nil\n\tcase []byte:\n\t\treturn s, nil\n\tcase *bytes.Buffer:\n\t\t// is io.Reader, but src is already available in []byte form\n\t\tif s != nil {\n\t\t\treturn s.Bytes(), nil\n\t\t}\n\tcase io.Reader:\n\t\treturn io.ReadAll(s)\n\t}\n\treturn nil, os.ErrInvalid\n}\n"
  },
  {
    "path": "parser/fsx/memfs/memfs.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage memfs\n\nimport (\n\t\"io/fs\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"syscall\"\n\t\"time\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype memFileInfo struct {\n\tname string\n\tsize int\n}\n\nfunc (p *memFileInfo) Name() string {\n\treturn filepath.Base(p.name)\n}\n\nfunc (p *memFileInfo) Size() int64 {\n\treturn int64(p.size)\n}\n\nfunc (p *memFileInfo) Mode() fs.FileMode {\n\treturn 0\n}\n\nfunc (p *memFileInfo) Type() fs.FileMode {\n\treturn 0\n}\n\nfunc (p *memFileInfo) ModTime() (t time.Time) {\n\treturn time.Now()\n}\n\nfunc (p *memFileInfo) IsDir() bool {\n\treturn false\n}\n\nfunc (p *memFileInfo) Sys() any {\n\treturn nil\n}\n\nfunc (p *memFileInfo) Info() (fs.FileInfo, error) {\n\treturn p, nil\n}\n\n// -----------------------------------------------------------------------------\n\n// FS represents a file system in memory.\ntype FS struct {\n\tdirs  map[string][]string\n\tfiles map[string]string\n}\n\n// New creates a file system instance.\nfunc New(dirs map[string][]string, files map[string]string) *FS {\n\treturn &FS{dirs: dirs, files: files}\n}\n\n// ReadDir reads the directory named by dirname and returns\n// a list of directory entries sorted by filename.\nfunc (p *FS) ReadDir(dirname string) ([]fs.DirEntry, error) {\n\tif items, ok := p.dirs[dirname]; ok {\n\t\tfis := make([]fs.DirEntry, len(items))\n\t\tfor i, item := range items {\n\t\t\tfis[i] = &memFileInfo{name: item}\n\t\t}\n\t\treturn fis, nil\n\t}\n\treturn nil, syscall.ENOENT\n}\n\n// ReadFile reads the file named by filename and returns the contents.\n// A successful call returns err == nil, not err == EOF. Because ReadFile\n// reads the whole file, it does not treat an EOF from Read as an error\n// to be reported.\nfunc (p *FS) ReadFile(filename string) ([]byte, error) {\n\tif data, ok := p.files[filename]; ok {\n\t\treturn []byte(data), nil\n\t}\n\treturn nil, syscall.ENOENT\n}\n\n// Join joins any number of path elements into a single path,\n// separating them with slashes. Empty elements are ignored.\n// The result is Cleaned. However, if the argument list is\n// empty or all its elements are empty, Join returns\n// an empty string.\nfunc (p *FS) Join(elem ...string) string {\n\treturn path.Join(elem...)\n}\n\n// Base returns the last element of path.\n// Trailing slashes are removed before extracting the last element.\n// If the path is empty, Base returns \".\".\n// If the path consists entirely of slashes, Base returns \"/\".\nfunc (p *FS) Base(filename string) string {\n\treturn path.Base(filename)\n}\n\nfunc (p *FS) Abs(path string) (string, error) {\n\treturn path, nil\n}\n\n// -----------------------------------------------------------------------------\n\n// SingleFile creates a file system that only contains a single file.\nfunc SingleFile(dir string, fname string, data string) *FS {\n\treturn New(map[string][]string{\n\t\tdir: {fname},\n\t}, map[string]string{\n\t\tpath.Join(dir, fname): data,\n\t})\n}\n\n// TwoFiles creates a file system that contains two files.\nfunc TwoFiles(dir string, fname1, data1, fname2, data2 string) *FS {\n\treturn New(map[string][]string{\n\t\tdir: {fname1, fname2},\n\t}, map[string]string{\n\t\tpath.Join(dir, fname1): data1,\n\t\tpath.Join(dir, fname2): data2,\n\t})\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "parser/interface.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This file contains the exported entry points for invoking the parser.\n\npackage parser\n\nimport (\n\tgoparser \"go/parser\"\n\t\"go/scanner\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/qiniu/x/stream\"\n)\n\n// A Mode value is a set of flags (or 0).\n// They control the amount of source code parsed and other optional\n// parser functionality.\ntype Mode uint\n\nconst (\n\t// PackageClauseOnly - stop parsing after package clause\n\tPackageClauseOnly = Mode(goparser.PackageClauseOnly)\n\t// ImportsOnly - stop parsing after import declarations\n\tImportsOnly = Mode(goparser.ImportsOnly)\n\t// ParseComments - parse comments and add them to AST\n\tParseComments = Mode(goparser.ParseComments)\n\t// Trace - print a trace of parsed productions\n\tTrace = Mode(goparser.Trace)\n\t// DeclarationErrors - report declaration errors\n\tDeclarationErrors = Mode(goparser.DeclarationErrors)\n\t// AllErrors - report all errors (not just the first 10 on different lines)\n\tAllErrors = Mode(goparser.AllErrors)\n\n\t// ParseGoAsXGo - parse Go files by xgo/parser\n\tParseGoAsXGo Mode = 1 << 16\n\t// ParseXGoClass - parse XGo classfile by xgo/parser\n\tParseXGoClass Mode = 1 << 17\n\t// SaveAbsFile - parse and save absolute path to pkg.Files\n\tSaveAbsFile Mode = 1 << 18\n\n\t// Deprecated: use ParseGoAsXGo instead.\n\tParseGoAsGoPlus = ParseGoAsXGo\n\t// Deprecated: use ParseXGoClass instead.\n\tParseGoPlusClass = ParseXGoClass\n\n\tgoReservedFlags Mode = ((1 << 16) - 1)\n)\n\n// -----------------------------------------------------------------------------\n\n// ParseFile parses the source code of a single Go source file and returns\n// the corresponding ast.File node. The source code may be provided via\n// the filename of the source file, or via the src parameter.\n//\n// If src != nil, ParseFile parses the source from src and the filename is\n// only used when recording position information. The type of the argument\n// for the src parameter must be string, []byte, or io.Reader.\n// If src == nil, ParseFile parses the file specified by filename.\n//\n// The mode parameter controls the amount of source text parsed and other\n// optional parser functionality. Position information is recorded in the\n// file set fset, which must not be nil.\n//\n// If the source couldn't be read, the returned AST is nil and the error\n// indicates the specific failure. If the source was read but syntax\n// errors were found, the result is a partial AST (with ast.Bad* nodes\n// representing the fragments of erroneous source code). Multiple errors\n// are returned via a scanner.ErrorList which is sorted by source position.\nfunc parseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast.File, err error) {\n\tif fset == nil {\n\t\tpanic(\"parser.ParseFile: no token.FileSet provided (fset == nil)\")\n\t}\n\n\t// get source\n\ttext, err := stream.ReadSourceLocal(filename, src)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tvar p parser\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\t// resume same panic if it's not a bailout\n\t\t\tif _, ok := e.(bailout); !ok {\n\t\t\t\tpanic(e)\n\t\t\t}\n\t\t}\n\n\t\t// set result values\n\t\tif f == nil {\n\t\t\t// source is not a valid Go source file - satisfy\n\t\t\t// ParseFile API and return a valid (but) empty *ast.File\n\t\t\tf = &ast.File{\n\t\t\t\tName: new(ast.Ident),\n\t\t\t}\n\t\t}\n\t\tf.Code = text\n\n\t\tp.errors.Sort()\n\t\terr = p.errors.Err()\n\t}()\n\n\t// parse source\n\tp.init(fset, filename, text, mode)\n\tf = p.parseFile()\n\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\n// ParseExprFrom is a convenience function for parsing an expression.\n// The arguments have the same meaning as for ParseFile, but the source must\n// be a valid Go/XGo (type or value) expression. Specifically, fset must not\n// be nil.\n//\n// If the source couldn't be read, the returned AST is nil and the error\n// indicates the specific failure. If the source was read but syntax\n// errors were found, the result is a partial AST (with ast.Bad* nodes\n// representing the fragments of erroneous source code). Multiple errors\n// are returned via a scanner.ErrorList which is sorted by source position.\nfunc ParseExprFrom(fset *token.FileSet, filename string, src any, mode Mode) (expr ast.Expr, err error) {\n\t// get source\n\ttext, err := stream.ReadSourceLocal(filename, src)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tvar p parser\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\t// resume same panic if it's not a bailout\n\t\t\tif _, ok := e.(bailout); !ok {\n\t\t\t\tpanic(e)\n\t\t\t}\n\t\t}\n\t\tp.errors.Sort()\n\t\terr = p.errors.Err()\n\t}()\n\n\t// parse expr\n\tp.init(fset, filename, text, mode)\n\texpr = p.parseRHS()\n\n\t// If a semicolon was inserted, consume it;\n\t// report an error if there's more tokens.\n\tif p.tok == token.SEMICOLON && p.lit == \"\\n\" {\n\t\tp.next()\n\t}\n\tp.expect(token.EOF)\n\n\treturn\n}\n\n// ParseExprEx is a convenience function for parsing an expression.\n// The arguments have the same meaning as for ParseFile, but the source must\n// be a valid Go/XGo (type or value) expression. Specifically, fset must not\n// be nil.\n//\n// If the source couldn't be read, the returned AST is nil and the error\n// indicates the specific failure. If the source was read but syntax\n// errors were found, the result is a partial AST (with ast.Bad* nodes\n// representing the fragments of erroneous source code). Multiple errors\n// are returned via a scanner.ErrorList which is sorted by source position.\nfunc ParseExprEx(file *token.File, src []byte, offset int, mode Mode) (expr ast.Expr, err scanner.ErrorList) {\n\tvar p parser\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\t// resume same panic if it's not a bailout\n\t\t\tif _, ok := e.(bailout); !ok {\n\t\t\t\tpanic(e)\n\t\t\t}\n\t\t}\n\t\terr = p.errors\n\t}()\n\n\t// parse expr\n\tp.initSub(file, src, offset, mode)\n\texpr = p.parseRHS()\n\n\t// If a semicolon was inserted, consume it;\n\t// report an error if there's more tokens.\n\tif p.tok == token.SEMICOLON && p.lit == \"\\n\" {\n\t\tp.next()\n\t}\n\tp.expect(token.EOF)\n\n\treturn\n}\n\n// ParseExpr is a convenience function for obtaining the AST of an expression x.\n// The position information recorded in the AST is undefined. The filename used\n// in error messages is the empty string.\n//\n// If syntax errors were found, the result is a partial AST (with ast.Bad* nodes\n// representing the fragments of erroneous source code). Multiple errors are\n// returned via a scanner.ErrorList which is sorted by source position.\nfunc ParseExpr(x string) (ast.Expr, error) {\n\treturn ParseExprFrom(token.NewFileSet(), \"\", []byte(x), 0)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "parser/parser.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package parser implements a parser for XGo source files. Input may be\n// provided in a variety of forms (see the various Parse* functions); the\n// output is an abstract syntax tree (AST) representing the Go source. The\n// parser is invoked through one of the Parse* functions.\n//\n// The parser accepts a larger language than is syntactically permitted by\n// the XGo spec, for simplicity, and for improved robustness in the presence\n// of syntax errors. For instance, in method declarations, the receiver is\n// treated like an ordinary parameter list and thus may contain multiple\n// entries where the spec permits exactly one. Consequently, the corresponding\n// field in the AST (ast.FuncDecl.Recv) field is not restricted to one entry.\npackage parser\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/scanner\"\n\t\"github.com/goplus/xgo/token\"\n\ttplast \"github.com/goplus/xgo/tpl/ast\"\n\ttpl \"github.com/goplus/xgo/tpl/parser\"\n\t\"github.com/qiniu/x/log\"\n)\n\n// Parser flags control parsing behavior using bitwise operations:\n//\n//\tflagInLHS - parsing left-hand side expression (identifiers not resolved)\n//\tflagAllowCmd - allow command-style function calls without parentheses\n//\tflagAllowRangeExpr - allow range expressions (first:last or first:last:step)\nconst (\n\tflagInLHS = 1 << iota\n\tflagAllowCmd\n\tflagAllowRangeExpr\n\tflagAllowKwargExpr\n)\n\nconst (\n\texprNormal = iota\n\texprTuple  // (expr1, expr2, ...)\n\texprKwarg  // name=expr\n)\n\n// The parser structure holds the parser's internal state.\ntype parser struct {\n\tfile    *token.File\n\terrors  scanner.ErrorList\n\tscanner scanner.Scanner\n\n\t// Tracing/debugging\n\tmode   Mode // parsing mode\n\ttrace  bool // == (mode & Trace != 0)\n\tindent int  // indentation used for tracing output\n\n\t// Comments\n\tcomments    []*ast.CommentGroup\n\tleadComment *ast.CommentGroup // last lead comment\n\tlineComment *ast.CommentGroup // last line comment\n\n\t// Next token\n\tpos token.Pos   // token position\n\ttok token.Token // one token look-ahead\n\tlit string      // token literal\n\told struct {\n\t\tpos token.Pos\n\t\ttok token.Token\n\t\tlit string\n\t}\n\n\t// Error recovery\n\t// (used to limit the number of calls to parser.advance\n\t// w/o making scanning progress - avoids potential endless\n\t// loops across multiple parser functions during error recovery)\n\tsyncPos token.Pos // last synchronization position\n\tsyncCnt int       // number of parser.advance calls without progress\n\n\tvarDeclCnt int // number of var decl\n\n\t// Non-syntactic parser control\n\texprLev int  // < 0: in control clause, >= 0: in expression\n\tinRHS   bool // if set, the parser is parsing a rhs expression\n\n\t// Ordinary identifier scopes\n\tpkgScope   *ast.Scope        // pkgScope.Outer == nil\n\ttopScope   *ast.Scope        // top-most scope; may be pkgScope\n\tunresolved []*ast.Ident      // unresolved identifiers\n\timports    []*ast.ImportSpec // list of imports\n\n\t// Label scopes\n\t// (maintained by open/close LabelScope)\n\tlabelScope  *ast.Scope     // label scope for current function\n\ttargetStack [][]*ast.Ident // stack of unresolved labels\n}\n\nfunc (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {\n\tp.file = fset.AddFile(filename, -1, len(src))\n\tvar m scanner.Mode\n\tif mode&ParseComments != 0 {\n\t\tm = scanner.ScanComments\n\t}\n\teh := func(pos token.Position, msg string) { p.errors.Add(pos, msg) }\n\tp.scanner.Init(p.file, src, eh, m)\n\n\tp.mode = mode\n\tp.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)\n\tp.next()\n}\n\nfunc (p *parser) initSub(file *token.File, src []byte, offset int, mode Mode) {\n\tp.file = file\n\teh := func(pos token.Position, msg string) { p.errors.Add(pos, msg) }\n\tp.scanner.InitEx(p.file, src, offset, eh, 0)\n\n\tp.mode = mode\n\tp.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)\n\tp.next()\n}\n\n// ----------------------------------------------------------------------------\n// Scoping support\n\nfunc (p *parser) openScope() {\n\tp.topScope = ast.NewScope(p.topScope)\n}\n\nfunc (p *parser) closeScope() {\n\tp.topScope = p.topScope.Outer\n}\n\nfunc (p *parser) openLabelScope() {\n\tp.labelScope = ast.NewScope(p.labelScope)\n\tp.targetStack = append(p.targetStack, nil)\n}\n\nfunc (p *parser) closeLabelScope() {\n\t// resolve labels\n\tn := len(p.targetStack) - 1\n\tscope := p.labelScope\n\tfor _, ident := range p.targetStack[n] {\n\t\tident.Obj = scope.Lookup(ident.Name)\n\t\tif ident.Obj == nil && p.mode&DeclarationErrors != 0 {\n\t\t\tp.error(ident.Pos(), fmt.Sprintf(\"label %s undefined\", ident.Name))\n\t\t}\n\t}\n\t// pop label scope\n\tp.targetStack = p.targetStack[0:n]\n\tp.labelScope = p.labelScope.Outer\n}\n\nfunc (p *parser) declare(decl, data any, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {\n\tfor _, ident := range idents {\n\t\tassert(ident.Obj == nil, \"identifier already declared or resolved\")\n\t\tobj := ast.NewObj(kind, ident.Name)\n\t\t// remember the corresponding declaration for redeclaration\n\t\t// errors and global variable resolution/typechecking phase\n\t\tobj.Decl = decl\n\t\tobj.Data = data\n\t\tident.Obj = obj\n\t\tif ident.Name != \"_\" {\n\t\t\tif alt := scope.Insert(obj); alt != nil && p.mode&DeclarationErrors != 0 {\n\t\t\t\tprevDecl := \"\"\n\t\t\t\tif pos := alt.Pos(); pos.IsValid() {\n\t\t\t\t\tprevDecl = fmt.Sprintf(\"\\n\\tprevious declaration at %s\", p.file.Position(pos))\n\t\t\t\t}\n\t\t\t\tp.error(ident.Pos(), fmt.Sprintf(\"%s redeclared in this block%s\", ident.Name, prevDecl))\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (p *parser) shortVarDecl(decl *ast.AssignStmt, list []ast.Expr) {\n\t// Go spec: A short variable declaration may redeclare variables\n\t// provided they were originally declared in the same block with\n\t// the same type, and at least one of the non-blank variables is new.\n\tn := 0 // number of new variables\n\tfor _, x := range list {\n\t\tif ident, isIdent := x.(*ast.Ident); isIdent {\n\t\t\tassert(ident.Obj == nil, \"identifier already declared or resolved\")\n\t\t\tobj := ast.NewObj(ast.Var, ident.Name)\n\t\t\t// remember corresponding assignment for other tools\n\t\t\tobj.Decl = decl\n\t\t\tident.Obj = obj\n\t\t\tif ident.Name != \"_\" {\n\t\t\t\tif alt := p.topScope.Insert(obj); alt != nil {\n\t\t\t\t\tident.Obj = alt // redeclaration\n\t\t\t\t} else {\n\t\t\t\t\tn++ // new declaration\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tp.errorExpected(x.Pos(), \"identifier on left side of :=\", 2)\n\t\t}\n\t}\n\tif n == 0 && p.mode&DeclarationErrors != 0 {\n\t\tp.error(list[0].Pos(), \"no new variables on left side of :=\")\n\t}\n}\n\n// The unresolved object is a sentinel to mark identifiers that have been added\n// to the list of unresolved identifiers. The sentinel is only used for verifying\n// internal consistency.\nvar unresolved = new(ast.Object)\n\n// If x is an identifier, tryResolve attempts to resolve x by looking up\n// the object it denotes. If no object is found and collectUnresolved is\n// set, x is marked as unresolved and collected in the list of unresolved\n// identifiers.\nfunc (p *parser) tryResolve(x ast.Expr, collectUnresolved bool) {\n\t// nothing to do if x is not an identifier or the blank identifier\n\tident, _ := x.(*ast.Ident)\n\tif ident == nil {\n\t\treturn\n\t}\n\tassert(ident.Obj == nil, \"identifier already declared or resolved\")\n\tif ident.Name == \"_\" {\n\t\treturn\n\t}\n\t// try to resolve the identifier\n\tfor s := p.topScope; s != nil; s = s.Outer {\n\t\tif obj := s.Lookup(ident.Name); obj != nil {\n\t\t\tident.Obj = obj\n\t\t\treturn\n\t\t}\n\t}\n\t// all local scopes are known, so any unresolved identifier\n\t// must be found either in the file scope, package scope\n\t// (perhaps in another file), or universe scope --- collect\n\t// them so that they can be resolved later\n\tif collectUnresolved {\n\t\tident.Obj = unresolved\n\t\tp.unresolved = append(p.unresolved, ident)\n\t}\n}\n\nfunc (p *parser) resolve(x ast.Expr) {\n\tp.tryResolve(x, true)\n}\n\n// ----------------------------------------------------------------------------\n// Parsing support\n\nfunc (p *parser) printTrace(a ...any) {\n\tconst dots = \". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . \"\n\tconst n = len(dots)\n\tpos := p.file.Position(p.pos)\n\tfmt.Printf(\"%5d:%3d: \", pos.Line, pos.Column)\n\ti := 2 * p.indent\n\tfor i > n {\n\t\tfmt.Print(dots)\n\t\ti -= n\n\t}\n\t// i <= n\n\tfmt.Print(dots[0:i])\n\tfmt.Println(a...)\n}\n\nfunc trace(p *parser, msg string) *parser {\n\tp.printTrace(msg, \"(\")\n\tp.indent++\n\treturn p\n}\n\n// Usage pattern: defer un(trace(p, \"...\"))\nfunc un(p *parser) {\n\tp.indent--\n\tp.printTrace(\")\")\n}\n\nfunc (p *parser) unget(pos token.Pos, tok token.Token, lit string) {\n\tp.old.pos, p.old.tok, p.old.lit = p.pos, p.tok, p.lit\n\tp.pos, p.tok, p.lit = pos, tok, lit\n}\n\n// Advance to the next token.\nfunc (p *parser) next0() {\n\tif p.old.pos != 0 { // XGo: support unget\n\t\tp.pos, p.tok, p.lit = p.old.pos, p.old.tok, p.old.lit\n\t\tp.old.pos = 0\n\t\treturn\n\t}\n\n\t// Because of one-token look-ahead, print the previous token\n\t// when tracing as it provides a more readable output. The\n\t// very first token (!p.pos.IsValid()) is not initialized\n\t// (it is token.ILLEGAL), so don't print it .\n\tif p.trace && p.pos.IsValid() {\n\t\ts := p.tok.String()\n\t\tswitch {\n\t\tcase p.tok.IsLiteral():\n\t\t\tp.printTrace(s, p.lit)\n\t\tcase p.tok.IsOperator(), p.tok.IsKeyword():\n\t\t\tp.printTrace(\"\\\"\" + s + \"\\\"\")\n\t\tdefault:\n\t\t\tp.printTrace(s)\n\t\t}\n\t}\n\n\tp.pos, p.tok, p.lit = p.scanner.Scan()\n}\n\n// Consume a comment and return it and the line on which it ends.\nfunc (p *parser) consumeComment() (comment *ast.Comment, endline int) {\n\t// /*-style comments may end on a different line than where they start.\n\t// Scan the comment for '\\n' chars and adjust endline accordingly.\n\tendline = p.file.Line(p.pos)\n\tif p.lit[1] == '*' {\n\t\t// don't use range here - no need to decode Unicode code points\n\t\tfor i := 0; i < len(p.lit); i++ {\n\t\t\tif p.lit[i] == '\\n' {\n\t\t\t\tendline++\n\t\t\t}\n\t\t}\n\t}\n\n\tcomment = &ast.Comment{Slash: p.pos, Text: p.lit}\n\tp.next0()\n\n\treturn\n}\n\n// Consume a group of adjacent comments, add it to the parser's\n// comments list, and return it together with the line at which\n// the last comment in the group ends. A non-comment token or n\n// empty lines terminate a comment group.\nfunc (p *parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) {\n\tvar list []*ast.Comment\n\tendline = p.file.Line(p.pos)\n\tfor p.tok == token.COMMENT && p.file.Line(p.pos) <= endline+n {\n\t\tvar comment *ast.Comment\n\t\tcomment, endline = p.consumeComment()\n\t\tlist = append(list, comment)\n\t}\n\n\t// add comment group to the comments list\n\tcomments = &ast.CommentGroup{List: list}\n\tp.comments = append(p.comments, comments)\n\n\treturn\n}\n\n// Advance to the next non-comment token. In the process, collect\n// any comment groups encountered, and remember the last lead and\n// line comments.\n//\n// A lead comment is a comment group that starts and ends in a\n// line without any other tokens and that is followed by a non-comment\n// token on the line immediately after the comment group.\n//\n// A line comment is a comment group that follows a non-comment\n// token on the same line, and that has no tokens after it on the line\n// where it ends.\n//\n// Lead and line comments may be considered documentation that is\n// stored in the AST.\nfunc (p *parser) next() {\n\tp.leadComment = nil\n\tp.lineComment = nil\n\tprev := p.pos\n\tp.next0()\n\n\tif p.tok == token.COMMENT {\n\t\tvar comment *ast.CommentGroup\n\t\tvar endline int\n\n\t\tif p.file.Line(p.pos) == p.file.Line(prev) || p.lit[0] == '#' {\n\t\t\t// The comment is on same line as the previous token; it\n\t\t\t// cannot be a lead comment but may be a line comment.\n\t\t\tcomment, endline = p.consumeCommentGroup(0)\n\t\t\tif p.file.Line(p.pos) != endline || p.tok == token.EOF {\n\t\t\t\t// The next token is on a different line, thus\n\t\t\t\t// the last comment group is a line comment.\n\t\t\t\tp.lineComment = comment\n\t\t\t}\n\t\t}\n\n\t\t// consume successor comments, if any\n\t\tendline = -1\n\t\tfor p.tok == token.COMMENT {\n\t\t\tcomment, endline = p.consumeCommentGroup(1)\n\t\t}\n\n\t\tif endline+1 == p.file.Line(p.pos) {\n\t\t\t// The next token is following on the line immediately after the\n\t\t\t// comment group, thus the last comment group is a lead comment.\n\t\t\tp.leadComment = comment\n\t\t}\n\t}\n}\n\n// A bailout panic is raised to indicate early termination.\ntype bailout struct {\n}\n\nfunc (p *parser) error(pos token.Pos, msg string) {\n\tepos := p.file.Position(pos)\n\n\t// If AllErrors is not set, discard errors reported on the same line\n\t// as the last recorded error and stop parsing if there are more than\n\t// 10 errors.\n\tif p.mode&AllErrors == 0 {\n\t\tn := len(p.errors)\n\t\tif n > 0 && p.errors[n-1].Pos.Line == epos.Line {\n\t\t\treturn // discard - likely a spurious error\n\t\t}\n\t\tif n > 10 {\n\t\t\tpanic(bailout{})\n\t\t}\n\t}\n\n\tp.errors.Add(epos, msg)\n}\n\nfunc (p *parser) errorExpected(pos token.Pos, msg string, calldepth int) {\n\tmsg = \"expected \" + msg\n\tif pos == p.pos {\n\t\t// the error happened at the current position;\n\t\t// make the error message more specific\n\t\tswitch {\n\t\tcase p.tok == token.SEMICOLON && p.lit == \"\\n\":\n\t\t\tmsg += \", found newline\"\n\t\tcase p.tok.IsLiteral():\n\t\t\t// print 123 rather than 'INT', etc.\n\t\t\tmsg += \", found \" + p.lit\n\t\tdefault:\n\t\t\tmsg += \", found '\" + p.tok.String() + \"'\"\n\t\t}\n\t}\n\tif debugParseError {\n\t\tlog.Std.Output(\"\", log.Linfo, calldepth, msg)\n\t}\n\tp.error(pos, msg)\n}\n\nfunc (p *parser) expect(tok token.Token) token.Pos {\n\tpos := p.pos\n\tif p.tok != tok {\n\t\tp.errorExpected(pos, \"'\"+tok.String()+\"'\", 3)\n\t}\n\tp.next() // make progress\n\treturn pos\n}\n\n// expect2 is like expect, but it returns an invalid position\n// if the expected token is not found.\nfunc (p *parser) expect2(tok token.Token) (pos token.Pos) {\n\tif p.tok == tok {\n\t\tpos = p.pos\n\t} else {\n\t\tp.errorExpected(p.pos, \"'\"+tok.String()+\"'\", 3)\n\t}\n\tp.next() // make progress\n\treturn\n}\n\nfunc (p *parser) expectIn() token.Pos {\n\tpos := p.pos\n\tswitch p.tok {\n\tcase token.ARROW: // <- (backward compatibility)\n\tcase token.IDENT: // in\n\t\tif p.lit == \"in\" {\n\t\t\tbreak\n\t\t}\n\t\tfallthrough\n\tdefault:\n\t\tp.errorExpected(pos, \"'in'\", 3)\n\t}\n\tp.next() // make progress\n\treturn pos\n}\n\n// expectClosing is like expect but provides a better error message\n// for the common case of a missing comma before a newline.\nfunc (p *parser) expectClosing(tok token.Token, context string) token.Pos {\n\tif p.tok != tok && p.tok == token.SEMICOLON && p.lit == \"\\n\" {\n\t\tp.error(p.pos, \"missing ',' before newline in \"+context)\n\t\tp.next()\n\t}\n\treturn p.expect(tok)\n}\n\nfunc (p *parser) expectSemi() {\n\t// semicolon is optional before a closing ')' or '}'\n\tif p.tok != token.RPAREN && p.tok != token.RBRACE {\n\t\tswitch p.tok {\n\t\tcase token.COMMA:\n\t\t\t// permit a ',' instead of a ';' but complain\n\t\t\tp.errorExpected(p.pos, \"';'\", 3)\n\t\t\tfallthrough\n\t\tcase token.SEMICOLON:\n\t\t\tp.next()\n\t\tdefault:\n\t\t\tp.errorExpected(p.pos, \"';'\", 3)\n\t\t\tp.advance(stmtStart)\n\t\t}\n\t}\n}\n\nfunc (p *parser) atComma(context string, follow token.Token) bool {\n\tif p.tok == token.COMMA {\n\t\treturn true\n\t}\n\tif p.tok != follow {\n\t\tmsg := \"missing ','\"\n\t\tif p.tok == token.SEMICOLON && p.lit == \"\\n\" {\n\t\t\tmsg += \" before newline\"\n\t\t}\n\t\tmsgctx := msg + \" in \" + context\n\t\tp.error(p.pos, msgctx)\n\t\tif debugParseError {\n\t\t\tlog.Std.Output(\"\", log.Linfo, 2, msgctx)\n\t\t}\n\t\treturn true // \"insert\" comma and continue\n\t}\n\treturn false\n}\n\nfunc assert(cond bool, msg string) {\n\tif !cond {\n\t\tpanic(\"go/parser internal error: \" + msg)\n\t}\n}\n\n// advance consumes tokens until the current token p.tok\n// is in the 'to' set, or token.EOF. For error recovery.\nfunc (p *parser) advance(to map[token.Token]bool) {\n\tfor ; p.tok != token.EOF; p.next() {\n\t\tif to[p.tok] {\n\t\t\t// Return only if parser made some progress since last\n\t\t\t// sync or if it has not reached 10 advance calls without\n\t\t\t// progress. Otherwise consume at least one token to\n\t\t\t// avoid an endless parser loop (it is possible that\n\t\t\t// both parseOperand and parseStmt call advance and\n\t\t\t// correctly do not advance, thus the need for the\n\t\t\t// invocation limit p.syncCnt).\n\t\t\tif p.pos == p.syncPos && p.syncCnt < 10 {\n\t\t\t\tp.syncCnt++\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif p.pos > p.syncPos {\n\t\t\t\tp.syncPos = p.pos\n\t\t\t\tp.syncCnt = 0\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Reaching here indicates a parser bug, likely an\n\t\t\t// incorrect token list in this function, but it only\n\t\t\t// leads to skipping of possibly correct code if a\n\t\t\t// previous error is present, and thus is preferred\n\t\t\t// over a non-terminating parse.\n\t\t}\n\t}\n}\n\nvar stmtStart = map[token.Token]bool{\n\ttoken.BREAK:       true,\n\ttoken.CONST:       true,\n\ttoken.CONTINUE:    true,\n\ttoken.DEFER:       true,\n\ttoken.FALLTHROUGH: true,\n\ttoken.FOR:         true,\n\ttoken.GO:          true,\n\ttoken.GOTO:        true,\n\ttoken.IF:          true,\n\ttoken.RETURN:      true,\n\ttoken.SELECT:      true,\n\ttoken.SWITCH:      true,\n\ttoken.TYPE:        true,\n\ttoken.VAR:         true,\n}\n\nvar declStart = map[token.Token]bool{\n\ttoken.CONST: true,\n\ttoken.TYPE:  true,\n\ttoken.VAR:   true,\n}\n\nvar exprEnd = map[token.Token]bool{\n\ttoken.COMMA:     true,\n\ttoken.COLON:     true,\n\ttoken.SEMICOLON: true,\n\ttoken.RPAREN:    true,\n\ttoken.RBRACK:    true,\n\ttoken.RBRACE:    true,\n}\n\n// safePos returns a valid file position for a given position: If pos\n// is valid to begin with, safePos returns pos. If pos is out-of-range,\n// safePos returns the EOF position.\n//\n// This is hack to work around \"artificial\" end positions in the AST which\n// are computed by adding 1 to (presumably valid) token positions. If the\n// token positions are invalid due to parse errors, the resulting end position\n// may be past the file's EOF position, which would lead to panics if used\n// later on.\nfunc (p *parser) safePos(pos token.Pos) (res token.Pos) {\n\tdefer func() {\n\t\tif recover() != nil {\n\t\t\tres = token.Pos(p.file.Base() + p.file.Size()) // EOF position\n\t\t}\n\t}()\n\t_ = p.file.Offset(pos) // trigger a panic if position is out-of-range\n\treturn pos\n}\n\n// ----------------------------------------------------------------------------\n// Identifiers\n\nfunc (p *parser) parseIdentOrOp() (*ast.Ident, bool) { // function Name\n\tif int(p.tok) < len(overloadOps) {\n\t\tflags := overloadOps[p.tok]\n\t\tif flags != 0 {\n\t\t\tpos := p.pos\n\t\t\ttok := p.tok\n\t\t\tp.next()\n\t\t\tif debugParseOutput {\n\t\t\t\tlog.Printf(\"ast.Ident{Tok: %v}\\n\", tok)\n\t\t\t}\n\t\t\treturn &ast.Ident{NamePos: pos, Name: tok.String()}, true\n\t\t}\n\t}\n\treturn p.parseIdent(), false\n}\n\nconst (\n\topUnary = 1 << iota\n\topBinary\n\topAssign\n\topAssignOp\n\topIncDec\n)\n\nvar overloadOps = [...]byte{\n\ttoken.ADD: opBinary,           // +\n\ttoken.SUB: opBinary | opUnary, // -\n\ttoken.MUL: opBinary | opUnary, // *\n\ttoken.QUO: opBinary,           // /\n\ttoken.REM: opBinary,           // %\n\n\ttoken.AND:     opBinary, // &\n\ttoken.OR:      opBinary, // |\n\ttoken.XOR:     opBinary, // ^\n\ttoken.SHL:     opBinary, // <<\n\ttoken.SHR:     opBinary, // >>\n\ttoken.AND_NOT: opBinary, // &^\n\n\ttoken.SRARROW:   opBinary, // ->\n\ttoken.BIDIARROW: opBinary, // <>\n\n\ttoken.ADD_ASSIGN: opAssignOp, // +=\n\ttoken.SUB_ASSIGN: opAssignOp, // -=\n\ttoken.MUL_ASSIGN: opAssignOp, // *=\n\ttoken.QUO_ASSIGN: opAssignOp, // /=\n\ttoken.REM_ASSIGN: opAssignOp, // %=\n\n\ttoken.AND_ASSIGN:     opAssignOp, // &=\n\ttoken.OR_ASSIGN:      opAssignOp, // |=\n\ttoken.XOR_ASSIGN:     opAssignOp, // ^=\n\ttoken.SHL_ASSIGN:     opAssignOp, // <<=\n\ttoken.SHR_ASSIGN:     opAssignOp, // >>=\n\ttoken.AND_NOT_ASSIGN: opAssignOp, // &^=\n\n\ttoken.ASSIGN: opAssign, // =\n\n\ttoken.INC: opIncDec, // ++\n\ttoken.DEC: opIncDec, // --\n\n\ttoken.EQL:  opBinary, // ==\n\ttoken.LSS:  opBinary, // <\n\ttoken.GTR:  opBinary, // >\n\ttoken.NEQ:  opBinary, // !=\n\ttoken.LEQ:  opBinary, // <=\n\ttoken.GEQ:  opBinary, // >=\n\ttoken.LAND: opBinary, // &&\n\ttoken.LOR:  opBinary, // ||\n\ttoken.NOT:  opUnary,  // !\n\n\ttoken.ARROW: opBinary | opUnary, // <-\n}\n\nfunc (p *parser) parseIdent() *ast.Ident {\n\tpos := p.pos\n\tname := \"_\"\n\tif p.tok == token.IDENT {\n\t\tname = p.lit\n\t\tp.next()\n\t} else {\n\t\tp.expect(token.IDENT) // use expect() error handling\n\t}\n\tif debugParseOutput {\n\t\tlog.Printf(\"ast.Ident{Name: %v}\\n\", name)\n\t}\n\treturn &ast.Ident{NamePos: pos, Name: name}\n}\n\nfunc (p *parser) parseIdentList() (list []*ast.Ident) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"IdentList\"))\n\t}\n\n\tlist = append(list, p.parseIdent())\n\tfor p.tok == token.COMMA {\n\t\tp.next()\n\t\tlist = append(list, p.parseIdent())\n\t}\n\treturn\n}\n\n// ----------------------------------------------------------------------------\n// Common productions\n\n// If flagInLHS is set in flags, result list elements which are identifiers are\n// not resolved.\n// flags support flagInLHS, flagAllowCmd\nfunc (p *parser) parseExprList(flags int) (list []ast.Expr) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ExpressionList\"))\n\t}\n\n\tlist = append(list, p.checkExpr(p.parseExpr(flags)))\n\tfor p.tok == token.COMMA {\n\t\tp.next()\n\t\tlist = append(list, p.checkExpr(p.parseExpr(flags&flagInLHS))) // clear all but flagInLHS\n\t}\n\treturn\n}\n\n// flags support flagAllowCmd\nfunc (p *parser) parseLHSList(flags int) []ast.Expr {\n\told := p.inRHS\n\tp.inRHS = false\n\tlist := p.parseExprList(flags | flagInLHS)\n\tswitch p.tok {\n\tcase token.DEFINE:\n\t\t// lhs of a short variable declaration\n\t\t// but doesn't enter scope until later:\n\t\t// caller must call p.shortVarDecl(p.makeIdentList(list))\n\t\t// at appropriate time.\n\tcase token.COLON:\n\t\t// lhs of a label declaration or a communication clause of a select\n\t\t// statement (parseLhsList is not called when parsing the case clause\n\t\t// of a switch statement):\n\t\t// - labels are declared by the caller of parseLhsList\n\t\t// - for communication clauses, if there is a stand-alone identifier\n\t\t//   followed by a colon, we have a syntax error; there is no need\n\t\t//   to resolve the identifier in that case\n\tdefault:\n\t\t// identifiers must be declared elsewhere\n\t\tfor _, x := range list {\n\t\t\tp.resolve(x)\n\t\t}\n\t}\n\tp.inRHS = old\n\treturn list\n}\n\nfunc (p *parser) parseRHSList() []ast.Expr {\n\told := p.inRHS\n\tp.inRHS = true\n\tlist := p.parseExprList(0)\n\tp.inRHS = old\n\treturn list\n}\n\n// ----------------------------------------------------------------------------\n// Types\n\nfunc (p *parser) parseType() ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Type\"))\n\t}\n\n\ttyp := p.tryType()\n\n\tif typ == nil {\n\t\tpos := p.pos\n\t\tp.errorExpected(pos, \"type\", 2)\n\t\tp.advance(exprEnd)\n\t\treturn &ast.BadExpr{From: pos, To: p.pos}\n\t}\n\n\treturn typ\n}\n\n// If the result is an identifier, it is not resolved.\nfunc (p *parser) parseTypeName(ident *ast.Ident) ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"TypeName\"))\n\t}\n\n\tif ident == nil {\n\t\tident = p.parseIdent()\n\t}\n\t// don't resolve ident yet - it may be a parameter or field name\n\n\tif p.tok == token.PERIOD {\n\t\t// ident is a package name\n\t\tp.next()\n\t\tp.resolve(ident)\n\t\tsel := p.parseIdent()\n\t\treturn &ast.SelectorExpr{X: ident, Sel: sel}\n\t}\n\n\treturn ident\n}\n\nconst (\n\tstateArrayTypeOrSliceLit = iota\n\tstateTypeOrSliceOp\n\tstateType\n)\n\nconst (\n\tresultNone      = 0\n\tresultArrayType = 1 << iota\n\tresultSliceLit\n\tresultSliceOp\n\tresultComprehensionExpr\n\tresultParenType\n\tresultType\n\tresultIdent     // expr or type\n\tresultExprFlags = resultSliceLit | resultSliceOp | resultComprehensionExpr\n\tresultTypeFlags = resultArrayType | resultParenType | resultType\n)\n\n// state = stateArrayTypeOrSliceLit | stateTypeOrSliceOp | stateType | ...\nfunc (p *parser) parseArrayTypeOrSliceLit(state int, slice ast.Expr) (expr ast.Expr, result int) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ArrayType\"))\n\t}\n\n\tlbrack := p.expect(token.LBRACK)\n\tp.exprLev++\n\tvar len ast.Expr\n\t// always permit ellipsis for more fault-tolerant parsing\n\tif p.tok == token.ELLIPSIS {\n\t\tlen = &ast.Ellipsis{Ellipsis: p.pos}\n\t\tp.next()\n\t} else if p.tok != token.RBRACK {\n\t\tlen = p.parseRHS()\n\t\tswitch state {\n\t\tcase stateArrayTypeOrSliceLit:\n\t\t\tswitch p.tok {\n\t\t\tcase token.COMMA: // [a, b, c, d ...]\n\t\t\t\tsliceLit := p.parseSliceOrMatrixLit(lbrack, len)\n\t\t\t\tp.exprLev--\n\t\t\t\treturn sliceLit, resultSliceLit\n\t\t\tcase token.FOR: // [expr for k, v in container if cond]\n\t\t\t\tphrases := p.parseForPhrases()\n\t\t\t\tp.exprLev--\n\t\t\t\trbrack := p.expect(token.RBRACK)\n\t\t\t\tif debugParseOutput {\n\t\t\t\t\tlog.Printf(\"ast.ComprehensionExpr{Tok: [, Elt: %v, Fors: %v}\\n\", len, phrases)\n\t\t\t\t}\n\t\t\t\treturn &ast.ComprehensionExpr{\n\t\t\t\t\tLpos: lbrack, Tok: token.LBRACK, Elt: len,\n\t\t\t\t\tFors: phrases, Rpos: rbrack,\n\t\t\t\t}, resultComprehensionExpr\n\t\t\t}\n\t\tcase stateTypeOrSliceOp:\n\t\t\tswitch p.tok {\n\t\t\tcase token.COLON: // slice[i:j:k]\n\t\t\t\treturn p.parseIndexOrSliceContinue(slice, lbrack, len), resultSliceOp\n\t\t\t}\n\t\t}\n\t}\n\tp.exprLev--\n\trbrack := p.expect(token.RBRACK)\n\n\tvar elt ast.Expr\n\tswitch state {\n\tcase stateType:\n\t\telt = p.parseType()\n\tcase stateArrayTypeOrSliceLit:\n\t\tsliceLit := newSliceLit(lbrack, rbrack, len)\n\t\telt, result = p.tryIdentOrType(stateTypeOrSliceOp, sliceLit)\n\t\tswitch result {\n\t\tcase resultNone:\n\t\t\tif debugParseOutput {\n\t\t\t\tlog.Printf(\"ast.SliceLit{Elts: %v}\\n\", sliceLit.Elts)\n\t\t\t}\n\t\t\treturn sliceLit, resultSliceLit\n\t\tcase resultSliceOp:\n\t\t\treturn elt, resultSliceOp\n\t\t}\n\t\tp.resolve(elt)\n\tcase stateTypeOrSliceOp:\n\t\telt = p.tryType()\n\t\tif elt == nil {\n\t\t\tif len == nil {\n\t\t\t\tlog.Panicln(\"TODO: expect slice index\")\n\t\t\t}\n\t\t\tif debugParseOutput {\n\t\t\t\tlog.Printf(\"ast.IndexExpr{X: %v, Index: %v}\\n\", slice, len)\n\t\t\t}\n\t\t\treturn &ast.IndexExpr{X: slice, Index: len}, resultSliceOp\n\t\t}\n\tdefault:\n\t\tpanic(\"parseArrayTypeOrSliceLit: unexpected state\")\n\t}\n\n\tif debugParseOutput {\n\t\tlog.Printf(\"ast.ArrayType{Len: %v, Elt: %v}\\n\", len, elt)\n\t}\n\treturn &ast.ArrayType{Lbrack: lbrack, Len: len, Elt: elt}, resultArrayType\n}\n\n// [first, ...]\n// [first, a2, a2, ...; b1, b2, b3, ...]\nfunc (p *parser) parseSliceOrMatrixLit(lbrack token.Pos, first ast.Expr) ast.Expr {\n\tvar mat [][]ast.Expr\n\telts := make([]ast.Expr, 1, 8)\n\telts[0] = first\n\tfor {\n\t\tswitch p.tok {\n\t\tcase token.COMMA:\n\t\tcase token.SEMICOLON:\n\t\t\tmat = append(mat, elts)\n\t\t\telts = make([]ast.Expr, 0, len(elts))\n\t\tcase token.ELLIPSIS:\n\t\t\tn := len(elts)\n\t\t\telts[n-1] = &ast.ElemEllipsis{Ellipsis: p.pos, Elt: elts[n-1]}\n\t\t\tp.next()\n\t\t\tcontinue\n\t\tdefault:\n\t\t\tgoto done\n\t\t}\n\t\tp.next()\n\t\tif p.tok != token.RBRACK {\n\t\t\telt := p.parseRHS()\n\t\t\telts = append(elts, elt)\n\t\t}\n\t}\ndone:\n\trbrack := p.expect(token.RBRACK)\n\n\tif mat != nil {\n\t\tif len(elts) > 0 {\n\t\t\tmat = append(mat, elts)\n\t\t}\n\t\treturn &ast.MatrixLit{Lbrack: lbrack, Elts: mat, Rbrack: rbrack}\n\t}\n\treturn &ast.SliceLit{Lbrack: lbrack, Elts: elts, Rbrack: rbrack}\n}\n\nfunc newSliceLit(lbrack, rbrack token.Pos, len ast.Expr) *ast.SliceLit {\n\tvar elts []ast.Expr\n\tif len != nil {\n\t\telts = []ast.Expr{len}\n\t}\n\treturn &ast.SliceLit{Lbrack: lbrack, Elts: elts, Rbrack: rbrack}\n}\n\nfunc (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {\n\tif p.trace {\n\t\tdefer un(trace(p, \"FieldDecl\"))\n\t}\n\n\tdoc := p.leadComment\n\n\tvar names []*ast.Ident\n\tvar typ ast.Expr\n\tswitch p.tok {\n\tcase token.IDENT:\n\t\tname := p.parseIdent()\n\t\tif p.tok == token.PERIOD || p.tok == token.STRING || p.tok == token.SEMICOLON || p.tok == token.RBRACE {\n\t\t\t// embedded type\n\t\t\ttyp = name\n\t\t\tif p.tok == token.PERIOD {\n\t\t\t\ttyp = p.parseQualifiedIdent(name)\n\t\t\t}\n\t\t} else {\n\t\t\t// name1, name2, ... T\n\t\t\tnames = []*ast.Ident{name}\n\t\t\tfor p.tok == token.COMMA {\n\t\t\t\tp.next()\n\t\t\t\tnames = append(names, p.parseIdent())\n\t\t\t}\n\t\t\t// Careful dance: We don't know if we have an embedded instantiated\n\t\t\t// type T[P1, P2, ...] or a field T of array type []E or [P]E.\n\t\t\tif len(names) == 1 && p.tok == token.LBRACK {\n\t\t\t\tname, typ = p.parseArrayFieldOrTypeInstance(name, stateType)\n\t\t\t\tif name == nil {\n\t\t\t\t\tnames = nil\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// T P\n\t\t\t\ttyp = p.parseType()\n\t\t\t}\n\t\t}\n\tcase token.MUL:\n\t\tstar := p.pos\n\t\tp.next()\n\t\tif p.tok == token.LPAREN {\n\t\t\t// *(T)\n\t\t\tp.error(p.pos, \"cannot parenthesize embedded type\")\n\t\t\tp.next()\n\t\t\ttyp = p.parseQualifiedIdent(nil)\n\t\t\t// expect closing ')' but no need to complain if missing\n\t\t\tif p.tok == token.RPAREN {\n\t\t\t\tp.next()\n\t\t\t}\n\t\t} else {\n\t\t\t// *T\n\t\t\ttyp = p.parseQualifiedIdent(nil)\n\t\t}\n\t\ttyp = &ast.StarExpr{Star: star, X: typ}\n\n\tcase token.LPAREN:\n\t\tp.error(p.pos, \"cannot parenthesize embedded type\")\n\t\tp.next()\n\t\tif p.tok == token.MUL {\n\t\t\t// (*T)\n\t\t\tstar := p.pos\n\t\t\tp.next()\n\t\t\ttyp = &ast.StarExpr{Star: star, X: p.parseQualifiedIdent(nil)}\n\t\t} else {\n\t\t\t// (T)\n\t\t\ttyp = p.parseQualifiedIdent(nil)\n\t\t}\n\t\t// expect closing ')' but no need to complain if missing\n\t\tif p.tok == token.RPAREN {\n\t\t\tp.next()\n\t\t}\n\n\tdefault:\n\t\tpos := p.pos\n\t\tp.errorExpected(pos, \"field name or embedded type\", 2)\n\t\tp.advance(exprEnd)\n\t\ttyp = &ast.BadExpr{From: pos, To: p.pos}\n\t}\n\n\tvar tag *ast.BasicLit\n\tif p.tok == token.STRING {\n\t\ttag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}\n\t\tp.next()\n\t}\n\n\tp.expectSemi()\n\n\tfield := &ast.Field{Doc: doc, Names: names, Type: typ, Tag: tag, Comment: p.lineComment}\n\tp.declare(field, nil, scope, ast.Var, names...)\n\n\treturn field\n}\n\nfunc (p *parser) parseStructType() *ast.StructType {\n\tif p.trace {\n\t\tdefer un(trace(p, \"StructType\"))\n\t}\n\n\tpos := p.expect(token.STRUCT)\n\tlbrace := p.expect(token.LBRACE)\n\tscope := ast.NewScope(nil) // struct scope\n\tvar list []*ast.Field\n\tfor p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {\n\t\t// a field declaration cannot start with a '(' but we accept\n\t\t// it here for more robust parsing and better error messages\n\t\t// (parseFieldDecl will check and complain if necessary)\n\t\tlist = append(list, p.parseFieldDecl(scope))\n\t}\n\trbrace := p.expect(token.RBRACE)\n\n\treturn &ast.StructType{\n\t\tStruct: pos,\n\t\tFields: &ast.FieldList{\n\t\t\tOpening: lbrace,\n\t\t\tList:    list,\n\t\t\tClosing: rbrace,\n\t\t},\n\t}\n}\n\nfunc (p *parser) parsePointerType() *ast.StarExpr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"PointerType\"))\n\t}\n\n\tstar := p.expect(token.MUL)\n\tbase := p.parseType()\n\n\treturn &ast.StarExpr{Star: star, X: base}\n}\n\ntype field struct {\n\tname     *ast.Ident\n\ttyp      ast.Expr\n\toptional token.Pos\n}\n\nfunc (p *parser) parseParameterList(scope *ast.Scope, name0 *ast.Ident, typ0 ast.Expr, closing token.Token) (params []*ast.Field) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ParameterList\"))\n\t}\n\n\t// Type parameters are the only parameter list closed by ']'.\n\ttparams := closing == token.RBRACK\n\t// Type set notation is ok in type parameter lists.\n\n\tpos := p.pos\n\tif name0 != nil {\n\t\tpos = name0.Pos()\n\t}\n\n\tvar list []field\n\tvar named int // number of parameters that have an explicit name and type\n\n\tfor name0 != nil || p.tok != closing && p.tok != token.EOF {\n\t\tvar par field\n\t\tif typ0 != nil {\n\t\t\tpar = field{name: name0, typ: typ0, optional: token.NoPos}\n\t\t} else {\n\t\t\tpar = p.parseParamDecl(name0)\n\t\t}\n\t\tname0 = nil // 1st name was consumed if present\n\t\ttyp0 = nil  // 1st typ was consumed if present\n\t\tif par.name != nil || par.typ != nil {\n\t\t\tlist = append(list, par)\n\t\t\tif par.name != nil && par.typ != nil {\n\t\t\t\tnamed++\n\t\t\t}\n\t\t}\n\t\tif !p.atComma(\"parameter list\", closing) {\n\t\t\tbreak\n\t\t}\n\t\tp.next()\n\t}\n\n\tif len(list) == 0 {\n\t\treturn // not uncommon\n\t}\n\n\t// TODO(gri) parameter distribution and conversion to []*ast.Field\n\t//           can be combined and made more efficient\n\n\t// distribute parameter types\n\tif named == 0 {\n\t\t// all unnamed => found names are type names\n\t\tfor i := 0; i < len(list); i++ {\n\t\t\tpar := &list[i]\n\t\t\tif typ := par.name; typ != nil {\n\t\t\t\tpar.typ = typ\n\t\t\t\tpar.name = nil\n\t\t\t}\n\t\t}\n\t\tif tparams {\n\t\t\tp.error(pos, \"type parameters must be named\")\n\t\t}\n\t} else if named != len(list) {\n\t\t// some named => all must be named\n\t\tok := true\n\t\tvar typ ast.Expr\n\t\tmissingName := pos\n\t\tfor i := len(list) - 1; i >= 0; i-- {\n\t\t\tif par := &list[i]; par.typ != nil {\n\t\t\t\ttyp = par.typ\n\t\t\t\tif par.name == nil {\n\t\t\t\t\tok = false\n\t\t\t\t\tmissingName = par.typ.Pos()\n\t\t\t\t\tn := ast.NewIdent(\"_\")\n\t\t\t\t\tn.NamePos = typ.Pos() // correct position\n\t\t\t\t\tpar.name = n\n\t\t\t\t}\n\t\t\t} else if typ != nil {\n\t\t\t\tpar.typ = typ\n\t\t\t} else {\n\t\t\t\t// par.typ == nil && typ == nil => we only have a par.name\n\t\t\t\tok = false\n\t\t\t\tmissingName = par.name.Pos()\n\t\t\t\tpar.typ = &ast.BadExpr{From: par.name.Pos(), To: p.pos}\n\t\t\t}\n\t\t}\n\t\tif !ok {\n\t\t\tif tparams {\n\t\t\t\tp.error(missingName, \"type parameters must be named\")\n\t\t\t} else {\n\t\t\t\tp.error(pos, \"mixed named and unnamed parameters\")\n\t\t\t}\n\t\t}\n\t}\n\n\t// convert list []*ast.Field\n\tif named == 0 {\n\t\t// parameter list consists of types only\n\t\tfor _, par := range list {\n\t\t\tassert(par.typ != nil, \"nil type in unnamed parameter list\")\n\t\t\tparams = append(params, &ast.Field{Type: par.typ, Optional: par.optional})\n\t\t}\n\t\treturn\n\t}\n\n\t// parameter list consists of named parameters with types\n\tvar names []*ast.Ident\n\tvar typ ast.Expr\n\tvar optional token.Pos\n\taddParams := func() {\n\t\tassert(typ != nil, \"nil type in named parameter list\")\n\t\tfield := &ast.Field{Names: names, Type: typ, Optional: optional}\n\t\t// Go spec: The scope of an identifier denoting a function\n\t\t// parameter or result variable is the function body.\n\t\tp.declare(field, nil, scope, ast.Var, names...)\n\t\tparams = append(params, field)\n\t\tnames = nil\n\t}\n\tfor _, par := range list {\n\t\tif par.typ != typ || par.optional != optional {\n\t\t\tif len(names) > 0 {\n\t\t\t\taddParams()\n\t\t\t}\n\t\t\ttyp = par.typ\n\t\t\toptional = par.optional\n\t\t}\n\t\tnames = append(names, par.name)\n\t}\n\tif len(names) > 0 {\n\t\taddParams()\n\t}\n\treturn\n}\n\nfunc (p *parser) parseParamDecl(name *ast.Ident) (f field) {\n\t// TODO(rFindley) refactor to be more similar to paramDeclOrNil in the syntax\n\t// package\n\tif p.trace {\n\t\tdefer un(trace(p, \"ParamDeclOrNil\"))\n\t}\n\n\tptok := p.tok\n\tif name != nil {\n\t\tp.tok = token.IDENT // force token.IDENT case in switch below\n\t}\n\n\tswitch p.tok {\n\tcase token.IDENT:\n\t\t// name\n\t\tif name != nil {\n\t\t\tf.name = name\n\t\t\tp.tok = ptok\n\t\t} else {\n\t\t\tf.name = p.parseIdent()\n\t\t}\n\t\tswitch p.tok {\n\t\tcase token.IDENT, token.MUL, token.ARROW, token.FUNC, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:\n\t\t\t// name type\n\t\t\tf.typ = p.parseType()\n\n\t\tcase token.LBRACK:\n\t\t\t// name \"[\" type1, ..., typeN \"]\" or name \"[\" n \"]\" type\n\t\t\tf.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name, stateType)\n\n\t\tcase token.ELLIPSIS:\n\t\t\t// name \"...\" type\n\t\t\tf.typ = p.parseDotsType()\n\t\t\treturn // don't allow ...type \"|\" ...\n\n\t\tcase token.PERIOD:\n\t\t\t// name \".\" ...\n\t\t\tf.typ = p.parseQualifiedIdent(f.name)\n\t\t\tf.name = nil\n\t\t}\n\n\tcase token.MUL, token.ARROW, token.FUNC, token.LBRACK, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:\n\t\t// type\n\t\tf.typ = p.parseType()\n\n\tcase token.ELLIPSIS:\n\t\t// \"...\" type\n\t\t// (always accepted)\n\t\tf.typ = p.parseDotsType()\n\t\treturn // don't allow ...type \"|\" ...\n\n\tdefault:\n\t\t// TODO(rfindley): this is incorrect in the case of type parameter lists\n\t\t//                 (should be \"']'\" in that case)\n\t\tp.errorExpected(p.pos, \"')'\", 2)\n\t\tp.advance(exprEnd)\n\t}\n\n\tif p.tok == token.QUESTION {\n\t\tf.optional = p.pos\n\t\tp.next()\n\t}\n\n\treturn\n}\n\nfunc (p *parser) parseQualifiedIdent(ident *ast.Ident) ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"QualifiedIdent\"))\n\t}\n\n\ttyp := p.parseTypeName(ident)\n\tif p.tok == token.LBRACK {\n\t\ttyp = p.parseTypeInstance(typ)\n\t}\n\n\treturn typ\n}\n\nfunc (p *parser) parseDotsType() *ast.Ellipsis {\n\tif p.trace {\n\t\tdefer un(trace(p, \"DotsType\"))\n\t}\n\n\tpos := p.expect(token.ELLIPSIS)\n\telt := p.parseType()\n\n\treturn &ast.Ellipsis{Ellipsis: pos, Elt: elt}\n}\n\nfunc (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldList {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Parameters\"))\n\t}\n\t_ = ellipsisOk\n\n\tvar params []*ast.Field\n\tlparen := p.expect(token.LPAREN)\n\n\tif p.tok != token.RPAREN {\n\t\tparams = p.parseParameterList(scope, nil, nil, token.RPAREN)\n\t}\n\trparen := p.expect(token.RPAREN)\n\n\treturn &ast.FieldList{Opening: lparen, List: params, Closing: rparen}\n}\n\nfunc (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Result\"))\n\t}\n\n\tif p.tok == token.LPAREN {\n\t\treturn p.parseParameters(scope, false)\n\t}\n\n\ttyp := p.tryType()\n\tif typ != nil {\n\t\tlist := make([]*ast.Field, 1)\n\t\tlist[0] = &ast.Field{Type: typ}\n\t\treturn &ast.FieldList{List: list}\n\t}\n\n\treturn nil\n}\n\nfunc (p *parser) parseSignature(scope *ast.Scope) (params, results *ast.FieldList) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Signature\"))\n\t}\n\n\tparams = p.parseParameters(scope, true)\n\tresults = p.parseResult(scope)\n\n\treturn\n}\n\nfunc (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"FuncType\"))\n\t}\n\n\tpos := p.expect(token.FUNC)\n\tscope := ast.NewScope(p.topScope) // function scope\n\tparams, results := p.parseSignature(scope)\n\n\treturn &ast.FuncType{Func: pos, Params: params, Results: results}, scope\n}\n\nfunc (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {\n\tif p.trace {\n\t\tdefer un(trace(p, \"MethodSpec\"))\n\t}\n\n\tdoc := p.leadComment\n\tvar idents []*ast.Ident\n\tvar typ ast.Expr\n\tx := p.parseTypeName(nil)\n\tif ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {\n\t\t// method\n\t\tidents = []*ast.Ident{ident}\n\t\tscope := ast.NewScope(nil) // method scope\n\t\tparams, results := p.parseSignature(scope)\n\t\ttyp = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}\n\t} else {\n\t\t// embedded interface\n\t\ttyp = x\n\t\tp.resolve(typ)\n\t}\n\tp.expectSemi() // call before accessing p.linecomment\n\n\tspec := &ast.Field{Doc: doc, Names: idents, Type: typ, Comment: p.lineComment}\n\tp.declare(spec, nil, scope, ast.Fun, idents...)\n\n\treturn spec\n}\n\nfunc (p *parser) parseInterfaceType() *ast.InterfaceType {\n\tif p.trace {\n\t\tdefer un(trace(p, \"InterfaceType\"))\n\t}\n\n\tpos := p.expect(token.INTERFACE)\n\tlbrace := p.expect(token.LBRACE)\n\tscope := ast.NewScope(nil) // interface scope\n\tvar list []*ast.Field\n\tfor p.tok == token.IDENT {\n\t\tlist = append(list, p.parseMethodSpec(scope))\n\t}\n\trbrace := p.expect(token.RBRACE)\n\n\treturn &ast.InterfaceType{\n\t\tInterface: pos,\n\t\tMethods: &ast.FieldList{\n\t\t\tOpening: lbrace,\n\t\t\tList:    list,\n\t\t\tClosing: rbrace,\n\t\t},\n\t}\n}\n\nfunc (p *parser) parseMapType() *ast.MapType {\n\tif p.trace {\n\t\tdefer un(trace(p, \"MapType\"))\n\t}\n\n\tpos := p.expect(token.MAP)\n\tp.expect(token.LBRACK)\n\tkey := p.parseType()\n\tp.expect(token.RBRACK)\n\tvalue := p.parseType()\n\tif debugParseOutput {\n\t\tlog.Printf(\"ast.MapType{Key: %v, Value: %v}\\n\", key, value)\n\t}\n\treturn &ast.MapType{Map: pos, Key: key, Value: value}\n}\n\nfunc (p *parser) parseChanType() *ast.ChanType {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ChanType\"))\n\t}\n\n\tpos := p.pos\n\tdir := ast.SEND | ast.RECV\n\tvar arrow token.Pos\n\tif p.tok == token.CHAN {\n\t\tp.next()\n\t\tif p.tok == token.ARROW {\n\t\t\tarrow = p.pos\n\t\t\tp.next()\n\t\t\tdir = ast.SEND\n\t\t}\n\t} else {\n\t\tarrow = p.expect(token.ARROW)\n\t\tp.expect(token.CHAN)\n\t\tdir = ast.RECV\n\t}\n\tvalue := p.parseType()\n\n\treturn &ast.ChanType{Begin: pos, Arrow: arrow, Dir: dir, Value: value}\n}\n\nfunc (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"TypeInstance\"))\n\t}\n\n\topening := p.expect(token.LBRACK)\n\tp.exprLev++\n\tvar list []ast.Expr\n\tfor p.tok != token.RBRACK && p.tok != token.EOF {\n\t\tlist = append(list, p.parseType())\n\t\tif !p.atComma(\"type argument list\", token.RBRACK) {\n\t\t\tbreak\n\t\t}\n\t\tp.next()\n\t}\n\tp.exprLev--\n\n\tclosing := p.expectClosing(token.RBRACK, \"type argument list\")\n\n\tif len(list) == 0 {\n\t\tp.errorExpected(closing, \"type argument list\", 2)\n\t\treturn &ast.IndexExpr{\n\t\t\tX:      typ,\n\t\t\tLbrack: opening,\n\t\t\tIndex:  &ast.BadExpr{From: opening + 1, To: closing},\n\t\t\tRbrack: closing,\n\t\t}\n\t}\n\n\treturn packIndexExpr(typ, opening, list, closing)\n}\n\nfunc (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident, state int) (*ast.Ident, ast.Expr) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ArrayFieldOrTypeInstance\"))\n\t}\n\n\tlbrack := p.expect(token.LBRACK)\n\ttrailingComma := token.NoPos // if valid, the position of a trailing comma preceding the ']'\n\tvar args []ast.Expr\n\tif p.tok != token.RBRACK {\n\t\tp.exprLev++\n\t\targs = append(args, p.parseRHS())\n\t\tfor p.tok == token.COMMA {\n\t\t\tcomma := p.pos\n\t\t\tp.next()\n\t\t\tif p.tok == token.RBRACK {\n\t\t\t\ttrailingComma = comma\n\t\t\t\tbreak\n\t\t\t}\n\t\t\targs = append(args, p.parseRHS())\n\t\t}\n\t\tp.exprLev--\n\t}\n\trbrack := p.expect(token.RBRACK)\n\n\tif len(args) == 0 {\n\t\t// x []E\n\t\telt := p.parseType()\n\t\treturn x, &ast.ArrayType{Lbrack: lbrack, Elt: elt}\n\t}\n\n\t// x [P]E or x[P]\n\tif len(args) == 1 {\n\t\telt, _ := p.tryIdentOrType(state, nil)\n\t\tif elt != nil {\n\t\t\t// x [P]E\n\t\t\tif trailingComma.IsValid() {\n\t\t\t\t// Trailing commas are invalid in array type fields.\n\t\t\t\tp.error(trailingComma, \"unexpected comma; expecting ]\")\n\t\t\t}\n\t\t\treturn x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}\n\t\t}\n\t}\n\n\t// x[P], x[P1, P2], ...\n\treturn nil, packIndexExpr(x, lbrack, args, rbrack)\n}\n\n// state = stateArrayTypeOrSliceLit | stateTypeOrSliceOp | stateType | ...\n// If the result is an identifier, it is not resolved.\nfunc (p *parser) tryIdentOrType(state int, len ast.Expr) (ast.Expr, int) {\n\tswitch p.tok {\n\tcase token.IDENT:\n\t\ttyp := p.parseTypeName(nil)\n\t\tif p.tok == token.LBRACK {\n\t\t\ttyp = p.parseTypeInstance(typ)\n\t\t}\n\t\treturn typ, resultIdent\n\tcase token.LBRACK:\n\t\treturn p.parseArrayTypeOrSliceLit(state, len)\n\tcase token.STRUCT:\n\t\treturn p.parseStructType(), resultType\n\tcase token.MUL:\n\t\treturn p.parsePointerType(), resultType\n\tcase token.FUNC:\n\t\ttyp, _ := p.parseFuncType()\n\t\treturn typ, resultType\n\tcase token.INTERFACE:\n\t\treturn p.parseInterfaceType(), resultType\n\tcase token.MAP:\n\t\treturn p.parseMapType(), resultType\n\tcase token.CHAN, token.ARROW:\n\t\treturn p.parseChanType(), resultType\n\tcase token.LPAREN:\n\t\treturn p.parseTupleType()\n\t}\n\n\t// no type found\n\treturn nil, resultNone\n}\n\n// parseTupleType parses a tuple type or parenthesized type expression.\n//\n// Syntax:\n//\n//\t()                              - empty tuple, equivalent to struct{}\n//\t(T)                             - parenthesized type, degenerates to T (not a tuple)\n//\t(T1, T2, ..., TN)               - anonymous tuple type\n//\t(name1 T1, name2 T2, ...)       - named tuple type (names are compile-time only)\n//\t(x, y T)                        - type shorthand, equivalent to (x T, y T)\n//\n// Returns the parsed type and result code.\nfunc (p *parser) parseTupleType() (ast.Expr, int) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"TupleType\"))\n\t}\n\n\tlparen := p.pos\n\tp.next() // consume '('\n\n\t// Parse tuple fields similar to parameter list parsing\n\tfields := p.parseTupleFieldList()\n\n\trparen := p.expect(token.RPAREN)\n\n\t// Single-element tuple with no name degenerates to the type itself\n\t// (T) is just a parenthesized type expression, not a tuple\n\tif len(fields) == 1 && len(fields[0].Names) == 0 {\n\t\treturn &ast.ParenExpr{Lparen: lparen, X: fields[0].Type, Rparen: rparen}, resultParenType\n\t}\n\n\t// Multi-element or named tuple\n\treturn &ast.TupleType{\n\t\tLparen: lparen,\n\t\tFields: &ast.FieldList{Opening: lparen, List: fields, Closing: rparen},\n\t\tRparen: rparen,\n\t}, resultType\n}\n\n// parseTupleFieldList parses a comma-separated list of tuple fields.\n// Each field can be:\n//   - A type alone: int, string, etc.\n//   - A named field: x int, name string, etc.\n//   - Multiple names with shared type: x, y int (shorthand for x int, y int)\n//\n// The function handles type distribution similar to Go's function parameter syntax.\nfunc (p *parser) parseTupleFieldList() (params []*ast.Field) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"TupleFieldList\"))\n\t}\n\n\t// Use shared field struct for type distribution\n\tvar list []field\n\tvar named int // count of fields with explicit name and type\n\n\tfor p.tok != token.RPAREN && p.tok != token.EOF {\n\t\tvar f field\n\n\t\tswitch p.tok {\n\t\tcase token.IDENT:\n\t\t\t// Could be a name or a type\n\t\t\tf.name = p.parseIdent()\n\n\t\t\t// Check what follows the identifier\n\t\t\tswitch p.tok {\n\t\t\tcase token.IDENT, token.MUL, token.ARROW, token.FUNC, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:\n\t\t\t\t// name followed by type: \"x int\" or \"x *int\" etc.\n\t\t\t\tf.typ = p.parseType()\n\t\t\t\tnamed++\n\n\t\t\tcase token.LBRACK:\n\t\t\t\t// Could be \"name [n]T\" or \"name []T\" or just an array/slice type\n\t\t\t\tf.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name, stateType)\n\t\t\t\tif f.typ != nil {\n\t\t\t\t\tnamed++\n\t\t\t\t}\n\n\t\t\tcase token.PERIOD:\n\t\t\t\t// Qualified type name: \"pkg.Type\"\n\t\t\t\tf.typ = p.parseQualifiedIdent(f.name)\n\t\t\t\tf.name = nil\n\n\t\t\tdefault:\n\t\t\t\t// Just an identifier - could be a type name or a field name\n\t\t\t\t// Will be resolved later during type distribution\n\t\t\t}\n\n\t\tcase token.MUL, token.ARROW, token.FUNC, token.LBRACK, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:\n\t\t\t// Type without name\n\t\t\tf.typ = p.parseType()\n\t\t}\n\n\t\tif f.typ == nil && f.name == nil {\n\t\t\tbreak // Not a valid tuple field, stop parsing\n\t\t}\n\n\t\tlist = append(list, f)\n\t\tif !p.atComma(\"tuple type\", token.RPAREN) {\n\t\t\tbreak\n\t\t}\n\t\tp.next() // consume ','\n\t}\n\n\tif len(list) == 0 {\n\t\treturn nil\n\t}\n\n\t// distribute parameter types\n\tif named == 0 {\n\t\t// all unnamed => found names are type names\n\t\tfor i := 0; i < len(list); i++ {\n\t\t\tpar := &list[i]\n\t\t\tif typ := par.name; typ != nil {\n\t\t\t\tpar.typ = typ\n\t\t\t\tpar.name = nil\n\t\t\t}\n\t\t}\n\t} else if named != len(list) {\n\t\t// some named => all must be named\n\t\tok := true\n\t\tvar typ ast.Expr\n\t\tfor i := len(list) - 1; i >= 0; i-- {\n\t\t\tif par := &list[i]; par.typ != nil {\n\t\t\t\ttyp = par.typ\n\t\t\t\tif par.name == nil {\n\t\t\t\t\tok = false\n\t\t\t\t\tn := ast.NewIdent(\"_\")\n\t\t\t\t\tn.NamePos = typ.Pos() // correct position\n\t\t\t\t\tpar.name = n\n\t\t\t\t}\n\t\t\t} else if typ != nil {\n\t\t\t\tpar.typ = typ\n\t\t\t} else {\n\t\t\t\t// par.typ == nil && typ == nil => we only have a par.name\n\t\t\t\tok = false\n\t\t\t\tpar.typ = &ast.BadExpr{From: par.name.Pos(), To: p.pos}\n\t\t\t}\n\t\t}\n\t\tif !ok {\n\t\t\tp.error(list[0].name.Pos(), \"mixed named and unnamed fields in tuple type\")\n\t\t}\n\t}\n\n\t// convert list []*ast.Field\n\tif named == 0 {\n\t\t// parameter list consists of types only\n\t\tfor _, par := range list {\n\t\t\tassert(par.typ != nil, \"nil type in unnamed field list\")\n\t\t\tparams = append(params, &ast.Field{Type: par.typ})\n\t\t}\n\t\treturn\n\t}\n\n\t// parameter list consists of named parameters with types\n\tvar names []*ast.Ident\n\tvar typ ast.Expr\n\taddFields := func() {\n\t\tassert(typ != nil, \"nil type in named field list\")\n\t\tfield := &ast.Field{Names: names, Type: typ}\n\t\tparams = append(params, field)\n\t\tnames = nil\n\t}\n\tfor _, par := range list {\n\t\tif par.typ != typ {\n\t\t\tif len(names) > 0 {\n\t\t\t\taddFields()\n\t\t\t}\n\t\t\ttyp = par.typ\n\t\t}\n\t\tnames = append(names, par.name)\n\t}\n\tif len(names) > 0 {\n\t\taddFields()\n\t}\n\treturn\n}\n\nfunc (p *parser) tryType() ast.Expr {\n\ttyp, _ := p.tryIdentOrType(stateType, nil)\n\tif typ != nil {\n\t\tp.resolve(typ)\n\t}\n\treturn typ\n}\n\n// ----------------------------------------------------------------------------\n// Blocks\n\nfunc (p *parser) parseStmtList() (list []ast.Stmt) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"StatementList\"))\n\t}\n\n\tfor p.tok != token.CASE && p.tok != token.DEFAULT && p.tok != token.RBRACE && p.tok != token.EOF {\n\t\tlist = append(list, p.parseStmt(flagAllowCmd))\n\t}\n\n\treturn\n}\n\nfunc (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Body\"))\n\t}\n\n\tlbrace := p.expect(token.LBRACE)\n\tp.topScope = scope // open function scope\n\tp.openLabelScope()\n\tlist := p.parseStmtList()\n\tp.closeLabelScope()\n\tp.closeScope()\n\trbrace := p.expect2(token.RBRACE)\n\n\treturn &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}\n}\n\nfunc (p *parser) parseBlockStmt() *ast.BlockStmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"BlockStmt\"))\n\t}\n\n\tlbrace := p.expect(token.LBRACE)\n\tp.openScope()\n\tlist := p.parseStmtList()\n\tp.closeScope()\n\trbrace := p.expect2(token.RBRACE)\n\n\treturn &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}\n}\n\n// ----------------------------------------------------------------------------\n// Expressions\n\nfunc (p *parser) parseFuncTypeOrLit() ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"FuncTypeOrLit\"))\n\t}\n\n\ttyp, scope := p.parseFuncType()\n\tif p.tok != token.LBRACE {\n\t\t// function type only\n\t\treturn typ\n\t}\n\n\tp.exprLev++\n\tbody := p.parseBody(scope)\n\tp.exprLev--\n\n\treturn &ast.FuncLit{Type: typ, Body: body}\n}\n\nfunc (p *parser) stringLit(pos token.Pos, val string) *ast.StringLitEx {\n\tparts := p.stringLitEx(nil, pos+1, val[1:len(val)-1])\n\tif parts != nil {\n\t\treturn &ast.StringLitEx{Parts: parts}\n\t}\n\treturn nil\n}\n\nfunc (p *parser) stringLitEx(parts []any, pos token.Pos, text string) []any {\n\textra := false\nloop:\n\tat := strings.IndexByte(text, '$')\n\tif at < 0 || at+1 == len(text) { // no '$' or end with '$'\n\t\tif extra {\n\t\t\tgoto normal\n\t\t}\n\t\treturn nil\n\t}\n\tswitch text[at+1] {\n\tcase '{': // ${\n\t\tfrom := at + 2\n\t\tleft := text[from:]\n\t\tif left == \"\" { // \"...${\" (string end with \"${\")\n\t\t\tgoto normal\n\t\t}\n\t\tend := strings.IndexByte(left, '}')\n\t\tif end < 0 {\n\t\t\tp.error(pos+token.Pos(at+1), \"invalid $ expression: ${ doesn't end with }\")\n\t\t\tgoto normal\n\t\t}\n\t\tif at != 0 {\n\t\t\tparts = append(parts, text[:at])\n\t\t}\n\t\tto := pos + token.Pos(from+end)\n\t\tparts = p.stringLitExpr(parts, pos+token.Pos(from), to)\n\t\tpos = to + 1\n\t\ttext = left[end+1:]\n\tcase '$': // $$\n\t\tparts = append(parts, text[:at+2])\n\t\tpos += token.Pos(at + 2)\n\t\ttext = text[at+2:]\n\tdefault:\n\t\tif extra || hasExtra(text[at+1:]) {\n\t\t\tp.error(pos+token.Pos(at), \"invalid $ expression: neither `${ ... }` nor `$$`\")\n\t\t}\n\t\treturn nil\n\t}\n\tif text != \"\" {\n\t\textra = true\n\t\tgoto loop\n\t}\n\treturn parts\nnormal:\n\tparts = append(parts, text)\n\treturn parts\n}\n\nfunc hasExtra(text string) bool {\n\tfor {\n\t\tat := strings.IndexByte(text, '$')\n\t\tif at < 0 || at+1 == len(text) { // no '$' or end with '$'\n\t\t\treturn false\n\t\t}\n\t\tch := text[at+1]\n\t\tif ch == '{' || ch == '$' {\n\t\t\treturn true\n\t\t}\n\t\ttext = text[at+2:]\n\t}\n}\n\nfunc (p *parser) stringLitExpr(parts []any, off, end token.Pos) []any {\n\tfile := p.file\n\tbase := file.Base()\n\tsrc := p.scanner.CodeTo(int(end) - base)\n\texpr, err := ParseExprEx(file, src, int(off)-base, 0)\n\tif err != nil {\n\t\tp.errors = append(p.errors, err...)\n\t\texpr = &ast.BadExpr{From: off, To: end}\n\t}\n\tparts = append(parts, expr)\n\treturn parts\n}\n\nfunc (p *parser) domainTextLitEx(off, end token.Pos) *ast.DomainTextLitEx {\n\tfile := p.file\n\tbase := file.Base()\n\tsrc := p.scanner.CodeTo(int(end) - base)\n\n\tvar args []ast.Expr\n\tvar sp parser\n\tsp.initSub(file, src, int(off)-base, 0)\n\n\tfor {\n\t\texpr := sp.parseRHS()\n\t\targs = append(args, expr)\n\t\tif sp.tok != token.COMMA {\n\t\t\tbreak\n\t\t}\n\t\tsp.next()\n\t}\n\tsp.expect(token.SEMICOLON)\n\treturn &ast.DomainTextLitEx{\n\t\tArgs:   args,\n\t\tRawPos: sp.pos,\n\t\tRaw:    string(src[int(sp.pos)-base:]),\n\t}\n}\n\nfunc (p *parser) tplLit(off, end token.Pos) any {\n\tfile := p.file\n\tbase := file.Base()\n\tsrc := p.scanner.CodeTo(int(end) - base)\n\texpr, err := tpl.ParseEx(file, src, int(off)-base, &tpl.Config{\n\t\tParseRetProc: parseTplRetProc,\n\t})\n\tif err != nil {\n\t\tp.errors = append(p.errors, err...)\n\t\treturn nil\n\t}\n\treturn expr\n}\n\nfunc parseTplRetProc(file *token.File, src []byte, offset int) (tplast.Node, scanner.ErrorList) {\n\treturn ParseExprEx(file, src, offset, 0)\n}\n\n// parseOperand may return an expression or a raw type (incl. array\n// types of the form [...]T. Callers must verify the result.\n// If lhs is set and the result is an identifier, it is not resolved.\n// flags support flagInLHS, flagAllowCmd\nfunc (p *parser) parseOperand(flags int) (x ast.Expr, exprKind int) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Operand\"))\n\t}\n\n\tswitch p.tok {\n\tcase token.IDENT:\n\t\tident := p.parseIdent()\n\t\tif p.tok == token.STRING && p.pos == ident.End() && p.lit[0] == '`' {\n\t\t\t// domain text: tpl`...`\n\t\t\tvar pos, lit = p.pos, p.lit\n\t\t\tvar extra any\n\t\t\tif ident.Name == \"tpl\" {\n\t\t\t\textra = p.tplLit(pos+1, pos+token.Pos(len(lit))-1)\n\t\t\t} else if strings.HasPrefix(lit, \"`> \") { // domainTag`> ...`\n\t\t\t\textra = p.domainTextLitEx(pos+3, pos+token.Pos(len(lit))-1)\n\t\t\t}\n\t\t\tx = &ast.DomainTextLit{\n\t\t\t\tDomain:   ident,\n\t\t\t\tValuePos: pos,\n\t\t\t\tValue:    lit,\n\t\t\t\tExtra:    extra,\n\t\t\t}\n\t\t\tif debugParseOutput {\n\t\t\t\tlog.Printf(\"ast.DomainTextLit{Domain: %s, Value: %s}\\n\", ident.Name, lit)\n\t\t\t}\n\t\t\tp.next()\n\t\t} else {\n\t\t\tx = ident\n\t\t\tif flags&flagInLHS == 0 { // not inLHS\n\t\t\t\tp.resolve(x)\n\t\t\t}\n\t\t}\n\t\treturn\n\n\tcase token.STRING, token.CSTRING, token.PYSTRING, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.RAT:\n\t\tbl := &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}\n\t\tif p.tok == token.STRING && len(p.lit) > 1 {\n\t\t\tbl.Extra = p.stringLit(p.pos, p.lit)\n\t\t}\n\t\tp.next()\n\t\tif p.tok == token.UNIT {\n\t\t\tnu := &ast.NumberUnitLit{\n\t\t\t\tValuePos: bl.ValuePos,\n\t\t\t\tKind:     bl.Kind,\n\t\t\t\tValue:    bl.Value,\n\t\t\t\tUnit:     p.lit,\n\t\t\t}\n\t\t\tx = nu\n\t\t\tif debugParseOutput {\n\t\t\t\tlog.Printf(\"ast.NumberUnitLit{Kind: %v, Value: %v, Unit: %v}\\n\", nu.Kind, nu.Value, nu.Unit)\n\t\t\t}\n\t\t\tp.next()\n\t\t} else {\n\t\t\tx = bl\n\t\t\tif debugParseOutput {\n\t\t\t\tlog.Printf(\"ast.BasicLit{Kind: %v, Value: %v}\\n\", bl.Kind, bl.Value)\n\t\t\t}\n\t\t}\n\t\treturn\n\n\tcase token.LPAREN:\n\t\tlparen := p.pos\n\t\tp.next()\n\t\tif p.tok == token.RPAREN { // () => expr\n\t\t\tp.next()\n\t\t\treturn &ast.TupleLit{Lparen: lparen, Rparen: p.pos}, exprTuple\n\t\t}\n\t\tp.exprLev++\n\t\tx = p.parseRHSOrType() // types may be parenthesized: (some type)\n\t\tif p.tok == token.COMMA || p.tok == token.ELLIPSIS {\n\t\t\t// (x, y, ...) => expr\n\t\t\titems := make([]ast.Expr, 1, 2)\n\t\t\titems[0] = x\n\t\t\tfor p.tok == token.COMMA {\n\t\t\t\tp.next()\n\t\t\t\titems = append(items, p.parseRHSOrType())\n\t\t\t}\n\t\t\tt := &ast.TupleLit{Lparen: lparen, Elts: items, Rparen: p.pos}\n\t\t\tif p.tok == token.ELLIPSIS {\n\t\t\t\tt.Ellipsis = p.pos\n\t\t\t\tp.next()\n\t\t\t}\n\t\t\tp.exprLev--\n\t\t\tp.expect(token.RPAREN)\n\t\t\treturn t, exprTuple\n\t\t}\n\t\tp.exprLev--\n\t\trparen := p.expect(token.RPAREN)\n\t\tif debugParseOutput {\n\t\t\tlog.Printf(\"ast.ParenExpr{X: %v}\\n\", x)\n\t\t}\n\t\treturn &ast.ParenExpr{Lparen: lparen, X: x, Rparen: rparen}, 0\n\n\tcase token.FUNC:\n\t\treturn p.parseFuncTypeOrLit(), 0\n\n\tcase token.LBRACE:\n\t\tif flags&flagInLHS == 0 { // in RHS: mapLit - {k1: v1, k2: v2, ...}\n\t\t\treturn p.parseLiteralValueOrMapComprehension(), 0\n\t\t}\n\n\tcase token.MAP:\n\t\toldpos, oldlit := p.pos, p.lit // XGo: save token to allow map() as a function\n\t\tp.next()\n\t\tpos, tok := p.pos, p.tok\n\t\tp.unget(oldpos, token.MAP, oldlit)\n\t\tif tok == token.LBRACK && (flags&flagAllowCmd == 0 || oldpos+3 == pos) {\n\t\t\tbreak\n\t\t}\n\t\tfallthrough\n\tcase token.GOTO, token.TYPE, token.BREAK, token.CONTINUE, token.FALLTHROUGH:\n\t\t// token.RANGE, token.IMPORT, token.SELECT, token.INTERFACE:\n\t\t// XGo: allow goto() as a function\n\t\tp.tok = token.IDENT\n\t\tx = p.parseIdent()\n\t\tif flags&flagInLHS == 0 { // in RHS\n\t\t\tp.resolve(x)\n\t\t}\n\t\treturn\n\n\tcase token.ENV:\n\t\treturn p.parseEnvExpr(), 0\n\t}\n\n\ttyp, result := p.tryIdentOrType(stateArrayTypeOrSliceLit, nil)\n\tif (result & resultExprFlags) != 0 { // is an expr, not a type\n\t\treturn typ, 0\n\t}\n\tif typ != nil {\n\t\t// could be type for composite literal or conversion\n\t\t_, isIdent := typ.(*ast.Ident)\n\t\tassert(!isIdent, \"type cannot be identifier\")\n\t\treturn typ, 0\n\t}\n\n\t// we have an error\n\tpos := p.pos\n\tp.errorExpected(pos, \"operand\", 2)\n\tp.advance(stmtStart)\n\treturn &ast.BadExpr{From: pos, To: p.pos}, 0\n}\n\nfunc (p *parser) parseEnvExpr() (ret *ast.EnvExpr) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"EnvExpr\"))\n\t}\n\tret = &ast.EnvExpr{TokPos: p.pos}\n\tp.next()\n\tswitch p.tok {\n\tcase token.LBRACE: // ${name}\n\t\tret.Lbrace = p.pos\n\t\tp.next()\n\t\tret.Name = p.parseIdent()\n\t\tret.Rbrace = p.expect(token.RBRACE)\n\tcase token.STRING: // $\"attr-name\"\n\t\tret.Name = &ast.Ident{NamePos: p.pos, Name: p.lit}\n\t\tp.next()\n\tdefault: // $name\n\t\tret.Name = p.parseIdent()\n\t}\n\treturn\n}\n\nfunc (p *parser) parseSelector(x ast.Expr) ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Selector\"))\n\t}\n\n\tsel := p.parseIdent()\n\n\treturn &ast.SelectorExpr{X: x, Sel: sel}\n}\n\nfunc (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"TypeAssertion\"))\n\t}\n\n\tlparen := p.expect(token.LPAREN)\n\tvar typ ast.Expr\n\tif p.tok == token.TYPE {\n\t\t// type switch: typ == nil\n\t\tp.next()\n\t} else {\n\t\ttyp = p.parseType()\n\t}\n\trparen := p.expect(token.RPAREN)\n\n\treturn &ast.TypeAssertExpr{X: x, Type: typ, Lparen: lparen, Rparen: rparen}\n}\n\nfunc (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"IndexOrSlice\"))\n\t}\n\tlbrack := p.expect(token.LBRACK)\n\tp.exprLev++\n\n\tvar idx ast.Expr\n\tif p.tok != token.COLON {\n\t\tidx = p.parseRHS()\n\t}\n\treturn p.parseIndexOrSliceContinue(x, lbrack, idx)\n}\n\nfunc (p *parser) parseIndexOrSliceContinue(x ast.Expr, lbrack token.Pos, idx ast.Expr) ast.Expr {\n\tconst N = 3 // change the 3 to 2 to disable 3-index slices\n\tvar args []ast.Expr\n\tvar index [N]ast.Expr\n\tvar colons [N - 1]token.Pos\n\tif idx != nil {\n\t\tindex[0] = idx\n\t}\n\tncolons := 0\n\tswitch p.tok {\n\tcase token.COLON:\n\t\t// slice expression\n\t\tfor p.tok == token.COLON && ncolons < len(colons) {\n\t\t\tcolons[ncolons] = p.pos\n\t\t\tncolons++\n\t\t\tp.next()\n\t\t\tif p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {\n\t\t\t\tindex[ncolons] = p.parseRHS()\n\t\t\t}\n\t\t}\n\tcase token.COMMA:\n\t\t// instance expression\n\t\targs = append(args, index[0])\n\t\tfor p.tok == token.COMMA {\n\t\t\tp.next()\n\t\t\tif p.tok != token.RBRACK && p.tok != token.EOF {\n\t\t\t\targs = append(args, p.parseType())\n\t\t\t}\n\t\t}\n\t}\n\n\tp.exprLev--\n\trbrack := p.expect(token.RBRACK)\n\n\tif ncolons > 0 {\n\t\t// slice expression\n\t\tslice3 := false\n\t\tif ncolons == 2 {\n\t\t\tslice3 = true\n\t\t\t// Check presence of middle and final index here rather than during type-checking\n\t\t\t// to prevent erroneous programs from passing through gofmt (was issue 7305).\n\t\t\tif index[1] == nil {\n\t\t\t\tp.error(colons[0], \"middle index required in 3-index slice\")\n\t\t\t\tindex[1] = &ast.BadExpr{From: colons[0] + 1, To: colons[1]}\n\t\t\t}\n\t\t\tif index[2] == nil {\n\t\t\t\tp.error(colons[1], \"final index required in 3-index slice\")\n\t\t\t\tindex[2] = &ast.BadExpr{From: colons[1] + 1, To: rbrack}\n\t\t\t}\n\t\t}\n\t\treturn &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: slice3, Rbrack: rbrack}\n\t}\n\n\tif len(args) == 0 {\n\t\tif debugParseOutput {\n\t\t\tlog.Printf(\"ast.IndexExpr{X: %v, Index: %v}\\n\", x, index[0])\n\t\t}\n\t\t// index expression\n\t\treturn &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}\n\t}\n\n\t// instance expression\n\treturn packIndexExpr(x, lbrack, args, rbrack)\n}\n\nfunc packIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token.Pos) ast.Expr {\n\tswitch len(exprs) {\n\tcase 0:\n\t\tpanic(\"internal error: packIndexExpr with empty expr slice\")\n\tcase 1:\n\t\tif debugParseOutput {\n\t\t\tlog.Printf(\"ast.IndexExpr{X: %v, Index: %v}\\n\", x, exprs[0])\n\t\t}\n\t\treturn &ast.IndexExpr{\n\t\t\tX:      x,\n\t\t\tLbrack: lbrack,\n\t\t\tIndex:  exprs[0],\n\t\t\tRbrack: rbrack,\n\t\t}\n\tdefault:\n\t\tif debugParseOutput {\n\t\t\tlog.Printf(\"ast.IndexListExpr{X: %v, Index: %v}\\n\", x, exprs)\n\t\t}\n\t\treturn &ast.IndexListExpr{\n\t\t\tX:       x,\n\t\t\tLbrack:  lbrack,\n\t\t\tIndices: exprs,\n\t\t\tRbrack:  rbrack,\n\t\t}\n\t}\n}\n\nfunc (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"CallOrConversion\"))\n\t}\n\tvar lparen, rparen token.Pos\n\tvar endTok token.Token\n\tif isCmd {\n\t\tendTok = token.SEMICOLON\n\t} else {\n\t\tlparen, endTok = p.expect(token.LPAREN), token.RPAREN\n\t}\n\tp.exprLev++\n\tvar args []ast.Expr\n\tvar kwargs []*ast.KwargExpr\n\tvar ellipsis token.Pos\n\tfor p.tok != endTok && p.tok != token.EOF && !ellipsis.IsValid() {\n\t\tflags := flagAllowKwargExpr\n\t\texpr, exprKind := p.parseRHSOrTypeEx(flags)\n\t\tif exprKind == exprKwarg {\n\t\t\tkwargs = append(kwargs, expr.(*ast.KwargExpr))\n\t\t} else {\n\t\t\tif len(kwargs) > 0 {\n\t\t\t\tp.error(expr.Pos(), \"positional argument follows keyword argument\")\n\t\t\t}\n\t\t\targs = append(args, expr) // builtins may expect a type: make(some type, ...)\n\t\t\tif p.tok == token.ELLIPSIS {\n\t\t\t\tellipsis = p.pos\n\t\t\t\tp.next()\n\t\t\t}\n\t\t}\n\t\tif isCmd && p.tok == token.RBRACE {\n\t\t\tbreak\n\t\t}\n\t\tif !p.atComma(\"argument list\", endTok) {\n\t\t\tbreak\n\t\t}\n\t\tp.next()\n\t}\n\tp.exprLev--\n\tvar noParenEnd token.Pos\n\tif isCmd {\n\t\tnoParenEnd = p.pos\n\t} else if rparen == token.NoPos {\n\t\trparen = p.expectClosing(token.RPAREN, \"argument list\")\n\t}\n\tif debugParseOutput {\n\t\tlog.Printf(\"ast.CallExpr{Fun: %v, Ellipsis: %v, isCmd: %v}\\n\", fun, ellipsis != 0, isCmd)\n\t}\n\treturn &ast.CallExpr{\n\t\tFun: fun, Lparen: lparen, Args: args, Ellipsis: ellipsis, Kwargs: kwargs, Rparen: rparen, NoParenEnd: noParenEnd}\n}\n\n// flags support flagInLHS (keyOk = true)\nfunc (p *parser) parseValue(flags int) ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Element\"))\n\t}\n\n\tif p.tok == token.LBRACE {\n\t\treturn p.parseLiteralValueOrMapComprehension()\n\t}\n\n\t// Because the parser doesn't know the composite literal type, it cannot\n\t// know if a key that's an identifier is a struct field name or a name\n\t// denoting a value. The former is not resolved by the parser or the\n\t// resolver.\n\t//\n\t// Instead, _try_ to resolve such a key if possible. If it resolves,\n\t// it a) has correctly resolved, or b) incorrectly resolved because\n\t// the key is a struct field with a name matching another identifier.\n\t// In the former case we are done, and in the latter case we don't\n\t// care because the type checker will do a separate field lookup.\n\t//\n\t// If the key does not resolve, it a) must be defined at the top\n\t// level in another file of the same package, the universe scope, or be\n\t// undeclared; or b) it is a struct field. In the former case, the type\n\t// checker can do a top-level lookup, and in the latter case it will do\n\t// a separate field lookup.\n\tx := p.checkExpr(p.parseExpr(flags))\n\tif flags&flagInLHS != 0 { // keyOk\n\t\tif p.tok == token.COLON {\n\t\t\t// Try to resolve the key but don't collect it\n\t\t\t// as unresolved identifier if it fails so that\n\t\t\t// we don't get (possibly false) errors about\n\t\t\t// undeclared names.\n\t\t\tp.tryResolve(x, false)\n\t\t} else {\n\t\t\t// not a key\n\t\t\tp.resolve(x)\n\t\t}\n\t}\n\n\treturn x\n}\n\nfunc (p *parser) parseElement() ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Element\"))\n\t}\n\n\tx := p.parseValue(flagInLHS)\n\tif p.tok == token.COLON {\n\t\tcolon := p.pos\n\t\tp.next()\n\t\tx = &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseValue(0)}\n\t}\n\n\treturn x\n}\n\n// {k1: v1, k2: v2, ...}\n// {for k, v <- listOrMap, cond}\n// {expr for k, v <- listOrMap, cond}\n// {kexpr: vexpr for k, v <- listOrMap, cond}\nfunc (p *parser) parseLiteralValueOrMapComprehension() ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"LiteralValue\"))\n\t}\n\n\tlbrace := p.expect(token.LBRACE)\n\tvar elts []ast.Expr\n\tvar mce *ast.ComprehensionExpr\n\tp.exprLev++\n\tif p.tok != token.RBRACE {\n\t\telts, mce = p.parseElementListOrComprehension()\n\t}\n\tp.exprLev--\n\trbrace := p.expectClosing(token.RBRACE, \"composite literal\")\n\tif mce != nil {\n\t\tmce.Lpos, mce.Rpos, mce.Tok = lbrace, rbrace, token.LBRACE\n\t\treturn mce\n\t}\n\treturn &ast.CompositeLit{Lbrace: lbrace, Elts: elts, Rbrace: rbrace}\n}\n\nfunc (p *parser) parseElementListOrComprehension() (list []ast.Expr, mce *ast.ComprehensionExpr) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ElementList\"))\n\t}\n\n\tif p.tok == token.FOR {\n\t\tphrases := p.parseForPhrases()\n\t\treturn nil, &ast.ComprehensionExpr{Fors: phrases}\n\t}\n\tfor p.tok != token.RBRACE && p.tok != token.EOF {\n\t\tlist = append(list, p.parseElement())\n\t\tif p.tok == token.FOR { // for k, v <- container\n\t\t\tif len(list) != 1 {\n\t\t\t\tlog.Panicln(\"TODO: invalid comprehension: too may elements.\")\n\t\t\t}\n\t\t\tphrases := p.parseForPhrases()\n\t\t\treturn nil, &ast.ComprehensionExpr{Elt: list[0], Fors: phrases}\n\t\t}\n\t\tif !p.atComma(\"composite literal\", token.RBRACE) {\n\t\t\tbreak\n\t\t}\n\t\tp.next()\n\t}\n\treturn\n}\n\nfunc (p *parser) parseElementList() (list []ast.Expr) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ElementList\"))\n\t}\n\n\tfor p.tok != token.RBRACE && p.tok != token.EOF {\n\t\tlist = append(list, p.parseElement())\n\t\tif !p.atComma(\"composite literal\", token.RBRACE) {\n\t\t\tbreak\n\t\t}\n\t\tp.next()\n\t}\n\n\treturn\n}\n\nfunc (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr {\n\tif p.trace {\n\t\tdefer un(trace(p, \"LiteralValue\"))\n\t}\n\n\tlbrace := p.expect(token.LBRACE)\n\tvar elts []ast.Expr\n\tp.exprLev++\n\tif p.tok != token.RBRACE {\n\t\telts = p.parseElementList()\n\t}\n\tp.exprLev--\n\trbrace := p.expectClosing(token.RBRACE, \"composite literal\")\n\treturn &ast.CompositeLit{Type: typ, Lbrace: lbrace, Elts: elts, Rbrace: rbrace}\n}\n\n// checkExpr checks that x is an expression (and not a type).\nfunc (p *parser) checkExpr(x ast.Expr) ast.Expr {\n\tswitch unparen(x).(type) {\n\tcase *ast.BadExpr:\n\tcase *ast.Ident:\n\tcase *ast.BasicLit:\n\tcase *ast.FuncLit:\n\tcase *ast.CompositeLit:\n\tcase *ast.SliceLit:\n\tcase *ast.ComprehensionExpr:\n\tcase *ast.SelectorExpr:\n\tcase *ast.AnySelectorExpr:\n\tcase *ast.IndexExpr:\n\tcase *ast.IndexListExpr:\n\tcase *ast.ArrayType:\n\tcase *ast.StructType:\n\tcase *ast.InterfaceType:\n\tcase *ast.FuncType:\n\tcase *ast.MapType:\n\tcase *ast.ChanType:\n\tcase *ast.SliceExpr:\n\tcase *ast.TypeAssertExpr:\n\t\t// If t.Type == nil we have a type assertion of the form\n\t\t// y.(type), which is only allowed in type switch expressions.\n\t\t// It's hard to exclude those but for the case where we are in\n\t\t// a type switch. Instead be lenient and test this in the type\n\t\t// checker.\n\tcase *ast.CallExpr:\n\tcase *ast.StarExpr:\n\tcase *ast.UnaryExpr:\n\tcase *ast.BinaryExpr:\n\tcase *ast.RangeExpr:\n\tcase *ast.ErrWrapExpr:\n\tcase *ast.LambdaExpr:\n\tcase *ast.LambdaExpr2:\n\tcase *ast.TupleLit:\n\tcase *ast.EnvExpr:\n\tcase *ast.CondExpr:\n\tcase *ast.ElemEllipsis:\n\tcase *ast.NumberUnitLit:\n\tcase *ast.DomainTextLit:\n\tdefault:\n\t\t// all other nodes are not proper expressions\n\t\tp.errorExpected(x.Pos(), \"expression\", 3)\n\t\tx = &ast.BadExpr{From: x.Pos(), To: p.safePos(x.End())}\n\t}\n\treturn x\n}\n\n/*\n// If x is of the form *T, deref returns T, otherwise it returns x.\nfunc deref(x ast.Expr) ast.Expr {\n\tif p, isPtr := x.(*ast.StarExpr); isPtr {\n\t\tx = p.X\n\t}\n\treturn x\n}\n*/\n\n// If x is of the form (T), unparen returns unparen(T), otherwise it returns x.\nfunc unparen(x ast.Expr) ast.Expr {\n\tif p, isParen := x.(*ast.ParenExpr); isParen {\n\t\tx = unparen(p.X)\n\t}\n\treturn x\n}\n\n// checkExprOrType checks that x is an expression or a type\n// (and not a raw type such as [...]T).\nfunc (p *parser) checkExprOrType(x ast.Expr) ast.Expr {\n\tswitch t := unparen(x).(type) {\n\tcase *ast.ArrayType:\n\t\tif len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis {\n\t\t\tp.error(len.Pos(), \"expected array length, found '...'\")\n\t\t\tx = &ast.BadExpr{From: x.Pos(), To: p.safePos(x.End())}\n\t\t}\n\t}\n\t// all other nodes are expressions or types\n\treturn x\n}\n\n// If lhs is set and the result is an identifier, it is not resolved.\n// flags support flagInLHS, flagAllowCmd, flagAllowKwargExpr\nfunc (p *parser) parsePrimaryExpr(iden *ast.Ident, flags int) (x ast.Expr, exprKind int) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"PrimaryExpr\"))\n\t}\n\n\tif iden != nil {\n\t\tx = iden\n\t} else if x, exprKind = p.parseOperand(flags); exprKind > 0 {\n\t\treturn\n\t}\n\n\tlhs := flags&flagInLHS != 0\n\tallowCmd := flags&flagAllowCmd != 0\nL:\n\tfor {\n\t\tswitch p.tok {\n\t\tcase token.PERIOD: // .\n\t\t\tposDot := p.pos\n\t\t\tp.next()\n\t\t\tif lhs {\n\t\t\t\tp.resolve(x)\n\t\t\t}\n\t\t\tswitch p.tok {\n\t\t\tcase token.IDENT:\n\t\t\t\tx = p.parseSelector(p.checkExprOrType(x))\n\t\t\tcase token.LPAREN:\n\t\t\t\tx = p.parseTypeAssertion(p.checkExpr(x))\n\t\t\tdefault:\n\t\t\t\tprocessed := posDot+1 == p.pos\n\t\t\t\tif processed {\n\t\t\t\t\tswitch p.tok {\n\t\t\t\t\tcase token.MUL: // .* .**\n\t\t\t\t\t\tposTok := p.pos\n\t\t\t\t\t\tp.next()\n\t\t\t\t\t\tif p.tok == token.MUL && p.pos == posDot+2 { // .**\n\t\t\t\t\t\t\tp.next()\n\t\t\t\t\t\t\tp.expect(token.PERIOD)\n\t\t\t\t\t\t\tsel := &ast.Ident{NamePos: p.pos}\n\t\t\t\t\t\t\tx = &ast.AnySelectorExpr{X: p.checkExpr(x), TokPos: posTok, Sel: sel}\n\t\t\t\t\t\t\tswitch p.tok {\n\t\t\t\t\t\t\tcase token.IDENT, token.STRING: // .**.name .**.\"name\"\n\t\t\t\t\t\t\t\tsel.Name = p.lit\n\t\t\t\t\t\t\t\tp.next()\n\t\t\t\t\t\t\tcase token.MUL: // .**.*\n\t\t\t\t\t\t\t\tsel.Name = \"*\"\n\t\t\t\t\t\t\t\tp.next()\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tp.errorExpected(p.pos, \"identifier after '**.'\", 2)\n\t\t\t\t\t\t\t\tsel.Name = \"_\"\n\t\t\t\t\t\t\t\tif p.tok != token.RBRACE { // TODO(rFindley)\n\t\t\t\t\t\t\t\t\tp.next() // make progress\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsel := &ast.Ident{NamePos: posTok, Name: \"*\"}\n\t\t\t\t\t\t\tx = &ast.SelectorExpr{X: p.checkExpr(x), Sel: sel}\n\t\t\t\t\t\t}\n\t\t\t\t\tcase token.ENV: // .$attr .$\"attr-name\"\n\t\t\t\t\t\tsel := &ast.Ident{NamePos: p.pos}\n\t\t\t\t\t\tp.next()\n\t\t\t\t\t\tif sel.NamePos+1 != p.pos || (p.tok != token.IDENT && p.tok != token.STRING) {\n\t\t\t\t\t\t\tp.errorExpected(p.pos, \"identifier after '$'\", 2)\n\t\t\t\t\t\t\tsel.Name = \"$_\"\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsel.Name = \"$\" + p.lit\n\t\t\t\t\t\t\tp.next()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tx = &ast.SelectorExpr{X: p.checkExpr(x), Sel: sel}\n\t\t\t\t\tcase token.STRING: // .\"field-name\"\n\t\t\t\t\t\tsel := &ast.Ident{NamePos: p.pos, Name: p.lit}\n\t\t\t\t\t\tp.next()\n\t\t\t\t\t\tx = &ast.SelectorExpr{X: p.checkExpr(x), Sel: sel}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tprocessed = false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !processed {\n\t\t\t\t\tpos := p.pos\n\t\t\t\t\tp.errorExpected(pos, \"selector or type assertion\", 2)\n\t\t\t\t\t// TODO(rFindley) The check for token.RBRACE below is a targeted fix\n\t\t\t\t\t//                to error recovery sufficient to make the x/tools tests to\n\t\t\t\t\t//                pass with the new parsing logic introduced for type\n\t\t\t\t\t//                parameters. Remove this once error recovery has been\n\t\t\t\t\t//                more generally reconsidered.\n\t\t\t\t\tif p.tok != token.RBRACE {\n\t\t\t\t\t\tp.next() // make progress\n\t\t\t\t\t}\n\t\t\t\t\tsel := &ast.Ident{NamePos: pos, Name: \"_\"}\n\t\t\t\t\tx = &ast.SelectorExpr{X: x, Sel: sel}\n\t\t\t\t}\n\t\t\t}\n\t\tcase token.AT: // @\n\t\t\tce := &ast.CondExpr{X: x, OpPos: p.pos}\n\t\t\tp.next()\n\t\t\tswitch p.tok {\n\t\t\tcase token.LPAREN:\n\t\t\t\tcond, kind := p.parseOperand(0) // @(cond)\n\t\t\t\tif kind != exprNormal {\n\t\t\t\t\tp.error(cond.Pos(), \"invalid condition expression\")\n\t\t\t\t}\n\t\t\t\tce.Cond = p.checkExpr(cond)\n\t\t\tcase token.IDENT:\n\t\t\t\tfun := p.parseIdent()\n\t\t\t\tif p.tok == token.LPAREN {\n\t\t\t\t\tce.Cond = p.parseCallOrConversion(fun, false) // @fun(...)\n\t\t\t\t} else {\n\t\t\t\t\tce.Cond = fun // @name\n\t\t\t\t}\n\t\t\tcase token.STRING: // @\"elem-name\"\n\t\t\t\tce.Cond = &ast.Ident{NamePos: p.pos, Name: p.lit}\n\t\t\t\tp.next()\n\t\t\tdefault:\n\t\t\t\tpos := p.pos\n\t\t\t\tp.errorExpected(pos, \"condition expression\", 2)\n\t\t\t\tp.next() // make progress\n\t\t\t\tce.Cond = &ast.Ident{NamePos: pos, Name: \"_\"}\n\t\t\t}\n\t\t\tx = ce\n\t\tcase token.LBRACK: // [\n\t\t\tif lhs {\n\t\t\t\tp.resolve(x)\n\t\t\t}\n\t\t\tif allowCmd && p.isCmd(x) { // println [...]\n\t\t\t\tx = p.parseCallOrConversion(x, true)\n\t\t\t} else {\n\t\t\t\tx = p.parseIndexOrSlice(p.checkExpr(x))\n\t\t\t}\n\t\tcase token.LPAREN: // (\n\t\t\tif lhs {\n\t\t\t\tp.resolve(x)\n\t\t\t}\n\t\t\tisCmd := allowCmd && p.isCmd(x) // println (...)\n\t\t\tx = p.parseCallOrConversion(p.checkExprOrType(x), isCmd)\n\t\tcase token.LBRACE: // {\n\t\t\tif allowCmd && p.isCmd(x) { // println {...}\n\t\t\t\tx = p.parseCallOrConversion(x, true)\n\t\t\t} else {\n\t\t\t\tt := unparen(x)\n\t\t\t\t// determine if '{' belongs to a composite literal or a block statement\n\t\t\t\tswitch t.(type) {\n\t\t\t\tcase *ast.BadExpr, *ast.Ident, *ast.SelectorExpr:\n\t\t\t\t\tif p.exprLev < 0 {\n\t\t\t\t\t\tbreak L\n\t\t\t\t\t}\n\t\t\t\t\t// x is possibly a composite literal type\n\t\t\t\tcase *ast.IndexExpr, *ast.IndexListExpr:\n\t\t\t\t\tif p.exprLev < 0 {\n\t\t\t\t\t\tbreak L\n\t\t\t\t\t}\n\t\t\t\t\t// x is possibly a composite literal type\n\t\t\t\tcase *ast.ArrayType, *ast.StructType, *ast.MapType:\n\t\t\t\t\t// x is a composite literal type\n\t\t\t\tdefault:\n\t\t\t\t\tbreak L\n\t\t\t\t}\n\t\t\t\tif t != x {\n\t\t\t\t\tp.error(t.Pos(), \"cannot parenthesize type in composite literal\")\n\t\t\t\t\t// already progressed, no need to advance\n\t\t\t\t}\n\t\t\t\tx = p.parseLiteralValue(x)\n\t\t\t}\n\t\tcase token.NOT: // !\n\t\t\tif allowCmd && p.isCmd(x) {\n\t\t\t\tx = p.parseCallOrConversion(x, true)\n\t\t\t} else {\n\t\t\t\tx = &ast.ErrWrapExpr{X: x, Tok: token.NOT, TokPos: p.pos}\n\t\t\t\tp.next()\n\t\t\t}\n\t\tcase token.QUESTION: // ?\n\t\t\tx = &ast.ErrWrapExpr{X: x, Tok: p.tok, TokPos: p.pos}\n\t\t\tp.next()\n\t\tcase token.ASSIGN: // =\n\t\t\tif flags&flagAllowKwargExpr != 0 {\n\t\t\t\tif name, ok := x.(*ast.Ident); ok { // name=expr\n\t\t\t\t\tp.next()\n\t\t\t\t\tval := p.parseExpr(0)\n\t\t\t\t\treturn &ast.KwargExpr{Name: name, Value: val}, exprKwarg\n\t\t\t\t}\n\t\t\t}\n\t\t\tfallthrough\n\t\tdefault:\n\t\t\tif allowCmd && p.isCmd(x) && p.checkCmd() {\n\t\t\t\tif lhs {\n\t\t\t\t\tp.resolve(x)\n\t\t\t\t}\n\t\t\t\tx = p.parseCallOrConversion(x, true)\n\t\t\t} else if p.tok == token.FLOAT && p.lit[0] == '.' && x.End() == p.pos {\n\t\t\t\t// tuple field: .0 .1 etc.\n\t\t\t\tsel := &ast.Ident{NamePos: p.pos + 1, Name: p.lit[1:]}\n\t\t\t\tp.next()\n\t\t\t\tx = &ast.SelectorExpr{X: x, Sel: sel}\n\t\t\t} else {\n\t\t\t\tbreak L\n\t\t\t}\n\t\t}\n\t\tlhs = false // no need to try to resolve again\n\t}\n\treturn\n}\n\nfunc (p *parser) isCmd(x ast.Expr) bool {\n\tswitch x.(type) {\n\tcase *ast.Ident, *ast.SelectorExpr, *ast.ErrWrapExpr:\n\t\treturn x.End() != p.pos\n\t}\n\treturn false\n}\n\nfunc (p *parser) checkCmd() bool {\n\tswitch p.tok {\n\tcase token.IDENT, token.DRARROW,\n\t\ttoken.STRING, token.CSTRING, token.PYSTRING,\n\t\ttoken.INT, token.FLOAT, token.IMAG, token.CHAR, token.RAT,\n\t\ttoken.FUNC, token.GOTO, token.TYPE, token.MAP, token.INTERFACE,\n\t\ttoken.CHAN, token.STRUCT, token.ENV:\n\t\treturn true\n\tcase token.SUB, token.AND, token.MUL, token.ARROW, token.XOR, token.ADD:\n\t\toldtok, oldpos := p.tok, p.pos\n\t\tp.next()\n\t\tnewpos := int(p.pos)\n\t\tp.unget(oldpos, oldtok, \"\")\n\t\treturn int(oldpos)+len(oldtok.String()) == newpos // x -y\n\t}\n\treturn false\n}\n\n// parseErrWrapExpr: expr! expr? expr?:defval\n// flags support flagInLHS, flagAllowCmd, flagAllowKwargExpr\nfunc (p *parser) parseErrWrapExpr(flags int) (x ast.Expr, exprKind int) {\n\tif x, exprKind = p.parsePrimaryExpr(nil, flags); exprKind > 0 {\n\t\treturn\n\t}\n\tif expr, ok := x.(*ast.ErrWrapExpr); ok {\n\t\tif p.tok == token.COLON {\n\t\t\tp.next()\n\t\t\texpr.Default, _ = p.parseUnaryExpr(0)\n\t\t}\n\t}\n\treturn\n}\n\n// If lhs is set and the result is an identifier, it is not resolved.\n// flags support flagInLHS, flagAllowCmd, flagAllowKwargExpr\nfunc (p *parser) parseUnaryExpr(flags int) (ast.Expr, int) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"UnaryExpr\"))\n\t}\n\n\tswitch p.tok {\n\tcase token.ADD, token.SUB, token.NOT, token.XOR, token.AND:\n\t\tpos, op := p.pos, p.tok\n\t\tp.next()\n\t\tx, _ := p.parseUnaryExpr(0)\n\t\treturn &ast.UnaryExpr{OpPos: pos, Op: op, X: p.checkExpr(x)}, 0\n\n\tcase token.ARROW:\n\t\t// channel type or receive expression\n\t\tarrow := p.pos\n\t\tp.next()\n\n\t\t// If the next token is token.CHAN we still don't know if it\n\t\t// is a channel type or a receive operation - we only know\n\t\t// once we have found the end of the unary expression. There\n\t\t// are two cases:\n\t\t//\n\t\t//   <- type  => (<-type) must be channel type\n\t\t//   <- expr  => <-(expr) is a receive from an expression\n\t\t//\n\t\t// In the first case, the arrow must be re-associated with\n\t\t// the channel type parsed already:\n\t\t//\n\t\t//   <- (chan type)    =>  (<-chan type)\n\t\t//   <- (chan<- type)  =>  (<-chan (<-type))\n\n\t\tx, _ := p.parseUnaryExpr(0)\n\n\t\t// determine which case we have\n\t\tif typ, ok := x.(*ast.ChanType); ok {\n\t\t\t// (<-type)\n\n\t\t\t// re-associate position info and <-\n\t\t\tdir := ast.SEND\n\t\t\tfor ok && dir == ast.SEND {\n\t\t\t\tif typ.Dir == ast.RECV {\n\t\t\t\t\t// error: (<-type) is (<-(<-chan T))\n\t\t\t\t\tp.errorExpected(typ.Arrow, \"'chan'\", 2)\n\t\t\t\t}\n\t\t\t\tarrow, typ.Begin, typ.Arrow = typ.Arrow, arrow, arrow\n\t\t\t\tdir, typ.Dir = typ.Dir, ast.RECV\n\t\t\t\ttyp, ok = typ.Value.(*ast.ChanType)\n\t\t\t}\n\t\t\tif dir == ast.SEND {\n\t\t\t\tp.errorExpected(arrow, \"channel type\", 2)\n\t\t\t}\n\n\t\t\treturn x, 0\n\t\t}\n\n\t\t// <-(expr)\n\t\treturn &ast.UnaryExpr{OpPos: arrow, Op: token.ARROW, X: p.checkExpr(x)}, 0\n\n\tcase token.MUL:\n\t\t// pointer type or unary \"*\" expression\n\t\tpos := p.pos\n\t\tp.next()\n\t\tx, _ := p.parseUnaryExpr(0)\n\t\treturn &ast.StarExpr{Star: pos, X: p.checkExprOrType(x)}, 0\n\t}\n\n\treturn p.parseErrWrapExpr(flags)\n}\n\nfunc (p *parser) tokPrec() (token.Token, int) {\n\ttok := p.tok\n\tif p.inRHS && tok == token.ASSIGN {\n\t\ttok = token.EQL\n\t}\n\treturn tok, tok.Precedence()\n}\n\n// If lhs is set and the result is an identifier, it is not resolved.\n// flags support flagInLHS, flagAllowCmd, flagAllowKwargExpr\nfunc (p *parser) parseBinaryExpr(prec1 int, flags int) (x ast.Expr, exprKind int) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"BinaryExpr\"))\n\t}\n\n\tif x, exprKind = p.parseUnaryExpr(flags); exprKind > 0 {\n\t\treturn\n\t}\n\tlhs := flags&flagInLHS != 0\n\tfor {\n\t\top, oprec := p.tokPrec()\n\t\tif oprec < prec1 {\n\t\t\treturn\n\t\t}\n\t\tpos := p.expect(op)\n\t\tif lhs {\n\t\t\tp.resolve(x)\n\t\t\tlhs = false\n\t\t}\n\t\ty, _ := p.parseBinaryExpr(oprec+1, 0)\n\t\tx = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)}\n\t}\n}\n\n// flags support flagAllowCmd, flagAllowKwargExpr\nfunc (p *parser) parseRangeExpr(first ast.Expr, flags int) (x ast.Expr, exprKind int) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"RangeExpr\"))\n\t}\n\tif p.tok != token.COLON {\n\t\tx, exprKind = p.parseBinaryExpr(token.LowestPrec+1, flags)\n\t\tif exprKind > 0 || p.tok != token.COLON { // not RangeExpr\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tx = first\n\t}\n\tto := p.pos\n\tp.next()\n\thigh, _ := p.parseBinaryExpr(token.LowestPrec+1, 0)\n\tvar colon2 token.Pos\n\tvar expr3 ast.Expr\n\tif p.tok == token.COLON {\n\t\tcolon2 = p.pos\n\t\tp.next()\n\t\texpr3, _ = p.parseBinaryExpr(token.LowestPrec+1, 0)\n\t}\n\tif debugParseOutput {\n\t\tlog.Printf(\"ast.RangeExpr{First: %v, Last: %v, Expr3: %v}\\n\", x, high, expr3)\n\t}\n\treturn &ast.RangeExpr{First: x, To: to, Last: high, Colon2: colon2, Expr3: expr3}, 0\n}\n\n// flags support flagAllowCmd, flagAllowRangeExpr, flagAllowKwargExpr\nfunc (p *parser) parseLambdaExpr(flags int) (x ast.Expr, exprKind int) {\n\tvar first = p.pos\n\tif p.tok != token.DRARROW {\n\t\tif flags&flagAllowRangeExpr != 0 {\n\t\t\tx, exprKind = p.parseRangeExpr(nil, flags)\n\t\t} else {\n\t\t\tx, exprKind = p.parseBinaryExpr(token.LowestPrec+1, flags)\n\t\t}\n\t\tif exprKind == exprKwarg { // not a lambda\n\t\t\treturn\n\t\t}\n\t}\n\tif p.tok == token.DRARROW { // =>\n\t\tvar rarrow = p.pos\n\t\tvar rhs []ast.Expr\n\t\tvar body *ast.BlockStmt\n\t\tvar lhsHasParen, rhsHasParen bool\n\t\tp.next()\n\t\tswitch p.tok {\n\t\tcase token.LPAREN: // (\n\t\t\trhsHasParen = true\n\t\t\tp.next()\n\t\t\tfor {\n\t\t\t\titem := p.parseExpr(0)\n\t\t\t\trhs = append(rhs, item)\n\t\t\t\tif p.tok != token.COMMA {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tp.next()\n\t\t\t}\n\t\t\tp.expect(token.RPAREN)\n\t\tcase token.LBRACE: // {\n\t\t\tbody = p.parseBlockStmt()\n\t\tdefault:\n\t\t\trhs = []ast.Expr{p.parseExpr(0)}\n\t\t}\n\t\tvar lhs []*ast.Ident\n\t\tif x != nil {\n\t\t\te := x\n\t\tretry:\n\t\t\tswitch v := e.(type) {\n\t\t\tcase *ast.TupleLit:\n\t\t\t\titems := make([]*ast.Ident, len(v.Elts))\n\t\t\t\tfor i, item := range v.Elts {\n\t\t\t\t\tident := p.toIdent(item)\n\t\t\t\t\tif ident == nil {\n\t\t\t\t\t\treturn &ast.BadExpr{From: item.Pos(), To: p.safePos(item.End())}, 0\n\t\t\t\t\t}\n\t\t\t\t\titems[i] = ident\n\t\t\t\t}\n\t\t\t\tlhs, lhsHasParen = items, true\n\t\t\tcase *ast.ParenExpr:\n\t\t\t\te, lhsHasParen = v.X, true\n\t\t\t\tgoto retry\n\t\t\tdefault:\n\t\t\t\tident := p.toIdent(v)\n\t\t\t\tif ident == nil {\n\t\t\t\t\treturn &ast.BadExpr{From: v.Pos(), To: p.safePos(v.End())}, 0\n\t\t\t\t}\n\t\t\t\tlhs = []*ast.Ident{ident}\n\t\t\t}\n\t\t}\n\t\tif debugParseOutput {\n\t\t\tlog.Printf(\"ast.LambdaExpr{Lhs: %v}\\n\", lhs)\n\t\t}\n\t\tif body != nil {\n\t\t\treturn &ast.LambdaExpr2{\n\t\t\t\tFirst:       first,\n\t\t\t\tLhs:         lhs,\n\t\t\t\tRarrow:      rarrow,\n\t\t\t\tBody:        body,\n\t\t\t\tLhsHasParen: lhsHasParen,\n\t\t\t}, 0\n\t\t}\n\t\treturn &ast.LambdaExpr{\n\t\t\tFirst:       first,\n\t\t\tLast:        p.pos,\n\t\t\tLhs:         lhs,\n\t\t\tRarrow:      rarrow,\n\t\t\tRhs:         rhs,\n\t\t\tLhsHasParen: lhsHasParen,\n\t\t\tRhsHasParen: rhsHasParen,\n\t\t}, 0\n\t}\n\treturn\n}\n\n// If lhs is set and the result is an identifier, it is not resolved.\n// The result may be a type or even a raw type ([...]int). Callers must\n// check the result (using checkExpr or checkExprOrType), depending on\n// context.\n// flags support flagInLHS, flagAllowCmd, flagAllowRangeExpr, flagAllowKwargExpr\nfunc (p *parser) parseExprEx(flags int) (ast.Expr, int) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Expression\"))\n\t}\n\tif flags&flagInLHS != 0 {\n\t\treturn p.parseBinaryExpr(token.LowestPrec+1, flags&(flagInLHS|flagAllowCmd))\n\t}\n\treturn p.parseLambdaExpr(flags)\n}\n\n// flags support flagInLHS, flagAllowCmd, flagAllowRangeExpr\nfunc (p *parser) parseExpr(flags int) ast.Expr {\n\tx, _ := p.parseExprEx(flags)\n\treturn x\n}\n\nfunc (p *parser) parseRHS() ast.Expr {\n\treturn p.parseRHSEx(0)\n}\n\n// flags support flagAllowRangeExpr\nfunc (p *parser) parseRHSEx(flags int) ast.Expr {\n\told := p.inRHS\n\tp.inRHS = true\n\tx := p.checkExpr(p.parseExpr(flags))\n\tp.inRHS = old\n\treturn x\n}\n\n// flags support flagAllowKwargExpr\nfunc (p *parser) parseRHSOrTypeEx(flags int) (x ast.Expr, exprKind int) {\n\told := p.inRHS\n\tp.inRHS = true\n\tx, exprKind = p.parseExprEx(flags)\n\tif exprKind == 0 {\n\t\tx = p.checkExprOrType(x)\n\t}\n\tp.inRHS = old\n\treturn\n}\n\nfunc (p *parser) parseRHSOrType() ast.Expr {\n\tx, _ := p.parseRHSOrTypeEx(0)\n\treturn x\n}\n\n// ----------------------------------------------------------------------------\n// Statements\n\n// Parsing modes for parseSimpleStmt.\nconst (\n\tbasic = iota\n\tlabelOk\n\trangeOk\n)\n\n// parseSimpleStmt returns true as 2nd result if it parsed the assignment\n// of a range clause (with mode == rangeOk). The returned statement is an\n// assignment with a right-hand side that is a single unary expression of\n// the form \"range x\". No guarantees are given for the left-hand side.\n// flags support flagAllowCmd\nfunc (p *parser) parseSimpleStmt(mode int, flags int) ast.Stmt {\n\tss, _ /* isRange */ := p.parseSimpleStmtEx(mode, flags)\n\treturn ss\n}\n\nfunc (p *parser) parseBranchCmdStmt(iden *ast.Ident) ast.Stmt { // XGo: goto as command\n\tx, _ := p.parsePrimaryExpr(iden, flagAllowCmd)\n\treturn &ast.ExprStmt{X: x}\n}\n\n// flags support flagAllowCmd, flagAllowRangeExpr\nfunc (p *parser) parseSimpleStmtEx(mode int, flags int) (ast.Stmt, bool) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"SimpleStmt\"))\n\t}\n\n\tif flags&flagAllowRangeExpr != 0 && p.tok == token.COLON { // rangeExpr\n\t\tre, _ := p.parseRangeExpr(nil, 0)\n\t\treturn &ast.ExprStmt{X: re}, true\n\t}\n\tx := p.parseLHSList(flags & flagAllowCmd)\n\n\tswitch p.tok {\n\tcase\n\t\ttoken.DEFINE, token.ASSIGN, token.ADD_ASSIGN,\n\t\ttoken.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN,\n\t\ttoken.REM_ASSIGN, token.AND_ASSIGN, token.OR_ASSIGN,\n\t\ttoken.XOR_ASSIGN, token.SHL_ASSIGN, token.SHR_ASSIGN, token.AND_NOT_ASSIGN:\n\t\t// assignment statement, possibly part of a range clause\n\t\tpos, tok := p.pos, p.tok\n\t\tp.next()\n\t\tvar y []ast.Expr\n\t\tisRange := false\n\t\tif mode == rangeOk && p.tok == token.RANGE && (tok == token.DEFINE || tok == token.ASSIGN) {\n\t\t\tpos := p.pos\n\t\t\tp.next()\n\t\t\ty = []ast.Expr{&ast.UnaryExpr{OpPos: pos, Op: token.RANGE, X: p.parseRHSEx(flagAllowRangeExpr)}}\n\t\t\tisRange = true\n\t\t} else {\n\t\t\ty = p.parseRHSList()\n\t\t}\n\t\tas := &ast.AssignStmt{Lhs: x, TokPos: pos, Tok: tok, Rhs: y}\n\t\tif tok == token.DEFINE {\n\t\t\tp.shortVarDecl(as, x)\n\t\t}\n\t\treturn as, isRange\n\tcase token.IDENT: // in\n\t\tif mode != rangeOk || p.lit != \"in\" {\n\t\t\tbreak\n\t\t}\n\t\tfallthrough\n\tcase token.ARROW: // <- (backward compatibility)\n\t\tif mode == rangeOk {\n\t\t\treturn p.parseForPhraseStmtPart(x), true\n\t\t}\n\t}\n\n\tif len(x) > 1 {\n\t\tp.errorExpected(x[0].Pos(), \"1 expression\", 2)\n\t\t// continue with first expression\n\t}\n\n\tswitch p.tok {\n\tcase token.COLON:\n\t\tif flags&flagAllowRangeExpr != 0 {\n\t\t\tre, _ := p.parseRangeExpr(x[0], 0)\n\t\t\treturn &ast.ExprStmt{X: re}, true\n\t\t}\n\t\t// labeled statement\n\t\tcolon := p.pos\n\t\tp.next()\n\t\tif label, isIdent := x[0].(*ast.Ident); mode == labelOk && isIdent {\n\t\t\t// Go spec: The scope of a label is the body of the function\n\t\t\t// in which it is declared and excludes the body of any nested\n\t\t\t// function.\n\t\t\tstmt := &ast.LabeledStmt{Label: label, Colon: colon, Stmt: p.parseStmt(flags)}\n\t\t\tp.declare(stmt, nil, p.labelScope, ast.Lbl, label)\n\t\t\treturn stmt, false\n\t\t}\n\t\t// The label declaration typically starts at x[0].Pos(), but the label\n\t\t// declaration may be erroneous due to a token after that position (and\n\t\t// before the ':'). If SpuriousErrors is not set, the (only) error\n\t\t// reported for the line is the illegal label error instead of the token\n\t\t// before the ':' that caused the problem. Thus, use the (latest) colon\n\t\t// position for error reporting.\n\t\tp.error(colon, \"illegal label declaration\")\n\t\treturn &ast.BadStmt{From: x[0].Pos(), To: colon + 1}, false\n\n\tcase token.ARROW:\n\t\t// send statement\n\t\tarrow := p.pos\n\t\tp.next()\n\t\tvar ellipsis token.Pos\n\t\tvals := make([]ast.Expr, 1)\n\t\tvals[0] = p.parseRHS()\n\t\tif p.tok == token.ELLIPSIS { // a <- v... (issues #2107)\n\t\t\tellipsis = p.pos\n\t\t\tp.next()\n\t\t} else {\n\t\t\tfor p.tok == token.COMMA { // a <- v1, v2, v3 (issues #2107)\n\t\t\t\tp.next()\n\t\t\t\tvals = append(vals, p.parseRHS())\n\t\t\t}\n\t\t}\n\t\treturn &ast.SendStmt{Chan: x[0], Arrow: arrow, Values: vals, Ellipsis: ellipsis}, false\n\n\tcase token.INC, token.DEC:\n\t\t// increment or decrement\n\t\ts := &ast.IncDecStmt{X: x[0], TokPos: p.pos, Tok: p.tok}\n\t\tp.next()\n\t\treturn s, false\n\t}\n\n\t// expression\n\treturn &ast.ExprStmt{X: x[0]}, false\n}\n\nfunc (p *parser) parseCallExpr(callType string) *ast.CallExpr {\n\tx := p.parseRHSOrType() // could be a conversion: (some type)(x)\n\tif call, isCall := x.(*ast.CallExpr); isCall {\n\t\treturn call\n\t}\n\tif _, isBad := x.(*ast.BadExpr); !isBad {\n\t\t// only report error if it's a new one\n\t\tp.error(p.safePos(x.End()), fmt.Sprintf(\"function must be invoked in %s statement\", callType))\n\t}\n\treturn nil\n}\n\nfunc (p *parser) parseGoStmt() ast.Stmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"GoStmt\"))\n\t}\n\n\tpos := p.expect(token.GO)\n\tcall := p.parseCallExpr(\"go\")\n\tp.expectSemi()\n\tif call == nil {\n\t\treturn &ast.BadStmt{From: pos, To: pos + 2} // len(\"go\")\n\t}\n\n\treturn &ast.GoStmt{Go: pos, Call: call}\n}\n\nfunc (p *parser) parseDeferStmt() ast.Stmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"DeferStmt\"))\n\t}\n\n\tpos := p.expect(token.DEFER)\n\tcall := p.parseCallExpr(\"defer\")\n\tp.expectSemi()\n\tif call == nil {\n\t\treturn &ast.BadStmt{From: pos, To: pos + 5} // len(\"defer\")\n\t}\n\n\treturn &ast.DeferStmt{Defer: pos, Call: call}\n}\n\nfunc (p *parser) parseReturnStmt() *ast.ReturnStmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ReturnStmt\"))\n\t}\n\n\tpos := p.pos\n\tp.expect(token.RETURN)\n\tvar x []ast.Expr\n\tif p.tok != token.SEMICOLON && p.tok != token.RBRACE {\n\t\tx = p.parseRHSList()\n\t}\n\tp.expectSemi()\n\n\treturn &ast.ReturnStmt{Return: pos, Results: x}\n}\n\nfunc (p *parser) parseBranchStmt(tok token.Token) ast.Stmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"BranchStmt\"))\n\t}\n\n\toldpos, oldlit := p.pos, p.lit // XGo: save token to allow goto() as a function\n\tpos := p.expect(tok)\n\tnext := p.tok\n\tif next != token.IDENT && next != token.SEMICOLON { // XGo: allow goto() as a function\n\t\tp.unget(oldpos, token.IDENT, oldlit)\n\t\ts := p.parseSimpleStmt(basic, flagAllowCmd)\n\t\tp.expectSemi()\n\t\treturn s\n\t}\n\n\tvar label *ast.Ident\n\tif tok != token.FALLTHROUGH && next == token.IDENT {\n\t\tlabel = p.parseIdent()\n\t\t// add to list of unresolved targets\n\t\tn := len(p.targetStack) - 1\n\t\tp.targetStack[n] = append(p.targetStack[n], label)\n\t}\n\tif p.tok != token.SEMICOLON { // XGo: goto command\n\t\tif label != nil {\n\t\t\tp.unget(label.NamePos, token.IDENT, label.Name)\n\t\t}\n\t\ts := p.parseBranchCmdStmt(&ast.Ident{NamePos: oldpos, Name: oldlit})\n\t\tp.expectSemi()\n\t\treturn s\n\t}\n\tp.expectSemi()\n\n\treturn &ast.BranchStmt{TokPos: pos, Tok: tok, Label: label}\n}\n\nfunc (p *parser) makeExpr(s ast.Stmt, want string) ast.Expr {\n\tif s == nil {\n\t\treturn nil\n\t}\n\tif es, isExpr := s.(*ast.ExprStmt); isExpr {\n\t\treturn p.checkExpr(es.X)\n\t}\n\tfound := \"simple statement\"\n\tif _, isAss := s.(*ast.AssignStmt); isAss {\n\t\tfound = \"assignment\"\n\t}\n\tp.error(s.Pos(), fmt.Sprintf(\"expected %s, found %s (missing parentheses around composite literal?)\", want, found))\n\treturn &ast.BadExpr{From: s.Pos(), To: p.safePos(s.End())}\n}\n\n// parseIfHeader is an adjusted version of parser.header\n// in cmd/compile/internal/syntax/parser.go, which has\n// been tuned for better error handling.\nfunc (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) {\n\tif p.tok == token.LBRACE {\n\t\tp.error(p.pos, \"missing condition in if statement\")\n\t\tcond = &ast.BadExpr{From: p.pos, To: p.pos}\n\t\treturn\n\t}\n\t// p.tok != token.LBRACE\n\n\touter := p.exprLev\n\tp.exprLev = -1\n\n\tif p.tok != token.SEMICOLON {\n\t\t// accept potential variable declaration but complain\n\t\tif p.tok == token.VAR {\n\t\t\tp.next()\n\t\t\tp.error(p.pos, \"var declaration not allowed in 'IF' initializer\")\n\t\t}\n\t\tinit = p.parseSimpleStmt(basic, 0)\n\t}\n\n\tvar condStmt ast.Stmt\n\tvar semi struct {\n\t\tpos token.Pos\n\t\tlit string // \";\" or \"\\n\"; valid if pos.IsValid()\n\t}\n\tif p.tok != token.LBRACE {\n\t\tif p.tok == token.SEMICOLON {\n\t\t\tsemi.pos = p.pos\n\t\t\tsemi.lit = p.lit\n\t\t\tp.next()\n\t\t} else {\n\t\t\tp.expect(token.SEMICOLON)\n\t\t}\n\t\tif p.tok != token.LBRACE {\n\t\t\tcondStmt = p.parseSimpleStmt(basic, 0)\n\t\t}\n\t} else {\n\t\tcondStmt = init\n\t\tinit = nil\n\t}\n\n\tif condStmt != nil {\n\t\tcond = p.makeExpr(condStmt, \"boolean expression\")\n\t} else if semi.pos.IsValid() {\n\t\tif semi.lit == \"\\n\" {\n\t\t\tp.error(semi.pos, \"unexpected newline, expecting { after if clause\")\n\t\t} else {\n\t\t\tp.error(semi.pos, \"missing condition in if statement\")\n\t\t}\n\t}\n\n\t// make sure we have a valid AST\n\tif cond == nil {\n\t\tcond = &ast.BadExpr{From: p.pos, To: p.pos}\n\t}\n\n\tp.exprLev = outer\n\treturn\n}\n\nfunc isForPhraseCondEnd(tok token.Token) bool {\n\treturn tok == token.RBRACK || tok == token.RBRACE || tok == token.FOR\n}\n\n// parseForPhraseCond is an adjusted version of parseIfHeader\nfunc (p *parser) parseForPhraseCond() (init ast.Stmt, cond ast.Expr) {\n\tif isForPhraseCondEnd(p.tok) {\n\t\tp.error(p.pos, \"missing condition in for..in statement\")\n\t\tcond = &ast.BadExpr{From: p.pos, To: p.pos}\n\t\treturn\n\t}\n\n\touter := p.exprLev\n\tp.exprLev = -1\n\n\tif p.tok != token.SEMICOLON {\n\t\t// accept potential variable declaration but complain\n\t\tif p.tok == token.VAR {\n\t\t\tp.next()\n\t\t\tp.error(p.pos, \"var declaration not allowed in 'IF' initializer\")\n\t\t}\n\t\tinit = p.parseSimpleStmt(basic, 0)\n\t}\n\n\tvar condStmt ast.Stmt\n\tvar semiPos token.Pos\n\tif !isForPhraseCondEnd(p.tok) {\n\t\tif p.tok == token.SEMICOLON {\n\t\t\tsemiPos = p.pos\n\t\t\tp.next()\n\t\t} else {\n\t\t\tp.expect(token.SEMICOLON)\n\t\t}\n\t\tif !isForPhraseCondEnd(p.tok) {\n\t\t\tcondStmt = p.parseSimpleStmt(basic, 0)\n\t\t}\n\t} else {\n\t\tcondStmt = init\n\t\tinit = nil\n\t}\n\n\tif condStmt != nil {\n\t\tcond = p.makeExpr(condStmt, \"boolean expression\")\n\t} else if semiPos.IsValid() {\n\t\tp.error(semiPos, \"missing condition in for..in statement\")\n\t}\n\n\t// make sure we have a valid AST\n\tif cond == nil {\n\t\tcond = &ast.BadExpr{From: p.pos, To: p.pos}\n\t}\n\n\tp.exprLev = outer\n\treturn\n}\n\nfunc (p *parser) parseIfStmt() *ast.IfStmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"IfStmt\"))\n\t}\n\n\tpos := p.expect(token.IF)\n\tp.openScope()\n\tdefer p.closeScope()\n\n\tinit, cond := p.parseIfHeader()\n\tbody := p.parseBlockStmt()\n\n\tvar elseStmt ast.Stmt\n\tif p.tok == token.ELSE {\n\t\tp.next()\n\t\tswitch p.tok {\n\t\tcase token.IF:\n\t\t\telseStmt = p.parseIfStmt()\n\t\tcase token.LBRACE:\n\t\t\telseStmt = p.parseBlockStmt()\n\t\t\tp.expectSemi()\n\t\tdefault:\n\t\t\tp.errorExpected(p.pos, \"if statement or block\", 2)\n\t\t\telseStmt = &ast.BadStmt{From: p.pos, To: p.pos}\n\t\t}\n\t} else {\n\t\tp.expectSemi()\n\t}\n\n\treturn &ast.IfStmt{If: pos, Init: init, Cond: cond, Body: body, Else: elseStmt}\n}\n\nfunc (p *parser) parseTypeList() (list []ast.Expr) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"TypeList\"))\n\t}\n\n\tlist = append(list, p.parseType())\n\tfor p.tok == token.COMMA {\n\t\tp.next()\n\t\tlist = append(list, p.parseType())\n\t}\n\n\treturn\n}\n\nfunc (p *parser) parseCaseClause(typeSwitch bool) *ast.CaseClause {\n\tif p.trace {\n\t\tdefer un(trace(p, \"CaseClause\"))\n\t}\n\n\tpos := p.pos\n\tvar list []ast.Expr\n\tif p.tok == token.CASE {\n\t\tp.next()\n\t\tif typeSwitch {\n\t\t\tlist = p.parseTypeList()\n\t\t} else {\n\t\t\tlist = p.parseRHSList()\n\t\t}\n\t} else {\n\t\tp.expect(token.DEFAULT)\n\t}\n\n\tcolon := p.expect(token.COLON)\n\tp.openScope()\n\tbody := p.parseStmtList()\n\tp.closeScope()\n\n\treturn &ast.CaseClause{Case: pos, List: list, Colon: colon, Body: body}\n}\n\nfunc isTypeSwitchAssert(x ast.Expr) bool {\n\ta, ok := x.(*ast.TypeAssertExpr)\n\treturn ok && a.Type == nil\n}\n\nfunc (p *parser) isTypeSwitchGuard(s ast.Stmt) bool {\n\tswitch t := s.(type) {\n\tcase *ast.ExprStmt:\n\t\t// x.(type)\n\t\treturn isTypeSwitchAssert(t.X)\n\tcase *ast.AssignStmt:\n\t\t// v := x.(type)\n\t\tif len(t.Lhs) == 1 && len(t.Rhs) == 1 && isTypeSwitchAssert(t.Rhs[0]) {\n\t\t\tswitch t.Tok {\n\t\t\tcase token.ASSIGN:\n\t\t\t\t// permit v = x.(type) but complain\n\t\t\t\tp.error(t.TokPos, \"expected ':=', found '='\")\n\t\t\t\tfallthrough\n\t\t\tcase token.DEFINE:\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (p *parser) parseSwitchStmt() ast.Stmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"SwitchStmt\"))\n\t}\n\n\tpos := p.expect(token.SWITCH)\n\tp.openScope()\n\tdefer p.closeScope()\n\n\tvar s1, s2 ast.Stmt\n\tif p.tok != token.LBRACE {\n\t\tprevLev := p.exprLev\n\t\tp.exprLev = -1\n\t\tif p.tok != token.SEMICOLON {\n\t\t\ts2 = p.parseSimpleStmt(basic, 0)\n\t\t}\n\t\tif p.tok == token.SEMICOLON {\n\t\t\tp.next()\n\t\t\ts1 = s2\n\t\t\ts2 = nil\n\t\t\tif p.tok != token.LBRACE {\n\t\t\t\t// A TypeSwitchGuard may declare a variable in addition\n\t\t\t\t// to the variable declared in the initial SimpleStmt.\n\t\t\t\t// Introduce extra scope to avoid redeclaration errors:\n\t\t\t\t//\n\t\t\t\t//\tswitch t := 0; t := x.(T) { ... }\n\t\t\t\t//\n\t\t\t\t// (this code is not valid Go because the first t\n\t\t\t\t// cannot be accessed and thus is never used, the extra\n\t\t\t\t// scope is needed for the correct error message).\n\t\t\t\t//\n\t\t\t\t// If we don't have a type switch, s2 must be an expression.\n\t\t\t\t// Having the extra nested but empty scope won't affect it.\n\t\t\t\tp.openScope()\n\t\t\t\tdefer p.closeScope()\n\t\t\t\ts2 = p.parseSimpleStmt(basic, 0)\n\t\t\t}\n\t\t}\n\t\tp.exprLev = prevLev\n\t}\n\n\ttypeSwitch := p.isTypeSwitchGuard(s2)\n\tlbrace := p.expect(token.LBRACE)\n\tvar list []ast.Stmt\n\tfor p.tok == token.CASE || p.tok == token.DEFAULT {\n\t\tlist = append(list, p.parseCaseClause(typeSwitch))\n\t}\n\trbrace := p.expect(token.RBRACE)\n\tp.expectSemi()\n\tbody := &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}\n\n\tif typeSwitch {\n\t\treturn &ast.TypeSwitchStmt{Switch: pos, Init: s1, Assign: s2, Body: body}\n\t}\n\n\treturn &ast.SwitchStmt{Switch: pos, Init: s1, Tag: p.makeExpr(s2, \"switch expression\"), Body: body}\n}\n\nfunc (p *parser) parseCommClause() *ast.CommClause {\n\tif p.trace {\n\t\tdefer un(trace(p, \"CommClause\"))\n\t}\n\n\tp.openScope()\n\tpos := p.pos\n\tvar comm ast.Stmt\n\tif p.tok == token.CASE {\n\t\tp.next()\n\t\tlhs := p.parseLHSList(0)\n\t\tif p.tok == token.ARROW {\n\t\t\t// SendStmt\n\t\t\tif len(lhs) > 1 {\n\t\t\t\tp.errorExpected(lhs[0].Pos(), \"1 expression\", 2)\n\t\t\t\t// continue with first expression\n\t\t\t}\n\t\t\tarrow := p.pos\n\t\t\tp.next()\n\t\t\trhs := p.parseRHS()\n\t\t\tcomm = &ast.SendStmt{Chan: lhs[0], Arrow: arrow, Values: []ast.Expr{rhs}}\n\t\t} else {\n\t\t\t// RecvStmt\n\t\t\tif tok := p.tok; tok == token.ASSIGN || tok == token.DEFINE {\n\t\t\t\t// RecvStmt with assignment\n\t\t\t\tif len(lhs) > 2 {\n\t\t\t\t\tp.errorExpected(lhs[0].Pos(), \"1 or 2 expressions\", 2)\n\t\t\t\t\t// continue with first two expressions\n\t\t\t\t\tlhs = lhs[0:2]\n\t\t\t\t}\n\t\t\t\tpos := p.pos\n\t\t\t\tp.next()\n\t\t\t\trhs := p.parseRHS()\n\t\t\t\tas := &ast.AssignStmt{Lhs: lhs, TokPos: pos, Tok: tok, Rhs: []ast.Expr{rhs}}\n\t\t\t\tif tok == token.DEFINE {\n\t\t\t\t\tp.shortVarDecl(as, lhs)\n\t\t\t\t}\n\t\t\t\tcomm = as\n\t\t\t} else {\n\t\t\t\t// lhs must be single receive operation\n\t\t\t\tif len(lhs) > 1 {\n\t\t\t\t\tp.errorExpected(lhs[0].Pos(), \"1 expression\", 2)\n\t\t\t\t\t// continue with first expression\n\t\t\t\t}\n\t\t\t\tcomm = &ast.ExprStmt{X: lhs[0]}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tp.expect(token.DEFAULT)\n\t}\n\n\tcolon := p.expect(token.COLON)\n\tbody := p.parseStmtList()\n\tp.closeScope()\n\n\treturn &ast.CommClause{Case: pos, Comm: comm, Colon: colon, Body: body}\n}\n\nfunc (p *parser) parseSelectStmt() *ast.SelectStmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"SelectStmt\"))\n\t}\n\n\tpos := p.expect(token.SELECT)\n\tlbrace := p.expect(token.LBRACE)\n\tvar list []ast.Stmt\n\tfor p.tok == token.CASE || p.tok == token.DEFAULT {\n\t\tlist = append(list, p.parseCommClause())\n\t}\n\trbrace := p.expect(token.RBRACE)\n\tp.expectSemi()\n\tbody := &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}\n\n\treturn &ast.SelectStmt{Select: pos, Body: body}\n}\n\nfunc (p *parser) parseForPhrases() (phrases []*ast.ForPhrase) {\n\tfor {\n\t\tphrase := p.parseForPhrase()\n\t\tphrases = append(phrases, phrase)\n\t\tif p.tok != token.FOR {\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (p *parser) parseForPhraseStmtPart(lhs []ast.Expr) *ast.ForPhraseStmt {\n\ttokPos := p.expectIn() // in\n\tx := p.parseExpr(flagAllowRangeExpr)\n\tvar cond ast.Expr\n\tvar ifPos token.Pos\n\tif p.tok == token.IF || p.tok == token.COMMA {\n\t\tifPos = p.pos\n\t\tp.next()\n\t\tcond = p.parseExpr(0)\n\t\t// NOTE(xsw): why not parseForPhraseCond?\n\t\t// init statements are not supported in for-phrase-if (can't use ';' in for loop)\n\t}\n\n\tstmt := &ast.ForPhraseStmt{\n\t\tForPhrase: &ast.ForPhrase{\n\t\t\tTokPos: tokPos, X: x, IfPos: ifPos, Cond: cond,\n\t\t},\n\t}\n\tswitch len(lhs) {\n\tcase 1:\n\t\tstmt.Value = p.toIdent(lhs[0])\n\tcase 2:\n\t\tstmt.Key, stmt.Value = p.toIdent(lhs[0]), p.toIdent(lhs[1])\n\tdefault:\n\t\tp.errorExpected(lhs[0].Pos(), \"expect 1 or 2 identifiers\", 2)\n\t}\n\treturn stmt\n}\n\nfunc (p *parser) toIdent(e ast.Expr) *ast.Ident {\n\tswitch v := e.(type) {\n\tcase *ast.Ident:\n\t\treturn v\n\tcase *ast.BasicLit:\n\t\tp.errorExpected(e.Pos(), fmt.Sprintf(\"'IDENT', found %v\", v.Value), 2)\n\tcase *ast.NumberUnitLit:\n\t\tp.errorExpected(e.Pos(), fmt.Sprintf(\"'IDENT', found %v\", v.Value+v.Unit), 2)\n\tdefault:\n\t\tp.errorExpected(e.Pos(), \"'IDENT'\", 2)\n\t}\n\treturn nil\n}\n\nfunc (p *parser) parseForPhrase() *ast.ForPhrase { // for k, v in container if cond\n\tif p.trace {\n\t\tdefer un(trace(p, \"ForPhrase\"))\n\t}\n\n\tpos := p.expect(token.FOR)\n\tp.openScope()\n\tdefer p.closeScope()\n\n\tvar k, v *ast.Ident\n\tv = p.parseIdent()\n\tif p.tok == token.COMMA { // k, v\n\t\tp.next()\n\t\tk, v = v, p.parseIdent()\n\t}\n\n\ttokPos := p.expectIn() // in container\n\tx := p.parseExpr(flagAllowRangeExpr)\n\tvar init ast.Stmt\n\tvar cond ast.Expr\n\tvar ifPos token.Pos\n\tif p.tok == token.IF || p.tok == token.COMMA { // `if condition` or `if init; condition`\n\t\tifPos = p.pos\n\t\tp.next()\n\t\tinit, cond = p.parseForPhraseCond()\n\t}\n\treturn &ast.ForPhrase{For: pos, Key: k, Value: v, TokPos: tokPos, X: x, IfPos: ifPos, Init: init, Cond: cond}\n}\n\nfunc (p *parser) parseForStmt() ast.Stmt {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ForStmt\"))\n\t}\n\n\tpos := p.expect(token.FOR)\n\tp.openScope()\n\tdefer p.closeScope()\n\n\tvar s1, s2, s3 ast.Stmt\n\tvar isRange bool\n\tif p.tok != token.LBRACE {\n\t\tprevLev := p.exprLev\n\t\tp.exprLev = -1\n\t\tif p.tok != token.SEMICOLON {\n\t\t\tif p.tok == token.RANGE {\n\t\t\t\t// \"for range x\" (nil lhs in assignment)\n\t\t\t\tpos := p.pos\n\t\t\t\tp.next()\n\t\t\t\ty := []ast.Expr{&ast.UnaryExpr{OpPos: pos, Op: token.RANGE, X: p.parseRHSEx(flagAllowRangeExpr)}}\n\t\t\t\ts2 = &ast.AssignStmt{Rhs: y}\n\t\t\t\tisRange = true\n\t\t\t} else {\n\t\t\t\ts2, isRange = p.parseSimpleStmtEx(rangeOk, flagAllowRangeExpr)\n\t\t\t}\n\t\t}\n\t\tif !isRange && p.tok == token.SEMICOLON {\n\t\t\tp.next()\n\t\t\ts1 = s2\n\t\t\ts2 = nil\n\t\t\tif p.tok != token.SEMICOLON {\n\t\t\t\ts2 = p.parseSimpleStmt(basic, 0)\n\t\t\t}\n\t\t\tp.expectSemi()\n\t\t\tif p.tok != token.LBRACE {\n\t\t\t\ts3 = p.parseSimpleStmt(basic, 0)\n\t\t\t}\n\t\t}\n\t\tp.exprLev = prevLev\n\t}\n\n\tbody := p.parseBlockStmt()\n\tp.expectSemi()\n\n\tif isRange {\n\t\tswitch stmt := s2.(type) {\n\t\tcase *ast.ForPhraseStmt:\n\t\t\tstmt.For = pos\n\t\t\tstmt.Body = body\n\t\t\treturn stmt\n\t\tcase *ast.ExprStmt:\n\t\t\treturn &ast.RangeStmt{\n\t\t\t\tFor:       pos,\n\t\t\t\tX:         stmt.X,\n\t\t\t\tBody:      body,\n\t\t\t\tNoRangeOp: true,\n\t\t\t}\n\t\t}\n\t\tas := s2.(*ast.AssignStmt)\n\t\t// check lhs\n\t\tvar key, value ast.Expr\n\t\tswitch len(as.Lhs) {\n\t\tcase 0:\n\t\t\t// nothing to do\n\t\tcase 1:\n\t\t\tkey = as.Lhs[0]\n\t\tcase 2:\n\t\t\tkey, value = as.Lhs[0], as.Lhs[1]\n\t\tdefault:\n\t\t\tp.errorExpected(as.Lhs[len(as.Lhs)-1].Pos(), \"at most 2 expressions\", 2)\n\t\t\treturn &ast.BadStmt{From: pos, To: p.safePos(body.End())}\n\t\t}\n\t\t// parseSimpleStmt returned a right-hand side that\n\t\t// is a single unary expression of the form \"range x\"\n\t\tx := as.Rhs[0].(*ast.UnaryExpr).X\n\t\treturn &ast.RangeStmt{\n\t\t\tFor:    pos,\n\t\t\tKey:    key,\n\t\t\tValue:  value,\n\t\t\tTokPos: as.TokPos,\n\t\t\tTok:    as.Tok,\n\t\t\tX:      x,\n\t\t\tBody:   body,\n\t\t}\n\t}\n\n\t// regular for statement\n\treturn &ast.ForStmt{\n\t\tFor:  pos,\n\t\tInit: s1,\n\t\tCond: p.makeExpr(s2, \"boolean or range expression\"),\n\t\tPost: s3,\n\t\tBody: body,\n\t}\n}\n\n// flags support flagAllowCmd\nfunc (p *parser) parseStmt(flags int) (s ast.Stmt) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Statement\"))\n\t}\n\n\tswitch p.tok {\n\tcase token.TYPE:\n\t\ts = &ast.DeclStmt{Decl: p.parseGenDecl(p.tok, p.parseTypeSpec)}\n\tcase token.CONST, token.VAR:\n\t\ts = &ast.DeclStmt{Decl: p.parseGenDecl(p.tok, p.parseValueSpec)}\n\tcase\n\t\t// tokens that may start an expression\n\t\ttoken.INT, token.FLOAT, token.IMAG, token.RAT, token.CHAR, token.STRING, token.CSTRING, token.PYSTRING, token.FUNC, token.LPAREN, // operands\n\t\ttoken.ADD, token.SUB, token.MUL, token.AND, token.XOR, token.ARROW, token.NOT, token.ENV, // unary operators\n\t\ttoken.LBRACK, token.STRUCT, token.CHAN, token.INTERFACE: // composite types\n\t\tflags = 0\n\t\tfallthrough\n\tcase token.IDENT, token.MAP: // operands\n\t\ts = p.parseSimpleStmt(labelOk, flags)\n\t\t// because of the required look-ahead, labeled statements are\n\t\t// parsed by parseSimpleStmt - don't expect a semicolon after\n\t\t// them\n\t\tif _, isLabeledStmt := s.(*ast.LabeledStmt); !isLabeledStmt {\n\t\t\tp.expectSemi()\n\t\t}\n\tcase token.GO:\n\t\ts = p.parseGoStmt()\n\tcase token.DEFER:\n\t\ts = p.parseDeferStmt()\n\tcase token.RETURN:\n\t\ts = p.parseReturnStmt()\n\tcase token.BREAK, token.CONTINUE, token.GOTO, token.FALLTHROUGH:\n\t\ts = p.parseBranchStmt(p.tok)\n\tcase token.LBRACE:\n\t\ts = p.parseBlockStmt()\n\t\tp.expectSemi()\n\tcase token.IF:\n\t\ts = p.parseIfStmt()\n\tcase token.SWITCH:\n\t\ts = p.parseSwitchStmt()\n\tcase token.SELECT:\n\t\ts = p.parseSelectStmt()\n\tcase token.FOR:\n\t\ts = p.parseForStmt()\n\tcase token.SEMICOLON:\n\t\t// Is it ever possible to have an implicit semicolon\n\t\t// producing an empty statement in a valid program?\n\t\t// (handle correctly anyway)\n\t\ts = &ast.EmptyStmt{Semicolon: p.pos, Implicit: p.lit == \"\\n\"}\n\t\tp.next()\n\tcase token.RBRACE:\n\t\t// a semicolon may be omitted before a closing \"}\"\n\t\ts = &ast.EmptyStmt{Semicolon: p.pos, Implicit: true}\n\tdefault:\n\t\t// no statement found\n\t\tpos := p.pos\n\t\tp.errorExpected(pos, \"statement\", 2)\n\t\tp.advance(stmtStart)\n\t\ts = &ast.BadStmt{From: pos, To: p.pos}\n\t}\n\n\treturn\n}\n\n// ----------------------------------------------------------------------------\n// Declarations\n\ntype parseSpecFunction func(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec\n\nfunc isValidImport(lit string) bool {\n\tconst illegalChars = `!\"#$%&'()*,:;<=>?[\\]^{|}` + \"`\\uFFFD\"\n\ts, _ := strconv.Unquote(lit) // go/scanner returns a legal string literal\n\tfor _, r := range s {\n\t\tif !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn s != \"\"\n}\n\nfunc (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.Spec {\n\tif p.trace {\n\t\tdefer un(trace(p, \"ImportSpec\"))\n\t}\n\n\tvar ident *ast.Ident\n\tswitch p.tok {\n\tcase token.PERIOD:\n\t\tident = &ast.Ident{NamePos: p.pos, Name: \".\"}\n\t\tp.next()\n\tcase token.IDENT:\n\t\tident = p.parseIdent()\n\t}\n\n\tpos := p.pos\n\tvar path string\n\tif p.tok == token.STRING {\n\t\tpath = p.lit\n\t\tif !isValidImport(path) {\n\t\t\tp.error(pos, \"invalid import path: \"+path)\n\t\t}\n\t\tp.next()\n\t} else {\n\t\tp.expect(token.STRING) // use expect() error handling\n\t}\n\tp.expectSemi() // call before accessing p.linecomment\n\n\t// collect imports\n\tspec := &ast.ImportSpec{\n\t\tDoc:     doc,\n\t\tName:    ident,\n\t\tPath:    &ast.BasicLit{ValuePos: pos, Kind: token.STRING, Value: path},\n\t\tComment: p.lineComment,\n\t}\n\tp.imports = append(p.imports, spec)\n\n\treturn spec\n}\n\nfunc (p *parser) inClassFile() bool {\n\treturn p.mode&ParseXGoClass != 0\n}\n\nfunc (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec {\n\tif p.trace {\n\t\tdefer un(trace(p, keyword.String()+\"Spec\"))\n\t}\n\n\tpos := p.pos\n\tvar idents []*ast.Ident\n\tvar typ ast.Expr\n\tvar tag *ast.BasicLit\n\tvar values []ast.Expr\n\tif p.inClassFile() && p.topScope == p.pkgScope && keyword == token.VAR && p.varDeclCnt == 1 {\n\t\tvar starPos token.Pos\n\t\tif p.tok == token.MUL {\n\t\t\tstarPos = p.pos\n\t\t\tp.next()\n\t\t}\n\t\tident := p.parseIdent()\n\t\tif p.tok == token.PERIOD {\n\t\t\tp.next()\n\t\t\ttyp = &ast.SelectorExpr{\n\t\t\t\tX:   ident,\n\t\t\t\tSel: p.parseIdent(),\n\t\t\t}\n\t\t\tif starPos != token.NoPos {\n\t\t\t\ttyp = &ast.StarExpr{\n\t\t\t\t\tStar: starPos,\n\t\t\t\t\tX:    typ,\n\t\t\t\t}\n\t\t\t}\n\t\t} else if starPos != token.NoPos {\n\t\t\ttyp = &ast.StarExpr{\n\t\t\t\tStar: starPos,\n\t\t\t\tX:    ident,\n\t\t\t}\n\t\t} else {\n\t\t\tidents = append(idents, ident)\n\t\t\tfor p.tok == token.COMMA {\n\t\t\t\tp.next()\n\t\t\t\tidents = append(idents, p.parseIdent())\n\t\t\t}\n\t\t\ttyp = p.tryType()\n\t\t\tif p.tok == token.ASSIGN {\n\t\t\t\tp.next()\n\t\t\t\tvalues = p.parseRHSList()\n\t\t\t} else if len(idents) == 1 && typ == nil {\n\t\t\t\ttyp = ident\n\t\t\t\tidents = nil\n\t\t\t}\n\t\t}\n\t\tif p.tok == token.STRING {\n\t\t\ttag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}\n\t\t\tp.next()\n\t\t}\n\t\tp.expect(token.SEMICOLON)\n\t} else {\n\t\tidents = p.parseIdentList()\n\t\ttyp = p.tryType()\n\t\t// always permit optional initialization for more tolerant parsing\n\t\tif p.tok == token.ASSIGN {\n\t\t\tp.next()\n\t\t\tvalues = p.parseRHSList()\n\t\t}\n\t\tp.expectSemi() // call before accessing p.linecomment\n\t}\n\n\tswitch keyword {\n\tcase token.VAR:\n\t\tif typ == nil && values == nil {\n\t\t\tp.error(pos, \"missing variable type or initialization\")\n\t\t}\n\tcase token.CONST:\n\t\tif values == nil && (iota == 0 || typ != nil) {\n\t\t\tp.error(pos, \"missing constant value\")\n\t\t}\n\t}\n\t// Go spec: The scope of a constant or variable identifier declared inside\n\t// a function begins at the end of the ConstSpec or VarSpec and ends at\n\t// the end of the innermost containing block.\n\t// (Global identifiers are resolved in a separate phase after parsing.)\n\tspec := &ast.ValueSpec{\n\t\tDoc:     doc,\n\t\tNames:   idents,\n\t\tType:    typ,\n\t\tTag:     tag,\n\t\tValues:  values,\n\t\tComment: p.lineComment,\n\t}\n\tkind := ast.Con\n\tif keyword == token.VAR {\n\t\tkind = ast.Var\n\t}\n\tp.declare(spec, iota, p.topScope, kind, idents...)\n\n\treturn spec\n}\n\nfunc (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.Spec {\n\tif p.trace {\n\t\tdefer un(trace(p, \"TypeSpec\"))\n\t}\n\n\tident := p.parseIdent()\n\n\t// Go spec: The scope of a type identifier declared inside a function begins\n\t// at the identifier in the TypeSpec and ends at the end of the innermost\n\t// containing block.\n\t// (Global identifiers are resolved in a separate phase after parsing.)\n\tspec := &ast.TypeSpec{Doc: doc, Name: ident}\n\tp.declare(spec, nil, p.topScope, ast.Typ, ident)\n\tif p.tok == token.ASSIGN {\n\t\tspec.Assign = p.pos\n\t\tp.next()\n\t}\n\tspec.Type = p.parseType()\n\tp.expectSemi() // call before accessing p.linecomment\n\tspec.Comment = p.lineComment\n\n\treturn spec\n}\n\nfunc (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl {\n\tif p.trace {\n\t\tdefer un(trace(p, \"GenDecl(\"+keyword.String()+\")\"))\n\t}\n\tif keyword == token.VAR {\n\t\tp.varDeclCnt++\n\t}\n\tdoc := p.leadComment\n\tpos := p.expect(keyword)\n\tvar lparen, rparen token.Pos\n\tvar list []ast.Spec\n\tif p.tok == token.LPAREN {\n\t\tlparen = p.pos\n\t\tp.next()\n\t\tfor iota := 0; p.tok != token.RPAREN && p.tok != token.EOF; iota++ {\n\t\t\tlist = append(list, f(p.leadComment, keyword, iota))\n\t\t}\n\t\trparen = p.expect(token.RPAREN)\n\t\tp.expectSemi()\n\t} else {\n\t\tlist = append(list, f(nil, keyword, 0))\n\t}\n\n\treturn &ast.GenDecl{\n\t\tDoc:    doc,\n\t\tTokPos: pos,\n\t\tTok:    keyword,\n\t\tLparen: lparen,\n\t\tSpecs:  list,\n\t\tRparen: rparen,\n\t}\n}\n\nfunc isOverloadOp(tok token.Token) bool {\n\treturn int(tok) < len(overloadOps) && overloadOps[tok] != 0\n}\n\n// `funcName`\n// `(*T).methodName`\n// `func(params) results {...}`\nfunc (p *parser) parseOverloadFunc() (ast.Expr, bool) {\n\tswitch p.tok {\n\tcase token.IDENT:\n\t\treturn p.parseIdent(), true\n\tcase token.FUNC:\n\t\treturn p.parseFuncTypeOrLit(), true\n\tcase token.LPAREN:\n\t\tx, _ := p.parsePrimaryExpr(nil, 0)\n\t\treturn x, true\n\t}\n\treturn nil, false\n}\n\n// `= (overloadFuncs)`\n//\n// here overloadFunc represents\n//\n// `funcName`\n// `(*T).methodName`\n// `func(params) results {...}`\nfunc (p *parser) parseOverloadDecl(decl *ast.OverloadFuncDecl) *ast.OverloadFuncDecl {\n\tdecl.Assign = p.expect(token.ASSIGN)\n\tdecl.Lparen = p.expect(token.LPAREN)\n\tfuncs := make([]ast.Expr, 0, 4)\n\tfor {\n\t\tf, ok := p.parseOverloadFunc()\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\t\tfuncs = append(funcs, f)\n\t\tif p.tok == token.SEMICOLON {\n\t\t\tp.next()\n\t\t}\n\t}\n\tdecl.Funcs = funcs\n\tdecl.Rparen = p.expect(token.RPAREN)\n\tp.expectSemi()\n\tif debugParseOutput {\n\t\tvar recvt ast.Expr\n\t\tif recv := decl.Recv; recv != nil {\n\t\t\trecvt = recv.List[0].Type\n\t\t}\n\t\tlog.Printf(\"ast.OverloadFuncDecl{Recv: %v, Name: %v, ...}\\n\", recvt, decl.Name)\n\t}\n\treturn decl\n}\n\n// `func identOrOp(params) results {...}`\n// `func identOrOp = (overloadFuncs)`\n//\n// `func T.ident(params) results { ... }`\n// `func .ident(params) results { ... }` (only in classfile)\n//\n// `func (recv) identOrOp(params) results { ... }`\n// `func (T).identOrOp = (overloadFuncs)`\n// `func (params) results { ... }()`\nfunc (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) {\n\tif p.trace {\n\t\tdefer un(trace(p, \"FunctionDeclOrCall\"))\n\t}\n\n\tdoc := p.leadComment\n\tpos := p.expect(token.FUNC)\n\tscope := ast.NewScope(p.topScope) // function scope\n\n\tvar recv, params, results *ast.FieldList\n\tvar ident *ast.Ident\n\tvar isOp, isStatic, isFunLit, ok bool\n\n\tswitch p.tok {\n\tcase token.LPAREN:\n\t\t// method: `func (recv) identOrOp(params) results { ... }`\n\t\t// overload: `func (T).identOrOp = (overloadFuncs)`\n\t\t// funlit: `func (params) results { ... }()`\n\t\tparams = p.parseParameters(scope, true)\n\t\tif p.tok == token.LPAREN {\n\t\t\t// func (params) (results) { ... }()\n\t\t\tisFunLit, results = true, p.parseParameters(scope, false)\n\t\t} else if p.tok == token.PERIOD {\n\t\t\tp.next()\n\t\t\t// func (T).identOrOp = (overloadFuncs)\n\t\t\tident, isOp = p.parseIdentOrOp()\n\t\t\treturn p.parseOverloadDecl(&ast.OverloadFuncDecl{\n\t\t\t\tDoc:      doc,\n\t\t\t\tFunc:     pos,\n\t\t\t\tRecv:     params,\n\t\t\t\tName:     ident,\n\t\t\t\tOperator: isOp,\n\t\t\t}), nil\n\t\t} else if isOp = isOverloadOp(p.tok); isOp {\n\t\t\toldtok, oldpos, oldlit := p.tok, p.pos, p.lit\n\t\t\tp.next()\n\t\t\tif p.tok == token.LPAREN {\n\t\t\t\t// func (recv) op(params) results { ... }\n\t\t\t\trecv, ident = params, &ast.Ident{NamePos: oldpos, Name: oldtok.String()}\n\t\t\t\tparams, results = p.parseSignature(scope)\n\t\t\t} else {\n\t\t\t\t// func (params) typ { ... }()\n\t\t\t\tp.unget(oldpos, oldtok, oldlit)\n\t\t\t\ttyp := p.tryType()\n\t\t\t\tif typ == nil {\n\t\t\t\t\tp.errorExpected(oldpos, \"type\", 1)\n\t\t\t\t\tp.next()\n\t\t\t\t\ttyp = &ast.BadExpr{From: oldpos, To: p.pos}\n\t\t\t\t}\n\t\t\t\tisFunLit, results = true, &ast.FieldList{List: []*ast.Field{{Type: typ}}}\n\t\t\t}\n\t\t} else if typ := p.tryType(); typ == nil {\n\t\t\t// func (params) { ... }()\n\t\t\tisFunLit = true\n\t\t} else if ident, ok = typ.(*ast.Ident); ok && p.tok == token.LPAREN {\n\t\t\t// func (recv) ident(params) results { ... }\n\t\t\trecv = params\n\t\t\tparams, results = p.parseSignature(scope)\n\t\t} else {\n\t\t\t// func (params) typ { ... }()\n\t\t\tisFunLit, results = true, &ast.FieldList{List: []*ast.Field{{Type: typ}}}\n\t\t}\n\t\tif isFunLit {\n\t\t\tbody := p.parseBody(scope)\n\t\t\tfunLit := &ast.FuncLit{\n\t\t\t\tType: &ast.FuncType{\n\t\t\t\t\tFunc:    pos,\n\t\t\t\t\tParams:  params,\n\t\t\t\t\tResults: results,\n\t\t\t\t},\n\t\t\t\tBody: body,\n\t\t\t}\n\t\t\tcall := p.parseCallOrConversion(funLit, false)\n\t\t\tp.expectSemi()\n\t\t\treturn nil, call\n\t\t}\n\tcase token.PERIOD:\n\t\t// func .ident(...) results (only in classfile)\n\t\tif p.inClassFile() {\n\t\t\tp.next()\n\t\t\trecv = &ast.FieldList{}\n\t\t\tisStatic = true\n\t\t}\n\t\tident = p.parseIdent()\n\t\tparams, results = p.parseSignature(scope)\n\tdefault:\n\t\t// func: `func identOrOp(...) results`\n\t\t// overload: `func identOrOp = (overloadFuncs)`\n\t\t// static method: `func T.ident(...) results`\n\t\tident, isOp = p.parseIdentOrOp()\n\t\tswitch p.tok {\n\t\tcase token.ASSIGN:\n\t\t\t// func identOrOp = (overloadFuncs)\n\t\t\treturn p.parseOverloadDecl(&ast.OverloadFuncDecl{\n\t\t\t\tDoc:      doc,\n\t\t\t\tFunc:     pos,\n\t\t\t\tName:     ident,\n\t\t\t\tOperator: isOp,\n\t\t\t}), nil\n\t\tcase token.PERIOD:\n\t\t\t// func T.ident(...) results\n\t\t\tif !isOp {\n\t\t\t\tp.next()\n\t\t\t\trecv = &ast.FieldList{List: []*ast.Field{{Type: ident}}}\n\t\t\t\tident = p.parseIdent()\n\t\t\t\tisStatic = true\n\t\t\t}\n\t\t}\n\t\tparams, results = p.parseSignature(scope)\n\t}\n\n\tif isOp {\n\t\tif params == nil || len(params.List) != 1 {\n\t\t\tp.error(ident.Pos(), \"overload operator can only have one parameter\")\n\t\t}\n\t}\n\tvar body *ast.BlockStmt\n\tswitch p.tok {\n\tcase token.LBRACE:\n\t\tbody = p.parseBody(scope)\n\t\tp.expectSemi()\n\tcase token.SEMICOLON:\n\t\tp.next()\n\t\tif p.tok == token.LBRACE {\n\t\t\t// opening { of function declaration on next line\n\t\t\tp.error(p.pos, \"unexpected semicolon or newline before {\")\n\t\t\tbody = p.parseBody(scope)\n\t\t\tp.expectSemi()\n\t\t}\n\tdefault:\n\t\tp.expectSemi()\n\t}\n\n\tdecl := &ast.FuncDecl{\n\t\tDoc:  doc,\n\t\tRecv: recv,\n\t\tName: ident,\n\t\tType: &ast.FuncType{\n\t\t\tFunc:    pos,\n\t\t\tParams:  params,\n\t\t\tResults: results,\n\t\t},\n\t\tBody:     body,\n\t\tOperator: isOp,\n\t\tStatic:   isStatic,\n\t}\n\tif recv == nil {\n\t\t// Go spec: The scope of an identifier denoting a constant, type,\n\t\t// variable, or function (but not method) declared at top level\n\t\t// (outside any function) is the package block.\n\t\t//\n\t\t// init() functions cannot be referred to and there may\n\t\t// be more than one - don't put them in the pkgScope\n\t\tif ident.Name != \"init\" {\n\t\t\tp.declare(decl, nil, p.pkgScope, ast.Fun, ident)\n\t\t}\n\t}\n\tif debugParseOutput {\n\t\tlog.Printf(\"ast.FuncDecl{Name: %v, ...}\\n\", ident.Name)\n\t}\n\treturn decl, nil\n}\n\nfunc (p *parser) parseDecl(sync map[token.Token]bool) ast.Decl {\n\tif p.trace {\n\t\tdefer un(trace(p, \"Declaration\"))\n\t}\n\tvar f parseSpecFunction\n\tpos := p.pos\n\tswitch p.tok {\n\tcase token.CONST, token.VAR:\n\t\tf = p.parseValueSpec\n\tcase token.TYPE:\n\t\tf = p.parseTypeSpec\n\tcase token.FUNC:\n\t\tdecl, call := p.parseFuncDeclOrCall()\n\t\tif decl != nil {\n\t\t\tif p.errors.Len() != 0 {\n\t\t\t\tp.advance(sync)\n\t\t\t}\n\t\t\treturn decl\n\t\t}\n\t\treturn p.parseGlobalStmts(sync, pos, &ast.ExprStmt{X: call})\n\tdefault:\n\t\treturn p.parseGlobalStmts(sync, pos)\n\t}\n\treturn p.parseGenDecl(p.tok, f)\n}\n\nfunc (p *parser) parseGlobalStmts(sync map[token.Token]bool, pos token.Pos, stmts ...ast.Stmt) *ast.FuncDecl {\n\tp.topScope = ast.NewScope(p.topScope)\n\tdoc := p.leadComment\n\tp.openLabelScope()\n\tlist := p.parseStmtList()\n\tp.closeLabelScope()\n\tp.closeScope()\n\tif stmts != nil {\n\t\tlist = append(stmts, list...)\n\t}\n\tif p.errors.Len() != 0 { // TODO(xsw): error\n\t\tp.advance(sync)\n\t}\n\tif p.tok != token.EOF {\n\t\tp.errorExpected(p.pos, \"statement\", 2)\n\t}\n\n\t// Determine appropriate positions for the block statement\n\t// Use file beginning position as fallback when no statements are available\n\tstartPos := pos\n\tendPos := p.pos\n\tif len(list) > 0 {\n\t\tstartPos = list[0].Pos()\n\t\tendPos = list[len(list)-1].End()\n\t}\n\treturn &ast.FuncDecl{\n\t\tName: ast.NewIdentEx(pos, \"main\", ast.ImplicitFun),\n\t\tDoc:  doc,\n\t\tType: &ast.FuncType{\n\t\t\tFunc:   pos,\n\t\t\tParams: &ast.FieldList{},\n\t\t},\n\t\tBody: &ast.BlockStmt{\n\t\t\tLbrace: startPos,\n\t\t\tList:   list,\n\t\t\tRbrace: endPos,\n\t\t},\n\t\tShadow: true,\n\t}\n}\n\n// ----------------------------------------------------------------------------\n// Source files\n\nfunc (p *parser) parseFile() *ast.File {\n\tif p.trace {\n\t\tdefer un(trace(p, \"File\"))\n\t}\n\n\t// Don't bother parsing the rest if we had errors scanning the first token.\n\t// Likely not a Go source file at all.\n\tif p.errors.Len() != 0 {\n\t\treturn nil\n\t}\n\n\tvar noPkgDecl bool\n\t// package clause\n\tdoc := p.leadComment\n\tvar pos token.Pos\n\tvar ident *ast.Ident\n\tif p.tok == token.PACKAGE {\n\t\tpos = p.expect(token.PACKAGE)\n\t\t// Go spec: The package clause is not a declaration;\n\t\t// the package name does not appear in any scope.\n\t\tident = p.parseIdent()\n\t\tif ident.Name == \"_\" && p.mode&DeclarationErrors != 0 {\n\t\t\tp.error(p.pos, \"invalid package name _\")\n\t\t}\n\t\tp.expectSemi()\n\n\t\t// Don't bother parsing the rest if we had errors parsing the package clause.\n\t\t// Likely not a Go source file at all.\n\t\tif p.errors.Len() != 0 {\n\t\t\treturn nil\n\t\t}\n\t} else {\n\t\tnoPkgDecl = true\n\t\tpos = token.NoPos\n\t\tident = ast.NewIdentEx(p.file.Pos(0), \"main\", ast.ImplicitPkg)\n\t}\n\n\tp.openScope()\n\tp.pkgScope = p.topScope\n\tvar decls []ast.Decl\n\tvar shadowEntry *ast.FuncDecl\n\tif p.mode&PackageClauseOnly == 0 {\n\t\t// import decls\n\t\tfor p.tok == token.IMPORT {\n\t\t\tdecls = append(decls, p.parseGenDecl(token.IMPORT, p.parseImportSpec))\n\t\t}\n\n\t\tif p.mode&ImportsOnly == 0 {\n\t\t\t// rest of package body\n\t\t\tfor p.tok != token.EOF {\n\t\t\t\tdecls = append(decls, p.parseDecl(declStart))\n\t\t\t}\n\t\t\tif n := len(decls); n > 0 {\n\t\t\t\tif f, ok := decls[n-1].(*ast.FuncDecl); ok && f.Shadow {\n\t\t\t\t\tshadowEntry = f\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tp.closeScope()\n\tassert(p.topScope == nil, \"unbalanced scopes\")\n\tassert(p.labelScope == nil, \"unbalanced label scopes\")\n\n\treturn &ast.File{\n\t\tDoc:         doc,\n\t\tPackage:     pos,\n\t\tName:        ident,\n\t\tDecls:       decls,\n\t\tImports:     p.imports,\n\t\tComments:    p.comments,\n\t\tShadowEntry: shadowEntry,\n\t\tNoPkgDecl:   noPkgDecl,\n\t}\n}\n"
  },
  {
    "path": "parser/parser_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage parser\n\nimport (\n\t\"io/fs\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n\tfsx \"github.com/qiniu/x/http/fs\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype fileInfo struct {\n\t*fsx.FileInfo\n}\n\nfunc (p fileInfo) Info() (fs.FileInfo, error) {\n\treturn nil, fs.ErrNotExist\n}\n\nfunc TestFilter(t *testing.T) {\n\td := fsx.NewFileInfo(\"foo.go\", 10)\n\tif filter(d, func(fi fs.FileInfo) bool {\n\t\treturn false\n\t}) {\n\t\tt.Fatal(\"TestFilter: true?\")\n\t}\n\tif !filter(d, func(fi fs.FileInfo) bool {\n\t\treturn true\n\t}) {\n\t\tt.Fatal(\"TestFilter: false?\")\n\t}\n\td2 := fileInfo{d}\n\tif filter(d2, func(fi fs.FileInfo) bool {\n\t\treturn true\n\t}) {\n\t\tt.Fatal(\"TestFilter: true?\")\n\t}\n}\n\nfunc TestAssert(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e != \"go/parser internal error: panic msg\" {\n\t\t\tt.Fatal(\"TestAssert:\", e)\n\t\t}\n\t}()\n\tassert(false, \"panic msg\")\n}\n\nfunc panicMsg(e any) string {\n\tswitch v := e.(type) {\n\tcase string:\n\t\treturn v\n\tcase error:\n\t\treturn v.Error()\n\t}\n\treturn \"\"\n}\n\nfunc testErrCode(t *testing.T, code string, errExp, panicExp string) {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tif panicMsg(e) != panicExp {\n\t\t\t\tt.Fatal(\"testErrCode panic:\", e)\n\t\t\t}\n\t\t}\n\t}()\n\tt.Helper()\n\tfset := token.NewFileSet()\n\t_, err := Parse(fset, \"/foo/bar.xgo\", code, 0)\n\tif err == nil || err.Error() != errExp {\n\t\tt.Fatal(\"testErrCode error:\", err)\n\t}\n}\n\nfunc testShadowEntry(t *testing.T, code string, errExp string, decl *ast.FuncDecl) {\n\tt.Helper()\n\tfset := token.NewFileSet()\n\tf, err := ParseEntry(fset, \"/foo/bar.xgo\", code, Config{\n\t\tMode: ParseXGoClass | ParseComments | AllErrors,\n\t})\n\tif err != nil && err.Error() != errExp {\n\t\tt.Fatal(\"testShadowEntry error:\", err)\n\t}\n\tif err == nil && errExp != \"\" {\n\t\tt.Fatal(\"testShadowEntry: nil, errExp:\", errExp)\n\t}\n\n\tif f.ShadowEntry == nil {\n\t\tt.Fatal(\"testShadowEntry: nil\")\n\t}\n\tif f.ShadowEntry.Body.Lbrace != decl.Body.Lbrace || f.ShadowEntry.Body.Rbrace != decl.Body.Rbrace {\n\t\tt.Fatal(\"testShadowEntry: brace mismatch\", f.ShadowEntry.Body.Lbrace, decl.Body.Lbrace, f.ShadowEntry.Body.Rbrace, decl.Body.Rbrace)\n\t}\n}\n\nfunc testErrCodeParseExpr(t *testing.T, code string, errExp, panicExp string) {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tif panicMsg(e) != panicExp {\n\t\t\t\tt.Fatal(\"testErrCodeParseExpr panic:\", e)\n\t\t\t}\n\t\t}\n\t}()\n\tt.Helper()\n\t_, err := ParseExpr(code)\n\tif err == nil || err.Error() != errExp {\n\t\tt.Fatal(\"testErrCodeParseExpr error:\", err)\n\t}\n}\n\nfunc testClassErrCode(t *testing.T, code string, errExp, panicExp string) {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tif panicMsg(e) != panicExp {\n\t\t\t\tt.Fatal(\"testErrCode panic:\", e)\n\t\t\t}\n\t\t}\n\t}()\n\tt.Helper()\n\tfset := token.NewFileSet()\n\t_, err := Parse(fset, \"/foo/bar.gox\", code, ParseXGoClass)\n\tif err == nil || err.Error() != errExp {\n\t\tt.Fatal(\"testErrCode error:\", err)\n\t}\n}\n\nfunc TestErrLabel(t *testing.T) {\n\ttestErrCode(t, `a.x:`, `/foo/bar.xgo:1:4: illegal label declaration`, ``)\n}\n\nfunc TestErrTplLit(t *testing.T) {\n\ttestErrCode(t, \"tpl`a =`\", `/foo/bar.xgo:1:8: expected ';', found 'EOF' (and 1 more errors)`, ``)\n}\n\nfunc TestErrOperand(t *testing.T) {\n\ttestErrCode(t, `a :=`, `/foo/bar.xgo:1:5: expected operand, found 'EOF'`, ``)\n}\n\nfunc TestErrMissingComma(t *testing.T) {\n\ttestErrCode(t, `func a(b int c)`, `/foo/bar.xgo:1:14: missing ',' in parameter list`, ``)\n}\n\nfunc TestErrLambda(t *testing.T) {\n\ttestErrCode(t, `func test(v string, f func( int)) {\n}\ntest \"hello\" => {\n\tprintln \"lambda\",x\n}\n`, `/foo/bar.xgo:3:6: expected 'IDENT', found \"hello\"`, ``)\n\ttestErrCode(t, `func test(v string, f func( int)) {\n}\ntest \"hello\", \"x\" => {\n\tprintln \"lambda\",x\n}\n`, `/foo/bar.xgo:3:15: expected 'IDENT', found \"x\"`, ``)\n\ttestErrCode(t, `func test(v string, f func( int)) {\n}\ntest \"hello\", (\"x\") => {\n\tprintln \"lambda\",x\n}\n`, `/foo/bar.xgo:3:16: expected 'IDENT', found \"x\"`, ``)\n\ttestErrCode(t, `func test(v string, f func(int,int)) {\n}\ntest \"hello\", (x, \"y\") => {\n\tprintln \"lambda\",x,y\n}\n`, `/foo/bar.xgo:3:19: expected 'IDENT', found \"y\"`, ``)\n\ttestErrCode(t, `onTouchStart \"someone\" => {\n\tsay \"touched by someone\"\n}\n`, `/foo/bar.xgo:1:14: expected 'IDENT', found \"someone\"`, ``)\n}\n\nfunc TestErrTooManyParseExpr(t *testing.T) {\n\ttestErrCodeParseExpr(t, `func() int {\n  var\n  var\n  var\n  var\n  var\n  var\n  var\n  var\n  var\n  var\n  var\n  var\n}()\n`, `3:3: expected 'IDENT', found 'var' (and 10 more errors)`, ``)\n}\n\nfunc TestErrTooMany(t *testing.T) {\n\ttestErrCode(t, `\nfunc f() { var }\nfunc g() { var }\nfunc h() { var }\nfunc h() { var }\nfunc h() { var }\nfunc h() { var }\nfunc h() { var }\nfunc h() { var }\nfunc h() { var }\nfunc h() { var }\nfunc h() { var }\nfunc h() { var }\n`, `/foo/bar.xgo:2:16: expected 'IDENT', found '}' (and 10 more errors)`, ``)\n}\n\nfunc TestErrInFunc(t *testing.T) {\n\ttestErrCode(t, `func test() {\n\ta,\n}`, `/foo/bar.xgo:2:2: expected 1 expression (and 2 more errors)`, ``)\n\ttestErrCode(t, `func test() {\n\ta.test, => {\n\t}\n}`, `/foo/bar.xgo:2:10: expected operand, found '=>' (and 1 more errors)`, ``)\n\ttestErrCode(t, `func test() {\n\t\t,\n\t}\n}`, `/foo/bar.xgo:2:3: expected statement, found ',' (and 1 more errors)`, ``)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc TestClassErrCode(t *testing.T) {\n\ttestClassErrCode(t, `var (\n\tA,B\n\tv int\n)\n`, `/foo/bar.gox:2:2: missing variable type or initialization`, ``)\n\ttestClassErrCode(t, `var (\n\tA.*B\n\tv int\n)\n`, `/foo/bar.gox:2:4: expected 'IDENT', found '*' (and 2 more errors)`, ``)\n\ttestClassErrCode(t, `var (\n\t[]A\n\tv int\n)\n`, `/foo/bar.gox:2:2: expected 'IDENT', found '['`, ``)\n\ttestClassErrCode(t, `var (\n\t*[]A\n\tv int\n)\n`, `/foo/bar.gox:2:3: expected 'IDENT', found '['`, ``)\n\ttestClassErrCode(t, `\nvar (\n)\nconst c = 100\nconst d\n`, `/foo/bar.gox:5:7: missing constant value`, ``)\n}\n\nfunc TestErrGlobal(t *testing.T) {\n\ttestErrCode(t, `func test() {}\n}`, `/foo/bar.xgo:2:1: expected statement, found '}'`, ``)\n}\n\nfunc TestErrCompositeLiteral(t *testing.T) {\n\ttestErrCode(t, `println (T[int]){a: 1, b: 2}\n`, `/foo/bar.xgo:1:10: cannot parenthesize type in composite literal`, ``)\n}\n\nfunc TestErrCondExpr(t *testing.T) {\n\ttestErrCode(t, `\nx@*p\n`, `/foo/bar.xgo:2:3: expected condition expression, found '*'`, ``)\n\ttestErrCode(t, `\nx@(a, b)\n`, `/foo/bar.xgo:2:3: invalid condition expression`, ``)\n}\n\nfunc TestErrSelectorExpr(t *testing.T) {\n\ttestErrCode(t, `\nx.\n*p\n`, `/foo/bar.xgo:3:1: expected selector or type assertion, found '*'`, ``)\n\ttestErrCode(t, `\nx.$\na = 1\n`, \"/foo/bar.xgo:3:1: expected identifier after '$', found a\", ``)\n\ttestErrCode(t, `\nx./\n`, `/foo/bar.xgo:2:3: expected selector or type assertion, found '/'`, ``)\n\ttestErrCode(t, `\nx.**.$\n`, `/foo/bar.xgo:2:6: expected identifier after '**.', found '$'`, ``)\n}\n\nfunc TestErrStringLitEx(t *testing.T) {\n\ttestErrCode(t, `\nprintln \"${ ... }\"\n`, \"/foo/bar.xgo:2:13: expected operand, found '...'\", ``)\n\ttestErrCode(t, `\nprintln \"${b\"\n`, \"/foo/bar.xgo:2:11: invalid $ expression: ${ doesn't end with }\", ``)\n\ttestErrCode(t, `\nprintln \"$a${b}\"\n`, \"/foo/bar.xgo:2:10: invalid $ expression: neither `${ ... }` nor `$$`\", ``)\n}\n\nfunc TestErrStringLiteral(t *testing.T) {\n\ttestErrCode(t, `run \"\n`, `/foo/bar.xgo:1:5: string literal not terminated`, ``)\n}\n\nfunc TestErrFieldDecl(t *testing.T) {\n\ttestErrCode(t, `\ntype T struct {\n\t*(Foo)\n}\n`, `/foo/bar.xgo:3:3: cannot parenthesize embedded type`, ``)\n\ttestErrCode(t, `\ntype T struct {\n\t(Foo)\n}\n`, `/foo/bar.xgo:3:2: cannot parenthesize embedded type`, ``)\n\ttestErrCode(t, `\ntype T struct {\n\t(*Foo)\n}\n`, `/foo/bar.xgo:3:2: cannot parenthesize embedded type`, ``)\n}\n\nfunc TestParseFieldDecl(t *testing.T) {\n\tvar p parser\n\tp.init(token.NewFileSet(), \"/foo/bar.xgo\", []byte(`type T struct {\n}\n`), 0)\n\tp.parseFieldDecl(nil)\n}\n\nfunc TestCheckExpr(t *testing.T) {\n\tvar p parser\n\tp.init(token.NewFileSet(), \"/foo/bar.xgo\", []byte(``), 0)\n\tp.checkExpr(&ast.Ellipsis{})\n\tp.checkExpr(&ast.ElemEllipsis{})\n\tp.checkExpr(&ast.StarExpr{})\n\tp.checkExpr(&ast.IndexListExpr{})\n\tp.checkExpr(&ast.FuncType{})\n\tp.checkExpr(&ast.FuncLit{})\n}\n\nfunc TestErrTupleType(t *testing.T) {\n\ttestErrCode(t, `var a (a float64, x int, chan int)\n`, `/foo/bar.xgo:1:8: mixed named and unnamed fields in tuple type`, ``)\n\ttestErrCode(t, `var a (a float64, x int, string)\n`, `/foo/bar.xgo:1:8: mixed named and unnamed fields in tuple type`, ``)\n}\n\nfunc TestErrFuncDecl(t *testing.T) {\n\ttestErrCode(t, `func test()\n{\n}\n`, `/foo/bar.xgo:2:1: unexpected semicolon or newline before {`, ``)\n\ttestErrCode(t, `func test() +1\n`, `/foo/bar.xgo:1:13: expected ';', found '+'`, ``)\n\ttestErrCode(t, `\nfunc (a T) +{}\n`, `/foo/bar.xgo:2:12: expected type, found '+'`, ``)\n\ttestErrCode(t, `func +(a T, b T) {}\n`, `/foo/bar.xgo:1:6: overload operator can only have one parameter`, ``)\n}\n\nfunc TestErrForIn(t *testing.T) {\n\ttestErrCode(t, `x := [a for a i b]\n`, `/foo/bar.xgo:1:15: expected 'in', found i`, ``)\n}\n\nfunc TestErrKwargExpr(t *testing.T) {\n\ttestErrCode(t, `\nf a=1, 13\n`, `/foo/bar.xgo:2:8: positional argument follows keyword argument`, ``)\n}\n\nfunc TestNumberUnitLit(t *testing.T) {\n\tvar p parser\n\tp.checkExpr(&ast.NumberUnitLit{})\n\tp.toIdent(&ast.NumberUnitLit{})\n}\n\nfunc TestImplicitIdent(t *testing.T) {\n\tif ast.NewIdentEx(100, \"foo\", ast.ImplicitPkg).End() != 100 {\n\t\tt.Fatal(\"TestImplicitPkg: not 100\")\n\t}\n\tif ast.NewIdentEx(100, \"foo\", ast.ImplicitFun).End() != 100 {\n\t\tt.Fatal(\"TestImplicitFun: not 100\")\n\t}\n\tif ast.NewIdentEx(100, \"foo\", ast.Fun).End() != 103 {\n\t\tt.Fatal(\"TestFun: not 103\")\n\t}\n}\n\nfunc TestErrGlobalVarWithSyntaxError(t *testing.T) {\n\t// Parse the code\n\ttestShadowEntry(t, `var (\n\tfoo int.=2\n\tsprites []Sprite\n)\n\nfunc reset() {\n\tfoo = 10\n\tsprites = make([]Sprite, 0)\n}\n\nonStart => {\n\treset()\n}\n`, `/foo/bar.xgo:2:10: expected 'IDENT', found '=' (and 19 more errors)`, &ast.FuncDecl{\n\t\tBody: &ast.BlockStmt{\n\t\t\tLbrace: 119,\n\t\t\tRbrace: 121,\n\t\t},\n\t})\n\n\ttestShadowEntry(t, `var (\n\tfoo int\n\tsprites []Sprite\n)\n\nfunc reset() {\n\tfoo = 10\n\tsprites = make([]Sprite, 0)\n}\n\nonStart => {\n\treset()\n}`, \"\", &ast.FuncDecl{\n\t\tBody: &ast.BlockStmt{\n\t\t\tLbrace: 94,\n\t\t\tRbrace: 117,\n\t\t},\n\t})\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "parser/parser_xgo.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage parser\n\nimport (\n\t\"errors\"\n\t\"io/fs\"\n\t\"path\"\n\t\"strings\"\n\n\tgoast \"go/ast\"\n\tgoparser \"go/parser\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/parser/fsx\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/qiniu/x/stream\"\n)\n\nconst (\n\tDbgFlagParseOutput = 1 << iota\n\tDbgFlagParseError\n\tDbgFlagAll = DbgFlagParseOutput | DbgFlagParseError\n)\n\nvar (\n\tdebugParseOutput bool\n\tdebugParseError  bool\n)\n\nfunc SetDebug(dbgFlags int) {\n\tdebugParseOutput = (dbgFlags & DbgFlagParseOutput) != 0\n\tdebugParseError = (dbgFlags & DbgFlagParseError) != 0\n}\n\nvar (\n\tErrUnknownFileKind = errors.New(\"unknown file kind\")\n)\n\ntype FileSystem = fsx.FileSystem\n\n// -----------------------------------------------------------------------------\n\n// Parse parses a single XGo source file. The filename specifies the XGo source file.\n// If the file couldn't be read, a nil map and the respective error are returned.\nfunc Parse(fset *token.FileSet, filename string, src any, mode Mode) (pkgs map[string]*ast.Package, err error) {\n\tfile, err := ParseFile(fset, filename, src, mode)\n\tif err != nil {\n\t\treturn\n\t}\n\tpkgs = make(map[string]*ast.Package)\n\tpkgs[file.Name.Name] = astFileToPkg(file, filename)\n\treturn\n}\n\n// astFileToPkg translate ast.File to ast.Package\nfunc astFileToPkg(file *ast.File, fileName string) (pkg *ast.Package) {\n\tpkg = &ast.Package{\n\t\tName:  file.Name.Name,\n\t\tFiles: make(map[string]*ast.File),\n\t}\n\tpkg.Files[fileName] = file\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\n// ParseDir calls ParseFSDir by passing a local filesystem.\nfunc ParseDir(fset *token.FileSet, path string, filter func(fs.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {\n\treturn ParseFSDir(fset, fsx.Local, path, Config{Filter: filter, Mode: mode})\n}\n\ntype Config struct {\n\tClassKind func(fname string) (isProj, ok bool)\n\tFilter    func(fs.FileInfo) bool\n\tMode      Mode\n}\n\n// ParseDirEx calls ParseFSDir by passing a local filesystem.\nfunc ParseDirEx(fset *token.FileSet, path string, conf Config) (pkgs map[string]*ast.Package, first error) {\n\treturn ParseFSDir(fset, fsx.Local, path, conf)\n}\n\n// ParseEntry calls ParseFSEntry by passing a local filesystem.\nfunc ParseEntry(fset *token.FileSet, filename string, src any, conf Config) (f *ast.File, err error) {\n\treturn ParseFSEntry(fset, fsx.Local, filename, src, conf)\n}\n\n// ParseFSDir calls ParseFile for all XGo files in the directory specified by\n// path and returns a map of package name -> package AST with all the packages\n// found.\n//\n// If filter != nil, only the XGo files with fs.FileInfo entries passing through\n// the filter are considered. The mode bits are passed to ParseFile unchanged.\n// Position information is recorded in fset, which must not be nil.\n//\n// If the directory couldn't be read, a nil map and the respective error are\n// returned. If a parse error occurred, a non-nil but incomplete map and the\n// first error encountered are returned.\nfunc ParseFSDir(fset *token.FileSet, fs FileSystem, dir string, conf Config) (pkgs map[string]*ast.Package, first error) {\n\tif conf.Mode&SaveAbsFile != 0 {\n\t\tdir, _ = fs.Abs(dir)\n\t\tconf.Mode &^= SaveAbsFile\n\t}\n\tlist, err := fs.ReadDir(dir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif conf.ClassKind == nil {\n\t\tconf.ClassKind = defaultClassKind\n\t}\n\tpkgs = make(map[string]*ast.Package)\n\tfor _, d := range list {\n\t\tif d.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tfname := d.Name()\n\t\text := path.Ext(fname)\n\t\tvar isProj, isClass, isNormalGox, useGoParser bool\n\t\tswitch ext {\n\t\tcase \".xgo\", \".gop\":\n\t\tcase \".go\":\n\t\t\tif strings.HasPrefix(fname, \"xgo_autogen\") || strings.HasPrefix(fname, \"gop_autogen\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tuseGoParser = (conf.Mode & ParseGoAsGoPlus) == 0\n\t\tcase \".gox\":\n\t\t\tisNormalGox = true\n\t\t\tfallthrough\n\t\tdefault:\n\t\t\tif isProj, isClass = conf.ClassKind(fname); isClass {\n\t\t\t\tisNormalGox = false\n\t\t\t} else if isNormalGox { // not found XGo class by ext, but is a .gox file\n\t\t\t\tisClass = true\n\t\t\t} else { // unknown fileKind\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tmode := conf.Mode\n\t\tif isClass {\n\t\t\tmode |= ParseXGoClass\n\t\t}\n\t\tif !strings.HasPrefix(fname, \"_\") && (conf.Filter == nil || filter(d, conf.Filter)) {\n\t\t\tfilename := fs.Join(dir, fname)\n\t\t\tif useGoParser {\n\t\t\t\tif filedata, err := fs.ReadFile(filename); err == nil {\n\t\t\t\t\tif src, err := goparser.ParseFile(fset, filename, filedata, goparser.Mode(conf.Mode&goReservedFlags)); err == nil {\n\t\t\t\t\t\tpkg := reqPkg(pkgs, src.Name.Name)\n\t\t\t\t\t\tif pkg.GoFiles == nil {\n\t\t\t\t\t\t\tpkg.GoFiles = make(map[string]*goast.File)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpkg.GoFiles[filename] = src\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfirst = err\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfirst = err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tf, err := ParseFSFile(fset, fs, filename, nil, mode)\n\t\t\t\tif f != nil {\n\t\t\t\t\tf.IsProj, f.IsClass = isProj, isClass\n\t\t\t\t\tf.IsNormalGox = isNormalGox\n\t\t\t\t\tif f.Name != nil {\n\t\t\t\t\t\tpkg := reqPkg(pkgs, f.Name.Name)\n\t\t\t\t\t\tpkg.Files[filename] = f\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif err != nil && first == nil {\n\t\t\t\t\tfirst = err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// ParseFSEntry parses the source code of a single XGo source file and returns the corresponding ast.File node.\n// Compared to ParseFSFile, ParseFSEntry detects fileKind by its filename.\nfunc ParseFSEntry(fset *token.FileSet, fs FileSystem, filename string, src any, conf Config) (f *ast.File, err error) {\n\tfname := fs.Base(filename)\n\text := path.Ext(fname)\n\tvar isProj, isClass, isNormalGox bool\n\tswitch ext {\n\tcase \".xgo\", \".gop\", \".go\":\n\tcase \".gox\":\n\t\tisNormalGox = true\n\t\tfallthrough\n\tdefault:\n\t\tif conf.ClassKind == nil {\n\t\t\tconf.ClassKind = defaultClassKind\n\t\t}\n\t\tif isProj, isClass = conf.ClassKind(fname); isClass {\n\t\t\tisNormalGox = false\n\t\t} else if isNormalGox { // not found XGo class by ext, but is a .gox file\n\t\t\tisClass = true\n\t\t} else {\n\t\t\treturn nil, ErrUnknownFileKind\n\t\t}\n\t}\n\tmode := conf.Mode\n\tif isClass {\n\t\tmode |= ParseXGoClass\n\t}\n\tf, err = ParseFSFile(fset, fs, filename, src, mode)\n\tif f != nil {\n\t\tf.IsProj, f.IsClass = isProj, isClass\n\t\tf.IsNormalGox = isNormalGox\n\t}\n\treturn\n}\n\nfunc filter(d fs.DirEntry, fn func(fs.FileInfo) bool) bool {\n\tfi, err := d.Info()\n\treturn err == nil && fn(fi)\n}\n\nfunc reqPkg(pkgs map[string]*ast.Package, name string) *ast.Package {\n\tpkg, found := pkgs[name]\n\tif !found {\n\t\tpkg = &ast.Package{\n\t\t\tName:  name,\n\t\t\tFiles: make(map[string]*ast.File),\n\t\t}\n\t\tpkgs[name] = pkg\n\t}\n\treturn pkg\n}\n\nfunc defaultClassKind(fname string) (isProj bool, ok bool) {\n\text := path.Ext(fname)\n\tswitch ext {\n\tcase \".spx\":\n\t\treturn fname == \"main.spx\", true\n\tcase \".gsh\", \".gmx\":\n\t\treturn true, true\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\n// ParseFiles parses XGo source files and returns the corresponding packages.\nfunc ParseFiles(fset *token.FileSet, files []string, mode Mode) (map[string]*ast.Package, error) {\n\treturn ParseFSFiles(fset, fsx.Local, files, mode)\n}\n\n// ParseFSFiles parses XGo source files and returns the corresponding packages.\nfunc ParseFSFiles(fset *token.FileSet, fs FileSystem, files []string, mode Mode) (map[string]*ast.Package, error) {\n\tret := map[string]*ast.Package{}\n\tfabs := (mode & SaveAbsFile) != 0\n\tif fabs {\n\t\tmode &^= SaveAbsFile\n\t}\n\tfor _, file := range files {\n\t\tif fabs {\n\t\t\tfile, _ = fs.Abs(file)\n\t\t}\n\t\tf, err := ParseFSFile(fset, fs, file, nil, mode)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpkgName := f.Name.Name\n\t\tpkg, ok := ret[pkgName]\n\t\tif !ok {\n\t\t\tpkg = &ast.Package{Name: pkgName, Files: make(map[string]*ast.File)}\n\t\t\tret[pkgName] = pkg\n\t\t}\n\t\tpkg.Files[file] = f\n\t}\n\treturn ret, nil\n}\n\n// ParseEntries parses XGo source files and returns the corresponding packages.\n// Compared to ParseFiles, ParseEntries detects fileKind by its filename.\nfunc ParseEntries(fset *token.FileSet, files []string, conf Config) (map[string]*ast.Package, error) {\n\treturn ParseFSEntries(fset, fsx.Local, files, conf)\n}\n\n// ParseFSEntries parses XGo source files and returns the corresponding packages.\n// Compared to ParseFSFiles, ParseFSEntries detects fileKind by its filename.\nfunc ParseFSEntries(fset *token.FileSet, fs FileSystem, files []string, conf Config) (map[string]*ast.Package, error) {\n\tret := map[string]*ast.Package{}\n\tfabs := (conf.Mode & SaveAbsFile) != 0\n\tif fabs {\n\t\tconf.Mode &^= SaveAbsFile\n\t}\n\tfor _, file := range files {\n\t\tif fabs {\n\t\t\tfile, _ = fs.Abs(file)\n\t\t}\n\t\tf, err := ParseFSEntry(fset, fs, file, nil, conf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpkgName := f.Name.Name\n\t\tpkg, ok := ret[pkgName]\n\t\tif !ok {\n\t\t\tpkg = &ast.Package{Name: pkgName, Files: make(map[string]*ast.File)}\n\t\t\tret[pkgName] = pkg\n\t\t}\n\t\tpkg.Files[file] = f\n\t}\n\treturn ret, nil\n}\n\n// -----------------------------------------------------------------------------\n\n// ParseFile parses the source code of a single XGo source file and returns the corresponding ast.File node.\nfunc ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast.File, err error) {\n\treturn ParseFSFile(fset, fsx.Local, filename, src, mode)\n}\n\n// ParseFSFile parses the source code of a single XGo source file and returns the corresponding ast.File node.\nfunc ParseFSFile(fset *token.FileSet, fs FileSystem, filename string, src any, mode Mode) (f *ast.File, err error) {\n\tcode, err := readSourceFS(fs, filename, src)\n\tif err != nil {\n\t\treturn\n\t}\n\tif mode&SaveAbsFile != 0 {\n\t\tfilename, _ = fs.Abs(filename)\n\t}\n\treturn parseFile(fset, filename, code, mode)\n}\n\nfunc readSourceFS(fs FileSystem, filename string, src any) ([]byte, error) {\n\tif src != nil {\n\t\treturn stream.ReadSource(src)\n\t}\n\treturn fs.ReadFile(filename)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "parser/parserdir_go118_test.go",
    "content": "//go:build go1.18\n// +build go1.18\n\n/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage parser\n\nimport (\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/parser/parsertest\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nvar testStdCode = `package bar; import \"io\"\n\t// comment\n\tx := 0\n\tif t := false; t {\n\t\tx = 3\n\t} else {\n\t\tx = 5\n\t}\n\tprintln(\"x:\", x)\n\n\t// comment 1\n\t// comment 2\n\tx = 0\n\tswitch s := \"Hello\"; s {\n\tdefault:\n\t\tx = 7\n\tcase \"world\", \"hi\":\n\t\tx = 5\n\tcase \"xsw\":\n\t\tx = 3\n\t}\n\tprintln(\"x:\", x)\n\n\tc := make(chan bool, 100)\n\tselect {\n\tcase c <- true:\n\tcase v := <-c:\n\tdefault:\n\t\tpanic(\"error\")\n\t}\n`\n\nfunc TestStd(t *testing.T) {\n\tfset := token.NewFileSet()\n\tpkgs, err := Parse(fset, \"/foo/bar.xgo\", testStdCode, ParseComments)\n\tif err != nil || len(pkgs) != 1 {\n\t\tt.Fatal(\"Parse failed:\", err, len(pkgs))\n\t}\n\tbar := pkgs[\"bar\"]\n\tparsertest.Expect(t, bar, `package bar\n\nfile bar.xgo\nnoEntrypoint\nast.GenDecl:\n  Tok: import\n  Specs:\n    ast.ImportSpec:\n      Path:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"io\"\nast.FuncDecl:\n  Doc:\n    ast.CommentGroup:\n      List:\n        ast.Comment:\n          Text: // comment\n  Name:\n    ast.Ident:\n      Name: main\n  Type:\n    ast.FuncType:\n      Params:\n        ast.FieldList:\n  Body:\n    ast.BlockStmt:\n      List:\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: x\n          Tok: :=\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 0\n        ast.IfStmt:\n          Init:\n            ast.AssignStmt:\n              Lhs:\n                ast.Ident:\n                  Name: t\n              Tok: :=\n              Rhs:\n                ast.Ident:\n                  Name: false\n          Cond:\n            ast.Ident:\n              Name: t\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.AssignStmt:\n                  Lhs:\n                    ast.Ident:\n                      Name: x\n                  Tok: =\n                  Rhs:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 3\n          Else:\n            ast.BlockStmt:\n              List:\n                ast.AssignStmt:\n                  Lhs:\n                    ast.Ident:\n                      Name: x\n                  Tok: =\n                  Rhs:\n                    ast.BasicLit:\n                      Kind: INT\n                      Value: 5\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"x:\"\n                ast.Ident:\n                  Name: x\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: x\n          Tok: =\n          Rhs:\n            ast.BasicLit:\n              Kind: INT\n              Value: 0\n        ast.SwitchStmt:\n          Init:\n            ast.AssignStmt:\n              Lhs:\n                ast.Ident:\n                  Name: s\n              Tok: :=\n              Rhs:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"Hello\"\n          Tag:\n            ast.Ident:\n              Name: s\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.CaseClause:\n                  Body:\n                    ast.AssignStmt:\n                      Lhs:\n                        ast.Ident:\n                          Name: x\n                      Tok: =\n                      Rhs:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 7\n                ast.CaseClause:\n                  List:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"world\"\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"hi\"\n                  Body:\n                    ast.AssignStmt:\n                      Lhs:\n                        ast.Ident:\n                          Name: x\n                      Tok: =\n                      Rhs:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 5\n                ast.CaseClause:\n                  List:\n                    ast.BasicLit:\n                      Kind: STRING\n                      Value: \"xsw\"\n                  Body:\n                    ast.AssignStmt:\n                      Lhs:\n                        ast.Ident:\n                          Name: x\n                      Tok: =\n                      Rhs:\n                        ast.BasicLit:\n                          Kind: INT\n                          Value: 3\n        ast.ExprStmt:\n          X:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: println\n              Args:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"x:\"\n                ast.Ident:\n                  Name: x\n        ast.AssignStmt:\n          Lhs:\n            ast.Ident:\n              Name: c\n          Tok: :=\n          Rhs:\n            ast.CallExpr:\n              Fun:\n                ast.Ident:\n                  Name: make\n              Args:\n                ast.ChanType:\n                  Value:\n                    ast.Ident:\n                      Name: bool\n                ast.BasicLit:\n                  Kind: INT\n                  Value: 100\n        ast.SelectStmt:\n          Body:\n            ast.BlockStmt:\n              List:\n                ast.CommClause:\n                  Comm:\n                    ast.SendStmt:\n                      Chan:\n                        ast.Ident:\n                          Name: c\n                      Values:\n                        ast.Ident:\n                          Name: true\n                ast.CommClause:\n                  Comm:\n                    ast.AssignStmt:\n                      Lhs:\n                        ast.Ident:\n                          Name: v\n                      Tok: :=\n                      Rhs:\n                        ast.UnaryExpr:\n                          Op: <-\n                          X:\n                            ast.Ident:\n                              Name: c\n                ast.CommClause:\n                  Body:\n                    ast.ExprStmt:\n                      X:\n                        ast.CallExpr:\n                          Fun:\n                            ast.Ident:\n                              Name: panic\n                          Args:\n                            ast.BasicLit:\n                              Kind: STRING\n                              Value: \"error\"\n`)\n}\n\nfunc TestFromInstance(t *testing.T) {\n\ttestFromDir(t, \"\", \"./_instance\")\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "parser/parserdir_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage parser\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"path\"\n\t\"reflect\"\n\t\"strings\"\n\t\"syscall\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/parser/fsx\"\n\t\"github.com/goplus/xgo/parser/fsx/memfs\"\n\t\"github.com/goplus/xgo/parser/parsertest\"\n\t\"github.com/goplus/xgo/scanner\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/qiniu/x/log\"\n\t\"github.com/qiniu/x/stream\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc init() {\n\tlog.SetFlags(log.Llongfile)\n\tSetDebug(DbgFlagAll)\n}\n\nfunc TestParseExprFrom(t *testing.T) {\n\tfset := token.NewFileSet()\n\tif _, err := ParseExprFrom(fset, \"/foo/bar/not-exists\", nil, 0); err == nil {\n\t\tt.Fatal(\"ParseExprFrom: no error?\")\n\t}\n\tif _, err := ParseExpr(\"1+1\\n;\"); err == nil || err.Error() != \"2:1: expected 'EOF', found ';'\" {\n\t\tt.Fatal(\"ParseExpr:\", err)\n\t}\n}\n\nfunc TestReadSource(t *testing.T) {\n\tbuf := bytes.NewBuffer(nil)\n\tif _, err := stream.ReadSource(buf); err != nil {\n\t\tt.Fatal(\"readSource failed:\", err)\n\t}\n\tsr := strings.NewReader(\"\")\n\tif _, err := stream.ReadSource(sr); err != nil {\n\t\tt.Fatal(\"readSource strings.Reader failed:\", err)\n\t}\n\tif _, err := stream.ReadSource(0); err == nil {\n\t\tt.Fatal(\"readSource int failed: no error?\")\n\t}\n\tif _, err := stream.ReadSourceLocal(\"/foo/bar/not-exists\", nil); err == nil {\n\t\tt.Fatal(\"readSourceLocal int failed: no error?\")\n\t}\n}\n\nfunc TestParseFiles(t *testing.T) {\n\tfset := token.NewFileSet()\n\tif _, err := ParseFSFiles(fset, fsx.Local, []string{\"/foo/bar/not-exists\"}, PackageClauseOnly|SaveAbsFile); err == nil {\n\t\tt.Fatal(\"ParseFiles failed: no error?\")\n\t}\n}\n\nfunc TestIparseFileInvalidSrc(t *testing.T) {\n\tfset := token.NewFileSet()\n\tif _, err := parseFile(fset, \"/foo/bar/not-exists\", 1, PackageClauseOnly); err != stream.ErrInvalidSource {\n\t\tt.Fatal(\"ParseFile failed: not errInvalidSource?\")\n\t}\n}\n\nfunc TestIparseFileNoFset(t *testing.T) {\n\tdefer func() {\n\t\tif e := recover(); e == nil {\n\t\t\tt.Fatal(\"ParseFile failed: no error?\")\n\t\t}\n\t}()\n\tparseFile(nil, \"/foo/bar/not-exists\", nil, PackageClauseOnly)\n}\n\nfunc TestParseDir(t *testing.T) {\n\tfset := token.NewFileSet()\n\tif _, err := ParseDir(fset, \"/foo/bar/not-exists\", nil, PackageClauseOnly); err == nil {\n\t\tt.Fatal(\"ParseDir failed: no error?\")\n\t}\n}\n\nfunc testFrom(t *testing.T, pkgDir, sel string, exclude Mode) {\n\tif sel != \"\" && !strings.Contains(pkgDir, sel) {\n\t\treturn\n\t}\n\tt.Helper()\n\tlog.Println(\"Parsing\", pkgDir)\n\tfset := token.NewFileSet()\n\tpkgs, err := ParseDir(fset, pkgDir, nil, (Trace|ParseComments|ParseGoAsGoPlus)&^exclude)\n\tif err != nil || len(pkgs) != 1 {\n\t\tif errs, ok := err.(scanner.ErrorList); ok {\n\t\t\tfor _, e := range errs {\n\t\t\t\tt.Log(e)\n\t\t\t}\n\t\t}\n\t\tt.Fatal(\"ParseDir failed:\", err, reflect.TypeOf(err), len(pkgs))\n\t}\n\tfor _, pkg := range pkgs {\n\t\tb, _ := os.ReadFile(pkgDir + \"/parser.expect\")\n\t\tparsertest.ExpectEx(t, pkgDir+\"/result.txt\", pkg, b)\n\t\treturn\n\t}\n}\n\nfunc testExprFrom(t *testing.T, pkgDir, sel string, exclude Mode) {\n\tif sel != \"\" && !strings.Contains(pkgDir, sel) {\n\t\treturn\n\t}\n\tt.Helper()\n\tlog.Println(\"Parsing\", pkgDir)\n\tfset := token.NewFileSet()\n\tfname := pkgDir + \"/in.xgo\"\n\tsrc, err := os.ReadFile(fname)\n\tif err != nil {\n\t\tt.Fatal(\"os.ReadFile:\", err)\n\t}\n\tfile := fset.AddFile(fname, -1, len(src))\n\texpr, errs := ParseExprEx(file, src, 0, (Trace|ParseComments)&^exclude)\n\tif errs != nil {\n\t\tif len(errs) > 0 {\n\t\t\tfor _, e := range errs {\n\t\t\t\tt.Log(e)\n\t\t\t}\n\t\t}\n\t\tt.Fatal(\"ParseExprEx failed:\", errs)\n\t}\n\tb, err := os.ReadFile(pkgDir + \"/out.expect\")\n\tif err != nil {\n\t\tt.Fatal(\"Parsing\", pkgDir, \"-\", err)\n\t}\n\tparsertest.ExpectNode(t, expr, string(b))\n}\n\nfunc TestParseGo(t *testing.T) {\n\tfset := token.NewFileSet()\n\tpkgs, err := ParseDirEx(fset, \"./_testdata/functype\", Config{Mode: Trace | ParseGoAsGoPlus})\n\tif err != nil {\n\t\tt.Fatal(\"TestParseGo: ParseDir failed -\", err)\n\t}\n\tif len(pkgs) != 1 {\n\t\tt.Fatal(\"TestParseGo failed: len(pkgs) =\", len(pkgs))\n\t}\n}\n\nfunc TestParseGoFiles(t *testing.T) {\n\tfset := token.NewFileSet()\n\tpkgs, err := ParseFiles(fset, []string{\"./_testdata/functype/functype.go\"}, Trace|ParseGoAsGoPlus)\n\tif err != nil {\n\t\tt.Fatal(\"TestParseGoFiles: ParseFiles failed -\", err)\n\t}\n\tif len(pkgs) != 1 {\n\t\tt.Fatal(\"TestParseGoFiles failed: len(pkgs) =\", len(pkgs))\n\t}\n}\n\nfunc TestParseEntries(t *testing.T) {\n\tdoTestParseEntries(t, Config{})\n\t_, err := ParseEntries(nil, []string{\"/not-found/gopfile.gox\"}, Config{})\n\tif err == nil {\n\t\tt.Fatal(\"ParseEntries: no error?\")\n\t}\n}\n\nfunc TestParseEntries_SaveAbsFile(t *testing.T) {\n\tdoTestParseEntries(t, Config{Mode: SaveAbsFile})\n}\n\nfunc doTestParseEntries(t *testing.T, confReal Config) {\n\tdoTestParseEntry(t, func(fset *token.FileSet, filename string, src any, conf Config) (f *ast.File, err error) {\n\t\tfs, _ := memfs.File(filename, src)\n\t\tpkgs, err := ParseFSEntries(fset, fs, []string{filename}, confReal)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tfor _, pkg := range pkgs {\n\t\t\tfor _, file := range pkg.Files {\n\t\t\t\tf = file\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tt.Fatal(\"TestParseEntries: no source?\")\n\t\treturn nil, syscall.ENOENT\n\t})\n}\n\nfunc TestParseEntry(t *testing.T) {\n\tdoTestParseEntry(t, ParseEntry)\n}\n\nfunc doTestParseEntry(t *testing.T, parseEntry func(fset *token.FileSet, filename string, src any, conf Config) (f *ast.File, err error)) {\n\tfset := token.NewFileSet()\n\tsrc, err := os.ReadFile(\"./_testdata/functype/functype.go\")\n\tif err != nil {\n\t\tt.Fatal(\"os.ReadFile:\", err)\n\t}\n\tconf := Config{}\n\tt.Run(\".xgo file\", func(t *testing.T) {\n\t\tf, err := parseEntry(fset, \"./functype.xgo\", src, conf)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"ParseEntry failed:\", err)\n\t\t}\n\t\tif f.IsClass || f.IsProj || f.IsNormalGox {\n\t\t\tt.Fatal(\"ParseEntry functype.xgo:\", f.IsClass, f.IsProj, f.IsNormalGox)\n\t\t}\n\t})\n\tt.Run(\".gox file\", func(t *testing.T) {\n\t\tf, err := parseEntry(fset, \"./functype.gox\", src, conf)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"ParseEntry failed:\", err)\n\t\t}\n\t\tif !f.IsClass || f.IsProj || !f.IsNormalGox {\n\t\t\tt.Fatal(\"ParseEntry functype.gox:\", f.IsClass, f.IsProj, f.IsNormalGox)\n\t\t}\n\t})\n\tt.Run(\".foo.gox file\", func(t *testing.T) {\n\t\tf, err := parseEntry(fset, \"./functype.foo.gox\", src, conf)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"ParseEntry failed:\", err)\n\t\t}\n\t\tif !f.IsClass || f.IsProj || !f.IsNormalGox {\n\t\t\tt.Fatal(\"ParseEntry functype.foo.gox:\", f.IsClass, f.IsProj, f.IsNormalGox)\n\t\t}\n\t})\n\tt.Run(\".foo file\", func(t *testing.T) {\n\t\t_, err := parseEntry(fset, \"./functype.foo\", src, conf)\n\t\tif err != ErrUnknownFileKind {\n\t\t\tt.Fatal(\"ParseEntry failed:\", err)\n\t\t}\n\t})\n\tt.Run(\".spx file\", func(t *testing.T) {\n\t\tf, err := parseEntry(fset, \"./main.spx\", src, conf)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"ParseEntry failed:\", err)\n\t\t}\n\t\tif !f.IsClass || !f.IsProj || f.IsNormalGox {\n\t\t\tt.Fatal(\"ParseEntry main.spx:\", f.IsClass, f.IsProj, f.IsNormalGox)\n\t\t}\n\t})\n}\n\nfunc TestParseEntry2(t *testing.T) {\n\tfset := token.NewFileSet()\n\tsrc, err := os.ReadFile(\"./_testdata/functype/functype.go\")\n\tif err != nil {\n\t\tt.Fatal(\"os.ReadFile:\", err)\n\t}\n\tconf := Config{}\n\tconf.ClassKind = func(fname string) (isProj bool, ok bool) {\n\t\tif strings.HasSuffix(fname, \"_yap.gox\") {\n\t\t\treturn true, true\n\t\t}\n\t\treturn defaultClassKind(fname)\n\t}\n\tt.Run(\"_yap.gox file\", func(t *testing.T) {\n\t\tf, err := ParseEntry(fset, \"./functype_yap.gox\", src, conf)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"ParseEntry failed:\", err)\n\t\t}\n\t\tif !f.IsClass || !f.IsProj || f.IsNormalGox {\n\t\t\tt.Fatal(\"ParseEntry functype_yap.gox:\", f.IsClass, f.IsProj, f.IsNormalGox)\n\t\t}\n\t})\n}\n\nfunc TestSaveAbsFile(t *testing.T) {\n\tfset := token.NewFileSet()\n\tsrc, err := os.ReadFile(\"./_testdata/functype/functype.go\")\n\tif err != nil {\n\t\tt.Fatal(\"os.ReadFile:\", err)\n\t}\n\tconf := Config{}\n\tconf.Mode = SaveAbsFile\n\tt.Run(\".xgo file\", func(t *testing.T) {\n\t\tf, err := ParseEntry(fset, \"./functype.xgo\", src, conf)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"ParseEntry failed:\", err)\n\t\t}\n\t\tif f.IsClass || f.IsProj || f.IsNormalGox {\n\t\t\tt.Fatal(\"ParseEntry functype.xgo:\", f.IsClass, f.IsProj, f.IsNormalGox)\n\t\t}\n\t})\n\tt.Run(\".xgo file\", func(t *testing.T) {\n\t\tf, err := ParseFile(fset, \"./functype.xgo\", src, conf.Mode)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"ParseEntry failed:\", err)\n\t\t}\n\t\tif f.IsClass || f.IsProj || f.IsNormalGox {\n\t\t\tt.Fatal(\"ParseEntry functype.xgo:\", f.IsClass, f.IsProj, f.IsNormalGox)\n\t\t}\n\t})\n\tt.Run(\"dir\", func(t *testing.T) {\n\t\t_, err := ParseDirEx(fset, \"./_nofmt/cmdlinestyle1\", conf)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"ParseDirEx failed:\", err)\n\t\t}\n\t})\n}\n\nfunc TestGopAutoGen(t *testing.T) {\n\tfset := token.NewFileSet()\n\tfs := memfs.SingleFile(\"/foo\", \"gop_autogen.go\", ``)\n\tpkgs, err := ParseFSDir(fset, fs, \"/foo\", Config{})\n\tif err != nil {\n\t\tt.Fatal(\"ParseFSDir:\", err)\n\t}\n\tif len(pkgs) != 0 {\n\t\tt.Fatal(\"TestGopAutoGen:\", len(pkgs))\n\t}\n}\n\nfunc TestGoFile(t *testing.T) {\n\tfset := token.NewFileSet()\n\tfs := memfs.SingleFile(\"/foo\", \"test.go\", `package foo`)\n\tpkgs, err := ParseFSDir(fset, fs, \"/foo\", Config{})\n\tif err != nil {\n\t\tt.Fatal(\"ParseFSDir:\", err)\n\t}\n\tif len(pkgs) != 1 || pkgs[\"foo\"].GoFiles[\"/foo/test.go\"] == nil {\n\t\tt.Fatal(\"TestGoFile:\", len(pkgs))\n\t}\n}\n\nfunc TestErrParse(t *testing.T) {\n\tfset := token.NewFileSet()\n\tfs := memfs.SingleFile(\"/foo\", \"test.go\", `package foo bar`)\n\t_, err := ParseFSDir(fset, fs, \"/foo\", Config{})\n\tif err == nil {\n\t\tt.Fatal(\"ParseFSDir test.go: no error?\")\n\t}\n\n\tfs = memfs.SingleFile(\"/foo\", \"test.xgo\", `package foo bar`)\n\t_, err = ParseFSDir(fset, fs, \"/foo\", Config{})\n\tif err == nil {\n\t\tt.Fatal(\"ParseFSDir test.xgo: no error?\")\n\t}\n\n\tfs = memfs.New(map[string][]string{\"/foo\": {\"test.go\"}}, map[string]string{})\n\t_, err = ParseFSDir(fset, fs, \"/foo\", Config{})\n\tif err != syscall.ENOENT {\n\t\tt.Fatal(\"ParseFSDir:\", err)\n\t}\n\n\tfs = memfs.SingleFile(\"/foo\", \"test.abc.gox\", `package foo`)\n\t_, err = ParseFSDir(fset, fs, \"/foo\", Config{})\n\tif err != nil {\n\t\tt.Fatal(\"ParseFSDir failed:\", err)\n\t}\n}\n\nfunc testFromDir(t *testing.T, sel, relDir string, testExpr ...bool) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = path.Join(dir, relDir)\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"ReadDir failed:\", err)\n\t}\n\tisTestExpr := len(testExpr) > 0 && testExpr[0]\n\tfor _, fi := range fis {\n\t\tname := fi.Name()\n\t\tif strings.HasPrefix(name, \"_\") {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tpkgDir := dir + \"/\" + name\n\t\t\tif isTestExpr {\n\t\t\t\ttestExprFrom(t, pkgDir, sel, 0)\n\t\t\t} else {\n\t\t\t\ttestFrom(t, pkgDir, sel, 0)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFromTestexpr(t *testing.T) {\n\ttestFromDir(t, \"\", \"./_testexpr\", true)\n}\n\nfunc TestFromTestdata(t *testing.T) {\n\ttestFromDir(t, \"\", \"./_testdata\")\n}\n\nfunc TestFromNofmt(t *testing.T) {\n\ttestFromDir(t, \"\", \"./_nofmt\")\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "parser/parsertest/parsertest.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage parsertest\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n\ttpltoken \"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/qiniu/x/test\"\n)\n\nfunc sortedKeys(m any) []string {\n\titer := reflect.ValueOf(m).MapRange()\n\tkeys := make([]string, 0, 8)\n\tfor iter.Next() {\n\t\tkey := iter.Key()\n\t\tkeys = append(keys, key.String())\n\t}\n\tsort.Strings(keys)\n\treturn keys\n}\n\nvar (\n\ttyNode      = reflect.TypeOf((*ast.Node)(nil)).Elem()\n\ttyString    = reflect.TypeOf(\"\")\n\ttyBytes     = reflect.TypeOf([]byte(nil))\n\ttyToken     = reflect.TypeOf(token.Token(0))\n\ttyObjectPtr = reflect.TypeOf((*ast.Object)(nil))\n\ttyScopePtr  = reflect.TypeOf((*ast.Scope)(nil))\n\ttplToken    = reflect.TypeOf(tpltoken.Token(0))\n)\n\n// FprintNode prints an XGo AST node.\nfunc FprintNode(w io.Writer, lead string, v any, prefix, indent string) {\n\tval := reflect.ValueOf(v)\n\tswitch val.Kind() {\n\tcase reflect.Slice:\n\t\tn := val.Len()\n\t\tif n > 0 && lead != \"\" {\n\t\t\tio.WriteString(w, lead)\n\t\t}\n\t\tfor i := 0; i < n; i++ {\n\t\t\tFprintNode(w, \"\", val.Index(i).Interface(), prefix, indent)\n\t\t}\n\tcase reflect.Ptr:\n\t\tt := val.Type()\n\t\tif val.IsNil() || t == tyObjectPtr || t == tyScopePtr {\n\t\t\treturn\n\t\t}\n\t\tif t.Implements(tyNode) {\n\t\t\tif lead != \"\" {\n\t\t\t\tio.WriteString(w, lead)\n\t\t\t}\n\t\t\telem, tyElem := val.Elem(), t.Elem()\n\t\t\tfmt.Fprintf(w, \"%s%v:\\n\", prefix, tyElem)\n\t\t\tn := elem.NumField()\n\t\t\tprefix += indent\n\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\tsf := tyElem.Field(i)\n\t\t\t\tsfv := elem.Field(i).Interface()\n\t\t\t\tswitch sf.Type {\n\t\t\t\tcase tyString, tyToken, tplToken:\n\t\t\t\t\tfmt.Fprintf(w, \"%s%v: %v\\n\", prefix, sf.Name, sfv)\n\t\t\t\tcase tyBytes: // skip\n\t\t\t\tdefault:\n\t\t\t\t\tif sf.Name == \"Optional\" {\n\t\t\t\t\t\tif pos, ok := sfv.(token.Pos); ok && pos != token.NoPos {\n\t\t\t\t\t\t\tfmt.Fprintf(w, \"%s%v: true\\n\", prefix, sf.Name)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tFprintNode(w, fmt.Sprintf(\"%s%v:\\n\", prefix, sf.Name), sfv, prefix+indent, indent)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif m, ok := v.(*ast.MatrixLit); ok {\n\t\t\t\tfmt.Fprintf(w, \"%sNElt: %d\\n\", prefix, len(m.Elts))\n\t\t\t}\n\t\t} else if lit, ok := v.(*ast.StringLitEx); ok {\n\t\t\tfmt.Fprintf(w, \"%sExtra:\\n\", prefix)\n\t\t\tprefix += indent\n\t\t\tfor _, part := range lit.Parts {\n\t\t\t\tif val, ok := part.(string); ok {\n\t\t\t\t\tfmt.Fprintf(w, \"%s%v\\n\", prefix, val)\n\t\t\t\t} else {\n\t\t\t\t\tFprintNode(w, \"\", part, prefix, indent)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if lit, ok := v.(*ast.DomainTextLitEx); ok {\n\t\t\tfmt.Fprintf(w, \"%sExtra: args=%d\\n\", prefix, len(lit.Args))\n\t\t\tprefix += indent\n\t\t\tfor _, arg := range lit.Args {\n\t\t\t\tFprintNode(w, \"\", arg, prefix, indent)\n\t\t\t}\n\t\t\tfmt.Fprintf(w, \"%s%v\\n\", prefix, lit.Raw)\n\t\t} else {\n\t\t\tlog.Panicln(\"FprintNode unexpected type:\", t)\n\t\t}\n\tcase reflect.Int, reflect.Bool, reflect.Invalid:\n\t\t// skip\n\tdefault:\n\t\tlog.Panicln(\"FprintNode unexpected kind:\", val.Kind(), \"type:\", val.Type())\n\t}\n}\n\n// Fprint prints an XGo package.\nfunc Fprint(w io.Writer, pkg *ast.Package) {\n\tfmt.Fprintf(w, \"package %s\\n\", pkg.Name)\n\tpaths := sortedKeys(pkg.Files)\n\tfor _, fpath := range paths {\n\t\tfmt.Fprintf(w, \"\\nfile %s\\n\", filepath.Base(fpath))\n\t\tfile := pkg.Files[fpath]\n\t\tif file.HasShadowEntry() {\n\t\t\tfmt.Fprintf(w, \"noEntrypoint\\n\")\n\t\t}\n\t\tFprintNode(w, \"\", file.Decls, \"\", \"  \")\n\t}\n}\n\n// Expect asserts an XGo AST package equals output or not.\nfunc Expect(t *testing.T, pkg *ast.Package, expected string) {\n\tb := bytes.NewBuffer(nil)\n\tFprint(b, pkg)\n\toutput := b.String()\n\tif expected != output {\n\t\tfmt.Fprint(os.Stderr, output)\n\t\tt.Fatal(\"xgo.Parser: unexpect result\")\n\t}\n}\n\nfunc ExpectEx(t *testing.T, outfile string, pkg *ast.Package, expected []byte) {\n\tb := bytes.NewBuffer(nil)\n\tFprint(b, pkg)\n\tif test.Diff(t, outfile, b.Bytes(), expected) {\n\t\tt.Fatal(\"xgo.Parser: unexpect result\")\n\t}\n}\n\n// ExpectNode asserts an XGo AST node equals output or not.\nfunc ExpectNode(t *testing.T, node any, expected string) {\n\tb := bytes.NewBuffer(nil)\n\tFprintNode(b, \"\", node, \"\", \"  \")\n\toutput := b.String()\n\tif expected != output {\n\t\tfmt.Fprint(os.Stderr, output)\n\t\tt.Fatal(\"xgo.Parser: unexpect result\")\n\t}\n}\n"
  },
  {
    "path": "printer/_testdata/02-Var-and-operator/var_and_op.xgo",
    "content": "x := 123.1 - 3i\ny, z := \"Hello, \", 123\nprintln(y+\"complex:\", x+1, \"int:\", z)\n"
  },
  {
    "path": "printer/_testdata/03-Import-go-package/import.xgo",
    "content": "import (\n\t\"fmt\"\n\t\"strings\"\n)\n\nx := strings.NewReplacer(\"?\", \"!\").Replace(\"hello, world???\")\nfmt.Println(\"x:\", x)\n"
  },
  {
    "path": "printer/_testdata/04-Func/func.xgo",
    "content": "import (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc foo(x string) string {\n\treturn strings.NewReplacer(\"?\", \"!\").Replace(x)\n}\n\nfunc printf(format string, args ...interface{}) (n int, err error) {\n\tn, err = fmt.Printf(format, args...)\n\treturn\n}\n\nfunc bar(foo func(string, ...interface{}) (int, error)) {\n\tfoo(\"Hello, %v!\\n\", \"XGo\")\n}\n\nbar(printf)\nfmt.Println(foo(\"Hello, world???\"))\nfmt.Println(printf(\"Hello, %v\\n\", \"XGo\"))\n"
  },
  {
    "path": "printer/_testdata/05-Closure/closure.xgo",
    "content": "import \"fmt\"\n\nvar x = \"Hello, world!\"\n\nfoo := func(prompt string) (n int, err error) {\n\tn, err = fmt.Println(prompt + x)\n\treturn\n}\n\nfmt.Println(foo(\"x: \"))\n\nprintf := func(format string, args ...interface{}) (n int, err error) {\n\tn, err = fmt.Printf(format, args...)\n\treturn\n}\n\nbar := func(foo func(string, ...interface{}) (int, error)) {\n\tfoo(\"Hello, %v!\\n\", \"XGo\")\n}\n\nbar(printf)\nfmt.Println(printf(\"Hello, %v\\n\", \"XGo\"))\n"
  },
  {
    "path": "printer/_testdata/06-String-Map-Array-Slice/datastruct.xgo",
    "content": "x := []float64{1, 3.4, 5}\ny := map[string]float64{\"Hello\": 1, \"xsw\": 3.4}\n\na := [...]float64{1, 3.4, 5}\nb := [...]float64{1, 3: 3.4, 5}\nc := []float64{2: 1.2, 3, 6: 4.5}\n\nx[1], y[\"xsw\"] = 1.7, 2.8\nprintln(\"x:\", x, \"y:\", y)\nprintln(`x[1]:`, x[1], `y[\"xsw\"]:`, y[\"xsw\"], `a[1]:`, a[1])\n\ni := uint16(4)\nb[uint32(4)], c[i] = 123, 1.7\nprintln(\"a:\", a, \"b:\", b, \"c:\", c)\n\narr := [...]float64{1, 2}\ntitle := \"Hello,world!\" + \"2020-05-27\"\nprintln(title[:len(title)-len(\"2006-01-02\")], len(arr), arr[1:])\n"
  },
  {
    "path": "printer/_testdata/07-MapLit/maplit.xgo",
    "content": "x := {\"Hello\": 1, \"xsw\": 3.4} // map[string]float64\nprintln(\"x:\", x)\n\ny := {\"Hello\": 1, \"xsw\": \"XGo\"} // map[string]any\nprintln(\"y:\", y)\n\nprintln({\"Hello\": 1, \"xsw\": 3}) // map[string]int\nprintln({1: 1.4, 3: \"XGo\"})     // map[int]any\n\nprintln(\"empty map:\", {}) // map[string]any\n"
  },
  {
    "path": "printer/_testdata/08-SliceLit/slicelit.xgo",
    "content": "x := [1, 3.4] // []float64\nprintln(\"x:\", x)\n\ny := [1] // []int\nprintln(\"y:\", y)\n\nz := [1+2i, \"xsw\"] // []interface{}\nprintln(\"z:\", z)\n\nprintln([1, 3.4, 3+4i]) // []complex128\nprintln([5+6i])         // []complex128\nprintln([\"xsw\", 3])     // []interface{}\n\nprintln(\"empty slice:\", []) // []interface{}\n"
  },
  {
    "path": "printer/_testdata/09-IfElse-SwitchCase/flow.xgo",
    "content": "x := 0\nif t := false; t {\n\tx = 3\n} else {\n\tx = 5\n}\nprintln(\"x:\", x)\n\nx = 0\nswitch s := \"Hello\"; s {\ndefault:\n\tx = 7\ncase \"world\", \"hi\":\n\tx = 5\ncase \"xsw\":\n\tx = 3\n}\nprintln(\"x:\", x)\n\nv := \"Hello\"\nswitch {\ncase v == \"xsw\":\n\tx = 3\ncase v == \"Hello\", v == \"world\":\n\tx = 9\ndefault:\n\tx = 7\n}\nprintln(\"x:\", x)\n\nv = \"Hello\"\nswitch {\ncase v == \"xsw\":\n\tx = 3\ncase v == \"hi\", v == \"world\":\n\tx = 9\ndefault:\n\tx = 11\n}\nprintln(\"x:\", x)\n\nswitch v {\ncase \"Hello\":\n\tprintln(v)\n\tfallthrough\ncase \"hi\":\n\tprintln(v)\n\tfallthrough\ndefault:\n\tprintln(v)\n}\n\nz := 3\nswitch {\ncase z < 10:\n\tprintln(z)\n\tfallthrough\ncase z == 10:\n\tprintln(z)\n\tfallthrough\ncase z > 10:\n\tprintln(z)\n\tfallthrough\ndefault:\n\tprintln(z)\n}\n"
  },
  {
    "path": "printer/_testdata/10-List-comprehension/list_comprehens.xgo",
    "content": "y := [x*x for x in [1, 3, 5, 7, 11]]\nprintln(y)\n\ny = [x*x for x in [1, 3, 5, 7, 11] if x > 3]\nprintln(y)\n\nz := [i+v for i, v in [1, 3, 5, 7, 11] if i%2 == 1]\nprintln(z)\n\nprintln([k+\",\"+s for k, s in {\"Hello\": \"xsw\", \"Hi\": \"XGo\"}])\n\narr := [1, 2, 3, 4, 5, 6]\nx := [[a, b] for a in arr if a < b for b in arr if b > 2]\nprintln(\"x:\", x)\n"
  },
  {
    "path": "printer/_testdata/11-Map-comprehension/map_comprehens.xgo",
    "content": "y := {x: i for i, x in [1, 3, 5, 7, 11]}\nprintln(y)\n\ny = {x: i for i, x in [1, 3, 5, 7, 11] if i%2 == 1}\nprintln(y)\n\nz := {v: k for k, v in {1: \"Hello\", 3: \"Hi\", 5: \"xsw\", 7: \"XGo\"} if k > 3}\nprintln(z)\n"
  },
  {
    "path": "printer/_testdata/12-Select-comprehension/select.xgo",
    "content": "a := [2, 3, 5, 7, 9, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59]\nwhere := {i for i, v in a if v == 19}\nprintln(\"19 is at index\", where)\n\nif at, ok := {i for i, v in a if v == 37}; ok {\n\tprintln(\"37 is found! index =\", at)\n}\n\nprintln(\"first element of a is:\", {v for v in a})\n"
  },
  {
    "path": "printer/_testdata/12-Select-comprehension2/findscore.xgo",
    "content": "type student struct {\n\tname  string\n\tscore int\n}\n\nfunc findScore(a []student, name string) (score int, ok bool) {\n\treturn {x.score for x in a if x.name == name}\n}\n\na := [student{\"ken\", 95}, student{\"john\", 90}, student{\"tom\", 58}]\nprintln(findScore(a, \"tom\"))\n"
  },
  {
    "path": "printer/_testdata/13-Exists-comprehension/exists.xgo",
    "content": "type student struct {\n\tname  string\n\tscore int\n}\n\na := [student{\"du\", 84}, student{\"wang\", 70}, student{\"ken\", 100}]\n\nhasFullMark := {for x in a if x.score == 100}\nprintln(\"is any student full mark:\", hasFullMark)\n\nhasFailed := {for x in a if x.score < 60}\nprintln(\"is any student failed:\", hasFailed)\n"
  },
  {
    "path": "printer/_testdata/14-Using-goplus-in-Go/foo/foo.xgo",
    "content": "package foo\n\nfunc ReverseMap(m map[string]int) map[int]string {\n\treturn {v: k for k, v in m}\n}\n"
  },
  {
    "path": "printer/_testdata/14-Using-goplus-in-Go/foo/foo_test.xgo",
    "content": "package foo\n\nimport (\n\t\"testing\"\n)\n\nfunc TestReverseMap(t *testing.T) {\n\tout := ReverseMap({\"a\": 1})\n\tif len(out) != 1 || out[1] != \"a\" {\n\t\tt.Fatal(\"ReverseMap failed:\", out)\n\t}\n}\n"
  },
  {
    "path": "printer/_testdata/14-Using-goplus-in-Go/foo/footest_test.xgo",
    "content": "package foo_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/tutorial/14-Using-goplus-in-Go/foo\"\n)\n\nfunc TestReverseMap(t *testing.T) {\n\tout := foo.ReverseMap({\"b\": 2})\n\tif len(out) != 1 || out[2] != \"b\" {\n\t\tt.Fatal(\"ReverseMap failed:\", out)\n\t}\n}\n"
  },
  {
    "path": "printer/_testdata/15-ErrWrap/err_wrap.xgo",
    "content": "import (\n\t\"strconv\"\n)\n\nfunc add(x, y string) (int, error) {\n\treturn strconv.Atoi(x)? + strconv.Atoi(y)?, nil\n}\n\nfunc addSafe(x, y string) int {\n\treturn strconv.Atoi(x)?:0 + strconv.Atoi(y)?:0\n}\n\nprintln(`add(\"100\", \"23\"):`, add(\"100\", \"23\")!)\n\nsum, err := add(\"10\", \"abc\")\nprintln(`add(\"10\", \"abc\"):`, sum, err)\n\nprintln(`addSafe(\"10\", \"abc\"):`, addSafe(\"10\", \"abc\"))\n"
  },
  {
    "path": "printer/_testdata/16-Fib/fib.xgo",
    "content": "func fib(n int) int {\n\tif n == 0 {\n\t\treturn 0\n\t}\n\tif n == 1 {\n\t\treturn 1\n\t}\n\treturn fib(n-1) + fib(n-2)\n}\n\nprintln `fib(27):`, fib(27)\n"
  },
  {
    "path": "printer/_testdata/17-Fibtc/fibtc.xgo",
    "content": "func fibtc(n, a, b int) int {\n\tif n == 0 {\n\t\treturn a\n\t}\n\tif n == 1 {\n\t\treturn b\n\t}\n\treturn fibtc(n-1, b, a+b)\n}\n\nprintln `fibtc(27):`, fibtc(27, 0, 1)\n"
  },
  {
    "path": "printer/_testdata/18-Rational/rational.xgo",
    "content": "import \"math/big\"\n\nvar a bigint = 1r << 65 // bigint, large than int64\nvar b bigrat = 4/5r     // bigrat\nc := b - 1/3r + 3*1/2r // bigrat\nprintln(a, b, c)\n\nvar x *big.Int = 1r << 65 // (1r << 65) is untyped bigint, and can be assigned to *big.Int\nvar y *big.Rat = 4/5r\nprintln(x, y)\n\na = new(big.Int).Abs(-265r)\nprintln(\"abs(-265r):\", a)\n"
  },
  {
    "path": "printer/_testdata/21-Break-continue-goto/flow.xgo",
    "content": "println(\"start\")\n\ngoto L\nprintln(\"before\")\nL:\n\tprintln(\"over\")\n\ti := 0\nL2:\n\tif i < 3 {\n\t\tprintln(i)\n\t\ti++\n\t\tgoto L2\n\t}\n\tprintln(\"over\")\n\n\tsum := 0\n\tarr := [1, 3, 5, 7, 11, 13, 17]\n\tfor i = 0; i < len(arr); i++ {\n\t\tif arr[i] < 3 {\n\t\t\tcontinue\n\t\t}\n\t\tif arr[i] > 11 {\n\t\t\tbreak\n\t\t}\n\t\tsum += arr[i]\n\t}\n\tprintln(\"sum(3,5,7,11):\", sum == 26, sum)\n\tsum = 0\nL3:\n\tfor i = 0; i < len(arr); i++ {\n\t\tif arr[i] < 3 {\n\t\t\tcontinue L3\n\t\t}\n\t\tif arr[i] > 11 {\n\t\t\tbreak L3\n\t\t}\n\t\tsum += arr[i]\n\t}\n\tprintln(\"sum(3,5,7,11):\", sum == 26, sum)\n\n\tz := 3\n\tv := \"Hello\"\n\tswitch z {\n\tcase 3:\n\t\tif v == \"Hello\" {\n\t\t\tprintln(\"break\")\n\t\t\tbreak\n\t\t}\n\t\tprintln(\"break fail\")\n\tdefault:\n\t\tprintln(z)\n\t}\nL4:\n\tswitch z {\n\tcase 3:\n\t\tif v == \"Hello\" {\n\t\t\tprintln(\"break\")\n\t\t\tbreak L4\n\t\t}\n\t\tprintln(\"break fail\")\n\tdefault:\n\t\tprintln(z)\n\t}\n"
  },
  {
    "path": "printer/_testdata/22-For-loop/for.xgo",
    "content": "// -----------------------------------------------------------------------------\n// for..in\n\nsum := 0\nfor x in [1, 3, 5, 7, 11, 13, 17] if x > 3 {\n\tsum += x\n}\nprintln(\"sum(5,7,11,13,17):\", sum)\n\nfns := make([]func() int, 3)\nfor i, x in [3, 15, 777] {\n\tv := x\n\tfns[i] = func() int {\n\t\treturn v\n\t}\n}\nprintln(\"values:\", fns[0](), fns[1](), fns[2]())\n\n// -----------------------------------------------------------------------------\n// for with range with define tok (k,v := range x)\n\nsum = 0\nfor _, x := range [1, 3, 5, 7, 11, 13, 17] {\n\tif x > 3 {\n\t\tsum += x\n\t}\n}\nprintln(\"sum(5,7,11,13,17):\", sum)\n\nsum = 0\nfor i, x := range [3, 15, 777] {\n\tv := x\n\tfns[i] = func() int {\n\t\treturn v\n\t}\n}\nprintln(\"values:\", fns[0](), fns[1](), fns[2]())\n\n// -----------------------------------------------------------------------------\n// for with range with assign tok (k,v = range x)\n\nsum = 0\nx := 0\nfor _, x = range [1, 3, 5, 7, 11, 13, 17] {\n\tif x > 3 {\n\t\tsum += x\n\t}\n}\nprintln(\"x:\", x, x == 17)\nprintln(\"sum(5,7,11,13,17):\", sum)\n\n// -----------------------------------------------------------------------------\n// normal for\n\nsum = 0\narr := [1, 3, 5, 7, 11, 13, 17]\ni := 10\nfor i = 0; i < len(arr); i++ {\n\tif arr[i] > 3 {\n\t\tsum += arr[i]\n\t}\n}\nprintln(\"sum(5,7,11,13,17):\", sum)\n\n/* TODO:\narr = [3, 15, 777]\nsum = 0\nfor i = 0; i < len(arr); i++ {\n\tv := arr[i]\n\tfns[i] = func() int {\n\t\treturn v\n\t}\n}\nprintln(\"values:\", fns[0](), fns[1](), fns[2]())\n*/\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "printer/_testdata/23-Defer/defer.xgo",
    "content": "func f() (x int) {\n\tdefer func() {\n\t\tx = 3\n\t}()\n\treturn 1\n}\n\nfunc g() (x int) {\n\tdefer func() {\n\t\tx = 3\n\t}()\n\tx = 1\n\treturn\n}\n\nfunc h() (x int) {\n\tfor i in [3, 2, 1] {\n\t\tv := i\n\t\tdefer func() {\n\t\t\tx = v\n\t\t}()\n\t}\n\treturn\n}\n\nprintln(\"f-x:\", f())\nprintln(\"g-x:\", g())\nprintln(\"h-x:\", h())\n\ndefer println(println(\"Hello, defer\"))\nprintln(\"Hello, XGo\")\n"
  },
  {
    "path": "printer/_testdata/24-Goroutine/goroutine.xgo",
    "content": "import \"time\"\n\ngo func() {\n\tprintln(\"Hello, goroutine!\")\n}()\n\ntime.Sleep(1e8)\n"
  },
  {
    "path": "printer/_testdata/25-Struct/struct.xgo",
    "content": "a := struct {\n\tA int    `json:\"a\"` // comment a\n\tB string `b`        // comment b\n}{1, \"Hello\"}\n\nprintln(a)\n\nb := &struct {\n\tA int // a\n\tB string\n}{1, \"Hello\"}\n\nc := &struct {\n\ta int\n\tb string\n}{1, \"Hello\"}\n\nprintln(b)\n\na.A, a.B = 1, \"Hi\"\nprintln(a)\n\nb.A, b.B = 2, \"Hi2\"\nprintln(b)\n\nc.a, c.b = 3, \"Hi3\"\nprintln(c)\n"
  },
  {
    "path": "printer/_testdata/26-Method/method.xgo",
    "content": "type Person struct {\n\tName    string\n\tAge     int\n\tFriends []string\n}\n\nfunc (p *Person) SetName(name string) {\n\tp.Name = name\n\tprintln(p.Name)\n}\n\nfunc (p *Person) SetAge(age int) {\n\tage = age + 5\n\tp.Age = age\n\tprintln(p.Age)\n}\n\nfunc (p *Person) AddFriends(args ...string) {\n\tp.Friends = append(p.Friends, args...)\n}\n\ntype M int\n\nfunc (m M) Foo() {\n\tprintln(\"foo\", m)\n}\n\np := Person{\n\tName: \"bar\",\n\tAge:  30,\n}\n\np.Name, p.Age = \"bar2\", 31\n\np.SetName(\"foo\")\np.SetAge(32)\np.AddFriends(\"a\", \"b\", \"c\")\n\na := int32(0)\nm := M(a)\nm.Foo()\n\nprintln(p.Name)\nprintln(p.Age)\nprintln(p.Friends)\nprintln(m)\n"
  },
  {
    "path": "printer/_testdata/27-Func-Set/func.xgo",
    "content": "func A(a *int, c *struct {\n\tb *int\n\tm map[string]*int\n\ts []*int\n}) {\n\t*a = 5\n\t*c.b = 3\n\t*c.m[\"foo\"] = 7\n\t*c.s[0] = 9\n}\n\nfunc Index() int {\n\treturn 0\n}\n\na1 := 6\na2 := 6\na3 := 6\nc := struct {\n\tb *int\n\tm map[string]*int\n\ts []*int\n}{\n\tb: &a1,\n\tm: map[string]*int{\n\t\t\"foo\": &a2,\n\t},\n\ts: []*int{&a3},\n}\nA(&a1, &c)\n*c.m[\"foo\"] = 8\n*c.s[0] = 10\n*c.s[Index()] = 11\nprintln(a1, *c.b, *c.m[\"foo\"], *c.s[0])\n"
  },
  {
    "path": "printer/_testdata/28-Chan/chan.xgo",
    "content": "c := make(chan int, 10)\nc <- 3\nclose(c)\n\nd := <-c\ne, ok := <-c\n\nprintln(d)\nprintln(e, ok)\n"
  },
  {
    "path": "printer/_testdata/29-CompareToNil/ref.xgo",
    "content": "func foo() []int {\n\treturn nil\n}\n\nfunc foo1() map[int]int {\n\treturn make(map[int]int, 10)\n}\n\nfunc foo2() chan int {\n\treturn make(chan int, 10)\n}\n\nfunc foo3() *int {\n\treturn nil\n}\n\nprintln(foo() == nil)\nprintln(nil == foo())\nprintln(foo() != nil)\nprintln(nil != foo())\n\nprintln(foo1() == nil)\nprintln(nil == foo1())\nprintln(foo1() != nil)\nprintln(nil != foo1())\n\nprintln(foo2() == nil)\nprintln(nil == foo2())\nprintln(foo2() != nil)\nprintln(nil != foo2())\n\nprintln(foo3() == nil)\nprintln(nil == foo3())\nprintln(foo3() != nil)\nprintln(nil != foo3())\n"
  },
  {
    "path": "printer/_testdata/30-Recover/recover.xgo",
    "content": "defer func() {\n\tvar err interface{}\n\tif err = recover(); err != nil {\n\t\tprintln(err)\n\t}\n}()\n\npanic(\"hello recover\")\n"
  },
  {
    "path": "printer/_testdata/31-Builtin-Typecast/builtin_and_typecast.xgo",
    "content": "n := 2\na := make([]int, uint64(n))\na = append(a, 1, 2, 3)\nprintln(a)\nprintln(\"len:\", len(a))\n\nb := make([]int, 0, uint16(4))\nc := [1, 2, 3]\nb = append(b, c...)\nprintln(b)\nprintln(\"len:\", len(b), \"cap:\", cap(b))\n"
  },
  {
    "path": "printer/_testdata/32-Import-gop-package/import_gop_pkg.xgo",
    "content": "import \"github.com/goplus/xgo/tutorial/14-Using-goplus-in-Go/foo\"\n\nrmap := foo.ReverseMap({\"Hi\": 1, \"Hello\": 2})\nprintln(rmap)\n"
  },
  {
    "path": "printer/_testdata/33-Interface/shape.xgo",
    "content": "import \"math\"\n\ntype Shape interface {\n\tArea() float64\n}\n\ntype Rect struct {\n\tx, y, w, h float64\n}\n\nfunc (p *Rect) Area() float64 {\n\treturn p.w * p.h\n}\n\ntype Circle struct {\n\tx, y, r float64\n}\n\nfunc (p *Circle) Area() float64 {\n\treturn math.Pi * p.r * p.r\n}\n\nfunc Area(shapes ...Shape) float64 {\n\ts := 0.0\n\tfor shape in shapes {\n\t\ts += shape.Area()\n\t}\n\treturn s\n}\n\nrect := &Rect{0, 0, 2, 5}\ncircle := &Circle{0, 0, 3}\nprintln(\"area:\", Area(circle, rect))\n"
  },
  {
    "path": "printer/_testdata/34-Type-assert/type_assert.xgo",
    "content": "func foo(v interface{}) string {\n\tif _, ok := v.(bool); ok {\n\t\treturn \"bool\"\n\t}\n\tswitch v.(type) {\n\tcase int:\n\t\treturn \"int\"\n\tcase string:\n\t\treturn \"string\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n\nfunc add(v, delta interface{}) interface{} {\n\tswitch a := v.(type) {\n\tcase int:\n\t\treturn a + delta.(int)\n\tcase float64:\n\t\treturn a + delta.(float64)\n\tcase string:\n\t\treturn a + delta.(string)\n\t}\n\treturn nil\n}\n\nprintln(foo(1), foo(\"Hi\"))\nprintln(add(4, 3), add(\"n\", \"iu\"))\n"
  },
  {
    "path": "printer/_testdata/35-Chan-select/select.xgo",
    "content": "var done = make(chan bool, 1)\nvar exited = make(chan bool, 1)\n\nfunc consume(xchg chan int) {\n\tfor {\n\t\tselect {\n\t\tcase c := <-xchg:\n\t\t\tprintln(c)\n\t\tcase <-done:\n\t\t\tprintln(\"done!\")\n\t\t\texited <- true\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc product(xchg chan int, from chan int) {\n\tfor x in from {\n\t\txchg <- x\n\t}\n\tdone <- true\n}\n\nfrom := make(chan int, 10)\nxchg := make(chan int, 1)\ngo consume(xchg)\ngo product(xchg, from)\nfor i := 1; i <= 10; i++ {\n\tfrom <- i\n}\nclose(from)\n<-exited\n"
  },
  {
    "path": "printer/_testdata/36-Auto-Property/autoprop.xgo",
    "content": "import (\n\t\"gop/ast/goptest\"\n)\n\nscript := `\n    import (\n        gio \"io\"\n    )\n\n    func New() (*Bar, error) {\n        return nil, gio.EOF\n    }\n\n    bar, err := New()\n    if err != nil {\n        log.Println(err)\n    }\n`\n\ndoc := goptest.New(script)!\n\nprintln(doc.any.funcDecl.name)\nprintln(doc.any.importSpec.name)\n"
  },
  {
    "path": "printer/_testdata/37-Cmdline/cmdline.xgo",
    "content": "import \"os\"\n\nprintln(\"args:\", os.Args[1:])\n"
  },
  {
    "path": "printer/_testdata/38-Overload-operator/overload_op.xgo",
    "content": "import \"math/big\"\n\ntype MyBigInt struct {\n\t*big.Int\n}\n\nfunc Int(v *big.Int) MyBigInt {\n\treturn MyBigInt{v}\n}\n\nfunc (a MyBigInt) + (b MyBigInt) MyBigInt {\n\treturn MyBigInt{new(big.Int).Add(a.Int, b.Int)}\n}\n\nfunc (a MyBigInt) += (b MyBigInt) {\n\ta.Int.Add(a.Int, b.Int)\n}\n\nfunc -(a MyBigInt) MyBigInt {\n\treturn MyBigInt{new(big.Int).Neg(a.Int)}\n}\n\na := Int(1r)\na += Int(2r)\nprintln(a + Int(3r))\nprintln(-a)\n"
  },
  {
    "path": "printer/_testdata/39-Lambda-expression/lambda.xgo",
    "content": "func Map(c []float64, t func(float64) float64) []float64 {\n\treturn [t(x) for x in c]\n}\n\nprintln(Map([1.2, 3.5, 6], x => x * x))\n"
  },
  {
    "path": "printer/_testdata/40-Deduce-struct-type/deduce.xgo",
    "content": "type Config struct {\n\tDir   string\n\tLevel int\n}\n\ntype Result struct {\n\tText string\n}\n\nfunc foo(conf *Config) *Result {\n\tprintln(\"conf:\", *conf)\n\treturn {Text: \"Hello, XGo\"}\n}\n\nret := foo({Dir: \"/foo/bar\", Level: 1})\nprintln(ret)\n"
  },
  {
    "path": "printer/_testdata/41-UDT-RangeForEach/udt_range.xgo",
    "content": "type foo struct {\n}\n\nfunc (p *foo) Gop_Enum(proc func(key int, val string)) {\n\tproc(3, \"Hi\")\n\tproc(7, \"XGo\")\n}\n\nfor k, v in new(foo) {\n\tprintln(k, v)\n}\n\nprintln({v: k for k, v in new(foo)})\n"
  },
  {
    "path": "printer/_testdata/42-UDT-RangeIterator/udt_range_iter.xgo",
    "content": "type fooIter struct {\n\tdata *foo\n\tidx  int\n}\n\nfunc (p *fooIter) Next() (key int, val string, ok bool) {\n\tif p.idx < len(p.data.key) {\n\t\tkey, val, ok = p.data.key[p.idx], p.data.val[p.idx], true\n\t\tp.idx++\n\t}\n\treturn\n}\n\ntype foo struct {\n\tkey []int\n\tval []string\n}\n\nfunc newFoo() *foo {\n\treturn &foo{key: [3, 7], val: [\"Hi\", \"XGo\"]}\n}\n\nfunc (p *foo) Gop_Enum() *fooIter {\n\treturn &fooIter{data: p}\n}\n\nobj := newFoo()\nfor k, v in obj {\n\tprintln(k, v)\n}\n\nprintln({v: k for k, v in obj})\n"
  },
  {
    "path": "printer/_testdata/43-RangeExpr/rangeexpr.xgo",
    "content": "println \"---------------------------\"\n\nfor i in :10 {\n\tprintln(i)\n}\n\nprintln \"---------------------------\"\n\nfor i in 9:-1:-1 {\n\tprintln(i)\n}\n\nprintln \"---------------------------\"\n\nfor i := range :10:2 {\n\tprintln(i)\n}\n\nprintln \"---------------------------\"\n\nfor i := range 1:10:3 {\n\tprintln(i)\n}\n\nprintln \"---------------------------\"\n\nfor range :10 {\n\tprintln(\"Range expression\")\n}\n\nprintln \"---------------------------\"\n"
  },
  {
    "path": "printer/bugfix_test.go",
    "content": "package printer_test\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\tgoast \"go/ast\"\n\tgoprinter \"go/printer\"\n\tgotoken \"go/token\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/printer\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nfunc goIdents(name string) []*goast.Ident {\n\treturn []*goast.Ident{{Name: name}}\n}\n\nfunc TestGoFormat(t *testing.T) {\n\tconst (\n\t\tmode = goprinter.UseSpaces | goprinter.TabIndent\n\t)\n\tconfig := goprinter.Config{Mode: mode, Indent: 0, Tabwidth: 8}\n\tdecl := &goast.GenDecl{Tok: gotoken.VAR, Lparen: 1, Rparen: 1}\n\tdecl.Specs = []goast.Spec{\n\t\t&goast.ValueSpec{Names: goIdents(\"foo\"), Type: goast.NewIdent(\"int\")},\n\t\t&goast.ValueSpec{Names: goIdents(\"bar\"), Type: goast.NewIdent(\"string\")},\n\t}\n\tb := bytes.NewBuffer(nil)\n\tfset := gotoken.NewFileSet()\n\tconfig.Fprint(b, fset, decl)\n\tconst codeExp = `var (\n\tfoo int\n\tbar string\n)`\n\tif code := b.String(); code != codeExp {\n\t\tt.Fatal(\"config.Fprint:\", code, codeExp)\n\t}\n}\n\nfunc gopIdents(name string) []*ast.Ident {\n\treturn []*ast.Ident{{Name: name}}\n}\n\nfunc TestGopFormat(t *testing.T) {\n\tconst (\n\t\tmode = printer.UseSpaces | printer.TabIndent\n\t)\n\tconfig := printer.Config{Mode: mode, Indent: 0, Tabwidth: 8}\n\tdecl := &ast.GenDecl{Tok: token.VAR, Lparen: 1, Rparen: 1}\n\tdecl.Specs = []ast.Spec{\n\t\t&ast.ValueSpec{Names: gopIdents(\"foo\"), Type: ast.NewIdent(\"int\")},\n\t\t&ast.ValueSpec{Names: gopIdents(\"bar\"), Type: ast.NewIdent(\"string\")},\n\t}\n\tb := bytes.NewBuffer(nil)\n\tfset := token.NewFileSet()\n\tconfig.Fprint(b, fset, decl)\n\tconst codeExp = `var (\n\tfoo int\n\tbar string\n)`\n\tif code := b.String(); code != codeExp {\n\t\tt.Fatal(\"config.Fprint:\", code, codeExp)\n\t}\n}\n"
  },
  {
    "path": "printer/nodes.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This file implements printing of AST nodes; specifically\n// expressions, statements, declarations, and files. It uses\n// the print functionality implemented in printer.go.\n\npackage printer\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nconst (\n\tDbgFlagAll = 1\n)\n\nvar (\n\tdebugFormat bool\n)\n\nfunc SetDebug(flags int) {\n\tif flags != 0 {\n\t\tdebugFormat = true\n\t}\n}\n\n// Formatting issues:\n// - better comment formatting for /*-style comments at the end of a line (e.g. a declaration)\n//   when the comment spans multiple lines; if such a comment is just two lines, formatting is\n//   not idempotent\n// - formatting of expression lists\n// - should use blank instead of tab to separate one-line function bodies from\n//   the function header unless there is a group of consecutive one-liners\n\n// ----------------------------------------------------------------------------\n// Common AST nodes.\n\n// Print as many newlines as necessary (but at least min newlines) to get to\n// the current line. ws is printed before the first line break. If newSection\n// is set, the first line break is printed as formfeed. Returns 0 if no line\n// breaks were printed, returns 1 if there was exactly one newline printed,\n// and returns a value > 1 if there was a formfeed or more than one newline\n// printed.\n//\n// TODO(gri): linebreak may add too many lines if the next statement at \"line\"\n//\n//\tis preceded by comments because the computation of n assumes\n//\tthe current position before the comment and the target position\n//\tafter the comment. Thus, after interspersing such comments, the\n//\tspace taken up by them is not considered to reduce the number of\n//\tlinebreaks. At the moment there is no easy way to know about\n//\tfuture (not yet interspersed) comments in this function.\nfunc (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (nbreaks int) {\n\tn := nlimit(line - p.pos.Line)\n\tif n < min {\n\t\tn = min\n\t}\n\tif n > 0 {\n\t\tp.print(ws)\n\t\tif newSection {\n\t\t\tp.print(formfeed)\n\t\t\tn--\n\t\t\tnbreaks = 2\n\t\t}\n\t\tnbreaks += n\n\t\tfor ; n > 0; n-- {\n\t\t\tp.print(newline)\n\t\t}\n\t}\n\treturn\n}\n\n// setComment sets g as the next comment if g != nil and if node comments\n// are enabled - this mode is used when printing source code fragments such\n// as exports only. It assumes that there is no pending comment in p.comments\n// and at most one pending comment in the p.comment cache.\nfunc (p *printer) setComment(g *ast.CommentGroup) {\n\tif g == nil || !p.useNodeComments {\n\t\treturn\n\t}\n\tif p.comments == nil {\n\t\t// initialize p.comments lazily\n\t\tp.comments = make([]*ast.CommentGroup, 1)\n\t} else if p.cindex < len(p.comments) {\n\t\t// for some reason there are pending comments; this\n\t\t// should never happen - handle gracefully and flush\n\t\t// all comments up to g, ignore anything after that\n\t\tp.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL)\n\t\tp.comments = p.comments[0:1]\n\t\t// in debug mode, report error\n\t\tp.internalError(\"setComment found pending comments\")\n\t}\n\tp.comments[0] = g\n\tp.cindex = 0\n\t// don't overwrite any pending comment in the p.comment cache\n\t// (there may be a pending comment when a line comment is\n\t// immediately followed by a lead comment with no other\n\t// tokens between)\n\tif p.commentOffset == infinity {\n\t\tp.nextComment() // get comment ready for use\n\t}\n}\n\ntype exprListMode uint\n\nconst (\n\tcommaTerm exprListMode = 1 << iota // list is optionally terminated by a comma\n\tnoIndent                           // no extra indentation in multi-line lists\n)\n\n// If indent is set, a multi-line identifier list is indented after the\n// first linebreak encountered.\nfunc (p *printer) identList(list []*ast.Ident, indent bool) {\n\t// convert into an expression list so we can re-use exprList formatting\n\txlist := make([]ast.Expr, len(list))\n\tfor i, x := range list {\n\t\txlist[i] = x\n\t}\n\tvar mode exprListMode\n\tif !indent {\n\t\tmode = noIndent\n\t}\n\tp.exprList(token.NoPos, xlist, 1, mode, token.NoPos, false)\n}\n\nconst filteredMsg = \"contains filtered or unexported fields\"\n\n// Print a list of expressions. If the list spans multiple\n// source lines, the original line breaks are respected between\n// expressions.\n//\n// TODO(gri) Consider rewriting this to be independent of []ast.Expr\n//\n//\tso that we can use the algorithm for any kind of list\n//\t(e.g., pass list via a channel over which to range).\nfunc (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos, isIncomplete bool) {\n\tif len(list) == 0 {\n\t\tif isIncomplete {\n\t\t\tprev := p.posFor(prev0)\n\t\t\tnext := p.posFor(next0)\n\t\t\tif prev.IsValid() && prev.Line == next.Line {\n\t\t\t\tp.print(\"/* \" + filteredMsg + \" */\")\n\t\t\t} else {\n\t\t\t\tp.print(newline)\n\t\t\t\tp.print(indent, \"// \"+filteredMsg, unindent, newline)\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tprev := p.posFor(prev0)\n\tnext := p.posFor(next0)\n\tline := p.lineFor(list[0].Pos())\n\tendLine := p.lineFor(list[len(list)-1].End())\n\n\tif prev.IsValid() && prev.Line == line && line == endLine {\n\t\t// all list entries on a single line\n\t\tfor i, x := range list {\n\t\t\tif i > 0 {\n\t\t\t\t// use position of expression following the comma as\n\t\t\t\t// comma position for correct comment placement\n\t\t\t\tp.print(x.Pos(), token.COMMA, blank)\n\t\t\t}\n\t\t\tp.expr0(x, depth)\n\t\t}\n\t\tif isIncomplete {\n\t\t\tp.print(token.COMMA, blank, \"/* \"+filteredMsg+\" */\")\n\t\t}\n\t\treturn\n\t}\n\n\t// list entries span multiple lines;\n\t// use source code positions to guide line breaks\n\n\t// Don't add extra indentation if noIndent is set;\n\t// i.e., pretend that the first line is already indented.\n\tws := ignore\n\tif mode&noIndent == 0 {\n\t\tws = indent\n\t}\n\n\t// The first linebreak is always a formfeed since this section must not\n\t// depend on any previous formatting.\n\tprevBreak := -1 // index of last expression that was followed by a linebreak\n\tif prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) > 0 {\n\t\tws = ignore\n\t\tprevBreak = 0\n\t}\n\n\t// initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line\n\tsize := 0\n\n\t// We use the ratio between the geometric mean of the previous key sizes and\n\t// the current size to determine if there should be a break in the alignment.\n\t// To compute the geometric mean we accumulate the ln(size) values (lnsum)\n\t// and the number of sizes included (count).\n\tlnsum := 0.0\n\tcount := 0\n\n\t// print all list elements\n\tprevLine := prev.Line\n\tfor i, x := range list {\n\t\tline = p.lineFor(x.Pos())\n\n\t\t// Determine if the next linebreak, if any, needs to use formfeed:\n\t\t// in general, use the entire node size to make the decision; for\n\t\t// key:value expressions, use the key size.\n\t\t// TODO(gri) for a better result, should probably incorporate both\n\t\t//           the key and the node size into the decision process\n\t\tuseFF := true\n\n\t\t// Determine element size: All bets are off if we don't have\n\t\t// position information for the previous and next token (likely\n\t\t// generated code - simply ignore the size in this case by setting\n\t\t// it to 0).\n\t\tprevSize := size\n\t\tconst infinity = 1e6 // larger than any source line\n\t\tsize = p.nodeSize(x, infinity)\n\t\tpair, isPair := x.(*ast.KeyValueExpr)\n\t\tif size <= infinity && prev.IsValid() && next.IsValid() {\n\t\t\t// x fits on a single line\n\t\t\tif isPair {\n\t\t\t\tsize = p.nodeSize(pair.Key, infinity) // size <= infinity\n\t\t\t}\n\t\t} else {\n\t\t\t// size too large or we don't have good layout information\n\t\t\tsize = 0\n\t\t}\n\n\t\t// If the previous line and the current line had single-\n\t\t// line-expressions and the key sizes are small or the\n\t\t// ratio between the current key and the geometric mean\n\t\t// if the previous key sizes does not exceed a threshold,\n\t\t// align columns and do not use formfeed.\n\t\tif prevSize > 0 && size > 0 {\n\t\t\tconst smallSize = 40\n\t\t\tif count == 0 || prevSize <= smallSize && size <= smallSize {\n\t\t\t\tuseFF = false\n\t\t\t} else {\n\t\t\t\tconst r = 2.5                               // threshold\n\t\t\t\tgeomean := math.Exp(lnsum / float64(count)) // count > 0\n\t\t\t\tratio := float64(size) / geomean\n\t\t\t\tuseFF = r*ratio <= 1 || r <= ratio\n\t\t\t}\n\t\t}\n\n\t\tneedsLinebreak := 0 < prevLine && prevLine < line\n\t\tif i > 0 {\n\t\t\t// Use position of expression following the comma as\n\t\t\t// comma position for correct comment placement, but\n\t\t\t// only if the expression is on the same line.\n\t\t\tif !needsLinebreak {\n\t\t\t\tp.print(x.Pos())\n\t\t\t}\n\t\t\tp.print(token.COMMA)\n\t\t\tneedsBlank := true\n\t\t\tif needsLinebreak {\n\t\t\t\t// Lines are broken using newlines so comments remain aligned\n\t\t\t\t// unless useFF is set or there are multiple expressions on\n\t\t\t\t// the same line in which case formfeed is used.\n\t\t\t\tnbreaks := p.linebreak(line, 0, ws, useFF || prevBreak+1 < i)\n\t\t\t\tif nbreaks > 0 {\n\t\t\t\t\tws = ignore\n\t\t\t\t\tprevBreak = i\n\t\t\t\t\tneedsBlank = false // we got a line break instead\n\t\t\t\t}\n\t\t\t\t// If there was a new section or more than one new line\n\t\t\t\t// (which means that the tabwriter will implicitly break\n\t\t\t\t// the section), reset the geomean variables since we are\n\t\t\t\t// starting a new group of elements with the next element.\n\t\t\t\tif nbreaks > 1 {\n\t\t\t\t\tlnsum = 0\n\t\t\t\t\tcount = 0\n\t\t\t\t}\n\t\t\t}\n\t\t\tif needsBlank {\n\t\t\t\tp.print(blank)\n\t\t\t}\n\t\t}\n\n\t\tif len(list) > 1 && isPair && size > 0 && needsLinebreak {\n\t\t\t// We have a key:value expression that fits onto one line\n\t\t\t// and it's not on the same line as the prior expression:\n\t\t\t// Use a column for the key such that consecutive entries\n\t\t\t// can align if possible.\n\t\t\t// (needsLinebreak is set if we started a new line before)\n\t\t\tp.expr(pair.Key)\n\t\t\tp.print(pair.Colon, token.COLON, vtab)\n\t\t\tp.expr(pair.Value)\n\t\t} else {\n\t\t\tp.expr0(x, depth)\n\t\t}\n\n\t\tif size > 0 {\n\t\t\tlnsum += math.Log(float64(size))\n\t\t\tcount++\n\t\t}\n\n\t\tprevLine = line\n\t}\n\n\tif mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line {\n\t\t// Print a terminating comma if the next token is on a new line.\n\t\tp.print(token.COMMA)\n\t\tif isIncomplete {\n\t\t\tp.print(newline)\n\t\t\tp.print(\"// \" + filteredMsg)\n\t\t}\n\t\tif ws == ignore && mode&noIndent == 0 {\n\t\t\t// unindent if we indented\n\t\t\tp.print(unindent)\n\t\t}\n\t\tp.print(formfeed) // terminating comma needs a line break to look good\n\t\treturn\n\t}\n\n\tif isIncomplete {\n\t\tp.print(token.COMMA, newline)\n\t\tp.print(\"// \"+filteredMsg, newline)\n\t}\n\n\tif ws == ignore && mode&noIndent == 0 {\n\t\t// unindent if we indented\n\t\tp.print(unindent)\n\t}\n}\n\nfunc (p *printer) parameters(fields *ast.FieldList) {\n\tp.print(fields.Opening, token.LPAREN)\n\tif len(fields.List) > 0 {\n\t\tprevLine := p.lineFor(fields.Opening)\n\t\tws := indent\n\t\tfor i, par := range fields.List {\n\t\t\t// determine par begin and end line (may be different\n\t\t\t// if there are multiple parameter names for this par\n\t\t\t// or the type is on a separate line)\n\t\t\tvar parLineBeg int\n\t\t\tif len(par.Names) > 0 {\n\t\t\t\tparLineBeg = p.lineFor(par.Names[0].Pos())\n\t\t\t} else {\n\t\t\t\tparLineBeg = p.lineFor(par.Type.Pos())\n\t\t\t}\n\t\t\tvar parLineEnd = p.lineFor(par.Type.End())\n\t\t\t// separating \",\" if needed\n\t\t\tneedsLinebreak := 0 < prevLine && prevLine < parLineBeg\n\t\t\tif i > 0 {\n\t\t\t\t// use position of parameter following the comma as\n\t\t\t\t// comma position for correct comma placement, but\n\t\t\t\t// only if the next parameter is on the same line\n\t\t\t\tif !needsLinebreak {\n\t\t\t\t\tp.print(par.Pos())\n\t\t\t\t}\n\t\t\t\tp.print(token.COMMA)\n\t\t\t}\n\t\t\t// separator if needed (linebreak or blank)\n\t\t\tif needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) > 0 {\n\t\t\t\t// break line if the opening \"(\" or previous parameter ended on a different line\n\t\t\t\tws = ignore\n\t\t\t} else if i > 0 {\n\t\t\t\tp.print(blank)\n\t\t\t}\n\t\t\t// parameter names\n\t\t\tif len(par.Names) > 0 {\n\t\t\t\t// Very subtle: If we indented before (ws == ignore), identList\n\t\t\t\t// won't indent again. If we didn't (ws == indent), identList will\n\t\t\t\t// indent if the identList spans multiple lines, and it will outdent\n\t\t\t\t// again at the end (and still ws == indent). Thus, a subsequent indent\n\t\t\t\t// by a linebreak call after a type, or in the next multi-line identList\n\t\t\t\t// will do the right thing.\n\t\t\t\tp.identList(par.Names, ws == indent)\n\t\t\t\tp.print(blank)\n\t\t\t}\n\t\t\t// parameter type\n\t\t\tp.expr(stripParensAlways(par.Type))\n\t\t\t// optional parameter marker\n\t\t\tif par.Optional.IsValid() {\n\t\t\t\tp.print(token.QUESTION)\n\t\t\t}\n\t\t\tprevLine = parLineEnd\n\t\t}\n\t\t// if the closing \")\" is on a separate line from the last parameter,\n\t\t// print an additional \",\" and line break\n\t\tif closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {\n\t\t\tp.print(token.COMMA)\n\t\t\tp.linebreak(closing, 0, ignore, true)\n\t\t}\n\t\t// unindent if we indented\n\t\tif ws == ignore {\n\t\t\tp.print(unindent)\n\t\t}\n\t}\n\tp.print(fields.Closing, token.RPAREN)\n}\n\nfunc (p *printer) signature(params, result *ast.FieldList) {\n\tif params != nil {\n\t\tp.parameters(params)\n\t} else {\n\t\tp.print(token.LPAREN, token.RPAREN)\n\t}\n\tn := result.NumFields()\n\tif n > 0 {\n\t\t// result != nil\n\t\tp.print(blank)\n\t\tif n == 1 && result.List[0].Names == nil {\n\t\t\t// single anonymous result; no ()'s\n\t\t\tp.expr(stripParensAlways(result.List[0].Type))\n\t\t\treturn\n\t\t}\n\t\tp.parameters(result)\n\t}\n}\n\nfunc identListSize(list []*ast.Ident, maxSize int) (size int) {\n\tfor i, x := range list {\n\t\tif i > 0 {\n\t\t\tsize += len(\", \")\n\t\t}\n\t\tsize += utf8.RuneCountInString(x.Name)\n\t\tif size >= maxSize {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\nfunc (p *printer) isOneLineFieldList(list []*ast.Field) bool {\n\tif len(list) != 1 {\n\t\treturn false // allow only one field\n\t}\n\tf := list[0]\n\tif f.Tag != nil || f.Comment != nil {\n\t\treturn false // don't allow tags or comments\n\t}\n\t// only name(s) and type\n\tconst maxSize = 30 // adjust as appropriate, this is an approximate value\n\tnamesSize := identListSize(f.Names, maxSize)\n\tif namesSize > 0 {\n\t\tnamesSize = 1 // blank between names and types\n\t}\n\ttypeSize := p.nodeSize(f.Type, maxSize)\n\treturn namesSize+typeSize <= maxSize\n}\n\nfunc (p *printer) setLineComment(text string) {\n\tp.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}})\n}\n\nfunc (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {\n\tlbrace := fields.Opening\n\tlist := fields.List\n\trbrace := fields.Closing\n\thasComments := isIncomplete || p.commentBefore(p.posFor(rbrace))\n\tsrcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.lineFor(lbrace) == p.lineFor(rbrace)\n\n\tif !hasComments && srcIsOneLine {\n\t\t// possibly a one-line struct/interface\n\t\tif len(list) == 0 {\n\t\t\t// no blank between keyword and {} in this case\n\t\t\tp.print(lbrace, token.LBRACE, rbrace, token.RBRACE)\n\t\t\treturn\n\t\t} else if p.isOneLineFieldList(list) {\n\t\t\t// small enough - print on one line\n\t\t\t// (don't use identList and ignore source line breaks)\n\t\t\tp.print(lbrace, token.LBRACE, blank)\n\t\t\tf := list[0]\n\t\t\tif isStruct {\n\t\t\t\tfor i, x := range f.Names {\n\t\t\t\t\tif i > 0 {\n\t\t\t\t\t\t// no comments so no need for comma position\n\t\t\t\t\t\tp.print(token.COMMA, blank)\n\t\t\t\t\t}\n\t\t\t\t\tp.expr(x)\n\t\t\t\t}\n\t\t\t\tif len(f.Names) > 0 {\n\t\t\t\t\tp.print(blank)\n\t\t\t\t}\n\t\t\t\tp.expr(f.Type)\n\t\t\t} else { // interface\n\t\t\t\tif ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {\n\t\t\t\t\t// method\n\t\t\t\t\tp.expr(f.Names[0])\n\t\t\t\t\tp.signature(ftyp.Params, ftyp.Results)\n\t\t\t\t} else {\n\t\t\t\t\t// embedded interface\n\t\t\t\t\tp.expr(f.Type)\n\t\t\t\t}\n\t\t\t}\n\t\t\tp.print(blank, rbrace, token.RBRACE)\n\t\t\treturn\n\t\t}\n\t}\n\t// hasComments || !srcIsOneLine\n\n\tp.print(blank, lbrace, token.LBRACE, indent)\n\tif hasComments || len(list) > 0 {\n\t\tp.print(formfeed)\n\t}\n\n\tif isStruct {\n\n\t\tsep := vtab\n\t\tif len(list) == 1 {\n\t\t\tsep = blank\n\t\t}\n\t\tvar line int\n\t\tfor i, f := range list {\n\t\t\tif i > 0 {\n\t\t\t\tp.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0)\n\t\t\t}\n\t\t\textraTabs := 0\n\t\t\tp.setComment(f.Doc)\n\t\t\tp.recordLine(&line)\n\t\t\tif len(f.Names) > 0 {\n\t\t\t\t// named fields\n\t\t\t\tp.identList(f.Names, false)\n\t\t\t\tp.print(sep)\n\t\t\t\tp.expr(f.Type)\n\t\t\t\textraTabs = 1\n\t\t\t} else {\n\t\t\t\t// anonymous field\n\t\t\t\tp.expr(f.Type)\n\t\t\t\textraTabs = 2\n\t\t\t}\n\t\t\tif f.Tag != nil {\n\t\t\t\tif len(f.Names) > 0 && sep == vtab {\n\t\t\t\t\tp.print(sep)\n\t\t\t\t}\n\t\t\t\tp.print(sep)\n\t\t\t\tp.expr(f.Tag)\n\t\t\t\textraTabs = 0\n\t\t\t}\n\t\t\tif f.Comment != nil {\n\t\t\t\tfor ; extraTabs > 0; extraTabs-- {\n\t\t\t\t\tp.print(sep)\n\t\t\t\t}\n\t\t\t\tp.setComment(f.Comment)\n\t\t\t}\n\t\t}\n\t\tif isIncomplete {\n\t\t\tif len(list) > 0 {\n\t\t\t\tp.print(formfeed)\n\t\t\t}\n\t\t\tp.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment\n\t\t\tp.setLineComment(\"// \" + filteredMsg)\n\t\t}\n\n\t} else { // interface\n\n\t\tvar line int\n\t\tfor i, f := range list {\n\t\t\tif i > 0 {\n\t\t\t\tp.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0)\n\t\t\t}\n\t\t\tp.setComment(f.Doc)\n\t\t\tp.recordLine(&line)\n\t\t\tif ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {\n\t\t\t\t// method\n\t\t\t\tp.expr(f.Names[0])\n\t\t\t\tp.signature(ftyp.Params, ftyp.Results)\n\t\t\t} else {\n\t\t\t\t// embedded interface\n\t\t\t\tp.expr(f.Type)\n\t\t\t}\n\t\t\tp.setComment(f.Comment)\n\t\t}\n\t\tif isIncomplete {\n\t\t\tif len(list) > 0 {\n\t\t\t\tp.print(formfeed)\n\t\t\t}\n\t\t\tp.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment\n\t\t\tp.setLineComment(\"// contains filtered or unexported methods\")\n\t\t}\n\n\t}\n\tp.print(unindent, formfeed, rbrace, token.RBRACE)\n}\n\n// ----------------------------------------------------------------------------\n// Expressions\n\nfunc walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {\n\tswitch e.Op.Precedence() {\n\tcase 4:\n\t\thas4 = true\n\tcase 5:\n\t\thas5 = true\n\t}\n\n\tswitch l := e.X.(type) {\n\tcase *ast.BinaryExpr:\n\t\tif l.Op.Precedence() < e.Op.Precedence() {\n\t\t\t// parens will be inserted.\n\t\t\t// pretend this is an *ast.ParenExpr and do nothing.\n\t\t\tbreak\n\t\t}\n\t\th4, h5, mp := walkBinary(l)\n\t\thas4 = has4 || h4\n\t\thas5 = has5 || h5\n\t\tif maxProblem < mp {\n\t\t\tmaxProblem = mp\n\t\t}\n\t}\n\n\tswitch r := e.Y.(type) {\n\tcase *ast.BinaryExpr:\n\t\tif r.Op.Precedence() <= e.Op.Precedence() {\n\t\t\t// parens will be inserted.\n\t\t\t// pretend this is an *ast.ParenExpr and do nothing.\n\t\t\tbreak\n\t\t}\n\t\th4, h5, mp := walkBinary(r)\n\t\thas4 = has4 || h4\n\t\thas5 = has5 || h5\n\t\tif maxProblem < mp {\n\t\t\tmaxProblem = mp\n\t\t}\n\n\tcase *ast.StarExpr:\n\t\tif e.Op == token.QUO { // `*/`\n\t\t\tmaxProblem = 5\n\t\t}\n\n\tcase *ast.UnaryExpr:\n\t\tswitch e.Op.String() + r.Op.String() {\n\t\tcase \"/*\", \"&&\", \"&^\":\n\t\t\tmaxProblem = 5\n\t\tcase \"++\", \"--\":\n\t\t\tif maxProblem < 4 {\n\t\t\t\tmaxProblem = 4\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc cutoff(e *ast.BinaryExpr, depth int) int {\n\thas4, has5, maxProblem := walkBinary(e)\n\tif maxProblem > 0 {\n\t\treturn maxProblem + 1\n\t}\n\tif has4 && has5 {\n\t\tif depth == 1 {\n\t\t\treturn 5\n\t\t}\n\t\treturn 4\n\t}\n\tif depth == 1 {\n\t\treturn 6\n\t}\n\treturn 4\n}\n\nfunc diffPrec(expr ast.Expr, prec int) int {\n\tx, ok := expr.(*ast.BinaryExpr)\n\tif !ok || prec != x.Op.Precedence() {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\nfunc reduceDepth(depth int) int {\n\tdepth--\n\tif depth < 1 {\n\t\tdepth = 1\n\t}\n\treturn depth\n}\n\n// Format the binary expression: decide the cutoff and then format.\n// Let's call depth == 1 Normal mode, and depth > 1 Compact mode.\n// (Algorithm suggestion by Russ Cox.)\n//\n// The precedences are:\n//\n//\t5             *  /  %  <<  >>  &  &^\n//\t4             +  -  |  ^\n//\t3             ==  !=  <  <=  >  >=\n//\t2             &&\n//\t1             ||\n//\n// The only decision is whether there will be spaces around levels 4 and 5.\n// There are never spaces at level 6 (unary), and always spaces at levels 3 and below.\n//\n// To choose the cutoff, look at the whole expression but excluding primary\n// expressions (function calls, parenthesized exprs), and apply these rules:\n//\n//  1. If there is a binary operator with a right side unary operand\n//     that would clash without a space, the cutoff must be (in order):\n//\n//     /*\t6\n//     &&\t6\n//     &^\t6\n//     ++\t5\n//     --\t5\n//\n//     (Comparison operators always have spaces around them.)\n//\n//  2. If there is a mix of level 5 and level 4 operators, then the cutoff\n//     is 5 (use spaces to distinguish precedence) in Normal mode\n//     and 4 (never use spaces) in Compact mode.\n//\n//  3. If there are no level 4 operators or no level 5 operators, then the\n//     cutoff is 6 (always use spaces) in Normal mode\n//     and 4 (never use spaces) in Compact mode.\nfunc (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {\n\tprec := x.Op.Precedence()\n\tif prec < prec1 {\n\t\t// parenthesis needed\n\t\t// Note: The parser inserts an ast.ParenExpr node; thus this case\n\t\t//       can only occur if the AST is created in a different way.\n\t\tp.print(token.LPAREN)\n\t\tp.expr0(x, reduceDepth(depth)) // parentheses undo one level of depth\n\t\tp.print(token.RPAREN)\n\t\treturn\n\t}\n\n\tprintBlank := prec < cutoff\n\n\tws := indent\n\tp.expr1(x.X, prec, depth+diffPrec(x.X, prec))\n\tif printBlank {\n\t\tp.print(blank)\n\t}\n\txline := p.pos.Line // before the operator (it may be on the next line!)\n\tyline := p.lineFor(x.Y.Pos())\n\tp.print(x.OpPos, x.Op)\n\tif xline != yline && xline > 0 && yline > 0 {\n\t\t// at least one line break, but respect an extra empty line\n\t\t// in the source\n\t\tif p.linebreak(yline, 1, ws, true) > 0 {\n\t\t\tws = ignore\n\t\t\tprintBlank = false // no blank after line break\n\t\t}\n\t}\n\tif printBlank {\n\t\tp.print(blank)\n\t}\n\tp.expr1(x.Y, prec+1, depth+1)\n\tif ws == ignore {\n\t\tp.print(unindent)\n\t}\n}\n\nfunc isBinary(expr ast.Expr) bool {\n\t_, ok := expr.(*ast.BinaryExpr)\n\treturn ok\n}\n\nfunc (p *printer) expr1(expr ast.Expr, prec1, depth int) {\n\tp.print(expr.Pos())\n\n\tswitch x := expr.(type) {\n\tcase *ast.BadExpr:\n\t\tp.print(\"BadExpr\")\n\n\tcase *ast.Ident:\n\t\tp.print(x)\n\n\tcase *ast.BinaryExpr:\n\t\tif depth < 1 {\n\t\t\tp.internalError(\"depth < 1:\", depth)\n\t\t\tdepth = 1\n\t\t}\n\t\tif v, ok := x.Y.(*ast.BasicLit); ok && v.Kind == token.RAT {\n\t\t\tdepth++\n\t\t}\n\t\tp.binaryExpr(x, prec1, cutoff(x, depth), depth)\n\n\tcase *ast.KeyValueExpr:\n\t\tp.expr(x.Key)\n\t\tp.print(x.Colon, token.COLON, blank)\n\t\tp.expr(x.Value)\n\n\tcase *ast.StarExpr:\n\t\tconst prec = token.UnaryPrec\n\t\tif prec < prec1 {\n\t\t\t// parenthesis needed\n\t\t\tp.print(token.LPAREN)\n\t\t\tp.print(token.MUL)\n\t\t\tp.expr(x.X)\n\t\t\tp.print(token.RPAREN)\n\t\t} else {\n\t\t\t// no parenthesis needed\n\t\t\tp.print(token.MUL)\n\t\t\tp.expr(x.X)\n\t\t}\n\n\tcase *ast.UnaryExpr:\n\t\tconst prec = token.UnaryPrec\n\t\tif prec < prec1 {\n\t\t\t// parenthesis needed\n\t\t\tp.print(token.LPAREN)\n\t\t\tp.expr(x)\n\t\t\tp.print(token.RPAREN)\n\t\t} else {\n\t\t\t// no parenthesis needed\n\t\t\tp.print(x.Op)\n\t\t\tif x.Op == token.RANGE {\n\t\t\t\t// TODO(gri) Remove this code if it cannot be reached.\n\t\t\t\tp.print(blank)\n\t\t\t}\n\t\t\tp.expr1(x.X, prec, depth)\n\t\t}\n\n\tcase *ast.BasicLit:\n\t\tp.print(x)\n\n\tcase *ast.NumberUnitLit:\n\t\tp.print(&ast.BasicLit{Kind: x.Kind, Value: x.Value})\n\t\tp.print(&ast.Ident{Name: x.Unit})\n\n\tcase *ast.FuncLit:\n\t\tp.print(x.Type.Pos(), token.FUNC)\n\t\t// See the comment in funcDecl about how the header size is computed.\n\t\tstartCol := p.out.Column - len(\"func\")\n\t\tp.signature(x.Type.Params, x.Type.Results)\n\t\tp.funcBody(p.distanceFrom(x.Type.Pos(), startCol), blank, x.Body)\n\n\tcase *ast.ParenExpr:\n\t\tif _, hasParens := x.X.(*ast.ParenExpr); hasParens {\n\t\t\t// don't print parentheses around an already parenthesized expression\n\t\t\t// TODO(gri) consider making this more general and incorporate precedence levels\n\t\t\tp.expr0(x.X, depth)\n\t\t} else {\n\t\t\tp.print(token.LPAREN)\n\t\t\tp.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth\n\t\t\tp.print(x.Rparen, token.RPAREN)\n\t\t}\n\n\tcase *ast.SelectorExpr:\n\t\tp.selectorExpr(x, depth, false)\n\n\tcase *ast.AnySelectorExpr:\n\t\tp.anySelectorExpr(x, depth)\n\n\tcase *ast.TypeAssertExpr:\n\t\tp.expr1(x.X, token.HighestPrec, depth)\n\t\tp.print(token.PERIOD, x.Lparen, token.LPAREN)\n\t\tif x.Type != nil {\n\t\t\tp.expr(x.Type)\n\t\t} else {\n\t\t\tp.print(token.TYPE)\n\t\t}\n\t\tp.print(x.Rparen, token.RPAREN)\n\n\tcase *ast.IndexExpr:\n\t\t// TODO(gri): should treat[] like parentheses and undo one level of depth\n\t\tp.expr1(x.X, token.HighestPrec, 1)\n\t\tp.print(x.Lbrack, token.LBRACK)\n\t\tp.expr0(x.Index, depth+1)\n\t\tp.print(x.Rbrack, token.RBRACK)\n\n\tcase *ast.IndexListExpr:\n\t\t// TODO(gri): should treat[] like parentheses and undo one level of depth\n\t\tp.expr1(x.X, token.HighestPrec, 1)\n\t\tp.print(x.Lbrack, token.LBRACK)\n\t\tp.exprList(x.Lbrack, x.Indices, depth+1, commaTerm, x.Rbrack, false)\n\t\tp.print(x.Rbrack, token.RBRACK)\n\n\tcase *ast.SliceExpr:\n\t\t// TODO(gri): should treat[] like parentheses and undo one level of depth\n\t\tp.expr1(x.X, token.HighestPrec, 1)\n\t\tp.print(x.Lbrack, token.LBRACK)\n\t\tindices := []ast.Expr{x.Low, x.High}\n\t\tif x.Max != nil {\n\t\t\tindices = append(indices, x.Max)\n\t\t}\n\t\t// determine if we need extra blanks around ':'\n\t\tvar needsBlanks bool\n\t\tif depth <= 1 {\n\t\t\tvar indexCount int\n\t\t\tvar hasBinaries bool\n\t\t\tfor _, x := range indices {\n\t\t\t\tif x != nil {\n\t\t\t\t\tindexCount++\n\t\t\t\t\tif isBinary(x) {\n\t\t\t\t\t\thasBinaries = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif indexCount > 1 && hasBinaries {\n\t\t\t\tneedsBlanks = true\n\t\t\t}\n\t\t}\n\t\tfor i, x := range indices {\n\t\t\tif i > 0 {\n\t\t\t\tif indices[i-1] != nil && needsBlanks {\n\t\t\t\t\tp.print(blank)\n\t\t\t\t}\n\t\t\t\tp.print(token.COLON)\n\t\t\t\tif x != nil && needsBlanks {\n\t\t\t\t\tp.print(blank)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif x != nil {\n\t\t\t\tp.expr0(x, depth+1)\n\t\t\t}\n\t\t}\n\t\tp.print(x.Rbrack, token.RBRACK)\n\n\tcase *ast.CallExpr:\n\t\tif len(x.Args) > 1 {\n\t\t\tdepth++\n\t\t}\n\t\tvar wasIndented bool\n\t\tif _, ok := x.Fun.(*ast.FuncType); ok {\n\t\t\t// conversions to literal function types require parentheses around the type\n\t\t\tp.print(token.LPAREN)\n\t\t\twasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)\n\t\t\tp.print(token.RPAREN)\n\t\t} else {\n\t\t\twasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)\n\t\t}\n\t\tif x.NoParenEnd != token.NoPos {\n\t\t\tp.print(blank)\n\t\t\tdepth++\n\t\t} else {\n\t\t\tp.print(x.Lparen, token.LPAREN)\n\t\t}\n\t\tif x.Ellipsis.IsValid() {\n\t\t\tp.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis, false)\n\t\t\tp.print(x.Ellipsis, token.ELLIPSIS)\n\t\t\tif x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) {\n\t\t\t\tp.print(token.COMMA, formfeed)\n\t\t\t}\n\t\t} else {\n\t\t\tp.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen, false)\n\t\t}\n\t\tif len(x.Kwargs) > 0 {\n\t\t\tif len(x.Args) > 0 {\n\t\t\t\tp.print(token.COMMA, blank)\n\t\t\t}\n\t\t\tfor i, arg := range x.Kwargs {\n\t\t\t\tif i > 0 {\n\t\t\t\t\tp.print(token.COMMA, blank)\n\t\t\t\t}\n\t\t\t\tp.print(arg.Name, blank, token.ASSIGN, blank)\n\t\t\t\tp.expr0(arg.Value, depth)\n\t\t\t}\n\t\t}\n\t\tif x.NoParenEnd == token.NoPos {\n\t\t\tp.print(x.Rparen, token.RPAREN)\n\t\t}\n\t\tif wasIndented {\n\t\t\tp.print(unindent)\n\t\t}\n\n\tcase *ast.CompositeLit:\n\t\t// composite literal elements that are composite literals themselves may have the type omitted\n\t\tif x.Type != nil {\n\t\t\tp.expr1(x.Type, token.HighestPrec, depth)\n\t\t}\n\t\tp.level++\n\t\tp.print(x.Lbrace, token.LBRACE)\n\t\tp.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace, x.Incomplete)\n\t\t// do not insert extra line break following a /*-style comment\n\t\t// before the closing '}' as it might break the code if there\n\t\t// is no trailing ','\n\t\tmode := noExtraLinebreak\n\t\t// do not insert extra blank following a /*-style comment\n\t\t// before the closing '}' unless the literal is empty\n\t\tif len(x.Elts) > 0 {\n\t\t\tmode |= noExtraBlank\n\t\t}\n\t\t// need the initial indent to print lone comments with\n\t\t// the proper level of indentation\n\t\tp.print(indent, unindent, mode, x.Rbrace, token.RBRACE, mode)\n\t\tp.level--\n\n\tcase *ast.MatrixLit:\n\t\tp.level++\n\t\tp.print(x.Lbrack, token.LBRACK, newline, indent)\n\t\tvar last = len(x.Elts) - 1\n\t\tvar incomplete bool\n\t\tfor i, elts := range x.Elts {\n\t\t\tif i == last {\n\t\t\t\tincomplete = x.Incomplete\n\t\t\t}\n\t\t\tp.exprList(elts[0].Pos(), elts, 1, 0, elts[len(elts)-1].End(), incomplete)\n\t\t\tp.print(newline)\n\t\t}\n\t\tmode := noExtraLinebreak | noExtraBlank\n\t\t// need the initial indent to print lone comments with\n\t\t// the proper level of indentation\n\t\tp.print(unindent, mode, x.Rbrack, token.RBRACK, mode)\n\t\tp.level--\n\n\tcase *ast.Ellipsis:\n\t\tp.print(token.ELLIPSIS)\n\t\tif x.Elt != nil {\n\t\t\tp.expr(x.Elt)\n\t\t}\n\n\tcase *ast.ArrayType:\n\t\tp.print(token.LBRACK)\n\t\tif x.Len != nil {\n\t\t\tp.expr(x.Len)\n\t\t}\n\t\tp.print(token.RBRACK)\n\t\tp.expr(x.Elt)\n\n\tcase *ast.StructType:\n\t\tp.print(token.STRUCT)\n\t\tp.fieldList(x.Fields, true, x.Incomplete)\n\n\tcase *ast.FuncType:\n\t\tp.print(token.FUNC)\n\t\tp.signature(x.Params, x.Results)\n\n\tcase *ast.InterfaceType:\n\t\tp.print(token.INTERFACE)\n\t\tp.fieldList(x.Methods, false, x.Incomplete)\n\n\tcase *ast.MapType:\n\t\tp.print(token.MAP, token.LBRACK)\n\t\tp.expr(x.Key)\n\t\tp.print(token.RBRACK)\n\t\tp.expr(x.Value)\n\n\tcase *ast.ChanType:\n\t\tswitch x.Dir {\n\t\tcase ast.SEND | ast.RECV:\n\t\t\tp.print(token.CHAN)\n\t\tcase ast.RECV:\n\t\t\tp.print(token.ARROW, token.CHAN) // x.Arrow and x.Pos() are the same\n\t\tcase ast.SEND:\n\t\t\tp.print(token.CHAN, x.Arrow, token.ARROW)\n\t\t}\n\t\tp.print(blank)\n\t\tp.expr(x.Value)\n\n\tcase *ast.TupleType:\n\t\tp.parameters(x.Fields)\n\t\t/*\tcase *ast.TernaryExpr:\n\t\t\tp.expr1(x.X, token.HighestPrec, 1)\n\t\t\tp.expr0(x.Cond, 1)\n\t\t\tp.print(x.Question, token.QUESTION)\n\t\t\tp.expr0(x.Y, depth+1)\n\t\t\tp.print(x.Colon, token.COLON)\n\t\t\tp.expr0(x.Y, depth+1)\n\t\t*/\n\tcase *ast.SliceLit:\n\t\tp.print(token.LBRACK)\n\t\tp.exprList(x.Lbrack, x.Elts, depth+1, commaTerm, x.Rbrack, x.Incomplete)\n\t\tmode := noExtraLinebreak\n\t\tif len(x.Elts) > 0 {\n\t\t\tmode |= noExtraBlank\n\t\t}\n\t\tp.print(mode, x.Rbrack, token.RBRACK, mode)\n\n\tcase *ast.TupleLit:\n\t\tp.print(token.LPAREN)\n\t\tp.exprList(x.Lparen, x.Elts, depth+1, commaTerm, x.Rparen, false)\n\t\tmode := noExtraLinebreak\n\t\tif len(x.Elts) > 0 {\n\t\t\tmode |= noExtraBlank\n\t\t}\n\t\tp.print(mode, x.Rparen, token.RPAREN, mode)\n\n\tcase *ast.ComprehensionExpr:\n\t\tswitch x.Tok {\n\t\tcase token.LBRACK: // [...]\n\t\t\tp.print(token.LBRACK)\n\t\t\tp.expr0(x.Elt, depth+1)\n\t\t\tp.print(blank)\n\t\t\tp.listForPhrase(x.Fors)\n\t\t\tp.print(token.RBRACK)\n\t\tdefault: // {...}\n\t\t\tp.print(token.LBRACE)\n\t\t\tif x.Elt != nil {\n\t\t\t\tif elt, ok := x.Elt.(*ast.KeyValueExpr); ok {\n\t\t\t\t\tp.expr0(elt.Key, depth+1)\n\t\t\t\t\tp.print(elt.Colon, token.COLON, blank)\n\t\t\t\t\tp.expr0(elt.Value, depth+1)\n\t\t\t\t} else {\n\t\t\t\t\tp.expr0(x.Elt, depth+1)\n\t\t\t\t}\n\t\t\t\tp.print(blank)\n\t\t\t}\n\t\t\tp.listForPhrase(x.Fors)\n\t\t\tp.print(token.RBRACE)\n\t\t}\n\tcase *ast.ErrWrapExpr:\n\t\tp.expr(x.X)\n\t\tp.print(x.Tok)\n\t\tif x.Default != nil {\n\t\t\tp.print(token.COLON)\n\t\t\tp.expr(x.Default)\n\t\t}\n\tcase *ast.LambdaExpr:\n\t\tif x.LhsHasParen {\n\t\t\tp.print(token.LPAREN)\n\t\t\tp.identList(x.Lhs, false)\n\t\t\tp.print(token.RPAREN, blank)\n\t\t} else if x.Lhs != nil {\n\t\t\tp.expr(x.Lhs[0])\n\t\t\tp.print(blank)\n\t\t}\n\t\tp.print(token.DRARROW, blank)\n\t\tif x.RhsHasParen {\n\t\t\tp.print(token.LPAREN)\n\t\t\tp.exprList(token.NoPos, x.Rhs, 1, noIndent, token.NoPos, false)\n\t\t\tp.print(token.RPAREN)\n\t\t} else {\n\t\t\tp.expr(x.Rhs[0])\n\t\t}\n\n\tcase *ast.LambdaExpr2:\n\t\tif x.LhsHasParen {\n\t\t\tp.print(token.LPAREN)\n\t\t\tp.identList(x.Lhs, false)\n\t\t\tp.print(token.RPAREN, blank)\n\t\t} else if x.Lhs != nil {\n\t\t\tp.expr(x.Lhs[0])\n\t\t\tp.print(blank)\n\t\t}\n\t\tp.print(token.DRARROW, blank)\n\t\tp.block(x.Body, 1)\n\n\tcase *ast.RangeExpr:\n\t\tif x.First != nil {\n\t\t\tp.expr(x.First)\n\t\t}\n\t\tp.print(token.COLON)\n\t\tif x.Last != nil {\n\t\t\tp.expr(x.Last)\n\t\t}\n\t\tif x.Expr3 != nil {\n\t\t\tp.print(token.COLON)\n\t\t\tp.expr(x.Expr3)\n\t\t}\n\n\tcase *ast.EnvExpr:\n\t\tp.print(token.ENV)\n\t\tif x.HasBrace() {\n\t\t\tp.print(token.LBRACE, x.Name, token.RBRACE)\n\t\t} else {\n\t\t\tp.print(x.Name)\n\t\t}\n\n\tcase *ast.CondExpr:\n\t\tp.expr(x.X)\n\t\tp.print(token.AT)\n\t\tp.expr(x.Cond)\n\n\tcase *ast.ElemEllipsis:\n\t\tp.expr(x.Elt)\n\t\tp.print(token.ELLIPSIS)\n\n\tcase *ast.DomainTextLit:\n\t\tp.print(x.Domain)\n\t\tp.print(&ast.BasicLit{Kind: token.STRING, Value: x.Value})\n\n\tdefault:\n\t\tlog.Fatalf(\"unreachable %T\\n\", x)\n\t}\n}\n\nvar (\n\tin = &ast.Ident{Name: \"in\"}\n)\n\nfunc (p *printer) listForPhrase(list []*ast.ForPhrase) {\n\tfor i, x := range list {\n\t\tif i > 0 {\n\t\t\tp.print(blank)\n\t\t}\n\t\tp.print(token.FOR, blank)\n\t\tif x.Key != nil {\n\t\t\tp.expr(x.Key)\n\t\t\tp.print(token.COMMA, blank)\n\t\t}\n\t\tp.print(x.Value, blank)\n\t\tp.print(x.TokPos, in, blank)\n\t\tp.expr(x.X)\n\t\tif x.Cond != nil {\n\t\t\tp.print(blank, x.Cond.Pos(), token.IF, blank)\n\t\t\tif x.Init != nil {\n\t\t\t\tp.stmt(x.Init, false)\n\t\t\t\tp.print(token.SEMICOLON, blank)\n\t\t\t}\n\t\t\tp.expr(x.Cond)\n\t\t}\n\t}\n}\n\nfunc (p *printer) possibleSelectorExpr(expr ast.Expr, prec1, depth int) bool {\n\tif x, ok := expr.(*ast.SelectorExpr); ok {\n\t\treturn p.selectorExpr(x, depth, true)\n\t}\n\tp.expr1(expr, prec1, depth)\n\treturn false\n}\n\n// selectorExpr handles an *ast.SelectorExpr node and reports whether x spans\n// multiple lines.\nfunc (p *printer) selectorExpr(x *ast.SelectorExpr, depth int, isMethod bool) bool {\n\tp.expr1(x.X, token.HighestPrec, depth)\n\tp.print(token.PERIOD)\n\tif line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line {\n\t\tp.print(indent, newline, x.Sel.Pos(), x.Sel)\n\t\tif !isMethod {\n\t\t\tp.print(unindent)\n\t\t}\n\t\treturn true\n\t}\n\tp.print(x.Sel.Pos(), x.Sel)\n\treturn false\n}\n\nfunc (p *printer) anySelectorExpr(x *ast.AnySelectorExpr, depth int) bool {\n\tp.expr1(x.X, token.HighestPrec, depth)\n\tp.print(token.PERIOD, token.MUL, token.MUL, token.PERIOD) // .**.\n\tif line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line {\n\t\tp.print(indent, newline, x.Sel.Pos(), x.Sel, unindent)\n\t\treturn true\n\t}\n\tp.print(x.Sel.Pos(), x.Sel)\n\treturn false\n}\n\nfunc (p *printer) expr0(x ast.Expr, depth int) {\n\tp.expr1(x, token.LowestPrec, depth)\n}\n\nfunc (p *printer) expr(x ast.Expr) {\n\tconst depth = 1\n\tp.expr1(x, token.LowestPrec, depth)\n}\n\n// ----------------------------------------------------------------------------\n// Statements\n\n// Print the statement list indented, but without a newline after the last statement.\n// Extra line breaks between statements in the source are respected but at most one\n// empty line is printed between statements.\nfunc (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {\n\tif nindent > 0 {\n\t\tp.print(indent)\n\t}\n\tvar line int\n\ti := 0\n\tfor _, s := range list {\n\t\t// ignore empty statements (was issue 3466)\n\t\tif _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {\n\t\t\t// nindent == 0 only for lists of switch/select case clauses;\n\t\t\t// in those cases each clause is a new section\n\t\t\tif len(p.output) > 0 {\n\t\t\t\t// only print line break if we are not at the beginning of the output\n\t\t\t\t// (i.e., we are not printing only a partial program)\n\t\t\t\tp.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || p.linesFrom(line) > 0)\n\t\t\t}\n\t\t\tp.recordLine(&line)\n\t\t\tp.stmt(s, nextIsRBrace && i == len(list)-1)\n\t\t\t// labeled statements put labels on a separate line, but here\n\t\t\t// we only care about the start line of the actual statement\n\t\t\t// without label - correct line for each label\n\t\t\tfor t := s; ; {\n\t\t\t\tlt, _ := t.(*ast.LabeledStmt)\n\t\t\t\tif lt == nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tline++\n\t\t\t\tt = lt.Stmt\n\t\t\t}\n\t\t\ti++\n\t\t}\n\t}\n\tif nindent > 0 {\n\t\tp.print(unindent)\n\t}\n}\n\n// block prints an *ast.BlockStmt; it always spans at least two lines.\nfunc (p *printer) block(b *ast.BlockStmt, nindent int) {\n\tp.print(b.Lbrace, token.LBRACE)\n\tp.stmtList(b.List, nindent, true)\n\tp.linebreak(p.lineFor(b.Rbrace), 1, ignore, true)\n\tp.print(b.Rbrace, token.RBRACE)\n}\n\nfunc isTypeName(x ast.Expr) bool {\n\tswitch t := x.(type) {\n\tcase *ast.Ident:\n\t\treturn true\n\tcase *ast.SelectorExpr:\n\t\treturn isTypeName(t.X)\n\t}\n\treturn false\n}\n\nfunc stripParens(x ast.Expr) ast.Expr {\n\tif px, strip := x.(*ast.ParenExpr); strip {\n\t\t// parentheses must not be stripped if there are any\n\t\t// unparenthesized composite literals starting with\n\t\t// a type name\n\t\tast.Inspect(px.X, func(node ast.Node) bool {\n\t\t\tswitch x := node.(type) {\n\t\t\tcase *ast.ParenExpr:\n\t\t\t\t// parentheses protect enclosed composite literals\n\t\t\t\treturn false\n\t\t\tcase *ast.CompositeLit:\n\t\t\t\tif isTypeName(x.Type) {\n\t\t\t\t\tstrip = false // do not strip parentheses\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// in all other cases, keep inspecting\n\t\t\treturn true\n\t\t})\n\t\tif strip {\n\t\t\treturn stripParens(px.X)\n\t\t}\n\t}\n\treturn x\n}\n\nfunc stripParensAlways(x ast.Expr) ast.Expr {\n\tif x, ok := x.(*ast.ParenExpr); ok {\n\t\treturn stripParensAlways(x.X)\n\t}\n\treturn x\n}\n\nfunc (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {\n\tp.print(blank)\n\tneedsBlank := false\n\tif init == nil && post == nil {\n\t\t// no semicolons required\n\t\tif expr != nil {\n\t\t\tp.expr(stripParens(expr))\n\t\t\tneedsBlank = true\n\t\t}\n\t} else {\n\t\t// all semicolons required\n\t\t// (they are not separators, print them explicitly)\n\t\tif init != nil {\n\t\t\tp.stmt(init, false)\n\t\t}\n\t\tp.print(token.SEMICOLON, blank)\n\t\tif expr != nil {\n\t\t\tp.expr(stripParens(expr))\n\t\t\tneedsBlank = true\n\t\t}\n\t\tif isForStmt {\n\t\t\tp.print(token.SEMICOLON, blank)\n\t\t\tneedsBlank = false\n\t\t\tif post != nil {\n\t\t\t\tp.stmt(post, false)\n\t\t\t\tneedsBlank = true\n\t\t\t}\n\t\t}\n\t}\n\tif needsBlank {\n\t\tp.print(blank)\n\t}\n}\n\n// indentList reports whether an expression list would look better if it\n// were indented wholesale (starting with the very first element, rather\n// than starting at the first line break).\nfunc (p *printer) indentList(list []ast.Expr) bool {\n\t// Heuristic: indentList reports whether there are more than one multi-\n\t// line element in the list, or if there is any element that is not\n\t// starting on the same line as the previous one ends.\n\tif len(list) >= 2 {\n\t\tvar b = p.lineFor(list[0].Pos())\n\t\tvar e = p.lineFor(list[len(list)-1].End())\n\t\tif 0 < b && b < e {\n\t\t\t// list spans multiple lines\n\t\t\tn := 0 // multi-line element count\n\t\t\tline := b\n\t\t\tfor _, x := range list {\n\t\t\t\txb := p.lineFor(x.Pos())\n\t\t\t\txe := p.lineFor(x.End())\n\t\t\t\tif line < xb {\n\t\t\t\t\t// x is not starting on the same\n\t\t\t\t\t// line as the previous one ended\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif xb < xe {\n\t\t\t\t\t// x is a multi-line element\n\t\t\t\t\tn++\n\t\t\t\t}\n\t\t\t\tline = xe\n\t\t\t}\n\t\t\treturn n > 1\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {\n\tp.print(stmt.Pos())\n\n\tif p.commentedStmts != nil {\n\t\tif comments, ok := p.commentedStmts[stmt]; ok {\n\t\t\tp.setComment(comments)\n\t\t}\n\t}\n\n\tswitch s := stmt.(type) {\n\tcase *ast.BadStmt:\n\t\tp.print(\"BadStmt\")\n\n\tcase *ast.DeclStmt:\n\t\tp.decl(s.Decl)\n\n\tcase *ast.EmptyStmt:\n\t\t// nothing to do\n\n\tcase *ast.LabeledStmt:\n\t\t// a \"correcting\" unindent immediately following a line break\n\t\t// is applied before the line break if there is no comment\n\t\t// between (see writeWhitespace)\n\t\tp.print(unindent)\n\t\tp.expr(s.Label)\n\t\tp.print(s.Colon, token.COLON, indent)\n\t\tif e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty {\n\t\t\tif !nextIsRBrace {\n\t\t\t\tp.print(newline, e.Pos(), token.SEMICOLON)\n\t\t\t\tbreak\n\t\t\t}\n\t\t} else {\n\t\t\tp.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true)\n\t\t}\n\t\tp.stmt(s.Stmt, nextIsRBrace)\n\n\tcase *ast.ExprStmt:\n\t\tif debugFormat {\n\t\t\tif e, ok := s.X.(*ast.CallExpr); ok {\n\t\t\t\tlog.Println(\"==> ExprStmt\", e.Fun)\n\t\t\t}\n\t\t}\n\t\tconst depth = 1\n\t\tp.expr0(s.X, depth)\n\n\tcase *ast.SendStmt:\n\t\tconst depth = 1\n\t\tp.expr0(s.Chan, depth)\n\t\tp.print(blank, s.Arrow, token.ARROW, blank)\n\t\tfor i, val := range s.Values {\n\t\t\tif i > 0 {\n\t\t\t\tp.print(token.COMMA, blank)\n\t\t\t}\n\t\t\tp.expr0(val, depth)\n\t\t}\n\t\tif s.Ellipsis.IsValid() {\n\t\t\tp.print(s.Ellipsis, token.ELLIPSIS)\n\t\t}\n\n\tcase *ast.IncDecStmt:\n\t\tconst depth = 1\n\t\tp.expr0(s.X, depth+1)\n\t\tp.print(s.TokPos, s.Tok)\n\n\tcase *ast.AssignStmt:\n\t\tif debugFormat {\n\t\t\tlog.Println(\"==> AssignStmt\", s.Lhs)\n\t\t}\n\t\tvar depth = 1\n\t\tif len(s.Lhs) > 1 && len(s.Rhs) > 1 {\n\t\t\tdepth++\n\t\t}\n\t\tp.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos, false)\n\t\tp.print(blank, s.TokPos, s.Tok, blank)\n\t\tp.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos, false)\n\n\tcase *ast.GoStmt:\n\t\tp.print(token.GO, blank)\n\t\tp.expr(s.Call)\n\n\tcase *ast.DeferStmt:\n\t\tp.print(token.DEFER, blank)\n\t\tp.expr(s.Call)\n\n\tcase *ast.ReturnStmt:\n\t\tp.print(token.RETURN)\n\t\tif s.Results != nil {\n\t\t\tp.print(blank)\n\t\t\t// Use indentList heuristic to make corner cases look\n\t\t\t// better (issue 1207). A more systematic approach would\n\t\t\t// always indent, but this would cause significant\n\t\t\t// reformatting of the code base and not necessarily\n\t\t\t// lead to more nicely formatted code in general.\n\t\t\tif p.indentList(s.Results) {\n\t\t\t\tp.print(indent)\n\t\t\t\t// Use NoPos so that a newline never goes before\n\t\t\t\t// the results (see issue #32854).\n\t\t\t\tp.exprList(token.NoPos, s.Results, 1, noIndent, token.NoPos, false)\n\t\t\t\tp.print(unindent)\n\t\t\t} else {\n\t\t\t\tp.exprList(token.NoPos, s.Results, 1, 0, token.NoPos, false)\n\t\t\t}\n\t\t}\n\n\tcase *ast.BranchStmt:\n\t\tp.print(s.Tok)\n\t\tif s.Label != nil {\n\t\t\tp.print(blank)\n\t\t\tp.expr(s.Label)\n\t\t}\n\n\tcase *ast.BlockStmt:\n\t\tp.block(s, 1)\n\n\tcase *ast.IfStmt:\n\t\tp.print(token.IF)\n\t\tp.controlClause(false, s.Init, s.Cond, nil)\n\t\tp.block(s.Body, 1)\n\t\tif s.Else != nil {\n\t\t\tp.print(blank, token.ELSE, blank)\n\t\t\tswitch s.Else.(type) {\n\t\t\tcase *ast.BlockStmt, *ast.IfStmt:\n\t\t\t\tp.stmt(s.Else, nextIsRBrace)\n\t\t\tdefault:\n\t\t\t\t// This can only happen with an incorrectly\n\t\t\t\t// constructed AST. Permit it but print so\n\t\t\t\t// that it can be parsed without errors.\n\t\t\t\tp.print(token.LBRACE, indent, formfeed)\n\t\t\t\tp.stmt(s.Else, true)\n\t\t\t\tp.print(unindent, formfeed, token.RBRACE)\n\t\t\t}\n\t\t}\n\n\tcase *ast.CaseClause:\n\t\tif s.List != nil {\n\t\t\tp.print(token.CASE, blank)\n\t\t\tp.exprList(s.Pos(), s.List, 1, 0, s.Colon, false)\n\t\t} else {\n\t\t\tp.print(token.DEFAULT)\n\t\t}\n\t\tp.print(s.Colon, token.COLON)\n\t\tp.stmtList(s.Body, 1, nextIsRBrace)\n\n\tcase *ast.SwitchStmt:\n\t\tp.print(token.SWITCH)\n\t\tp.controlClause(false, s.Init, s.Tag, nil)\n\t\tp.block(s.Body, 0)\n\n\tcase *ast.TypeSwitchStmt:\n\t\tp.print(token.SWITCH)\n\t\tif s.Init != nil {\n\t\t\tp.print(blank)\n\t\t\tp.stmt(s.Init, false)\n\t\t\tp.print(token.SEMICOLON)\n\t\t}\n\t\tp.print(blank)\n\t\tp.stmt(s.Assign, false)\n\t\tp.print(blank)\n\t\tp.block(s.Body, 0)\n\n\tcase *ast.CommClause:\n\t\tif s.Comm != nil {\n\t\t\tp.print(token.CASE, blank)\n\t\t\tp.stmt(s.Comm, false)\n\t\t} else {\n\t\t\tp.print(token.DEFAULT)\n\t\t}\n\t\tp.print(s.Colon, token.COLON)\n\t\tp.stmtList(s.Body, 1, nextIsRBrace)\n\n\tcase *ast.SelectStmt:\n\t\tp.print(token.SELECT, blank)\n\t\tbody := s.Body\n\t\tif len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) {\n\t\t\t// print empty select statement w/o comments on one line\n\t\t\tp.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)\n\t\t} else {\n\t\t\tp.block(body, 0)\n\t\t}\n\n\tcase *ast.ForStmt:\n\t\tp.print(token.FOR)\n\t\tp.controlClause(true, s.Init, s.Cond, s.Post)\n\t\tp.block(s.Body, 1)\n\n\tcase *ast.RangeStmt:\n\t\tp.print(token.FOR, blank)\n\t\tif s.Key != nil {\n\t\t\tp.expr(s.Key)\n\t\t\tif s.Value != nil {\n\t\t\t\t// use position of value following the comma as\n\t\t\t\t// comma position for correct comment placement\n\t\t\t\tp.print(s.Value.Pos(), token.COMMA, blank)\n\t\t\t\tp.expr(s.Value)\n\t\t\t}\n\t\t\tp.print(blank, s.TokPos, s.Tok, blank)\n\t\t}\n\t\tif !s.NoRangeOp {\n\t\t\tp.print(token.RANGE, blank)\n\t\t}\n\t\tp.expr(stripParens(s.X))\n\t\tp.print(blank)\n\t\tp.block(s.Body, 1)\n\tcase *ast.ForPhraseStmt:\n\t\tp.print(token.FOR, blank)\n\t\tif s.Key != nil {\n\t\t\tp.expr(s.Key)\n\t\t\tp.print(token.COMMA, blank)\n\t\t}\n\t\tp.expr(s.Value)\n\t\tp.print(blank, s.TokPos, in, blank)\n\t\tp.expr(s.X)\n\t\tif s.Cond != nil {\n\t\t\tp.print(blank, s.Cond.Pos(), token.IF, blank)\n\t\t\tp.expr(s.Cond)\n\t\t}\n\t\tp.print(blank)\n\t\tp.block(s.Body, 1)\n\tcase *NewlineStmt:\n\t\tp.print(ignore)\n\tdefault:\n\t\tlog.Printf(\"unreachable %T\\n\", s)\n\t}\n}\n\n// NewlineStmt represents a statement that formats as a newline\ntype NewlineStmt struct {\n\tast.EmptyStmt\n}\n\n// ----------------------------------------------------------------------------\n// Declarations\n\n// The keepTypeColumn function determines if the type column of a series of\n// consecutive const or var declarations must be kept, or if initialization\n// values (V) can be placed in the type column (T) instead. The i'th entry\n// in the result slice is true if the type column in spec[i] must be kept.\n//\n// For example, the declaration:\n//\n//\t\tconst (\n//\t\t\tfoobar int = 42 // comment\n//\t\t\tx          = 7  // comment\n//\t\t\tfoo\n//\t             bar = 991\n//\t\t)\n//\n// leads to the type/values matrix below. A run of value columns (V) can\n// be moved into the type column if there is no type for any of the values\n// in that column (we only move entire columns so that they align properly).\n//\n//\t\tmatrix        formatted     result\n//\t                   matrix\n//\t\tT  V    ->    T  V     ->   true      there is a T and so the type\n//\t\t-  V          -  V          true      column must be kept\n//\t\t-  -          -  -          false\n//\t\t-  V          V  -          false     V is moved into T column\nfunc keepTypeColumn(specs []ast.Spec) []bool {\n\tm := make([]bool, len(specs))\n\n\tpopulate := func(i, j int, keepType bool) {\n\t\tif keepType {\n\t\t\tfor ; i < j; i++ {\n\t\t\t\tm[i] = true\n\t\t\t}\n\t\t}\n\t}\n\n\ti0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run\n\tvar keepType bool\n\tfor i, s := range specs {\n\t\tt := s.(*ast.ValueSpec)\n\t\tif t.Values != nil {\n\t\t\tif i0 < 0 {\n\t\t\t\t// start of a run of ValueSpecs with non-nil Values\n\t\t\t\ti0 = i\n\t\t\t\tkeepType = false\n\t\t\t}\n\t\t} else {\n\t\t\tif i0 >= 0 {\n\t\t\t\t// end of a run\n\t\t\t\tpopulate(i0, i, keepType)\n\t\t\t\ti0 = -1\n\t\t\t}\n\t\t}\n\t\tif t.Type != nil {\n\t\t\tkeepType = true\n\t\t}\n\t}\n\tif i0 >= 0 {\n\t\t// end of a run\n\t\tpopulate(i0, len(specs), keepType)\n\t}\n\n\treturn m\n}\n\nfunc (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) {\n\tp.setComment(s.Doc)\n\tp.identList(s.Names, false) // always present\n\textraTabs := 3\n\tif s.Type != nil || keepType {\n\t\tif len(s.Names) > 0 {\n\t\t\tp.print(vtab)\n\t\t}\n\t\textraTabs--\n\t}\n\tif s.Type != nil {\n\t\tp.expr(s.Type)\n\t}\n\tif s.Tag != nil {\n\t\tif len(s.Names) > 0 {\n\t\t\tp.print(vtab)\n\t\t}\n\t\tp.print(vtab)\n\t\tp.expr(s.Tag)\n\t\textraTabs--\n\t}\n\tif s.Values != nil {\n\t\tp.print(vtab, token.ASSIGN, blank)\n\t\tp.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false)\n\t\textraTabs--\n\t}\n\tif s.Comment != nil {\n\t\tfor ; extraTabs > 0; extraTabs-- {\n\t\t\tp.print(vtab)\n\t\t}\n\t\tp.setComment(s.Comment)\n\t}\n}\n\nfunc sanitizeImportPath(lit *ast.BasicLit) *ast.BasicLit {\n\t// Note: An unmodified AST generated by go/parser will already\n\t// contain a backward- or double-quoted path string that does\n\t// not contain any invalid characters, and most of the work\n\t// here is not needed. However, a modified or generated AST\n\t// may possibly contain non-canonical paths. Do the work in\n\t// all cases since it's not too hard and not speed-critical.\n\n\t// if we don't have a proper string, be conservative and return whatever we have\n\tif lit.Kind != token.STRING {\n\t\treturn lit\n\t}\n\ts, err := strconv.Unquote(lit.Value)\n\tif err != nil {\n\t\treturn lit\n\t}\n\n\t// if the string is an invalid path, return whatever we have\n\t//\n\t// spec: \"Implementation restriction: A compiler may restrict\n\t// ImportPaths to non-empty strings using only characters belonging\n\t// to Unicode's L, M, N, P, and S general categories (the Graphic\n\t// characters without spaces) and may also exclude the characters\n\t// !\"#$%&'()*,:;<=>?[\\]^`{|} and the Unicode replacement character\n\t// U+FFFD.\"\n\tif s == \"\" {\n\t\treturn lit\n\t}\n\tconst illegalChars = `!\"#$%&'()*,:;<=>?[\\]^{|}` + \"`\\uFFFD\"\n\tfor _, r := range s {\n\t\tif !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {\n\t\t\treturn lit\n\t\t}\n\t}\n\n\t// otherwise, return the double-quoted path\n\ts = strconv.Quote(s)\n\tif s == lit.Value {\n\t\treturn lit // nothing wrong with lit\n\t}\n\treturn &ast.BasicLit{ValuePos: lit.ValuePos, Kind: token.STRING, Value: s}\n}\n\n// The parameter n is the number of specs in the group. If doIndent is set,\n// multi-line identifier lists in the spec are indented when the first\n// linebreak is encountered.\nfunc (p *printer) spec(spec ast.Spec, n int, doIndent bool) {\n\tswitch s := spec.(type) {\n\tcase *ast.ImportSpec:\n\t\tp.setComment(s.Doc)\n\t\tif s.Name != nil {\n\t\t\tp.expr(s.Name)\n\t\t\tp.print(blank)\n\t\t}\n\t\tp.expr(sanitizeImportPath(s.Path))\n\t\tp.setComment(s.Comment)\n\t\tp.print(s.EndPos)\n\n\tcase *ast.ValueSpec:\n\t\tif n != 1 {\n\t\t\tp.internalError(\"expected n = 1; got\", n)\n\t\t}\n\t\tp.setComment(s.Doc)\n\t\tp.identList(s.Names, doIndent) // always present\n\t\tif s.Type != nil {\n\t\t\tif len(s.Names) > 0 {\n\t\t\t\tp.print(blank)\n\t\t\t}\n\t\t\tp.expr(s.Type)\n\t\t}\n\t\tif s.Values != nil {\n\t\t\tp.print(blank, token.ASSIGN, blank)\n\t\t\tp.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false)\n\t\t}\n\t\tp.setComment(s.Comment)\n\n\tcase *ast.TypeSpec:\n\t\tp.setComment(s.Doc)\n\t\tp.expr(s.Name)\n\t\tif n == 1 {\n\t\t\tp.print(blank)\n\t\t} else {\n\t\t\tp.print(vtab)\n\t\t}\n\t\tif s.Assign.IsValid() {\n\t\t\tp.print(token.ASSIGN, blank)\n\t\t}\n\t\tp.expr(s.Type)\n\t\tp.setComment(s.Comment)\n\n\tdefault:\n\t\tpanic(\"unreachable\")\n\t}\n}\n\nfunc (p *printer) genDecl(d *ast.GenDecl) {\n\tp.setComment(d.Doc)\n\tp.setPos(d.Pos())\n\tp.print(d.Tok, blank)\n\n\tif d.Lparen.IsValid() || len(d.Specs) > 1 {\n\t\t// group of parenthesized declarations\n\t\tp.setPos(d.Lparen)\n\t\tp.print(token.LPAREN)\n\t\tif n := len(d.Specs); n > 0 {\n\t\t\tp.print(indent, formfeed)\n\t\t\tif n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {\n\t\t\t\t// two or more grouped const/var declarations:\n\t\t\t\t// determine if the type column must be kept\n\t\t\t\tkeepType := keepTypeColumn(d.Specs)\n\t\t\t\tvar line int\n\t\t\t\tfor i, s := range d.Specs {\n\t\t\t\t\tif i > 0 {\n\t\t\t\t\t\tp.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)\n\t\t\t\t\t}\n\t\t\t\t\tp.recordLine(&line)\n\t\t\t\t\tp.valueSpec(s.(*ast.ValueSpec), keepType[i])\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar line int\n\t\t\t\tfor i, s := range d.Specs {\n\t\t\t\t\tif i > 0 {\n\t\t\t\t\t\tp.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)\n\t\t\t\t\t}\n\t\t\t\t\tp.recordLine(&line)\n\t\t\t\t\tp.spec(s, n, false)\n\t\t\t\t}\n\t\t\t}\n\t\t\tp.print(unindent, formfeed)\n\t\t}\n\t\tp.setPos(d.Rparen)\n\t\tp.print(token.RPAREN)\n\n\t} else if len(d.Specs) > 0 {\n\t\t// single declaration\n\t\tp.spec(d.Specs[0], 1, true)\n\t}\n}\n\n// nodeSize determines the size of n in chars after formatting.\n// The result is <= maxSize if the node fits on one line with at\n// most maxSize chars and the formatted output doesn't contain\n// any control chars. Otherwise, the result is > maxSize.\nfunc (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {\n\t// nodeSize invokes the printer, which may invoke nodeSize\n\t// recursively. For deep composite literal nests, this can\n\t// lead to an exponential algorithm. Remember previous\n\t// results to prune the recursion (was issue 1628).\n\tif size, found := p.nodeSizes[n]; found {\n\t\treturn size\n\t}\n\n\tsize = maxSize + 1 // assume n doesn't fit\n\tp.nodeSizes[n] = size\n\n\t// nodeSize computation must be independent of particular\n\t// style so that we always get the same decision; print\n\t// in RawFormat\n\tcfg := Config{Mode: RawFormat}\n\tvar buf bytes.Buffer\n\tif err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {\n\t\treturn\n\t}\n\tif buf.Len() <= maxSize {\n\t\tfor _, ch := range buf.Bytes() {\n\t\t\tif ch < ' ' {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tsize = buf.Len() // n fits\n\t\tp.nodeSizes[n] = size\n\t}\n\treturn\n}\n\n// numLines returns the number of lines spanned by node n in the original source.\nfunc (p *printer) numLines(n ast.Node) int {\n\tif from := n.Pos(); from.IsValid() {\n\t\tif to := n.End(); to.IsValid() {\n\t\t\treturn p.lineFor(to) - p.lineFor(from) + 1\n\t\t}\n\t}\n\treturn infinity\n}\n\n// bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.\nfunc (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {\n\tpos1 := b.Pos()\n\tpos2 := b.Rbrace\n\tif pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {\n\t\t// opening and closing brace are on different lines - don't make it a one-liner\n\t\treturn maxSize + 1\n\t}\n\tif len(b.List) > 5 {\n\t\t// too many statements - don't make it a one-liner\n\t\treturn maxSize + 1\n\t}\n\t// otherwise, estimate body size\n\tbodySize := p.commentSizeBefore(p.posFor(pos2))\n\tfor i, s := range b.List {\n\t\tif bodySize > maxSize {\n\t\t\tbreak // no need to continue\n\t\t}\n\t\tif i > 0 {\n\t\t\tbodySize += 2 // space for a semicolon and blank\n\t\t}\n\t\tbodySize += p.nodeSize(s, maxSize)\n\t}\n\treturn bodySize\n}\n\n// funcBody prints a function body following a function header of given headerSize.\n// If the header's and block's size are \"small enough\" and the block is \"simple enough\",\n// the block is printed on the current line, without line breaks, spaced from the header\n// by sep. Otherwise the block's opening \"{\" is printed on the current line, followed by\n// lines for the block's statements and its closing \"}\".\nfunc (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {\n\tif b == nil {\n\t\treturn\n\t}\n\n\t// save/restore composite literal nesting level\n\tdefer func(level int) {\n\t\tp.level = level\n\t}(p.level)\n\tp.level = 0\n\n\tconst maxSize = 100\n\tif headerSize+p.bodySize(b, maxSize) <= maxSize {\n\t\tp.print(sep, b.Lbrace, token.LBRACE)\n\t\tif len(b.List) > 0 {\n\t\t\tp.print(blank)\n\t\t\tfor i, s := range b.List {\n\t\t\t\tif i > 0 {\n\t\t\t\t\tp.print(token.SEMICOLON, blank)\n\t\t\t\t}\n\t\t\t\tp.stmt(s, i == len(b.List)-1)\n\t\t\t}\n\t\t\tp.print(blank)\n\t\t}\n\t\tp.print(noExtraLinebreak, b.Rbrace, token.RBRACE, noExtraLinebreak)\n\t\treturn\n\t}\n\n\tif sep != ignore {\n\t\tp.print(blank) // always use blank\n\t}\n\tp.block(b, 1)\n}\n\n// funcBodyUnnamed prints a function body following a function header of given headerSize.\n// If the header's and block's size are \"small enough\" and the block is \"simple enough\",\n// the block is printed on the current line, without line breaks, spaced from the header\n// by sep. Otherwise the block's opening \"{\" is printed on the current line, followed by\n// lines for the block's statements and its closing \"}\".\nfunc (p *printer) funcBodyUnnamed(headerSize int, sep whiteSpace, b *ast.BlockStmt) {\n\t_, _ = headerSize, sep\n\tif b == nil {\n\t\treturn\n\t}\n\n\t// save/restore composite literal nesting level\n\tdefer func(level int) {\n\t\tp.level = level\n\t}(p.level)\n\tp.level = 0\n\n\t// const maxSize = 100\n\t// if headerSize+p.bodySize(b, maxSize) <= maxSize {\n\t// \tif len(b.List) > 0 {\n\t// \t\tp.print(blank)\n\t// \t\tfor i, s := range b.List {\n\t// \t\t\tif i > 0 {\n\t// \t\t\t\tp.print(token.SEMICOLON, blank)\n\t// \t\t\t}\n\t// \t\t\tp.stmt(s, i == len(b.List)-1)\n\t// \t\t}\n\t// \t\tp.print(blank)\n\t// \t}\n\t// \treturn\n\t// }\n\n\t/*\tif sep != ignore {\n\t\t\t//\tp.print(blank) // always use blank\n\t\t}\n\t*/\n\tvar line int\n\ti := 0\n\tfor _, s := range b.List {\n\t\t// ignore empty statements (was issue 3466)\n\t\tif _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {\n\t\t\t// nindent == 0 only for lists of switch/select case clauses;\n\t\t\t// in those cases each clause is a new section\n\t\t\tif len(p.output) > 0 && i > 0 {\n\t\t\t\t// only print line break if we are not at the beginning of the output\n\t\t\t\t// (i.e., we are not printing only a partial program)\n\t\t\t\tp.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)\n\t\t\t}\n\t\t\tp.recordLine(&line)\n\t\t\tp.stmt(s, true && i == len(b.List)-1)\n\t\t\t// labeled statements put labels on a separate line, but here\n\t\t\t// we only care about the start line of the actual statement\n\t\t\t// without label - correct line for each label\n\t\t\tfor t := s; ; {\n\t\t\t\tlt, _ := t.(*ast.LabeledStmt)\n\t\t\t\tif lt == nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tline++\n\t\t\t\tt = lt.Stmt\n\t\t\t}\n\t\t\ti++\n\t\t}\n\t}\n}\n\n// distanceFrom returns the column difference between p.out (the current output\n// position) and startOutCol. If the start position is on a different line from\n// the current position (or either is unknown), the result is infinity.\nfunc (p *printer) distanceFrom(startPos token.Pos, startOutCol int) int {\n\tif startPos.IsValid() && p.pos.IsValid() && p.posFor(startPos).Line == p.pos.Line {\n\t\treturn p.out.Column - startOutCol\n\t}\n\treturn infinity\n}\n\nfunc (p *printer) funcDecl(d *ast.FuncDecl) {\n\tif debugFormat {\n\t\tlog.Println(\"==> Format Func\", d.Name.Name)\n\t}\n\tp.setComment(d.Doc)\n\n\tif p.shadowEntry == d {\n\t\tp.funcBodyUnnamed(0, vtab, d.Body)\n\t\treturn\n\t}\n\n\tpos := d.Pos()\n\tp.print(pos, token.FUNC, blank)\n\t// We have to save startCol only after emitting FUNC; otherwise it can be on a\n\t// different line (all whitespace preceding the FUNC is emitted only when the\n\t// FUNC is emitted).\n\tstartCol := p.out.Column - len(\"func \")\n\tif d.Recv != nil {\n\t\tif d.Static { // static method\n\t\t\tif !d.IsClass {\n\t\t\t\tif list := d.Recv.List; len(list) > 0 {\n\t\t\t\t\tp.expr(list[0].Type)\n\t\t\t\t}\n\t\t\t}\n\t\t\tp.print(token.PERIOD)\n\t\t} else if !d.IsClass {\n\t\t\tp.parameters(d.Recv) // method: print receiver\n\t\t\tp.print(blank)\n\t\t}\n\t}\n\tp.expr(d.Name)\n\tif d.Operator && d.Recv != nil {\n\t\tp.print(blank)\n\t}\n\tp.signature(d.Type.Params, d.Type.Results)\n\tp.funcBody(p.distanceFrom(d.Pos(), startCol), vtab, d.Body)\n}\n\nfunc (p *printer) overloadFuncDecl(d *ast.OverloadFuncDecl) {\n\tif debugFormat {\n\t\tlog.Println(\"==> Format OverloadFunc\", d.Name.Name)\n\t}\n\tp.setComment(d.Doc)\n\n\tpos := d.Pos()\n\tp.print(pos, token.FUNC, blank)\n\tif d.Recv != nil && !d.IsClass {\n\t\tp.parameters(d.Recv) // method: print receiver\n\t\tp.print(token.PERIOD)\n\t}\n\tp.expr(d.Name)\n\tp.print(blank, token.ASSIGN, blank, token.LPAREN, newline)\n\tfor _, fn := range d.Funcs {\n\t\tp.print(indent)\n\t\tp.expr1(fn, token.LowestPrec, 1)\n\t\tp.print(unindent, newline)\n\t}\n\tp.print(token.RPAREN)\n}\n\nfunc (p *printer) decl(decl ast.Decl) {\n\tswitch d := decl.(type) {\n\tcase *ast.BadDecl:\n\t\tp.print(d.Pos(), \"BadDecl\")\n\tcase *ast.GenDecl:\n\t\tp.genDecl(d)\n\tcase *ast.FuncDecl:\n\t\tp.funcDecl(d)\n\tcase *ast.OverloadFuncDecl:\n\t\tp.overloadFuncDecl(d)\n\tdefault:\n\t\tpanic(\"unreachable\")\n\t}\n}\n\n// ----------------------------------------------------------------------------\n// Files\n\nfunc declToken(decl ast.Decl) (tok token.Token) {\n\ttok = token.ILLEGAL\n\tswitch d := decl.(type) {\n\tcase *ast.GenDecl:\n\t\ttok = d.Tok\n\tcase *ast.FuncDecl:\n\t\ttok = token.FUNC\n\t}\n\treturn\n}\n\nfunc (p *printer) declList(list []ast.Decl) {\n\ttok := token.ILLEGAL\n\tfor _, d := range list {\n\t\t// skip no entry shadow\n\t\tif decl, ok := d.(*ast.FuncDecl); ok && decl.Shadow && decl != p.shadowEntry {\n\t\t\tcontinue\n\t\t}\n\t\tprev := tok\n\t\ttok = declToken(d)\n\t\t// If the declaration token changed (e.g., from CONST to TYPE)\n\t\t// or the next declaration has documentation associated with it,\n\t\t// print an empty line between top-level declarations.\n\t\t// (because p.linebreak is called with the position of d, which\n\t\t// is past any documentation, the minimum requirement is satisfied\n\t\t// even w/o the extra getDoc(d) nil-check - leave it in case the\n\t\t// linebreak logic improves - there's already a TODO).\n\t\tif len(p.output) > 0 {\n\t\t\t// only print line break if we are not at the beginning of the output\n\t\t\t// (i.e., we are not printing only a partial program)\n\t\t\tmin := 1\n\t\t\tif tok == token.FUNC || tok == token.TYPE || prev != tok || getDoc(d) != nil {\n\t\t\t\tmin = 2\n\t\t\t}\n\t\t\t// start a new section if the next declaration is a function\n\t\t\t// that spans multiple lines (see also issue #19544)\n\t\t\tp.linebreak(p.lineFor(d.Pos()), min, ignore, tok == token.FUNC && p.numLines(d) > 1)\n\t\t}\n\t\tp.decl(d)\n\t}\n}\n\nfunc (p *printer) file(src *ast.File) {\n\tp.shadowEntry = src.ShadowEntry\n\tp.setComment(src.Doc)\n\tif !src.NoPkgDecl {\n\t\tp.print(src.Pos(), token.PACKAGE, blank)\n\t\tp.expr(src.Name)\n\t}\n\tp.declList(src.Decls)\n\tp.print(newline)\n}\n"
  },
  {
    "path": "printer/printer.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package printer implements printing of AST nodes.\npackage printer\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"text/tabwriter\"\n\t\"unicode\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nconst (\n\tmaxNewlines = 2     // max. number of newlines between source text\n\tdebug       = false // enable for debugging\n\tinfinity    = 1 << 30\n)\n\ntype whiteSpace byte\n\nconst (\n\tignore   = whiteSpace(0)\n\tblank    = whiteSpace(' ')\n\tvtab     = whiteSpace('\\v')\n\tnewline  = whiteSpace('\\n')\n\tformfeed = whiteSpace('\\f')\n\tindent   = whiteSpace('>')\n\tunindent = whiteSpace('<')\n)\n\n// A pmode value represents the current printer mode.\ntype pmode int\n\nconst (\n\tnoExtraBlank     pmode = 1 << iota // disables extra blank after /*-style comment\n\tnoExtraLinebreak                   // disables extra line break after /*-style comment\n)\n\ntype commentInfo struct {\n\tcindex         int               // current comment index\n\tcomment        *ast.CommentGroup // = printer.comments[cindex]; or nil\n\tcommentOffset  int               // = printer.posFor(printer.comments[cindex].List[0].Pos()).Offset; or infinity\n\tcommentNewline bool              // true if the comment group contains newlines\n}\n\ntype printer struct {\n\t// Configuration (does not change after initialization)\n\tConfig\n\tfset *token.FileSet\n\n\t// Current state\n\toutput       []byte       // raw printer result\n\tindent       int          // current indentation\n\tlevel        int          // level == 0: outside composite literal; level > 0: inside composite literal\n\tmode         pmode        // current printer mode\n\tendAlignment bool         // if set, terminate alignment immediately\n\timpliedSemi  bool         // if set, a linebreak implies a semicolon\n\tlastTok      token.Token  // last token printed (token.ILLEGAL if it's whitespace)\n\tprevOpen     token.Token  // previous non-brace \"open\" token (, [, or token.ILLEGAL\n\twsbuf        []whiteSpace // delayed white space\n\n\t// Positions\n\t// The out position differs from the pos position when the result\n\t// formatting differs from the source formatting (in the amount of\n\t// white space). If there's a difference and SourcePos is set in\n\t// ConfigMode, //line directives are used in the output to restore\n\t// original source positions for a reader.\n\tpos     token.Position // current position in AST (source) space\n\tout     token.Position // current position in output space\n\tlast    token.Position // value of pos after calling writeString\n\tlinePtr *int           // if set, record out.Line for the next token in *linePtr\n\n\t// The list of all source comments, in order of appearance.\n\tcomments        []*ast.CommentGroup // may be nil\n\tuseNodeComments bool                // if not set, ignore lead and line comments of nodes\n\n\t// Information about p.comments[p.cindex]; set up by nextComment.\n\tcommentInfo\n\n\t// Cache of already computed node sizes.\n\tnodeSizes map[ast.Node]int\n\n\t// Cache of most recently computed line position.\n\tcachedPos  token.Pos\n\tcachedLine int // line corresponding to cachedPos\n\n\tshadowEntry    *ast.FuncDecl                  // ast.File NoEntrypoint\n\tcommentedStmts map[ast.Stmt]*ast.CommentGroup // statements with leading comments (XGo)\n}\n\nfunc (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {\n\tp.Config = *cfg\n\tp.fset = fset\n\tp.pos = token.Position{Line: 1, Column: 1}\n\tp.out = token.Position{Line: 1, Column: 1}\n\tp.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short\n\tp.nodeSizes = nodeSizes\n\tp.cachedPos = -1\n}\n\nfunc (p *printer) internalError(msg ...any) {\n\tif debug {\n\t\tfmt.Print(p.pos.String() + \": \")\n\t\tfmt.Println(msg...)\n\t\tpanic(\"xgo/printer\")\n\t}\n}\n\n// commentsHaveNewline reports whether a list of comments belonging to\n// an *ast.CommentGroup contains newlines. Because the position information\n// may only be partially correct, we also have to read the comment text.\nfunc (p *printer) commentsHaveNewline(list []*ast.Comment) bool {\n\t// len(list) > 0\n\tline := p.lineFor(list[0].Pos())\n\tfor i, c := range list {\n\t\tif i > 0 && p.lineFor(list[i].Pos()) != line {\n\t\t\t// not all comments on the same line\n\t\t\treturn true\n\t\t}\n\t\tif t := c.Text; len(t) >= 2 && (t[1] == '/' || strings.Contains(t, \"\\n\")) {\n\t\t\treturn true\n\t\t}\n\t}\n\t_ = line\n\treturn false\n}\n\nfunc (p *printer) nextComment() {\n\tfor p.cindex < len(p.comments) {\n\t\tc := p.comments[p.cindex]\n\t\tp.cindex++\n\t\tif list := c.List; len(list) > 0 {\n\t\t\tp.comment = c\n\t\t\tp.commentOffset = p.posFor(list[0].Pos()).Offset\n\t\t\tp.commentNewline = p.commentsHaveNewline(list)\n\t\t\treturn\n\t\t}\n\t\t// we should not reach here (correct ASTs don't have empty\n\t\t// ast.CommentGroup nodes), but be conservative and try again\n\t}\n\t// no more comments\n\tp.commentOffset = infinity\n}\n\n// commentBefore reports whether the current comment group occurs\n// before the next position in the source code and printing it does\n// not introduce implicit semicolons.\nfunc (p *printer) commentBefore(next token.Position) bool {\n\treturn p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline)\n}\n\n// commentSizeBefore returns the estimated size of the\n// comments on the same line before the next position.\nfunc (p *printer) commentSizeBefore(next token.Position) int {\n\t// save/restore current p.commentInfo (p.nextComment() modifies it)\n\tdefer func(info commentInfo) {\n\t\tp.commentInfo = info\n\t}(p.commentInfo)\n\n\tsize := 0\n\tfor p.commentBefore(next) {\n\t\tfor _, c := range p.comment.List {\n\t\t\tsize += len(c.Text)\n\t\t}\n\t\tp.nextComment()\n\t}\n\treturn size\n}\n\n// recordLine records the output line number for the next non-whitespace\n// token in *linePtr. It is used to compute an accurate line number for a\n// formatted construct, independent of pending (not yet emitted) whitespace\n// or comments.\nfunc (p *printer) recordLine(linePtr *int) {\n\tp.linePtr = linePtr\n}\n\n// linesFrom returns the number of output lines between the current\n// output line and the line argument, ignoring any pending (not yet\n// emitted) whitespace or comments. It is used to compute an accurate\n// size (in number of lines) for a formatted construct.\nfunc (p *printer) linesFrom(line int) int {\n\treturn p.out.Line - line\n}\n\nfunc (p *printer) posFor(pos token.Pos) token.Position {\n\t// not used frequently enough to cache entire token.Position\n\treturn p.fset.PositionFor(pos, false /* absolute position */)\n}\n\nfunc (p *printer) lineFor(pos token.Pos) int {\n\tif pos != p.cachedPos {\n\t\tp.cachedPos = pos\n\t\tp.cachedLine = p.fset.PositionFor(pos, false /* absolute position */).Line\n\t}\n\treturn p.cachedLine\n}\n\n// writeLineDirective writes a //line directive if necessary.\nfunc (p *printer) writeLineDirective(pos token.Position) {\n\tif pos.IsValid() && (p.out.Line != pos.Line || p.out.Filename != pos.Filename) {\n\t\tp.output = append(p.output, tabwriter.Escape) // protect '\\n' in //line from tabwriter interpretation\n\t\tp.output = append(p.output, fmt.Sprintf(\"//line %s:%d\\n\", pos.Filename, pos.Line)...)\n\t\tp.output = append(p.output, tabwriter.Escape)\n\t\t// p.out must match the //line directive\n\t\tp.out.Filename = pos.Filename\n\t\tp.out.Line = pos.Line\n\t}\n}\n\n// writeIndent writes indentation.\nfunc (p *printer) writeIndent() {\n\t// use \"hard\" htabs - indentation columns\n\t// must not be discarded by the tabwriter\n\tn := p.Config.Indent + p.indent // include base indentation\n\tfor i := 0; i < n; i++ {\n\t\tp.output = append(p.output, '\\t')\n\t}\n\n\t// update positions\n\tp.pos.Offset += n\n\tp.pos.Column += n\n\tp.out.Column += n\n}\n\n// writeByte writes ch n times to p.output and updates p.pos.\n// Only used to write formatting (white space) characters.\nfunc (p *printer) writeByte(ch byte, n int) {\n\tif p.endAlignment {\n\t\t// Ignore any alignment control character;\n\t\t// and at the end of the line, break with\n\t\t// a formfeed to indicate termination of\n\t\t// existing columns.\n\t\tswitch ch {\n\t\tcase '\\t', '\\v':\n\t\t\tch = ' '\n\t\tcase '\\n', '\\f':\n\t\t\tch = '\\f'\n\t\t\tp.endAlignment = false\n\t\t}\n\t}\n\n\tif p.out.Column == 1 {\n\t\t// no need to write line directives before white space\n\t\tp.writeIndent()\n\t}\n\n\tfor i := 0; i < n; i++ {\n\t\tp.output = append(p.output, ch)\n\t}\n\n\t// update positions\n\tp.pos.Offset += n\n\tif ch == '\\n' || ch == '\\f' {\n\t\tp.pos.Line += n\n\t\tp.out.Line += n\n\t\tp.pos.Column = 1\n\t\tp.out.Column = 1\n\t\treturn\n\t}\n\tp.pos.Column += n\n\tp.out.Column += n\n}\n\n// writeString writes the string s to p.output and updates p.pos, p.out,\n// and p.last. If isLit is set, s is escaped w/ tabwriter.Escape characters\n// to protect s from being interpreted by the tabwriter.\n//\n// Note: writeString is only used to write Go tokens, literals, and\n// comments, all of which must be written literally. Thus, it is correct\n// to always set isLit = true. However, setting it explicitly only when\n// needed (i.e., when we don't know that s contains no tabs or line breaks)\n// avoids processing extra escape characters and reduces run time of the\n// printer benchmark by up to 10%.\nfunc (p *printer) writeString(pos token.Position, s string, isLit bool) {\n\tif p.out.Column == 1 {\n\t\tif p.Config.Mode&SourcePos != 0 {\n\t\t\tp.writeLineDirective(pos)\n\t\t}\n\t\tp.writeIndent()\n\t}\n\n\tif pos.IsValid() {\n\t\t// update p.pos (if pos is invalid, continue with existing p.pos)\n\t\t// Note: Must do this after handling line beginnings because\n\t\t// writeIndent updates p.pos if there's indentation, but p.pos\n\t\t// is the position of s.\n\t\tp.pos = pos\n\t}\n\n\tif isLit {\n\t\t// Protect s such that is passes through the tabwriter\n\t\t// unchanged. Note that valid Go programs cannot contain\n\t\t// tabwriter.Escape bytes since they do not appear in legal\n\t\t// UTF-8 sequences.\n\t\tp.output = append(p.output, tabwriter.Escape)\n\t\tswitch p.lastTok {\n\t\tcase token.CSTRING:\n\t\t\tp.output = append(p.output, 'c')\n\t\tcase token.PYSTRING:\n\t\t\tp.output = append(p.output, 'p', 'y')\n\t\t}\n\t}\n\n\tif debug {\n\t\tp.output = append(p.output, fmt.Sprintf(\"/*%s*/\", pos)...) // do not update p.pos!\n\t}\n\tp.output = append(p.output, s...)\n\n\t// update positions\n\tnlines := 0\n\tvar li int // index of last newline; valid if nlines > 0\n\tfor i := 0; i < len(s); i++ {\n\t\t// Raw string literals may contain any character except back quote (`).\n\t\tif ch := s[i]; ch == '\\n' || ch == '\\f' {\n\t\t\t// account for line break\n\t\t\tnlines++\n\t\t\tli = i\n\t\t\t// A line break inside a literal will break whatever column\n\t\t\t// formatting is in place; ignore any further alignment through\n\t\t\t// the end of the line.\n\t\t\tp.endAlignment = true\n\t\t}\n\t}\n\tp.pos.Offset += len(s)\n\tif nlines > 0 {\n\t\tp.pos.Line += nlines\n\t\tp.out.Line += nlines\n\t\tc := len(s) - li\n\t\tp.pos.Column = c\n\t\tp.out.Column = c\n\t} else {\n\t\tp.pos.Column += len(s)\n\t\tp.out.Column += len(s)\n\t}\n\n\tif isLit {\n\t\tp.output = append(p.output, tabwriter.Escape)\n\t}\n\n\tp.last = p.pos\n}\n\n// writeCommentPrefix writes the whitespace before a comment.\n// If there is any pending whitespace, it consumes as much of\n// it as is likely to help position the comment nicely.\n// pos is the comment position, next the position of the item\n// after all pending comments, prev is the previous comment in\n// a group of comments (or nil), and tok is the next token.\nfunc (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment, tok token.Token) {\n\tif len(p.output) == 0 {\n\t\t// the comment is the first item to be printed - don't write any whitespace\n\t\treturn\n\t}\n\n\tif pos.IsValid() && pos.Filename != p.last.Filename {\n\t\t// comment in a different file - separate with newlines\n\t\tp.writeByte('\\f', maxNewlines)\n\t\treturn\n\t}\n\n\tif pos.Line == p.last.Line && (prev == nil || prev.Text[1] != '/') {\n\t\t// comment on the same line as last item:\n\t\t// separate with at least one separator\n\t\thasSep := false\n\t\tif prev == nil {\n\t\t\t// first comment of a comment group\n\t\t\tj := 0\n\t\t\tfor i, ch := range p.wsbuf {\n\t\t\t\tswitch ch {\n\t\t\t\tcase blank:\n\t\t\t\t\t// ignore any blanks before a comment\n\t\t\t\t\tp.wsbuf[i] = ignore\n\t\t\t\t\tcontinue\n\t\t\t\tcase vtab:\n\t\t\t\t\t// respect existing tabs - important\n\t\t\t\t\t// for proper formatting of commented structs\n\t\t\t\t\thasSep = true\n\t\t\t\t\tcontinue\n\t\t\t\tcase indent:\n\t\t\t\t\t// apply pending indentation\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tj = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tp.writeWhitespace(j)\n\t\t}\n\t\t// make sure there is at least one separator\n\t\tif !hasSep {\n\t\t\tsep := byte('\\t')\n\t\t\tif pos.Line == next.Line {\n\t\t\t\t// next item is on the same line as the comment\n\t\t\t\t// (which must be a /*-style comment): separate\n\t\t\t\t// with a blank instead of a tab\n\t\t\t\tsep = ' '\n\t\t\t}\n\t\t\tp.writeByte(sep, 1)\n\t\t}\n\n\t} else {\n\t\t// comment on a different line:\n\t\t// separate with at least one line break\n\t\tdroppedLinebreak := false\n\t\tj := 0\n\t\tfor i, ch := range p.wsbuf {\n\t\t\tswitch ch {\n\t\t\tcase blank, vtab:\n\t\t\t\t// ignore any horizontal whitespace before line breaks\n\t\t\t\tp.wsbuf[i] = ignore\n\t\t\t\tcontinue\n\t\t\tcase indent:\n\t\t\t\t// apply pending indentation\n\t\t\t\tcontinue\n\t\t\tcase unindent:\n\t\t\t\t// if this is not the last unindent, apply it\n\t\t\t\t// as it is (likely) belonging to the last\n\t\t\t\t// construct (e.g., a multi-line expression list)\n\t\t\t\t// and is not part of closing a block\n\t\t\t\tif i+1 < len(p.wsbuf) && p.wsbuf[i+1] == unindent {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// if the next token is not a closing }, apply the unindent\n\t\t\t\t// if it appears that the comment is aligned with the\n\t\t\t\t// token; otherwise assume the unindent is part of a\n\t\t\t\t// closing block and stop (this scenario appears with\n\t\t\t\t// comments before a case label where the comments\n\t\t\t\t// apply to the next case instead of the current one)\n\t\t\t\tif tok != token.RBRACE && pos.Column == next.Column {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\tcase newline, formfeed:\n\t\t\t\tp.wsbuf[i] = ignore\n\t\t\t\tdroppedLinebreak = prev == nil // record only if first comment of a group\n\t\t\t}\n\t\t\tj = i\n\t\t\tbreak\n\t\t}\n\t\tp.writeWhitespace(j)\n\n\t\t// determine number of linebreaks before the comment\n\t\tn := 0\n\t\tif pos.IsValid() && p.last.IsValid() {\n\t\t\tn = pos.Line - p.last.Line\n\t\t\tif n < 0 { // should never happen\n\t\t\t\tn = 0\n\t\t\t}\n\t\t}\n\n\t\t// at the package scope level only (p.indent == 0),\n\t\t// add an extra newline if we dropped one before:\n\t\t// this preserves a blank line before documentation\n\t\t// comments at the package scope level (issue 2570)\n\t\tif p.indent == 0 && droppedLinebreak {\n\t\t\tn++\n\t\t}\n\n\t\t// make sure there is at least one line break\n\t\t// if the previous comment was a line comment\n\t\tif n == 0 && prev != nil && prev.Text[1] == '/' {\n\t\t\tn = 1\n\t\t}\n\n\t\tif n > 0 {\n\t\t\t// use formfeeds to break columns before a comment;\n\t\t\t// this is analogous to using formfeeds to separate\n\t\t\t// individual lines of /*-style comments\n\t\t\tp.writeByte('\\f', nlimit(n))\n\t\t}\n\t}\n}\n\n// Returns true if s contains only white space\n// (only tabs and blanks can appear in the printer's context).\nfunc isBlank(s string) bool {\n\tfor i := 0; i < len(s); i++ {\n\t\tif s[i] > ' ' {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// commonPrefix returns the common prefix of a and b.\nfunc commonPrefix(a, b string) string {\n\ti := 0\n\tfor i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') {\n\t\ti++\n\t}\n\treturn a[0:i]\n}\n\n// trimRight returns s with trailing whitespace removed.\nfunc trimRight(s string) string {\n\treturn strings.TrimRightFunc(s, unicode.IsSpace)\n}\n\n// stripCommonPrefix removes a common prefix from /*-style comment lines (unless no\n// comment line is indented, all but the first line have some form of space prefix).\n// The prefix is computed using heuristics such that is likely that the comment\n// contents are nicely laid out after re-printing each line using the printer's\n// current indentation.\nfunc stripCommonPrefix(lines []string) {\n\tif len(lines) <= 1 {\n\t\treturn // at most one line - nothing to do\n\t}\n\t// len(lines) > 1\n\n\t// The heuristic in this function tries to handle a few\n\t// common patterns of /*-style comments: Comments where\n\t// the opening /* and closing */ are aligned and the\n\t// rest of the comment text is aligned and indented with\n\t// blanks or tabs, cases with a vertical \"line of stars\"\n\t// on the left, and cases where the closing */ is on the\n\t// same line as the last comment text.\n\n\t// Compute maximum common white prefix of all but the first,\n\t// last, and blank lines, and replace blank lines with empty\n\t// lines (the first line starts with /* and has no prefix).\n\t// In cases where only the first and last lines are not blank,\n\t// such as two-line comments, or comments where all inner lines\n\t// are blank, consider the last line for the prefix computation\n\t// since otherwise the prefix would be empty.\n\t//\n\t// Note that the first and last line are never empty (they\n\t// contain the opening /* and closing */ respectively) and\n\t// thus they can be ignored by the blank line check.\n\tprefix := \"\"\n\tprefixSet := false\n\tif len(lines) > 2 {\n\t\tfor i, line := range lines[1 : len(lines)-1] {\n\t\t\tif isBlank(line) {\n\t\t\t\tlines[1+i] = \"\" // range starts with lines[1]\n\t\t\t} else {\n\t\t\t\tif !prefixSet {\n\t\t\t\t\tprefix = line\n\t\t\t\t\tprefixSet = true\n\t\t\t\t}\n\t\t\t\tprefix = commonPrefix(prefix, line)\n\t\t\t}\n\n\t\t}\n\t}\n\t// If we don't have a prefix yet, consider the last line.\n\tif !prefixSet {\n\t\tline := lines[len(lines)-1]\n\t\tprefix = commonPrefix(line, line)\n\t}\n\n\t/*\n\t * Check for vertical \"line of stars\" and correct prefix accordingly.\n\t */\n\tlineOfStars := false\n\tif i := strings.Index(prefix, \"*\"); i >= 0 {\n\t\t// Line of stars present.\n\t\tif i > 0 && prefix[i-1] == ' ' {\n\t\t\ti-- // remove trailing blank from prefix so stars remain aligned\n\t\t}\n\t\tprefix = prefix[0:i]\n\t\tlineOfStars = true\n\t} else {\n\t\t// No line of stars present.\n\t\t// Determine the white space on the first line after the /*\n\t\t// and before the beginning of the comment text, assume two\n\t\t// blanks instead of the /* unless the first character after\n\t\t// the /* is a tab. If the first comment line is empty but\n\t\t// for the opening /*, assume up to 3 blanks or a tab. This\n\t\t// whitespace may be found as suffix in the common prefix.\n\t\tfirst := lines[0]\n\t\tif isBlank(first[2:]) {\n\t\t\t// no comment text on the first line:\n\t\t\t// reduce prefix by up to 3 blanks or a tab\n\t\t\t// if present - this keeps comment text indented\n\t\t\t// relative to the /* and */'s if it was indented\n\t\t\t// in the first place\n\t\t\ti := len(prefix)\n\t\t\tfor n := 0; n < 3 && i > 0 && prefix[i-1] == ' '; n++ {\n\t\t\t\ti--\n\t\t\t}\n\t\t\tif i == len(prefix) && i > 0 && prefix[i-1] == '\\t' {\n\t\t\t\ti--\n\t\t\t}\n\t\t\tprefix = prefix[0:i]\n\t\t} else {\n\t\t\t// comment text on the first line\n\t\t\tsuffix := make([]byte, len(first))\n\t\t\tn := 2 // start after opening /*\n\t\t\tfor n < len(first) && first[n] <= ' ' {\n\t\t\t\tsuffix[n] = first[n]\n\t\t\t\tn++\n\t\t\t}\n\t\t\tif n > 2 && suffix[2] == '\\t' {\n\t\t\t\t// assume the '\\t' compensates for the /*\n\t\t\t\tsuffix = suffix[2:n]\n\t\t\t} else {\n\t\t\t\t// otherwise assume two blanks\n\t\t\t\tsuffix[0], suffix[1] = ' ', ' '\n\t\t\t\tsuffix = suffix[0:n]\n\t\t\t}\n\t\t\t// Shorten the computed common prefix by the length of\n\t\t\t// suffix, if it is found as suffix of the prefix.\n\t\t\tprefix = strings.TrimSuffix(prefix, string(suffix))\n\t\t}\n\t}\n\n\t// Handle last line: If it only contains a closing */, align it\n\t// with the opening /*, otherwise align the text with the other\n\t// lines.\n\tlast := lines[len(lines)-1]\n\tclosing := \"*/\"\n\ti := strings.Index(last, closing) // i >= 0 (closing is always present)\n\tif isBlank(last[0:i]) {\n\t\t// last line only contains closing */\n\t\tif lineOfStars {\n\t\t\tclosing = \" */\" // add blank to align final star\n\t\t}\n\t\tlines[len(lines)-1] = prefix + closing\n\t} else {\n\t\t// last line contains more comment text - assume\n\t\t// it is aligned like the other lines and include\n\t\t// in prefix computation\n\t\tprefix = commonPrefix(prefix, last)\n\t}\n\n\t// Remove the common prefix from all but the first and empty lines.\n\tfor i, line := range lines {\n\t\tif i > 0 && line != \"\" {\n\t\t\tlines[i] = line[len(prefix):]\n\t\t}\n\t}\n}\n\nfunc (p *printer) writeComment(comment *ast.Comment) {\n\ttext := comment.Text\n\tpos := p.posFor(comment.Pos())\n\n\tconst linePrefix = \"//line \"\n\tif strings.HasPrefix(text, linePrefix) && (!pos.IsValid() || pos.Column == 1) {\n\t\t// Possibly a //-style line directive.\n\t\t// Suspend indentation temporarily to keep line directive valid.\n\t\tdefer func(indent int) { p.indent = indent }(p.indent)\n\t\tp.indent = 0\n\t}\n\n\t// shortcut common case of //-style comments\n\tif text[1] == '/' {\n\t\tp.writeString(pos, trimRight(text), true)\n\t\treturn\n\t}\n\n\t// for /*-style comments, print line by line and let the\n\t// write function take care of the proper indentation\n\tlines := strings.Split(text, \"\\n\")\n\n\t// The comment started in the first column but is going\n\t// to be indented. For an idempotent result, add indentation\n\t// to all lines such that they look like they were indented\n\t// before - this will make sure the common prefix computation\n\t// is the same independent of how many times formatting is\n\t// applied (was issue 1835).\n\tif pos.IsValid() && pos.Column == 1 && p.indent > 0 {\n\t\tfor i, line := range lines[1:] {\n\t\t\tlines[1+i] = \"   \" + line\n\t\t}\n\t}\n\n\tstripCommonPrefix(lines)\n\n\t// write comment lines, separated by formfeed,\n\t// without a line break after the last line\n\tfor i, line := range lines {\n\t\tif i > 0 {\n\t\t\tp.writeByte('\\f', 1)\n\t\t\tpos = p.pos\n\t\t}\n\t\tif len(line) > 0 {\n\t\t\tp.writeString(pos, trimRight(line), true)\n\t\t}\n\t}\n}\n\n// writeCommentSuffix writes a line break after a comment if indicated\n// and processes any leftover indentation information. If a line break\n// is needed, the kind of break (newline vs formfeed) depends on the\n// pending whitespace. The writeCommentSuffix result indicates if a\n// newline was written or if a formfeed was dropped from the whitespace\n// buffer.\nfunc (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, droppedFF bool) {\n\tfor i, ch := range p.wsbuf {\n\t\tswitch ch {\n\t\tcase blank, vtab:\n\t\t\t// ignore trailing whitespace\n\t\t\tp.wsbuf[i] = ignore\n\t\tcase indent, unindent:\n\t\t\t// don't lose indentation information\n\t\tcase newline, formfeed:\n\t\t\t// if we need a line break, keep exactly one\n\t\t\t// but remember if we dropped any formfeeds\n\t\t\tif needsLinebreak {\n\t\t\t\tneedsLinebreak = false\n\t\t\t\twroteNewline = true\n\t\t\t} else {\n\t\t\t\tif ch == formfeed {\n\t\t\t\t\tdroppedFF = true\n\t\t\t\t}\n\t\t\t\tp.wsbuf[i] = ignore\n\t\t\t}\n\t\t}\n\t}\n\tp.writeWhitespace(len(p.wsbuf))\n\n\t// make sure we have a line break\n\tif needsLinebreak {\n\t\tp.writeByte('\\n', 1)\n\t\twroteNewline = true\n\t}\n\n\treturn\n}\n\n// containsLinebreak reports whether the whitespace buffer contains any line breaks.\nfunc (p *printer) containsLinebreak() bool {\n\tfor _, ch := range p.wsbuf {\n\t\tif ch == newline || ch == formfeed {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// intersperseComments consumes all comments that appear before the next token\n// tok and prints it together with the buffered whitespace (i.e., the whitespace\n// that needs to be written before the next token). A heuristic is used to mix\n// the comments and whitespace. The intersperseComments result indicates if a\n// newline was written or if a formfeed was dropped from the whitespace buffer.\nfunc (p *printer) intersperseComments(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {\n\tvar last *ast.Comment\n\tfor p.commentBefore(next) {\n\t\tfor _, c := range p.comment.List {\n\t\t\tp.writeCommentPrefix(p.posFor(c.Pos()), next, last, tok)\n\t\t\tp.writeComment(c)\n\t\t\tlast = c\n\t\t}\n\t\tp.nextComment()\n\t}\n\n\tif last != nil {\n\t\t// If the last comment is a /*-style comment and the next item\n\t\t// follows on the same line but is not a comma, and not a \"closing\"\n\t\t// token immediately following its corresponding \"opening\" token,\n\t\t// add an extra separator unless explicitly disabled. Use a blank\n\t\t// as separator unless we have pending linebreaks, they are not\n\t\t// disabled, and we are outside a composite literal, in which case\n\t\t// we want a linebreak (issue 15137).\n\t\t// TODO(gri) This has become overly complicated. We should be able\n\t\t// to track whether we're inside an expression or statement and\n\t\t// use that information to decide more directly.\n\t\tneedsLinebreak := false\n\t\tif p.mode&noExtraBlank == 0 &&\n\t\t\tlast.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&\n\t\t\ttok != token.COMMA &&\n\t\t\t(tok != token.RPAREN || p.prevOpen == token.LPAREN) &&\n\t\t\t(tok != token.RBRACK || p.prevOpen == token.LBRACK) {\n\t\t\tif p.containsLinebreak() && p.mode&noExtraLinebreak == 0 && p.level == 0 {\n\t\t\t\tneedsLinebreak = true\n\t\t\t} else {\n\t\t\t\tp.writeByte(' ', 1)\n\t\t\t}\n\t\t}\n\t\t// Ensure that there is a line break after a //-style comment,\n\t\t// before EOF, and before a closing '}' unless explicitly disabled.\n\t\tif last.Text[1] == '/' ||\n\t\t\ttok == token.EOF ||\n\t\t\ttok == token.RBRACE && p.mode&noExtraLinebreak == 0 {\n\t\t\tneedsLinebreak = true\n\t\t}\n\t\treturn p.writeCommentSuffix(needsLinebreak)\n\t}\n\n\t// no comment was written - we should never reach here since\n\t// intersperseComments should not be called in that case\n\tp.internalError(\"intersperseComments called without pending comments\")\n\treturn\n}\n\n// writeWhitespace writes the first n whitespace entries.\nfunc (p *printer) writeWhitespace(n int) {\n\t// write entries\n\tfor i := 0; i < n; i++ {\n\t\tswitch ch := p.wsbuf[i]; ch {\n\t\tcase ignore:\n\t\t\t// ignore!\n\t\tcase indent:\n\t\t\tp.indent++\n\t\tcase unindent:\n\t\t\tp.indent--\n\t\t\tif p.indent < 0 {\n\t\t\t\tp.internalError(\"negative indentation:\", p.indent)\n\t\t\t\tp.indent = 0\n\t\t\t}\n\t\tcase newline, formfeed:\n\t\t\t// A line break immediately followed by a \"correcting\"\n\t\t\t// unindent is swapped with the unindent - this permits\n\t\t\t// proper label positioning. If a comment is between\n\t\t\t// the line break and the label, the unindent is not\n\t\t\t// part of the comment whitespace prefix and the comment\n\t\t\t// will be positioned correctly indented.\n\t\t\tif i+1 < n && p.wsbuf[i+1] == unindent {\n\t\t\t\t// Use a formfeed to terminate the current section.\n\t\t\t\t// Otherwise, a long label name on the next line leading\n\t\t\t\t// to a wide column may increase the indentation column\n\t\t\t\t// of lines before the label; effectively leading to wrong\n\t\t\t\t// indentation.\n\t\t\t\tp.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed\n\t\t\t\ti-- // do it again\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfallthrough\n\t\tdefault:\n\t\t\tp.writeByte(byte(ch), 1)\n\t\t}\n\t}\n\n\t// shift remaining entries down\n\tl := copy(p.wsbuf, p.wsbuf[n:])\n\tp.wsbuf = p.wsbuf[:l]\n}\n\n// ----------------------------------------------------------------------------\n// Printing interface\n\n// nlimit limits n to maxNewlines.\nfunc nlimit(n int) int {\n\tif n > maxNewlines {\n\t\tn = maxNewlines\n\t}\n\treturn n\n}\n\nfunc mayCombine(prev token.Token, next byte) (b bool) {\n\tswitch prev {\n\tcase token.INT:\n\t\tb = next == '.' // 1.\n\tcase token.ADD:\n\t\tb = next == '+' // ++\n\tcase token.SUB:\n\t\tb = next == '-' // --\n\tcase token.QUO:\n\t\tb = next == '*' // /*\n\tcase token.LSS:\n\t\tb = next == '-' || next == '<' // <- or <<\n\tcase token.AND:\n\t\tb = next == '&' || next == '^' // && or &^\n\t}\n\treturn\n}\n\nfunc (p *printer) setPos(pos token.Pos) {\n\tif pos.IsValid() {\n\t\tp.pos = p.posFor(pos) // accurate position of next item\n\t}\n}\n\n// print prints a list of \"items\" (roughly corresponding to syntactic\n// tokens, but also including whitespace and formatting information).\n// It is the only print function that should be called directly from\n// any of the AST printing functions in nodes.go.\n//\n// Whitespace is accumulated until a non-whitespace token appears. Any\n// comments that need to appear before that token are printed first,\n// taking into account the amount and structure of any pending white-\n// space for best comment placement. Then, any leftover whitespace is\n// printed, followed by the actual token.\nfunc (p *printer) print(args ...any) {\n\tfor _, arg := range args {\n\t\t// information about the current arg\n\t\tvar data string\n\t\tvar isLit bool\n\t\tvar impliedSemi bool // value for p.impliedSemi after this arg\n\n\t\t// record previous opening token, if any\n\t\tswitch p.lastTok {\n\t\tcase token.ILLEGAL:\n\t\t\t// ignore (white space)\n\t\tcase token.LPAREN, token.LBRACK:\n\t\t\tp.prevOpen = p.lastTok\n\t\tdefault:\n\t\t\t// other tokens followed any opening token\n\t\t\tp.prevOpen = token.ILLEGAL\n\t\t}\n\n\t\tswitch x := arg.(type) {\n\t\tcase pmode:\n\t\t\t// toggle printer mode\n\t\t\tp.mode ^= x\n\t\t\tcontinue\n\n\t\tcase whiteSpace:\n\t\t\tif x == ignore {\n\t\t\t\t// don't add ignore's to the buffer; they\n\t\t\t\t// may screw up \"correcting\" unindents (see\n\t\t\t\t// LabeledStmt)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ti := len(p.wsbuf)\n\t\t\tif i == cap(p.wsbuf) {\n\t\t\t\t// Whitespace sequences are very short so this should\n\t\t\t\t// never happen. Handle gracefully (but possibly with\n\t\t\t\t// bad comment placement) if it does happen.\n\t\t\t\tp.writeWhitespace(i)\n\t\t\t\ti = 0\n\t\t\t}\n\t\t\tp.wsbuf = p.wsbuf[0 : i+1]\n\t\t\tp.wsbuf[i] = x\n\t\t\tif x == newline || x == formfeed {\n\t\t\t\t// newlines affect the current state (p.impliedSemi)\n\t\t\t\t// and not the state after printing arg (impliedSemi)\n\t\t\t\t// because comments can be interspersed before the arg\n\t\t\t\t// in this case\n\t\t\t\tp.impliedSemi = false\n\t\t\t}\n\t\t\tp.lastTok = token.ILLEGAL\n\t\t\tcontinue\n\n\t\tcase *ast.Ident:\n\t\t\tdata = x.Name\n\t\t\timpliedSemi = true\n\t\t\tp.lastTok = token.IDENT\n\n\t\tcase *ast.BasicLit:\n\t\t\tdata = x.Value\n\t\t\tisLit = true\n\t\t\timpliedSemi = true\n\t\t\tp.lastTok = x.Kind\n\n\t\tcase token.Token:\n\t\t\ts := x.String()\n\t\t\tif mayCombine(p.lastTok, s[0]) {\n\t\t\t\t// the previous and the current token must be\n\t\t\t\t// separated by a blank otherwise they combine\n\t\t\t\t// into a different incorrect token sequence\n\t\t\t\t// (except for token.INT followed by a '.' this\n\t\t\t\t// should never happen because it is taken care\n\t\t\t\t// of via binary expression formatting)\n\t\t\t\tif len(p.wsbuf) != 0 {\n\t\t\t\t\tp.internalError(\"whitespace buffer not empty\")\n\t\t\t\t}\n\t\t\t\tp.wsbuf = p.wsbuf[0:1]\n\t\t\t\tp.wsbuf[0] = ' '\n\t\t\t}\n\t\t\tdata = s\n\t\t\t// some keywords followed by a newline imply a semicolon\n\t\t\tswitch x {\n\t\t\tcase token.BREAK, token.CONTINUE, token.FALLTHROUGH, token.RETURN,\n\t\t\t\ttoken.INC, token.DEC, token.RPAREN, token.RBRACK, token.RBRACE:\n\t\t\t\timpliedSemi = true\n\t\t\t}\n\t\t\tp.lastTok = x\n\n\t\tcase token.Pos:\n\t\t\tif x.IsValid() {\n\t\t\t\tp.pos = p.posFor(x) // accurate position of next item\n\t\t\t}\n\t\t\tcontinue\n\n\t\tcase string:\n\t\t\t// incorrect AST - print error message\n\t\t\tdata = x\n\t\t\tisLit = true\n\t\t\timpliedSemi = true\n\t\t\tp.lastTok = token.STRING\n\n\t\tdefault:\n\t\t\tfmt.Fprintf(os.Stderr, \"print: unsupported argument %v (%T)\\n\", arg, arg)\n\t\t\tpanic(\"xgo/printer type\")\n\t\t}\n\t\t// data != \"\"\n\n\t\tnext := p.pos // estimated/accurate position of next item\n\t\twroteNewline, droppedFF := p.flush(next, p.lastTok)\n\n\t\t// intersperse extra newlines if present in the source and\n\t\t// if they don't cause extra semicolons (don't do this in\n\t\t// flush as it will cause extra newlines at the end of a file)\n\t\tif !p.impliedSemi {\n\t\t\tn := nlimit(next.Line - p.pos.Line)\n\t\t\t// don't exceed maxNewlines if we already wrote one\n\t\t\tif wroteNewline && n == maxNewlines {\n\t\t\t\tn = maxNewlines - 1\n\t\t\t}\n\t\t\tif n > 0 {\n\t\t\t\tch := byte('\\n')\n\t\t\t\tif droppedFF {\n\t\t\t\t\tch = '\\f' // use formfeed since we dropped one before\n\t\t\t\t}\n\t\t\t\tp.writeByte(ch, n)\n\t\t\t\timpliedSemi = false\n\t\t\t}\n\t\t}\n\n\t\t// the next token starts now - record its line number if requested\n\t\tif p.linePtr != nil {\n\t\t\t*p.linePtr = p.out.Line\n\t\t\tp.linePtr = nil\n\t\t}\n\n\t\tp.writeString(next, data, isLit)\n\t\tp.impliedSemi = impliedSemi\n\t}\n}\n\n// flush prints any pending comments and whitespace occurring textually\n// before the position of the next token tok. The flush result indicates\n// if a newline was written or if a formfeed was dropped from the whitespace\n// buffer.\nfunc (p *printer) flush(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {\n\tif p.commentBefore(next) {\n\t\t// if there are comments before the next item, intersperse them\n\t\twroteNewline, droppedFF = p.intersperseComments(next, tok)\n\t} else {\n\t\t// otherwise, write any leftover whitespace\n\t\tp.writeWhitespace(len(p.wsbuf))\n\t}\n\treturn\n}\n\n// getDoc returns the ast.CommentGroup associated with n, if any.\nfunc getDoc(n ast.Node) *ast.CommentGroup {\n\tswitch n := n.(type) {\n\tcase *ast.Field:\n\t\treturn n.Doc\n\tcase *ast.ImportSpec:\n\t\treturn n.Doc\n\tcase *ast.ValueSpec:\n\t\treturn n.Doc\n\tcase *ast.TypeSpec:\n\t\treturn n.Doc\n\tcase *ast.GenDecl:\n\t\treturn n.Doc\n\tcase *ast.FuncDecl:\n\t\treturn n.Doc\n\tcase *ast.File:\n\t\treturn n.Doc\n\t}\n\treturn nil\n}\n\nfunc getLastComment(n ast.Node) *ast.CommentGroup {\n\tswitch n := n.(type) {\n\tcase *ast.Field:\n\t\treturn n.Comment\n\tcase *ast.ImportSpec:\n\t\treturn n.Comment\n\tcase *ast.ValueSpec:\n\t\treturn n.Comment\n\tcase *ast.TypeSpec:\n\t\treturn n.Comment\n\tcase *ast.GenDecl:\n\t\tif len(n.Specs) > 0 {\n\t\t\treturn getLastComment(n.Specs[len(n.Specs)-1])\n\t\t}\n\tcase *ast.File:\n\t\tif len(n.Comments) > 0 {\n\t\t\treturn n.Comments[len(n.Comments)-1]\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *printer) printNode(node any) error {\n\t// unpack *CommentedNode or *CommentedNodes, if any\n\tvar comments []*ast.CommentGroup\n\tif cnodes, ok := node.(*CommentedNodes); ok {\n\t\tnode = cnodes.Node\n\t\tp.commentedStmts = cnodes.CommentedStmts\n\t} else if cnode, ok := node.(*CommentedNode); ok {\n\t\tnode = cnode.Node\n\t\tcomments = cnode.Comments\n\t}\n\n\tif comments != nil {\n\t\t// commented node - restrict comment list to relevant range\n\t\tn, ok := node.(ast.Node)\n\t\tif !ok {\n\t\t\tgoto unsupported\n\t\t}\n\t\tbeg := n.Pos()\n\t\tend := n.End()\n\t\t// if the node has associated documentation,\n\t\t// include that commentgroup in the range\n\t\t// (the comment list is sorted in the order\n\t\t// of the comment appearance in the source code)\n\t\tif doc := getDoc(n); doc != nil {\n\t\t\tbeg = doc.Pos()\n\t\t}\n\t\tif com := getLastComment(n); com != nil {\n\t\t\tif e := com.End(); e > end {\n\t\t\t\tend = e\n\t\t\t}\n\t\t}\n\t\t// token.Pos values are global offsets, we can\n\t\t// compare them directly\n\t\ti := 0\n\t\tfor i < len(comments) && comments[i].End() < beg {\n\t\t\ti++\n\t\t}\n\t\tj := i\n\t\tfor j < len(comments) && comments[j].Pos() < end {\n\t\t\tj++\n\t\t}\n\t\tif i < j {\n\t\t\tp.comments = comments[i:j]\n\t\t}\n\t} else if n, ok := node.(*ast.File); ok {\n\t\t// use ast.File comments, if any\n\t\tp.comments = n.Comments\n\t}\n\n\t// if there are no comments, use node comments\n\tp.useNodeComments = p.comments == nil\n\n\t// get comments ready for use\n\tp.nextComment()\n\n\t// format node\n\tswitch n := node.(type) {\n\tcase ast.Expr:\n\t\tp.expr(n)\n\tcase ast.Stmt:\n\t\t// A labeled statement will un-indent to position the label.\n\t\t// Set p.indent to 1 so we don't get indent \"underflow\".\n\t\tif _, ok := n.(*ast.LabeledStmt); ok {\n\t\t\tp.indent = 1\n\t\t}\n\t\tp.stmt(n, false)\n\tcase ast.Decl:\n\t\tp.decl(n)\n\tcase ast.Spec:\n\t\tp.spec(n, 1, false)\n\tcase []ast.Stmt:\n\t\t// A labeled statement will un-indent to position the label.\n\t\t// Set p.indent to 1 so we don't get indent \"underflow\".\n\t\tfor _, s := range n {\n\t\t\tif _, ok := s.(*ast.LabeledStmt); ok {\n\t\t\t\tp.indent = 1\n\t\t\t}\n\t\t}\n\t\tp.stmtList(n, 0, false)\n\tcase []ast.Decl:\n\t\tp.declList(n)\n\tcase *ast.File:\n\t\tp.file(n)\n\tdefault:\n\t\tgoto unsupported\n\t}\n\n\treturn nil\n\nunsupported:\n\treturn fmt.Errorf(\"go/printer: unsupported node type %T\", node)\n}\n\n// ----------------------------------------------------------------------------\n// Trimmer\n\n// A trimmer is an io.Writer filter for stripping tabwriter.Escape\n// characters, trailing blanks and tabs, and for converting formfeed\n// and vtab characters into newlines and htabs (in case no tabwriter\n// is used). Text bracketed by tabwriter.Escape characters is passed\n// through unchanged.\ntype trimmer struct {\n\toutput io.Writer\n\tstate  int\n\tspace  []byte\n}\n\n// trimmer is implemented as a state machine.\n// It can be in one of the following states:\nconst (\n\tinSpace  = iota // inside space\n\tinEscape        // inside text bracketed by tabwriter.Escapes\n\tinText          // inside text\n)\n\nfunc (p *trimmer) resetSpace() {\n\tp.state = inSpace\n\tp.space = p.space[0:0]\n}\n\n// Design note: It is tempting to eliminate extra blanks occurring in\n//              whitespace in this function as it could simplify some\n//              of the blanks logic in the node printing functions.\n//              However, this would mess up any formatting done by\n//              the tabwriter.\n\nvar aNewline = []byte(\"\\n\")\n\nfunc (p *trimmer) Write(data []byte) (n int, err error) {\n\t// invariants:\n\t// p.state == inSpace:\n\t//\tp.space is unwritten\n\t// p.state == inEscape, inText:\n\t//\tdata[m:n] is unwritten\n\tm := 0\n\tvar b byte\n\tfor n, b = range data {\n\t\tif b == '\\v' {\n\t\t\tb = '\\t' // convert to htab\n\t\t}\n\t\tswitch p.state {\n\t\tcase inSpace:\n\t\t\tswitch b {\n\t\t\tcase '\\t', ' ':\n\t\t\t\tp.space = append(p.space, b)\n\t\t\tcase '\\n', '\\f':\n\t\t\t\tp.resetSpace() // discard trailing space\n\t\t\t\t_, err = p.output.Write(aNewline)\n\t\t\tcase tabwriter.Escape:\n\t\t\t\t_, err = p.output.Write(p.space)\n\t\t\t\tp.state = inEscape\n\t\t\t\tm = n + 1 // +1: skip tabwriter.Escape\n\t\t\tdefault:\n\t\t\t\t_, err = p.output.Write(p.space)\n\t\t\t\tp.state = inText\n\t\t\t\tm = n\n\t\t\t}\n\t\tcase inEscape:\n\t\t\tif b == tabwriter.Escape {\n\t\t\t\t_, err = p.output.Write(data[m:n])\n\t\t\t\tp.resetSpace()\n\t\t\t}\n\t\tcase inText:\n\t\t\tswitch b {\n\t\t\tcase '\\t', ' ':\n\t\t\t\t_, err = p.output.Write(data[m:n])\n\t\t\t\tp.resetSpace()\n\t\t\t\tp.space = append(p.space, b)\n\t\t\tcase '\\n', '\\f':\n\t\t\t\t_, err = p.output.Write(data[m:n])\n\t\t\t\tp.resetSpace()\n\t\t\t\tif err == nil {\n\t\t\t\t\t_, err = p.output.Write(aNewline)\n\t\t\t\t}\n\t\t\tcase tabwriter.Escape:\n\t\t\t\t_, err = p.output.Write(data[m:n])\n\t\t\t\tp.state = inEscape\n\t\t\t\tm = n + 1 // +1: skip tabwriter.Escape\n\t\t\t}\n\t\tdefault:\n\t\t\tpanic(\"unreachable\")\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tn = len(data)\n\n\tswitch p.state {\n\tcase inEscape, inText:\n\t\t_, err = p.output.Write(data[m:n])\n\t\tp.resetSpace()\n\t}\n\n\treturn\n}\n\n// ----------------------------------------------------------------------------\n// Public interface\n\n// A Mode value is a set of flags (or 0). They control printing.\ntype Mode uint\n\nconst (\n\tRawFormat Mode = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored\n\tTabIndent                  // use tabs for indentation independent of UseSpaces\n\tUseSpaces                  // use spaces instead of tabs for alignment\n\tSourcePos                  // emit //line directives to preserve original source positions\n)\n\n// A Config node controls the output of Fprint.\ntype Config struct {\n\tMode     Mode // default: 0\n\tTabwidth int  // default: 8\n\tIndent   int  // default: 0 (all code is indented at least by this much)\n}\n\n// fprint implements Fprint and takes a nodesSizes map for setting up the printer state.\nfunc (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeSizes map[ast.Node]int) (err error) {\n\t// print node\n\tvar p printer\n\tp.init(cfg, fset, nodeSizes)\n\tif err = p.printNode(node); err != nil {\n\t\treturn\n\t}\n\t// print outstanding comments\n\tp.impliedSemi = false // EOF acts like a newline\n\tp.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF)\n\n\t// redirect output through a trimmer to eliminate trailing whitespace\n\t// (Input to a tabwriter must be untrimmed since trailing tabs provide\n\t// formatting information. The tabwriter could provide trimming\n\t// functionality but no tabwriter is used when RawFormat is set.)\n\toutput = &trimmer{output: output}\n\n\t// redirect output through a tabwriter if necessary\n\tif cfg.Mode&RawFormat == 0 {\n\t\tminwidth := cfg.Tabwidth\n\n\t\tpadchar := byte('\\t')\n\t\tif cfg.Mode&UseSpaces != 0 {\n\t\t\tpadchar = ' '\n\t\t}\n\n\t\ttwmode := tabwriter.DiscardEmptyColumns\n\t\tif cfg.Mode&TabIndent != 0 {\n\t\t\tminwidth = 0\n\t\t\ttwmode |= tabwriter.TabIndent\n\t\t}\n\n\t\toutput = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padchar, twmode)\n\t}\n\n\t// write printer result via tabwriter/trimmer to output\n\tif _, err = output.Write(p.output); err != nil {\n\t\treturn\n\t}\n\n\t// flush tabwriter, if any\n\tif tw, _ := output.(*tabwriter.Writer); tw != nil {\n\t\terr = tw.Flush()\n\t}\n\n\treturn\n}\n\n// A CommentedNode bundles an AST node and corresponding comments.\n// It may be provided as argument to any of the Fprint functions.\ntype CommentedNode struct {\n\tNode     any // *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt\n\tComments []*ast.CommentGroup\n}\n\n// CommentedNodes holds an AST node and a map of statements with their leading comments.\n// This allows attaching diagnostic comments to specific statements during code generation.\ntype CommentedNodes struct {\n\tNode           any                            // *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt\n\tCommentedStmts map[ast.Stmt]*ast.CommentGroup // statements with their leading comments\n}\n\n// Fprint \"pretty-prints\" an AST node to output for a given configuration cfg.\n// Position information is interpreted relative to the file set fset.\n// The node type must be *ast.File, *CommentedNode, *CommentedNodes, []ast.Decl, []ast.Stmt,\n// or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.\nfunc (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node any) error {\n\treturn cfg.fprint(output, fset, node, make(map[ast.Node]int))\n}\n\n// Fprint \"pretty-prints\" an AST node to output.\n// It calls Config.Fprint with default settings.\n// Note that gofmt uses tabs for indentation but spaces for alignment;\n// use format.Node (package go/format) for output that matches gofmt.\nfunc Fprint(output io.Writer, fset *token.FileSet, node any) error {\n\treturn (&Config{Tabwidth: 8}).Fprint(output, fset, node)\n}\n"
  },
  {
    "path": "printer/printer_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage printer\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nconst (\n\tdataDir  = \"demo\"\n\ttabwidth = 8\n)\n\nvar fset = token.NewFileSet()\n\ntype checkMode uint\n\nconst (\n\texport checkMode = 1 << iota\n\trawFormat\n\tidempotent\n)\n\n// format parses src, prints the corresponding AST, verifies the resulting\n// src is syntactically correct, and returns the resulting src or an error\n// if any.\nfunc format(src []byte, mode checkMode) ([]byte, error) {\n\t// parse src\n\tf, err := parser.ParseFile(fset, \"\", src, parser.ParseComments)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse: %s\\n%s\", err, src)\n\t}\n\n\t// filter exports if necessary\n\tif mode&export != 0 {\n\t\tast.FileExports(f) // ignore result\n\t\tf.Comments = nil   // don't print comments that are not in AST\n\t}\n\n\t// determine printer configuration\n\tcfg := Config{Tabwidth: tabwidth}\n\tif mode&rawFormat != 0 {\n\t\tcfg.Mode |= RawFormat\n\t}\n\n\t// print AST\n\tvar buf bytes.Buffer\n\tif err := cfg.Fprint(&buf, fset, f); err != nil {\n\t\treturn nil, fmt.Errorf(\"print: %s\", err)\n\t}\n\n\t// make sure formatted output is syntactically correct\n\tres := buf.Bytes()\n\tif _, err := parser.ParseFile(fset, \"\", res, 0); err != nil {\n\t\treturn nil, fmt.Errorf(\"re-parse: %s\\n%s\", err, buf.Bytes())\n\t}\n\n\treturn res, nil\n}\n\n// TestLineComments, using a simple test case, checks that consecutive line\n// comments are properly terminated with a newline even if the AST position\n// information is incorrect.\nfunc TestLineComments(t *testing.T) {\n\tconst src = `// comment 1\n\t// comment 2\n\t// comment 3\n\t# comment 4\n\tpackage main\n\t`\n\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"\", src, parser.ParseComments)\n\tif err != nil {\n\t\tpanic(err) // error in test\n\t}\n\n\tvar buf bytes.Buffer\n\tfset = token.NewFileSet() // use the wrong file set\n\tFprint(&buf, fset, f)\n\tnlines := 0\n\tfor _, ch := range buf.Bytes() {\n\t\tif ch == '\\n' {\n\t\t\tnlines++\n\t\t}\n\t}\n\n\tconst expected = 4\n\tif nlines < expected {\n\t\tt.Errorf(\"got %d, expected %d\\n\", nlines, expected)\n\t\tt.Errorf(\"result:\\n%s\", buf.Bytes())\n\t}\n}\n\n// Verify that the printer can be invoked during initialization.\nfunc init() {\n\tconst name = \"foobar\"\n\tvar buf bytes.Buffer\n\tif err := Fprint(&buf, fset, &ast.Ident{Name: name}); err != nil {\n\t\tpanic(err) // error in test\n\t}\n\t// in debug mode, the result contains additional information;\n\t// ignore it\n\tif s := buf.String(); !debug && s != name {\n\t\tpanic(\"got \" + s + \", want \" + name)\n\t}\n}\n\n// testComment verifies that f can be parsed again after printing it\n// with its first comment set to comment at any possible source offset.\nfunc testComment(t *testing.T, f *ast.File, srclen int, comment *ast.Comment) {\n\tf.Comments[0].List[0] = comment\n\tvar buf bytes.Buffer\n\tfor offs := 0; offs <= srclen; offs++ {\n\t\tbuf.Reset()\n\t\t// Printing f should result in a correct program no\n\t\t// matter what the (incorrect) comment position is.\n\t\tif err := Fprint(&buf, fset, f); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif _, err := parser.ParseFile(fset, \"\", buf.Bytes(), 0); err != nil {\n\t\t\tt.Fatalf(\"incorrect program for pos = %d:\\n%s\", comment.Slash, buf.String())\n\t\t}\n\t\t// Position information is just an offset.\n\t\t// Move comment one byte down in the source.\n\t\tcomment.Slash++\n\t}\n}\n\n// Verify that the printer produces a correct program\n// even if the position information of comments introducing newlines\n// is incorrect.\nfunc TestBadComments(t *testing.T) {\n\tt.Parallel()\n\tconst src = `\n// first comment - text and position changed by test\npackage p\nimport \"fmt\"\nconst pi = 3.14 // rough circle\nvar (\n\tx, y, z int = 1, 2, 3\n\tu, v float64\n)\nfunc fibo(n int) {\n\tif n < 2 {\n\t\treturn n /* seed values */\n\t}\n\treturn fibo(n-1) + fibo(n-2)\n}\n`\n\n\tf, err := parser.ParseFile(fset, \"\", src, parser.ParseComments)\n\tif err != nil {\n\t\tt.Error(err) // error in test\n\t}\n\n\tcomment := f.Comments[0].List[0]\n\tpos := comment.Pos()\n\tif fset.PositionFor(pos, false /* absolute position */).Offset != 1 {\n\t\tt.Error(\"expected offset 1\") // error in test\n\t}\n\n\ttestComment(t, f, len(src), &ast.Comment{Slash: pos, Text: \"//-style comment\"})\n\ttestComment(t, f, len(src), &ast.Comment{Slash: pos, Text: \"/*-style comment */\"})\n\ttestComment(t, f, len(src), &ast.Comment{Slash: pos, Text: \"/*-style \\n comment */\"})\n\ttestComment(t, f, len(src), &ast.Comment{Slash: pos, Text: \"/*-style comment \\n\\n\\n */\"})\n}\n\ntype visitor chan *ast.Ident\n\nfunc (v visitor) Visit(n ast.Node) (w ast.Visitor) {\n\tif ident, ok := n.(*ast.Ident); ok {\n\t\tv <- ident\n\t}\n\treturn v\n}\n\n// idents is an iterator that returns all idents in f via the result channel.\nfunc idents(f *ast.File) <-chan *ast.Ident {\n\tv := make(visitor)\n\tgo func() {\n\t\tast.Walk(v, f)\n\t\tclose(v)\n\t}()\n\treturn v\n}\n\n// identCount returns the number of identifiers found in f.\nfunc identCount(f *ast.File) int {\n\tn := 0\n\tfor range idents(f) {\n\t\tn++\n\t}\n\treturn n\n}\n\n// Verify that the SourcePos mode emits correct //line directives\n// by testing that position information for matching identifiers\n// is maintained.\nfunc TestSourcePos(t *testing.T) {\n\tconst src = `\npackage p\nimport ( \"go/printer\"; \"math\" )\nconst pi = 3.14; var x = 0\ntype t struct{ x, y, z int; u, v, w float32 }\nfunc (t *t) foo(a, b, c int) int {\n\treturn a*t.x + b*t.y +\n\t\t// two extra lines here\n\t\t// ...\n\t\tc*t.z\n}\n`\n\n\t// parse original\n\tf1, err := parser.ParseFile(fset, \"src\", src, parser.ParseComments)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// pretty-print original\n\tvar buf bytes.Buffer\n\terr = (&Config{Mode: UseSpaces | SourcePos, Tabwidth: 8}).Fprint(&buf, fset, f1)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// parse pretty printed original\n\t// (//line directives must be interpreted even w/o parser.ParseComments set)\n\tf2, err := parser.ParseFile(fset, \"\", buf.Bytes(), 0)\n\tif err != nil {\n\t\tt.Fatalf(\"%s\\n%s\", err, buf.Bytes())\n\t}\n\n\t// At this point the position information of identifiers in f2 should\n\t// match the position information of corresponding identifiers in f1.\n\n\t// number of identifiers must be > 0 (test should run) and must match\n\tn1 := identCount(f1)\n\tn2 := identCount(f2)\n\tif n1 == 0 {\n\t\tt.Fatal(\"got no idents\")\n\t}\n\tif n2 != n1 {\n\t\tt.Errorf(\"got %d idents; want %d\", n2, n1)\n\t}\n\n\t// verify that all identifiers have correct line information\n\ti2range := idents(f2)\n\tfor i1 := range idents(f1) {\n\t\ti2 := <-i2range\n\n\t\tif i2.Name != i1.Name {\n\t\t\tt.Errorf(\"got ident %s; want %s\", i2.Name, i1.Name)\n\t\t}\n\n\t\t// here we care about the relative (line-directive adjusted) positions\n\t\tl1 := fset.Position(i1.Pos()).Line\n\t\tl2 := fset.Position(i2.Pos()).Line\n\t\tif l2 != l1 {\n\t\t\tt.Errorf(\"got line %d; want %d for %s\", l2, l1, i1.Name)\n\t\t}\n\t}\n\n\tif t.Failed() {\n\t\tt.Logf(\"\\n%s\", buf.Bytes())\n\t}\n}\n\n// Verify that the SourcePos mode doesn't emit unnecessary //line directives\n// before empty lines.\nfunc TestIssue5945(t *testing.T) {\n\tconst orig = `\npackage p   // line 2\nfunc f() {} // line 3\n\nvar x, y, z int\n\n\nfunc g() { // line 8\n}\n`\n\n\tconst want = `//line src.go:2\npackage p\n\n//line src.go:3\nfunc f() {}\n\nvar x, y, z int\n\n//line src.go:8\nfunc g() {\n}\n`\n\n\t// parse original\n\tf1, err := parser.ParseFile(fset, \"src.go\", orig, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// pretty-print original\n\tvar buf bytes.Buffer\n\terr = (&Config{Mode: UseSpaces | SourcePos, Tabwidth: 8}).Fprint(&buf, fset, f1)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tgot := buf.String()\n\n\t// compare original with desired output\n\tif got != want {\n\t\tt.Errorf(\"got:\\n%s\\nwant:\\n%s\\n\", got, want)\n\t}\n}\n\nvar decls = []string{\n\t`import \"fmt\"`,\n\t\"const pi = 3.1415\\nconst e = 2.71828\\n\\nvar x = pi\",\n\t\"func sum(x, y int) int\\t{ return x + y }\",\n}\n\nfunc TestDeclLists(t *testing.T) {\n\tfor _, src := range decls {\n\t\tfile, err := parser.ParseFile(fset, \"\", \"package p;\"+src, parser.ParseComments)\n\t\tif err != nil {\n\t\t\tpanic(err) // error in test\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\terr = Fprint(&buf, fset, file.Decls) // only print declarations\n\t\tif err != nil {\n\t\t\tpanic(err) // error in test\n\t\t}\n\n\t\tout := buf.String()\n\t\tif out != src {\n\t\t\tt.Errorf(\"\\ngot : %q\\nwant: %q\\n\", out, src)\n\t\t}\n\t}\n}\n\nvar stmts = []string{\n\t\"i := 0\",\n\t\"select {}\\nvar a, b = 1, 2\\nreturn a + b\",\n\t\"go f()\\ndefer func() {}()\",\n}\n\nfunc TestStmtLists(t *testing.T) {\n\tfor _, src := range stmts {\n\t\tfile, err := parser.ParseFile(fset, \"\", \"package p; func _() {\"+src+\"}\", parser.ParseComments)\n\t\tif err != nil {\n\t\t\tpanic(err) // error in test\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\terr = Fprint(&buf, fset, file.Decls[0].(*ast.FuncDecl).Body.List) // only print statements\n\t\tif err != nil {\n\t\t\tpanic(err) // error in test\n\t\t}\n\n\t\tout := buf.String()\n\t\tif out != src {\n\t\t\tt.Errorf(\"\\ngot : %q\\nwant: %q\\n\", out, src)\n\t\t}\n\t}\n}\n\nfunc TestBaseIndent(t *testing.T) {\n\tt.Parallel()\n\t// The testfile must not contain multi-line raw strings since those\n\t// are not indented (because their values must not change) and make\n\t// this test fail.\n\tconst filename = \"printer.go\"\n\tsrc, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\tpanic(err) // error in test\n\t}\n\n\tfile, err := parser.ParseFile(fset, filename, src, 0)\n\tif err != nil {\n\t\tpanic(err) // error in test\n\t}\n\n\tfor indent := 0; indent < 4; indent++ {\n\t\tindent := indent\n\t\tt.Run(fmt.Sprint(indent), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tvar buf bytes.Buffer\n\t\t\t(&Config{Tabwidth: tabwidth, Indent: indent}).Fprint(&buf, fset, file)\n\t\t\t// all code must be indented by at least 'indent' tabs\n\t\t\tlines := bytes.Split(buf.Bytes(), []byte{'\\n'})\n\t\t\tfor i, line := range lines {\n\t\t\t\tif len(line) == 0 {\n\t\t\t\t\tcontinue // empty lines don't have indentation\n\t\t\t\t}\n\t\t\t\tn := 0\n\t\t\t\tfor j, b := range line {\n\t\t\t\t\tif b != '\\t' {\n\t\t\t\t\t\t// end of indentation\n\t\t\t\t\t\tn = j\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif n < indent {\n\t\t\t\t\tt.Errorf(\"line %d: got only %d tabs; want at least %d: %q\", i, n, indent, line)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestFuncType tests that an ast.FuncType with a nil Params field\n// can be printed (per go/ast specification). Test case for issue 3870.\nfunc TestFuncType(t *testing.T) {\n\tsrc := &ast.File{\n\t\tName: &ast.Ident{Name: \"p\"},\n\t\tDecls: []ast.Decl{\n\t\t\t&ast.FuncDecl{\n\t\t\t\tName: &ast.Ident{Name: \"f\"},\n\t\t\t\tType: &ast.FuncType{},\n\t\t\t},\n\t\t},\n\t}\n\n\tvar buf bytes.Buffer\n\tif err := Fprint(&buf, fset, src); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tgot := buf.String()\n\n\tconst want = `package p\n\nfunc f()\n`\n\n\tif got != want {\n\t\tt.Fatalf(\"got:\\n%s\\nwant:\\n%s\\n\", got, want)\n\t}\n}\n\ntype limitWriter struct {\n\tremaining int\n\terrCount  int\n}\n\nfunc (l *limitWriter) Write(buf []byte) (n int, err error) {\n\tn = len(buf)\n\tif n >= l.remaining {\n\t\tn = l.remaining\n\t\terr = io.EOF\n\t\tl.errCount++\n\t}\n\tl.remaining -= n\n\treturn n, err\n}\n\n// Test whether the printer stops writing after the first error\nfunc TestWriteErrors(t *testing.T) {\n\tt.Parallel()\n\tconst filename = \"printer.go\"\n\tsrc, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\tpanic(err) // error in test\n\t}\n\tfile, err := parser.ParseFile(fset, filename, src, 0)\n\tif err != nil {\n\t\tpanic(err) // error in test\n\t}\n\tfor i := 0; i < 20; i++ {\n\t\tlw := &limitWriter{remaining: i}\n\t\terr := (&Config{Mode: RawFormat}).Fprint(lw, fset, file)\n\t\tif lw.errCount > 1 {\n\t\t\tt.Fatal(\"Writes continued after first error returned\")\n\t\t}\n\t\t// We expect errCount be 1 iff err is set\n\t\tif (lw.errCount != 0) != (err != nil) {\n\t\t\tt.Fatal(\"Expected err when errCount != 0\")\n\t\t}\n\t}\n}\n\n// TestX is a skeleton test that can be filled in for debugging one-off cases.\n// Do not remove.\nfunc TestX(t *testing.T) {\n\tconst src = `\npackage p\nfunc _() {}\n`\n\t_, err := format([]byte(src), 0)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestCommentedNode(t *testing.T) {\n\tconst (\n\t\tinput = `package main\n\nfunc foo() {\n\t// comment inside func\n}\n\n// leading comment\ntype bar int // comment2\n\n`\n\n\t\tfoo = `func foo() {\n\t// comment inside func\n}`\n\n\t\tbar = `// leading comment\ntype bar int\t// comment2\n`\n\t)\n\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"input.go\", input, parser.ParseComments)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar buf bytes.Buffer\n\n\terr = Fprint(&buf, fset, &CommentedNode{Node: f.Decls[0], Comments: f.Comments})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif buf.String() != foo {\n\t\tt.Errorf(\"got %q, want %q\", buf.String(), foo)\n\t}\n\n\tbuf.Reset()\n\n\terr = Fprint(&buf, fset, &CommentedNode{Node: f.Decls[1], Comments: f.Comments})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif buf.String() != bar {\n\t\tt.Errorf(\"got %q, want %q\", buf.String(), bar)\n\t}\n}\n\nfunc TestIssue11151(t *testing.T) {\n\tconst src = \"package p\\t/*\\r/1\\r*\\r/2*\\r\\r\\r\\r/3*\\r\\r+\\r\\r/4*/\\n\"\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"\", src, parser.ParseComments)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar buf bytes.Buffer\n\tFprint(&buf, fset, f)\n\tgot := buf.String()\n\tconst want = \"package p\\t/*/1*\\r/2*\\r/3*+/4*/\\n\" // \\r following opening /* should be stripped\n\tif got != want {\n\t\tt.Errorf(\"\\ngot : %q\\nwant: %q\", got, want)\n\t}\n\n\t// the resulting program must be valid\n\t_, err = parser.ParseFile(fset, \"\", got, 0)\n\tif err != nil {\n\t\tt.Errorf(\"%v\\norig: %q\\ngot : %q\", err, src, got)\n\t}\n}\n\n// If a declaration has multiple specifications, a parenthesized\n// declaration must be printed even if Lparen is token.NoPos.\nfunc TestParenthesizedDecl(t *testing.T) {\n\t// a package with multiple specs in a single declaration\n\tconst src = \"package p; var ( a float64; b int )\"\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"\", src, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// print the original package\n\tvar buf bytes.Buffer\n\terr = Fprint(&buf, fset, f)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\toriginal := buf.String()\n\n\t// now remove parentheses from the declaration\n\tfor i := 0; i != len(f.Decls); i++ {\n\t\tf.Decls[i].(*ast.GenDecl).Lparen = token.NoPos\n\t}\n\tbuf.Reset()\n\terr = Fprint(&buf, fset, f)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tnoparen := buf.String()\n\n\tif noparen != original {\n\t\tt.Errorf(\"got %q, want %q\", noparen, original)\n\t}\n}\n\n// Verify that we don't print a newline between \"return\" and its results, as\n// that would incorrectly cause a naked return.\nfunc TestIssue32854(t *testing.T) {\n\tsrc := `package foo\n\nfunc f() {\n        return Composite{\n                call(),\n        }\n}`\n\tfset := token.NewFileSet()\n\tfile, err := parser.ParseFile(fset, \"\", src, 0)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Replace the result with call(), which is on the next line.\n\tfd := file.Decls[0].(*ast.FuncDecl)\n\tret := fd.Body.List[0].(*ast.ReturnStmt)\n\tret.Results[0] = ret.Results[0].(*ast.CompositeLit).Elts[0]\n\n\tvar buf bytes.Buffer\n\tif err := Fprint(&buf, fset, ret); err != nil {\n\t\tt.Fatal(err)\n\t}\n\twant := \"return call()\"\n\tif got := buf.String(); got != want {\n\t\tt.Fatalf(\"got %q, want %q\", got, want)\n\t}\n}\n\nfunc TestStripParens(t *testing.T) {\n\tx := stripParens(&ast.ParenExpr{\n\t\tX: &ast.CompositeLit{\n\t\t\tType: &ast.Ident{Name: \"foo\"},\n\t\t},\n\t})\n\tif _, ok := x.(*ast.ParenExpr); !ok {\n\t\tt.Fatal(\"TestStripParens failed:\", x)\n\t}\n\n\tx = stripParens(&ast.ParenExpr{\n\t\tX: &ast.CompositeLit{\n\t\t\tType: &ast.SelectorExpr{\n\t\t\t\tX: &ast.Ident{Name: \"foo\"},\n\t\t\t},\n\t\t},\n\t})\n\tif _, ok := x.(*ast.ParenExpr); !ok {\n\t\tt.Fatal(\"TestStripParens failed:\", x)\n\t}\n\n\tx = stripParens(&ast.ParenExpr{\n\t\tX: &ast.ParenExpr{\n\t\t\tX: &ast.CompositeLit{\n\t\t\t\tType: &ast.Ident{Name: \"foo\"},\n\t\t\t},\n\t\t},\n\t})\n\tif y, ok := x.(*ast.ParenExpr); !ok {\n\t\tt.Fatal(\"TestStripParens failed:\", x)\n\t} else if _, ok := y.X.(*ast.ParenExpr); ok {\n\t\tt.Fatal(\"TestStripParens failed:\", x)\n\t}\n\n\tx = stripParens(&ast.ParenExpr{\n\t\tX: &ast.CompositeLit{\n\t\t\tType: &ast.BasicLit{},\n\t\t},\n\t})\n\tif _, ok := x.(*ast.ParenExpr); ok {\n\t\tt.Fatal(\"TestStripParens stripParens failed:\", x)\n\t}\n\n\tx = stripParens(&ast.ParenExpr{\n\t\tX: &ast.BasicLit{},\n\t})\n\tif _, ok := x.(*ast.ParenExpr); ok {\n\t\tt.Fatal(\"TestStripParens stripParens failed:\", x)\n\t}\n\n\tx = stripParensAlways(&ast.ParenExpr{\n\t\tX: &ast.ParenExpr{\n\t\t\tX: &ast.CompositeLit{\n\t\t\t\tType: &ast.Ident{Name: \"foo\"},\n\t\t\t},\n\t\t},\n\t})\n\tif _, ok := x.(*ast.ParenExpr); ok {\n\t\tt.Fatal(\"TestStripParens stripParensAlways failed:\", x)\n\t}\n}\n\n// TestCommentedNodes tests the CommentedNodes type which allows attaching\n// diagnostic comments to specific statements during code generation.\nfunc TestCommentedNodes(t *testing.T) {\n\tconst input = `package main\n\nfunc foo() {\n\tx := 1\n\ty := 2\n\tprintln(x + y)\n}\n`\n\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"input.go\", input, parser.ParseComments)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Get the function body statements\n\tfuncDecl := f.Decls[0].(*ast.FuncDecl)\n\tstmts := funcDecl.Body.List\n\n\t// Test case 1: Single statement with comment\n\tt.Run(\"SingleStatement\", func(t *testing.T) {\n\t\tcommentedStmts := make(map[ast.Stmt]*ast.CommentGroup)\n\t\tcommentedStmts[stmts[0]] = &ast.CommentGroup{\n\t\t\tList: []*ast.Comment{\n\t\t\t\t{Text: \"// TYPECHECK(sb2xbp): type inference failed\"},\n\t\t\t},\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\tnode := &CommentedNodes{\n\t\t\tNode:           stmts[0],\n\t\t\tCommentedStmts: commentedStmts,\n\t\t}\n\n\t\terr := Fprint(&buf, fset, node)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tgot := buf.String()\n\t\twant := \"// TYPECHECK(sb2xbp): type inference failed\\nx := 1\"\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n\n\t// Test case 2: Multiple statements with comments\n\tt.Run(\"MultipleStatements\", func(t *testing.T) {\n\t\tcommentedStmts := make(map[ast.Stmt]*ast.CommentGroup)\n\t\tcommentedStmts[stmts[0]] = &ast.CommentGroup{\n\t\t\tList: []*ast.Comment{\n\t\t\t\t{Text: \"// FIXME(sb2xbp): unsupported block type\"},\n\t\t\t},\n\t\t}\n\t\tcommentedStmts[stmts[2]] = &ast.CommentGroup{\n\t\t\tList: []*ast.Comment{\n\t\t\t\t{Text: \"// TYPECHECK(sb2xbp): type mismatch\"},\n\t\t\t},\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\tnode := &CommentedNodes{\n\t\t\tNode:           stmts,\n\t\t\tCommentedStmts: commentedStmts,\n\t\t}\n\n\t\terr := Fprint(&buf, fset, node)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tgot := buf.String()\n\t\twant := \"// FIXME(sb2xbp): unsupported block type\\nx := 1\\ny := 2\\n// TYPECHECK(sb2xbp): type mismatch\\nprintln(x + y)\"\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n\n\t// Test case 3: Complete file with commented statements\n\tt.Run(\"CompleteFile\", func(t *testing.T) {\n\t\tcommentedStmts := make(map[ast.Stmt]*ast.CommentGroup)\n\t\tcommentedStmts[stmts[1]] = &ast.CommentGroup{\n\t\t\tList: []*ast.Comment{\n\t\t\t\t{Text: \"// NOTE: converted from Scratch block\"},\n\t\t\t},\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\tnode := &CommentedNodes{\n\t\t\tNode:           f,\n\t\t\tCommentedStmts: commentedStmts,\n\t\t}\n\n\t\terr := Fprint(&buf, fset, node)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tgot := buf.String()\n\t\t// The output should contain the comment before the second statement\n\t\tif !bytes.Contains([]byte(got), []byte(\"// NOTE: converted from Scratch block\\n\\ty := 2\")) {\n\t\t\tt.Errorf(\"output does not contain expected comment:\\n%s\", got)\n\t\t}\n\t})\n\n\t// Test case 4: Empty CommentedStmts map (no comments added)\n\tt.Run(\"EmptyComments\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tnode := &CommentedNodes{\n\t\t\tNode:           stmts[0],\n\t\t\tCommentedStmts: make(map[ast.Stmt]*ast.CommentGroup),\n\t\t}\n\n\t\terr := Fprint(&buf, fset, node)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tgot := buf.String()\n\t\twant := \"x := 1\"\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n\n\t// Test case 5: Nil CommentedStmts map\n\tt.Run(\"NilComments\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\tnode := &CommentedNodes{\n\t\t\tNode:           stmts[0],\n\t\t\tCommentedStmts: nil,\n\t\t}\n\n\t\terr := Fprint(&buf, fset, node)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tgot := buf.String()\n\t\twant := \"x := 1\"\n\t\tif got != want {\n\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t}\n\t})\n\n\t// Test case 6: Multi-line comment\n\tt.Run(\"MultiLineComment\", func(t *testing.T) {\n\t\tcommentedStmts := make(map[ast.Stmt]*ast.CommentGroup)\n\t\tcommentedStmts[stmts[0]] = &ast.CommentGroup{\n\t\t\tList: []*ast.Comment{\n\t\t\t\t{Text: \"// TYPECHECK(sb2xbp): type inference failed\"},\n\t\t\t\t{Text: \"// TODO: manual type annotation required\"},\n\t\t\t},\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\tnode := &CommentedNodes{\n\t\t\tNode:           stmts[0],\n\t\t\tCommentedStmts: commentedStmts,\n\t\t}\n\n\t\terr := Fprint(&buf, fset, node)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tgot := buf.String()\n\t\t// Should contain both comment lines\n\t\tif !bytes.Contains([]byte(got), []byte(\"TYPECHECK(sb2xbp)\")) ||\n\t\t\t!bytes.Contains([]byte(got), []byte(\"TODO: manual type annotation\")) {\n\t\t\tt.Errorf(\"output does not contain expected multi-line comments:\\n%s\", got)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "printer/xgo_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage printer_test\n\nimport (\n\t\"bytes\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/format\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/printer\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nfunc init() {\n\tprinter.SetDebug(printer.DbgFlagAll)\n}\n\nfunc TestNoPkgDecl(t *testing.T) {\n\tvar dst bytes.Buffer\n\tfset := token.NewFileSet()\n\tif err := format.Node(&dst, fset, &ast.File{\n\t\tName:      &ast.Ident{Name: \"main\"},\n\t\tNoPkgDecl: true,\n\t}); err != nil {\n\t\tt.Fatal(\"format.Node failed:\", err)\n\t}\n\tif dst.String() != \"\\n\" {\n\t\tt.Fatal(\"TestNoPkgDecl:\", dst.String())\n\t}\n}\n\nfunc TestFuncs(t *testing.T) {\n\tvar dst bytes.Buffer\n\tfset := token.NewFileSet()\n\tif err := format.Node(&dst, fset, &ast.File{\n\t\tName: &ast.Ident{Name: \"main\"},\n\t\tDecls: []ast.Decl{\n\t\t\t&ast.FuncDecl{\n\t\t\t\tType: &ast.FuncType{Params: &ast.FieldList{}},\n\t\t\t\tName: &ast.Ident{Name: \"foo\"},\n\t\t\t\tBody: &ast.BlockStmt{\n\t\t\t\t\tList: []ast.Stmt{&printer.NewlineStmt{}},\n\t\t\t\t},\n\t\t\t},\n\t\t\t&ast.FuncDecl{\n\t\t\t\tType: &ast.FuncType{Params: &ast.FieldList{}},\n\t\t\t\tName: &ast.Ident{Name: \"bar\"},\n\t\t\t\tBody: &ast.BlockStmt{},\n\t\t\t},\n\t\t\t&ast.FuncDecl{\n\t\t\t\tType:   &ast.FuncType{Params: &ast.FieldList{}},\n\t\t\t\tName:   &ast.Ident{Name: \"Classname\"},\n\t\t\t\tBody:   &ast.BlockStmt{},\n\t\t\t\tShadow: true,\n\t\t\t},\n\t\t},\n\t\tNoPkgDecl: true,\n\t}); err != nil {\n\t\tt.Fatal(\"format.Node failed:\", err)\n\t}\n\tif dst.String() != `func foo() {\n\n}\n\nfunc bar() {\n}\n` {\n\t\tt.Fatal(\"TestNoPkgDecl:\", dst.String())\n\t}\n}\n\nfunc diffBytes(t *testing.T, dst, src []byte) {\n\tline := 1\n\toffs := 0 // line offset\n\tfor i := 0; i < len(dst) && i < len(src); i++ {\n\t\td := dst[i]\n\t\ts := src[i]\n\t\tif d != s {\n\t\t\tt.Errorf(\"dst:%d: %s\\n\", line, dst[offs:])\n\t\t\tt.Errorf(\"src:%d: %s\\n\", line, src[offs:])\n\t\t\treturn\n\t\t}\n\t\tif s == '\\n' {\n\t\t\tline++\n\t\t\toffs = i + 1\n\t\t}\n\t}\n\tif len(dst) != len(src) {\n\t\tt.Errorf(\"len(dst) = %d, len(src) = %d\\ndst = %q\\nsrc = %q\", len(dst), len(src), dst, src)\n\t}\n}\n\nconst (\n\texcludeFormatSource = 1\n\texcludeFormatNode   = 2\n)\n\nfunc testFrom(t *testing.T, fpath, sel string, mode int) {\n\tif sel != \"\" && !strings.Contains(fpath, sel) {\n\t\treturn\n\t}\n\tsrc, err := os.ReadFile(fpath)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif (mode & excludeFormatSource) == 0 {\n\t\tt.Run(\"format.Source \"+fpath, func(t *testing.T) {\n\t\t\tvar class bool\n\t\t\tif filepath.Ext(fpath) == \".gox\" {\n\t\t\t\tclass = true\n\t\t\t}\n\t\t\tres, err := format.Source(src, class, fpath)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(\"Source failed:\", err)\n\t\t\t}\n\t\t\tdiffBytes(t, res, src)\n\t\t})\n\t}\n\n\tif (mode & excludeFormatNode) == 0 {\n\t\tt.Run(\"format.Node \"+fpath, func(t *testing.T) {\n\t\t\tfset := token.NewFileSet()\n\t\t\tm := parser.ParseComments\n\t\t\tif filepath.Ext(fpath) == \".gox\" {\n\t\t\t\tm |= parser.ParseXGoClass\n\t\t\t}\n\t\t\tf, err := parser.ParseFile(fset, fpath, src, m)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tvar buf bytes.Buffer\n\t\t\terr = format.Node(&buf, fset, f)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdiffBytes(t, buf.Bytes(), src)\n\t\t})\n\t}\n}\n\nfunc TestFromGopPrinter(t *testing.T) {\n\ttestFrom(t, \"nodes.go\", \"\", 0)\n\ttestFrom(t, \"printer.go\", \"\", 0)\n\ttestFrom(t, \"printer_test.go\", \"\", 0)\n}\n\nfunc TestFromTestdata(t *testing.T) {\n\tsel := \"\"\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = filepath.Join(dir, \"./_testdata\")\n\tfilepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !info.IsDir() && filepath.Ext(path) == \".xgo\" {\n\t\t\ttestFrom(t, path, sel, 0)\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc TestFromParse(t *testing.T) {\n\tsel := \"\"\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = filepath.Join(dir, \"../parser/_testdata\")\n\tfilepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tname := info.Name()\n\t\text := filepath.Ext(name)\n\t\tif !info.IsDir() && (ext == \".xgo\" || ext == \".gox\") {\n\t\t\ttestFrom(t, path, sel, 0)\n\t\t}\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "scanner/gop.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage scanner\n\nimport (\n\t\"go/scanner\"\n\t\"io\"\n\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/qiniu/x/byteutil\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Error is an alias of go/scanner.Error\ntype Error = scanner.Error\n\n// ErrorList is an alias of go/scanner.ErrorList\ntype ErrorList = scanner.ErrorList\n\n// PrintError is a utility function that prints a list of errors to w,\n// one error per line, if the err parameter is an ErrorList. Otherwise\n// it prints the err string.\nfunc PrintError(w io.Writer, err error) {\n\tscanner.PrintError(w, err)\n}\n\n// -----------------------------------------------------------------------------\n\n// New creates a scanner to tokenize the text src.\n//\n// Calls to Scan will invoke the error handler err if they encounter a\n// syntax error and err is not nil. Also, for each error encountered,\n// the Scanner field ErrorCount is incremented by one. The mode parameter\n// determines how comments are handled.\nfunc New(src string, err ErrorHandler, mode Mode) *Scanner {\n\tfset := token.NewFileSet()\n\tfile := fset.AddFile(\"\", fset.Base(), len(src))\n\tscanner := new(Scanner)\n\tscanner.Init(file, byteutil.Bytes(src), err, mode)\n\treturn scanner\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "scanner/scanner.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package scanner implements a scanner for XGo source text.\n// It takes a []byte as source which can then be tokenized\n// through repeated calls to the Scan method.\npackage scanner\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/scanner\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/goplus/xgo/token\"\n)\n\n// An ErrorHandler may be provided to Scanner.Init. If a syntax error is\n// encountered and a handler was installed, the handler is called with a\n// position and an error message. The position points to the beginning of\n// the offending token.\ntype ErrorHandler = scanner.ErrorHandler\n\n// A Scanner holds the scanner's internal state while processing\n// a given text. It can be allocated as part of another data\n// structure but must be initialized via Init before use.\ntype Scanner struct {\n\t// immutable state\n\tfile *token.File  // source file handle\n\tdir  string       // directory portion of file.Name()\n\tsrc  []byte       // source\n\terr  ErrorHandler // error reporting; or nil\n\tmode Mode         // scanning mode\n\n\t// scanning state\n\tch         rune // current character\n\toffset     int  // character offset\n\trdOffset   int  // reading offset (position after current character)\n\tlineOffset int  // current line offset\n\tnParen     int\n\tpeekLit    string\n\tpeekTok    token.Token\n\tinsertSemi bool // insert a semicolon before next newline\n\n\t// public state - ok to modify\n\tErrorCount int // number of errors encountered\n}\n\nconst bom = 0xFEFF // byte order mark, only permitted as very first character\n\n// Read the next Unicode char into s.ch.\n// s.ch < 0 means end-of-file.\nfunc (s *Scanner) next() {\n\tif s.rdOffset < len(s.src) {\n\t\ts.offset = s.rdOffset\n\t\tif s.ch == '\\n' {\n\t\t\ts.lineOffset = s.offset\n\t\t\ts.file.AddLine(s.offset)\n\t\t}\n\t\tr, w := rune(s.src[s.rdOffset]), 1\n\t\tswitch {\n\t\tcase r == 0:\n\t\t\ts.error(s.offset, \"illegal character NUL\")\n\t\tcase r >= utf8.RuneSelf:\n\t\t\t// not ASCII\n\t\t\tr, w = utf8.DecodeRune(s.src[s.rdOffset:])\n\t\t\tif r == utf8.RuneError && w == 1 {\n\t\t\t\ts.error(s.offset, \"illegal UTF-8 encoding\")\n\t\t\t} else if r == bom && s.offset > 0 {\n\t\t\t\ts.error(s.offset, \"illegal byte order mark\")\n\t\t\t}\n\t\t}\n\t\ts.rdOffset += w\n\t\ts.ch = r\n\t} else {\n\t\ts.offset = len(s.src)\n\t\tif s.ch == '\\n' {\n\t\t\ts.lineOffset = s.offset\n\t\t\ts.file.AddLine(s.offset)\n\t\t}\n\t\ts.ch = -1 // eof\n\t}\n}\n\n// peek returns the byte following the most recently read character without\n// advancing the scanner. If the scanner is at EOF, peek returns 0.\nfunc (s *Scanner) peek() byte {\n\tif s.rdOffset < len(s.src) {\n\t\treturn s.src[s.rdOffset]\n\t}\n\treturn 0\n}\n\n// A Mode value is a set of flags (or 0).\n// They control scanner behavior.\ntype Mode uint\n\nconst (\n\t// ScanComments - return comments as COMMENT tokens\n\tScanComments    Mode = 1 << iota\n\tdontInsertSemis      // do not automatically insert semicolons - for testing only\n)\n\n// Init prepares the scanner s to tokenize the text src by setting the\n// scanner at the beginning of src. The scanner uses the file set file\n// for position information and it adds line information for each line.\n// It is ok to re-use the same file when re-scanning the same file as\n// line information which is already present is ignored. Init causes a\n// panic if the file size does not match the src size.\n//\n// Calls to Scan will invoke the error handler err if they encounter a\n// syntax error and err is not nil. Also, for each error encountered,\n// the Scanner field ErrorCount is incremented by one. The mode parameter\n// determines how comments are handled.\n//\n// Note that Init may call err if there is an error in the first character\n// of the file.\nfunc (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) {\n\t// Explicitly initialize all fields since a scanner may be reused.\n\tif file.Size() != len(src) {\n\t\tpanic(fmt.Sprintf(\"file size (%d) does not match src len (%d)\", file.Size(), len(src)))\n\t}\n\ts.InitEx(file, src, 0, err, mode)\n}\n\n// InitEx init the scanner with an offset (this means src[offset:] is all the code to scan).\nfunc (s *Scanner) InitEx(file *token.File, src []byte, offset int, err ErrorHandler, mode Mode) {\n\ts.file = file\n\ts.dir, _ = filepath.Split(file.Name())\n\ts.src = src\n\ts.err = err\n\ts.mode = mode\n\n\ts.ch = ' '\n\ts.offset = 0\n\ts.rdOffset = offset\n\ts.lineOffset = 0\n\ts.insertSemi = false\n\ts.ErrorCount = 0\n\n\ts.next()\n\tif s.ch == bom {\n\t\ts.next() // ignore BOM at file beginning\n\t}\n}\n\nfunc (s *Scanner) CodeTo(end int) []byte {\n\treturn s.src[:end]\n}\n\nfunc (s *Scanner) error(offs int, msg string) {\n\tif s.err != nil {\n\t\ts.err(s.file.Position(s.file.Pos(offs)), msg)\n\t}\n\ts.ErrorCount++\n}\n\nfunc (s *Scanner) errorf(offs int, format string, args ...any) {\n\ts.error(offs, fmt.Sprintf(format, args...))\n}\n\nfunc (s *Scanner) scanComment() string {\n\t// initial '/' already consumed; s.ch == '/' || s.ch == '*'\n\toffs := s.offset - 1 // position of initial '/'\n\tnext := -1           // position immediately following the comment; < 0 means invalid comment\n\tnumCR := 0\n\n\tif s.ch == '/' {\n\t\t//-style comment\n\t\t// (the final '\\n' is not considered part of the comment)\n\t\ts.next()\n\t\tfor s.ch != '\\n' && s.ch >= 0 {\n\t\t\tif s.ch == '\\r' {\n\t\t\t\tnumCR++\n\t\t\t}\n\t\t\ts.next()\n\t\t}\n\t\t// if we are at '\\n', the position following the comment is afterwards\n\t\tnext = s.offset\n\t\tif s.ch == '\\n' {\n\t\t\tnext++\n\t\t}\n\t\tgoto exit\n\t}\n\t/*-style comment */\n\tif s.ch == '*' {\n\t\ts.next()\n\t\tfor s.ch >= 0 {\n\t\t\tch := s.ch\n\t\t\tif ch == '\\r' {\n\t\t\t\tnumCR++\n\t\t\t}\n\t\t\ts.next()\n\t\t\tif ch == '*' && s.ch == '/' {\n\t\t\t\ts.next()\n\t\t\t\tnext = s.offset\n\t\t\t\tgoto exit\n\t\t\t}\n\t\t}\n\t\ts.error(offs, \"comment not terminated\")\n\t\tgoto exit\n\t}\n\t// # - style comment, as default\n\ts.next()\n\tfor s.ch != '\\n' && s.ch >= 0 {\n\t\tif s.ch == '\\r' {\n\t\t\tnumCR++\n\t\t}\n\t\ts.next()\n\t}\n\t// if we are at '\\n', the position following the comment is afterwards\n\tnext = s.offset\n\tif s.ch == '\\n' {\n\t\tnext++\n\t}\n\nexit:\n\tlit := s.src[offs:s.offset]\n\n\t// On Windows, a (//-comment) line may end in \"\\r\\n\".\n\t// Remove the final '\\r' before analyzing the text for\n\t// line directives (matching the compiler). Remove any\n\t// other '\\r' afterwards (matching the pre-existing be-\n\t// havior of the scanner).\n\tif numCR > 0 && len(lit) >= 2 && lit[1] == '/' && lit[len(lit)-1] == '\\r' {\n\t\tlit = lit[:len(lit)-1]\n\t\tnumCR--\n\t}\n\n\t// interpret line directives\n\t// (//line directives must start at the beginning of the current line)\n\tif next >= 0 /* implies valid comment */ && (lit[1] == '*' || offs == s.lineOffset) && bytes.HasPrefix(lit[2:], prefix) {\n\t\ts.updateLineInfo(next, offs, lit)\n\t}\n\n\tif numCR > 0 {\n\t\tlit = stripCR(lit, lit[1] == '*')\n\t}\n\n\treturn string(lit)\n}\n\nvar prefix = []byte(\"line \")\n\n// updateLineInfo parses the incoming comment text at offset offs\n// as a line directive. If successful, it updates the line info table\n// for the position next per the line directive.\nfunc (s *Scanner) updateLineInfo(next, offs int, text []byte) {\n\t// extract comment text\n\tif text[1] == '*' {\n\t\ttext = text[:len(text)-2] // lop off trailing \"*/\"\n\t}\n\ttext = text[7:] // lop off leading \"//line \" or \"/*line \"\n\toffs += 7\n\n\ti, n, ok := trailingDigits(text)\n\tif i == 0 {\n\t\treturn // ignore (not a line directive)\n\t}\n\t// i > 0\n\n\tif !ok {\n\t\t// text has a suffix :xxx but xxx is not a number\n\t\ts.error(offs+i, \"invalid line number: \"+string(text[i:]))\n\t\treturn\n\t}\n\n\tvar line, col int\n\ti2, n2, ok2 := trailingDigits(text[:i-1])\n\tif ok2 {\n\t\t//line filename:line:col\n\t\ti, i2 = i2, i\n\t\tline, col = n2, n\n\t\tif col == 0 {\n\t\t\ts.error(offs+i2, \"invalid column number: \"+string(text[i2:]))\n\t\t\treturn\n\t\t}\n\t\ttext = text[:i2-1] // lop off \":col\"\n\t} else {\n\t\t//line filename:line\n\t\tline = n\n\t}\n\n\tif line == 0 {\n\t\ts.error(offs+i, \"invalid line number: \"+string(text[i:]))\n\t\treturn\n\t}\n\n\t// If we have a column (//line filename:line:col form),\n\t// an empty filename means to use the previous filename.\n\tfilename := string(text[:i-1]) // lop off \":line\", and trim white space\n\tif filename == \"\" && ok2 {\n\t\tfilename = s.file.Position(s.file.Pos(offs)).Filename\n\t} else if filename != \"\" {\n\t\t// Put a relative filename in the current directory.\n\t\t// This is for compatibility with earlier releases.\n\t\t// See issue 26671.\n\t\tfilename = filepath.Clean(filename)\n\t\tif !filepath.IsAbs(filename) {\n\t\t\tfilename = filepath.Join(s.dir, filename)\n\t\t}\n\t}\n\n\ts.file.AddLineColumnInfo(next, filename, line, col)\n}\n\nfunc trailingDigits(text []byte) (int, int, bool) {\n\ti := bytes.LastIndexByte(text, ':') // look from right (Windows filenames may contain ':')\n\tif i < 0 {\n\t\treturn 0, 0, false // no \":\"\n\t}\n\t// i >= 0\n\tn, err := strconv.ParseUint(string(text[i+1:]), 10, 0)\n\treturn i + 1, int(n), err == nil\n}\n\nfunc (s *Scanner) findLineEnd() bool {\n\t// initial '/' already consumed\n\n\tdefer func(offs int) {\n\t\t// reset scanner state to where it was upon calling findLineEnd\n\t\ts.ch = '/'\n\t\ts.offset = offs\n\t\ts.rdOffset = offs + 1\n\t\ts.next() // consume initial '/' again\n\t}(s.offset - 1)\n\n\t// read ahead until a newline, EOF, or non-comment token is found\n\tfor s.ch == '/' || s.ch == '*' {\n\t\tif s.ch == '/' {\n\t\t\t//-style comment always contains a newline\n\t\t\treturn true\n\t\t}\n\t\t/*-style comment: look for newline */\n\t\ts.next()\n\t\tfor s.ch >= 0 {\n\t\t\tch := s.ch\n\t\t\tif ch == '\\n' {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\ts.next()\n\t\t\tif ch == '*' && s.ch == '/' {\n\t\t\t\ts.next()\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\ts.skipWhitespace() // s.insertSemi is set\n\t\tif s.ch < 0 || s.ch == '\\n' {\n\t\t\treturn true\n\t\t}\n\t\tif s.ch != '/' {\n\t\t\t// non-comment token\n\t\t\treturn false\n\t\t}\n\t\ts.next() // consume '/'\n\t}\n\n\treturn false\n}\n\nfunc isLetter(ch rune) bool {\n\treturn 'a' <= lower(ch) && lower(ch) <= 'z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)\n}\n\nfunc isDigit(ch rune) bool {\n\treturn isDecimal(ch) || ch >= utf8.RuneSelf && unicode.IsDigit(ch)\n}\n\nfunc (s *Scanner) scanIdentifier() string {\n\toffs := s.offset\n\tfor isLetter(s.ch) || isDigit(s.ch) {\n\t\ts.next()\n\t}\n\treturn string(s.src[offs:s.offset])\n}\n\nfunc digitVal(ch rune) int {\n\tswitch {\n\tcase '0' <= ch && ch <= '9':\n\t\treturn int(ch - '0')\n\tcase 'a' <= lower(ch) && lower(ch) <= 'f':\n\t\treturn int(lower(ch) - 'a' + 10)\n\t}\n\treturn 16 // larger than any legal digit val\n}\n\nfunc lower(ch rune) rune     { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter\nfunc isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' }\nfunc isHex(ch rune) bool     { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f' }\n\n// digits accepts the sequence { digit | '_' }.\n// If base <= 10, digits accepts any decimal digit but records\n// the offset (relative to the source start) of a digit >= base\n// in *invalid, if *invalid < 0.\n// digits returns a bitset describing whether the sequence contained\n// digits (bit 0 is set), or separators '_' (bit 1 is set).\nfunc (s *Scanner) digits(base int, invalid *int) (digsep int) {\n\tif base <= 10 {\n\t\tmax := rune('0' + base)\n\t\tfor isDecimal(s.ch) || s.ch == '_' {\n\t\t\tds := 1\n\t\t\tif s.ch == '_' {\n\t\t\t\tds = 2\n\t\t\t} else if s.ch >= max && *invalid < 0 {\n\t\t\t\t*invalid = int(s.offset) // record invalid rune offset\n\t\t\t}\n\t\t\tdigsep |= ds\n\t\t\ts.next()\n\t\t}\n\t} else {\n\t\tfor isHex(s.ch) || s.ch == '_' {\n\t\t\tds := 1\n\t\t\tif s.ch == '_' {\n\t\t\t\tds = 2\n\t\t\t}\n\t\t\tdigsep |= ds\n\t\t\ts.next()\n\t\t}\n\t}\n\treturn\n}\n\nfunc (s *Scanner) scanNumber() (token.Token, string) {\n\toffs := s.offset\n\ttok := token.ILLEGAL\n\n\tbase := 10        // number base\n\tprefix := rune(0) // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b'\n\tdigsep := 0       // bit 0: digit present, bit 1: '_' present\n\tinvalid := -1     // index of invalid digit in literal, or < 0\n\n\t// integer part\n\tif s.ch != '.' {\n\t\ttok = token.INT\n\t\tif s.ch == '0' {\n\t\t\ts.next()\n\t\t\tswitch lower(s.ch) {\n\t\t\tcase 'x':\n\t\t\t\ts.next()\n\t\t\t\tbase, prefix = 16, 'x'\n\t\t\tcase 'o':\n\t\t\t\ts.next()\n\t\t\t\tbase, prefix = 8, 'o'\n\t\t\tcase 'b':\n\t\t\t\ts.next()\n\t\t\t\tbase, prefix = 2, 'b'\n\t\t\tdefault:\n\t\t\t\tbase, prefix = 8, '0'\n\t\t\t\tdigsep = 1 // leading 0\n\t\t\t}\n\t\t}\n\t\tdigsep |= s.digits(base, &invalid)\n\t}\n\n\t// fractional part\n\tif s.ch == '.' {\n\t\ttok = token.FLOAT\n\t\tif prefix == 'o' || prefix == 'b' {\n\t\t\ts.error(s.offset, \"invalid radix point in \"+litname(prefix))\n\t\t}\n\t\ts.next()\n\t\tdigsep |= s.digits(base, &invalid)\n\t}\n\n\tif digsep&1 == 0 {\n\t\ts.error(s.offset, litname(prefix)+\" has no digits\")\n\t}\n\n\t// exponent\n\tif e := lower(s.ch); e == 'e' || e == 'p' {\n\t\tswitch {\n\t\tcase e == 'e' && prefix != 0 && prefix != '0':\n\t\t\ts.errorf(s.offset, \"%q exponent requires decimal mantissa\", s.ch)\n\t\tcase e == 'p' && prefix != 'x':\n\t\t\ts.errorf(s.offset, \"%q exponent requires hexadecimal mantissa\", s.ch)\n\t\t}\n\t\ts.next()\n\t\ttok = token.FLOAT\n\t\tif s.ch == '+' || s.ch == '-' {\n\t\t\ts.next()\n\t\t}\n\t\tds := s.digits(10, nil)\n\t\tdigsep |= ds\n\t\tif ds&1 == 0 {\n\t\t\ts.error(s.offset, \"exponent has no digits\")\n\t\t}\n\t} else if prefix == 'x' && tok == token.FLOAT {\n\t\ts.error(s.offset, \"hexadecimal mantissa requires a 'p' exponent\")\n\t}\n\n\tif isLetter(s.ch) {\n\t\tid := s.scanIdentifier()\n\t\tswitch id {\n\t\tcase \"i\":\n\t\t\ttok = token.IMAG\n\t\tcase \"r\":\n\t\t\ttok = token.RAT\n\t\tdefault:\n\t\t\ts.peekTok, s.peekLit = token.UNIT, id\n\t\t}\n\t}\n\n\tlit := string(s.src[offs : s.offset-len(s.peekLit)])\n\tif tok == token.INT && invalid >= 0 {\n\t\ts.errorf(invalid, \"invalid digit %q in %s\", lit[invalid-offs], litname(prefix))\n\t}\n\tif digsep&2 != 0 {\n\t\tif i := invalidSep(lit); i >= 0 {\n\t\t\ts.error(offs+i, \"'_' must separate successive digits\")\n\t\t}\n\t}\n\n\treturn tok, lit\n}\n\nfunc litname(prefix rune) string {\n\tswitch prefix {\n\tcase 'x':\n\t\treturn \"hexadecimal literal\"\n\tcase 'o', '0':\n\t\treturn \"octal literal\"\n\tcase 'b':\n\t\treturn \"binary literal\"\n\t}\n\treturn \"decimal literal\"\n}\n\n// invalidSep returns the index of the first invalid separator in x, or -1.\nfunc invalidSep(x string) int {\n\tx1 := ' ' // prefix char, we only care if it's 'x'\n\td := '.'  // digit, one of '_', '0' (a digit), or '.' (anything else)\n\ti := 0\n\n\t// a prefix counts as a digit\n\tif len(x) >= 2 && x[0] == '0' {\n\t\tx1 = lower(rune(x[1]))\n\t\tif x1 == 'x' || x1 == 'o' || x1 == 'b' {\n\t\t\td = '0'\n\t\t\ti = 2\n\t\t}\n\t}\n\n\t// mantissa and exponent\n\tfor ; i < len(x); i++ {\n\t\tp := d // previous digit\n\t\td = rune(x[i])\n\t\tswitch {\n\t\tcase d == '_':\n\t\t\tif p != '0' {\n\t\t\t\treturn i\n\t\t\t}\n\t\tcase isDecimal(d) || x1 == 'x' && isHex(d):\n\t\t\td = '0'\n\t\tdefault:\n\t\t\tif p == '_' {\n\t\t\t\treturn i - 1\n\t\t\t}\n\t\t\td = '.'\n\t\t}\n\t}\n\tif d == '_' {\n\t\treturn len(x) - 1\n\t}\n\n\treturn -1\n}\n\n// scanEscape parses an escape sequence where rune is the accepted\n// escaped quote. In case of a syntax error, it stops at the offending\n// character (without consuming it) and returns false. Otherwise\n// it returns true.\nfunc (s *Scanner) scanEscape(quote rune) bool {\n\toffs := s.offset\n\n\tvar n int\n\tvar base, max uint32\n\tswitch s.ch {\n\tcase 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\\\', quote:\n\t\ts.next()\n\t\treturn true\n\tcase '0', '1', '2', '3', '4', '5', '6', '7':\n\t\tn, base, max = 3, 8, 255\n\tcase 'x':\n\t\ts.next()\n\t\tn, base, max = 2, 16, 255\n\tcase 'u':\n\t\ts.next()\n\t\tn, base, max = 4, 16, unicode.MaxRune\n\tcase 'U':\n\t\ts.next()\n\t\tn, base, max = 8, 16, unicode.MaxRune\n\tdefault:\n\t\tmsg := \"unknown escape sequence\"\n\t\tif s.ch < 0 {\n\t\t\tmsg = \"escape sequence not terminated\"\n\t\t}\n\t\ts.error(offs, msg)\n\t\treturn false\n\t}\n\n\tvar x uint32\n\tfor n > 0 {\n\t\td := uint32(digitVal(s.ch))\n\t\tif d >= base {\n\t\t\tmsg := fmt.Sprintf(\"illegal character %#U in escape sequence\", s.ch)\n\t\t\tif s.ch < 0 {\n\t\t\t\tmsg = \"escape sequence not terminated\"\n\t\t\t}\n\t\t\ts.error(s.offset, msg)\n\t\t\treturn false\n\t\t}\n\t\tx = x*base + d\n\t\ts.next()\n\t\tn--\n\t}\n\n\tif x > max || 0xD800 <= x && x < 0xE000 {\n\t\ts.error(offs, \"escape sequence is invalid Unicode code point\")\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (s *Scanner) scanRune() string {\n\t// '\\'' opening already consumed\n\toffs := s.offset - 1\n\n\tvalid := true\n\tn := 0\n\tfor {\n\t\tch := s.ch\n\t\tif ch == '\\n' || ch < 0 {\n\t\t\t// only report error if we don't have one already\n\t\t\tif valid {\n\t\t\t\ts.error(offs, \"rune literal not terminated\")\n\t\t\t\tvalid = false\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\ts.next()\n\t\tif ch == '\\'' {\n\t\t\tbreak\n\t\t}\n\t\tn++\n\t\tif ch == '\\\\' {\n\t\t\tif !s.scanEscape('\\'') {\n\t\t\t\tvalid = false\n\t\t\t}\n\t\t\t// continue to read to closing quote\n\t\t}\n\t}\n\n\tif valid && n != 1 {\n\t\ts.error(offs, \"illegal rune literal\")\n\t}\n\n\treturn string(s.src[offs:s.offset])\n}\n\nfunc (s *Scanner) scanString() string {\n\t// '\"' opening already consumed\n\toffs := s.offset - 1\n\n\tfor {\n\t\tch := s.ch\n\t\tif ch == '\\n' || ch < 0 {\n\t\t\ts.error(offs, \"string literal not terminated\")\n\t\t\tbreak\n\t\t}\n\t\ts.next()\n\t\tif ch == '\"' {\n\t\t\tbreak\n\t\t}\n\t\tif ch == '\\\\' {\n\t\t\ts.scanEscape('\"')\n\t\t}\n\t}\n\n\treturn string(s.src[offs:s.offset])\n}\n\nfunc stripCR(b []byte, comment bool) []byte {\n\tc := make([]byte, len(b))\n\ti := 0\n\tfor j, ch := range b {\n\t\t// In a /*-style comment, don't strip \\r from *\\r/ (incl.\n\t\t// sequences of \\r from *\\r\\r...\\r/) since the resulting\n\t\t// */ would terminate the comment too early unless the \\r\n\t\t// is immediately following the opening /* in which case\n\t\t// it's ok because /*/ is not closed yet (issue #11151).\n\t\tif ch != '\\r' || comment && i > len(\"/*\") && c[i-1] == '*' && j+1 < len(b) && b[j+1] == '/' {\n\t\t\tc[i] = ch\n\t\t\ti++\n\t\t}\n\t}\n\treturn c[:i]\n}\n\nfunc (s *Scanner) scanRawString() string {\n\t// '`' opening already consumed\n\toffs := s.offset - 1\n\n\thasCR := false\n\tfor {\n\t\tch := s.ch\n\t\tif ch < 0 {\n\t\t\ts.error(offs, \"raw string literal not terminated\")\n\t\t\tbreak\n\t\t}\n\t\ts.next()\n\t\tif ch == '`' {\n\t\t\tbreak\n\t\t}\n\t\tif ch == '\\r' {\n\t\t\thasCR = true\n\t\t}\n\t}\n\n\tlit := s.src[offs:s.offset]\n\tif hasCR {\n\t\tlit = stripCR(lit, false)\n\t}\n\n\treturn string(lit)\n}\n\nfunc (s *Scanner) skipWhitespace() {\n\tfor s.ch == ' ' || s.ch == '\\t' || s.ch == '\\n' && !s.insertSemi || s.ch == '\\r' {\n\t\ts.next()\n\t}\n}\n\n// Helper functions for scanning multi-byte tokens such as >> += >>= .\n// Different routines recognize different length tok_i based on matches\n// of ch_i. If a token ends in '=', the result is tok1 or tok3\n// respectively. Otherwise, the result is tok0 if there was no other\n// matching character, or tok2 if the matching character was ch2.\n\nfunc (s *Scanner) switch2(tok0, tok1 token.Token) token.Token {\n\tif s.ch == '=' {\n\t\ts.next()\n\t\treturn tok1\n\t}\n\treturn tok0\n}\n\nfunc (s *Scanner) switch3(tok0, tok1 token.Token, ch2 rune, tok2 token.Token) token.Token {\n\tif s.ch == '=' {\n\t\ts.next()\n\t\treturn tok1\n\t}\n\tif s.ch == ch2 {\n\t\ts.next()\n\t\treturn tok2\n\t}\n\treturn tok0\n}\n\nfunc (s *Scanner) switch4(tok0, tok1 token.Token, ch2 rune, tok2, tok3 token.Token) token.Token {\n\tif s.ch == '=' {\n\t\ts.next()\n\t\treturn tok1\n\t}\n\tif s.ch == ch2 {\n\t\ts.next()\n\t\tif s.ch == '=' {\n\t\t\ts.next()\n\t\t\treturn tok3\n\t\t}\n\t\treturn tok2\n\t}\n\treturn tok0\n}\n\nfunc (s *Scanner) tokSEMICOLON() token.Token {\n\ts.nParen = 0\n\treturn token.SEMICOLON\n}\n\n// Scan scans the next token and returns the token position, the token,\n// and its literal string if applicable. The source end is indicated by\n// token.EOF.\n//\n// If the returned token is a literal (token.IDENT, token.INT, token.FLOAT,\n// token.IMAG, token.CHAR, token.STRING) or token.COMMENT, the literal string\n// has the corresponding value.\n//\n// If the returned token is a keyword, the literal string is the keyword.\n//\n// If the returned token is token.SEMICOLON, the corresponding\n// literal string is \";\" if the semicolon was present in the source,\n// and \"\\n\" if the semicolon was inserted because of a newline or\n// at EOF.\n//\n// If the returned token is token.ILLEGAL, the literal string is the\n// offending character.\n//\n// In all other cases, Scan returns an empty literal string.\n//\n// For more tolerant parsing, Scan will return a valid token if\n// possible even if a syntax error was encountered. Thus, even\n// if the resulting token sequence contains no illegal tokens,\n// a client may not assume that no error occurred. Instead it\n// must check the scanner's ErrorCount or the number of calls\n// of the error handler, if there was one installed.\n//\n// Scan adds line information to the file added to the file\n// set with Init. Token positions are relative to that file\n// and thus relative to the file set.\nfunc (s *Scanner) Scan() (pos token.Pos, tok token.Token, lit string) {\nscanAgain:\n\tinsertSemi := false\n\tif s.peekTok != 0 {\n\t\tinsertSemi = true\n\t\tpos = s.file.Pos(s.offset - len(s.peekLit))\n\t\ttok, lit = s.peekTok, s.peekLit\n\t\ts.peekTok, s.peekLit = 0, \"\"\n\t\tgoto done\n\t}\n\n\ts.skipWhitespace()\n\n\t// current token start\n\tpos = s.file.Pos(s.offset)\n\n\t// determine token value\n\tswitch ch := s.ch; {\n\tcase isLetter(ch):\n\t\tlit = s.scanIdentifier()\n\t\tif len(lit) > 1 {\n\t\t\t// keywords are longer than one letter - avoid lookup otherwise\n\t\t\ttok = token.Lookup(lit)\n\t\t\tswitch tok {\n\t\t\tcase token.IDENT:\n\t\t\t\tinsertSemi = true\n\t\t\t\tif lit == \"py\" && s.ch == '\"' { // py\"...\"\n\t\t\t\t\ts.next()\n\t\t\t\t\ttok = token.PYSTRING\n\t\t\t\t\tlit = s.scanString()\n\t\t\t\t}\n\t\t\tcase token.BREAK, token.CONTINUE, token.FALLTHROUGH, token.RETURN:\n\t\t\t\tinsertSemi = true\n\t\t\t}\n\t\t} else if (lit == \"c\" || lit == \"C\") && s.ch == '\"' { // c\"...\"\n\t\t\ts.next()\n\t\t\tinsertSemi = true\n\t\t\ttok = token.CSTRING\n\t\t\tlit = s.scanString()\n\t\t} else {\n\t\t\tinsertSemi = true\n\t\t\ttok = token.IDENT\n\t\t}\n\tcase isDecimal(ch) || ch == '.' && isDecimal(rune(s.peek())):\n\t\tinsertSemi = true\n\t\ttok, lit = s.scanNumber()\n\tdefault:\n\t\ts.next() // always make progress\n\t\tswitch ch {\n\t\tcase -1:\n\t\t\tif s.insertSemi {\n\t\t\t\ts.insertSemi = false // EOF consumed\n\t\t\t\treturn pos, s.tokSEMICOLON(), \"\\n\"\n\t\t\t}\n\t\t\ttok = token.EOF\n\t\tcase '\\n':\n\t\t\t// we only reach here if s.insertSemi was\n\t\t\t// set in the first place and exited early\n\t\t\t// from s.skipWhitespace()\n\t\t\ts.insertSemi = false // newline consumed\n\t\t\treturn pos, s.tokSEMICOLON(), \"\\n\"\n\t\tcase '\"':\n\t\t\tinsertSemi = true\n\t\t\ttok = token.STRING\n\t\t\tlit = s.scanString()\n\t\tcase '\\'':\n\t\t\tinsertSemi = true\n\t\t\ttok = token.CHAR\n\t\t\tlit = s.scanRune()\n\t\tcase '`':\n\t\t\tinsertSemi = true\n\t\t\ttok = token.STRING\n\t\t\tlit = s.scanRawString()\n\t\tcase ':':\n\t\t\ttok = s.switch2(token.COLON, token.DEFINE)\n\t\tcase '.':\n\t\t\t// fractions starting with a '.' are handled by outer switch\n\t\t\tif s.ch == '.' && s.peek() == '.' {\n\t\t\t\ts.next()\n\t\t\t\ts.next() // consume last '.'\n\t\t\t\tif s.nParen == 0 {\n\t\t\t\t\tinsertSemi = true\n\t\t\t\t}\n\t\t\t\ttok = token.ELLIPSIS\n\t\t\t} else {\n\t\t\t\ttok = token.PERIOD\n\t\t\t\tif ch := ('a' - 'A') | s.ch; 'a' <= ch && ch <= 'z' {\n\t\t\t\t\ts.peekTok, s.peekLit = token.IDENT, s.scanIdentifier()\n\t\t\t\t}\n\t\t\t}\n\t\tcase ',':\n\t\t\ttok = token.COMMA\n\t\tcase ';':\n\t\t\ttok = s.tokSEMICOLON()\n\t\t\tlit = \";\"\n\t\tcase '(':\n\t\t\ts.nParen++\n\t\t\ttok = token.LPAREN\n\t\tcase ')':\n\t\t\ts.nParen--\n\t\t\tinsertSemi = true\n\t\t\ttok = token.RPAREN\n\t\tcase '[':\n\t\t\ttok = token.LBRACK\n\t\tcase ']':\n\t\t\tinsertSemi = true\n\t\t\ttok = token.RBRACK\n\t\tcase '{':\n\t\t\ttok = token.LBRACE\n\t\tcase '}':\n\t\t\tinsertSemi = true\n\t\t\ttok = token.RBRACE\n\t\tcase '+':\n\t\t\ttok = s.switch3(token.ADD, token.ADD_ASSIGN, '+', token.INC)\n\t\t\tif tok == token.INC {\n\t\t\t\tinsertSemi = true\n\t\t\t}\n\t\tcase '-':\n\t\t\tif s.ch == '>' { // ->\n\t\t\t\ts.next()\n\t\t\t\ttok = token.SRARROW\n\t\t\t} else { // -- -=\n\t\t\t\ttok = s.switch3(token.SUB, token.SUB_ASSIGN, '-', token.DEC)\n\t\t\t\tif tok == token.DEC {\n\t\t\t\t\tinsertSemi = true\n\t\t\t\t}\n\t\t\t}\n\t\tcase '*':\n\t\t\ttok = s.switch2(token.MUL, token.MUL_ASSIGN)\n\t\tcase '#':\n\t\t\tif s.insertSemi {\n\t\t\t\ts.ch = '#'\n\t\t\t\ts.offset = s.file.Offset(pos)\n\t\t\t\ts.rdOffset = s.offset + 1\n\t\t\t\ts.insertSemi = false // newline consumed\n\t\t\t\treturn pos, s.tokSEMICOLON(), \"\\n\"\n\t\t\t}\n\t\t\tcomment := s.scanComment()\n\t\t\tif s.mode&ScanComments == 0 {\n\t\t\t\t// skip comment\n\t\t\t\ts.insertSemi = false // newline consumed\n\t\t\t\tgoto scanAgain\n\t\t\t}\n\t\t\ttok = token.COMMENT\n\t\t\tlit = comment\n\t\tcase '/':\n\t\t\tif s.ch == '/' || s.ch == '*' {\n\t\t\t\t// comment\n\t\t\t\tif s.insertSemi && s.findLineEnd() {\n\t\t\t\t\t// reset position to the beginning of the comment\n\t\t\t\t\ts.ch = '/'\n\t\t\t\t\ts.offset = s.file.Offset(pos)\n\t\t\t\t\ts.rdOffset = s.offset + 1\n\t\t\t\t\ts.insertSemi = false // newline consumed\n\t\t\t\t\treturn pos, s.tokSEMICOLON(), \"\\n\"\n\t\t\t\t}\n\t\t\t\tcomment := s.scanComment()\n\t\t\t\tif s.mode&ScanComments == 0 {\n\t\t\t\t\t// skip comment\n\t\t\t\t\ts.insertSemi = false // newline consumed\n\t\t\t\t\tgoto scanAgain\n\t\t\t\t}\n\t\t\t\ttok = token.COMMENT\n\t\t\t\tlit = comment\n\t\t\t} else {\n\t\t\t\ttok = s.switch2(token.QUO, token.QUO_ASSIGN)\n\t\t\t}\n\t\tcase '%':\n\t\t\ttok = s.switch2(token.REM, token.REM_ASSIGN)\n\t\tcase '^':\n\t\t\ttok = s.switch2(token.XOR, token.XOR_ASSIGN)\n\t\tcase '<':\n\t\t\tswitch s.ch {\n\t\t\tcase '-': // <-\n\t\t\t\ts.next()\n\t\t\t\ttok = token.ARROW\n\t\t\tcase '>': // <>\n\t\t\t\ts.next()\n\t\t\t\ttok = token.BIDIARROW\n\t\t\tdefault: // < <= << <<=\n\t\t\t\ttok = s.switch4(token.LSS, token.LEQ, '<', token.SHL, token.SHL_ASSIGN)\n\t\t\t}\n\t\tcase '>':\n\t\t\ttok = s.switch4(token.GTR, token.GEQ, '>', token.SHR, token.SHR_ASSIGN)\n\t\tcase '=':\n\t\t\ttok = s.switch3(token.ASSIGN, token.EQL, '>', token.DRARROW)\n\t\tcase '!':\n\t\t\ttok = s.switch2(token.NOT, token.NEQ)\n\t\t\tif tok == token.NOT {\n\t\t\t\tinsertSemi = true\n\t\t\t}\n\t\tcase '&':\n\t\t\tif s.ch == '^' {\n\t\t\t\ts.next()\n\t\t\t\ttok = s.switch2(token.AND_NOT, token.AND_NOT_ASSIGN)\n\t\t\t} else {\n\t\t\t\ttok = s.switch3(token.AND, token.AND_ASSIGN, '&', token.LAND)\n\t\t\t}\n\t\tcase '|':\n\t\t\ttok = s.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR)\n\t\tcase '?':\n\t\t\ttok = token.QUESTION\n\t\t\tinsertSemi = true\n\t\tcase '$':\n\t\t\ttok = token.ENV\n\t\tcase '~':\n\t\t\ttok = token.TILDE\n\t\tcase '@':\n\t\t\ttok = token.AT\n\t\tdefault:\n\t\t\t// next reports unexpected BOMs - don't repeat\n\t\t\tif ch != bom {\n\t\t\t\ts.errorf(s.file.Offset(pos), \"illegal character %#U\", ch)\n\t\t\t}\n\t\t\tinsertSemi = s.insertSemi // preserve insertSemi info\n\t\t\ttok = token.ILLEGAL\n\t\t\tlit = string(ch)\n\t\t}\n\t}\n\ndone:\n\tif s.mode&dontInsertSemis == 0 {\n\t\ts.insertSemi = insertSemi\n\t}\n\treturn\n}\n"
  },
  {
    "path": "test/classfile.go",
    "content": "/*\n * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage test\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nconst (\n\tGopPackage = true\n)\n\n// -----------------------------------------------------------------------------\n\ntype testingT = testing.T\n\n// Case represents an XGo testcase.\ntype Case struct {\n\tt *testingT\n}\n\nfunc (p *Case) initCase(t *testing.T) {\n\tp.t = t\n}\n\n// T returns the *testing.T object .\nfunc (p Case) T() *testing.T { return p.t }\n\n// Run runs f as a subtest of t called name. It runs f in a separate goroutine\n// and blocks until f returns or calls t.Parallel to become a parallel test.\n// Run reports whether f succeeded (or at least did not fail before calling t.Parallel).\nfunc (p Case) Run(name string, f func(t *testing.T)) bool {\n\treturn p.t.Run(name, f)\n}\n\n// Gopt_Case_TestMain is required by XGo compiler as the test case entry.\nfunc Gopt_Case_TestMain(c interface{ initCase(t *testing.T) }, t *testing.T) {\n\tc.initCase(t)\n\tc.(interface{ Main() }).Main()\n}\n\n// -----------------------------------------------------------------------------\n\n// App represents an XGo testing main application.\ntype App struct {\n\tm *testing.M\n}\n\nfunc (p *App) initApp(m *testing.M) {\n\tp.m = m\n}\n\n// M returns the *testing.M object.\nfunc (p App) M() *testing.M { return p.m }\n\n// Gopt_App_TestMain is required by XGo compiler as the entry of an XGo testing project.\nfunc Gopt_App_TestMain(app interface{ initApp(m *testing.M) }, m *testing.M) {\n\tapp.initApp(m)\n\tif me, ok := app.(interface{ MainEntry() }); ok {\n\t\tme.MainEntry()\n\t}\n\tos.Exit(m.Run())\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "token/internal/tokenutil/lines_go118.go",
    "content": "//go:build !go1.19\n// +build !go1.19\n\n/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tokenutil\n\nimport (\n\t\"go/token\"\n\t\"sync\"\n\t\"unsafe\"\n)\n\n// -----------------------------------------------------------------------------\n// File\n\n// A File is a handle for a file belonging to a FileSet.\n// A File has a name, size, and line offset table.\ntype File struct {\n\tset  *token.FileSet\n\tname string // file name as provided to AddFile\n\tbase int    // Pos value range for this file is [base...base+size]\n\tsize int    // file size as provided to AddFile\n\n\t// lines and infos are protected by mutex\n\tmutex sync.Mutex\n\tlines []int // lines contains the offset of the first character for each line (the first entry is always 0)\n\tinfos []lineInfo\n}\n\n// A lineInfo object describes alternative file, line, and column\n// number information (such as provided via a //line directive)\n// for a given file offset.\ntype lineInfo struct {\n\t// fields are exported to make them accessible to gob\n\tOffset       int\n\tFilename     string\n\tLine, Column int\n}\n\n// Lines returns the effective line offset table of the form described by SetLines.\n// Callers must not mutate the result.\nfunc (f *File) Lines() []int {\n\tf.mutex.Lock()\n\tlines := f.lines\n\tf.mutex.Unlock()\n\treturn lines\n}\n\n// Lines returns the effective line offset table of the form described by SetLines.\n// Callers must not mutate the result.\nfunc Lines(f *token.File) []int {\n\tfile := (*File)(unsafe.Pointer(f))\n\treturn file.Lines()\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "token/internal/tokenutil/lines_go120.go",
    "content": "//go:build go1.19 && !go1.21\n// +build go1.19,!go1.21\n\n/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tokenutil\n\nimport (\n\t\"go/token\"\n\t\"sync\"\n\t\"unsafe\"\n)\n\n// -----------------------------------------------------------------------------\n// File\n\n// A File is a handle for a file belonging to a FileSet.\n// A File has a name, size, and line offset table.\ntype File struct {\n\tname string // file name as provided to AddFile\n\tbase int    // Pos value range for this file is [base...base+size]\n\tsize int    // file size as provided to AddFile\n\n\t// lines and infos are protected by mutex\n\tmutex sync.Mutex\n\tlines []int // lines contains the offset of the first character for each line (the first entry is always 0)\n\tinfos []lineInfo\n}\n\n// A lineInfo object describes alternative file, line, and column\n// number information (such as provided via a //line directive)\n// for a given file offset.\ntype lineInfo struct {\n\t// fields are exported to make them accessible to gob\n\tOffset       int\n\tFilename     string\n\tLine, Column int\n}\n\n// Lines returns the effective line offset table of the form described by SetLines.\n// Callers must not mutate the result.\nfunc (f *File) Lines() []int {\n\tf.mutex.Lock()\n\tlines := f.lines\n\tf.mutex.Unlock()\n\treturn lines\n}\n\n// Lines returns the effective line offset table of the form described by SetLines.\n// Callers must not mutate the result.\nfunc Lines(f *token.File) []int {\n\tfile := (*File)(unsafe.Pointer(f))\n\treturn file.Lines()\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "token/internal/tokenutil/lines_go121.go",
    "content": "//go:build go1.21\n// +build go1.21\n\n/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tokenutil\n\nimport (\n\t\"go/token\"\n)\n\n// Lines returns the effective line offset table of the form described by SetLines.\n// Callers must not mutate the result.\nfunc Lines(f *token.File) []int {\n\treturn f.Lines()\n}\n"
  },
  {
    "path": "token/internal/tokenutil/lines_test.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tokenutil\n\nimport (\n\t\"go/token\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestLines(t *testing.T) {\n\tfset := token.NewFileSet()\n\tf := fset.AddFile(\"foo.go\", 100, 100)\n\tlines := []int{0, 10, 50}\n\tf.SetLines(lines)\n\tret := Lines(f)\n\tif !reflect.DeepEqual(ret, lines) {\n\t\tt.Fatal(\"TestLines failed:\", ret)\n\t}\n}\n"
  },
  {
    "path": "token/token.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package token defines constants representing the lexical tokens of the XGo\n// programming language and basic operations on tokens (printing, predicates).\npackage token\n\nimport (\n\t\"go/token\"\n\t\"strconv\"\n\n\txtoken \"github.com/goplus/gogen/token\"\n)\n\n// Token is the set of lexical tokens of the XGo programming language.\ntype Token int\n\n// The list of tokens.\nconst (\n\t// Special tokens\n\tILLEGAL Token = iota\n\tEOF\n\tCOMMENT\n\n\tliteral_beg\n\t// Identifiers and basic type literals\n\t// (these tokens stand for classes of literals)\n\tIDENT  // main\n\tINT    // 12345\n\tFLOAT  // 123.45\n\tIMAG   // 123.45i\n\tCHAR   // 'a'\n\tSTRING // \"abc\"\n\tliteral_end\n\n\toperator_beg\n\t// Operators and delimiters\n\tADD // +\n\tSUB // -\n\tMUL // *\n\tQUO // /\n\tREM // %\n\n\tAND     // &\n\tOR      // |\n\tXOR     // ^\n\tSHL     // <<\n\tSHR     // >>\n\tAND_NOT // &^\n\n\tADD_ASSIGN // +=\n\tSUB_ASSIGN // -=\n\tMUL_ASSIGN // *=\n\tQUO_ASSIGN // /=\n\tREM_ASSIGN // %=\n\n\tAND_ASSIGN     // &=\n\tOR_ASSIGN      // |=\n\tXOR_ASSIGN     // ^=\n\tSHL_ASSIGN     // <<=\n\tSHR_ASSIGN     // >>=\n\tAND_NOT_ASSIGN // &^=\n\n\tLAND  // &&\n\tLOR   // ||\n\tARROW // <-\n\tINC   // ++\n\tDEC   // --\n\n\tEQL    // ==\n\tLSS    // <\n\tGTR    // >\n\tASSIGN // =\n\tNOT    // !\n\n\tNEQ      // !=\n\tLEQ      // <=\n\tGEQ      // >=\n\tDEFINE   // :=\n\tELLIPSIS // ...\n\n\tLPAREN // (\n\tLBRACK // [\n\tLBRACE // {\n\tCOMMA  // ,\n\tPERIOD // .\n\n\tRPAREN    // )\n\tRBRACK    // ]\n\tRBRACE    // }\n\tSEMICOLON // ;\n\tCOLON     // :\n\toperator_end\n\n\tkeyword_beg\n\t// Keywords\n\tBREAK\n\tCASE\n\tCHAN\n\tCONST\n\tCONTINUE\n\n\tDEFAULT\n\tDEFER\n\tELSE\n\tFALLTHROUGH\n\tFOR\n\n\tFUNC\n\tGO\n\tGOTO\n\tIF\n\tIMPORT\n\n\tINTERFACE\n\tMAP\n\tPACKAGE\n\tRANGE\n\tRETURN\n\n\tSELECT\n\tSTRUCT\n\tSWITCH\n\tTYPE\n\tVAR\n\tkeyword_end\n\n\tadditional_beg\n\tTILDE // additional tokens, handled in an ad-hoc manner\n\tadditional_op1\n\tadditional_op2\n\tadditional_op3\n\tadditional_end = additional_op3\n\n\tadditional_literal_beg = 96\n\tadditional_literal_end = 97\n\n\tAT  = additional_op2 // @\n\tENV = additional_op3 // ${name}\n\n\tPYSTRING = additional_literal_beg // py\"Hello\"\n\tUNIT     = additional_literal_end // 1m, 2.3s, 3ms, 4us, 5ns, 6.5m, 7h, 8d, 9w, 10y\n\n\tCSTRING  = literal_beg  // c\"Hello\"\n\tRAT      = literal_end  // 123.5r\n\tDRARROW  = operator_beg // => (double right arrow)\n\tQUESTION = operator_end // ?\n\n\tSRARROW   = Token(xtoken.SRARROW)   // -> (single right arrow) = additional_beg\n\tBIDIARROW = Token(xtoken.BIDIARROW) // <> (bidirectional arrow) = additional_op1\n\n\t// Deprecated: use DRARROW instead of RARROW\n\tRARROW = DRARROW\n)\n\nvar tokens = [...]string{\n\tILLEGAL: \"ILLEGAL\",\n\n\tEOF:     \"EOF\",\n\tCOMMENT: \"COMMENT\",\n\n\tIDENT:    \"IDENT\",\n\tINT:      \"INT\",\n\tFLOAT:    \"FLOAT\",\n\tIMAG:     \"IMAG\",\n\tCHAR:     \"CHAR\",\n\tSTRING:   \"STRING\",\n\tCSTRING:  \"CSTRING\",\n\tPYSTRING: \"PYSTRING\",\n\tRAT:      \"RAT\",\n\tUNIT:     \"UNIT\",\n\n\tADD: \"+\",\n\tSUB: \"-\",\n\tMUL: \"*\",\n\tQUO: \"/\",\n\tREM: \"%\",\n\n\tAND:     \"&\",\n\tOR:      \"|\",\n\tXOR:     \"^\",\n\tSHL:     \"<<\",\n\tSHR:     \">>\",\n\tAND_NOT: \"&^\",\n\n\tADD_ASSIGN: \"+=\",\n\tSUB_ASSIGN: \"-=\",\n\tMUL_ASSIGN: \"*=\",\n\tQUO_ASSIGN: \"/=\",\n\tREM_ASSIGN: \"%=\",\n\n\tAND_ASSIGN:     \"&=\",\n\tOR_ASSIGN:      \"|=\",\n\tXOR_ASSIGN:     \"^=\",\n\tSHL_ASSIGN:     \"<<=\",\n\tSHR_ASSIGN:     \">>=\",\n\tAND_NOT_ASSIGN: \"&^=\",\n\n\tLAND:  \"&&\",\n\tLOR:   \"||\",\n\tARROW: \"<-\",\n\tINC:   \"++\",\n\tDEC:   \"--\",\n\n\tEQL:    \"==\",\n\tLSS:    \"<\",\n\tGTR:    \">\",\n\tASSIGN: \"=\",\n\tNOT:    \"!\",\n\n\tNEQ:      \"!=\",\n\tLEQ:      \"<=\",\n\tGEQ:      \">=\",\n\tDEFINE:   \":=\",\n\tELLIPSIS: \"...\",\n\n\tLPAREN: \"(\",\n\tLBRACK: \"[\",\n\tLBRACE: \"{\",\n\tCOMMA:  \",\",\n\tPERIOD: \".\",\n\n\tRPAREN:    \")\",\n\tRBRACK:    \"]\",\n\tRBRACE:    \"}\",\n\tSEMICOLON: \";\",\n\tCOLON:     \":\",\n\tQUESTION:  \"?\",\n\tDRARROW:   \"=>\",\n\tSRARROW:   \"->\",\n\tBIDIARROW: \"<>\",\n\tENV:       \"$\",\n\tTILDE:     \"~\",\n\tAT:        \"@\",\n\n\tBREAK:    \"break\",\n\tCASE:     \"case\",\n\tCHAN:     \"chan\",\n\tCONST:    \"const\",\n\tCONTINUE: \"continue\",\n\n\tDEFAULT:     \"default\",\n\tDEFER:       \"defer\",\n\tELSE:        \"else\",\n\tFALLTHROUGH: \"fallthrough\",\n\tFOR:         \"for\",\n\n\tFUNC:   \"func\",\n\tGO:     \"go\",\n\tGOTO:   \"goto\",\n\tIF:     \"if\",\n\tIMPORT: \"import\",\n\n\tINTERFACE: \"interface\",\n\tMAP:       \"map\",\n\tPACKAGE:   \"package\",\n\tRANGE:     \"range\",\n\tRETURN:    \"return\",\n\n\tSELECT: \"select\",\n\tSTRUCT: \"struct\",\n\tSWITCH: \"switch\",\n\tTYPE:   \"type\",\n\tVAR:    \"var\",\n}\n\n// String returns the string corresponding to the token tok.\n// For operators, delimiters, and keywords the string is the actual\n// token character sequence (e.g., for the token ADD, the string is\n// \"+\"). For all other tokens the string corresponds to the token\n// constant name (e.g. for the token IDENT, the string is \"IDENT\").\nfunc (tok Token) String() string {\n\ts := \"\"\n\tif 0 <= tok && tok < Token(len(tokens)) {\n\t\ts = tokens[tok]\n\t}\n\tif s == \"\" {\n\t\ts = \"token(\" + strconv.Itoa(int(tok)) + \")\"\n\t}\n\treturn s\n}\n\n// A set of constants for precedence-based expression parsing.\n// Non-operators have lowest precedence, followed by operators\n// starting with precedence 1 up to unary operators. The highest\n// precedence serves as \"catch-all\" precedence for selector,\n// indexing, and other operator and delimiter tokens.\nconst (\n\tLowestPrec  = 0 // non-operators\n\tUnaryPrec   = 6\n\tHighestPrec = 7\n)\n\n// Precedence returns the operator precedence of the binary\n// operator op. If op is not a binary operator, the result\n// is LowestPrecedence.\nfunc (op Token) Precedence() int {\n\tswitch op {\n\tcase LOR:\n\t\treturn 1\n\tcase LAND:\n\t\treturn 2\n\tcase EQL, NEQ, LSS, LEQ, GTR, GEQ, SRARROW, BIDIARROW:\n\t\treturn 3\n\tcase ADD, SUB, OR, XOR:\n\t\treturn 4\n\tcase MUL, QUO, REM, SHL, SHR, AND, AND_NOT:\n\t\treturn 5\n\t}\n\treturn LowestPrec\n}\n\nvar keywords map[string]Token\n\nfunc init() {\n\tkeywords = make(map[string]Token)\n\tfor i := keyword_beg + 1; i < keyword_end; i++ {\n\t\tkeywords[tokens[i]] = i\n\t}\n}\n\n// Lookup maps an identifier to its keyword token or IDENT (if not a keyword).\nfunc Lookup(ident string) Token {\n\tif tok, is_keyword := keywords[ident]; is_keyword {\n\t\treturn tok\n\t}\n\treturn IDENT\n}\n\n// Predicates\n\n// IsLiteral returns true for tokens corresponding to identifiers\n// and basic type literals; it returns false otherwise.\nfunc (tok Token) IsLiteral() bool {\n\treturn literal_beg <= tok && tok <= literal_end || tok == PYSTRING\n}\n\n// IsOperator returns true for tokens corresponding to operators and\n// delimiters; it returns false otherwise.\nfunc (tok Token) IsOperator() bool {\n\treturn operator_beg <= tok && tok <= operator_end || tok >= additional_beg && tok <= additional_end\n}\n\n// IsKeyword returns true for tokens corresponding to keywords;\n// it returns false otherwise.\nfunc (tok Token) IsKeyword() bool {\n\treturn keyword_beg < tok && tok < keyword_end\n}\n\n// IsExported reports whether name starts with an upper-case letter.\nfunc IsExported(name string) bool {\n\treturn token.IsExported(name)\n}\n\n// IsKeyword reports whether name is a Go keyword, such as \"func\" or \"return\".\nfunc IsKeyword(name string) bool {\n\treturn token.IsKeyword(name)\n}\n\n// IsIdentifier reports whether name is a Go identifier, that is, a non-empty\n// string made up of letters, digits, and underscores, where the first character\n// is not a digit. Keywords are not identifiers.\nfunc IsIdentifier(name string) bool {\n\treturn token.IsIdentifier(name)\n}\n"
  },
  {
    "path": "token/token_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage token\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestArrowOp(t *testing.T) {\n\tif v := BIDIARROW.IsOperator(); !v {\n\t\tt.Fatal(\"BIDIARROW not op?\")\n\t}\n\tif v := SRARROW.IsOperator(); !v {\n\t\tt.Fatal(\"SRARROW not op?\")\n\t}\n\tif BIDIARROW.Precedence() != NEQ.Precedence() {\n\t\tt.Fatal(\"BIDIARROW.Precedence\")\n\t}\n\tif v := BIDIARROW.String(); v != \"<>\" {\n\t\tt.Fatal(\"BIDIARROW.String:\", v)\n\t}\n\tif v := SRARROW.String(); v != \"->\" {\n\t\tt.Fatal(\"SRARROW.String:\", v)\n\t}\n\tif v := AT.String(); v != \"@\" {\n\t\tt.Fatal(\"AT.String:\", v)\n\t}\n\tif v := (additional_end + 100).String(); v != \"token(191)\" {\n\t\tt.Fatal(\"token.String:\", v)\n\t}\n}\n\nfunc TestPrecedence(t *testing.T) {\n\tcases := map[Token]int{\n\t\tLOR:   1,\n\t\tLAND:  2,\n\t\tEQL:   3,\n\t\tSUB:   4,\n\t\tMUL:   5,\n\t\tARROW: LowestPrec,\n\t}\n\tfor op, prec := range cases {\n\t\tif v := op.Precedence(); v != prec {\n\t\t\tt.Fatal(\"Precedence:\", op, v)\n\t\t}\n\t}\n}\n\nfunc TestLookup(t *testing.T) {\n\tif v := Lookup(\"type\"); v != TYPE {\n\t\tt.Fatal(\"TestLookup type:\", v)\n\t} else if !v.IsKeyword() {\n\t\tt.Fatal(\"v.IsKeyword:\", v)\n\t}\n\tif v := Lookup(\"new\"); v != IDENT {\n\t\tt.Fatal(\"TestLookup new:\", v)\n\t} else if !v.IsLiteral() {\n\t\tt.Fatal(\"v.IsLiteral:\", v)\n\t}\n}\n\nfunc TestBasic(t *testing.T) {\n\tif !IsExported(\"Name\") {\n\t\tt.Fatal(\"IsExported\")\n\t}\n\tif !IsKeyword(\"func\") {\n\t\tt.Fatal(\"IsKeyword\")\n\t}\n\tif !IsIdentifier(\"new\") {\n\t\tt.Fatal(\"IsIdentifier\")\n\t}\n}\n\nfunc TestLines(t *testing.T) {\n\tfset := NewFileSet()\n\tf := fset.AddFile(\"foo.go\", 100, 100)\n\tlines := []int{0, 10, 50}\n\tf.SetLines(lines)\n\tret := Lines(f)\n\tif !reflect.DeepEqual(ret, lines) {\n\t\tt.Fatal(\"TestLines failed:\", ret)\n\t}\n}\n"
  },
  {
    "path": "token/types.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage token\n\nimport (\n\t\"go/token\"\n\n\t\"github.com/goplus/xgo/token/internal/tokenutil\"\n)\n\n// Pos is a compact encoding of a source position within a file set.\n// It can be converted into a Position for a more convenient, but much\n// larger, representation.\n//\n// The Pos value for a given file is a number in the range [base, base+size],\n// where base and size are specified when adding the file to the file set via\n// AddFile.\n//\n// To create the Pos value for a specific source offset (measured in bytes),\n// first add the respective file to the current file set using FileSet.AddFile\n// and then call File.Pos(offset) for that file. Given a Pos value p\n// for a specific file set fset, the corresponding Position value is\n// obtained by calling fset.Position(p).\n//\n// Pos values can be compared directly with the usual comparison operators:\n// If two Pos values p and q are in the same file, comparing p and q is\n// equivalent to comparing the respective source file offsets. If p and q\n// are in different files, p < q is true if the file implied by p was added\n// to the respective file set before the file implied by q.\ntype Pos = token.Pos\n\nconst (\n\t// NoPos - The zero value for Pos is NoPos; there is no file and line\n\t// information associated with it, and NoPos.IsValid() is false. NoPos\n\t// is always smaller than any other Pos value. The corresponding\n\t// Position value for NoPos is the zero value for Position.\n\tNoPos = token.NoPos\n)\n\n// Position describes an arbitrary source position\n// including the file, line, and column location.\n// A Position is valid if the line number is > 0.\ntype Position = token.Position\n\n// A File is a handle for a file belonging to a FileSet.\n// A File has a name, size, and line offset table.\ntype File = token.File\n\n// Lines returns the effective line offset table of the form described by SetLines.\n// Callers must not mutate the result.\nfunc Lines(f *File) []int {\n\treturn tokenutil.Lines(f)\n}\n\n// A FileSet represents a set of source files. Methods of file sets are\n// synchronized; multiple goroutines may invoke them concurrently.\ntype FileSet = token.FileSet\n\n// NewFileSet creates a new file set.\nfunc NewFileSet() *FileSet {\n\treturn token.NewFileSet()\n}\n"
  },
  {
    "path": "tool/_gendeps.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tool\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/goplus/mod/gopmod\"\n\t\"github.com/goplus/mod/modfetch\"\n\t\"github.com/goplus/xgo/parser\"\n\n\tastmod \"github.com/goplus/xgo/ast/mod\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc GenDepMods(mod *gopmod.Module, dir string, recursively bool) (ret map[string]struct{}, err error) {\n\tmodBase := mod.Path()\n\tret = make(map[string]struct{})\n\tfor _, r := range mod.Opt.Import {\n\t\tret[r.ClassfileMod] = struct{}{}\n\t}\n\terr = HandleDeps(mod, dir, recursively, func(pkgPath string) {\n\t\tmodPath, _ := modfetch.Split(pkgPath, modBase)\n\t\tif modPath != \"\" && modPath != modBase {\n\t\t\tret[modPath] = struct{}{}\n\t\t}\n\t})\n\treturn\n}\n\nfunc HandleDeps(mod *gopmod.Module, dir string, recursively bool, h func(pkgPath string)) (err error) {\n\tg := depsGen{\n\t\tdeps: astmod.Deps{HandlePkg: h},\n\t\tmod:  mod,\n\t\tfset: token.NewFileSet(),\n\t}\n\tif recursively {\n\t\terr = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {\n\t\t\tif err == nil && d.IsDir() {\n\t\t\t\tif strings.HasPrefix(d.Name(), \"_\") { // skip _\n\t\t\t\t\treturn filepath.SkipDir\n\t\t\t\t}\n\t\t\t\terr = g.gen(path)\n\t\t\t\tif err != nil {\n\t\t\t\t\tfmt.Fprintln(os.Stderr, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn err\n\t\t})\n\t} else {\n\t\terr = g.gen(dir)\n\t}\n\treturn\n}\n\ntype depsGen struct {\n\tdeps astmod.Deps\n\tmod  *gopmod.Module\n\tfset *token.FileSet\n}\n\nfunc (p depsGen) gen(dir string) (err error) {\n\tpkgs, err := parser.ParseDirEx(p.fset, dir, parser.Config{\n\t\tClassKind: p.mod.ClassKind,\n\t\tMode:      parser.ImportsOnly,\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tfor _, pkg := range pkgs {\n\t\tp.deps.Load(pkg, false)\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tool/build_install_run.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tool\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/goplus/xgo/x/gocmd\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc genFlags(flags []GenFlags) GenFlags {\n\tif flags != nil {\n\t\treturn flags[0]\n\t}\n\treturn 0\n}\n\n// -----------------------------------------------------------------------------\n\n// InstallDir installs an XGo package directory.\nfunc InstallDir(dir string, conf *Config, install *gocmd.InstallConfig, flags ...GenFlags) (err error) {\n\t_, _, err = GenGoEx(dir, conf, false, genFlags(flags))\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGo(dir, conf, false)`, -2, \"tool.GenGo\", dir, conf, false)\n\t}\n\treturn gocmd.Install(dir, install)\n}\n\n// InstallPkgPath installs an XGo package.\nfunc InstallPkgPath(workDir, pkgPath string, conf *Config, install *gocmd.InstallConfig, flags ...GenFlags) (err error) {\n\tlocalDir, recursively, err := GenGoPkgPathEx(workDir, pkgPath, conf, true, genFlags(flags))\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGoPkgPath(workDir, pkgPath, conf, true)`, -2, \"tool.GenGoPkgPath\", workDir, pkgPath, conf, true)\n\t}\n\told := chdir(localDir)\n\tdefer os.Chdir(old)\n\treturn gocmd.Install(cwdParam(recursively), install)\n}\n\nfunc cwdParam(recursively bool) string {\n\tif recursively {\n\t\treturn \"./...\"\n\t}\n\treturn \".\"\n}\n\n// InstallFiles installs specified XGo files.\nfunc InstallFiles(files []string, conf *Config, install *gocmd.InstallConfig) (err error) {\n\tfiles, err = GenGoFiles(\"\", files, conf)\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGoFiles(\"\", files, conf)`, -2, \"tool.GenGoFiles\", \"\", files, conf)\n\t}\n\treturn gocmd.InstallFiles(files, install)\n}\n\nfunc chdir(dir string) string {\n\told, err := os.Getwd()\n\tif err != nil {\n\t\tlog.Panicln(err)\n\t}\n\terr = os.Chdir(dir)\n\tif err != nil {\n\t\tlog.Panicln(err)\n\t}\n\treturn old\n}\n\n// -----------------------------------------------------------------------------\n\n// BuildDir builds an XGo package directory.\nfunc BuildDir(dir string, conf *Config, build *gocmd.BuildConfig, flags ...GenFlags) (err error) {\n\t_, _, err = GenGoEx(dir, conf, false, genFlags(flags))\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGo(dir, conf, false)`, -2, \"tool.GenGo\", dir, conf, false)\n\t}\n\treturn gocmd.Build(dir, build)\n}\n\n// BuildPkgPath builds an XGo package.\nfunc BuildPkgPath(workDir, pkgPath string, conf *Config, build *gocmd.BuildConfig, flags ...GenFlags) (err error) {\n\tlocalDir, recursively, err := GenGoPkgPathEx(workDir, pkgPath, conf, false, genFlags(flags))\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGoPkgPath(workDir, pkgPath, conf, false)`, -2, \"tool.GenGoPkgPath\", workDir, pkgPath, conf, false)\n\t}\n\told, mod := chdirAndMod(localDir)\n\tdefer restoreDirAndMod(old, mod)\n\treturn gocmd.Build(cwdParam(recursively), build)\n}\n\n// BuildFiles builds specified XGo files.\nfunc BuildFiles(files []string, conf *Config, build *gocmd.BuildConfig) (err error) {\n\tfiles, err = GenGoFiles(\"\", files, conf)\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGoFiles(\"\", files, conf)`, -2, \"tool.GenGoFiles\", \"\", files, conf)\n\t}\n\treturn gocmd.BuildFiles(files, build)\n}\n\nfunc chdirAndMod(dir string) (old string, mod os.FileMode) {\n\tmod = 0755\n\tif info, err := os.Stat(dir); err == nil {\n\t\tmod = info.Mode().Perm()\n\t}\n\tos.Chmod(dir, 0777)\n\told = chdir(dir)\n\treturn\n}\n\nfunc restoreDirAndMod(old string, mod os.FileMode) {\n\tos.Chmod(\".\", mod)\n\tos.Chdir(old)\n}\n\n// -----------------------------------------------------------------------------\n\n// If no go.mod and used XGo, use XGOROOT as buildDir.\nfunc getBuildDir(conf *Config) string {\n\tif conf != nil && conf.XGoDeps != nil && *conf.XGoDeps != 0 {\n\t\treturn conf.XGo.Root\n\t}\n\treturn \"\"\n}\n\n// RunDir runs an application from an XGo package directory.\nfunc RunDir(dir string, args []string, conf *Config, run *gocmd.RunConfig, flags ...GenFlags) (err error) {\n\t_, _, err = GenGoEx(dir, conf, false, genFlags(flags))\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGo(dir, conf, false)`, -2, \"tool.GenGo\", dir, conf, false)\n\t}\n\treturn gocmd.RunDir(getBuildDir(conf), dir, args, run)\n}\n\n// RunPkgPath runs an application from an XGo package.\nfunc RunPkgPath(pkgPath string, args []string, chDir bool, conf *Config, run *gocmd.RunConfig, flags ...GenFlags) (err error) {\n\tlocalDir, recursively, err := GenGoPkgPathEx(\"\", pkgPath, conf, true, genFlags(flags))\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGoPkgPath(\"\", pkgPath, conf, true)`, -2, \"tool.GenGoPkgPath\", \"\", pkgPath, conf, true)\n\t}\n\tif recursively {\n\t\treturn errors.NewWith(errors.New(\"can't use ... pattern for `xgo run` command\"), `recursively`, -1, \"\", recursively)\n\t}\n\tif chDir {\n\t\told := chdir(localDir)\n\t\tdefer os.Chdir(old)\n\t\tlocalDir = \".\"\n\t}\n\treturn gocmd.RunDir(\"\", localDir, args, run)\n}\n\n// RunFiles runs an application from specified XGo files.\nfunc RunFiles(autogen string, files []string, args []string, conf *Config, run *gocmd.RunConfig) (err error) {\n\tfiles, err = GenGoFiles(autogen, files, conf)\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGoFiles(autogen, files, conf)`, -2, \"tool.GenGoFiles\", autogen, files, conf)\n\t}\n\treturn gocmd.RunFiles(getBuildDir(conf), files, args, run)\n}\n\n// -----------------------------------------------------------------------------\n\n// TestDir tests an XGo package directory.\nfunc TestDir(dir string, conf *Config, test *gocmd.TestConfig, flags ...GenFlags) (err error) {\n\t_, _, err = GenGoEx(dir, conf, true, genFlags(flags))\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGo(dir, conf, true)`, -2, \"tool.GenGo\", dir, conf, true)\n\t}\n\treturn gocmd.Test(dir, test)\n}\n\n// TestPkgPath tests an XGo package.\nfunc TestPkgPath(workDir, pkgPath string, conf *Config, test *gocmd.TestConfig, flags ...GenFlags) (err error) {\n\tlocalDir, recursively, err := GenGoPkgPathEx(workDir, pkgPath, conf, false, genFlags(flags))\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGoPkgPath(workDir, pkgPath, conf, false)`, -2, \"tool.GenGoPkgPath\", workDir, pkgPath, conf, false)\n\t}\n\told, mod := chdirAndMod(localDir)\n\tdefer restoreDirAndMod(old, mod)\n\treturn gocmd.Test(cwdParam(recursively), test)\n}\n\n// TestFiles tests specified XGo files.\nfunc TestFiles(files []string, conf *Config, test *gocmd.TestConfig) (err error) {\n\tfiles, err = GenGoFiles(\"\", files, conf)\n\tif err != nil {\n\t\treturn errors.NewWith(err, `GenGoFiles(\"\", files, conf)`, -2, \"tool.GenGoFiles\", \"\", files, conf)\n\t}\n\treturn gocmd.TestFiles(files, test)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tool/gengo.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tool\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"syscall\"\n\n\t\"github.com/goplus/mod/modcache\"\n\t\"github.com/goplus/mod/modfetch\"\n\t\"github.com/goplus/mod/xgomod\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nconst (\n\ttestingGoFile    = \"_test\"\n\tautoGenFile      = \"xgo_autogen.go\"\n\tautoGenTestFile  = \"xgo_autogen_test.go\"\n\tautoGen2TestFile = \"xgo_autogen2_test.go\"\n)\n\ntype GenFlags int\n\nconst (\n\tGenFlagCheckOnly GenFlags = 1 << iota\n\tGenFlagSingleFile\n\tGenFlagPrintError\n\tGenFlagPrompt\n)\n\n// -----------------------------------------------------------------------------\n\n// GenGo generates xgo_autogen.go for an XGo package directory.\nfunc GenGo(dir string, conf *Config, genTestPkg bool) (string, bool, error) {\n\treturn GenGoEx(dir, conf, genTestPkg, 0)\n}\n\n// GenGoEx generates xgo_autogen.go for an XGo package directory.\nfunc GenGoEx(dir string, conf *Config, genTestPkg bool, flags GenFlags) (string, bool, error) {\n\trecursively := strings.HasSuffix(dir, \"/...\")\n\tif recursively {\n\t\tdir = dir[:len(dir)-4]\n\t}\n\treturn dir, recursively, genGoDir(dir, conf, genTestPkg, recursively, flags)\n}\n\nfunc genGoDir(dir string, conf *Config, genTestPkg, recursively bool, flags GenFlags) (err error) {\n\tif conf == nil {\n\t\tconf = new(Config)\n\t}\n\tif recursively {\n\t\tvar (\n\t\t\tlist errors.List\n\t\t\tfn   func(path string, d fs.DirEntry, err error) error\n\t\t)\n\t\tif flags&GenFlagSingleFile != 0 {\n\t\t\tfn = func(path string, d fs.DirEntry, err error) error {\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn genGoEntry(&list, path, d, conf, flags)\n\t\t\t}\n\t\t} else {\n\t\t\tfn = func(path string, d fs.DirEntry, err error) error {\n\t\t\t\tif err == nil && d.IsDir() {\n\t\t\t\t\tif strings.HasPrefix(d.Name(), \"_\") || (path != dir && hasMod(path)) { // skip _\n\t\t\t\t\t\treturn filepath.SkipDir\n\t\t\t\t\t}\n\t\t\t\t\tif e := genGoIn(path, conf, genTestPkg, flags); e != nil && notIgnNotated(e, conf) {\n\t\t\t\t\t\tif flags&GenFlagPrintError != 0 {\n\t\t\t\t\t\t\tfmt.Fprintln(os.Stderr, e)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlist.Add(e)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\terr = filepath.WalkDir(dir, fn)\n\t\tif err != nil {\n\t\t\treturn errors.NewWith(err, `filepath.WalkDir(dir, fn)`, -2, \"filepath.WalkDir\", dir, fn)\n\t\t}\n\t\treturn list.ToError()\n\t}\n\tif flags&GenFlagSingleFile != 0 {\n\t\tvar list errors.List\n\t\tvar entries, e = os.ReadDir(dir)\n\t\tif e != nil {\n\t\t\treturn errors.NewWith(e, `os.ReadDir(dir)`, -2, \"os.ReadDir\", dir)\n\t\t}\n\t\tfor _, d := range entries {\n\t\t\tgenGoEntry(&list, filepath.Join(dir, d.Name()), d, conf, flags)\n\t\t}\n\t\treturn list.ToError()\n\t}\n\tif e := genGoIn(dir, conf, genTestPkg, flags); e != nil && notIgnNotated(e, conf) {\n\t\tif (flags & GenFlagPrintError) != 0 {\n\t\t\tfmt.Fprintln(os.Stderr, e)\n\t\t}\n\t\terr = e\n\t}\n\treturn\n}\n\nfunc hasMod(dir string) bool {\n\t_, err := os.Lstat(dir + \"/go.mod\")\n\treturn err == nil\n}\n\nfunc notIgnNotated(e error, conf *Config) bool {\n\treturn !(conf != nil && conf.IgnoreNotatedError && IgnoreNotated(e))\n}\n\nfunc genGoEntry(list *errors.List, path string, d fs.DirEntry, conf *Config, flags GenFlags) error {\n\tfname := d.Name()\n\tif strings.HasPrefix(fname, \"_\") { // skip _\n\t\tif d.IsDir() {\n\t\t\treturn filepath.SkipDir\n\t\t}\n\t} else if !d.IsDir() && (strings.HasSuffix(fname, \".xgo\") || strings.HasSuffix(fname, \".gop\")) {\n\t\tif e := genGoSingleFile(path, 4, conf, flags); e != nil && notIgnNotated(e, conf) {\n\t\t\tif flags&GenFlagPrintError != 0 {\n\t\t\t\tfmt.Fprintln(os.Stderr, e)\n\t\t\t}\n\t\t\tlist.Add(e)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc genGoSingleFile(file string, extn int, conf *Config, flags GenFlags) (err error) {\n\tdir, fname := filepath.Split(file)\n\tautogen := dir + fname[:len(fname)-extn] + \"_autogen.go\"\n\tif (flags & GenFlagPrompt) != 0 {\n\t\tfmt.Fprintln(os.Stderr, \"GenGo\", file, \"...\")\n\t}\n\tout, err := LoadFiles(\".\", []string{file}, conf)\n\tif err != nil {\n\t\treturn errors.NewWith(err, `LoadFiles(files, conf)`, -2, \"tool.LoadFiles\", file)\n\t}\n\tif flags&GenFlagCheckOnly != 0 {\n\t\treturn nil\n\t}\n\tif err := out.WriteFile(autogen); err != nil {\n\t\treturn errors.NewWith(err, `out.WriteFile(autogen)`, -2, \"(*gogen.Package).WriteFile\", out, autogen)\n\t}\n\treturn nil\n}\n\nfunc genGoIn(dir string, conf *Config, genTestPkg bool, flags GenFlags, gen ...*bool) (err error) {\n\tout, test, err := LoadDir(dir, conf, genTestPkg, (flags&GenFlagPrompt) != 0)\n\tif err != nil {\n\t\tif NotFound(err) { // no XGo source files\n\t\t\treturn nil\n\t\t}\n\t\treturn errors.NewWith(err, `LoadDir(dir, conf, genTestPkg)`, -5, \"tool.LoadDir\", dir, conf, genTestPkg)\n\t}\n\tif flags&GenFlagCheckOnly != 0 {\n\t\treturn nil\n\t}\n\tos.MkdirAll(dir, 0755)\n\tfile := filepath.Join(dir, autoGenFile)\n\terr = out.WriteFile(file)\n\tif err != nil {\n\t\treturn errors.NewWith(err, `out.WriteFile(file)`, -2, \"(*gogen.Package).WriteFile\", out, file)\n\t}\n\tif gen != nil { // say `xgo_autogen.go generated`\n\t\t*gen[0] = true\n\t}\n\n\ttestFile := filepath.Join(dir, autoGenTestFile)\n\terr = out.WriteFile(testFile, testingGoFile)\n\tif err != nil && err != syscall.ENOENT {\n\t\treturn errors.NewWith(err, `out.WriteFile(testFile, testingGoFile)`, -2, \"(*gogen.Package).WriteFile\", out, testFile, testingGoFile)\n\t}\n\n\tif test != nil {\n\t\ttestFile = filepath.Join(dir, autoGen2TestFile)\n\t\terr = test.WriteFile(testFile, testingGoFile)\n\t\tif err != nil {\n\t\t\treturn errors.NewWith(err, `test.WriteFile(testFile, testingGoFile)`, -2, \"(*gogen.Package).WriteFile\", test, testFile, testingGoFile)\n\t\t}\n\t} else {\n\t\terr = nil\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\nconst (\n\tmodWritable = 0755\n\tmodReadonly = 0555\n)\n\n// GenGoPkgPath generates xgo_autogen.go for an XGo package.\nfunc GenGoPkgPath(workDir, pkgPath string, conf *Config, allowExtern bool) (localDir string, recursively bool, err error) {\n\treturn GenGoPkgPathEx(workDir, pkgPath, conf, allowExtern, 0)\n}\n\nfunc remotePkgPath(pkgPath string, conf *Config, recursively bool, flags GenFlags) (localDir string, _recursively bool, err error) {\n\tremotePkgPathDo(pkgPath, func(dir, _ string) {\n\t\tos.Chmod(dir, modWritable)\n\t\tdefer os.Chmod(dir, modReadonly)\n\t\tlocalDir = dir\n\t\t_recursively = recursively\n\t\terr = genGoDir(dir, conf, false, recursively, flags)\n\t}, func(e error) {\n\t\terr = e\n\t})\n\treturn\n}\n\n// GenGoPkgPathEx generates xgo_autogen.go for an XGo package.\nfunc GenGoPkgPathEx(workDir, pkgPath string, conf *Config, allowExtern bool, flags GenFlags) (localDir string, recursively bool, err error) {\n\trecursively = strings.HasSuffix(pkgPath, \"/...\")\n\tif recursively {\n\t\tpkgPath = pkgPath[:len(pkgPath)-4]\n\t} else if allowExtern && strings.Contains(pkgPath, \"@\") {\n\t\treturn remotePkgPath(pkgPath, conf, false, flags)\n\t}\n\n\tmod, err := xgomod.Load(workDir)\n\tif NotFound(err) && allowExtern {\n\t\treturn remotePkgPath(pkgPath, conf, recursively, flags)\n\t} else if err != nil {\n\t\treturn\n\t}\n\n\tpkg, err := mod.Lookup(pkgPath)\n\tif err != nil {\n\t\treturn\n\t}\n\tlocalDir = pkg.Dir\n\tif pkg.Type == xgomod.PkgtExtern {\n\t\tos.Chmod(localDir, modWritable)\n\t\tdefer os.Chmod(localDir, modReadonly)\n\t}\n\terr = genGoDir(localDir, conf, false, recursively, flags)\n\treturn\n}\n\nfunc remotePkgPathDo(pkgPath string, doSth func(pkgDir, modDir string), onErr func(e error)) {\n\tmodVer, leftPart, err := modfetch.GetPkg(pkgPath, \"\")\n\tif err != nil {\n\t\tonErr(err)\n\t} else if dir, err := modcache.Path(modVer); err != nil {\n\t\tonErr(err)\n\t} else {\n\t\tdoSth(filepath.Join(dir, leftPart), dir)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// GenGoFiles generates xgo_autogen.go for specified XGo files.\nfunc GenGoFiles(autogen string, files []string, conf *Config) (outFiles []string, err error) {\n\tif conf == nil {\n\t\tconf = new(Config)\n\t}\n\tif autogen == \"\" {\n\t\tautogen = \"xgo_autogen.go\"\n\t\tif len(files) == 1 {\n\t\t\tfile := files[0]\n\t\t\tsrcDir, fname := filepath.Split(file)\n\t\t\tif hasMultiXgoFiles(srcDir) {\n\t\t\t\tautogen = filepath.Join(srcDir, \"xgo_autogen_\"+fname+\".go\")\n\t\t\t}\n\t\t}\n\t}\n\tout, err := LoadFiles(\".\", files, conf)\n\tif err != nil {\n\t\terr = errors.NewWith(err, `LoadFiles(files, conf)`, -2, \"tool.LoadFiles\", files, conf)\n\t\treturn\n\t}\n\terr = out.WriteFile(autogen)\n\tif err != nil {\n\t\terr = errors.NewWith(err, `out.WriteFile(autogen)`, -2, \"(*gogen.Package).WriteFile\", out, autogen)\n\t}\n\toutFiles = []string{autogen}\n\treturn\n}\n\nfunc hasMultiXgoFiles(srcDir string) bool {\n\tvar has bool\n\tif f, err := os.Open(srcDir); err == nil {\n\t\tdefer f.Close()\n\t\tfis, _ := f.ReadDir(-1)\n\t\tfor _, fi := range fis {\n\t\t\text := filepath.Ext(fi.Name())\n\t\t\tif !fi.IsDir() && (ext == \".xgo\" || ext == \".gop\") {\n\t\t\t\tif has {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\thas = true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tool/imp.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tool\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/goplus/gogen/packages\"\n\t\"github.com/goplus/gogen/packages/cache\"\n\t\"github.com/goplus/mod/env\"\n\t\"github.com/goplus/mod/modfetch\"\n\t\"github.com/goplus/mod/modfile\"\n\t\"github.com/goplus/mod/xgomod\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Importer represents an XGo importer.\ntype Importer struct {\n\timpFrom *packages.Importer\n\tmod     *xgomod.Module\n\txgo     *env.XGo\n\tfset    *token.FileSet\n\n\tFlags GenFlags // can change this for loading XGo modules\n\n\timportStack map[string]bool\n}\n\n// NewImporter creates an XGo Importer.\nfunc NewImporter(mod *xgomod.Module, xgo *env.XGo, fset *token.FileSet) *Importer {\n\tconst (\n\t\tdefaultFlags = GenFlagPrompt | GenFlagPrintError\n\t)\n\tif mod == nil || !mod.HasModfile() {\n\t\tif modGop, e := xgomod.LoadFrom(filepath.Join(xgo.Root, \"go.mod\"), \"\"); e == nil {\n\t\t\tmodGop.ImportClasses()\n\t\t\tmod = modGop\n\t\t} else {\n\t\t\tmod = xgomod.Default\n\t\t}\n\t}\n\tdir := mod.Root()\n\timpFrom := packages.NewImporter(fset, dir)\n\tret := &Importer{mod: mod, xgo: xgo, impFrom: impFrom, fset: fset, Flags: defaultFlags, importStack: make(map[string]bool)}\n\timpFrom.SetCache(cache.New(ret.PkgHash))\n\treturn ret\n}\n\nfunc (p *Importer) SetTags(tags string) {\n\tp.impFrom.SetTags(tags)\n\tif c, ok := p.impFrom.Cache().(*cache.Impl); ok {\n\t\tc.SetTags(tags)\n\t}\n}\n\n// CacheFile returns file path of the cache.\nfunc (p *Importer) CacheFile() string {\n\tcacheDir, _ := os.UserCacheDir()\n\tcacheDir += \"/xgo-build/\"\n\tos.MkdirAll(cacheDir, 0755)\n\n\tfname := \"\"\n\th := sha256.New()\n\tio.WriteString(h, runtime.Version())\n\tif root := p.mod.Root(); root != \"\" {\n\t\tio.WriteString(h, root)\n\t\tfname = filepath.Base(root)\n\t}\n\tif tags := p.impFrom.Tags(); tags != \"\" {\n\t\tio.WriteString(h, tags)\n\t}\n\thash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))\n\treturn cacheDir + hash + fname\n}\n\n// Cache returns the cache object.\nfunc (p *Importer) Cache() *cache.Impl {\n\treturn p.impFrom.Cache().(*cache.Impl)\n}\n\n// PkgHash calculates hash value for a package.\n// It is required by cache.New func.\nfunc (p *Importer) PkgHash(pkgPath string, self bool) string {\n\tif pkg, e := p.mod.Lookup(pkgPath); e == nil {\n\t\tswitch pkg.Type {\n\t\tcase xgomod.PkgtStandard:\n\t\t\treturn cache.HashSkip\n\t\tcase xgomod.PkgtExtern:\n\t\t\tif pkg.Real.Version != \"\" {\n\t\t\t\treturn pkg.Real.String()\n\t\t\t}\n\t\t\tfallthrough\n\t\tcase xgomod.PkgtModule:\n\t\t\treturn dirHash(p.mod, p.xgo, pkg.Dir, self)\n\t\t}\n\t}\n\tif isPkgInMod(pkgPath, xgoMod) || isPkgInMod(pkgPath, xMod) {\n\t\treturn cache.HashSkip\n\t}\n\tlog.Println(\"PkgHash: unexpected package -\", pkgPath)\n\treturn cache.HashInvalid\n}\n\nconst (\n\txgoMod = \"github.com/goplus/xgo\"\n\txMod   = \"github.com/qiniu/x\"\n)\n\n// Import imports a Go/XGo package.\nfunc (p *Importer) Import(pkgPath string) (pkg *types.Package, err error) {\n\tif p.importStack[pkgPath] {\n\t\treturn nil, fmt.Errorf(\"cycle import detected: package %s imports itself\", pkgPath)\n\t}\n\tp.importStack[pkgPath] = true\n\tdefer delete(p.importStack, pkgPath)\n\tif strings.HasPrefix(pkgPath, xgoMod) {\n\t\tif suffix := pkgPath[len(xgoMod):]; suffix == \"\" || suffix[0] == '/' {\n\t\t\txgoRoot := p.xgo.Root\n\t\t\tif suffix == \"/cl/internal/gop-in-go/foo\" { // for test github.com/goplus/xgo/cl\n\t\t\t\tif err = p.genGoExtern(xgoRoot+suffix, false); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn p.impFrom.ImportFrom(pkgPath, xgoRoot, 0)\n\t\t}\n\t}\n\tif isPkgInMod(pkgPath, xMod) {\n\t\treturn p.impFrom.ImportFrom(pkgPath, p.xgo.Root, 0)\n\t}\n\tif mod := p.mod; mod.HasModfile() {\n\t\tret, e := mod.Lookup(pkgPath)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tswitch ret.Type {\n\t\tcase xgomod.PkgtExtern:\n\t\t\tisExtern := ret.Real.Version != \"\"\n\t\t\tif isExtern {\n\t\t\t\tif _, err = modfetch.Get(ret.Real.String()); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tmodDir := ret.ModDir\n\t\t\tgoModfile := filepath.Join(modDir, \"go.mod\")\n\t\t\tif _, e := os.Lstat(goModfile); e != nil { // no go.mod\n\t\t\t\tos.Chmod(modDir, modWritable)\n\t\t\t\tdefer os.Chmod(modDir, modReadonly)\n\t\t\t\tos.WriteFile(goModfile, defaultGoMod(ret.ModPath), 0644)\n\t\t\t}\n\t\t\treturn p.impFrom.ImportFrom(pkgPath, ret.ModDir, 0)\n\t\tcase xgomod.PkgtModule, xgomod.PkgtLocal:\n\t\t\tif pkgPath == p.mod.Path() {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif err = p.genGoExtern(ret.Dir, false); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase xgomod.PkgtStandard:\n\t\t\treturn p.impFrom.ImportFrom(pkgPath, p.xgo.Root, 0)\n\t\t}\n\t}\n\treturn p.impFrom.Import(pkgPath)\n}\n\nfunc (p *Importer) genGoExtern(dir string, isExtern bool) (err error) {\n\tgenfile := filepath.Join(dir, autoGenFile)\n\tif _, err = os.Lstat(genfile); err != nil { // no xgo_autogen.go\n\t\tif isExtern {\n\t\t\tos.Chmod(dir, modWritable)\n\t\t\tdefer os.Chmod(dir, modReadonly)\n\t\t}\n\t\tgen := false\n\t\terr = genGoIn(dir, &Config{XGo: p.xgo, Importer: p, Fset: p.fset}, false, p.Flags, &gen)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif gen {\n\t\t\tcmd := exec.Command(\"go\", \"mod\", \"tidy\")\n\t\t\tcmd.Stdout = os.Stdout\n\t\t\tcmd.Stderr = os.Stderr\n\t\t\tcmd.Dir = dir\n\t\t\terr = cmd.Run()\n\t\t}\n\t}\n\treturn\n}\n\nfunc isPkgInMod(pkgPath, modPath string) bool {\n\tif strings.HasPrefix(pkgPath, modPath) {\n\t\tsuffix := pkgPath[len(modPath):]\n\t\treturn suffix == \"\" || suffix[0] == '/'\n\t}\n\treturn false\n}\n\nfunc defaultGoMod(modPath string) []byte {\n\treturn []byte(`module ` + modPath + `\n\ngo 1.16\n`)\n}\n\nfunc dirHash(mod *xgomod.Module, xgo *env.XGo, dir string, self bool) string {\n\th := sha256.New()\n\tif self {\n\t\tfmt.Fprintf(h, \"go\\t%s\\n\", runtime.Version())\n\t\tfmt.Fprintf(h, \"xgo\\t%s\\n\", xgo.Version)\n\t}\n\tif fis, err := os.ReadDir(dir); err == nil {\n\t\tfor _, fi := range fis {\n\t\t\tif fi.IsDir() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfname := fi.Name()\n\t\t\tif strings.HasPrefix(fname, \"_\") || !canCl(mod, fname) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif v, e := fi.Info(); e == nil {\n\t\t\t\tfmt.Fprintf(h, \"file\\t%s\\t%x\\t%x\\n\", fname, v.Size(), v.ModTime().UnixNano())\n\t\t\t}\n\t\t}\n\t}\n\treturn base64.RawStdEncoding.EncodeToString(h.Sum(nil))\n}\n\nfunc canCl(mod *xgomod.Module, fname string) bool {\n\tswitch path.Ext(fname) {\n\tcase \".go\", \".xgo\", \".gop\", \".gox\":\n\t\treturn true\n\tdefault:\n\t\text := modfile.ClassExt(fname)\n\t\treturn mod.IsClass(ext)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tool/load.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tool\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/mod/env\"\n\t\"github.com/goplus/mod/xgomod\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/goplus/xgo/x/gocmd\"\n\t\"github.com/goplus/xgo/x/xgoenv\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nvar (\n\tErrNotFound      = xgomod.ErrNotFound\n\tErrIgnoreNotated = errors.New(\"notated error ignored\")\n)\n\n// NotFound returns if cause err is ErrNotFound or not\nfunc NotFound(err error) bool {\n\treturn xgomod.IsNotFound(err)\n}\n\n// IgnoreNotated returns if cause err is ErrIgnoreNotated or not.\nfunc IgnoreNotated(err error) bool {\n\treturn errors.Err(err) == ErrIgnoreNotated\n}\n\n// ErrorPos returns where the error occurs.\nfunc ErrorPos(err error) token.Pos {\n\tswitch v := err.(type) {\n\tcase *gogen.CodeError:\n\t\treturn v.Pos\n\tcase *gogen.MatchError:\n\t\treturn v.Pos()\n\tcase *gogen.ImportError:\n\t\treturn v.Pos\n\t}\n\treturn token.NoPos\n}\n\nfunc ignNotatedErrs(err error, pkg *ast.Package, fset *token.FileSet) error {\n\tswitch v := err.(type) {\n\tcase errors.List:\n\t\tvar ret errors.List\n\t\tfor _, e := range v {\n\t\t\tif isNotatedErr(e, pkg, fset) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tret = append(ret, e)\n\t\t}\n\t\tif len(ret) == 0 {\n\t\t\treturn ErrIgnoreNotated\n\t\t}\n\t\treturn ret\n\tdefault:\n\t\tif isNotatedErr(err, pkg, fset) {\n\t\t\treturn ErrIgnoreNotated\n\t\t}\n\t\treturn err\n\t}\n}\n\nfunc isNotatedErr(err error, pkg *ast.Package, fset *token.FileSet) (notatedErr bool) {\n\tpos := ErrorPos(err)\n\tf := fset.File(pos)\n\tif f == nil {\n\t\treturn\n\t}\n\txgof, ok := pkg.Files[f.Name()]\n\tif !ok {\n\t\treturn\n\t}\n\tlines := token.Lines(f)\n\ti := f.Line(pos) - 1 // base 0\n\tstart := lines[i]\n\tvar end int\n\tif i+1 < len(lines) {\n\t\tend = lines[i+1]\n\t} else {\n\t\tend = f.Size()\n\t}\n\ttext := string(xgof.Code[start:end])\n\tcommentOff := strings.Index(text, \"//\")\n\tif commentOff < 0 {\n\t\treturn\n\t}\n\treturn strings.Contains(text[commentOff+2:], \"compile error:\")\n}\n\n// -----------------------------------------------------------------------------\n\n// Config represents a configuration for loading XGo packages.\ntype Config struct {\n\tXGo      *env.XGo\n\tFset     *token.FileSet\n\tMod      *xgomod.Module\n\tImporter *Importer\n\n\tFilter func(fs.FileInfo) bool\n\n\t// If not nil, it is used for returning result of checks XGo dependencies.\n\t// see https://pkg.go.dev/github.com/goplus/gogen#File.CheckGopDeps\n\tXGoDeps *int\n\n\t// CacheFile specifies the file path of the cache.\n\tCacheFile string\n\n\tIgnoreNotatedError bool\n\tDontUpdateGoMod    bool\n}\n\n// ConfFlags represents configuration flags.\ntype ConfFlags int\n\nconst (\n\tConfFlagIgnoreNotatedError ConfFlags = 1 << iota\n\tConfFlagDontUpdateGoMod\n\tConfFlagNoTestFiles\n\tConfFlagNoCacheFile\n)\n\n// NewDefaultConf creates a dfault configuration for common cases.\nfunc NewDefaultConf(dir string, flags ConfFlags, tags ...string) (conf *Config, err error) {\n\tmod, err := LoadMod(dir)\n\tif err != nil {\n\t\treturn\n\t}\n\txgo := xgoenv.Get()\n\tfset := token.NewFileSet()\n\timp := NewImporter(mod, xgo, fset)\n\tif len(tags) > 0 {\n\t\timp.SetTags(strings.Join(tags, \",\"))\n\t}\n\tconf = &Config{\n\t\tXGo: xgo, Fset: fset, Mod: mod, Importer: imp,\n\t\tIgnoreNotatedError: flags&ConfFlagIgnoreNotatedError != 0,\n\t\tDontUpdateGoMod:    flags&ConfFlagDontUpdateGoMod != 0,\n\t}\n\tif flags&ConfFlagNoCacheFile == 0 {\n\t\tconf.CacheFile = imp.CacheFile()\n\t\timp.Cache().Load(conf.CacheFile)\n\t}\n\tif flags&ConfFlagNoTestFiles != 0 {\n\t\tconf.Filter = FilterNoTestFiles\n\t}\n\treturn\n}\n\nfunc (conf *Config) NewGoCmdConf() *gocmd.Config {\n\tif cl := conf.Mod.Opt.Compiler; cl != nil {\n\t\tif os.Getenv(\"XGO_GOCMD\") == \"\" {\n\t\t\tos.Setenv(\"XGO_GOCMD\", cl.Name)\n\t\t}\n\t}\n\treturn &gocmd.Config{\n\t\tXGo: conf.XGo,\n\t}\n}\n\n// UpdateCache updates the cache.\nfunc (conf *Config) UpdateCache(verbose ...bool) {\n\tif conf.CacheFile != \"\" {\n\t\tc := conf.Importer.Cache()\n\t\tc.Save(conf.CacheFile)\n\t\tif verbose != nil && verbose[0] {\n\t\t\tfmt.Println(\"Times of calling go list:\", c.ListTimes())\n\t\t}\n\t}\n}\n\n// LoadMod loads an XGo module from a specified directory.\nfunc LoadMod(dir string) (mod *xgomod.Module, err error) {\n\tmod, err = xgomod.Load(dir)\n\tif err != nil && !xgomod.IsNotFound(err) {\n\t\terr = errors.NewWith(err, `xgomod.Load(dir, 0)`, -2, \"xgomod.Load\", dir, 0)\n\t\treturn\n\t}\n\tif mod == nil {\n\t\tmod = xgomod.Default\n\t}\n\terr = mod.ImportClasses()\n\tif err != nil {\n\t\terr = errors.NewWith(err, `mod.ImportClasses()`, -2, \"(*xgomod.Module).ImportClasses\", mod)\n\t}\n\treturn\n}\n\n// FilterNoTestFiles filters to skip all testing files.\nfunc FilterNoTestFiles(fi fs.FileInfo) bool {\n\tfname := fi.Name()\n\tsuffix := \"\"\n\tswitch path.Ext(fname) {\n\tcase \".gox\":\n\t\tsuffix = \"test.gox\"\n\tcase \".xgo\":\n\t\tsuffix = \"_test.xgo\"\n\tcase \".gop\":\n\t\tsuffix = \"_test.gop\"\n\tcase \".go\":\n\t\tsuffix = \"_test.go\"\n\tdefault:\n\t\treturn true\n\t}\n\treturn !strings.HasSuffix(fname, suffix)\n}\n\n// -----------------------------------------------------------------------------\n\n// LoadDir loads XGo packages from a specified directory.\nfunc LoadDir(dir string, conf *Config, genTestPkg bool, promptGenGo ...bool) (out, test *gogen.Package, err error) {\n\tif conf == nil {\n\t\tconf = new(Config)\n\t}\n\n\tmod := conf.Mod\n\tif mod == nil {\n\t\tif mod, err = LoadMod(dir); err != nil {\n\t\t\terr = errors.NewWith(err, `LoadMod(dir)`, -2, \"tool.LoadMod\", dir)\n\t\t\treturn\n\t\t}\n\t}\n\n\tfset := conf.Fset\n\tif fset == nil {\n\t\tfset = token.NewFileSet()\n\t}\n\tpkgs, err := parser.ParseDirEx(fset, dir, parser.Config{\n\t\tClassKind: mod.ClassKind,\n\t\tFilter:    conf.Filter,\n\t\tMode:      parser.ParseComments | parser.SaveAbsFile,\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(pkgs) == 0 {\n\t\treturn nil, nil, ErrNotFound\n\t}\n\n\txgo := conf.XGo\n\tif xgo == nil {\n\t\txgo = xgoenv.Get()\n\t}\n\timp := conf.Importer\n\tif imp == nil {\n\t\timp = NewImporter(mod, xgo, fset)\n\t}\n\n\tvar pkgTest *ast.Package\n\tvar clConf = &cl.Config{\n\t\tFset:         fset,\n\t\tRelativeBase: relativeBaseOf(mod),\n\t\tImporter:     imp,\n\t\tLookupClass:  mod.LookupClass,\n\t}\n\n\tfor name, pkg := range pkgs {\n\t\tif strings.HasSuffix(name, \"_test\") {\n\t\t\tif pkgTest != nil {\n\t\t\t\treturn nil, nil, ErrMultiTestPackges\n\t\t\t}\n\t\t\tpkgTest = pkg\n\t\t\tcontinue\n\t\t}\n\t\tif out != nil {\n\t\t\treturn nil, nil, ErrMultiPackges\n\t\t}\n\t\tif len(pkg.Files) == 0 { // no XGo source files\n\t\t\tcontinue\n\t\t}\n\t\tif promptGenGo != nil && promptGenGo[0] {\n\t\t\tfmt.Fprintln(os.Stderr, \"GenGo\", dir, \"...\")\n\t\t}\n\t\tout, err = cl.NewPackage(\"\", pkg, clConf)\n\t\tif err != nil {\n\t\t\tif conf.IgnoreNotatedError {\n\t\t\t\terr = ignNotatedErrs(err, pkg, fset)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\tif out == nil {\n\t\treturn nil, nil, ErrNotFound\n\t}\n\tif genTestPkg && pkgTest != nil {\n\t\ttest, err = cl.NewPackage(\"\", pkgTest, clConf)\n\t}\n\tafterLoad(mod, xgo, out, test, conf)\n\treturn\n}\n\nfunc afterLoad(mod *xgomod.Module, xgo *env.XGo, out, test *gogen.Package, conf *Config) {\n\tif mod.Path() == xgoMod { // nothing to do for XGo itself\n\t\treturn\n\t}\n\tupdateMod := !conf.DontUpdateGoMod && mod.HasModfile()\n\tif updateMod || conf.XGoDeps != nil {\n\t\tflags := checkGopDeps(out)\n\t\tif conf.XGoDeps != nil { // for `xgo run`\n\t\t\t*conf.XGoDeps = flags\n\t\t}\n\t\tif updateMod {\n\t\t\tif test != nil {\n\t\t\t\tflags |= checkGopDeps(test)\n\t\t\t}\n\t\t\tif flags != 0 {\n\t\t\t\tmod.SaveWithXGoMod(xgo, flags)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc checkGopDeps(pkg *gogen.Package) (flags int) {\n\tpkg.ForEachFile(func(fname string, file *gogen.File) {\n\t\tflags |= file.CheckXGoDeps(pkg)\n\t})\n\treturn\n}\n\nfunc relativeBaseOf(mod *xgomod.Module) string {\n\tif mod.HasModfile() {\n\t\treturn mod.Root()\n\t}\n\tdir, _ := os.Getwd()\n\treturn dir\n}\n\n// -----------------------------------------------------------------------------\n\n// LoadFiles loads an XGo package from specified files.\nfunc LoadFiles(dir string, files []string, conf *Config) (out *gogen.Package, err error) {\n\tif conf == nil {\n\t\tconf = new(Config)\n\t}\n\n\tmod := conf.Mod\n\tif mod == nil {\n\t\tif mod, err = LoadMod(dir); err != nil {\n\t\t\terr = errors.NewWith(err, `LoadMod(dir)`, -2, \"tool.LoadMod\", dir)\n\t\t\treturn\n\t\t}\n\t}\n\n\tfset := conf.Fset\n\tif fset == nil {\n\t\tfset = token.NewFileSet()\n\t}\n\tpkgs, err := parser.ParseEntries(fset, files, parser.Config{\n\t\tClassKind: mod.ClassKind,\n\t\tFilter:    conf.Filter,\n\t\tMode:      parser.ParseComments | parser.SaveAbsFile,\n\t})\n\tif err != nil {\n\t\terr = errors.NewWith(err, `parser.ParseFiles(fset, files, parser.ParseComments)`, -2, \"parser.ParseFiles\", fset, files, parser.ParseComments)\n\t\treturn\n\t}\n\tif len(pkgs) != 1 {\n\t\terr = errors.NewWith(ErrMultiPackges, `len(pkgs) != 1`, -1, \"!=\", len(pkgs), 1)\n\t\treturn\n\t}\n\txgo := conf.XGo\n\tif xgo == nil {\n\t\txgo = xgoenv.Get()\n\t}\n\tfor _, pkg := range pkgs {\n\t\timp := conf.Importer\n\t\tif imp == nil {\n\t\t\timp = NewImporter(mod, xgo, fset)\n\t\t}\n\t\tclConf := &cl.Config{\n\t\t\tFset:         fset,\n\t\t\tRelativeBase: relativeBaseOf(mod),\n\t\t\tImporter:     imp,\n\t\t\tLookupClass:  mod.LookupClass,\n\t\t}\n\t\tout, err = cl.NewPackage(\"\", pkg, clConf)\n\t\tif err != nil {\n\t\t\tif conf.IgnoreNotatedError {\n\t\t\t\terr = ignNotatedErrs(err, pkg, fset)\n\t\t\t}\n\t\t}\n\t\tbreak\n\t}\n\tafterLoad(mod, xgo, out, nil, conf)\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\nvar (\n\tErrMultiPackges     = errors.New(\"multiple packages\")\n\tErrMultiTestPackges = errors.New(\"multiple test packages\")\n)\n\n// -----------------------------------------------------------------------------\n\n// GetFileClassType get xgo module file classType.\nfunc GetFileClassType(mod *xgomod.Module, file *ast.File, filename string) (classType string, isTest bool) {\n\treturn cl.GetFileClassType(file, filename, mod.LookupClass)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tool/outline.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tool\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/goplus/mod/xgomod\"\n\t\"github.com/goplus/xgo/cl/outline\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/x/xgoenv\"\n\t\"github.com/qiniu/x/errors\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc Outline(dir string, conf *Config) (out outline.Package, err error) {\n\tif dir, err = filepath.Abs(dir); err != nil {\n\t\treturn\n\t}\n\n\tif conf == nil {\n\t\tconf = new(Config)\n\t}\n\n\tmod := conf.Mod\n\tif mod == nil {\n\t\tif mod, err = LoadMod(dir); err != nil {\n\t\t\terr = errors.NewWith(err, `LoadMod(dir)`, -2, \"tool.LoadMod\", dir)\n\t\t\treturn\n\t\t}\n\t}\n\n\tfilterConf := conf.Filter\n\tfilter := func(fi fs.FileInfo) bool {\n\t\tif filterConf != nil && !filterConf(fi) {\n\t\t\treturn false\n\t\t}\n\t\tfname := fi.Name()\n\t\tif pos := strings.Index(fname, \".\"); pos > 0 {\n\t\t\tfname = fname[:pos]\n\t\t}\n\t\treturn !strings.HasSuffix(fname, \"_test\")\n\t}\n\tfset := conf.Fset\n\tif fset == nil {\n\t\tfset = token.NewFileSet()\n\t}\n\tpkgs, err := parser.ParseDirEx(fset, dir, parser.Config{\n\t\tClassKind: mod.ClassKind,\n\t\tFilter:    filter,\n\t\tMode:      parser.ParseComments,\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(pkgs) == 0 {\n\t\terr = ErrNotFound\n\t\treturn\n\t}\n\n\timp := conf.Importer\n\tif imp == nil {\n\t\txgo := conf.XGo\n\t\tif xgo == nil {\n\t\t\txgo = xgoenv.Get()\n\t\t}\n\t\timp = NewImporter(mod, xgo, fset)\n\t}\n\n\tfor name, pkg := range pkgs {\n\t\tif out.Valid() {\n\t\t\terr = fmt.Errorf(\"%w: %s, %s\", ErrMultiPackges, name, out.Pkg().Name())\n\t\t\treturn\n\t\t}\n\t\tif len(pkg.Files)+len(pkg.GoFiles) == 0 { // no Go/XGo source files\n\t\t\tbreak\n\t\t}\n\t\trelPart, _ := filepath.Rel(mod.Root(), dir)\n\t\tpkgPath := path.Join(mod.Path(), filepath.ToSlash(relPart))\n\t\tout, err = outline.NewPackage(pkgPath, pkg, &outline.Config{\n\t\t\tFset:        fset,\n\t\t\tImporter:    imp,\n\t\t\tLookupClass: mod.LookupClass,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif !out.Valid() {\n\t\terr = ErrNotFound\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\nfunc OutlinePkgPath(workDir, pkgPath string, conf *Config, allowExtern bool) (out outline.Package, err error) {\n\tmod := conf.Mod\n\tif mod == nil {\n\t\tif mod, err = LoadMod(workDir); err != nil {\n\t\t\terr = errors.NewWith(err, `LoadMod(dir)`, -2, \"tool.LoadMod\", workDir)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif NotFound(err) && allowExtern {\n\t\tremotePkgPathDo(pkgPath, func(pkgDir, modDir string) {\n\t\t\tmodFile := chmodModfile(modDir)\n\t\t\tdefer os.Chmod(modFile, modReadonly)\n\t\t\tout, err = Outline(pkgDir, conf)\n\t\t}, func(e error) {\n\t\t\terr = e\n\t\t})\n\t\treturn\n\t} else if err != nil {\n\t\treturn\n\t}\n\n\tpkg, err := mod.Lookup(pkgPath)\n\tif err != nil {\n\t\treturn\n\t}\n\tif pkg.Type == xgomod.PkgtExtern {\n\t\tmodFile := chmodModfile(pkg.ModDir)\n\t\tdefer os.Chmod(modFile, modReadonly)\n\t}\n\treturn Outline(pkg.Dir, conf)\n}\n\nfunc chmodModfile(modDir string) string {\n\tmodFile := modDir + \"/go.mod\"\n\tos.Chmod(modFile, modWritable)\n\treturn modFile\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tool/tidy.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tool\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\n\t\"github.com/goplus/mod/env\"\n\t\"github.com/goplus/mod/xgomod\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc Tidy(dir string, xgo *env.XGo) (err error) {\n\tmodObj, err := xgomod.Load(dir)\n\tif err != nil {\n\t\treturn errors.NewWith(err, `xgomod.Load(dir, mod.GopModOnly)`, -2, \"xgomod.Load\", dir)\n\t}\n\n\tmodRoot := modObj.Root()\n\t/*\n\t\tdepMods, err := GenDepMods(modObj, modRoot, true)\n\t\tif err != nil {\n\t\t\treturn errors.NewWith(err, `GenDepMods(modObj, modRoot, true)`, -2, \"tool.GenDepMods\", modObj, modRoot, true)\n\t\t}\n\n\t\told := modObj.DepMods()\n\t\tfor modPath := range old {\n\t\t\tif _, ok := depMods[modPath]; !ok { // removed\n\t\t\t\tmodObj.DropRequire(modPath)\n\t\t\t}\n\t\t}\n\t\tfor modPath := range depMods {\n\t\t\tif _, ok := old[modPath]; !ok { // added\n\t\t\t\tif newMod, e := modfetch.Get(modPath); e != nil {\n\t\t\t\t\treturn errors.NewWith(e, `modfetch.Get(modPath)`, -1, \"modfetch.Get\", modPath)\n\t\t\t\t} else {\n\t\t\t\t\tmodObj.AddRequire(newMod.Path, newMod.Version)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tmodObj.Cleanup()\n\t\terr = modObj.Save()\n\t\tif err != nil {\n\t\t\treturn errors.NewWith(err, `modObj.Save()`, -2, \"(*xgomod.Module).Save\")\n\t\t}\n\t*/\n\tconf := &Config{XGo: xgo}\n\terr = genGoDir(modRoot, conf, true, true, 0)\n\tif err != nil {\n\t\treturn errors.NewWith(err, `genGoDir(modRoot, conf, true, true)`, -2, \"tool.genGoDir\", modRoot, conf, true, true)\n\t}\n\n\tcmd := exec.Command(\"go\", \"mod\", \"tidy\")\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\tcmd.Dir = modRoot\n\terr = cmd.Run()\n\tif err != nil {\n\t\terr = errors.NewWith(err, `cmd.Run()`, -2, \"(*exec.Cmd).Run\")\n\t}\n\treturn\n}\n"
  },
  {
    "path": "tpl/README.md",
    "content": "TPL: Text Processing Language\n=====\n\nText processing is a common task in programming, and regular expressions have long been the go-to solution. However, regular expressions are notorious for their cryptic syntax and poor readability. Enter XGo TPL (Text Processing Language), an enhanced alternative that offers both power and intuitive syntax.\n\nXGo TPL is a grammar-based language similar to EBNF (Extended Backus-Naur Form) that seamlessly integrates with XGo. It provides a more readable and maintainable approach to text processing while offering capabilities beyond what regular expressions can achieve.\n\n## Understanding XGo TPL\n\nTo understand XGo TPL, you need to grasp three key concepts:\n\n### 1. Naming Rules\n\nThe foundation of TPL is its naming rules, expressed as `name = rule`. A TPL grammar consists of a series of named rules, with the first one being the root rule. The `rule` can be a combination of:\n\n* **Basic Tokens**: Fundamental syntax units like `INT`, `FLOAT`, `CHAR`, `STRING`, `IDENT`, `\"+\"`, `\"++\"`, `\"+=\"`, `\"<<=\"`, etc.\n* **Keywords**: An `IDENT` enclosed in quotes, such as `\"if\"`, `\"else\"`, `\"for\"`.\n* **References**: References to other named rules, including self-references.\n* **Sequence**: `R1 R2 ... Rn` - matches a sequence of rules.\n* **Alternatives**: `R1 | R2 | ... | Rn` - matches any one of the rules.\n* **Repetition Operators**:\n  * `*R` - matches the rule zero or more times\n  * `+R` - matches the rule one or more times\n  * `?R` - matches the rule zero or one time (optional)\n* **List Operator**: `R1 % R2` - shorthand for `R1 *(R2 R1)`, representing a sequence of R1 separated by R2. For example, `INT % \",\"` represents a comma-separated list of integers.\n* **Adjacency Operator**: `R1 ++ R2` - indicates that R1 and R2 must be adjacent with no whitespace or comments between them.\n\nThe default operator precedence is: unary operators (`*R`, `+R`, `?R`) > `++` > `%` > sequence (space) > `|`. Parentheses can be used to change the precedence.\n\n#### String Literals in Detail\n\n`STRING` (string literals) can take two forms:\n\n```go\n\"Hello\\nWorld\\n\"  // QSTRING (quoted string)\n\n`Hello\nWorld\n`               // RAWSTRING (raw string)\n```\n\n`STRING` can be defined as:\n\n```go\nSTRING = QSTRING | RAWSTRING\n```\n\n#### The Adjacency Operator Explained\n\nSince TPL rules automatically filter whitespace and comments, the sequence `R1 R2` doesn't express that R1 and R2 are adjacent. This is where the adjacency operator `++` comes in.\n\nFor example, XGo [domain text literal](../doc/domian-text-lit.md) is defined as `IDENT ++ RAWSTRING`, making these valid:\n\n```go\ntpl`expr = INT % \",\"`\njson`{\"name\": \"Ken\", age: 15}`\n```\n\nWhile these would match `IDENT STRING` but are not valid domain text literals:\n\n```go\ntpl\"expr = *INT\"              // IDENT must be followed by RAWSTRING, not QSTRING\ntpl/* comment */`expr = *INT` // No whitespace or comments allowed between IDENT and RAWSTRING\n```\n\n### 2. Matching Results\n\nEach rule has its built-in matching result:\n\n* **Tokens and Keywords**: Result is `*tpl.Token`.\n* **Sequence** (`R1 R2 ... Rn`): Result is a list (`[]any`) with n elements.\n* **Repetition** (`*R`, `+R`): Result is a list (`[]any`) with elements depending on how many times R matches.\n* **Alternatives** (`R1 | R2 | ... | Rn`): Result depends on which rule matches.\n* **Optional** (`?R`): Result is either the result of R or `nil` if no match.\n* **List Operator** (`R1 % R2`): Result is a complex tree-like structure with three levels. \n\n  Let's explain why it has three levels:\n  1. The first level is the result of the entire expression `R1 % R2` (i.e.`R1 *(R2 R1)`), which is a list with two elements.\n  2. The first element of this list is the result of the first `R1`.\n  3. The second element is a list containing the results of all subsequent `(R2 R1)` matches.\n     - Each element in this second-level list is itself a list with two elements: the result of `R2` and the result of `R1`.\n     \n  For example, when parsing `\"1, 2, 3\"` with `INT % \",\"`, the result structure would be:\n  ```\n  [\n    <INT:1>,                // First R1\n    [\n      [<COMMA>, <INT:2>],   // First (R2 R1)\n      [<COMMA>, <INT:3>]    // Second (R2 R1)\n    ]\n  ]\n  ```\n  This tree-like structure preserves all the information about the matched elements and their relationships, but can be complex to work with directly. That's why TPL provides helper functions like `ListOp` and `BinaryOp` to transform this structure into more usable forms.\n\n* **Adjacency Operator** (`R1 ++ R2`): Result is a list (`[]any`) with 2 elements, similar to a `R1 R2` sequence.\n\n### 3. Rewriting Matching Results\n\nThe default matching result is called \"self\" in TPL. You can rewrite this result using an XGo closure `=> { ... }`.\n\nThis feature is crucial as it allows seamless integration between TPL and XGo. In XGo, you reference TPL through [domain text literal](../doc/domian-text-lit.md), and within TPL, you can call XGo code through result rewriting.\n\n## Practical Examples\n\n### Basic Example: Parsing Integers\n\n```go\nimport \"xgo/tpl\"\n\ncl := tpl`\nexpr = INT % \",\" => {\n    return tpl.ListOp[int](self, v => {\n        return v.(*tpl.Token).Lit.int!\n    })\n}\n`!\n\necho cl.parseExpr(\"1, 2, 3\", nil)!  // Outputs: [1 2 3]\n```\n\nThis example parses a comma-separated list of integers and converts it to a flat list of integers using TPL's `ListOp` function.\n\n### Building a Calculator\n\nCreating a calculator with XGo TPL is remarkably concise:\n\n```go\nimport \"xgo/tpl\"\n\ncl := tpl`\nexpr = operand % (\"*\" | \"/\") % (\"+\" | \"-\") => {\n    return tpl.BinaryOp(true, self, (op, x, y) => {\n        switch op.Tok {\n        case '+': return x.(float64) + y.(float64)\n        case '-': return x.(float64) - y.(float64)\n        case '*': return x.(float64) * y.(float64)\n        case '/': return x.(float64) / y.(float64)\n        }\n        panic(\"unexpected\")\n    })\n}\n\noperand = basicLit | unaryExpr\n\nunaryExpr = \"-\" operand => {\n    return -(self[1].(float64))\n}\n\nbasicLit = INT | FLOAT => {\n    return self.(*tpl.Token).Lit.float!\n}\n`!\n\necho cl.parseExpr(\"1 + 2 * -3\", nil)!  // Outputs: -5\n```\n\nThis calculator handles basic arithmetic operations with proper operator precedence in less than 30 lines of code.\n\n## Conclusion\n\nXGo TPL offers a powerful yet intuitive alternative to regular expressions for text processing. By combining grammar-based parsing with seamless XGo integration, it enables developers to create clear, maintainable text processing solutions.\n\nFor more examples of TPL in action, check out the XGo demos starting with `tpl-` at [https://github.com/goplus/xgo/tree/main/demo](https://github.com/goplus/xgo/tree/main/demo). These examples showcase how to implement calculators, parse text to generate ASTs, and even implement entire languages in just a few hundred lines of code.\n\nWhether you're parsing structured text, building domain-specific languages, or implementing complex text transformations, XGo TPL provides a robust and readable approach that surpasses traditional regular expressions.\n"
  },
  {
    "path": "tpl/ast/ast.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ast\n\nimport (\n\t\"github.com/goplus/xgo/tpl/token\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Node: File, Decl, Expr\ntype Node interface {\n\tPos() token.Pos\n\tEnd() token.Pos\n}\n\n// Decl: Rule\ntype Decl interface {\n\tNode\n\tdeclNode()\n}\n\n// Expr: Ident, BasicLit, Choice, Sequence, UnaryExpr, BinaryExpr\ntype Expr interface {\n\tNode\n\texprNode()\n}\n\n// -----------------------------------------------------------------------------\n\n// File: *Decl\ntype File struct {\n\tDecls     []Decl\n\tFileStart token.Pos\n}\n\nfunc (p *File) Pos() token.Pos {\n\tif n := len(p.Decls); n > 0 {\n\t\treturn p.Decls[0].Pos()\n\t}\n\treturn p.FileStart\n}\n\nfunc (p *File) End() token.Pos {\n\tif n := len(p.Decls); n > 0 {\n\t\treturn p.Decls[n-1].End()\n\t}\n\treturn p.FileStart\n}\n\n// -----------------------------------------------------------------------------\n\n// Rule:\n//\n//\tIDENT '=' Expr\n//\tIDENT '=' Expr => { ... }\ntype Rule struct {\n\tName    *Ident\n\tTokPos  token.Pos // position of '='\n\tExpr    Expr\n\tRetProc Node // => { ... } (see xgo/ast.LambdaExpr2) or nil\n}\n\n// IsList reports whether the rule is a list rule.\nfunc (p *Rule) IsList() bool {\n\tswitch e := p.Expr.(type) {\n\tcase *Sequence:\n\t\treturn true\n\tcase *UnaryExpr:\n\t\tswitch e.Op {\n\t\tcase token.MUL, token.ADD: // *R, +R\n\t\t\treturn true\n\t\t}\n\tcase *BinaryExpr:\n\t\tswitch e.Op {\n\t\tcase token.REM, token.INC: // R1 % R2, R1 ++ R2\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (p *Rule) Pos() token.Pos { return p.Name.Pos() }\nfunc (p *Rule) End() token.Pos {\n\tif p.RetProc != nil {\n\t\treturn p.RetProc.End()\n\t}\n\treturn p.Expr.End()\n}\n\nfunc (p *Rule) declNode() {}\n\n// -----------------------------------------------------------------------------\n\n// Ident: IDENT\ntype Ident struct {\n\tNamePos token.Pos // identifier position\n\tName    string    // identifier name\n}\n\nfunc (p *Ident) Pos() token.Pos { return p.NamePos }\nfunc (p *Ident) End() token.Pos { return p.NamePos + token.Pos(len(p.Name)) }\nfunc (p *Ident) exprNode()      {}\n\n// -----------------------------------------------------------------------------\n\n// BasicLit: STRING | CHAR\ntype BasicLit struct {\n\tValuePos token.Pos   // literal position\n\tKind     token.Token // token.STRING or token.CHAR\n\tValue    string\n}\n\nfunc (p *BasicLit) Pos() token.Pos { return p.ValuePos }\nfunc (p *BasicLit) End() token.Pos { return p.ValuePos + token.Pos(len(p.Value)) }\nfunc (p *BasicLit) exprNode()      {}\n\n// -----------------------------------------------------------------------------\n\n// Choice: R1 | R2 | ... | Rn\ntype Choice struct {\n\tOptions []Expr // multiple options\n}\n\nfunc (p *Choice) Pos() token.Pos { return p.Options[0].Pos() }\nfunc (p *Choice) End() token.Pos { return p.Options[len(p.Options)-1].End() }\nfunc (p *Choice) exprNode()      {}\n\n// -----------------------------------------------------------------------------\n\n// Sequence: R1 R2 ... Rn\ntype Sequence struct {\n\tItems []Expr // multiple items\n}\n\nfunc (p *Sequence) Pos() token.Pos { return p.Items[0].Pos() }\nfunc (p *Sequence) End() token.Pos { return p.Items[len(p.Items)-1].End() }\nfunc (p *Sequence) exprNode()      {}\n\n// -----------------------------------------------------------------------------\n\n// UnaryExpr: *R, +R or ?R\ntype UnaryExpr struct {\n\tOpPos token.Pos   // operator position\n\tOp    token.Token // operator: token.MUL, token.ADD or token.QUESTION\n\tX     Expr        // operand\n}\n\nfunc (p *UnaryExpr) Pos() token.Pos { return p.OpPos }\nfunc (p *UnaryExpr) End() token.Pos { return p.X.End() }\nfunc (p *UnaryExpr) exprNode()      {}\n\n// -----------------------------------------------------------------------------\n\n// BinaryExpr: R1 % R2, R1 ++ R2\ntype BinaryExpr struct {\n\tX     Expr        // left operand\n\tOpPos token.Pos   // operator position\n\tOp    token.Token // operator: token.REM (list operator), token.INC (adjoin operator)\n\tY     Expr        // right operand\n}\n\nfunc (p *BinaryExpr) Pos() token.Pos { return p.X.Pos() }\nfunc (p *BinaryExpr) End() token.Pos { return p.Y.End() }\nfunc (p *BinaryExpr) exprNode()      {}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/cl/compile.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cl\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/goplus/xgo/tpl/ast\"\n\t\"github.com/goplus/xgo/tpl/matcher\"\n\t\"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nvar (\n\t// ErrNoDocFound error\n\tErrNoDocFound = errors.New(\"no document rule found\")\n)\n\n// Result represents the result of compiling a set of rules.\ntype Result struct {\n\tDoc   *matcher.Var\n\tRules map[string]*matcher.Var\n}\n\ntype choice struct {\n\tm *matcher.Choices\n\tc *ast.Choice\n}\n\ntype context struct {\n\trules   map[string]*matcher.Var\n\tchoices []choice\n\terrs    errors.List\n\tfset    *token.FileSet\n}\n\nfunc (p *context) newErrorf(pos token.Pos, format string, args ...any) error {\n\treturn &matcher.Error{Fset: p.fset, Pos: pos, Msg: fmt.Sprintf(format, args...)}\n}\n\nfunc (p *context) addErrorf(pos token.Pos, format string, args ...any) {\n\tp.errs.Add(p.newErrorf(pos, format, args...))\n}\n\nfunc (p *context) addError(pos token.Pos, msg string) {\n\tp.errs.Add(&matcher.Error{Fset: p.fset, Pos: pos, Msg: msg})\n}\n\n// New compiles a set of rules from the given files.\nfunc New(fset *token.FileSet, files ...*ast.File) (ret Result, err error) {\n\treturn NewEx(nil, fset, files...)\n}\n\n// Config configures the behavior of the compiler.\ntype Config struct {\n\tRetProcs   map[string]any\n\tOnConflict func(fset *token.FileSet, c *ast.Choice, firsts [][]any, i, at int)\n}\n\n// NewEx compiles a set of rules from the given files.\nfunc NewEx(conf *Config, fset *token.FileSet, files ...*ast.File) (ret Result, err error) {\n\tif conf == nil {\n\t\tconf = &Config{}\n\t}\n\tretProcs := conf.RetProcs\n\trules := make(map[string]*matcher.Var)\n\tctx := &context{rules: rules, fset: fset}\n\tfor _, f := range files {\n\t\tfor _, decl := range f.Decls {\n\t\t\tswitch decl := decl.(type) {\n\t\t\tcase *ast.Rule:\n\t\t\t\tident := decl.Name\n\t\t\t\tname := ident.Name\n\t\t\t\tif old, ok := rules[name]; ok {\n\t\t\t\t\toldPos := fset.Position(old.Pos)\n\t\t\t\t\tctx.addErrorf(ident.Pos(),\n\t\t\t\t\t\t\"duplicate rule `%s`, previous declaration at %v\", name, oldPos)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tv := matcher.NewVar(ident.Pos(), name)\n\t\t\t\trules[name] = v\n\t\t\tdefault:\n\t\t\t\tctx.addError(decl.Pos(), \"unknown declaration\")\n\t\t\t}\n\t\t}\n\t}\n\tvar doc *matcher.Var\n\tfor _, f := range files {\n\t\tfor _, decl := range f.Decls {\n\t\t\tswitch decl := decl.(type) {\n\t\t\tcase *ast.Rule:\n\t\t\t\tident := decl.Name\n\t\t\t\tname := ident.Name\n\t\t\t\tv := rules[name]\n\t\t\t\tif r, ok := compileExpr(decl.Expr, ctx); ok {\n\t\t\t\t\tv.RetProc = retProcs[name]\n\t\t\t\t\tif e := v.Assign(r); e != nil {\n\t\t\t\t\t\tctx.addError(ident.Pos(), e.Error())\n\t\t\t\t\t}\n\t\t\t\t\tif doc == nil {\n\t\t\t\t\t\tdoc = v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif doc == nil {\n\t\terr = ErrNoDocFound\n\t\treturn\n\t}\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tswitch e := e.(type) {\n\t\t\tcase matcher.RecursiveError:\n\t\t\t\tctx.addError(e.Pos, e.Error())\n\t\t\tdefault:\n\t\t\t\tpanic(e)\n\t\t\t}\n\t\t}\n\t\terr = ctx.errs.ToError()\n\t}()\n\tonConflict := conf.OnConflict\n\tif onConflict == nil {\n\t\tonConflict = onConflictDefault\n\t}\n\tfor _, item := range ctx.choices {\n\t\titem.m.CheckConflicts(func(firsts [][]any, i, at int) {\n\t\t\tonConflict(fset, item.c, firsts, i, at)\n\t\t})\n\t}\n\tret = Result{doc, rules}\n\treturn\n}\n\nfunc onConflictDefault(fset *token.FileSet, c *ast.Choice, firsts [][]any, i, at int) {\n\tpos := fset.Position(c.Options[i].Pos())\n\tLogConflict(pos, firsts, i, at)\n}\n\n// LogConflict logs a conflict between two choices.\nfunc LogConflict(pos token.Position, firsts [][]any, i, at int) {\n\tfmt.Fprintf(os.Stderr, \"%v: [WARN] conflict between %v and %v\\n\", pos, firsts[i], firsts[at])\n}\n\nvar (\n\tidents = map[string]token.Token{\n\t\t\"EOF\":     token.EOF,\n\t\t\"COMMENT\": token.COMMENT,\n\t\t\"IDENT\":   token.IDENT,\n\t\t\"INT\":     token.INT,\n\t\t\"FLOAT\":   token.FLOAT,\n\t\t\"IMAG\":    token.IMAG,\n\t\t\"CHAR\":    token.CHAR,\n\t\t\"STRING\":  token.STRING,\n\t\t\"RAT\":     token.RAT,\n\t\t\"UNIT\":    token.UNIT,\n\t\t\"LPAREN\":  token.LPAREN,\n\t\t\"RPAREN\":  token.RPAREN,\n\t\t\"LBRACK\":  token.LBRACK,\n\t\t\"RBRACK\":  token.RBRACK,\n\t\t\"LBRACE\":  token.LBRACE,\n\t\t\"RBRACE\":  token.RBRACE,\n\t}\n)\n\nfunc compileExpr(expr ast.Expr, ctx *context) (matcher.Matcher, bool) {\n\tswitch expr := expr.(type) {\n\tcase *ast.Ident:\n\t\tname := expr.Name\n\t\tif v, ok := ctx.rules[name]; ok {\n\t\t\treturn v, true\n\t\t} else if tok, ok := idents[name]; ok {\n\t\t\treturn matcher.Token(tok), true\n\t\t}\n\t\tvar quoteCh byte\n\t\tswitch name {\n\t\tcase \"RAWSTRING\":\n\t\t\tquoteCh = '`'\n\t\tcase \"QSTRING\":\n\t\t\tquoteCh = '\"'\n\t\tcase \"SPACE\":\n\t\t\treturn matcher.WhiteSpace(), true\n\t\tdefault:\n\t\t\tctx.addErrorf(expr.Pos(), \"`%s` is undefined\", name)\n\t\t}\n\t\treturn matcher.String(quoteCh), true\n\tcase *ast.BasicLit:\n\t\tlit := expr.Value\n\t\tswitch expr.Kind {\n\t\tcase token.CHAR:\n\t\t\tv, multibyte, tail, e := strconv.UnquoteChar(lit[1:len(lit)-1], '\\'')\n\t\t\tif e != nil {\n\t\t\t\tctx.addErrorf(expr.Pos(), \"invalid literal %s: %v\", lit, e)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif tail != \"\" || multibyte {\n\t\t\t\tctx.addError(expr.Pos(), \"invalid literal \"+lit)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn tokenExpr(token.Token(v), expr, ctx)\n\t\tcase token.STRING:\n\t\t\tv, e := strconv.Unquote(lit)\n\t\t\tif e != nil {\n\t\t\t\tctx.addError(expr.Pos(), \"invalid literal \"+lit)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif v == \"\" {\n\t\t\t\treturn matcher.True(), true\n\t\t\t}\n\t\t\tif c := v[0]; c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_' {\n\t\t\t\treturn matcher.Literal(token.IDENT, v), true\n\t\t\t}\n\t\t\tif t, ok := checkToken(v); ok {\n\t\t\t\treturn tokenExpr(t, expr, ctx)\n\t\t\t}\n\t\t\tfallthrough\n\t\tdefault:\n\t\t\tctx.addError(expr.Pos(), \"invalid literal \"+lit)\n\t\t}\n\tcase *ast.Sequence:\n\t\titems := make([]matcher.Matcher, len(expr.Items))\n\t\tfor i, item := range expr.Items {\n\t\t\tif r, ok := compileExpr(item, ctx); ok {\n\t\t\t\titems[i] = r\n\t\t\t} else {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t}\n\t\treturn matcher.Sequence(items...), true\n\tcase *ast.Choice:\n\t\toptions := make([]matcher.Matcher, len(expr.Options))\n\t\tfor i, option := range expr.Options {\n\t\t\tif r, ok := compileExpr(option, ctx); ok {\n\t\t\t\toptions[i] = r\n\t\t\t} else {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t}\n\t\tret := matcher.Choice(options...)\n\t\tctx.choices = append(ctx.choices, choice{ret, expr})\n\t\treturn ret, true\n\tcase *ast.UnaryExpr:\n\t\tif x, ok := compileExpr(expr.X, ctx); ok {\n\t\t\tswitch expr.Op {\n\t\t\tcase token.QUESTION:\n\t\t\t\treturn matcher.Repeat01(x), true\n\t\t\tcase token.MUL:\n\t\t\t\treturn matcher.Repeat0(x), true\n\t\t\tcase token.ADD:\n\t\t\t\treturn matcher.Repeat1(x), true\n\t\t\tdefault:\n\t\t\t\tctx.addErrorf(expr.Pos(), \"invalid token %v\", expr.Op)\n\t\t\t}\n\t\t}\n\tcase *ast.BinaryExpr:\n\t\tx, ok1 := compileExpr(expr.X, ctx)\n\t\ty, ok2 := compileExpr(expr.Y, ctx)\n\t\tif ok1 && ok2 {\n\t\t\tswitch expr.Op {\n\t\t\tcase token.REM: // %\n\t\t\t\treturn matcher.List(x, y), true\n\t\t\tcase token.INC: // ++\n\t\t\t\treturn matcher.Adjoin(x, y), true\n\t\t\tdefault:\n\t\t\t\tctx.addErrorf(expr.Pos(), \"invalid token %v\", expr.Op)\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tctx.addError(expr.Pos(), \"unknown expression\")\n\t}\n\treturn nil, false\n}\n\nfunc tokenExpr(tok token.Token, expr *ast.BasicLit, ctx *context) (matcher.Matcher, bool) {\n\tif tok.Len() > 0 {\n\t\treturn matcher.Token(tok), true\n\t}\n\tctx.addErrorf(expr.Pos(), \"invalid token: %s\", expr.Value)\n\treturn nil, false\n}\n\nfunc checkToken(v string) (ret token.Token, ok bool) {\n\tif len(v) == 1 {\n\t\treturn token.Token(v[0]), true\n\t}\n\ttoken.ForEach(0, func(tok token.Token, lit string) int {\n\t\tif lit == v {\n\t\t\tret, ok = tok, true\n\t\t\treturn token.Break\n\t\t}\n\t\treturn 0\n\t})\n\treturn\n}\n"
  },
  {
    "path": "tpl/matcher/match.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage matcher\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/goplus/xgo/tpl/types\"\n)\n\nvar (\n\t// ErrVarAssigned error\n\tErrVarAssigned = errors.New(\"variable is already assigned\")\n\n\terrNoWhitespace  = errors.New(\"no whitespace\")\n\terrAdjoinEmpty   = errors.New(\"adjoin empty\")\n\terrMultiMismatch = errors.New(\"multiple mismatch\")\n)\n\n// -----------------------------------------------------------------------------\n\ntype dbgFlags int\n\nconst (\n\tDbgFlagMatchVar dbgFlags = 1 << iota\n\tDbgFlagAll               = DbgFlagMatchVar\n)\n\nvar (\n\tenableMatchVar bool\n)\n\nfunc SetDebug(flags dbgFlags) {\n\tenableMatchVar = (flags & DbgFlagMatchVar) != 0\n}\n\n// -----------------------------------------------------------------------------\n\n// Error represents a matching error.\ntype Error struct {\n\tFset *token.FileSet\n\tPos  token.Pos\n\tMsg  string\n\n\t// is a runtime error\n\tDyn bool\n}\n\nfunc (p *Error) Error() string {\n\tpos := p.Fset.Position(p.Pos)\n\treturn fmt.Sprintf(\"%v: %s\", pos, p.Msg)\n}\n\nfunc isDyn(err error) bool {\n\tif e, ok := err.(*Error); ok {\n\t\treturn e.Dyn\n\t}\n\treturn false\n}\n\n// RecursiveError represents a recursive error.\ntype RecursiveError struct {\n\t*Var\n}\n\nfunc (e RecursiveError) Error() string {\n\treturn \"recursive variable \" + e.Name\n}\n\n// -----------------------------------------------------------------------------\n\n// Context represents the context of a matching process.\ntype Context struct {\n\tFset    *token.FileSet\n\tFileEnd token.Pos\n\ttoks    []*types.Token\n\n\tLeft    int\n\tLastErr error\n}\n\n// NewContext creates a new matching context.\nfunc NewContext(fset *token.FileSet, fileEnd token.Pos, toks []*types.Token) *Context {\n\treturn &Context{\n\t\tFset:    fset,\n\t\tFileEnd: fileEnd,\n\t\ttoks:    toks,\n\t\tLeft:    len(toks),\n\t}\n}\n\n// SetLastError sets the last error.\nfunc (p *Context) SetLastError(left int, err error) {\n\tif left < p.Left {\n\t\tp.Left, p.LastErr = left, err\n\t}\n}\n\n// NewError creates a new error.\nfunc (p *Context) NewError(pos token.Pos, msg string) *Error {\n\treturn &Error{p.Fset, pos, msg, false}\n}\n\n// NewErrorf creates a new error with a format string.\nfunc (p *Context) NewErrorf(pos token.Pos, format string, args ...any) error {\n\treturn &Error{p.Fset, pos, fmt.Sprintf(format, args...), false}\n}\n\n// -----------------------------------------------------------------------------\n\n// MatchToken represents a matching literal.\ntype MatchToken struct {\n\tTok token.Token\n\tLit string\n}\n\nfunc (p *MatchToken) String() string {\n\treturn p.Lit\n}\n\nfunc hasConflictToken(me token.Token, next []any) bool {\n\tfor _, n := range next {\n\t\tswitch n := n.(type) {\n\t\tcase *MatchToken:\n\t\t\tif n.Tok == me {\n\t\t\t\treturn true\n\t\t\t}\n\t\tcase token.Token:\n\t\t\tif n == me {\n\t\t\t\treturn true\n\t\t\t}\n\t\tdefault:\n\t\t\tpanic(\"unreachable\")\n\t\t}\n\t}\n\treturn false\n}\n\nfunc hasConflictMatchToken(me *MatchToken, next []any) bool {\n\tfor _, n := range next {\n\t\tswitch n := n.(type) {\n\t\tcase *MatchToken:\n\t\t\tif n.Tok == me.Tok && n.Lit == me.Lit {\n\t\t\t\treturn true\n\t\t\t}\n\t\tcase token.Token:\n\t\tdefault:\n\t\t\tpanic(\"unreachable\")\n\t\t}\n\t}\n\treturn false\n}\n\nfunc hasConflictMe(me any, next []any) bool {\n\tswitch me := me.(type) {\n\tcase token.Token:\n\t\treturn hasConflictToken(me, next)\n\tcase *MatchToken:\n\t\treturn hasConflictMatchToken(me, next)\n\t}\n\tpanic(\"unreachable\")\n}\n\nfunc hasConflict(me []any, next []any) bool {\n\tfor _, m := range me {\n\t\tif hasConflictMe(m, next) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc conflictWith(me []any, next [][]any, from int) int {\n\tfor i, n := from, len(next); i < n; i++ {\n\t\tif hasConflict(me, next[i]) {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// -----------------------------------------------------------------------------\n// Matcher\n\n// Matcher represents a matcher.\ntype Matcher interface {\n\tMatch(src []*types.Token, ctx *Context) (n int, result any, err error)\n\tFirst(in []any) (first []any, mayEmpty bool) // can be token.Token or *MatchToken\n}\n\n// -----------------------------------------------------------------------------\n\ntype gTrue struct{}\n\nfunc (p gTrue) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\treturn 0, nil, nil\n}\n\nfunc (p gTrue) First(in []any) (first []any, mayEmpty bool) {\n\treturn in, true\n}\n\n// True returns a matcher that always succeeds.\nfunc True() Matcher {\n\treturn gTrue{}\n}\n\n// -----------------------------------------------------------------------------\n\ntype gWS struct{}\n\nfunc (p gWS) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tif left := len(src); left > 0 {\n\t\ttoks := ctx.toks\n\t\tif n := len(toks); n > left {\n\t\t\tlast := n - left - 1\n\t\t\tif toks[last].End() != src[0].Pos {\n\t\t\t\treturn 0, nil, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn 0, nil, errNoWhitespace\n}\n\nfunc (p gWS) First(in []any) (first []any, mayEmpty bool) {\n\treturn in, true\n}\n\n// WhiteSpace returns a matcher that matches whitespace.\nfunc WhiteSpace() Matcher {\n\treturn gWS{}\n}\n\n// -----------------------------------------------------------------------------\n\ntype gString byte\n\nfunc (p gString) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tif len(src) == 0 {\n\t\treturn 0, nil, ctx.NewErrorf(ctx.FileEnd, \"expect `%s`, but got EOF\", stringType(p))\n\t}\n\tt := src[0]\n\tif t.Tok != token.STRING || t.Lit[0] != byte(p) {\n\t\treturn 0, nil, ctx.NewErrorf(t.Pos, \"expect `%s`, but got `%v`\", stringType(p), t)\n\t}\n\treturn 1, t, nil\n}\n\nfunc (p gString) First(in []any) (first []any, mayEmpty bool) {\n\treturn append(in, token.STRING), false\n}\n\n// String returns a matcher that matches a string literal.\nfunc String(quoteCh byte) Matcher {\n\treturn gString(quoteCh)\n}\n\nfunc stringType(quoteCh gString) string {\n\tif quoteCh == '\"' {\n\t\treturn \"QSTRING\"\n\t}\n\treturn \"RAWSTRING\"\n}\n\n// -----------------------------------------------------------------------------\n\ntype gToken struct {\n\ttok token.Token\n}\n\nfunc (p *gToken) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tif len(src) == 0 {\n\t\treturn 0, nil, ctx.NewErrorf(ctx.FileEnd, \"expect `%s`, but got EOF\", p.tok)\n\t}\n\tt := src[0]\n\tif t.Tok != p.tok {\n\t\treturn 0, nil, ctx.NewErrorf(t.Pos, \"expect `%s`, but got `%s`\", p.tok, t.Tok)\n\t}\n\treturn 1, t, nil\n}\n\nfunc (p *gToken) First(in []any) (first []any, mayEmpty bool) {\n\treturn append(in, p.tok), false\n}\n\n// Token: ADD, SUB, IDENT, INT, FLOAT, CHAR, STRING, etc.\nfunc Token(tok token.Token) Matcher {\n\treturn &gToken{tok}\n}\n\n// -----------------------------------------------------------------------------\n\ntype gLiteral MatchToken\n\nfunc (p *gLiteral) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tif len(src) == 0 {\n\t\treturn 0, nil, ctx.NewErrorf(ctx.FileEnd, \"expect `%s`, but got EOF\", p.Lit)\n\t}\n\tt := src[0]\n\tif t.Tok != p.Tok || t.Lit != p.Lit {\n\t\treturn 0, nil, ctx.NewErrorf(t.Pos, \"expect `%s`, but got `%v`\", p.Lit, t)\n\t}\n\treturn 1, t, nil\n}\n\nfunc (p *gLiteral) First(in []any) (first []any, mayEmpty bool) {\n\treturn append(in, (*MatchToken)(p)), false\n}\n\n// Literal: \"abc\", 'a', 123, 1.23, etc.\nfunc Literal(tok token.Token, lit string) Matcher {\n\treturn &gLiteral{tok, lit}\n}\n\n// -----------------------------------------------------------------------------\n\n// Choices represents a choice matcher.\ntype Choices struct {\n\toptions []Matcher\n\tstops   []bool\n}\n\nfunc (p *Choices) CheckConflicts(conflict func(firsts [][]any, i, at int)) {\n\toptions := p.options\n\tn := len(options)\n\tfirsts := make([][]any, n)\n\tfor i, g := range options {\n\t\tfirsts[i], _ = g.First(nil)\n\t}\n\tstops := make([]bool, n)\n\tfor i, me := range firsts {\n\t\tat := conflictWith(me, firsts, i+1)\n\t\tif at >= 0 {\n\t\t\tconflict(firsts, i, at)\n\t\t} else {\n\t\t\tstops[i] = true\n\t\t}\n\t}\n\tp.stops = stops\n}\n\nfunc (p *Choices) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tvar nMax = -1\n\tvar errMax error\n\tvar multiErr = true\n\n\tstops := p.stops // be set by CheckConflicts\n\tfor i, g := range p.options {\n\t\tif n, result, err = g.Match(src, ctx); err == nil || (n > 0 && stops[i]) {\n\t\t\treturn\n\t\t}\n\t\tif n >= nMax {\n\t\t\tif n == nMax {\n\t\t\t\tmultiErr = true\n\t\t\t} else {\n\t\t\t\tnMax, errMax, multiErr = n, err, false\n\t\t\t}\n\t\t}\n\t}\n\tif multiErr {\n\t\terrMax = errMultiMismatch\n\t}\n\treturn nMax, nil, errMax\n}\n\nfunc (p *Choices) First(in []any) (first []any, mayEmpty bool) {\n\tfor _, g := range p.options {\n\t\tvar me bool\n\t\tif in, me = g.First(in); me {\n\t\t\tmayEmpty = true\n\t\t}\n\t}\n\tfirst = in\n\treturn\n}\n\n// Choice: R1 | R2 | ... | Rn\n// Should be used with CheckConflicts.\nfunc Choice(options ...Matcher) *Choices {\n\treturn &Choices{options, nil}\n}\n\n// -----------------------------------------------------------------------------\n\ntype gSequence struct {\n\titems []Matcher\n}\n\nfunc (p *gSequence) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tnitems := len(p.items)\n\trets := make([]any, nitems)\n\tfor i, g := range p.items {\n\t\tn1, ret1, err1 := g.Match(src[n:], ctx)\n\t\tif err1 != nil {\n\t\t\tif isDyn(err1) {\n\t\t\t\terr = err1\n\t\t\t} else {\n\t\t\t\treturn n + n1, nil, err1\n\t\t\t}\n\t\t}\n\t\trets[i] = ret1\n\t\tn += n1\n\t}\n\tresult = rets\n\treturn\n}\n\nfunc (p *gSequence) First(in []any) (first []any, mayEmpty bool) {\n\tfor _, g := range p.items {\n\t\tif in, mayEmpty = g.First(in); !mayEmpty {\n\t\t\tbreak\n\t\t}\n\t}\n\tfirst = in\n\treturn\n}\n\n// Sequence: R1 R2 ... Rn\n// Should length of items > 0\nfunc Sequence(items ...Matcher) Matcher {\n\treturn &gSequence{items}\n}\n\n// -----------------------------------------------------------------------------\n\ntype gRepeat0 struct {\n\tr Matcher\n}\n\nfunc (p *gRepeat0) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tg := p.r\n\trets := make([]any, 0, 2)\n\tfor {\n\t\tn1, ret1, err1 := g.Match(src, ctx)\n\t\tif err1 != nil {\n\t\t\tif isDyn(err1) {\n\t\t\t\terr = err1\n\t\t\t} else {\n\t\t\t\tctx.SetLastError(len(src)-n1, err1)\n\t\t\t\tresult = rets\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\trets = append(rets, ret1)\n\t\tn += n1\n\t\tsrc = src[n1:]\n\t}\n}\n\nfunc (p *gRepeat0) First(in []any) (first []any, mayEmpty bool) {\n\tfirst, _ = p.r.First(in)\n\tmayEmpty = true\n\treturn\n}\n\n// Repeat0: *R\nfunc Repeat0(r Matcher) Matcher {\n\treturn &gRepeat0{r}\n}\n\n// -----------------------------------------------------------------------------\n\ntype gRepeat1 struct {\n\tr Matcher\n}\n\nfunc (p *gRepeat1) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tg := p.r\n\tn, ret0, err := g.Match(src, ctx)\n\tif err != nil {\n\t\treturn\n\t}\n\n\trets := make([]any, 1, 2)\n\trets[0] = ret0\n\tfor {\n\t\tn1, ret1, err1 := g.Match(src[n:], ctx)\n\t\tif err1 != nil {\n\t\t\tif isDyn(err1) {\n\t\t\t\terr = err1\n\t\t\t} else {\n\t\t\t\tctx.SetLastError(len(src)-n-n1, err1)\n\t\t\t\tresult = rets\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\trets = append(rets, ret1)\n\t\tn += n1\n\t}\n}\n\nfunc (p *gRepeat1) First(in []any) (first []any, mayEmpty bool) {\n\treturn p.r.First(in)\n}\n\n// Repeat1: +R\nfunc Repeat1(r Matcher) Matcher {\n\treturn &gRepeat1{r}\n}\n\n// -----------------------------------------------------------------------------\n\ntype gRepeat01 struct {\n\tr Matcher\n}\n\nfunc (p *gRepeat01) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tn, result, err = p.r.Match(src, ctx)\n\tif err != nil {\n\t\treturn 0, nil, nil\n\t}\n\treturn\n}\n\nfunc (p *gRepeat01) First(in []any) (first []any, mayEmpty bool) {\n\tfirst, _ = p.r.First(in)\n\tmayEmpty = true\n\treturn\n}\n\n// Repeat01: ?R\nfunc Repeat01(r Matcher) Matcher {\n\treturn &gRepeat01{r}\n}\n\n// -----------------------------------------------------------------------------\n\n// List: R1 % R2 is equivalent to R1 *(R2 R1)\nfunc List(a, b Matcher) Matcher {\n\treturn Sequence(a, Repeat0(Sequence(b, a)))\n}\n\n// -----------------------------------------------------------------------------\n\ntype gAdjoin struct {\n\ta, b Matcher\n}\n\nfunc (p *gAdjoin) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tn, ret0, err := p.a.Match(src, ctx)\n\tif err != nil {\n\t\treturn\n\t}\n\tif n == 0 {\n\t\terr = errAdjoinEmpty\n\t\treturn\n\t}\n\tn1, ret1, err := p.b.Match(src[n:], ctx)\n\tif err != nil && !isDyn(err) {\n\t\treturn\n\t}\n\tif n1 == 0 {\n\t\terr = errAdjoinEmpty\n\t\treturn\n\t}\n\tif src[n-1].End() != src[n].Pos {\n\t\terr = ctx.NewError(src[n].Pos, \"not adjoin\")\n\t\treturn\n\t}\n\tn += n1\n\tresult = []any{ret0, ret1}\n\treturn\n}\n\nfunc (p *gAdjoin) First(in []any) (first []any, mayEmpty bool) {\n\tfirst, _ = p.a.First(in)\n\treturn\n}\n\n// Adjoin: R1 ++ R2\nfunc Adjoin(a, b Matcher) Matcher {\n\treturn &gAdjoin{a, b}\n}\n\n// -----------------------------------------------------------------------------\n\ntype RetProc = func(any) any\ntype ListRetProc = func([]any) any\n\ntype Var struct {\n\tElem Matcher\n\tName string\n\tPos  token.Pos\n\n\tRetProc any\n}\n\nfunc (p *Var) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {\n\tg := p.Elem\n\tif g == nil {\n\t\treturn 0, nil, ctx.NewErrorf(p.Pos, \"variable `%s` not assigned\", p.Name)\n\t}\n\tif enableMatchVar && len(src) > 0 {\n\t\tlog.Println(\"==> Match\", p.Name, src[0])\n\t}\n\tn, result, err = g.Match(src, ctx)\n\tif err == nil {\n\t\tif retProc := p.RetProc; retProc != nil {\n\t\t\tdefer func() {\n\t\t\t\tif e := recover(); e != nil {\n\t\t\t\t\tswitch e := e.(type) {\n\t\t\t\t\tcase *Error:\n\t\t\t\t\t\tif e.Fset == nil {\n\t\t\t\t\t\t\te.Fset = ctx.Fset\n\t\t\t\t\t\t}\n\t\t\t\t\t\terr = e\n\t\t\t\t\tcase string:\n\t\t\t\t\t\terr = &Error{\n\t\t\t\t\t\t\tFset: ctx.Fset,\n\t\t\t\t\t\t\tPos:  src[0].Pos,\n\t\t\t\t\t\t\tMsg:  e,\n\t\t\t\t\t\t\tDyn:  true,\n\t\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\terr = e.(error)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif listRetPorc, ok := retProc.(ListRetProc); ok {\n\t\t\t\tresult = listRetPorc(result.([]any))\n\t\t\t} else {\n\t\t\t\tresult = retProc.(RetProc)(result)\n\t\t\t}\n\t\t}\n\t} else if err == errMultiMismatch {\n\t\tvar posErr token.Pos\n\t\tvar tokErr any\n\t\tif len(src) > 0 {\n\t\t\tposErr, tokErr = src[0].Pos, src[0]\n\t\t} else {\n\t\t\tposErr, tokErr = ctx.FileEnd, \"EOF\"\n\t\t}\n\t\terr = ctx.NewErrorf(posErr, \"expect `%s`, but got `%s`\", p.Name, tokErr)\n\t}\n\treturn\n}\n\nfunc (p *Var) First(in []any) (first []any, mayEmpty bool) {\n\telem := p.Elem\n\tif elem != nil {\n\t\tp.Elem = nil // to stop recursion\n\t\tfirst, mayEmpty = elem.First(in)\n\t\tp.Elem = elem\n\t} else {\n\t\tpanic(RecursiveError{p})\n\t}\n\treturn\n}\n\n// Assign assigns a value to this variable.\nfunc (p *Var) Assign(elem Matcher) error {\n\tif p.Elem != nil {\n\t\treturn ErrVarAssigned\n\t}\n\tp.Elem = elem\n\treturn nil\n}\n\n// NewVar creates a new Var instance.\nfunc NewVar(pos token.Pos, name string) *Var {\n\treturn &Var{Pos: pos, Name: name}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/parser/_testdata/adjoin/in.xgo",
    "content": "doc = IDENT ++ RAWSTRING | IDENT SPACE \"{\" \"}\"\n"
  },
  {
    "path": "tpl/parser/_testdata/adjoin/out.expect",
    "content": "ast.Rule:\n  Name:\n    ast.Ident:\n      Name: doc\n  Expr:\n    ast.Choice:\n      Options:\n        ast.BinaryExpr:\n          X:\n            ast.Ident:\n              Name: IDENT\n          Op: ++\n          Y:\n            ast.Ident:\n              Name: RAWSTRING\n        ast.Sequence:\n          Items:\n            ast.Ident:\n              Name: IDENT\n            ast.Ident:\n              Name: SPACE\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"{\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"}\"\n"
  },
  {
    "path": "tpl/parser/_testdata/pseudo/in.xgo",
    "content": "file = stmts => {\n\treturn &ast.File{\n\t\tStmts: this.([]ast.Stmt),\n\t}\n}\n\nstmts = *(stmt \";\") => {\n\treturn [n.(ast.Stmt) for n in this]\n}\n\nstmt = varStmt | constStmt | outputStmt | inputStmt | ifStmt | whileStmt | untilStmt | assignStmt\n\nvarStmt = \"DECLARE\" namelist \":\" typeExpr => {\n\treturn &ast.DeclareStmt{\n\t\tDeclare: this[0].(*tpl.Token).Pos,\n\t\tNames:   this[1].([]*ast.Ident),\n\t\tType:    this[3].(ast.Expr),\n\t}\n}\n\nconstStmt = \"CONSTANT\" IDENT \"<-\" expr => {\n\treturn &ast.ConstantStmt{\n\t\tConstant: this[0].(*tpl.Token).Pos,\n\t\tName:     this[1].(*ast.Ident),\n\t\tValue:    this[3].(ast.Expr),\n\t}\n}\n\nassignStmt = IDENT \"<-\" expr => {\n\treturn &ast.AssignStmt{\n\t\tName:  this[0].(*ast.Ident),\n\t\tValue: this[2].(ast.Expr),\n\t}\n}\n\noutputStmt = \"OUTPUT\" exprlist => {\n\treturn &ast.OutputStmt{\n\t\tOutput: this[0].(*tpl.Token).Pos,\n\t\tValues: this[1].([]ast.Expr),\n\t}\n}\n\ninputStmt = \"INPUT\" namelist => {\n\treturn &ast.InputStmt{\n\t\tInput: this[0].(*tpl.Token).Pos,\n\t\tNames: this[1].([]*ast.Ident),\n\t}\n}\n\nifStmt = \"IF\" expr \"THEN\" \";\" stmts ?(\"ELSE\" \";\" stmts) \"ENDIF\" => {\n\tvar elseBody []ast.Stmt\n\tif v := this[5]; v != nil {\n\t\telseStmt := v.([]any)\n\t\telseBody = elseStmt[2].([]ast.Stmt)\n\t}\n\treturn &ast.IfStmt{\n\t\tIf:    this[0].(*tpl.Token).Pos,\n\t\tCond:  this[1].(ast.Expr),\n\t\tBody:  this[4].([]ast.Stmt),\n\t\tElse:  elseBody,\n\t\tEndIf: this[6].(*tpl.Token).Pos,\n\t}\n}\n\nwhileStmt = \"WHILE\" expr \"DO\" \";\" stmts \"ENDWHILE\" => {\n\treturn &ast.WhileStmt{\n\t\tWhile:    this[0].(*tpl.Token).Pos,\n\t\tCond:     this[1].(ast.Expr),\n\t\tBody:     this[4].([]ast.Stmt),\n\t\tEndWhile: this[5].(*tpl.Token).Pos,\n\t}\n}\n\nuntilStmt = \"REPEAT\" \";\" stmts \"UNTIL\" expr => {\n\treturn &ast.UntilStmt{\n\t\tRepeat: this[0].(*tpl.Token).Pos,\n\t\tBody:   this[2].([]ast.Stmt),\n\t\tUntil:  this[3].(*tpl.Token).Pos,\n\t\tCond:   this[4].(ast.Expr),\n\t}\n}\n\ntypeExpr = \"INTEGER\" | \"REAL\" | \"STRING\" | \"BOOLEAN\" => {\n\treturn tpl.ident(this)\n}\n\nexpr = binaryExpr2 % (\"<\" | \"<=\" | \">\" | \">=\" | \"=\" | \"<>\") => {\n\treturn tpl.binaryExpr(this)\n}\n\nbinaryExpr2 = binaryExpr1 % (\"+\" | \"-\") => {\n\treturn tpl.binaryExpr(this)\n}\n\nbinaryExpr1 = operand % (\"*\" | \"/\") => {\n\treturn tpl.binaryExpr(this)\n}\n\noperand = basicLit | ident | parenExpr | unaryExpr\n\nunaryExpr = \"-\" operand => {\n\treturn tpl.unaryExpr(this)\n}\n\nbasicLit = INT | FLOAT | STRING => {\n\treturn tpl.basicLit(this)\n}\n\nident = IDENT => {\n\treturn tpl.ident(this)\n}\n\nparenExpr = \"(\" expr \")\" => {\n\treturn this[1]\n}\n\nexprlist = expr % \",\" => {\n\treturn [v.(ast.Expr) for v in tpl.list(this)]\n}\n\nnamelist = IDENT % \",\" => {\n\treturn [tpl.ident(v) for v in tpl.list(this)]\n}\n"
  },
  {
    "path": "tpl/parser/_testdata/pseudo/out.expect",
    "content": "ast.Rule:\n  Name:\n    ast.Ident:\n      Name: file\n  Expr:\n    ast.Ident:\n      Name: stmts\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: stmts\n  Expr:\n    ast.UnaryExpr:\n      Op: *\n      X:\n        ast.Sequence:\n          Items:\n            ast.Ident:\n              Name: stmt\n            ast.BasicLit:\n              Kind: STRING\n              Value: \";\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: stmt\n  Expr:\n    ast.Choice:\n      Options:\n        ast.Ident:\n          Name: varStmt\n        ast.Ident:\n          Name: constStmt\n        ast.Ident:\n          Name: outputStmt\n        ast.Ident:\n          Name: inputStmt\n        ast.Ident:\n          Name: ifStmt\n        ast.Ident:\n          Name: whileStmt\n        ast.Ident:\n          Name: untilStmt\n        ast.Ident:\n          Name: assignStmt\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: varStmt\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"DECLARE\"\n        ast.Ident:\n          Name: namelist\n        ast.BasicLit:\n          Kind: STRING\n          Value: \":\"\n        ast.Ident:\n          Name: typeExpr\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: constStmt\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"CONSTANT\"\n        ast.Ident:\n          Name: IDENT\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"<-\"\n        ast.Ident:\n          Name: expr\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: assignStmt\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.Ident:\n          Name: IDENT\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"<-\"\n        ast.Ident:\n          Name: expr\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: outputStmt\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"OUTPUT\"\n        ast.Ident:\n          Name: exprlist\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: inputStmt\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"INPUT\"\n        ast.Ident:\n          Name: namelist\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: ifStmt\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"IF\"\n        ast.Ident:\n          Name: expr\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"THEN\"\n        ast.BasicLit:\n          Kind: STRING\n          Value: \";\"\n        ast.Ident:\n          Name: stmts\n        ast.UnaryExpr:\n          Op: ?\n          X:\n            ast.Sequence:\n              Items:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"ELSE\"\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \";\"\n                ast.Ident:\n                  Name: stmts\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"ENDIF\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: whileStmt\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"WHILE\"\n        ast.Ident:\n          Name: expr\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"DO\"\n        ast.BasicLit:\n          Kind: STRING\n          Value: \";\"\n        ast.Ident:\n          Name: stmts\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"ENDWHILE\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: untilStmt\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"REPEAT\"\n        ast.BasicLit:\n          Kind: STRING\n          Value: \";\"\n        ast.Ident:\n          Name: stmts\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"UNTIL\"\n        ast.Ident:\n          Name: expr\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: typeExpr\n  Expr:\n    ast.Choice:\n      Options:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"INTEGER\"\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"REAL\"\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"STRING\"\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"BOOLEAN\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: expr\n  Expr:\n    ast.BinaryExpr:\n      X:\n        ast.Ident:\n          Name: binaryExpr2\n      Op: %\n      Y:\n        ast.Choice:\n          Options:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"<\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"<=\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \">\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \">=\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"=\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"<>\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: binaryExpr2\n  Expr:\n    ast.BinaryExpr:\n      X:\n        ast.Ident:\n          Name: binaryExpr1\n      Op: %\n      Y:\n        ast.Choice:\n          Options:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"+\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"-\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: binaryExpr1\n  Expr:\n    ast.BinaryExpr:\n      X:\n        ast.Ident:\n          Name: operand\n      Op: %\n      Y:\n        ast.Choice:\n          Options:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"*\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"/\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: operand\n  Expr:\n    ast.Choice:\n      Options:\n        ast.Ident:\n          Name: basicLit\n        ast.Ident:\n          Name: ident\n        ast.Ident:\n          Name: parenExpr\n        ast.Ident:\n          Name: unaryExpr\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: unaryExpr\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"-\"\n        ast.Ident:\n          Name: operand\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: basicLit\n  Expr:\n    ast.Choice:\n      Options:\n        ast.Ident:\n          Name: INT\n        ast.Ident:\n          Name: FLOAT\n        ast.Ident:\n          Name: STRING\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: ident\n  Expr:\n    ast.Ident:\n      Name: IDENT\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: parenExpr\n  Expr:\n    ast.Sequence:\n      Items:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \"(\"\n        ast.Ident:\n          Name: expr\n        ast.BasicLit:\n          Kind: STRING\n          Value: \")\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: exprlist\n  Expr:\n    ast.BinaryExpr:\n      X:\n        ast.Ident:\n          Name: expr\n      Op: %\n      Y:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \",\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: namelist\n  Expr:\n    ast.BinaryExpr:\n      X:\n        ast.Ident:\n          Name: IDENT\n      Op: %\n      Y:\n        ast.BasicLit:\n          Kind: STRING\n          Value: \",\"\n"
  },
  {
    "path": "tpl/parser/_testdata/simple1/in.xgo",
    "content": "expr = termExpr | expr (\"+\" | \"-\") expr\n\ntermExpr = unaryExpr | termExpr (\"*\" | \"/\") termExpr\n\nunaryExpr = operand | \"-\" unaryExpr\n\noperand = INT | FLOAT | \"(\" expr \")\"\n"
  },
  {
    "path": "tpl/parser/_testdata/simple1/out.expect",
    "content": "ast.Rule:\n  Name:\n    ast.Ident:\n      Name: expr\n  Expr:\n    ast.Choice:\n      Options:\n        ast.Ident:\n          Name: termExpr\n        ast.Sequence:\n          Items:\n            ast.Ident:\n              Name: expr\n            ast.Choice:\n              Options:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"+\"\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"-\"\n            ast.Ident:\n              Name: expr\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: termExpr\n  Expr:\n    ast.Choice:\n      Options:\n        ast.Ident:\n          Name: unaryExpr\n        ast.Sequence:\n          Items:\n            ast.Ident:\n              Name: termExpr\n            ast.Choice:\n              Options:\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"*\"\n                ast.BasicLit:\n                  Kind: STRING\n                  Value: \"/\"\n            ast.Ident:\n              Name: termExpr\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: unaryExpr\n  Expr:\n    ast.Choice:\n      Options:\n        ast.Ident:\n          Name: operand\n        ast.Sequence:\n          Items:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"-\"\n            ast.Ident:\n              Name: unaryExpr\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: operand\n  Expr:\n    ast.Choice:\n      Options:\n        ast.Ident:\n          Name: INT\n        ast.Ident:\n          Name: FLOAT\n        ast.Sequence:\n          Items:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"(\"\n            ast.Ident:\n              Name: expr\n            ast.BasicLit:\n              Kind: STRING\n              Value: \")\"\n"
  },
  {
    "path": "tpl/parser/_testdata/simple2/in.xgo",
    "content": "expr = termExpr % (\"+\" | \"-\")\n\ntermExpr = unaryExpr % (\"*\" | \"/\")\n\nunaryExpr = operand | \"-\" unaryExpr\n\noperand = INT | FLOAT | \"(\" expr \")\"\n"
  },
  {
    "path": "tpl/parser/_testdata/simple2/out.expect",
    "content": "ast.Rule:\n  Name:\n    ast.Ident:\n      Name: expr\n  Expr:\n    ast.BinaryExpr:\n      X:\n        ast.Ident:\n          Name: termExpr\n      Op: %\n      Y:\n        ast.Choice:\n          Options:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"+\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"-\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: termExpr\n  Expr:\n    ast.BinaryExpr:\n      X:\n        ast.Ident:\n          Name: unaryExpr\n      Op: %\n      Y:\n        ast.Choice:\n          Options:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"*\"\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"/\"\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: unaryExpr\n  Expr:\n    ast.Choice:\n      Options:\n        ast.Ident:\n          Name: operand\n        ast.Sequence:\n          Items:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"-\"\n            ast.Ident:\n              Name: unaryExpr\nast.Rule:\n  Name:\n    ast.Ident:\n      Name: operand\n  Expr:\n    ast.Choice:\n      Options:\n        ast.Ident:\n          Name: INT\n        ast.Ident:\n          Name: FLOAT\n        ast.Sequence:\n          Items:\n            ast.BasicLit:\n              Kind: STRING\n              Value: \"(\"\n            ast.Ident:\n              Name: expr\n            ast.BasicLit:\n              Kind: STRING\n              Value: \")\"\n"
  },
  {
    "path": "tpl/parser/parser.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage parser\n\nimport (\n\t\"github.com/goplus/xgo/tpl/ast\"\n\t\"github.com/goplus/xgo/tpl/scanner\"\n\t\"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/qiniu/x/stream\"\n)\n\n// -----------------------------------------------------------------------------\n\n// RetProcParser parses a RetProc.\ntype RetProcParser = func(file *token.File, src []byte, offset int) (ast.Node, scanner.ErrorList)\n\n// Config configures the behavior of the parser.\ntype Config struct {\n\tParseRetProc RetProcParser\n}\n\n// ParseFile parses a file and returns the AST.\nfunc ParseFile(fset *token.FileSet, filename string, src any, conf *Config) (f *ast.File, err error) {\n\tb, err := stream.ReadSourceLocal(filename, src)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfile := fset.AddFile(filename, -1, len(b))\n\tf, errs := ParseEx(file, b, 0, conf)\n\tswitch errs.Len() {\n\tcase 0:\n\tcase 1:\n\t\terr = errs[0]\n\tdefault:\n\t\terrs.Sort()\n\t\terr = errs\n\t}\n\treturn\n}\n\n// ParseEx parses src[offset:] and returns the AST.\nfunc ParseEx(file *token.File, src []byte, offset int, conf *Config) (f *ast.File, errs scanner.ErrorList) {\n\tvar p parser\n\tp.init(file, src, offset)\n\tif conf != nil {\n\t\tp.parseRetProc = conf.ParseRetProc\n\t}\n\treturn p.parseFile(), p.errors\n}\n\n// -----------------------------------------------------------------------------\n\n// parser represents a parser.\ntype parser struct {\n\tscanner scanner.Scanner\n\tfile    *token.File\n\n\t// Current token\n\tpos token.Pos\n\ttok token.Token\n\tlit string\n\n\t// Callback to parse RetProc.\n\tparseRetProc RetProcParser\n\n\t// Error handling\n\terrors scanner.ErrorList\n}\n\nfunc (p *parser) init(file *token.File, src []byte, offset int) {\n\tp.file = file\n\teh := func(pos token.Position, msg string) { p.errors.Add(pos, msg) }\n\tp.scanner.InitEx(p.file, src, offset, eh, 0)\n\tp.next() // initialize first token\n}\n\n// next advances to the next token.\nfunc (p *parser) next() {\n\tt := p.scanner.Scan()\n\tp.pos, p.tok, p.lit = t.Pos, t.Tok, t.Lit\n}\n\nfunc (p *parser) errorExpected(pos token.Pos, msg string) {\n\tmsg = \"expected \" + msg\n\tif pos == p.pos {\n\t\t// the error happened at the current position;\n\t\t// make the error message more specific\n\t\tswitch {\n\t\tcase p.tok == token.SEMICOLON && p.lit == \"\\n\":\n\t\t\tmsg += \", found newline\"\n\t\tcase len(p.lit) > 0:\n\t\t\t// print 123 rather than 'INT', etc.\n\t\t\tmsg += \", found \" + p.lit\n\t\tdefault:\n\t\t\tmsg += \", found '\" + p.tok.String() + \"'\"\n\t\t}\n\t}\n\tp.error(pos, msg)\n}\n\n// expect consumes the current token if it matches the expected token.\n// If not, it adds an error.\nfunc (p *parser) expect(tok token.Token) token.Pos {\n\tpos := p.pos\n\tif p.tok != tok {\n\t\tp.errorExpected(pos, \"'\"+tok.String()+\"'\")\n\t}\n\tp.next() // make progress\n\treturn pos\n}\n\nfunc (p *parser) error(pos token.Pos, msg string) {\n\tepos := p.file.Position(pos)\n\tp.errors.Add(epos, msg)\n}\n\n// parseFile parses a file and returns the AST.\nfunc (p *parser) parseFile() *ast.File {\n\tfile := &ast.File{\n\t\tFileStart: p.pos,\n\t}\n\n\tfor p.tok != token.EOF {\n\t\trule := p.parseRule()\n\t\tif rule == nil {\n\t\t\tbreak\n\t\t}\n\t\tfile.Decls = append(file.Decls, rule)\n\t}\n\n\treturn file\n}\n\nfunc (p *parser) parseIdent() *ast.Ident {\n\tpos := p.pos\n\tname := \"_\"\n\tif p.tok == token.IDENT {\n\t\tname = p.lit\n\t\tp.next()\n\t} else {\n\t\tp.errorExpected(p.pos, \"'IDENT'\")\n\t}\n\treturn &ast.Ident{NamePos: pos, Name: name}\n}\n\n// parseRule parses a rule:\n//\n//\tIDENT '=' expr ';'\n//\tIDENT '=' expr => { ... } ';'\nfunc (p *parser) parseRule() *ast.Rule {\n\tif p.tok != token.IDENT {\n\t\tp.errorExpected(p.pos, \"'IDENT'\")\n\t\treturn nil\n\t}\n\n\tname := p.parseIdent()\n\ttokPos := p.expect(token.ASSIGN)\n\texpr := p.parseExpr()\n\tif expr == nil {\n\t\treturn nil\n\t}\n\n\tvar retProc ast.Node\n\tif p.tok == token.DRARROW { // => { ... }\n\t\tif off, end, ok := p.lambdaExpr(); ok {\n\t\t\tif p.parseRetProc != nil {\n\t\t\t\tfile := p.file\n\t\t\t\tbase := file.Base()\n\t\t\t\tsrc := p.scanner.CodeTo(int(end) - base)\n\t\t\t\texpr, err := p.parseRetProc(file, src, int(off)-base)\n\t\t\t\tif err == nil {\n\t\t\t\t\tretProc = expr\n\t\t\t\t} else {\n\t\t\t\t\tp.errors = append(p.errors, err...)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tp.expect(token.SEMICOLON)\n\treturn &ast.Rule{\n\t\tName:    name,\n\t\tTokPos:  tokPos,\n\t\tExpr:    expr,\n\t\tRetProc: retProc,\n\t}\n}\n\nfunc (p *parser) lambdaExpr() (start, end token.Pos, ok bool) {\n\tstart = p.pos // => {\n\tp.next()\n\tp.expect(token.LBRACE)\n\tlevel := 1\n\tfor {\n\t\tswitch p.tok {\n\t\tcase token.RBRACE:\n\t\t\tlevel--\n\t\t\tif level == 0 { // }\n\t\t\t\tp.next()\n\t\t\t\tend, ok = p.pos, true\n\t\t\t\treturn\n\t\t\t}\n\t\tcase token.LBRACE:\n\t\t\tlevel++\n\t\tcase token.EOF:\n\t\t\treturn\n\t\t}\n\t\tp.next()\n\t}\n}\n\n// parseExpr: termList % '|'\nfunc (p *parser) parseExpr() ast.Expr {\n\ttermList := p.parseTermList()\n\tfor p.tok != token.OR {\n\t\treturn termList\n\t}\n\n\toptions := make([]ast.Expr, 0, 4)\n\toptions = append(options, termList)\n\tfor p.tok == token.OR {\n\t\tp.next()\n\t\ttermList = p.parseTermList()\n\t\toptions = append(options, termList)\n\t}\n\treturn &ast.Choice{Options: options}\n}\n\n// parseTermList: +term\nfunc (p *parser) parseTermList() ast.Expr {\n\tterms := make([]ast.Expr, 0, 1)\n\tfor {\n\t\tterm, ok := p.parseTerm()\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\t\tterms = append(terms, term)\n\t}\n\tswitch n := len(terms); n {\n\tcase 1:\n\t\treturn terms[0]\n\tcase 0:\n\t\tp.error(p.pos, \"expected factor\")\n\t\tfallthrough // TODO(xsw): BadExpr\n\tdefault:\n\t\treturn &ast.Sequence{Items: terms}\n\t}\n}\n\n// parseTerm: term2 % '%'\nfunc (p *parser) parseTerm() (ast.Expr, bool) {\n\tx, ok := p.parseTerm2()\n\tif !ok {\n\t\treturn nil, false\n\t}\n\n\tfor p.tok == token.REM {\n\t\topPos := p.pos\n\t\tp.next()\n\t\ty, ok := p.parseTerm2()\n\t\tif !ok {\n\t\t\tp.error(p.pos, \"expected factor\")\n\t\t\treturn x, false\n\t\t}\n\t\tx = &ast.BinaryExpr{\n\t\t\tX:     x,\n\t\t\tOpPos: opPos,\n\t\t\tOp:    token.REM,\n\t\t\tY:     y,\n\t\t}\n\t}\n\treturn x, true\n}\n\n// parseTerm2: factor % \"++\"\nfunc (p *parser) parseTerm2() (ast.Expr, bool) {\n\tx, ok := p.parseFactor()\n\tif !ok {\n\t\treturn nil, false\n\t}\n\n\tfor p.tok == token.INC {\n\t\topPos := p.pos\n\t\tp.next()\n\t\ty, ok := p.parseFactor()\n\t\tif !ok {\n\t\t\tp.error(p.pos, \"expected factor\")\n\t\t\treturn x, false\n\t\t}\n\t\tx = &ast.BinaryExpr{\n\t\t\tX:     x,\n\t\t\tOpPos: opPos,\n\t\t\tOp:    token.INC,\n\t\t\tY:     y,\n\t\t}\n\t}\n\treturn x, true\n}\n\n// parseFactor: IDENT | CHAR | STRING | ('*' | '+' | '?') factor | '(' expr ')'\nfunc (p *parser) parseFactor() (ast.Expr, bool) {\n\tswitch tok := p.tok; tok {\n\tcase token.IDENT:\n\t\tident := &ast.Ident{\n\t\t\tNamePos: p.pos,\n\t\t\tName:    p.lit,\n\t\t}\n\t\tp.next()\n\t\treturn ident, true\n\n\tcase token.CHAR, token.STRING:\n\t\tlit := &ast.BasicLit{\n\t\t\tValuePos: p.pos,\n\t\t\tKind:     tok,\n\t\t\tValue:    p.lit,\n\t\t}\n\t\tp.next()\n\t\treturn lit, true\n\n\tcase token.MUL, token.ADD, token.QUESTION:\n\t\topPos := p.pos\n\t\tp.next()\n\n\t\tfactor, ok := p.parseFactor()\n\t\tif !ok {\n\t\t\tp.error(p.pos, \"expected factor\")\n\t\t}\n\t\tret := &ast.UnaryExpr{\n\t\t\tOpPos: opPos,\n\t\t\tOp:    tok,\n\t\t\tX:     factor,\n\t\t}\n\t\treturn ret, true\n\n\tcase token.LPAREN:\n\t\tp.next()\n\t\texpr := p.parseExpr()\n\t\tp.expect(token.RPAREN)\n\t\treturn expr, true\n\n\tdefault:\n\t\treturn nil, false\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/parser/parser_test.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage parser_test\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\tgopp \"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/tpl/ast\"\n\t\"github.com/goplus/xgo/tpl/parser\"\n\t\"github.com/goplus/xgo/tpl/parser/parsertest\"\n\t\"github.com/goplus/xgo/tpl/scanner\"\n\t\"github.com/goplus/xgo/tpl/token\"\n)\n\nfunc testFrom(t *testing.T, pkgDir, sel string) {\n\tif sel != \"\" && !strings.Contains(pkgDir, sel) {\n\t\treturn\n\t}\n\tt.Helper()\n\tlog.Println(\"Parsing\", pkgDir)\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, pkgDir+\"/in.xgo\", nil, &parser.Config{\n\t\tParseRetProc: func(file *token.File, src []byte, offset int) (ast.Node, scanner.ErrorList) {\n\t\t\treturn gopp.ParseExprEx(file, src, offset, 0)\n\t\t},\n\t})\n\tif err != nil {\n\t\tif errs, ok := err.(scanner.ErrorList); ok {\n\t\t\tfor _, e := range errs {\n\t\t\t\tt.Log(e)\n\t\t\t}\n\t\t}\n\t\tt.Fatal(\"ParseFile failed:\", err, reflect.TypeOf(err))\n\t}\n\tb, _ := os.ReadFile(pkgDir + \"/out.expect\")\n\tparsertest.Expect(t, pkgDir+\"/result.txt\", f, b)\n}\n\nfunc testFromDir(t *testing.T, sel, relDir string) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = path.Join(dir, relDir)\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"ReadDir failed:\", err)\n\t}\n\tfor _, fi := range fis {\n\t\tname := fi.Name()\n\t\tif strings.HasPrefix(name, \"_\") {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttestFrom(t, dir+\"/\"+name, sel)\n\t\t})\n\t}\n}\n\nfunc TestFromTestdata(t *testing.T) {\n\ttestFromDir(t, \"\", \"./_testdata\")\n}\n"
  },
  {
    "path": "tpl/parser/parsertest/parsertest.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage parsertest\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/tpl/ast\"\n\t\"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/qiniu/x/test\"\n)\n\n// -----------------------------------------------------------------------------\n\nvar (\n\ttyNode   = reflect.TypeOf((*ast.Node)(nil)).Elem()\n\ttyString = reflect.TypeOf(\"\")\n\ttyToken  = reflect.TypeOf(token.Token(0))\n)\n\n// FprintNode prints a tpl AST node.\nfunc FprintNode(w io.Writer, lead string, v any, prefix, indent string) {\n\tval := reflect.ValueOf(v)\n\tswitch val.Kind() {\n\tcase reflect.Slice:\n\t\tn := val.Len()\n\t\tif n > 0 && lead != \"\" {\n\t\t\tio.WriteString(w, lead)\n\t\t}\n\t\tfor i := 0; i < n; i++ {\n\t\t\tFprintNode(w, \"\", val.Index(i).Interface(), prefix, indent)\n\t\t}\n\tcase reflect.Ptr:\n\t\tt := val.Type()\n\t\tif val.IsNil() {\n\t\t\treturn\n\t\t}\n\t\tif t.Implements(tyNode) {\n\t\t\tif lead != \"\" {\n\t\t\t\tio.WriteString(w, lead)\n\t\t\t}\n\t\t\telem, tyElem := val.Elem(), t.Elem()\n\t\t\tfmt.Fprintf(w, \"%s%v:\\n\", prefix, tyElem)\n\t\t\tn := elem.NumField()\n\t\t\tprefix += indent\n\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\tsf := tyElem.Field(i)\n\t\t\t\tif sf.Name == \"RetProc\" { // skip RetProc field, see xgo/tpl/ast.Rule\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tsfv := elem.Field(i).Interface()\n\t\t\t\tswitch sf.Type {\n\t\t\t\tcase tyString, tyToken:\n\t\t\t\t\tfmt.Fprintf(w, \"%s%v: %v\\n\", prefix, sf.Name, sfv)\n\t\t\t\tdefault:\n\t\t\t\t\tFprintNode(w, fmt.Sprintf(\"%s%v:\\n\", prefix, sf.Name), sfv, prefix+indent, indent)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tlog.Panicln(\"FprintNode unexpected type:\", t)\n\t\t}\n\tcase reflect.Int, reflect.Bool, reflect.Invalid:\n\t\t// skip\n\tdefault:\n\t\tlog.Panicln(\"FprintNode unexpected kind:\", val.Kind(), \"type:\", val.Type())\n\t}\n}\n\n// Fprint prints a tpl ast.File node.\nfunc Fprint(w io.Writer, f *ast.File) {\n\tFprintNode(w, \"\", f.Decls, \"\", \"  \")\n}\n\n// Expect asserts a tpl AST equals output or not.\nfunc Expect(t *testing.T, outfile string, f *ast.File, expected []byte) {\n\tb := bytes.NewBuffer(nil)\n\tFprint(b, f)\n\tif test.Diff(t, outfile, b.Bytes(), []byte(expected)) {\n\t\tt.Fatal(\"tpl.Parser: unexpect result\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/scanner/_testdata/cstr/go.expect",
    "content": "1 IDENT c\n2 . \n3 IDENT printf\n10 IDENT c\n11 STRING \"Hello\"\n18 ; \n\n19 IDENT std\n22 . \n23 IDENT print\n29 IDENT py\n31 STRING \"Hello\"\n38 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/cstr/gop.expect",
    "content": "1 IDENT c\n2 . \n3 IDENT printf\n10 CSTRING \"Hello\"\n18 ; \n\n19 IDENT std\n22 . \n23 IDENT print\n29 PYSTRING \"Hello\"\n38 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/cstr/in.xgo",
    "content": "c.printf c\"Hello\"\nstd.print py\"Hello\"\n"
  },
  {
    "path": "tpl/scanner/_testdata/cstr/tpl.expect",
    "content": "1 IDENT c\n2 . \n3 IDENT printf\n10 IDENT c\n11 STRING \"Hello\"\n18 ; \n\n19 IDENT std\n22 . \n23 IDENT print\n29 IDENT py\n31 STRING \"Hello\"\n38 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/num/go.expect",
    "content": "1 IDENT echo\n6 INT 1_002\n11 ; \n\n12 IDENT echo\n17 INT 2\n18 + \n19 IMAG 3i\n21 ; \n\n22 IDENT echo\n27 FLOAT 2.\n29 IDENT string\n35 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/num/gop.expect",
    "content": "1 IDENT echo\n6 INT 1_002\n11 ; \n\n12 IDENT echo\n17 INT 2\n18 + \n19 IMAG 3i\n21 ; \n\n22 IDENT echo\n27 FLOAT 2.\n29 UNIT string\n35 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/num/in.xgo",
    "content": "echo 1_002\necho 2+3i\necho 2.string\n"
  },
  {
    "path": "tpl/scanner/_testdata/num/tpl.expect",
    "content": "1 IDENT echo\n6 INT 1_002\n11 ; \n\n12 IDENT echo\n17 INT 2\n18 + \n19 IMAG 3i\n21 ; \n\n22 IDENT echo\n27 FLOAT 2.\n29 UNIT string\n35 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/pow/go.expect",
    "content": "1 IDENT echo\n6 INT 2\n7 * \n8 * \n9 INT 3\n10 ; \n\n11 IDENT echo\n16 STRING `abc`\n21 [ \n22 INT 0\n23 ] \n24 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/pow/gop.expect",
    "content": "1 IDENT echo\n6 INT 2\n7 * \n8 * \n9 INT 3\n10 ; \n\n11 IDENT echo\n16 STRING `abc`\n21 [ \n22 INT 0\n23 ] \n24 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/pow/in.xgo",
    "content": "echo 2**3\necho `abc`[0]\n"
  },
  {
    "path": "tpl/scanner/_testdata/pow/tpl.expect",
    "content": "1 IDENT echo\n6 INT 2\n7 * \n8 * \n9 INT 3\n10 ; \n\n11 IDENT echo\n16 STRING `abc`\n21 [ \n22 INT 0\n23 ] \n24 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/rat/go.expect",
    "content": "1 IDENT echo\n6 INT 1\n7 / \n8 INT 3\n9 IDENT r\n10 ; \n\n11 IDENT echo\n16 FLOAT 1.23\n20 IDENT r\n21 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/rat/gop.expect",
    "content": "1 IDENT echo\n6 INT 1\n7 / \n8 RAT 3r\n10 ; \n\n11 IDENT echo\n16 RAT 1.23r\n21 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/rat/in.xgo",
    "content": "echo 1/3r\necho 1.23r\n"
  },
  {
    "path": "tpl/scanner/_testdata/rat/tpl.expect",
    "content": "1 IDENT echo\n6 INT 1\n7 / \n8 RAT 3r\n10 ; \n\n11 IDENT echo\n16 RAT 1.23r\n21 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/unit/go.expect",
    "content": "1 import import\n8 STRING \"time\"\n14 ; \n\n16 IDENT x\n18 := \n21 STRING \"Let's wait for a second\"\n46 ; \n\n47 IDENT echo\n52 IDENT x\n53 ; \n\n55 IDENT wait\n60 INT 0x10\n64 IDENT s\n65 ; \n\n66 IDENT wait\n71 FLOAT .1\n73 IDENT s\n74 ; \n\n75 IDENT wait\n80 FLOAT 1.\n82 IDENT s\n83 ; \n\n84 IDENT wait\n89 FLOAT 1.e7\n93 IDENT s\n94 ; \n\n95 IDENT wait\n100 FLOAT 1.2\n103 IDENT s\n104 ; \n\n106 IDENT time\n110 . \n111 IDENT sleep\n117 IMAG 100i\n121 IDENT m\n122 ; \n\n124 IDENT spend\n130 FLOAT 10.0\n134 IDENT rmb\n137 ; \n\n138 IDENT spend\n144 FLOAT 10.\n147 IDENT rmb\n150 ; \n\n152 IDENT echo\n157 STRING \"Hello, world\"\n171 ... \n"
  },
  {
    "path": "tpl/scanner/_testdata/unit/gop.expect",
    "content": "1 import import\n8 STRING \"time\"\n14 ; \n\n16 IDENT x\n18 := \n21 STRING \"Let's wait for a second\"\n46 ; \n\n47 IDENT echo\n52 IDENT x\n53 ; \n\n55 IDENT wait\n60 INT 0x10\n64 UNIT s\n65 ; \n\n66 IDENT wait\n71 FLOAT .1\n73 UNIT s\n74 ; \n\n75 IDENT wait\n80 FLOAT 1.\n82 UNIT s\n83 ; \n\n84 IDENT wait\n89 FLOAT 1.e7\n93 UNIT s\n94 ; \n\n95 IDENT wait\n100 FLOAT 1.2\n103 UNIT s\n104 ; \n\n106 IDENT time\n110 . \n111 IDENT sleep\n117 INT 100\n120 UNIT im\n122 ; \n\n124 IDENT spend\n130 FLOAT 10.0\n134 UNIT rmb\n137 ; \n\n138 IDENT spend\n144 FLOAT 10.\n147 UNIT rmb\n150 ; \n\n152 IDENT echo\n157 STRING \"Hello, world\"\n171 ... \n174 ; \n\n"
  },
  {
    "path": "tpl/scanner/_testdata/unit/in.xgo",
    "content": "import \"time\"\n\nx := \"Let's wait for a second\"\necho x\n\nwait 0x10s\nwait .1s\nwait 1.s\nwait 1.e7s\nwait 1.2s\n\ntime.sleep 100im\n\nspend 10.0rmb\nspend 10.rmb\n\necho \"Hello, world\"...\n"
  },
  {
    "path": "tpl/scanner/_testdata/unit/tpl.expect",
    "content": "1 IDENT import\n8 STRING \"time\"\n14 ; \n\n16 IDENT x\n18 := \n21 STRING \"Let's wait for a second\"\n46 ; \n\n47 IDENT echo\n52 IDENT x\n53 ; \n\n55 IDENT wait\n60 INT 0x10\n64 UNIT s\n65 ; \n\n66 IDENT wait\n71 FLOAT .1\n73 UNIT s\n74 ; \n\n75 IDENT wait\n80 FLOAT 1.\n82 UNIT s\n83 ; \n\n84 IDENT wait\n89 FLOAT 1.e7\n93 UNIT s\n94 ; \n\n95 IDENT wait\n100 FLOAT 1.2\n103 UNIT s\n104 ; \n\n106 IDENT time\n110 . \n111 IDENT sleep\n117 INT 100\n120 UNIT im\n122 ; \n\n124 IDENT spend\n130 FLOAT 10.0\n134 UNIT rmb\n137 ; \n\n138 IDENT spend\n144 FLOAT 10.\n147 UNIT rmb\n150 ; \n\n152 IDENT echo\n157 STRING \"Hello, world\"\n171 ... \n174 ; \n\n"
  },
  {
    "path": "tpl/scanner/error.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage scanner\n\nimport (\n\t\"go/scanner\"\n\t\"io\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Error is an alias of go/scanner.Error\ntype Error = scanner.Error\n\n// ErrorList is an alias of go/scanner.ErrorList\ntype ErrorList = scanner.ErrorList\n\n// PrintError is a utility function that prints a list of errors to w,\n// one error per line, if the err parameter is an ErrorList. Otherwise\n// it prints the err string.\nfunc PrintError(w io.Writer, err error) {\n\tscanner.PrintError(w, err)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/scanner/scandir_test.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage scanner_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/tpl/scanner/scannertest\"\n\t\"github.com/qiniu/x/test\"\n)\n\nfunc testScan(\n\tt *testing.T, pkgDir string, in []byte,\n\texpFile string, scan func(w io.Writer, in []byte)) {\n\texpect, _ := os.ReadFile(pkgDir + \"/\" + expFile)\n\tvar b bytes.Buffer\n\tscan(&b, in)\n\tout := b.Bytes()\n\tif test.Diff(t, pkgDir+\"/result.txt\", out, expect) {\n\t\tt.Fatal(expFile, \": unexpect result\")\n\t}\n}\n\nfunc testFrom(t *testing.T, pkgDir, sel string) {\n\tif sel != \"\" && !strings.Contains(pkgDir, sel) {\n\t\treturn\n\t}\n\tt.Helper()\n\tlog.Println(\"Scanning\", pkgDir)\n\tin, err := os.ReadFile(pkgDir + \"/in.xgo\")\n\tif err != nil {\n\t\tt.Fatal(\"Scanning\", pkgDir, \"-\", err)\n\t}\n\ttestScan(t, pkgDir, in, \"tpl.expect\", scannertest.Scan)\n\ttestScan(t, pkgDir, in, \"go.expect\", scannertest.GoScan)\n\ttestScan(t, pkgDir, in, \"gop.expect\", scannertest.GopScan)\n}\n\nfunc testFromDir(t *testing.T, sel, relDir string) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = path.Join(dir, relDir)\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"ReadDir failed:\", err)\n\t}\n\tfor _, fi := range fis {\n\t\tname := fi.Name()\n\t\tif strings.HasPrefix(name, \"_\") {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tpkgDir := dir + \"/\" + name\n\t\t\ttestFrom(t, pkgDir, sel)\n\t\t})\n\t}\n}\n\nfunc TestFromTestdata(t *testing.T) {\n\ttestFromDir(t, \"\", \"./_testdata\")\n}\n"
  },
  {
    "path": "tpl/scanner/scanner.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage scanner\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/goplus/xgo/tpl/types\"\n)\n\n// An ErrorHandler may be provided to Scanner.Init. If a syntax error is\n// encountered and a handler was installed, the handler is called with a\n// position and an error message. The position points to the beginning of\n// the offending token.\ntype ErrorHandler func(pos token.Position, msg string)\n\n// A Scanner holds the scanner's internal state while processing\n// a given text.  It can be allocated as part of another data\n// structure but must be initialized via Init before use.\ntype Scanner struct {\n\t// immutable state\n\tfile *token.File  // source file handle\n\tdir  string       // directory portion of file.Name()\n\tsrc  []byte       // source\n\terr  ErrorHandler // error reporting; or nil\n\tmode Mode         // scanning mode\n\n\t// scanning state\n\tch         rune // current character\n\toffset     int  // character offset\n\trdOffset   int  // reading offset (position after current character)\n\tlineOffset int  // current line offset\n\tnParen     int\n\tunitVal    string\n\tinsertSemi bool // insert a semicolon before next newline\n\n\t// public state - ok to modify\n\tErrorCount int // number of errors encountered\n}\n\nconst bom = 0xFEFF // byte order mark, only permitted as very first character\n\n// Read the next Unicode char into s.ch.\n// s.ch < 0 means end-of-file.\nfunc (s *Scanner) next() {\n\tif s.rdOffset < len(s.src) {\n\t\ts.offset = s.rdOffset\n\t\tif s.ch == '\\n' {\n\t\t\ts.lineOffset = s.offset\n\t\t\ts.file.AddLine(s.offset)\n\t\t}\n\t\tr, w := rune(s.src[s.rdOffset]), 1\n\t\tswitch {\n\t\tcase r == 0:\n\t\t\ts.error(s.offset, \"illegal character NUL\")\n\t\tcase r >= 0x80:\n\t\t\t// not ASCII\n\t\t\tr, w = utf8.DecodeRune(s.src[s.rdOffset:])\n\t\t\tif r == utf8.RuneError && w == 1 {\n\t\t\t\ts.error(s.offset, \"illegal UTF-8 encoding\")\n\t\t\t} else if r == bom && s.offset > 0 {\n\t\t\t\ts.error(s.offset, \"illegal byte order mark\")\n\t\t\t}\n\t\t}\n\t\ts.rdOffset += w\n\t\ts.ch = r\n\t} else {\n\t\ts.offset = len(s.src)\n\t\tif s.ch == '\\n' {\n\t\t\ts.lineOffset = s.offset\n\t\t\ts.file.AddLine(s.offset)\n\t\t}\n\t\ts.ch = -1 // eof\n\t}\n}\n\n// peek returns the byte following the most recently read character without\n// advancing the scanner. If the scanner is at EOF, peek returns 0.\nfunc (s *Scanner) peek() byte {\n\tif s.rdOffset < len(s.src) {\n\t\treturn s.src[s.rdOffset]\n\t}\n\treturn 0\n}\n\n// A Mode value is a set of flags (or 0).\n// They control scanner behavior.\ntype Mode uint\n\nconst (\n\t// ScanComments means returning comments as COMMENT tokens\n\tScanComments Mode = 1 << iota\n\n\t// NoInsertSemis means don't automatically insert semicolons\n\tNoInsertSemis\n)\n\n// Init prepares the scanner s to tokenize the text src by setting the\n// scanner at the beginning of src. The scanner uses the file set file\n// for position information and it adds line information for each line.\n// It is ok to re-use the same file when re-scanning the same file as\n// line information which is already present is ignored. Init causes a\n// panic if the file size does not match the src size.\n//\n// Calls to Scan will invoke the error handler err if they encounter a\n// syntax error and err is not nil. Also, for each error encountered,\n// the Scanner field ErrorCount is incremented by one. The mode parameter\n// determines how comments are handled.\n//\n// Note that Init may call err if there is an error in the first character\n// of the file.\nfunc (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) {\n\t// Explicitly initialize all fields since a scanner may be reused.\n\tif file.Size() != len(src) {\n\t\tpanic(fmt.Sprintf(\"file size (%d) does not match src len (%d)\", file.Size(), len(src)))\n\t}\n\ts.InitEx(file, src, 0, err, mode)\n}\n\n// InitEx init the scanner with an offset (this means src[offset:] is all the code to scan).\nfunc (s *Scanner) InitEx(file *token.File, src []byte, offset int, err ErrorHandler, mode Mode) {\n\ts.file = file\n\ts.dir, _ = filepath.Split(file.Name())\n\ts.src = src\n\ts.err = err\n\ts.mode = mode\n\n\ts.ch = ' '\n\ts.offset = 0\n\ts.rdOffset = offset\n\ts.lineOffset = 0\n\ts.insertSemi = false\n\ts.ErrorCount = 0\n\n\ts.next()\n\tif s.ch == bom {\n\t\ts.next() // ignore BOM at file beginning\n\t}\n}\n\n// CodeTo returns the source code snippet for the given end.\nfunc (s *Scanner) CodeTo(end int) []byte {\n\treturn s.src[:end]\n}\n\nfunc (s *Scanner) error(offs int, msg string) {\n\tif s.err != nil {\n\t\ts.err(s.file.Position(s.file.Pos(offs)), msg)\n\t}\n\ts.ErrorCount++\n}\n\nfunc (s *Scanner) errorf(offs int, format string, args ...any) {\n\ts.error(offs, fmt.Sprintf(format, args...))\n}\n\nvar prefix = []byte(\"//line \")\n\nfunc (s *Scanner) interpretLineComment(text []byte) {\n\tif bytes.HasPrefix(text, prefix) {\n\t\t// get filename and line number, if any\n\t\tif i := bytes.LastIndex(text, []byte{':'}); i > 0 {\n\t\t\tif line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 {\n\t\t\t\t// valid //line filename:line comment\n\t\t\t\tfilename := string(bytes.TrimSpace(text[len(prefix):i]))\n\t\t\t\tif filename != \"\" {\n\t\t\t\t\tfilename = filepath.Clean(filename)\n\t\t\t\t\tif !filepath.IsAbs(filename) {\n\t\t\t\t\t\t// make filename relative to current directory\n\t\t\t\t\t\tfilename = filepath.Join(s.dir, filename)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// update scanner position\n\t\t\t\ts.file.AddLineInfo(\n\t\t\t\t\ts.lineOffset+len(text)+1, filename, line) // +len(text)+1 since comment applies to next line\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (s *Scanner) scanComment() string {\n\t// initial '/' already consumed; s.ch == '/' || s.ch == '*'\n\toffs := s.offset - 1 // position of initial '/'\n\thasCR := false\n\n\tif s.ch == '/' {\n\t\t//-style comment\n\t\ts.next()\n\t\tfor s.ch != '\\n' && s.ch >= 0 {\n\t\t\tif s.ch == '\\r' {\n\t\t\t\thasCR = true\n\t\t\t}\n\t\t\ts.next()\n\t\t}\n\t\tif offs == s.lineOffset {\n\t\t\t// comment starts at the beginning of the current line\n\t\t\ts.interpretLineComment(s.src[offs:s.offset])\n\t\t}\n\t\tgoto exit\n\t}\n\n\t/*-style comment */\n\ts.next()\n\tfor s.ch >= 0 {\n\t\tch := s.ch\n\t\tif ch == '\\r' {\n\t\t\thasCR = true\n\t\t}\n\t\ts.next()\n\t\tif ch == '*' && s.ch == '/' {\n\t\t\ts.next()\n\t\t\tgoto exit\n\t\t}\n\t}\n\n\ts.error(offs, \"comment not terminated\")\n\nexit:\n\tlit := s.src[offs:s.offset]\n\tif hasCR {\n\t\tlit = stripCR(lit)\n\t}\n\n\treturn string(lit)\n}\n\nfunc (s *Scanner) findLineEnd() bool {\n\t// initial '/' already consumed\n\n\tdefer func(offs int) {\n\t\t// reset scanner state to where it was upon calling findLineEnd\n\t\ts.ch = '/'\n\t\ts.offset = offs\n\t\ts.rdOffset = offs + 1\n\t\ts.next() // consume initial '/' again\n\t}(s.offset - 1)\n\n\t// read ahead until a newline, EOF, or non-comment token is found\n\tfor s.ch == '/' || s.ch == '*' {\n\t\tif s.ch == '/' {\n\t\t\t//-style comment always contains a newline\n\t\t\treturn true\n\t\t}\n\t\t/*-style comment: look for newline */\n\t\ts.next()\n\t\tfor s.ch >= 0 {\n\t\t\tch := s.ch\n\t\t\tif ch == '\\n' {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\ts.next()\n\t\t\tif ch == '*' && s.ch == '/' {\n\t\t\t\ts.next()\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\ts.skipWhitespace() // s.insertSemi is set\n\t\tif s.ch < 0 || s.ch == '\\n' {\n\t\t\treturn true\n\t\t}\n\t\tif s.ch != '/' {\n\t\t\t// non-comment token\n\t\t\treturn false\n\t\t}\n\t\ts.next() // consume '/'\n\t}\n\n\treturn false\n}\n\nfunc isLetter(ch rune) bool {\n\treturn 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)\n}\n\nfunc isDigit(ch rune) bool {\n\treturn '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)\n}\n\nfunc (s *Scanner) scanIdentifier() string {\n\toffs := s.offset\n\tfor isLetter(s.ch) || isDigit(s.ch) {\n\t\ts.next()\n\t}\n\treturn string(s.src[offs:s.offset])\n}\n\nfunc digitVal(ch rune) int {\n\tswitch {\n\tcase '0' <= ch && ch <= '9':\n\t\treturn int(ch - '0')\n\tcase 'a' <= lower(ch) && lower(ch) <= 'f':\n\t\treturn int(lower(ch) - 'a' + 10)\n\t}\n\treturn 16 // larger than any legal digit val\n}\n\nfunc lower(ch rune) rune     { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter\nfunc isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' }\nfunc isHex(ch rune) bool     { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f' }\n\n// digits accepts the sequence { digit | '_' }.\n// If base <= 10, digits accepts any decimal digit but records\n// the offset (relative to the source start) of a digit >= base\n// in *invalid, if *invalid < 0.\n// digits returns a bitset describing whether the sequence contained\n// digits (bit 0 is set), or separators '_' (bit 1 is set).\nfunc (s *Scanner) digits(base int, invalid *int) (digsep int) {\n\tif base <= 10 {\n\t\tmax := rune('0' + base)\n\t\tfor isDecimal(s.ch) || s.ch == '_' {\n\t\t\tds := 1\n\t\t\tif s.ch == '_' {\n\t\t\t\tds = 2\n\t\t\t} else if s.ch >= max && *invalid < 0 {\n\t\t\t\t*invalid = int(s.offset) // record invalid rune offset\n\t\t\t}\n\t\t\tdigsep |= ds\n\t\t\ts.next()\n\t\t}\n\t} else {\n\t\tfor isHex(s.ch) || s.ch == '_' {\n\t\t\tds := 1\n\t\t\tif s.ch == '_' {\n\t\t\t\tds = 2\n\t\t\t}\n\t\t\tdigsep |= ds\n\t\t\ts.next()\n\t\t}\n\t}\n\treturn\n}\n\nfunc (s *Scanner) scanNumber() (token.Token, string) {\n\toffs := s.offset\n\ttok := token.ILLEGAL\n\n\tbase := 10        // number base\n\tprefix := rune(0) // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b'\n\tdigsep := 0       // bit 0: digit present, bit 1: '_' present\n\tinvalid := -1     // index of invalid digit in literal, or < 0\n\n\t// integer part\n\tif s.ch != '.' {\n\t\ttok = token.INT\n\t\tif s.ch == '0' {\n\t\t\ts.next()\n\t\t\tswitch lower(s.ch) {\n\t\t\tcase 'x':\n\t\t\t\ts.next()\n\t\t\t\tbase, prefix = 16, 'x'\n\t\t\tcase 'o':\n\t\t\t\ts.next()\n\t\t\t\tbase, prefix = 8, 'o'\n\t\t\tcase 'b':\n\t\t\t\ts.next()\n\t\t\t\tbase, prefix = 2, 'b'\n\t\t\tdefault:\n\t\t\t\tbase, prefix = 8, '0'\n\t\t\t\tdigsep = 1 // leading 0\n\t\t\t}\n\t\t}\n\t\tdigsep |= s.digits(base, &invalid)\n\t}\n\n\t// fractional part\n\tif s.ch == '.' {\n\t\ttok = token.FLOAT\n\t\tif prefix == 'o' || prefix == 'b' {\n\t\t\ts.error(s.offset, \"invalid radix point in \"+litname(prefix))\n\t\t}\n\t\ts.next()\n\t\tdigsep |= s.digits(base, &invalid)\n\t}\n\n\tif digsep&1 == 0 {\n\t\ts.error(s.offset, litname(prefix)+\" has no digits\")\n\t}\n\n\t// exponent\n\tif e := lower(s.ch); e == 'e' || e == 'p' {\n\t\tswitch {\n\t\tcase e == 'e' && prefix != 0 && prefix != '0':\n\t\t\ts.errorf(s.offset, \"%q exponent requires decimal mantissa\", s.ch)\n\t\tcase e == 'p' && prefix != 'x':\n\t\t\ts.errorf(s.offset, \"%q exponent requires hexadecimal mantissa\", s.ch)\n\t\t}\n\t\ts.next()\n\t\ttok = token.FLOAT\n\t\tif s.ch == '+' || s.ch == '-' {\n\t\t\ts.next()\n\t\t}\n\t\tds := s.digits(10, nil)\n\t\tdigsep |= ds\n\t\tif ds&1 == 0 {\n\t\t\ts.error(s.offset, \"exponent has no digits\")\n\t\t}\n\t} else if prefix == 'x' && tok == token.FLOAT {\n\t\ts.error(s.offset, \"hexadecimal mantissa requires a 'p' exponent\")\n\t}\n\n\tif isLetter(s.ch) {\n\t\tid := s.scanIdentifier()\n\t\tswitch id {\n\t\tcase \"i\":\n\t\t\ttok = token.IMAG\n\t\tcase \"r\":\n\t\t\ttok = token.RAT\n\t\tdefault:\n\t\t\ts.unitVal = id\n\t\t}\n\t}\n\n\tlit := string(s.src[offs : s.offset-len(s.unitVal)])\n\tif tok == token.INT && invalid >= 0 {\n\t\ts.errorf(invalid, \"invalid digit %q in %s\", lit[invalid-offs], litname(prefix))\n\t}\n\tif digsep&2 != 0 {\n\t\tif i := invalidSep(lit); i >= 0 {\n\t\t\ts.error(offs+i, \"'_' must separate successive digits\")\n\t\t}\n\t}\n\n\treturn tok, lit\n}\n\nfunc litname(prefix rune) string {\n\tswitch prefix {\n\tcase 'x':\n\t\treturn \"hexadecimal literal\"\n\tcase 'o', '0':\n\t\treturn \"octal literal\"\n\tcase 'b':\n\t\treturn \"binary literal\"\n\t}\n\treturn \"decimal literal\"\n}\n\n// invalidSep returns the index of the first invalid separator in x, or -1.\nfunc invalidSep(x string) int {\n\tx1 := ' ' // prefix char, we only care if it's 'x'\n\td := '.'  // digit, one of '_', '0' (a digit), or '.' (anything else)\n\ti := 0\n\n\t// a prefix counts as a digit\n\tif len(x) >= 2 && x[0] == '0' {\n\t\tx1 = lower(rune(x[1]))\n\t\tif x1 == 'x' || x1 == 'o' || x1 == 'b' {\n\t\t\td = '0'\n\t\t\ti = 2\n\t\t}\n\t}\n\n\t// mantissa and exponent\n\tfor ; i < len(x); i++ {\n\t\tp := d // previous digit\n\t\td = rune(x[i])\n\t\tswitch {\n\t\tcase d == '_':\n\t\t\tif p != '0' {\n\t\t\t\treturn i\n\t\t\t}\n\t\tcase isDecimal(d) || x1 == 'x' && isHex(d):\n\t\t\td = '0'\n\t\tdefault:\n\t\t\tif p == '_' {\n\t\t\t\treturn i - 1\n\t\t\t}\n\t\t\td = '.'\n\t\t}\n\t}\n\tif d == '_' {\n\t\treturn len(x) - 1\n\t}\n\n\treturn -1\n}\n\n// scanEscape parses an escape sequence where rune is the accepted\n// escaped quote. In case of a syntax error, it stops at the offending\n// character (without consuming it) and returns false. Otherwise\n// it returns true.\nfunc (s *Scanner) scanEscape(quote rune) bool {\n\toffs := s.offset\n\n\tvar n int\n\tvar base, max uint32\n\tswitch s.ch {\n\tcase 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\\\', quote:\n\t\ts.next()\n\t\treturn true\n\tcase '0', '1', '2', '3', '4', '5', '6', '7':\n\t\tn, base, max = 3, 8, 255\n\tcase 'x':\n\t\ts.next()\n\t\tn, base, max = 2, 16, 255\n\tcase 'u':\n\t\ts.next()\n\t\tn, base, max = 4, 16, unicode.MaxRune\n\tcase 'U':\n\t\ts.next()\n\t\tn, base, max = 8, 16, unicode.MaxRune\n\tdefault:\n\t\tmsg := \"unknown escape sequence\"\n\t\tif s.ch < 0 {\n\t\t\tmsg = \"escape sequence not terminated\"\n\t\t}\n\t\ts.error(offs, msg)\n\t\treturn false\n\t}\n\n\tvar x uint32\n\tfor n > 0 {\n\t\td := uint32(digitVal(s.ch))\n\t\tif d >= base {\n\t\t\tmsg := fmt.Sprintf(\"illegal character %#U in escape sequence\", s.ch)\n\t\t\tif s.ch < 0 {\n\t\t\t\tmsg = \"escape sequence not terminated\"\n\t\t\t}\n\t\t\ts.error(s.offset, msg)\n\t\t\treturn false\n\t\t}\n\t\tx = x*base + d\n\t\ts.next()\n\t\tn--\n\t}\n\n\tif x > max || 0xD800 <= x && x < 0xE000 {\n\t\ts.error(offs, \"escape sequence is invalid Unicode code point\")\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (s *Scanner) scanRune() string {\n\t// '\\'' opening already consumed\n\toffs := s.offset - 1\n\n\tvalid := true\n\tn := 0\n\tfor {\n\t\tch := s.ch\n\t\tif ch == '\\n' || ch < 0 {\n\t\t\t// only report error if we don't have one already\n\t\t\tif valid {\n\t\t\t\ts.error(offs, \"rune literal not terminated\")\n\t\t\t\tvalid = false\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\ts.next()\n\t\tif ch == '\\'' {\n\t\t\tbreak\n\t\t}\n\t\tn++\n\t\tif ch == '\\\\' {\n\t\t\tif !s.scanEscape('\\'') {\n\t\t\t\tvalid = false\n\t\t\t}\n\t\t\t// continue to read to closing quote\n\t\t}\n\t}\n\n\tif valid && n != 1 {\n\t\ts.error(offs, \"illegal rune literal\")\n\t}\n\n\treturn string(s.src[offs:s.offset])\n}\n\nfunc (s *Scanner) scanSharpComment() string {\n\t// '#' opening already consumed\n\toffs := s.offset - 1\n\n\tfor {\n\t\tch := s.ch\n\t\tif ch == '\\n' || ch < 0 {\n\t\t\tbreak\n\t\t}\n\t\ts.next()\n\t}\n\n\treturn string(s.src[offs:s.offset])\n}\n\nfunc (s *Scanner) scanString() string {\n\t// '\"' opening already consumed\n\toffs := s.offset - 1\n\n\tfor {\n\t\tch := s.ch\n\t\tif ch == '\\n' || ch < 0 {\n\t\t\ts.error(offs, \"string literal not terminated\")\n\t\t\tbreak\n\t\t}\n\t\ts.next()\n\t\tif ch == '\"' {\n\t\t\tbreak\n\t\t}\n\t\tif ch == '\\\\' {\n\t\t\ts.scanEscape('\"')\n\t\t}\n\t}\n\n\treturn string(s.src[offs:s.offset])\n}\n\nfunc stripCR(b []byte) []byte {\n\tc := make([]byte, len(b))\n\ti := 0\n\tfor _, ch := range b {\n\t\tif ch != '\\r' {\n\t\t\tc[i] = ch\n\t\t\ti++\n\t\t}\n\t}\n\treturn c[:i]\n}\n\nfunc (s *Scanner) scanRawString() string {\n\t// '`' opening already consumed\n\toffs := s.offset - 1\n\n\thasCR := false\n\tfor {\n\t\tch := s.ch\n\t\tif ch < 0 {\n\t\t\ts.error(offs, \"raw string literal not terminated\")\n\t\t\tbreak\n\t\t}\n\t\ts.next()\n\t\tif ch == '`' {\n\t\t\tbreak\n\t\t}\n\t\tif ch == '\\r' {\n\t\t\thasCR = true\n\t\t}\n\t}\n\n\tlit := s.src[offs:s.offset]\n\tif hasCR {\n\t\tlit = stripCR(lit)\n\t}\n\n\treturn string(lit)\n}\n\nfunc (s *Scanner) skipWhitespace() {\n\tfor s.ch == ' ' || s.ch == '\\t' || s.ch == '\\n' && !s.insertSemi || s.ch == '\\r' {\n\t\ts.next()\n\t}\n}\n\n// Helper functions for scanning multi-byte tokens such as >> += >>= .\n// Different routines recognize different length tok_i based on matches\n// of ch_i. If a token ends in '=', the result is tok1 or tok3\n// respectively. Otherwise, the result is tok0 if there was no other\n// matching character, or tok2 if the matching character was ch2.\n\nfunc (s *Scanner) switch2(tok0, tok1 token.Token) token.Token {\n\tif s.ch == '=' {\n\t\ts.next()\n\t\treturn tok1\n\t}\n\treturn tok0\n}\n\nfunc (s *Scanner) switch3(tok0, tok1 token.Token, ch2 rune, tok2 token.Token) token.Token {\n\tif s.ch == '=' {\n\t\ts.next()\n\t\treturn tok1\n\t}\n\tif s.ch == ch2 {\n\t\ts.next()\n\t\treturn tok2\n\t}\n\treturn tok0\n}\n\nfunc (s *Scanner) switch4(tok0, tok1 token.Token, ch2 rune, tok2, tok3 token.Token) token.Token {\n\tif s.ch == '=' {\n\t\ts.next()\n\t\treturn tok1\n\t}\n\tif s.ch == ch2 {\n\t\ts.next()\n\t\tif s.ch == '=' {\n\t\t\ts.next()\n\t\t\treturn tok3\n\t\t}\n\t\treturn tok2\n\t}\n\treturn tok0\n}\n\nfunc (s *Scanner) tokSEMICOLON() token.Token {\n\ts.nParen = 0\n\treturn token.SEMICOLON\n}\n\n// Scan scans the next token and returns the token position, the token,\n// and its literal string if applicable. The source end is indicated by\n// token.EOF.\n//\n// If the returned token is a literal (IDENT, INT, FLOAT, IMAG, CHAR, STRING)\n// or COMMENT, the literal string has the corresponding value.\n//\n// If the returned token is SEMICOLON, the corresponding\n// literal string is \";\" if the semicolon was present in the source,\n// and \"\\n\" if the semicolon was inserted because of a newline or\n// at EOF.\n//\n// If the returned token is ILLEGAL, the literal string is the offending\n// character.\n//\n// In all other cases, Scan returns an empty literal string.\n//\n// For more tolerant parsing, Scan will return a valid token if\n// possible even if a syntax error was encountered. Thus, even\n// if the resulting token sequence contains no illegal tokens,\n// a client may not assume that no error occurred. Instead it\n// must check the scanner's ErrorCount or the number of calls\n// of the error handler, if there was one installed.\n//\n// Scan adds line information to the file added to the file\n// set with Init. Token positions are relative to that file\n// and thus relative to the file set.\nfunc (s *Scanner) Scan() (t types.Token) {\nscanAgain:\n\ts.skipWhitespace()\n\n\t// current token start\n\tt.Pos = s.file.Pos(s.offset)\n\n\t// determine token value\n\tinsertSemi := false\n\tif s.unitVal != \"\" { // number with unit\n\t\tinsertSemi = true\n\t\tt.Pos -= token.Pos(len(s.unitVal))\n\t\tt.Tok, t.Lit = token.UNIT, s.unitVal\n\t\ts.unitVal = \"\"\n\t\tgoto done\n\t}\n\tswitch ch := s.ch; {\n\tcase isLetter(ch):\n\t\tinsertSemi = true\n\t\tt.Lit = s.scanIdentifier()\n\t\tt.Tok = token.IDENT\n\tcase isDecimal(ch) || ch == '.' && isDecimal(rune(s.peek())):\n\t\tinsertSemi = true\n\t\tt.Tok, t.Lit = s.scanNumber()\n\tdefault:\n\t\ts.next() // always make progress\n\t\tswitch ch {\n\t\tcase -1:\n\t\t\tif s.insertSemi {\n\t\t\t\ts.insertSemi = false // EOF consumed\n\t\t\t\tt.Tok, t.Lit = s.tokSEMICOLON(), \"\\n\"\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.Tok = token.EOF\n\t\tcase '\\n':\n\t\t\t// we only reach here if s.insertSemi was\n\t\t\t// set in the first place and exited early\n\t\t\t// from s.skipWhitespace()\n\t\t\ts.insertSemi = false // newline consumed\n\t\t\tt.Tok, t.Lit = s.tokSEMICOLON(), \"\\n\"\n\t\t\treturn\n\t\tcase '\"':\n\t\t\tinsertSemi = true\n\t\t\tt.Tok = token.STRING\n\t\t\tt.Lit = s.scanString()\n\t\tcase '\\'':\n\t\t\tinsertSemi = true\n\t\t\tt.Tok = token.CHAR\n\t\t\tt.Lit = s.scanRune()\n\t\tcase '`':\n\t\t\tinsertSemi = true\n\t\t\tt.Tok = token.STRING\n\t\t\tt.Lit = s.scanRawString()\n\t\tcase ':':\n\t\t\tt.Tok = s.switch2(token.COLON, token.DEFINE)\n\t\tcase '.':\n\t\t\t// fractions starting with a '.' are handled by outer switch\n\t\t\tif s.ch == '.' && s.peek() == '.' {\n\t\t\t\ts.next()\n\t\t\t\ts.next() // consume last '.'\n\t\t\t\tif s.nParen == 0 {\n\t\t\t\t\tinsertSemi = true\n\t\t\t\t}\n\t\t\t\tt.Tok = token.ELLIPSIS\n\t\t\t} else {\n\t\t\t\tt.Tok = token.PERIOD\n\t\t\t}\n\t\tcase ',':\n\t\t\tt.Tok = token.COMMA\n\t\tcase ';':\n\t\t\tt.Tok = s.tokSEMICOLON()\n\t\t\tt.Lit = \";\"\n\t\tcase '(':\n\t\t\ts.nParen++\n\t\t\tt.Tok = token.LPAREN\n\t\tcase ')':\n\t\t\ts.nParen--\n\t\t\tinsertSemi = true\n\t\t\tt.Tok = token.RPAREN\n\t\tcase '[':\n\t\t\tt.Tok = token.LBRACK\n\t\tcase ']':\n\t\t\tinsertSemi = true\n\t\t\tt.Tok = token.RBRACK\n\t\tcase '{':\n\t\t\tt.Tok = token.LBRACE\n\t\tcase '}':\n\t\t\tinsertSemi = true\n\t\t\tt.Tok = token.RBRACE\n\t\tcase '+':\n\t\t\tt.Tok = s.switch3(token.ADD, token.ADD_ASSIGN, '+', token.INC)\n\t\t\tif t.Tok == token.INC {\n\t\t\t\tinsertSemi = true\n\t\t\t}\n\t\tcase '-':\n\t\t\tif s.ch == '>' { // ->\n\t\t\t\ts.next()\n\t\t\t\tt.Tok = token.SRARROW\n\t\t\t} else { // -- -=\n\t\t\t\tt.Tok = s.switch3(token.SUB, token.SUB_ASSIGN, '-', token.DEC)\n\t\t\t\tif t.Tok == token.DEC {\n\t\t\t\t\tinsertSemi = true\n\t\t\t\t}\n\t\t\t}\n\t\tcase '*':\n\t\t\tt.Tok = s.switch2(token.MUL, token.MUL_ASSIGN)\n\t\tcase '/':\n\t\t\tif s.ch == '/' || s.ch == '*' {\n\t\t\t\t// comment\n\t\t\t\tif s.insertSemi && s.findLineEnd() {\n\t\t\t\t\t// reset position to the beginning of the comment\n\t\t\t\t\ts.ch = '/'\n\t\t\t\t\ts.offset = s.file.Offset(t.Pos)\n\t\t\t\t\ts.rdOffset = s.offset + 1\n\t\t\t\t\ts.insertSemi = false // newline consumed\n\t\t\t\t\tt.Tok, t.Lit = s.tokSEMICOLON(), \"\\n\"\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tcomment := s.scanComment()\n\t\t\t\tif s.mode&ScanComments == 0 {\n\t\t\t\t\t// skip comment\n\t\t\t\t\ts.insertSemi = false // newline consumed\n\t\t\t\t\tgoto scanAgain\n\t\t\t\t}\n\t\t\t\tt.Tok = token.COMMENT\n\t\t\t\tt.Lit = comment\n\t\t\t} else {\n\t\t\t\tt.Tok = s.switch2(token.QUO, token.QUO_ASSIGN)\n\t\t\t}\n\t\tcase '#':\n\t\t\tif s.insertSemi {\n\t\t\t\ts.ch = '#'\n\t\t\t\ts.offset = s.file.Offset(t.Pos)\n\t\t\t\ts.rdOffset = s.offset + 1\n\t\t\t\ts.insertSemi = false\n\t\t\t\tt.Tok, t.Lit = s.tokSEMICOLON(), \"\\n\"\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcomment := s.scanSharpComment()\n\t\t\tif s.mode&ScanComments == 0 { // skip comment\n\t\t\t\tgoto scanAgain\n\t\t\t}\n\t\t\tt.Tok = token.COMMENT\n\t\t\tt.Lit = comment\n\t\tcase '%':\n\t\t\tt.Tok = s.switch2(token.REM, token.REM_ASSIGN)\n\t\tcase '^':\n\t\t\tt.Tok = s.switch2(token.XOR, token.XOR_ASSIGN)\n\t\tcase '<':\n\t\t\tswitch s.ch {\n\t\t\tcase '-': // <-\n\t\t\t\ts.next()\n\t\t\t\tt.Tok = token.ARROW\n\t\t\tcase '>': // <>\n\t\t\t\ts.next()\n\t\t\t\tt.Tok = token.BIDIARROW\n\t\t\tdefault: // <= << <<=\n\t\t\t\tt.Tok = s.switch4(token.LT, token.LE, '<', token.SHL, token.SHL_ASSIGN)\n\t\t\t}\n\t\tcase '>':\n\t\t\tt.Tok = s.switch4(token.GT, token.GE, '>', token.SHR, token.SHR_ASSIGN)\n\t\tcase '=':\n\t\t\tt.Tok = s.switch3(token.ASSIGN, token.EQ, '>', token.DRARROW)\n\t\tcase '!':\n\t\t\tt.Tok = s.switch2(token.NOT, token.NE)\n\t\t\tif t.Tok == token.NOT {\n\t\t\t\tinsertSemi = true\n\t\t\t}\n\t\tcase '&':\n\t\t\tif s.ch == '^' {\n\t\t\t\ts.next()\n\t\t\t\tt.Tok = s.switch2(token.AND_NOT, token.AND_NOT_ASSIGN)\n\t\t\t} else {\n\t\t\t\tt.Tok = s.switch3(token.AND, token.AND_ASSIGN, '&', token.LAND)\n\t\t\t}\n\t\tcase '|':\n\t\t\tt.Tok = s.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR)\n\t\tcase '?':\n\t\t\tt.Tok = token.QUESTION\n\t\t\tinsertSemi = true\n\t\tcase '$':\n\t\t\tt.Tok = token.ENV\n\t\tcase '~':\n\t\t\tt.Tok = token.TILDE\n\t\tcase '@':\n\t\t\tt.Tok = token.AT\n\t\tdefault:\n\t\t\t// next reports unexpected BOMs - don't repeat\n\t\t\tif ch != bom {\n\t\t\t\ts.error(s.file.Offset(t.Pos), fmt.Sprintf(\"illegal character %#U\", ch))\n\t\t\t}\n\t\t\tinsertSemi = s.insertSemi // preserve insertSemi info\n\t\t\tt.Tok = token.ILLEGAL\n\t\t\tt.Lit = string(ch)\n\t\t}\n\t}\n\ndone:\n\tif s.mode&NoInsertSemis == 0 {\n\t\ts.insertSemi = insertSemi\n\t}\n\treturn\n}\n"
  },
  {
    "path": "tpl/scanner/scanner_test.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage scanner\n\nimport (\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/goplus/xgo/tpl/types\"\n)\n\ntype Token = types.Token\n\ntype tokenTest struct {\n\tPos     token.Pos\n\tKind    token.Token\n\tLiteral string\n}\n\nfunc TestScanner(t *testing.T) {\n\tvar expected = []tokenTest{\n\t\t{3, token.IDENT, `term`},\n\t\t{8, '=', ``},\n\t\t{10, token.IDENT, `factor`},\n\t\t{17, '*', ``},\n\t\t{18, '(', ``},\n\t\t{19, token.CHAR, `'*'`},\n\t\t{23, token.IDENT, `factor`},\n\t\t{29, '/', ``},\n\t\t{30, token.IDENT, `mul`},\n\t\t{34, '|', ``},\n\t\t{36, token.CHAR, `'/'`},\n\t\t{40, token.IDENT, `factor`},\n\t\t{46, '/', ``},\n\t\t{47, token.IDENT, `div`},\n\t\t{50, ')', ``},\n\t\t{51, ';', \"\\n\"},\n\t\t{53, token.IDENT, `expr`},\n\t\t{58, '=', ``},\n\t\t{60, token.IDENT, `term`},\n\t\t{65, '*', ``},\n\t\t{66, '(', ``},\n\t\t{67, token.CHAR, `'+'`},\n\t\t{71, token.IDENT, `term`},\n\t\t{75, '/', ``},\n\t\t{76, token.IDENT, `add`},\n\t\t{80, '|', ``},\n\t\t{82, token.CHAR, `'-'`},\n\t\t{86, token.IDENT, `term`},\n\t\t{90, '/', ``},\n\t\t{91, token.IDENT, `sub`},\n\t\t{94, ')', ``},\n\t\t{95, ';', \"\\n\"},\n\t\t{97, token.IDENT, `factor`},\n\t\t{104, '=', ``},\n\t\t{107, token.IDENT, `FLOAT`},\n\t\t{112, '/', ``},\n\t\t{113, token.IDENT, `push`},\n\t\t{118, '|', ``},\n\t\t{121, token.CHAR, `'-'`},\n\t\t{125, token.IDENT, `factor`},\n\t\t{131, '/', ``},\n\t\t{132, token.IDENT, `neg`},\n\t\t{136, '|', ``},\n\t\t{139, token.CHAR, `'('`},\n\t\t{143, token.IDENT, `expr`},\n\t\t{148, token.CHAR, `')'`},\n\t\t{152, '|', ``},\n\t\t{155, '(', ``},\n\t\t{156, token.IDENT, `IDENT`},\n\t\t{162, token.CHAR, `'('`},\n\t\t{166, token.IDENT, `expr`},\n\t\t{171, '%', ``},\n\t\t{173, token.CHAR, `','`},\n\t\t{176, '/', ``},\n\t\t{177, token.IDENT, `arity`},\n\t\t{183, token.CHAR, `')'`},\n\t\t{186, ')', ``},\n\t\t{187, '/', ``},\n\t\t{188, token.IDENT, `call`},\n\t\t{193, '|', ``},\n\t\t{196, token.CHAR, `'+'`},\n\t\t{200, token.IDENT, `factor`},\n\t\t{206, ';', \"\\n\"},\n\t\t{208, token.IDENT, `hello`},\n\t\t{214, ';', \"\\n\"},\n\t\t{214, token.COMMENT, \"#!/user/bin/env tpl 中文\"},\n\t\t{241, token.IDENT, `hello`},\n\t\t{247, ';', \"\\n\"},\n\t\t{247, token.COMMENT, \"//!/user/bin/env tpl 中文\"},\n\t\t{275, token.IDENT, `world`},\n\t\t{281, token.NE, ``},\n\t}\n\n\tconst grammar = `\n\nterm = factor *('*' factor/mul | '/' factor/div)\n\nexpr = term *('+' term/add | '-' term/sub)\n\nfactor =\n\tFLOAT/push |\n\t'-' factor/neg |\n\t'(' expr ')' |\n\t(IDENT '(' expr % ','/arity ')')/call |\n\t'+' factor\n\nhello #!/user/bin/env tpl 中文\nhello //!/user/bin/env tpl 中文\nworld !=\n`\n\tvar s Scanner\n\n\tfset := token.NewFileSet()\n\tfile := fset.AddFile(\"\", -1, len(grammar))\n\n\ts.Init(file, []byte(grammar), nil /* no error handler */, ScanComments)\n\ti := 0\n\tfor {\n\t\tc := s.Scan()\n\t\tif c.Tok == token.EOF {\n\t\t\tbreak\n\t\t}\n\t\texpect := Token{Tok: expected[i].Kind, Pos: expected[i].Pos, Lit: expected[i].Literal}\n\t\tif c != expect {\n\t\t\tt.Fatal(\"Scan failed:\", c, expect)\n\t\t}\n\t\ti++\n\t}\n\tif len(expected) != i {\n\t\tt.Fatalf(\"len(expected) != i: %d, %d\\n\", len(expected), i)\n\t}\n\n\ts.Init(file, []byte(grammar), nil, NoInsertSemis)\n\ti = 0\n\tfor {\n\t\tc := s.Scan()\n\t\tif c.Tok == token.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif expected[i].Kind == ';' {\n\t\t\ti++\n\t\t}\n\t\tif expected[i].Kind == token.COMMENT {\n\t\t\ti++\n\t\t}\n\t\texpect := Token{Tok: expected[i].Kind, Pos: expected[i].Pos, Lit: expected[i].Literal}\n\t\tif c != expect {\n\t\t\tt.Fatal(\"Scan failed:\", c.Pos, c.Lit, expect)\n\t\t}\n\t\tswitch c.Tok.Len() {\n\t\tcase 0, 1, 2, 3:\n\t\tdefault:\n\t\t\tt.Fatal(\"TokenLen failed:\", c.Tok.Len())\n\t\t}\n\t\ti++\n\t}\n\tif i < len(expected) && expected[i].Kind == ';' {\n\t\ti++\n\t}\n\tif len(expected) != i {\n\t\tt.Fatalf(\"len(expected) != i: %d, %d\\n\", len(expected), i)\n\t}\n\n\ts.Init(file, []byte(grammar), nil, 0)\n\ti = 0\n\tfor {\n\t\tc := s.Scan()\n\t\tif c.Tok == token.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif expected[i].Kind == token.COMMENT {\n\t\t\ti++\n\t\t}\n\t\texpect := Token{Tok: expected[i].Kind, Pos: expected[i].Pos, Lit: expected[i].Literal}\n\t\tif c != expect {\n\t\t\tt.Fatal(\"Scan failed:\", c.Pos, c.Lit, expect)\n\t\t}\n\t\ti++\n\t}\n\tif len(expected) != i {\n\t\tt.Fatalf(\"len(expected) != i: %d, %d\\n\", len(expected), i)\n\t}\n}\n"
  },
  {
    "path": "tpl/scanner/scannertest/scannertest.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage scannertest\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/goplus/xgo/tpl/scanner\"\n\t\"github.com/goplus/xgo/tpl/token\"\n\n\tgoscanner \"go/scanner\"\n\tgotoken \"go/token\"\n\n\tgopscanner \"github.com/goplus/xgo/scanner\"\n\tgoptoken \"github.com/goplus/xgo/token\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Scan scans the input and writes the tokens to the writer.\nfunc Scan(w io.Writer, in []byte) {\n\tvar s scanner.Scanner\n\tfset := gotoken.NewFileSet()\n\tf := fset.AddFile(\"\", -1, len(in))\n\ts.Init(f, in, nil, scanner.ScanComments)\n\tfor {\n\t\tt := s.Scan()\n\t\tif t.Tok == token.EOF {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Fprintln(w, t.Pos, t.Tok, t.Lit)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// GopScan scans the input using the XGo standard library scanner and\n// writes the tokens to the writer.\nfunc GopScan(w io.Writer, in []byte) {\n\tvar s gopscanner.Scanner\n\tfset := gotoken.NewFileSet()\n\tf := fset.AddFile(\"\", -1, len(in))\n\ts.Init(f, in, nil, gopscanner.ScanComments)\n\tfor {\n\t\tpos, tok, lit := s.Scan()\n\t\tif tok == goptoken.EOF {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Fprintln(w, pos, tok, lit)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// GoScan scans the input using the Go standard library scanner and\n// writes the tokens to the writer.\nfunc GoScan(w io.Writer, in []byte) {\n\tvar s goscanner.Scanner\n\tfset := gotoken.NewFileSet()\n\tf := fset.AddFile(\"\", -1, len(in))\n\ts.Init(f, in, nil, goscanner.ScanComments)\n\tfor {\n\t\tpos, tok, lit := s.Scan()\n\t\tif tok == gotoken.EOF {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Fprintln(w, pos, tok, lit)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/token/token.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage token\n\nimport (\n\t\"strconv\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype Token uint\n\nfunc (tok Token) String() (s string) {\n\tif tok < Token(len(tokens)) {\n\t\ts = tokens[tok]\n\t}\n\tif s == \"\" {\n\t\ts = \"token(\" + strconv.Itoa(int(tok)) + \")\"\n\t}\n\treturn\n}\n\n// Len returns\n// 1) len of is token literal, if token is an operator.\n// 2) 0 for else.\nfunc (tok Token) Len() int {\n\tif tok > ' ' && tok <= Token(len(tokens)) {\n\t\treturn len(tokens[tok])\n\t}\n\treturn 0\n}\n\n// -----------------------------------------------------------------------------\n\nconst (\n\t// Special tokens\n\tILLEGAL Token = iota\n\tEOF\n\tCOMMENT\n\n\tliteral_beg\n\t// Identifiers and basic type literals\n\t// (these tokens stand for classes of literals)\n\tIDENT  // main\n\tINT    // 12345\n\tFLOAT  // 123.45\n\tIMAG   // 123.45i\n\tCHAR   // 'a'\n\tSTRING // \"abc\"\n\tRAT    // 3r, 3.4r\n\tUNIT   // 1m, 2.3s, 3ms, 4us, 5ns, 6.5m, 7h, 8d, 9w, 10y\n\tliteral_end\n\n\tADD = '+'\n\tSUB = '-'\n\tMUL = '*'\n\tQUO = '/'\n\tREM = '%'\n\n\tAND = '&'\n\tOR  = '|'\n\tXOR = '^'\n\n\tLT     = '<'\n\tGT     = '>'\n\tASSIGN = '='\n\tNOT    = '!'\n\n\tLPAREN = '('\n\tLBRACK = '['\n\tLBRACE = '{'\n\tCOMMA  = ','\n\tPERIOD = '.'\n\n\tRPAREN    = ')'\n\tRBRACK    = ']'\n\tRBRACE    = '}'\n\tSEMICOLON = ';'\n\tCOLON     = ':'\n\tQUESTION  = '?'\n\tTILDE     = '~'\n\tAT        = '@'\n\tENV       = '$'\n)\n\nconst (\n\toperator_beg Token = 0x80 + iota\n\n\tSHL     // <<\n\tSHR     // >>\n\tAND_NOT // &^\n\n\tADD_ASSIGN // +=\n\tSUB_ASSIGN // -=\n\tMUL_ASSIGN // *=\n\tQUO_ASSIGN // /=\n\tREM_ASSIGN // %=\n\n\tAND_ASSIGN     // &=\n\tOR_ASSIGN      // |=\n\tXOR_ASSIGN     // ^=\n\tSHL_ASSIGN     // <<=\n\tSHR_ASSIGN     // >>=\n\tAND_NOT_ASSIGN // &^=\n\n\tLAND  // &&\n\tLOR   // ||\n\tARROW // <-\n\tINC   // ++\n\tDEC   // --\n\n\tEQ       // ==\n\tNE       // !=\n\tLE       // <=\n\tGE       // >=\n\tDEFINE   // :=\n\tELLIPSIS // ...\n\n\tDRARROW   // =>\n\tSRARROW   // ->\n\tBIDIARROW // <>\n\n\toperator_end\n)\n\n// -----------------------------------------------------------------------------\n\nvar tokens = [...]string{\n\tILLEGAL: \"ILLEGAL\",\n\n\tEOF:     \"EOF\",\n\tCOMMENT: \"COMMENT\",\n\n\tIDENT:  \"IDENT\",\n\tINT:    \"INT\",\n\tFLOAT:  \"FLOAT\",\n\tIMAG:   \"IMAG\",\n\tCHAR:   \"CHAR\",\n\tSTRING: \"STRING\",\n\tRAT:    \"RAT\",\n\tUNIT:   \"UNIT\",\n\n\tADD: \"+\",\n\tSUB: \"-\",\n\tMUL: \"*\",\n\tQUO: \"/\",\n\tREM: \"%\",\n\n\tAND: \"&\",\n\tOR:  \"|\",\n\tXOR: \"^\",\n\n\tLT:     \"<\",\n\tGT:     \">\",\n\tASSIGN: \"=\",\n\tNOT:    \"!\",\n\n\tLPAREN: \"(\",\n\tLBRACK: \"[\",\n\tLBRACE: \"{\",\n\tCOMMA:  \",\",\n\tPERIOD: \".\",\n\n\tRPAREN:    \")\",\n\tRBRACK:    \"]\",\n\tRBRACE:    \"}\",\n\tSEMICOLON: \";\",\n\tCOLON:     \":\",\n\tQUESTION:  \"?\",\n\tTILDE:     \"~\",\n\tAT:        \"@\",\n\tENV:       \"$\",\n\n\tSHL:     \"<<\",\n\tSHR:     \">>\",\n\tAND_NOT: \"&^\",\n\n\tADD_ASSIGN: \"+=\",\n\tSUB_ASSIGN: \"-=\",\n\tMUL_ASSIGN: \"*=\",\n\tQUO_ASSIGN: \"/=\",\n\tREM_ASSIGN: \"%=\",\n\n\tAND_ASSIGN:     \"&=\",\n\tOR_ASSIGN:      \"|=\",\n\tXOR_ASSIGN:     \"^=\",\n\tSHL_ASSIGN:     \"<<=\",\n\tSHR_ASSIGN:     \">>=\",\n\tAND_NOT_ASSIGN: \"&^=\",\n\n\tLAND:  \"&&\",\n\tLOR:   \"||\",\n\tARROW: \"<-\",\n\tINC:   \"++\",\n\tDEC:   \"--\",\n\n\tEQ:       \"==\",\n\tNE:       \"!=\",\n\tLE:       \"<=\",\n\tGE:       \">=\",\n\tDEFINE:   \":=\",\n\tELLIPSIS: \"...\",\n\n\tDRARROW:   \"=>\",\n\tSRARROW:   \"->\",\n\tBIDIARROW: \"<>\",\n}\n\nconst (\n\tBreak = -1\n)\n\n// ForEach iterates tokens.\nfunc ForEach(from Token, f func(tok Token, lit string) int) {\n\tif from == 0 {\n\t\tfrom = operator_beg + 1\n\t}\n\tfor from < operator_end {\n\t\tif s := tokens[from]; s != \"\" {\n\t\t\tif f(Token(from), s) == Break {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tfrom++\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/token/token_test.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage token\n\nimport (\n\t\"go/token\"\n\t\"testing\"\n)\n\nfunc TestToken(t *testing.T) {\n\tif literal_beg != 3 {\n\t\tt.Fatal(\"literal_beg\")\n\t}\n\tfor i := Token(0); i < literal_end; i++ {\n\t\ts := i.String()\n\t\tif s == \"RAT\" || s == \"UNIT\" || s == \"CSTRING\" || s == \"PYSTRING\" {\n\t\t\tcontinue\n\t\t}\n\t\tif s != token.Token(i).String() {\n\t\t\tt.Fatal(\"String:\", i)\n\t\t}\n\t}\n\tif IDENT.String() != \"IDENT\" {\n\t\tt.Fatal(\"IDENT\")\n\t}\n\tif Token(' ').String() != \"token(32)\" {\n\t\tt.Fatal(\"token(32)\")\n\t}\n\tif IDENT.Len() != 0 {\n\t\tt.Fatal(\"TokLen IDENT\")\n\t}\n\tif Token('+').Len() != 1 {\n\t\tt.Fatal(\"TokLen +\")\n\t}\n\tcount := 0\n\tForEach(0, func(tok Token, name string) int {\n\t\tcount++\n\t\treturn 0\n\t})\n\tif count != 28 {\n\t\tt.Fatal(\"ForEach:\", count)\n\t}\n\tNewFileSet()\n\tForEach(0, func(tok Token, name string) int {\n\t\tif tok != SHL {\n\t\t\tt.Fatal(\"ForEach:\", tok)\n\t\t}\n\t\treturn Break\n\t})\n}\n"
  },
  {
    "path": "tpl/token/types.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage token\n\nimport (\n\t\"go/token\"\n)\n\n// Pos is a compact encoding of a source position within a file set.\n// It can be converted into a Position for a more convenient, but much\n// larger, representation.\n//\n// The Pos value for a given file is a number in the range [base, base+size],\n// where base and size are specified when adding the file to the file set via\n// AddFile.\n//\n// To create the Pos value for a specific source offset (measured in bytes),\n// first add the respective file to the current file set using FileSet.AddFile\n// and then call File.Pos(offset) for that file. Given a Pos value p\n// for a specific file set fset, the corresponding Position value is\n// obtained by calling fset.Position(p).\n//\n// Pos values can be compared directly with the usual comparison operators:\n// If two Pos values p and q are in the same file, comparing p and q is\n// equivalent to comparing the respective source file offsets. If p and q\n// are in different files, p < q is true if the file implied by p was added\n// to the respective file set before the file implied by q.\ntype Pos = token.Pos\n\nconst (\n\t// NoPos - The zero value for Pos is NoPos; there is no file and line\n\t// information associated with it, and NoPos.IsValid() is false. NoPos\n\t// is always smaller than any other Pos value. The corresponding\n\t// Position value for NoPos is the zero value for Position.\n\tNoPos = token.NoPos\n)\n\n// Position describes an arbitrary source position\n// including the file, line, and column location.\n// A Position is valid if the line number is > 0.\ntype Position = token.Position\n\n// A File is a handle for a file belonging to a FileSet.\n// A File has a name, size, and line offset table.\ntype File = token.File\n\n// A FileSet represents a set of source files. Methods of file sets are\n// synchronized; multiple goroutines may invoke them concurrently.\ntype FileSet = token.FileSet\n\n// NewFileSet creates a new file set.\nfunc NewFileSet() *FileSet {\n\treturn token.NewFileSet()\n}\n"
  },
  {
    "path": "tpl/tpl.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage tpl\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"reflect\"\n\n\t\"github.com/goplus/xgo/tpl/ast\"\n\t\"github.com/goplus/xgo/tpl/cl\"\n\t\"github.com/goplus/xgo/tpl/matcher\"\n\t\"github.com/goplus/xgo/tpl/parser\"\n\t\"github.com/goplus/xgo/tpl/scanner\"\n\t\"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/goplus/xgo/tpl/types\"\n\t\"github.com/qiniu/x/errors\"\n\t\"github.com/qiniu/x/stream\"\n)\n\n// -----------------------------------------------------------------------------\n\nvar showConflict = true\n\n// ShowConflict sets the flag to show or hide conflicts.\nfunc ShowConflict(f bool) int {\n\tshowConflict = f\n\treturn 0\n}\n\nfunc onConflictHidden(fset *token.FileSet, c *ast.Choice, firsts [][]any, i, at int) {\n}\n\n// -----------------------------------------------------------------------------\n\nfunc relocatePos(ePos *token.Position, filename string, line, col int) {\n\tePos.Filename = filename\n\tif ePos.Line == line {\n\t\tePos.Column += col - 1\n\t} else {\n\t\tePos.Line += line - 1\n\t}\n}\n\n// Relocate relocates the error positions.\nfunc Relocate(err error, filename string, line, col int) error {\n\tswitch e := err.(type) {\n\tcase *matcher.Error:\n\t\tpos := e.Fset.Position(e.Pos)\n\t\trelocatePos(&pos, filename, line, col)\n\t\treturn &scanner.Error{Pos: pos, Msg: e.Msg}\n\tcase errors.List:\n\t\tfor i, ie := range e {\n\t\t\te[i] = Relocate(ie, filename, line, col)\n\t\t}\n\tcase scanner.ErrorList:\n\t\tfor _, ie := range e {\n\t\t\trelocatePos(&ie.Pos, filename, line, col)\n\t\t}\n\tcase *scanner.Error:\n\t\trelocatePos(&e.Pos, filename, line, col)\n\tdefault:\n\t\tpanic(\"todo: \" + reflect.TypeOf(err).String())\n\t}\n\treturn err\n}\n\n// -----------------------------------------------------------------------------\n\n// Compiler represents a TPL compiler.\ntype Compiler struct {\n\tcl.Result\n}\n\n// New creates a new TPL compiler.\n// params: ruleName1, retProc1, ..., ruleNameN, retProcN\nfunc New(src any, params ...any) (ret Compiler, err error) {\n\tconf := &cl.Config{\n\t\tRetProcs: retProcs(params),\n\t}\n\tif !showConflict {\n\t\tconf.OnConflict = onConflictHidden\n\t}\n\treturn FromFile(nil, \"\", src, conf)\n}\n\n// NewEx creates a new TPL compiler.\n// params: ruleName1, retProc1, ..., ruleNameN, retProcN\nfunc NewEx(src any, filename string, line, col int, params ...any) (ret Compiler, err error) {\n\tconf := &cl.Config{\n\t\tRetProcs: retProcs(params),\n\t}\n\tif showConflict {\n\t\tconf.OnConflict = func(fset *token.FileSet, c *ast.Choice, firsts [][]any, i, at int) {\n\t\t\tif showConflict {\n\t\t\t\tpos := fset.Position(c.Options[i].Pos())\n\t\t\t\trelocatePos(&pos, filename, line, col)\n\t\t\t\tcl.LogConflict(pos, firsts, i, at)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tconf.OnConflict = onConflictHidden\n\t}\n\tret, err = FromFile(nil, \"\", src, conf)\n\tif err != nil {\n\t\terr = Relocate(err, filename, line, col)\n\t}\n\treturn\n}\n\n// FromFile creates a new TPL compiler from a file.\n// fset can be nil.\nfunc FromFile(fset *token.FileSet, filename string, src any, conf *cl.Config) (ret Compiler, err error) {\n\tif fset == nil {\n\t\tfset = token.NewFileSet()\n\t}\n\tf, err := parser.ParseFile(fset, filename, src, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\tret.Result, err = cl.NewEx(conf, fset, f)\n\treturn\n}\n\nfunc retProcs(params []any) map[string]any {\n\tn := len(params)\n\tif n == 0 {\n\t\treturn nil\n\t}\n\tif n&1 != 0 {\n\t\tpanic(\"tpl.New: invalid params. should be in form `ruleName1, retProc1, ..., ruleNameN, retProcN`\")\n\t}\n\tret := make(map[string]any, n>>1)\n\tfor i := 0; i < n; i += 2 {\n\t\tret[params[i].(string)] = params[i+1]\n\t}\n\treturn ret\n}\n\n// -----------------------------------------------------------------------------\n\n// Error represents a matching error.\ntype Error = matcher.Error\n\n// A Token is a lexical unit returned by Scan.\ntype Token = types.Token\n\n// Scanner represents a TPL scanner.\ntype Scanner interface {\n\tScan() Token\n\tInit(file *token.File, src []byte, err scanner.ErrorHandler, mode scanner.Mode)\n}\n\n// Config represents a parsing configuration of [Compiler.Parse].\ntype Config struct {\n\tScanner          Scanner\n\tScanErrorHandler scanner.ErrorHandler\n\tScanMode         scanner.Mode\n\tFset             *token.FileSet\n}\n\n// ParseExpr parses an expression.\nfunc (p *Compiler) ParseExpr(x string, conf *Config) (result any, err error) {\n\treturn p.ParseExprFrom(\"\", x, conf)\n}\n\n// ParseExprFrom parses an expression from a file.\nfunc (p *Compiler) ParseExprFrom(filename string, src any, conf *Config) (result any, err error) {\n\tms, result, err := p.Match(filename, src, conf)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ms.Toks) == ms.N || isEOL(ms.Toks[ms.N].Tok) {\n\t\treturn\n\t}\n\tt := ms.Next()\n\terr = ms.Ctx.NewErrorf(t.Pos, \"unexpected token: %v\", t)\n\treturn\n}\n\n// Parse parses a source file.\nfunc (p *Compiler) Parse(filename string, src any, conf *Config) (result any, err error) {\n\tms, result, err := p.Match(filename, src, conf)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ms.Toks) > ms.N {\n\t\tt := ms.Next()\n\t\terr = ms.Ctx.NewErrorf(t.Pos, \"unexpected token: %v\", t)\n\t}\n\treturn\n}\n\n// MatchState represents a matching state.\ntype MatchState struct {\n\tToks []*Token\n\tCtx  *matcher.Context\n\tN    int\n}\n\n// Next returns the next token.\nfunc (p *MatchState) Next() *Token {\n\tn := p.Ctx.Left\n\tif n > 0 {\n\t\treturn p.Toks[len(p.Toks)-n]\n\t}\n\treturn &Token{Tok: token.EOF, Pos: p.Ctx.FileEnd}\n}\n\n// Match matches a source file.\nfunc (p *Compiler) Match(filename string, src any, conf *Config) (ms MatchState, result any, err error) {\n\tb, err := stream.ReadSourceLocal(filename, src)\n\tif err != nil {\n\t\treturn\n\t}\n\tif conf == nil {\n\t\tconf = &Config{}\n\t}\n\ts := conf.Scanner\n\tif s == nil {\n\t\ts = new(scanner.Scanner)\n\t}\n\tfset := conf.Fset\n\tif fset == nil {\n\t\tfset = token.NewFileSet()\n\t}\n\tf := fset.AddFile(filename, fset.Base(), len(b))\n\ts.Init(f, b, conf.ScanErrorHandler, conf.ScanMode)\n\tn := (len(b) >> 3) &^ 7\n\tif n < 8 {\n\t\tn = 8\n\t}\n\ttoks := make([]*Token, 0, n)\n\tfor {\n\t\tt := s.Scan()\n\t\tif t.Tok == token.EOF {\n\t\t\tbreak\n\t\t}\n\t\ttoks = append(toks, &t)\n\t}\n\tms.Ctx = matcher.NewContext(fset, token.Pos(f.Base()+len(b)), toks)\n\tms.N, result, err = p.Doc.Match(toks, ms.Ctx)\n\tms.Ctx.SetLastError(len(toks)-ms.N, err)\n\tif err != nil {\n\t\treturn\n\t}\n\tms.Toks = toks\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\nfunc isEOL(tok token.Token) bool {\n\treturn tok == token.SEMICOLON || tok == token.EOF\n}\n\nfunc isPlain(result []any) bool {\n\tfor _, v := range result {\n\t\tif _, ok := scalar(v); !ok {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc scalar(v any) (any, bool) {\n\tif v == nil {\n\t\treturn nil, true\n\t}\nretry:\n\tswitch result := v.(type) {\n\tcase *Token:\n\t\treturn v, true\n\tcase []any:\n\t\tif len(result) == 2 {\n\t\t\tif isVoid(result[1]) {\n\t\t\t\tv = result[0]\n\t\t\t\tgoto retry\n\t\t\t}\n\t\t}\n\t\treturn v, len(result) == 0\n\t}\n\treturn v, false\n}\n\nfunc isVoid(v any) bool {\n\tif v == nil {\n\t\treturn true\n\t}\n\tswitch v := v.(type) {\n\tcase *Token:\n\t\treturn v.Tok == token.SEMICOLON || v.Tok == token.EOF\n\tcase []any:\n\t\treturn len(v) == 0\n\t}\n\treturn false\n}\n\n// -----------------------------------------------------------------------------\n\nfunc Dump(result any, omitSemi ...bool) {\n\tFdump(os.Stdout, result, \"\", \"  \", omitSemi != nil && omitSemi[0])\n}\n\nfunc Fdump(w io.Writer, ret any, prefix, indent string, omitSemi bool) {\nretry:\n\tswitch result := ret.(type) {\n\tcase *Token:\n\t\tif result.Tok != token.SEMICOLON {\n\t\t\tfmt.Fprint(w, prefix, result, \"\\n\")\n\t\t} else if !omitSemi {\n\t\t\tfmt.Fprint(w, prefix, \";\\n\")\n\t\t}\n\tcase []any:\n\t\tif len(result) == 2 {\n\t\t\tif isVoid(result[1]) {\n\t\t\t\tret = result[0]\n\t\t\t\tgoto retry\n\t\t\t}\n\t\t}\n\t\tif isPlain(result) {\n\t\t\tfmt.Print(prefix, \"[\")\n\t\t\tfor i, v := range result {\n\t\t\t\tif i > 0 {\n\t\t\t\t\tfmt.Print(\" \")\n\t\t\t\t}\n\t\t\t\tv, _ = scalar(v)\n\t\t\t\tfmt.Fprint(w, v)\n\t\t\t}\n\t\t\tfmt.Print(\"]\\n\")\n\t\t} else {\n\t\t\tfmt.Print(prefix, \"[\\n\")\n\t\t\tfor _, v := range result {\n\t\t\t\tFdump(w, v, prefix+indent, indent, omitSemi)\n\t\t\t}\n\t\t\tfmt.Print(prefix, \"]\\n\")\n\t\t}\n\tcase nil:\n\t\tfmt.Fprint(w, prefix, \"nil\\n\")\n\tdefault:\n\t\tpanic(\"unexpected node: \" + reflect.TypeOf(ret).String())\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// List converts the matching result of (R % \",\") to a flat list.\n// R % \",\" means R *(\",\" R)\nfunc List(in []any) []any {\n\tnext := in[1].([]any)\n\tret := make([]any, len(next)+1)\n\tret[0] = in[0]\n\tfor i, v := range next {\n\t\tret[i+1] = v.([]any)[1]\n\t}\n\treturn ret\n}\n\n// ListOp converts the matching result of (R % \",\") to a flat list.\n// R % \",\" means R *(\",\" R)\nfunc ListOp[T any](in []any, fn func(v any) T) []T {\n\tnext := in[1].([]any)\n\tret := make([]T, len(next)+1)\n\tret[0] = fn(in[0])\n\tfor i, v := range next {\n\t\tret[i+1] = fn(v.([]any)[1])\n\t}\n\treturn ret\n}\n\n// RangeOp travels the matching result of (R % \",\") and call fn(result of R).\n// R % \",\" means R *(\",\" R)\nfunc RangeOp(in []any, fn func(v any)) {\n\tnext := in[1].([]any)\n\tfn(in[0])\n\tfor _, v := range next {\n\t\tfn(v.([]any)[1])\n\t}\n}\n\n// BinaryExpr converts the matching result of (X % op) to a binary expression.\n// X % op means X *(op X)\nfunc BinaryExpr(recursive bool, in []any) ast.Expr {\n\tif recursive {\n\t\treturn BinaryExprR(in)\n\t}\n\treturn BinaryExprNR(in)\n}\n\nfunc BinaryExprR(in []any) ast.Expr {\n\tvar ret, y ast.Expr\n\tswitch v := in[0].(type) {\n\tcase []any:\n\t\tret = BinaryExprR(v)\n\tdefault:\n\t\tret = v.(ast.Expr)\n\t}\n\tfor _, v := range in[1].([]any) {\n\t\tnext := v.([]any)\n\t\top := next[0].(*Token)\n\t\tswitch v := next[1].(type) {\n\t\tcase []any:\n\t\t\ty = BinaryExprR(v)\n\t\tdefault:\n\t\t\ty = v.(ast.Expr)\n\t\t}\n\t\tret = &ast.BinaryExpr{\n\t\t\tX:     ret,\n\t\t\tOpPos: op.Pos,\n\t\t\tOp:    op.Tok,\n\t\t\tY:     y,\n\t\t}\n\t}\n\treturn ret\n}\n\nfunc BinaryExprNR(in []any) ast.Expr {\n\tret := in[0].(ast.Expr)\n\tfor _, v := range in[1].([]any) {\n\t\tnext := v.([]any)\n\t\top := next[0].(*Token)\n\t\ty := next[1].(ast.Expr)\n\t\tret = &ast.BinaryExpr{\n\t\t\tX:     ret,\n\t\t\tOpPos: op.Pos,\n\t\t\tOp:    op.Tok,\n\t\t\tY:     y,\n\t\t}\n\t}\n\treturn ret\n}\n\nfunc BinaryOp(recursive bool, in []any, fn func(op *Token, x, y any) any) any {\n\tif recursive {\n\t\treturn BinaryOpR(in, fn)\n\t}\n\treturn BinaryOpNR(in, fn)\n}\n\nfunc BinaryOpR(in []any, fn func(op *Token, x, y any) any) any {\n\tret := in[0]\n\tif v, ok := ret.([]any); ok {\n\t\tret = BinaryOpR(v, fn)\n\t}\n\tfor _, v := range in[1].([]any) {\n\t\tnext := v.([]any)\n\t\top := next[0].(*Token)\n\t\ty := next[1]\n\t\tif v, ok := y.([]any); ok {\n\t\t\ty = BinaryOpR(v, fn)\n\t\t}\n\t\tret = fncall(fn, op, ret, y)\n\t}\n\treturn ret\n}\n\nfunc BinaryOpNR(in []any, fn func(op *Token, x, y any) any) any {\n\tret := in[0]\n\tfor _, v := range in[1].([]any) {\n\t\tnext := v.([]any)\n\t\top := next[0].(*Token)\n\t\ty := next[1]\n\t\tret = fncall(fn, op, ret, y)\n\t}\n\treturn ret\n}\n\nfunc fncall(fn func(op *Token, x, y any) any, op *Token, x, y any) any {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tpanic(toErr(e, op))\n\t\t}\n\t}()\n\treturn fn(op, x, y)\n}\n\nfunc toErr(e any, op *Token) error {\n\tswitch e := e.(type) {\n\tcase *matcher.Error:\n\t\treturn e\n\tcase string:\n\t\treturn &matcher.Error{\n\t\t\tPos: op.Pos,\n\t\t\tMsg: e,\n\t\t\tDyn: true,\n\t\t}\n\t}\n\treturn e.(error)\n}\n\n// UnaryExpr converts the matching result of (op X) to a unary expression.\nfunc UnaryExpr(in []any) ast.Expr {\n\top := in[0].(*Token)\n\treturn &ast.UnaryExpr{\n\t\tOpPos: op.Pos,\n\t\tOp:    op.Tok,\n\t\tX:     in[1].(ast.Expr),\n\t}\n}\n\n// Ident converts the matching result of an identifier to an ast.Ident expression.\nfunc Ident(this any) *ast.Ident {\n\tv := this.(*Token)\n\treturn &ast.Ident{\n\t\tNamePos: v.Pos,\n\t\tName:    v.Lit,\n\t}\n}\n\n// BasicLit converts the matching result of a basic literal to an ast.BasicLit expression.\nfunc BasicLit(this any) *ast.BasicLit {\n\tv := this.(*Token)\n\treturn &ast.BasicLit{\n\t\tValuePos: v.Pos,\n\t\tKind:     v.Tok,\n\t\tValue:    v.Lit,\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// Panic panics with a matcher error.\nfunc Panic(pos token.Pos, msg string) {\n\terr := &matcher.Error{\n\t\tPos: pos,\n\t\tMsg: msg,\n\t\tDyn: true,\n\t}\n\tpanic(err)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/types/types.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage types\n\nimport (\n\t\"github.com/goplus/xgo/tpl/token\"\n)\n\n// A Token is a lexical unit returned by Scan.\ntype Token struct {\n\tTok token.Token\n\tPos token.Pos\n\tLit string\n}\n\n// String returns the string representation of a token.\nfunc (p *Token) String() string {\n\tn := len(p.Lit)\n\tif n == 0 {\n\t\treturn p.Tok.String()\n\t}\n\treturn p.Lit\n}\n\n// End returns end position of this token.\nfunc (p *Token) End() token.Pos {\n\tn := len(p.Lit)\n\tif n == 0 {\n\t\tn = p.Tok.Len()\n\t}\n\treturn p.Pos + token.Pos(n)\n}\n"
  },
  {
    "path": "tpl/variant/builtin/builtin.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage buitin\n\nimport (\n\t\"reflect\"\n\n\t\"github.com/goplus/xgo/tpl/variant\"\n)\n\n// -----------------------------------------------------------------------------\n\n// CastInt converts a value to int.\nfunc CastInt(args ...any) any {\n\tif len(args) != 1 {\n\t\tpanic(\"int: arity mismatch\")\n\t}\n\tswitch v := variant.Eval(args[0]).(type) {\n\tcase float64:\n\t\treturn int(v)\n\tcase int:\n\t\treturn v\n\t}\n\tpanic(\"can't convert to int\")\n}\n\n// -----------------------------------------------------------------------------\n\n// Type returns the type of an value.\nfunc Type(args ...any) any {\n\tif len(args) != 1 {\n\t\tpanic(\"type: arity mismatch\")\n\t}\n\treturn reflect.TypeOf(variant.Eval(args[0]))\n}\n\n// -----------------------------------------------------------------------------\n\nfunc init() {\n\tmod := variant.NewModule(\"builtin\")\n\tmod.Insert(\"int\", CastInt)\n\tmod.Insert(\"type\", Type)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/variant/delay/delay.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage delay\n\nimport (\n\t\"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/goplus/xgo/tpl/variant\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Value represents a delayed value.\ntype Value = func() any\n\n// Eval evaluates a value.\nfunc Eval(v any) any {\n\tif d, ok := v.(Value); ok {\n\t\treturn d()\n\t}\n\treturn v\n}\n\n// EvalOp delays to evaluate a value.\nfunc EvalOp(expr any, fn func(v any)) any {\n\treturn func() any {\n\t\tfn(Eval(expr))\n\t\treturn nil\n\t}\n}\n\n// List delays to convert the matching result of (R % \",\") to a flat list.\n// R % \",\" means R *(\",\" R)\nfunc List(in []any, fn func(flat []any)) any {\n\treturn func() any {\n\t\tfn(variant.List(in))\n\t\treturn nil\n\t}\n}\n\n// ListOp delays to convert the matching result of (R % \",\") to a flat list.\n// R % \",\" means R *(\",\" R)\nfunc ListOp[T any](in []any, op func(v any) T, fn func(flat []T)) any {\n\treturn func() any {\n\t\tfn(variant.ListOp(in, op))\n\t\treturn nil\n\t}\n}\n\n// RangeOp delays to travel the matching result of (R % \",\") and call fn(result of R).\n// R % \",\" means R *(\",\" R)\nfunc RangeOp(in []any, fn func(v any)) any {\n\treturn func() any {\n\t\tvariant.RangeOp(in, fn)\n\t\treturn nil\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// Compare delays a compare operation.\nfunc Compare(op token.Token, x, y any) any {\n\treturn func() any {\n\t\treturn variant.Compare(op, x, y)\n\t}\n}\n\n// MathOp delays a math operation.\nfunc MathOp(op token.Token, x, y any) any {\n\treturn func() any {\n\t\treturn variant.MathOp(op, x, y)\n\t}\n}\n\n// LogicOp delays a logic operation.\nfunc LogicOp(op token.Token, x, y any) any {\n\treturn func() any {\n\t\treturn variant.LogicOp(op, x, y)\n\t}\n}\n\n// UnaryOp delays a unary operation.\nfunc UnaryOp(op token.Token, x any) any {\n\treturn func() any {\n\t\treturn variant.UnaryOp(op, x)\n\t}\n}\n\n// Call delays to call a function.\nfunc Call(needList bool, name string, arglist any) any {\n\treturn func() any {\n\t\treturn variant.Call(needList, name, arglist)\n\t}\n}\n\n// CallObject delays to call a function object.\nfunc CallObject(needList bool, fn any, arglist any) any {\n\treturn func() any {\n\t\treturn variant.CallObject(needList, fn, arglist)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// ValueOf delays to get a value.\nfunc ValueOf(name string, getval func(name string) (any, bool)) any {\n\treturn func() any {\n\t\tv, ok := getval(name)\n\t\tif !ok {\n\t\t\tpanic(name + \" is undefined\")\n\t\t}\n\t\treturn v\n\t}\n}\n\n// SetValue delays to set a value.\nfunc SetValue(name string, setval func(name string, val any), expr any) any {\n\treturn func() any {\n\t\tsetval(name, Eval(expr))\n\t\treturn nil\n\t}\n}\n\n// ChgValue delays to change a value.\nfunc ChgValue(name string, chgval func(string, func(oldv any) any), chg func(oldv any) any) any {\n\treturn func() any {\n\t\tchgval(name, chg)\n\t\treturn nil\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// StmtList delays a statement list.\nfunc StmtList(stmts []any) any {\n\treturn func() any {\n\t\tfor _, stmt := range stmts {\n\t\t\tEval(stmt)\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// IfElse delays an if-else statement.\nfunc IfElse(cond, ifBody, elseStmt any, elseBodyAt int) any {\n\treturn func() any {\n\t\tif Eval(cond).(bool) {\n\t\t\tEval(ifBody)\n\t\t} else if elseStmt != nil {\n\t\t\tEval(elseStmt.([]any)[elseBodyAt])\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// While delays a while loop.\nfunc While(cond, body any) any {\n\treturn func() any {\n\t\tfor Eval(cond).(bool) {\n\t\t\tEval(body)\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// RepeatUntil delays a repeat-until loop.\nfunc RepeatUntil(body, cond any) any {\n\treturn func() any {\n\t\tfor {\n\t\t\tEval(body)\n\t\t\tif Eval(cond).(bool) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// InitUniverse initializes the universe module with the specified modules.\nfunc InitUniverse(names ...string) {\n\tvariant.InitUniverse(names...)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/variant/math/math.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage math\n\nimport (\n\t\"math\"\n\n\t\"github.com/goplus/xgo/tpl/token\"\n\t\"github.com/goplus/xgo/tpl/variant\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype namedFn struct {\n\tname string\n\tfn   func(...any) any\n}\n\ntype math1 struct {\n\tname string\n\tfn   func(float64) float64\n}\n\ntype math2 struct {\n\tname string\n\tfn   func(x, y float64) float64\n}\n\ntype mathIntFloat struct {\n\tname string\n\tfn   func(x int, y float64) float64\n}\n\nfunc float1(args []any) float64 {\n\tif len(args) != 1 {\n\t\tpanic(\"function call: arity mismatch\")\n\t}\n\treturn variant.Float(args[0])\n}\n\nfunc float2(args []any) (float64, float64) {\n\tif len(args) != 2 {\n\t\tpanic(\"function call: arity mismatch\")\n\t}\n\treturn variant.Float(args[0]), variant.Float(args[1])\n}\n\nfunc intFloat(args []any) (int, float64) {\n\tif len(args) != 2 {\n\t\tpanic(\"function call: arity mismatch\")\n\t}\n\treturn variant.Int(args[0]), variant.Float(args[1])\n}\n\nfunc regMath1(mod *variant.Module, name string, fn func(float64) float64) {\n\tmod.Insert(name, func(args ...any) any {\n\t\treturn fn(float1(args))\n\t})\n}\n\nfunc regMath2(mod *variant.Module, name string, fn func(x, y float64) float64) {\n\tmod.Insert(name, func(args ...any) any {\n\t\treturn fn(float2(args))\n\t})\n}\n\nfunc regMathIntFloat(mod *variant.Module, name string, fn func(x int, y float64) float64) {\n\tmod.Insert(name, func(args ...any) any {\n\t\treturn fn(intFloat(args))\n\t})\n}\n\n// -----------------------------------------------------------------------------\n\nfunc topValue(op token.Token, args []any) any {\n\tret := variant.Eval(args[0])\n\tfor _, v := range args[1:] {\n\t\tif variant.Compare(op, v, ret) {\n\t\t\tret = v\n\t\t}\n\t}\n\treturn ret\n}\n\n// Max returns the maximum value.\nfunc Max(args ...any) any {\n\tif len(args) == 0 {\n\t\tpanic(\"max: arity mismatch\")\n\t}\n\treturn topValue(token.GT, args)\n}\n\n// Min returns the minimum value.\nfunc Min(args ...any) any {\n\tif len(args) == 0 {\n\t\tpanic(\"min: arity mismatch\")\n\t}\n\treturn topValue(token.LT, args)\n}\n\n// -----------------------------------------------------------------------------\n\nvar fnsMath1 = [...]math1{\n\t{\"abs\", math.Abs},\n\t{\"acos\", math.Acos},\n\t{\"acosh\", math.Acosh},\n\t{\"asin\", math.Asin},\n\t{\"asinh\", math.Asinh},\n\t{\"atan\", math.Atan},\n\t{\"atanh\", math.Atanh},\n\t{\"ceil\", math.Ceil},\n\t{\"cos\", math.Cos},\n\t{\"cosh\", math.Cosh},\n\t{\"erf\", math.Erf},\n\t{\"erfc\", math.Erfc},\n\t{\"exp\", math.Exp},\n\t{\"expm1\", math.Expm1},\n\t{\"floor\", math.Floor},\n\t{\"gamma\", math.Gamma},\n\t{\"j0\", math.J0},\n\t{\"j1\", math.J1},\n\t{\"log\", math.Log},\n\t{\"log10\", math.Log10},\n\t{\"log1p\", math.Log1p},\n\t{\"log2\", math.Log2},\n\t{\"round\", math.Round},\n\t{\"roundToEven\", math.RoundToEven},\n\t{\"sin\", math.Sin},\n\t{\"sinh\", math.Sinh},\n\t{\"sqrt\", math.Sqrt},\n\t{\"tan\", math.Tan},\n\t{\"tanh\", math.Tanh},\n\t{\"trunc\", math.Trunc},\n\t{\"y0\", math.Y0},\n\t{\"y1\", math.Y1},\n}\n\nvar fnsMath2 = [...]math2{\n\t{\"atan2\", math.Atan2},\n\t{\"copysign\", math.Copysign},\n\t{\"dim\", math.Dim},\n\t{\"hypot\", math.Hypot},\n\t{\"mod\", math.Mod},\n\t{\"nextafter\", math.Nextafter},\n\t{\"pow\", math.Pow},\n\t{\"remainder\", math.Remainder},\n}\n\nvar fnsMathIntFloat = [...]mathIntFloat{\n\t{\"jn\", math.Jn},\n\t{\"yn\", math.Yn},\n}\n\nvar fnsNamed = [...]namedFn{\n\t{\"max\", Max},\n\t{\"min\", Min},\n}\n\nfunc init() {\n\tmod := variant.NewModule(\"math\")\n\tfor _, m := range fnsMath1 {\n\t\tregMath1(mod, m.name, m.fn)\n\t}\n\tfor _, m := range fnsMath2 {\n\t\tregMath2(mod, m.name, m.fn)\n\t}\n\tfor _, m := range fnsMathIntFloat {\n\t\tregMathIntFloat(mod, m.name, m.fn)\n\t}\n\tfor _, m := range fnsNamed {\n\t\tmod.Insert(m.name, m.fn)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/variant/module.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage variant\n\nimport (\n\t\"github.com/goplus/xgo/tpl\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Module represents a module.\ntype Module struct {\n\tName string\n\tobjs map[string]any\n}\n\n// NewModule creates a new module.\nfunc NewModule(name string) *Module {\n\tmod := &Module{\n\t\tName: name,\n\t\tobjs: make(map[string]any),\n\t}\n\tuniverse.Insert(name, mod)\n\treturn mod\n}\n\n// Lookup looks up an object in the module.\nfunc (p *Module) Lookup(name string) (v any, ok bool) {\n\tv, ok = p.objs[name]\n\treturn\n}\n\n// Insert inserts an object into the module.\nfunc (p *Module) Insert(name string, v any) {\n\tobjs := p.objs\n\tif _, ok := objs[name]; ok {\n\t\tpanic(\"object exists: \" + name)\n\t}\n\tobjs[name] = v\n}\n\n// Merge merges a module into the current module.\nfunc (p *Module) Merge(mod *Module) {\n\tfor name, obj := range mod.objs {\n\t\tp.Insert(name, obj)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\nvar (\n\tuniverse = &Module{\n\t\tobjs: map[string]any{},\n\t}\n)\n\n// Call calls a function.\nfunc Call(needList bool, name string, arglist any) any {\n\tif o, ok := universe.objs[name]; ok {\n\t\tif fn, ok := o.(func(...any) any); ok {\n\t\t\tvar args []any\n\t\t\tif arglist != nil {\n\t\t\t\targs = arglist.([]any)\n\t\t\t\tif needList {\n\t\t\t\t\targs = tpl.List(args)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fn(args...)\n\t\t}\n\t\tpanic(\"not a function: \" + name)\n\t}\n\tpanic(\"function not found: \" + name)\n}\n\n// CallObject calls a function object.\nfunc CallObject(needList bool, fn any, arglist any) any {\n\tif fn, ok := Eval(fn).(func(...any) any); ok {\n\t\tvar args []any\n\t\tif arglist != nil {\n\t\t\targs = arglist.([]any)\n\t\t\tif needList {\n\t\t\t\targs = tpl.List(args)\n\t\t\t}\n\t\t}\n\t\treturn fn(args...)\n\t}\n\tpanic(\"call of non function\")\n}\n\n// -----------------------------------------------------------------------------\n\n// InitUniverse initializes the universe module with the specified modules.\nfunc InitUniverse(names ...string) {\n\tfor _, name := range names {\n\t\tif o, ok := universe.objs[name]; ok {\n\t\t\tif mod, ok := o.(*Module); ok {\n\t\t\t\tuniverse.Merge(mod)\n\t\t\t} else {\n\t\t\t\tpanic(\"not a module: \" + name)\n\t\t\t}\n\t\t} else {\n\t\t\tpanic(\"module not found: \" + name)\n\t\t}\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/variant/time/time.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage time\n\nimport (\n\t\"time\"\n\n\t\"github.com/goplus/xgo/tpl/variant\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc now(args ...any) any {\n\tif len(args) > 0 {\n\t\tpanic(\"function call: arity mismatch\")\n\t}\n\treturn time.Now()\n}\n\nfunc init() {\n\tmod := variant.NewModule(\"time\")\n\tmod.Insert(\"now\", now)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "tpl/variant/variant.go",
    "content": "/*\n * Copyright (c) 2025 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage variant\n\nimport (\n\t\"github.com/goplus/xgo/tpl/token\"\n)\n\n// -----------------------------------------------------------------------------\n\n// DelayValue represents a delayed value.\ntype DelayValue = func() any\n\n// Eval evaluates a value.\nfunc Eval(v any) any {\n\tif d, ok := v.(DelayValue); ok {\n\t\treturn d()\n\t}\n\treturn v\n}\n\n// List converts the matching result of (R % \",\") to a flat list.\n// R % \",\" means R *(\",\" R)\nfunc List(in []any) []any {\n\tnext := in[1].([]any)\n\tret := make([]any, len(next)+1)\n\tret[0] = Eval(in[0])\n\tfor i, v := range next {\n\t\tret[i+1] = Eval(v.([]any)[1])\n\t}\n\treturn ret\n}\n\n// ListOp converts the matching result of (R % \",\") to a flat list.\n// R % \",\" means R *(\",\" R)\nfunc ListOp[T any](in []any, fn func(v any) T) []T {\n\tnext := in[1].([]any)\n\tret := make([]T, len(next)+1)\n\tret[0] = fn(Eval(in[0]))\n\tfor i, v := range next {\n\t\tret[i+1] = fn(Eval(v.([]any)[1]))\n\t}\n\treturn ret\n}\n\n// RangeOp travels the matching result of (R % \",\") and call fn(result of R).\n// R % \",\" means R *(\",\" R)\nfunc RangeOp(in []any, fn func(v any)) {\n\tnext := in[1].([]any)\n\tfn(Eval(in[0]))\n\tfor _, v := range next {\n\t\tfn(Eval(v.([]any)[1]))\n\t}\n}\n\n// Float converts a value to float64.\nfunc Float(v any) float64 {\n\tswitch v := Eval(v).(type) {\n\tcase int:\n\t\treturn float64(v)\n\tcase float64:\n\t\treturn v\n\t}\n\tpanic(\"can't convert to float\")\n}\n\n// Int ensures a value is int.\n// It doesn't convert float to int.\nfunc Int(v any) int {\n\tif v, ok := Eval(v).(int); ok {\n\t\treturn v\n\t}\n\tpanic(\"not an int\")\n}\n\n// -----------------------------------------------------------------------------\n\nfunc cmpInt(op token.Token, x, y int) bool {\n\tswitch op {\n\tcase token.EQ, token.ASSIGN:\n\t\treturn x == y\n\tcase token.NE, token.BIDIARROW:\n\t\treturn x != y\n\tcase token.LT:\n\t\treturn x < y\n\tcase token.LE:\n\t\treturn x <= y\n\tcase token.GT:\n\t\treturn x > y\n\tcase token.GE:\n\t\treturn x >= y\n\t}\n\tpanic(\"invalid int comparison operator: \" + op.String())\n}\n\nfunc cmpFloat(op token.Token, x, y float64) bool {\n\tswitch op {\n\tcase token.EQ, token.ASSIGN:\n\t\treturn x == y\n\tcase token.NE, token.BIDIARROW:\n\t\treturn x != y\n\tcase token.LT:\n\t\treturn x < y\n\tcase token.LE:\n\t\treturn x <= y\n\tcase token.GT:\n\t\treturn x > y\n\tcase token.GE:\n\t\treturn x >= y\n\t}\n\tpanic(\"invalid float comparison operator: \" + op.String())\n}\n\nfunc cmpString(op token.Token, x, y string) bool {\n\tswitch op {\n\tcase token.EQ, token.ASSIGN:\n\t\treturn x == y\n\tcase token.NE, token.BIDIARROW:\n\t\treturn x != y\n\tcase token.LT:\n\t\treturn x < y\n\tcase token.LE:\n\t\treturn x <= y\n\tcase token.GT:\n\t\treturn x > y\n\tcase token.GE:\n\t\treturn x >= y\n\t}\n\tpanic(\"invalid string comparison operator: \" + op.String())\n}\n\nfunc cmpBool(op token.Token, x, y bool) bool {\n\tswitch op {\n\tcase token.EQ, token.ASSIGN:\n\t\treturn x == y\n\tcase token.NE, token.BIDIARROW:\n\t\treturn x != y\n\t}\n\tpanic(\"invalid bool comparison operator: \" + op.String())\n}\n\n// Compare compares two values.\nfunc Compare(op token.Token, x, y any) bool {\n\tx, y = Eval(x), Eval(y)\n\tswitch x := x.(type) {\n\tcase int:\n\t\tswitch y := y.(type) {\n\t\tcase int:\n\t\t\treturn cmpInt(op, x, y)\n\t\tcase float64:\n\t\t\treturn cmpFloat(op, float64(x), y)\n\t\t}\n\tcase float64:\n\t\tswitch y := y.(type) {\n\t\tcase int:\n\t\t\treturn cmpFloat(op, x, float64(y))\n\t\tcase float64:\n\t\t\treturn cmpFloat(op, x, y)\n\t\t}\n\tcase string:\n\t\tif y, ok := y.(string); ok {\n\t\t\treturn cmpString(op, x, y)\n\t\t}\n\tcase bool:\n\t\tif y, ok := y.(bool); ok {\n\t\t\treturn cmpBool(op, x, y)\n\t\t}\n\t}\n\tpanic(\"compare: invalid operation\")\n}\n\n// -----------------------------------------------------------------------------\n\nfunc mopInt(op token.Token, x, y int) int {\n\tswitch op {\n\tcase token.ADD:\n\t\treturn x + y\n\tcase token.SUB:\n\t\treturn x - y\n\tcase token.MUL:\n\t\treturn x * y\n\tcase token.QUO:\n\t\treturn x / y\n\tcase token.REM:\n\t\treturn x % y\n\t}\n\tpanic(\"invalid int operator: \" + op.String())\n}\n\nfunc mopFloat(op token.Token, x, y float64) float64 {\n\tswitch op {\n\tcase token.ADD:\n\t\treturn x + y\n\tcase token.SUB:\n\t\treturn x - y\n\tcase token.MUL:\n\t\treturn x * y\n\tcase token.QUO:\n\t\treturn x / y\n\t}\n\tpanic(\"invalid float operator: \" + op.String())\n}\n\nfunc mopString(op token.Token, x, y string) string {\n\tswitch op {\n\tcase token.ADD:\n\t\treturn x + y\n\t}\n\tpanic(\"invalid string operator: \" + op.String())\n}\n\n// MathOp does a math operation.\nfunc MathOp(op token.Token, x, y any) any {\n\tx, y = Eval(x), Eval(y)\n\tswitch x := x.(type) {\n\tcase int:\n\t\tswitch y := y.(type) {\n\t\tcase int:\n\t\t\treturn mopInt(op, x, y)\n\t\tcase float64:\n\t\t\treturn mopFloat(op, float64(x), y)\n\t\t}\n\tcase float64:\n\t\tswitch y := y.(type) {\n\t\tcase int:\n\t\t\treturn mopFloat(op, x, float64(y))\n\t\tcase float64:\n\t\t\treturn mopFloat(op, x, y)\n\t\t}\n\tcase string:\n\t\tif y, ok := y.(string); ok {\n\t\t\treturn mopString(op, x, y)\n\t\t}\n\t}\n\tpanic(\"mathOp: invalid operation\")\n}\n\n// -----------------------------------------------------------------------------\n\n// LogicOp does a logic operation.\nfunc LogicOp(op token.Token, x, y any) bool {\n\tx, y = Eval(x), Eval(y)\n\tswitch x := x.(type) {\n\tcase bool:\n\t\tif y, ok := y.(bool); ok {\n\t\t\tswitch op {\n\t\t\tcase token.LAND:\n\t\t\t\treturn x && y\n\t\t\tcase token.LOR:\n\t\t\t\treturn x || y\n\t\t\t}\n\t\t}\n\t}\n\tpanic(\"logicOp: invalid operation\")\n}\n\n// -----------------------------------------------------------------------------\n\n// UnaryOp does a unary operation.\nfunc UnaryOp(op token.Token, x any) any {\n\tx = Eval(x)\n\tswitch x := x.(type) {\n\tcase int:\n\t\tswitch op {\n\t\tcase token.SUB:\n\t\t\treturn -x\n\t\tcase token.ADD:\n\t\t\treturn x\n\t\t}\n\tcase float64:\n\t\tswitch op {\n\t\tcase token.SUB:\n\t\t\treturn -x\n\t\tcase token.ADD:\n\t\t\treturn x\n\t\t}\n\tcase bool:\n\t\tif op == token.NOT {\n\t\t\treturn !x\n\t\t}\n\t}\n\tpanic(\"unaryOp: invalid operation\")\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/build/_testdata/hello/hello.expect",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"XGo\")\n}\n"
  },
  {
    "path": "x/build/_testdata/hello/main.xgo",
    "content": "println \"XGo\"\n"
  },
  {
    "path": "x/build/_testdata/multi/Rect.gox",
    "content": "var (\n\tBaseClass\n\tWidth, Height float64\n\t*AggClass\n)\n\ntype BaseClass struct {\n\tx int\n\ty int\n}\n\ntype AggClass struct{}\n\nfunc Area() float64 {\n\treturn Width * Height\n}\n"
  },
  {
    "path": "x/build/_testdata/multi/main.xgo",
    "content": "rc := &Rect{Width: 100, Height: 200}\nprintln rc\n"
  },
  {
    "path": "x/build/_testdata/multi/multi.expect",
    "content": "package main\n\nimport \"fmt\"\n\ntype BaseClass struct {\n\tx int\n\ty int\n}\ntype AggClass struct {\n}\ntype Rect struct {\n\tBaseClass\n\tWidth  float64\n\tHeight float64\n\t*AggClass\n}\n\nfunc (this *Rect) Area() float64 {\n\treturn this.Width * this.Height\n}\nfunc main() {\n\trc := &Rect{Width: 100, Height: 200}\n\tfmt.Println(rc)\n}\n"
  },
  {
    "path": "x/build/_testdata/pkg/pkg.expect",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc Hello() {\n\tfmt.Println(\"XGo\")\n}\n"
  },
  {
    "path": "x/build/_testdata/pkg/pkg.xgo",
    "content": "package pkg\n\nfunc Hello() {\n\tprintln \"XGo\"\n}\n"
  },
  {
    "path": "x/build/build.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage build\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\tgoast \"go/ast\"\n\t\"go/types\"\n\t\"path/filepath\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/gogen/packages\"\n\t\"github.com/goplus/mod/modfile\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/parser/fsx/memfs\"\n\t\"github.com/goplus/xgo/token\"\n)\n\ntype Class = cl.Class\n\nvar (\n\tprojects = make(map[string]*cl.Project)\n)\n\nfunc RegisterClassFileType(ext string, class string, works []*Class, pkgPaths ...string) {\n\tcls := &cl.Project{\n\t\tExt:      ext,\n\t\tClass:    class,\n\t\tWorks:    works,\n\t\tPkgPaths: pkgPaths,\n\t}\n\tif ext != \"\" {\n\t\tprojects[ext] = cls\n\t}\n\tfor _, w := range works {\n\t\tprojects[w.Ext] = cls\n\t}\n}\n\nfunc init() {\n\tRegisterClassFileType(\".gmx\", \"Game\", []*Class{{Ext: \".spx\", Class: \"Sprite\"}}, \"github.com/goplus/spx\", \"math\")\n}\n\ntype Package struct {\n\tFset *token.FileSet\n\tPkg  *gogen.Package\n}\n\nfunc (p *Package) ToSource() ([]byte, error) {\n\tvar buf bytes.Buffer\n\tif err := p.Pkg.WriteTo(&buf); err != nil {\n\t\treturn nil, err\n\t}\n\treturn buf.Bytes(), nil\n}\n\nfunc (p *Package) ToAst() *goast.File {\n\treturn p.Pkg.ASTFile()\n}\n\nfunc ClassKind(fname string) (isProj, ok bool) {\n\text := modfile.ClassExt(fname)\n\tswitch ext {\n\tcase \".gmx\":\n\t\treturn true, true\n\tcase \".spx\":\n\t\treturn fname == \"main.spx\", true\n\tdefault:\n\t\tif c, ok := projects[ext]; ok {\n\t\t\tfor _, w := range c.Works {\n\t\t\t\tif w.Ext == ext {\n\t\t\t\t\tif ext != c.Ext || fname != \"main\"+ext {\n\t\t\t\t\t\treturn false, true\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true, true\n\t\t}\n\t}\n\treturn\n}\n\ntype Context struct {\n\timpl       types.Importer\n\tfset       *token.FileSet\n\tLoadConfig func(*cl.Config)\n}\n\nfunc Default() *Context {\n\tfset := token.NewFileSet()\n\timpl := packages.NewImporter(fset)\n\treturn NewContext(impl, fset)\n}\n\nfunc NewContext(impl types.Importer, fset *token.FileSet) *Context {\n\tif fset == nil {\n\t\tfset = token.NewFileSet()\n\t}\n\treturn &Context{impl: impl, fset: fset}\n}\n\nfunc (c *Context) Import(path string) (*types.Package, error) {\n\treturn c.impl.Import(path)\n}\n\nfunc (c *Context) ParseDir(dir string) (*Package, error) {\n\tpkgs, err := parser.ParseDirEx(c.fset, dir, parser.Config{\n\t\tClassKind: ClassKind,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn c.loadPackage(dir, pkgs)\n}\n\nfunc (c *Context) ParseFSDir(fs parser.FileSystem, dir string) (*Package, error) {\n\tpkgs, err := parser.ParseFSDir(c.fset, fs, dir, parser.Config{\n\t\tClassKind: ClassKind,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn c.loadPackage(dir, pkgs)\n}\n\nfunc (c *Context) ParseFile(file string, src any) (*Package, error) {\n\tfs, err := memfs.File(file, src)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn c.ParseFSDir(fs, filepath.Dir(file))\n}\n\nfunc (c *Context) loadPackage(srcDir string, pkgs map[string]*ast.Package) (*Package, error) {\n\tmainPkg, ok := pkgs[\"main\"]\n\tif !ok {\n\t\tfor _, v := range pkgs {\n\t\t\tmainPkg = v\n\t\t\tbreak\n\t\t}\n\t}\n\tconf := &cl.Config{Fset: c.fset}\n\tconf.Importer = c\n\tconf.LookupClass = func(ext string) (c *cl.Project, ok bool) {\n\t\tc, ok = projects[ext]\n\t\treturn\n\t}\n\tif c.LoadConfig != nil {\n\t\tc.LoadConfig(conf)\n\t}\n\tout, err := cl.NewPackage(\"\", mainPkg, conf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Package{c.fset, out}, nil\n}\n\nfunc (ctx *Context) BuildFile(filename string, src any) (data []byte, err error) {\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\terr = fmt.Errorf(\"compile %v failed. %v\", filename, r)\n\t\t}\n\t}()\n\tpkg, err := ctx.ParseFile(filename, src)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn pkg.ToSource()\n}\n\nfunc (ctx *Context) BuildFSDir(fs parser.FileSystem, dir string) (data []byte, err error) {\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\terr = fmt.Errorf(\"compile %v failed. %v\", dir, err)\n\t\t}\n\t}()\n\tpkg, err := ctx.ParseFSDir(fs, dir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn pkg.ToSource()\n}\n\nfunc (ctx *Context) BuildDir(dir string) (data []byte, err error) {\n\tdefer func() {\n\t\tr := recover()\n\t\tif r != nil {\n\t\t\terr = fmt.Errorf(\"compile %v failed. %v\", dir, err)\n\t\t}\n\t}()\n\tpkg, err := ctx.ParseDir(dir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn pkg.ToSource()\n}\n"
  },
  {
    "path": "x/build/build_test.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage build_test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/printer\"\n\t\"go/types\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/parser/fsx\"\n\t\"github.com/goplus/xgo/x/build\"\n)\n\nvar (\n\tctx = build.Default()\n)\n\nfunc init() {\n\tcl.SetDebug(cl.FlagNoMarkAutogen)\n\tctx.LoadConfig = func(cfg *cl.Config) {\n\t\tcfg.NoFileLine = true\n\t}\n\tbuild.RegisterClassFileType(\".tspx\", \"MyGame\", []*build.Class{\n\t\t{Ext: \".tspx\", Class: \"Sprite\"},\n\t}, \"github.com/goplus/xgo/cl/internal/spx\")\n\tbuild.RegisterClassFileType(\"_yap.gox\", \"App\", nil, \"github.com/goplus/yap\")\n}\n\nfunc gopClTest(t *testing.T, gopcode any, expected string) {\n\tgopClTestEx(t, \"main.xgo\", gopcode, expected)\n}\n\nfunc gopClTestEx(t *testing.T, filename string, gopcode any, expected string) {\n\tdata, err := ctx.BuildFile(filename, gopcode)\n\tif err != nil {\n\t\tt.Fatalf(\"build gop error: %v\", err)\n\t}\n\tif string(data) != expected {\n\t\tfmt.Println(\"build gop error:\")\n\t\tfmt.Println(string(data))\n\t\tt.Fail()\n\t}\n}\n\nfunc testKind(t *testing.T, name string, proj, class bool) {\n\tisProj, ok := build.ClassKind(name)\n\tif isProj != proj || ok != class {\n\t\tt.Fatal(\"check classkind failed\", name, isProj, ok)\n\t}\n}\n\nfunc TestKind(t *testing.T) {\n\ttestKind(t, \"Cat.gox\", false, false)\n\ttestKind(t, \"Cat.spx\", false, true)\n\ttestKind(t, \"main.spx\", true, true)\n\ttestKind(t, \"main.gmx\", true, true)\n\ttestKind(t, \"Cat.tspx\", false, true)\n\ttestKind(t, \"main.tspx\", true, true)\n\ttestKind(t, \"blog_yap.gox\", true, true)\n}\n\nfunc TestXGo(t *testing.T) {\n\tvar src = `\nprintln \"XGo\"\n`\n\tvar expect = `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"XGo\")\n}\n`\n\tgopClTest(t, src, expect)\n\tgopClTest(t, []byte(src), expect)\n\tgopClTest(t, bytes.NewBufferString(src), expect)\n\tgopClTestEx(t, `./_testdata/hello/main.xgo`, nil, expect)\n\n\tf, err := os.Open(\"./_testdata/hello/main.xgo\")\n\tif err != nil {\n\t\tt.Fatal(\"open failed\", err)\n\t}\n\tdefer f.Close()\n\tgopClTest(t, f, expect)\n}\n\nfunc TestGox(t *testing.T) {\n\tgopClTestEx(t, \"Rect.gox\", `\nprintln \"XGo\"\n`, `package main\n\nimport \"fmt\"\n\ntype Rect struct {\n}\n\nfunc (this *Rect) Main() {\n\tfmt.Println(\"XGo\")\n}\nfunc main() {\n\tnew(Rect).Main()\n}\n`)\n\tgopClTestEx(t, \"Rect.gox\", `\nvar (\n\tBuffer\n\tv int\n)\ntype Buffer struct {\n\tbuf []byte\n}\nprintln \"XGo\"\n`, `package main\n\nimport \"fmt\"\n\ntype Buffer struct {\n\tbuf []byte\n}\ntype Rect struct {\n\tBuffer\n\tv int\n}\n\nfunc (this *Rect) Main() {\n\tfmt.Println(\"XGo\")\n}\nfunc main() {\n\tnew(Rect).Main()\n}\n`)\n\tgopClTestEx(t, \"Rect.gox\", `\nvar (\n\t*Buffer\n\tv int\n)\ntype Buffer struct {\n\tbuf []byte\n}\nprintln \"XGo\"\n`, `package main\n\nimport \"fmt\"\n\ntype Buffer struct {\n\tbuf []byte\n}\ntype Rect struct {\n\t*Buffer\n\tv int\n}\n\nfunc (this *Rect) Main() {\n\tfmt.Println(\"XGo\")\n}\nfunc main() {\n\tnew(Rect).Main()\n}\n`)\n\tgopClTestEx(t, \"Rect.gox\", `\nimport \"bytes\"\nvar (\n\t*bytes.Buffer\n\tv int\n)\nprintln \"XGo\"\n`, `package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n)\n\ntype Rect struct {\n\t*bytes.Buffer\n\tv int\n}\n\nfunc (this *Rect) Main() {\n\tfmt.Println(\"XGo\")\n}\nfunc main() {\n\tnew(Rect).Main()\n}\n`)\n\tgopClTestEx(t, \"Rect.gox\", `\nimport \"bytes\"\nvar (\n\tbytes.Buffer\n\tv int\n)\nprintln \"XGo\"\n`, `package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n)\n\ntype Rect struct {\n\tbytes.Buffer\n\tv int\n}\n\nfunc (this *Rect) Main() {\n\tfmt.Println(\"XGo\")\n}\nfunc main() {\n\tnew(Rect).Main()\n}\n`)\n}\n\nfunc TestBig(t *testing.T) {\n\tgopClTest(t, `\na := 1/2r\nprintln a+1/2r\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/xgo/ng\"\n\t\"math/big\"\n)\n\nfunc main() {\n\ta := ng.Bigrat_Init__2(big.NewRat(1, 2))\n\tfmt.Println((ng.Bigrat).XGo_Add(a, ng.Bigrat_Init__2(big.NewRat(1, 2))))\n}\n`)\n}\n\nfunc TestIoxLines(t *testing.T) {\n\tgopClTest(t, `\nimport \"io\"\n\nvar r io.Reader\n\nfor line <- lines(r) {\n\tprintln line\n}\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/osx\"\n\t\"io\"\n)\n\nvar r io.Reader\n\nfunc main() {\n\tfor _xgo_it := osx.Lines(r).XGo_Enum(); ; {\n\t\tvar _xgo_ok bool\n\t\tline, _xgo_ok := _xgo_it.Next()\n\t\tif !_xgo_ok {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Println(line)\n\t}\n}\n`)\n}\n\nfunc TestErrorWrap(t *testing.T) {\n\tgopClTest(t, `\nimport (\n    \"strconv\"\n)\n\nfunc add(x, y string) (int, error) {\n    return strconv.Atoi(x)? + strconv.Atoi(y)?, nil\n}\n\nfunc addSafe(x, y string) int {\n    return strconv.Atoi(x)?:0 + strconv.Atoi(y)?:0\n}\n\nprintln add(\"100\", \"23\")!\n\nsum, err := add(\"10\", \"abc\")\nprintln sum, err\n\nprintln addSafe(\"10\", \"abc\")\n`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/qiniu/x/errors\"\n\t\"strconv\"\n)\n\nfunc add(x string, y string) (int, error) {\n\tvar _autoGo_1 int\n\t{\n\t\tvar _xgo_err error\n\t\t_autoGo_1, _xgo_err = strconv.Atoi(x)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"strconv.Atoi(x)\", \"main.xgo\", 7, \"main.add\")\n\t\t\treturn 0, _xgo_err\n\t\t}\n\t\tgoto _autoGo_2\n\t_autoGo_2:\n\t}\n\tvar _autoGo_3 int\n\t{\n\t\tvar _xgo_err error\n\t\t_autoGo_3, _xgo_err = strconv.Atoi(y)\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"strconv.Atoi(y)\", \"main.xgo\", 7, \"main.add\")\n\t\t\treturn 0, _xgo_err\n\t\t}\n\t\tgoto _autoGo_4\n\t_autoGo_4:\n\t}\n\treturn _autoGo_1 + _autoGo_3, nil\n}\nfunc addSafe(x string, y string) int {\n\treturn func() (_xgo_ret int) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = strconv.Atoi(x)\n\t\tif _xgo_err != nil {\n\t\t\treturn 0\n\t\t}\n\t\treturn\n\t}() + func() (_xgo_ret int) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = strconv.Atoi(y)\n\t\tif _xgo_err != nil {\n\t\t\treturn 0\n\t\t}\n\t\treturn\n\t}()\n}\nfunc main() {\n\tfmt.Println(func() (_xgo_ret int) {\n\t\tvar _xgo_err error\n\t\t_xgo_ret, _xgo_err = add(\"100\", \"23\")\n\t\tif _xgo_err != nil {\n\t\t\t_xgo_err = errors.NewFrame(_xgo_err, \"add(\\\"100\\\", \\\"23\\\")\", \"main.xgo\", 14, \"main.main\")\n\t\t\tpanic(_xgo_err)\n\t\t}\n\t\treturn\n\t}())\n\tsum, err := add(\"10\", \"abc\")\n\tfmt.Println(sum, err)\n\tfmt.Println(addSafe(\"10\", \"abc\"))\n}\n`)\n}\n\nfunc TestSpx(t *testing.T) {\n\tgopClTestEx(t, \"main.tspx\", `println \"hi\"`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx\"\n)\n\ntype MyGame struct {\n\tspx.MyGame\n}\n\nfunc (this *MyGame) MainEntry() {\n\tfmt.Println(\"hi\")\n}\nfunc (this *MyGame) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc main() {\n\tnew(MyGame).Main()\n}\n`)\n\tgopClTestEx(t, \"Cat.tspx\", `println \"hi\"`, `package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/goplus/xgo/cl/internal/spx\"\n)\n\ntype Cat struct {\n\tspx.Sprite\n\t*MyGame\n}\ntype MyGame struct {\n\tspx.MyGame\n}\n\nfunc (this *MyGame) Main() {\n\tspx.Gopt_MyGame_Main(this)\n}\nfunc (this *Cat) Main() {\n\tfmt.Println(\"hi\")\n}\nfunc main() {\n\tnew(MyGame).Main()\n}\n`)\n}\n\nfunc testFromDir(t *testing.T, relDir string) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = path.Join(dir, relDir)\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"ReadDir failed:\", err)\n\t}\n\tfor _, fi := range fis {\n\t\tname := fi.Name()\n\t\tif strings.HasPrefix(name, \"_\") {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttestFrom(t, name, dir+\"/\"+name)\n\t\t})\n\t}\n}\n\nfunc testFrom(t *testing.T, name, dir string) {\n\tdata, err := ctx.BuildDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"BuildDir failed:\", err)\n\t}\n\tif chk, err := os.ReadFile(filepath.Join(dir, name+\".expect\")); err == nil {\n\t\tif !bytes.Equal(data, chk) {\n\t\t\tt.Fatalf(\"-- %v output check error --\\n%v\\n--\\n%v\", name, string(data), string(chk))\n\t\t}\n\t}\n}\n\nfunc TestFromTestdata(t *testing.T) {\n\ttestFromDir(t, \"./_testdata\")\n}\n\nfunc TestFS(t *testing.T) {\n\tvar expect = []byte(`package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"XGo\")\n}\n`)\n\tdata, err := ctx.BuildFSDir(fsx.Local, \"./_testdata/hello\")\n\tif err != nil {\n\t\tt.Fatal(\"build fs dir failed\", err)\n\t}\n\tif !bytes.Equal(data, expect) {\n\t\tt.Fatal(\"build fs data failed\", string(data))\n\t}\n}\n\nfunc TestAst(t *testing.T) {\n\tvar expect = []byte(`package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"XGo\")\n}\n`)\n\tpkg, err := ctx.ParseFSDir(fsx.Local, \"./_testdata/hello\")\n\tif err != nil {\n\t\tt.Fatal(\"parser fs dir failed\", err)\n\t}\n\tvar buf bytes.Buffer\n\terr = printer.Fprint(&buf, pkg.Fset, pkg.ToAst())\n\tif err != nil {\n\t\tt.Fatal(\"fprint ast error\", err)\n\t}\n\tif !bytes.Equal(buf.Bytes(), expect) {\n\t\tt.Fatal(\"build ast data failed\", buf.String())\n\t}\n}\n\nfunc TestError(t *testing.T) {\n\t_, err := ctx.BuildFile(\"main.xgo\", \"bad code\")\n\tif err == nil {\n\t\tt.Fatal(\"BuildFile: no error?\")\n\t}\n\t_, err = ctx.BuildDir(\"./demo/nofound\")\n\tif err == nil {\n\t\tt.Fatal(\"BuildDir: no error?\")\n\t}\n\t_, err = ctx.BuildFSDir(fsx.Local, \"./demo/nofound\")\n\tif err == nil {\n\t\tt.Fatal(\"BuildDir: no error?\")\n\t}\n\t_, err = ctx.BuildFile(\"main.xgo\", \"func main()\")\n\tif err == nil {\n\t\tt.Fatal(\"BuildFile: no error?\")\n\t}\n\t_, err = ctx.ParseFile(\"main.xgo\", 123)\n\tif err == nil {\n\t\tt.Fatal(\"ParseFile: no error?\")\n\t}\n\t_, err = ctx.ParseFile(\"./demo/nofound/main.xgo\", nil)\n\tif err == nil {\n\t\tt.Fatal(\"ParseFile: no error?\")\n\t}\n}\n\ntype emptyImporter struct {\n}\n\nfunc (i *emptyImporter) Import(path string) (*types.Package, error) {\n\treturn nil, fmt.Errorf(\"not found %v\", path)\n}\n\nfunc TestContext(t *testing.T) {\n\tctx := build.NewContext(&emptyImporter{}, nil)\n\t_, err := ctx.BuildFile(\"main.xgo\", `import \"fmt\"; fmt.Println \"XGo\"`)\n\tif err == nil {\n\t\tt.Fatal(\"BuildFile: no error?\")\n\t}\n}\n"
  },
  {
    "path": "x/fakenet/conn.go",
    "content": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage fakenet\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\n// NewConn returns a net.Conn built on top of the supplied reader and writer.\n// It decouples the read and write on the conn from the underlying stream\n// to enable Close to abort ones that are in progress.\n// It's primary use is to fake a network connection from stdin and stdout.\nfunc NewConn(name string, in io.ReadCloser, out io.WriteCloser) net.Conn {\n\tc := &fakeConn{\n\t\tname:   name,\n\t\treader: newFeeder(in.Read),\n\t\twriter: newFeeder(out.Write),\n\t\tin:     in,\n\t\tout:    out,\n\t}\n\tgo c.reader.run()\n\tgo c.writer.run()\n\treturn c\n}\n\ntype fakeConn struct {\n\tname   string\n\treader *connFeeder\n\twriter *connFeeder\n\tin     io.ReadCloser\n\tout    io.WriteCloser\n}\n\ntype fakeAddr string\n\n// connFeeder serializes calls to the source function (io.Reader.Read or\n// io.Writer.Write) by delegating them to a channel. This also allows calls to\n// be intercepted when the connection is closed, and cancelled early if the\n// connection is closed while the calls are still outstanding.\ntype connFeeder struct {\n\tsource func([]byte) (int, error)\n\tinput  chan []byte\n\tresult chan feedResult\n\tmu     sync.Mutex\n\tclosed bool\n\tdone   chan struct{}\n}\n\ntype feedResult struct {\n\tn   int\n\terr error\n}\n\nfunc (c *fakeConn) Close() error {\n\tc.reader.close()\n\tc.writer.close()\n\tc.in.Close()\n\tc.out.Close()\n\treturn nil\n}\n\nfunc (c *fakeConn) Read(b []byte) (n int, err error)   { return c.reader.do(b) }\nfunc (c *fakeConn) Write(b []byte) (n int, err error)  { return c.writer.do(b) }\nfunc (c *fakeConn) LocalAddr() net.Addr                { return fakeAddr(c.name) }\nfunc (c *fakeConn) RemoteAddr() net.Addr               { return fakeAddr(c.name) }\nfunc (c *fakeConn) SetDeadline(t time.Time) error      { return nil }\nfunc (c *fakeConn) SetReadDeadline(t time.Time) error  { return nil }\nfunc (c *fakeConn) SetWriteDeadline(t time.Time) error { return nil }\nfunc (a fakeAddr) Network() string                     { return \"fake\" }\nfunc (a fakeAddr) String() string                      { return string(a) }\n\nfunc newFeeder(source func([]byte) (int, error)) *connFeeder {\n\treturn &connFeeder{\n\t\tsource: source,\n\t\tinput:  make(chan []byte),\n\t\tresult: make(chan feedResult),\n\t\tdone:   make(chan struct{}),\n\t}\n}\n\nfunc (f *connFeeder) close() {\n\tf.mu.Lock()\n\tif !f.closed {\n\t\tf.closed = true\n\t\tclose(f.done)\n\t}\n\tf.mu.Unlock()\n}\n\nfunc (f *connFeeder) do(b []byte) (n int, err error) {\n\t// send the request to the worker\n\tselect {\n\tcase f.input <- b:\n\tcase <-f.done:\n\t\treturn 0, io.EOF\n\t}\n\t// get the result from the worker\n\tselect {\n\tcase r := <-f.result:\n\t\treturn r.n, r.err\n\tcase <-f.done:\n\t\treturn 0, io.EOF\n\t}\n}\n\nfunc (f *connFeeder) run() {\n\tvar b []byte\n\tfor {\n\t\t// wait for an input request\n\t\tselect {\n\t\tcase b = <-f.input:\n\t\tcase <-f.done:\n\t\t\treturn\n\t\t}\n\t\t// invoke the underlying method\n\t\tn, err := f.source(b)\n\t\t// send the result back to the requester\n\t\tselect {\n\t\tcase f.result <- feedResult{n: n, err: err}:\n\t\tcase <-f.done:\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "x/format/README.md",
    "content": "XGo Code Style\n======\n\nTODO\n\n\n## Specification\n\n### No package main\n\n```go\npackage main\n\nfunc f() {\n}\n```\n\nwill be converted into:\n\n```go\nfunc f() {\n}\n```\n\n\n### No func main\n\n```go\npackage main\n\nfunc main() {\n    a := 0\n}\n```\n\nwill be converted into:\n\n```go\na := 0\n```\n\n\n### Replace fmt.Print to builtin\n\n```go\nimport \"fmt\"\n\nn, err := fmt.Println(\"Hello world\")\n```\n\nwill be converted into:\n\n```go\nn, err := echo(\"Hello world\")\n```\n\nNote:\n\n* Convert `fmt.Errorf => errorf`\n* Convert `fmt.Fprint => fprint`\n* Convert `fmt.Fprintf => fprintf`\n* Convert `fmt.Fprintln => fprintln`\n* Convert `fmt.Print` => `print`\n* Convert `fmt.Printf` => `printf`\n* Convert `fmt.Println` => `echo`\n* Convert `fmt.Sprint => sprint`\n* Convert `fmt.Sprintf => sprintf`\n* Convert `fmt.Sprintln => sprintln`\n\n### Command style first\n\n```go\nimport \"fmt\"\n\nfmt.Println()\nfmt.Println(fmt.Println(\"Hello world\"))\n```\n\nwill be converted into:\n\n```go\necho\necho echo(\"Hello world\")\n```\n\nNote:\n\n* Only the outermost function call statement is converted into command style. So `fmt.Println(fmt.Println(\"Hello world\"))` is converted into `echo echo(\"Hello world\")`, not `echo echo \"Hello world\"`.\n\n\n### pkg.Fncall starting with lowercase\n\n```go\nimport \"math\"\n\necho math.Sin(math.Pi/3)\n```\n\nwill be converted into:\n\n```go\necho math.sin(math.Pi/3)\n```\n\n### Funclit of params convert to lambda in fncall (skip named results)\n\n```go\necho(demo(func(n int) int {\n\treturn n+100\n}))\n\necho(demo(func(n int) (v int) {\n\treturn n+100\n}))\n\nonStart(func() {\n\techo(\"start\")\n})\n```\n\nwill be converted into:\n```\necho demo(n => n + 100)\n\necho demo(func(n int) (v int) {\n\treturn n + 100\n})\n\nonStart => {\n\techo \"start\"\n}\n```\n"
  },
  {
    "path": "x/format/_testdata/collection/format.expect",
    "content": "// We often need our programs to perform operations on\n// collections of data, like selecting all items that\n// satisfy a given predicate or mapping all items to a new\n// collection with a custom function.\n\n// In some languages it's idiomatic to use [generic](http://en.wikipedia.org/wiki/Generic_programming)\n// data structures and algorithms. Go does not support\n// generics; in Go it's common to provide collection\n// functions if and when they are specifically needed for\n// your program and data types.\n\n// Here are some example collection functions for slices\n// of `strings`. You can use these examples to build your\n// own functions. Note that in some cases it may be\n// clearest to just inline the collection-manipulating\n// code directly, instead of creating and calling a\n// helper function.\n\nimport (\n\t\"strings\"\n)\n\n// Index returns the first index of the target string `t`, or\n// -1 if no match is found.\nfunc Index(vs []string, t string) int {\n\tfor i, v := range vs {\n\t\tif v == t {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// Include returns `true` if the target string t is in the\n// slice.\nfunc Include(vs []string, t string) bool {\n\treturn Index(vs, t) >= 0\n}\n\n// Any returns `true` if one of the strings in the slice\n// satisfies the predicate `f`.\nfunc Any(vs []string, f func(string) bool) bool {\n\tfor _, v := range vs {\n\t\tif f(v) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// All returns `true` if all of the strings in the slice\n// satisfy the predicate `f`.\nfunc All(vs []string, f func(string) bool) bool {\n\tfor _, v := range vs {\n\t\tif !f(v) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Filter returns a new slice containing all strings in the\n// slice that satisfy the predicate `f`.\nfunc Filter(vs []string, f func(string) bool) []string {\n\tvsf := make([]string, 0)\n\tfor _, v := range vs {\n\t\tif f(v) {\n\t\t\tvsf = append(vsf, v)\n\t\t}\n\t}\n\treturn vsf\n}\n\n// Map returns a new slice containing the results of applying\n// the function `f` to each string in the original slice.\nfunc Map(vs []string, f func(string) string) []string {\n\tvsm := make([]string, len(vs))\n\tfor i, v := range vs {\n\t\tvsm[i] = f(v)\n\t}\n\treturn vsm\n}\n\n// Here we try out our various collection functions.\nvar strs = []string{\"peach\", \"apple\", \"pear\", \"plum\"}\n\necho Index(strs, \"pear\")\n\necho Include(strs, \"grape\")\n\necho Any(strs, v => strings.hasPrefix(v, \"p\"))\n\necho All(strs, v => strings.hasPrefix(v, \"p\"))\n\necho Filter(strs, v => strings.contains(v, \"e\"))\n\n// The above examples all used anonymous functions,\n// but you can also use named functions of the correct\n// type.\necho Map(strs, strings.ToUpper)\n"
  },
  {
    "path": "x/format/_testdata/collection/index.xgo",
    "content": "// We often need our programs to perform operations on\n// collections of data, like selecting all items that\n// satisfy a given predicate or mapping all items to a new\n// collection with a custom function.\n\n// In some languages it's idiomatic to use [generic](http://en.wikipedia.org/wiki/Generic_programming)\n// data structures and algorithms. Go does not support\n// generics; in Go it's common to provide collection\n// functions if and when they are specifically needed for\n// your program and data types.\n\n// Here are some example collection functions for slices\n// of `strings`. You can use these examples to build your\n// own functions. Note that in some cases it may be\n// clearest to just inline the collection-manipulating\n// code directly, instead of creating and calling a\n// helper function.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// Index returns the first index of the target string `t`, or\n// -1 if no match is found.\nfunc Index(vs []string, t string) int {\n\tfor i, v := range vs {\n\t\tif v == t {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// Include returns `true` if the target string t is in the\n// slice.\nfunc Include(vs []string, t string) bool {\n\treturn Index(vs, t) >= 0\n}\n\n// Any returns `true` if one of the strings in the slice\n// satisfies the predicate `f`.\nfunc Any(vs []string, f func(string) bool) bool {\n\tfor _, v := range vs {\n\t\tif f(v) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// All returns `true` if all of the strings in the slice\n// satisfy the predicate `f`.\nfunc All(vs []string, f func(string) bool) bool {\n\tfor _, v := range vs {\n\t\tif !f(v) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Filter returns a new slice containing all strings in the\n// slice that satisfy the predicate `f`.\nfunc Filter(vs []string, f func(string) bool) []string {\n\tvsf := make([]string, 0)\n\tfor _, v := range vs {\n\t\tif f(v) {\n\t\t\tvsf = append(vsf, v)\n\t\t}\n\t}\n\treturn vsf\n}\n\n// Map returns a new slice containing the results of applying\n// the function `f` to each string in the original slice.\nfunc Map(vs []string, f func(string) string) []string {\n\tvsm := make([]string, len(vs))\n\tfor i, v := range vs {\n\t\tvsm[i] = f(v)\n\t}\n\treturn vsm\n}\n\nfunc main() {\n\n\t// Here we try out our various collection functions.\n\tvar strs = []string{\"peach\", \"apple\", \"pear\", \"plum\"}\n\n\tfmt.Println(Index(strs, \"pear\"))\n\n\tfmt.Println(Include(strs, \"grape\"))\n\n\tfmt.Println(Any(strs, func(v string) bool {\n\t\treturn strings.HasPrefix(v, \"p\")\n\t}))\n\n\tfmt.Println(All(strs, func(v string) bool {\n\t\treturn strings.HasPrefix(v, \"p\")\n\t}))\n\n\tfmt.Println(Filter(strs, func(v string) bool {\n\t\treturn strings.Contains(v, \"e\")\n\t}))\n\n\t// The above examples all used anonymous functions,\n\t// but you can also use named functions of the correct\n\t// type.\n\tfmt.Println(Map(strs, strings.ToUpper))\n\n}\n"
  },
  {
    "path": "x/format/_testdata/gopsyntax/format.expect",
    "content": "a := [1, 2]\nplot x => x * x\nonStart x => {\n\tprintln x\n}\nfor i in :10 {\n}\nprintln [x*x for x in :10]\nprint f()?:10\n"
  },
  {
    "path": "x/format/_testdata/gopsyntax/index.xgo",
    "content": "a := [1, 2]\nplot x => x * x\nonStart x => {\n\tprintln x\n}\nfor i <- :10 {\n}\nprintln [x*x for x <- :10]\nprint f()?:10\n"
  },
  {
    "path": "x/format/_testdata/syntax/format.expect",
    "content": "func f(...T) {\n}\n\na++\narr := [...]int{1: 3}\nm := make(map[string]chan interface{})\nfor range arr[1:] {\nfoo:\n\t*(p) = 1\n\techo f(arr...)\n\tm[\"a\"] <- true\n}\nfor i := 0; i < 10; i++ {\n\tvar t *T\n\tif false {\n\t} else {\n\t}\n\tselect {\n\tcase <-m[\"a\"]:\n\t}\n\tswitch {\n\tcase i == 10:\n\t}\n\tswitch t.(type) {\n\tcase int:\n\t\tdefer f()\n\t\tgo f().println(\"Hi\")\n\t}\n}\n"
  },
  {
    "path": "x/format/_testdata/syntax/index.xgo",
    "content": "import \"fmt\"\n\nfunc f(...T) {\n}\n\na++\narr := [...]int{1: 3}\nm := make(map[string]chan interface{})\nfor range arr[1:] {\nfoo:\n\t*(p) = 1\n\tfmt.Println(f(arr...))\n\tm[\"a\"] <- true\n}\nfor i := 0; i < 10; i++ {\n\tvar t *T\n\tif false {\n\t} else {\n\t}\n\tselect {\n\tcase <-m[\"a\"]:\n\t}\n\tswitch {\n\tcase i == 10:\n\t}\n\tswitch t.(type) {\n\tcase int:\n\t\tdefer f()\n\t\tgo f().Println(\"Hi\")\n\t}\n}\n"
  },
  {
    "path": "x/format/format.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage format\n\nimport (\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// -----------------------------------------------------------------------------\n\nvar printFuncs = [][2]string{\n\t{\"Errorf\", \"errorf\"},\n\t{\"Print\", \"print\"},\n\t{\"Printf\", \"printf\"},\n\t{\"Println\", \"println\"},\n\t{\"Fprint\", \"fprint\"},\n\t{\"Fprintf\", \"fprintf\"},\n\t{\"Fprintln\", \"fprintln\"},\n\t{\"Sprint\", \"sprint\"},\n\t{\"Sprintf\", \"sprintf\"},\n\t{\"Sprintln\", \"sprintln\"},\n}\n\nfunc fmtToBuiltin(ctx *importCtx, sel *ast.Ident, ref *ast.Expr) bool {\n\tif ctx.pkgPath == \"fmt\" {\n\t\tfor _, fns := range printFuncs {\n\t\t\tif fns[0] == sel.Name || fns[1] == sel.Name {\n\t\t\t\tname := fns[1]\n\t\t\t\tif name == \"println\" {\n\t\t\t\t\tname = \"echo\"\n\t\t\t\t}\n\t\t\t\t*ref = &ast.Ident{NamePos: sel.NamePos, Name: name}\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n// -----------------------------------------------------------------------------\n\nfunc commandStyleFirst(v *ast.CallExpr) {\n\tswitch v.Fun.(type) {\n\tcase *ast.Ident, *ast.SelectorExpr:\n\t\tif v.NoParenEnd == token.NoPos {\n\t\t\tv.NoParenEnd = v.Rparen\n\t\t}\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\nfunc fncallStartingLowerCase(v *ast.CallExpr) {\n\tswitch fn := v.Fun.(type) {\n\tcase *ast.SelectorExpr:\n\t\tstartWithLowerCase(fn.Sel)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\nfunc funcLitToLambdaExpr(v *ast.FuncLit, ret *ast.Expr) {\n\tnres, named := checkResult(v.Type.Results)\n\tif len(named) > 0 {\n\t\treturn\n\t}\n\tvar lsh []*ast.Ident\n\tfor _, p := range v.Type.Params.List {\n\t\tif p.Names == nil {\n\t\t\tlsh = append(lsh, ast.NewIdent(\"_\"))\n\t\t} else {\n\t\t\tlsh = append(lsh, p.Names...)\n\t\t}\n\t}\n\tif len(v.Body.List) == 1 {\n\t\tif stmt, ok := v.Body.List[0].(*ast.ReturnStmt); ok && len(stmt.Results) == nres {\n\t\t\t*ret = &ast.LambdaExpr{First: v.Pos(), Last: v.Pos(), Lhs: lsh, Rhs: stmt.Results, LhsHasParen: len(lsh) > 1, RhsHasParen: len(stmt.Results) > 1}\n\t\t\treturn\n\t\t}\n\t}\n\t*ret = &ast.LambdaExpr2{Lhs: lsh, Body: v.Body, LhsHasParen: len(lsh) > 1}\n}\n\nfunc checkResult(v *ast.FieldList) (nres int, named []*ast.Ident) {\n\tif v != nil {\n\t\tfor _, f := range v.List {\n\t\t\tif f.Names == nil {\n\t\t\t\tnres++\n\t\t\t} else {\n\t\t\t\tnres += len(f.Names)\n\t\t\t\tnamed = append(named, f.Names...)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/format/gopstyle.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage format\n\nimport (\n\t\"bytes\"\n\t\"go/types\"\n\t\"path\"\n\t\"strconv\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/format\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc GopstyleSource(src []byte, filename ...string) (ret []byte, err error) {\n\tvar fname string\n\tif filename != nil {\n\t\tfname = filename[0]\n\t}\n\tfset := token.NewFileSet()\n\tvar f *ast.File\n\tif f, err = parser.ParseFile(fset, fname, src, parser.ParseComments); err == nil {\n\t\tGopstyle(f)\n\t\tvar buf bytes.Buffer\n\t\tif err = format.Node(&buf, fset, f); err == nil {\n\t\t\tret = buf.Bytes()\n\t\t}\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\nfunc Gopstyle(file *ast.File) {\n\tif identEqual(file.Name, \"main\") {\n\t\tfile.NoPkgDecl = true\n\t}\n\tif idx, fn := findFuncDecl(file.Decls, \"main\"); idx >= 0 {\n\t\tlast := len(file.Decls) - 1\n\t\tif idx == last {\n\t\t\tfile.ShadowEntry = fn\n\t\t\t// TODO: idx != last: swap main func to last\n\t\t\t// TODO: should also swap file.Comments\n\t\t\t/*\n\t\t\t\tfn := file.Decls[idx]\n\t\t\t\tcopy(file.Decls[idx:], file.Decls[idx+1:])\n\t\t\t\tfile.Decls[last] = fn\n\t\t\t*/\n\t\t}\n\t}\n\tformatFile(file)\n}\n\nfunc findFuncDecl(decls []ast.Decl, name string) (int, *ast.FuncDecl) {\n\tfor i, decl := range decls {\n\t\tif fn, ok := decl.(*ast.FuncDecl); ok {\n\t\t\tif identEqual(fn.Name, name) {\n\t\t\t\treturn i, fn\n\t\t\t}\n\t\t}\n\t}\n\treturn -1, nil\n}\n\nfunc findDecl(decls []ast.Decl, v ast.Decl) int {\n\tfor i, decl := range decls {\n\t\tif decl == v {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc findSpec(specs []ast.Spec, v ast.Spec) int {\n\tfor i, spec := range specs {\n\t\tif spec == v {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc deleteDecl(decls []ast.Decl, v ast.Decl) []ast.Decl {\n\tif idx := findDecl(decls, v); idx >= 0 {\n\t\tdecls = append(decls[:idx], decls[idx+1:]...)\n\t}\n\treturn decls\n}\n\nfunc deleteSpec(specs []ast.Spec, v ast.Spec) []ast.Spec {\n\tif idx := findSpec(specs, v); idx >= 0 {\n\t\tspecs = append(specs[:idx], specs[idx+1:]...)\n\t}\n\treturn specs\n}\n\nfunc startWithLowerCase(v *ast.Ident) {\n\tif c := v.Name[0]; c >= 'A' && c <= 'Z' {\n\t\tv.Name = string(c+('a'-'A')) + v.Name[1:]\n\t}\n}\n\nfunc identEqual(v *ast.Ident, name string) bool {\n\treturn v != nil && v.Name == name\n}\n\nfunc toString(l *ast.BasicLit) string {\n\tif l.Kind == token.STRING {\n\t\ts, err := strconv.Unquote(l.Value)\n\t\tif err == nil {\n\t\t\treturn s\n\t\t}\n\t}\n\tpanic(\"TODO: toString - convert ast.BasicLit to string failed\")\n}\n\n// -----------------------------------------------------------------------------\n\ntype importCtx struct {\n\tpkgPath string\n\tdecl    *ast.GenDecl\n\tspec    *ast.ImportSpec\n\tisUsed  bool\n}\n\ntype formatCtx struct {\n\timports map[string]*importCtx\n\tscope   *types.Scope\n}\n\nfunc (ctx *formatCtx) insert(name string) {\n\to := types.NewParam(token.NoPos, nil, name, types.Typ[types.UntypedNil])\n\tctx.scope.Insert(o)\n}\n\nfunc (ctx *formatCtx) enterBlock() *types.Scope {\n\told := ctx.scope\n\tctx.scope = types.NewScope(old, token.NoPos, token.NoPos, \"\")\n\treturn old\n}\n\nfunc (ctx *formatCtx) leaveBlock(old *types.Scope) {\n\tctx.scope = old\n}\n\nfunc formatFile(file *ast.File) {\n\tvar funcs []*ast.FuncDecl\n\tctx := &formatCtx{\n\t\timports: make(map[string]*importCtx),\n\t\tscope:   types.NewScope(nil, token.NoPos, token.NoPos, \"\"),\n\t}\n\tfor _, decl := range file.Decls {\n\t\tswitch v := decl.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\t// delay the process, because package level vars need to be processed first.\n\t\t\tfuncs = append(funcs, v)\n\t\tcase *ast.GenDecl:\n\t\t\tswitch v.Tok {\n\t\t\tcase token.IMPORT:\n\t\t\t\tfor _, item := range v.Specs {\n\t\t\t\t\tvar spec = item.(*ast.ImportSpec)\n\t\t\t\t\tvar pkgPath = toString(spec.Path)\n\t\t\t\t\tvar name string\n\t\t\t\t\tif spec.Name == nil {\n\t\t\t\t\t\tname = path.Base(pkgPath) // TODO: open pkgPath to get pkgName\n\t\t\t\t\t} else {\n\t\t\t\t\t\tname = spec.Name.Name\n\t\t\t\t\t\tif name == \".\" || name == \"_\" {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tctx.imports[name] = &importCtx{pkgPath: pkgPath, decl: v, spec: spec}\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tformatGenDecl(ctx, v)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, fn := range funcs {\n\t\tformatFuncDecl(ctx, fn)\n\t}\n\tfor _, imp := range ctx.imports {\n\t\tif imp.pkgPath == \"fmt\" && !imp.isUsed {\n\t\t\tif len(imp.decl.Specs) == 1 {\n\t\t\t\tfile.Decls = deleteDecl(file.Decls, imp.decl)\n\t\t\t} else {\n\t\t\t\timp.decl.Specs = deleteSpec(imp.decl.Specs, imp.spec)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc formatGenDecl(ctx *formatCtx, v *ast.GenDecl) {\n\tswitch v.Tok {\n\tcase token.VAR, token.CONST:\n\t\tfor _, item := range v.Specs {\n\t\t\tspec := item.(*ast.ValueSpec)\n\t\t\tformatType(ctx, spec.Type, &spec.Type)\n\t\t\tformatExprs(ctx, spec.Values)\n\t\t\tfor _, name := range spec.Names {\n\t\t\t\tctx.insert(name.Name)\n\t\t\t}\n\t\t}\n\tcase token.TYPE:\n\t\tfor _, item := range v.Specs {\n\t\t\tspec := item.(*ast.TypeSpec)\n\t\t\tformatType(ctx, spec.Type, &spec.Type)\n\t\t}\n\t}\n}\n\nfunc formatFuncDecl(ctx *formatCtx, v *ast.FuncDecl) {\n\tformatFuncType(ctx, v.Type)\n\tformatBlockStmt(ctx, v.Body)\n}\n\n/*\nfunc fillVarCtx(ctx *formatCtx, spec *ast.ValueSpec) {\n\tfor _, name := range spec.Names {\n\t\tctx.scp.addVar(name.Name)\n\t}\n}\n\ntype scope struct {\n\tvars []map[string]bool\n}\n\nfunc newScope() *scope {\n\treturn &scope{\n\t\tvars: []map[string]bool{make(map[string]bool)},\n\t}\n}\n\nfunc (s *scope) addVar(name string) {\n\ts.vars[len(s.vars)-1][name] = true\n}\n\nfunc (s *scope) containsVar(name string) bool {\n\tfor _, m := range s.vars {\n\t\tif m[name] {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (s *scope) enterScope() {\n\ts.vars = append(s.vars, make(map[string]bool))\n}\n\nfunc (s *scope) exitScope() {\n\ts.vars = s.vars[:len(s.vars)-1]\n}\n*/\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/format/gopstyle_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage format\n\nimport (\n\t\"testing\"\n)\n\nfunc testFormat(t *testing.T, name string, src, expect string) {\n\tt.Run(name, func(t *testing.T) {\n\t\tresult, err := GopstyleSource([]byte(src), name)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"format.Source failed:\", err)\n\t\t}\n\t\tif ret := string(result); ret != expect {\n\t\t\tt.Fatalf(\"%s => Expect:\\n%s\\n=> Got:\\n%s\\n\", name, expect, ret)\n\t\t}\n\t})\n}\n\n// -----------------------------------------------------------------------------\n\nfunc TestMain(t *testing.T) {\n\ttestFormat(t, \"hello world 1\", `package main\n\nimport \"fmt\"\n\n// this is main\nfunc main() {\n\t// say hello\n\tfmt.Println(\"Hello world\")\n}\n`, `// this is main\n\n// say hello\necho \"Hello world\"\n`)\n\ttestFormat(t, \"hello world 2\", `package main\n\nimport \"fmt\"\n\n// this is main\nfunc main() {\n\t// say hello\n\tfmt.Println(\"Hello world\")\n}\n\nfunc f() {\n}\n`, `// this is main\nfunc main() {\n\t// say hello\n\techo \"Hello world\"\n}\n\nfunc f() {\n}\n`)\n\ttestFormat(t, \"hello world 3\", `package main\n\nimport \"fmt\"\n\nfunc f() {\n}\n`, `func f() {\n}\n`)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc TestPrint(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"fmt\"\n\nfunc f() {\n\tfmt.Print(\"hello\")\n}\n`, `func f() {\n\tprint \"hello\"\n}\n`)\n}\n\nfunc TestPrintf(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"fmt\"\n\nfunc f() {\n\tfmt.Printf(\"hello\")\n}\n`, `func f() {\n\tprintf \"hello\"\n}\n`)\n}\n\nfunc TestPrintln(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"fmt\"\n\nfunc f() {\n\tfmt.Println(\"hello\")\n}\n`, `func f() {\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnGroup(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc f() {\n\tfmt.Println(\"hello\")\n}\n`, `func f() {\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnWithOtherFmtCalls(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport . \"errors\"\nimport \"fmt\"\n\nfunc f() {\n\tfmt.Errorf(\"%w\", New(\"hello\"))\n\tfmt.Println(\"hello\")\n}\n`, `import . \"errors\"\n\nfunc f() {\n\terrorf \"%w\", New(\"hello\")\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnWithOtherFmtCallsGroup(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc f() {\n\tfmt.Errorf(\"%w\", errors.New(\"hello\"))\n\tfmt.println \"hello\"\n}\n`, `import (\n\t\"errors\"\n)\n\nfunc f() {\n\terrorf \"%w\", errors.new(\"hello\")\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnWithOtherFmtCallsWithAssign(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"errors\"\nimport \"fmt\"\n\nfunc f() {\n\t_ = fmt.Errorf(\"%w\", errors.New(\"hello\"))\n\tfmt.println(\"hello\")\n}\n`, `import \"errors\"\n\nfunc f() {\n\t_ = errorf(\"%w\", errors.new(\"hello\"))\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnWithOtherFmtCallsWithGroupWithAssign(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc f() {\n\t_ = fmt.Errorf(\"%w\", errors.New(\"hello\"))\n\tfmt.Println(\"hello\")\n}\n`, `import (\n\t\"errors\"\n)\n\nfunc f() {\n\t_ = errorf(\"%w\", errors.new(\"hello\"))\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnWithOtherFmtDecls(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"fmt\"\n\nfunc f() {\n\t_ = fmt.Stringer\n\tfmt.Println(\"hello\")\n}\n`, `import \"fmt\"\n\nfunc f() {\n\t_ = fmt.Stringer\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnWithOtherFmtVars(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"fmt\"\n\nfunc f() {\n\tvar _ fmt.Stringer\n\tfmt.Println(\"hello\")\n}\n`, `import \"fmt\"\n\nfunc f() {\n\tvar _ fmt.Stringer\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnWithOtherFmtType(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"fmt\"\n\nfunc f() {\n\tvar _ struct {\n\t\tfmt.Stringer\n\t\tfn func()\n\t}\n\tfmt.Println(\"hello\")\n}\n`, `import \"fmt\"\n\nfunc f() {\n\tvar _ struct {\n\t\tfmt.Stringer\n\t\tfn func()\n\t}\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnImportAlias(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport fmt1 \"fmt\"\n\nfunc f() {\n\tfmt1.Println(\"hello\")\n}\n`, `func f() {\n\techo \"hello\"\n}\n`)\n}\n\nfunc TestPrintlnImportMultiAliases(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport (\n\tfmt1 \"fmt\"\n\tfmt2 \"fmt\"\n)\n\nfunc f() {\n\tfmt1.Println(1)\n\tfmt2.Println(2)\n}\n`, `func f() {\n\techo 1\n\techo 2\n}\n`)\n}\n\nfunc TestPrintlnImportMultiAliasesDifferentGroups(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"fmt\"\n\nimport (\n\tfmt1 \"fmt\"\n\tfmt2 \"fmt\"\n)\n\nfunc f() {\n\tvar _ fmt.Stringer\n\tfmt1.Println(1)\n\tfmt2.Println(2)\n}\n`, `import \"fmt\"\n\nfunc f() {\n\tvar _ fmt.Stringer\n\techo 1\n\techo 2\n}\n`)\n}\n\nfunc TestErrorfWithPackageLevelVar(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"errors\"\nimport \"fmt\"\n\nvar _ = fmt.Errorf(\"hello %w\", errors.New(\"world\"))\n`, `import \"errors\"\n\nvar _ = errorf(\"hello %w\", errors.new(\"world\"))\n`)\n}\n\nfunc TestPrintlnWithFmtVarNoImport(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nfunc f() {\n\tvar fmt Foo\n\tfmt.Println(1)\n}\n`, `func f() {\n\tvar fmt Foo\n\tfmt.println 1\n}\n`)\n}\n\nfunc TestPrintlnWithFmtVar(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"fmt\"\n\nvar _ fmt.Stringer\n\nfunc f() {\n\tvar fmt Foo\n\tfmt.Println(1)\n}\n`, `import \"fmt\"\n\nvar _ fmt.Stringer\n\nfunc f() {\n\tvar fmt Foo\n\tfmt.println 1\n}\n`)\n}\n\n// todo: fix inner scope vars\n// func TestPrintlnWithScopedFmtVar(t *testing.T) {\n// \ttestFormat(t, \"print\", `package main\n//\n// import \"fmt\"\n//\n// func f() {\n// \t{\n// \t\tvar fmt Foo\n// \t\t_ = fmt\n// \t}\n// \tfmt.Println(1)\n// }\n// `, `func f() {\n// \t{\n// \t\tvar fmt Foo\n// \t\t_ = fmt\n// \t}\n// \tprintln 1\n// }\n// `)\n// }\n\nfunc TestPrintlnWithFmtVarAfter(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nimport \"fmt\"\n\nfunc f() {\n\tfmt.Println(1)\n\tvar fmt Foo\n\t_ = fmt\n}\n`, `func f() {\n\techo 1\n\tvar fmt Foo\n\t_ = fmt\n}\n`)\n}\n\nfunc TestPrintlnWithPackageLevelFmtVar(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nvar fmt Foo\n\nfunc f() {\n\tfmt.Println(1)\n}\n`, `var fmt Foo\n\nfunc f() {\n\tfmt.println 1\n}\n`)\n}\n\nfunc TestPrintlnWithPackageLevelFmtVarAfter(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nfunc f() {\n\tfmt.Println(1)\n}\n\nvar fmt Foo\n`, `func f() {\n\tfmt.println 1\n}\n\nvar fmt Foo\n`)\n}\n\nfunc TestPrintlnWithVarFromCall(t *testing.T) {\n\ttestFormat(t, \"print\", `package main\n\nfunc f() {\n\tvar fmt = Foo()\n\tfmt.Println(1)\n}\n`, `func f() {\n\tvar fmt = Foo()\n\tfmt.println 1\n}\n`)\n}\n\nfunc TestLambdaFromFuncLit(t *testing.T) {\n\ttestFormat(t, \"funclit to lambda\", `package main\nprintln(demo(func(n int) int {\n\treturn n+100\n}))\nprintln(demo(func(n int) int {\n\treturn n+100\n}),200)\ndemo1(100, func(n int) {\n\tprintln(n)\n})\ndemo1(100, func(int) {\n\tprintln(100)\n})\ndemo2(300, func(n1, n2 int) int {\n\treturn(n1 + n2)\n})\ndemo2(300, func(int, int) int {\n\treturn -600\n})\ndemo3(100,func(n1,n2 int)(int) {\n\treturn n1+n2,n1-n2\n},100)\ndemo3(100,func(n1,n2 int)(v int) {\n\treturn n1+n2,n1-n2\n},100)\ndemo4(100,func(n1,n2 int)(a,b int) {\n\tprintln(a,b)\n\treturn n1+n2,n1-n2\n},100)\n`, `println demo(n => n + 100)\nprintln demo(n => n + 100), 200\ndemo1 100, n => {\n\tprintln n\n}\ndemo1 100, _ => {\n\tprintln 100\n}\ndemo2 300, (n1, n2) => (n1 + n2)\n\ndemo2 300, (_, _) => -600\n\ndemo3 100, (n1, n2) => {\n\treturn n1 + n2, n1 - n2\n}, 100\ndemo3 100, func(n1, n2 int) (v int) {\n\treturn n1 + n2, n1 - n2\n}, 100\ndemo4 100, func(n1, n2 int) (a, b int) {\n\tprintln a, b\n\treturn n1 + n2, n1 - n2\n}, 100\n`)\n}\n"
  },
  {
    "path": "x/format/gopstyledir_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage format\n\nimport (\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc TestFromTestdata(t *testing.T) {\n\tsel := \"\"\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatal(\"Getwd failed:\", err)\n\t}\n\tdir = path.Join(dir, \"./_testdata\")\n\tfis, err := ioutil.ReadDir(dir)\n\tif err != nil {\n\t\tt.Fatal(\"ReadDir failed:\", err)\n\t}\n\tfor _, fi := range fis {\n\t\tname := fi.Name()\n\t\tif strings.HasPrefix(name, \"_\") {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttestFrom(t, dir+\"/\"+name, sel)\n\t\t})\n\t}\n}\n\nfunc testFrom(t *testing.T, pkgDir, sel string) {\n\tif sel != \"\" && !strings.Contains(pkgDir, sel) {\n\t\treturn\n\t}\n\tlog.Println(\"Formatting\", pkgDir)\n\tfile := pkgDir + \"/index.xgo\"\n\tsrc, err := os.ReadFile(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tret, err := GopstyleSource(src, file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\texpect, err := os.ReadFile(pkgDir + \"/format.expect\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdiffBytes(t, pkgDir+\"/format.result\", ret, expect)\n}\n\nfunc diffBytes(t *testing.T, outfile string, dst, src []byte) {\n\tline := 1\n\toffs := 0 // line offset\n\tfor i := 0; i < len(dst) && i < len(src); i++ {\n\t\td := dst[i]\n\t\ts := src[i]\n\t\tif d != s {\n\t\t\tos.WriteFile(outfile, dst, 0644)\n\t\t\tt.Errorf(\"dst:%d: %s\\n\", line, dst[offs:])\n\t\t\tt.Errorf(\"src:%d: %s\\n\", line, src[offs:])\n\t\t\treturn\n\t\t}\n\t\tif s == '\\n' {\n\t\t\tline++\n\t\t\toffs = i + 1\n\t\t}\n\t}\n\tif len(dst) != len(src) {\n\t\tt.Errorf(\"len(dst) = %d, len(src) = %d\\ndst = %q\\nsrc = %q\", len(dst), len(src), dst, src)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/format/stmt_expr_or_type.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage format\n\nimport (\n\t\"go/token\"\n\t\"log\"\n\t\"reflect\"\n\n\t\"github.com/goplus/xgo/ast\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc formatType(ctx *formatCtx, typ ast.Expr, ref *ast.Expr) {\n\tswitch t := typ.(type) {\n\tcase *ast.Ident, nil:\n\tcase *ast.SelectorExpr:\n\t\tformatSelectorExpr(ctx, t, ref)\n\tcase *ast.StarExpr:\n\t\tformatType(ctx, t.X, &t.X)\n\tcase *ast.MapType:\n\t\tformatType(ctx, t.Key, &t.Key)\n\t\tformatType(ctx, t.Value, &t.Value)\n\tcase *ast.StructType:\n\t\tformatFields(ctx, t.Fields)\n\tcase *ast.ArrayType:\n\t\tformatExpr(ctx, t.Len, &t.Len)\n\t\tformatType(ctx, t.Elt, &t.Elt)\n\tcase *ast.ChanType:\n\t\tformatType(ctx, t.Value, &t.Value)\n\tcase *ast.InterfaceType:\n\t\tformatFields(ctx, t.Methods)\n\tcase *ast.FuncType:\n\t\tformatFuncType(ctx, t)\n\tcase *ast.Ellipsis:\n\t\tformatType(ctx, t.Elt, &t.Elt)\n\tdefault:\n\t\tlog.Panicln(\"TODO: format -\", reflect.TypeOf(typ))\n\t}\n}\n\nfunc formatFuncType(ctx *formatCtx, t *ast.FuncType) {\n\tformatFields(ctx, t.Params)\n\tformatFields(ctx, t.Results)\n}\n\nfunc formatFields(ctx *formatCtx, flds *ast.FieldList) {\n\tif flds != nil {\n\t\tfor _, fld := range flds.List {\n\t\t\tformatField(ctx, fld)\n\t\t}\n\t}\n}\n\nfunc formatField(ctx *formatCtx, fld *ast.Field) {\n\tformatType(ctx, fld.Type, &fld.Type)\n}\n\n// -----------------------------------------------------------------------------\n\nfunc formatExprs(ctx *formatCtx, exprs []ast.Expr) {\n\tfor i, expr := range exprs {\n\t\tformatExpr(ctx, expr, &exprs[i])\n\t}\n}\n\nfunc formatExpr(ctx *formatCtx, expr ast.Expr, ref *ast.Expr) {\n\tswitch v := expr.(type) {\n\tcase *ast.Ident, *ast.BasicLit, *ast.BadExpr, nil:\n\tcase *ast.BinaryExpr:\n\t\tformatExpr(ctx, v.X, &v.X)\n\t\tformatExpr(ctx, v.Y, &v.Y)\n\tcase *ast.UnaryExpr:\n\t\tformatExpr(ctx, v.X, &v.X)\n\tcase *ast.CallExpr:\n\t\tformatCallExpr(ctx, v)\n\tcase *ast.SelectorExpr:\n\t\tformatSelectorExpr(ctx, v, ref)\n\tcase *ast.SliceExpr:\n\t\tformatSliceExpr(ctx, v)\n\tcase *ast.IndexExpr:\n\t\tformatExpr(ctx, v.X, &v.X)\n\t\tformatExpr(ctx, v.Index, &v.Index)\n\tcase *ast.SliceLit:\n\t\tformatExprs(ctx, v.Elts)\n\tcase *ast.CompositeLit:\n\t\tformatType(ctx, v.Type, &v.Type)\n\t\tformatExprs(ctx, v.Elts)\n\tcase *ast.StarExpr:\n\t\tformatExpr(ctx, v.X, &v.X)\n\tcase *ast.KeyValueExpr:\n\t\tformatExpr(ctx, v.Key, &v.Key)\n\t\tformatExpr(ctx, v.Value, &v.Value)\n\tcase *ast.FuncLit:\n\t\tformatFuncType(ctx, v.Type)\n\t\tformatBlockStmt(ctx, v.Body)\n\tcase *ast.TypeAssertExpr:\n\t\tformatExpr(ctx, v.X, &v.X)\n\t\tformatType(ctx, v.Type, &v.Type)\n\tcase *ast.LambdaExpr:\n\t\tformatExprs(ctx, v.Rhs)\n\tcase *ast.LambdaExpr2:\n\t\tformatBlockStmt(ctx, v.Body)\n\tcase *ast.RangeExpr:\n\t\tformatRangeExpr(ctx, v)\n\tcase *ast.ComprehensionExpr:\n\t\tformatComprehensionExpr(ctx, v)\n\tcase *ast.ErrWrapExpr:\n\t\tformatExpr(ctx, v.X, &v.X)\n\t\tformatExpr(ctx, v.Default, &v.Default)\n\tcase *ast.ParenExpr:\n\t\tformatExpr(ctx, v.X, &v.X)\n\tcase *ast.Ellipsis:\n\t\tformatExpr(ctx, v.Elt, &v.Elt)\n\tdefault:\n\t\tformatType(ctx, expr, ref)\n\t}\n}\n\nfunc formatRangeExpr(ctx *formatCtx, v *ast.RangeExpr) {\n\tformatExpr(ctx, v.First, &v.First)\n\tformatExpr(ctx, v.Last, &v.Last)\n\tformatExpr(ctx, v.Expr3, &v.Expr3)\n}\n\nfunc formatComprehensionExpr(ctx *formatCtx, v *ast.ComprehensionExpr) {\n\told := ctx.enterBlock()\n\tdefer ctx.leaveBlock(old)\n\n\tformatForPhrases(ctx, v.Fors)\n\tformatExpr(ctx, v.Elt, &v.Elt)\n}\n\nfunc formatForPhrases(ctx *formatCtx, fors []*ast.ForPhrase) {\n\tfor _, f := range fors {\n\t\tformatForPhrase(ctx, f)\n\t}\n}\n\nfunc formatForPhrase(ctx *formatCtx, v *ast.ForPhrase) {\n\tformatExpr(ctx, v.X, &v.X)\n\tformatStmt(ctx, v.Init)\n\tformatExpr(ctx, v.Cond, &v.Cond)\n}\n\nfunc formatSliceExpr(ctx *formatCtx, v *ast.SliceExpr) {\n\tformatExpr(ctx, v.X, &v.X)\n\tformatExpr(ctx, v.Low, &v.Low)\n\tformatExpr(ctx, v.High, &v.High)\n\tformatExpr(ctx, v.Max, &v.Max)\n}\n\nfunc formatCallExpr(ctx *formatCtx, v *ast.CallExpr) {\n\tformatExpr(ctx, v.Fun, &v.Fun)\n\tfncallStartingLowerCase(v)\n\tfor i, arg := range v.Args {\n\t\tif fn, ok := arg.(*ast.FuncLit); ok {\n\t\t\tfuncLitToLambdaExpr(fn, &v.Args[i])\n\t\t}\n\t}\n\tformatExprs(ctx, v.Args)\n}\n\nfunc formatSelectorExpr(ctx *formatCtx, v *ast.SelectorExpr, ref *ast.Expr) {\n\tswitch x := v.X.(type) {\n\tcase *ast.Ident:\n\t\tif _, o := ctx.scope.LookupParent(x.Name, token.NoPos); o != nil {\n\t\t\tbreak\n\t\t}\n\t\tif imp, ok := ctx.imports[x.Name]; ok {\n\t\t\tif !fmtToBuiltin(imp, v.Sel, ref) {\n\t\t\t\timp.isUsed = true\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tformatExpr(ctx, x, &v.X)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\nfunc formatBlockStmt(ctx *formatCtx, stmt *ast.BlockStmt) {\n\tif stmt != nil {\n\t\told := ctx.enterBlock()\n\t\tdefer ctx.leaveBlock(old)\n\t\tformatStmts(ctx, stmt.List)\n\t}\n}\n\nfunc formatStmts(ctx *formatCtx, stmts []ast.Stmt) {\n\tfor _, stmt := range stmts {\n\t\tformatStmt(ctx, stmt)\n\t}\n}\n\nfunc formatStmt(ctx *formatCtx, stmt ast.Stmt) {\n\tswitch v := stmt.(type) {\n\tcase *ast.ExprStmt:\n\t\tformatExprStmt(ctx, v)\n\tcase *ast.AssignStmt:\n\t\tformatAssignStmt(ctx, v)\n\tcase *ast.IncDecStmt:\n\t\tformatExpr(ctx, v.X, &v.X)\n\tcase *ast.ForStmt:\n\t\tformatForStmt(ctx, v)\n\tcase *ast.RangeStmt:\n\t\tformatRangeStmt(ctx, v)\n\tcase *ast.ForPhraseStmt:\n\t\tformatForPhraseStmt(ctx, v)\n\tcase *ast.IfStmt:\n\t\tformatIfStmt(ctx, v)\n\tcase *ast.CaseClause:\n\t\tformatExprs(ctx, v.List)\n\t\tformatStmts(ctx, v.Body)\n\tcase *ast.SwitchStmt:\n\t\tformatSwitchStmt(ctx, v)\n\tcase *ast.TypeSwitchStmt:\n\t\tformatTypeSwitchStmt(ctx, v)\n\tcase *ast.CommClause:\n\t\tformatStmt(ctx, v.Comm)\n\t\tformatStmts(ctx, v.Body)\n\tcase *ast.SelectStmt:\n\t\tformatBlockStmt(ctx, v.Body)\n\tcase *ast.DeclStmt:\n\t\tformatDeclStmt(ctx, v)\n\tcase *ast.ReturnStmt:\n\t\tformatExprs(ctx, v.Results)\n\tcase *ast.BlockStmt:\n\t\tformatBlockStmt(ctx, v)\n\tcase *ast.DeferStmt:\n\t\tformatCallExpr(ctx, v.Call)\n\tcase *ast.GoStmt:\n\t\tformatCallExpr(ctx, v.Call)\n\tcase *ast.SendStmt:\n\t\tformatExpr(ctx, v.Chan, &v.Chan)\n\t\tfor i, val := range v.Values {\n\t\t\tformatExpr(ctx, val, &v.Values[i])\n\t\t}\n\tcase *ast.LabeledStmt:\n\t\tformatStmt(ctx, v.Stmt)\n\tcase *ast.BranchStmt, *ast.EmptyStmt, nil, *ast.BadStmt:\n\tdefault:\n\t\tlog.Panicln(\"TODO: formatStmt -\", reflect.TypeOf(stmt))\n\t}\n}\n\nfunc formatExprStmt(ctx *formatCtx, v *ast.ExprStmt) {\n\tswitch x := v.X.(type) {\n\tcase *ast.CallExpr:\n\t\tcommandStyleFirst(x)\n\t}\n\tformatExpr(ctx, v.X, &v.X)\n}\n\nfunc formatAssignStmt(ctx *formatCtx, v *ast.AssignStmt) {\n\tformatExprs(ctx, v.Lhs)\n\tformatExprs(ctx, v.Rhs)\n}\n\nfunc formatSwitchStmt(ctx *formatCtx, v *ast.SwitchStmt) {\n\told := ctx.enterBlock()\n\tdefer ctx.leaveBlock(old)\n\n\tformatStmt(ctx, v.Init)\n\tformatExpr(ctx, v.Tag, &v.Tag)\n\tformatBlockStmt(ctx, v.Body)\n}\n\nfunc formatTypeSwitchStmt(ctx *formatCtx, v *ast.TypeSwitchStmt) {\n\told := ctx.enterBlock()\n\tdefer ctx.leaveBlock(old)\n\n\tformatStmt(ctx, v.Init)\n\tformatStmt(ctx, v.Assign)\n\tformatBlockStmt(ctx, v.Body)\n}\n\nfunc formatIfStmt(ctx *formatCtx, v *ast.IfStmt) {\n\told := ctx.enterBlock()\n\tdefer ctx.leaveBlock(old)\n\n\tformatStmt(ctx, v.Init)\n\tformatExpr(ctx, v.Cond, &v.Cond)\n\tformatBlockStmt(ctx, v.Body)\n\tformatStmt(ctx, v.Else)\n}\n\nfunc formatRangeStmt(ctx *formatCtx, v *ast.RangeStmt) {\n\told := ctx.enterBlock()\n\tdefer ctx.leaveBlock(old)\n\n\tformatExpr(ctx, v.Key, &v.Key)\n\tformatExpr(ctx, v.Value, &v.Value)\n\tformatExpr(ctx, v.X, &v.X)\n\tformatBlockStmt(ctx, v.Body)\n}\n\nfunc formatForPhraseStmt(ctx *formatCtx, v *ast.ForPhraseStmt) {\n\told := ctx.enterBlock()\n\tdefer ctx.leaveBlock(old)\n\n\tformatForPhrase(ctx, v.ForPhrase)\n\tformatBlockStmt(ctx, v.Body)\n}\n\nfunc formatForStmt(ctx *formatCtx, v *ast.ForStmt) {\n\told := ctx.enterBlock()\n\tdefer ctx.leaveBlock(old)\n\n\tformatStmt(ctx, v.Init)\n\tformatExpr(ctx, v.Cond, &v.Cond)\n\tformatBlockStmt(ctx, v.Body)\n}\n\nfunc formatDeclStmt(ctx *formatCtx, v *ast.DeclStmt) {\n\tif decl, ok := v.Decl.(*ast.GenDecl); ok {\n\t\tformatGenDecl(ctx, decl)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/fsnotify/fsnotify.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage fsnotify\n\nimport (\n\t\"errors\"\n\t\"io/fs\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/fsnotify/fsnotify\"\n)\n\nvar (\n\tdebugEvent bool\n)\n\nconst (\n\tDbgFlagEvent = 1 << iota\n\tDbgFlagAll   = DbgFlagEvent\n)\n\nfunc SetDebug(dbgFlags int) {\n\tdebugEvent = (dbgFlags & DbgFlagEvent) != 0\n}\n\n// -----------------------------------------------------------------------------------------\n\ntype FSChanged interface {\n\tFileChanged(name string)\n\tDirAdded(name string)\n\tEntryDeleted(name string, isDir bool)\n}\n\ntype Ignore = func(name string, isDir bool) bool\n\n// -----------------------------------------------------------------------------------------\n\ntype Watcher struct {\n\tw *fsnotify.Watcher\n}\n\nfunc New() Watcher {\n\tw, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\tlog.Panicln(\"[FATAL] fsnotify.NewWatcher:\", err)\n\t}\n\treturn Watcher{w}\n}\n\nfunc (p Watcher) Run(root string, fc FSChanged, ignore Ignore) error {\n\tgo p.watchLoop(root, fc, ignore)\n\treturn watchRecursive(p.w, root)\n}\n\nfunc (p Watcher) watchLoop(root string, fc FSChanged, ignore Ignore) {\n\tconst (\n\t\teventModify = fsnotify.Write | fsnotify.Create\n\t)\n\tfor {\n\t\tselect {\n\t\tcase event, ok := <-p.w.Events:\n\t\t\tif !ok { // closed\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif debugEvent {\n\t\t\t\tlog.Println(\"==> event:\", event)\n\t\t\t}\n\t\t\tif (event.Op & fsnotify.Remove) != 0 {\n\t\t\t\te := p.w.Remove(event.Name)\n\t\t\t\tname, err := filepath.Rel(root, event.Name)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Println(\"[ERROR] fsnotify.EntryDeleted filepath.Rel:\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tisDir := (e == nil || !errors.Is(e, fsnotify.ErrNonExistentWatch))\n\t\t\t\tname = filepath.ToSlash(name)\n\t\t\t\tif ignore != nil && ignore(name, isDir) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tfc.EntryDeleted(name, isDir)\n\t\t\t\t}\n\t\t\t} else if (event.Op & eventModify) != 0 {\n\t\t\t\tname, err := filepath.Rel(root, event.Name)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Println(\"[ERROR] fsnotify.FileChanged filepath.Rel:\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tisDir := isDir(event.Name)\n\t\t\t\tname = filepath.ToSlash(name)\n\t\t\t\tif ignore != nil && ignore(name, isDir) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else if isDir {\n\t\t\t\t\tif (event.Op & fsnotify.Create) != 0 {\n\t\t\t\t\t\twatchRecursive(p.w, event.Name)\n\t\t\t\t\t\tfc.DirAdded(name)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfc.FileChanged(name)\n\t\t\t\t}\n\t\t\t}\n\t\tcase err, ok := <-p.w.Errors:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Println(\"[ERROR] fsnotify Errors:\", err)\n\t\t}\n\t}\n}\n\nfunc (p *Watcher) Close() error {\n\tif w := p.w; w != nil {\n\t\tp.w = nil\n\t\treturn w.Close()\n\t}\n\treturn nil\n}\n\nfunc watchRecursive(w *fsnotify.Watcher, path string) error {\n\terr := filepath.WalkDir(path, func(walkPath string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif d.IsDir() {\n\t\t\tif err = w.Add(walkPath); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\treturn err\n}\n\nfunc isDir(name string) bool {\n\tif fs, err := os.Lstat(name); err == nil {\n\t\treturn fs.IsDir()\n\t}\n\treturn false\n}\n\n// -----------------------------------------------------------------------------------------\n"
  },
  {
    "path": "x/gocmd/build_install.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage gocmd\n\n// -----------------------------------------------------------------------------\n\ntype InstallConfig = Config\n\nfunc Install(arg string, conf *InstallConfig) (err error) {\n\treturn doWithArgs(\"\", \"install\", conf, arg)\n}\n\nfunc InstallFiles(files []string, conf *InstallConfig) (err error) {\n\treturn doWithArgs(\"\", \"install\", conf, files...)\n}\n\n// -----------------------------------------------------------------------------\n\ntype BuildConfig = Config\n\nfunc Build(arg string, conf *BuildConfig) (err error) {\n\treturn doWithArgs(\"\", \"build\", conf, arg)\n}\n\nfunc BuildFiles(files []string, conf *BuildConfig) (err error) {\n\treturn doWithArgs(\"\", \"build\", conf, files...)\n}\n\n// -----------------------------------------------------------------------------\n\ntype TestConfig = Config\n\nfunc Test(arg string, conf *TestConfig) (err error) {\n\treturn doWithArgs(\"\", \"test\", conf, arg)\n}\n\nfunc TestFiles(files []string, conf *TestConfig) (err error) {\n\treturn doWithArgs(\"\", \"test\", conf, files...)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/gocmd/gocmd.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage gocmd\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\n\t\"github.com/goplus/mod/env\"\n\t\"github.com/goplus/xgo/x/xgoenv\"\n)\n\ntype XGoEnv = env.XGo\n\ntype Config struct {\n\tXGo   *XGoEnv\n\tGoCmd string\n\tFlags []string\n\tRun   func(cmd *exec.Cmd) error\n}\n\n// -----------------------------------------------------------------------------\n\nfunc doWithArgs(dir, op string, conf *Config, args ...string) (err error) {\n\tif conf == nil {\n\t\tconf = new(Config)\n\t}\n\tgoCmd := conf.GoCmd\n\tif goCmd == \"\" {\n\t\tgoCmd = Name()\n\t}\n\texargs := make([]string, 1, 16)\n\texargs[0] = op\n\texargs = appendLdflags(exargs, conf.XGo)\n\texargs = append(exargs, conf.Flags...)\n\texargs = append(exargs, args...)\n\tcmd := exec.Command(goCmd, exargs...)\n\tcmd.Dir = dir\n\trun := conf.Run\n\tif run == nil {\n\t\trun = runCmd\n\t}\n\treturn run(cmd)\n}\n\nfunc runCmd(cmd *exec.Cmd) (err error) {\n\tcmd.Stdin = os.Stdin\n\tcmd.Stderr = os.Stderr\n\tcmd.Stdout = os.Stdout\n\treturn cmd.Run()\n}\n\n// -----------------------------------------------------------------------------\n\nconst (\n\tldFlagVersion   = \"-X \\\"github.com/goplus/xgo/env.buildVersion=%s\\\"\"\n\tldFlagBuildDate = \"-X \\\"github.com/goplus/xgo/env.buildDate=%s\\\"\"\n\tldFlagGopRoot   = \"-X \\\"github.com/goplus/xgo/env.defaultXGoRoot=%s\\\"\"\n)\n\nconst (\n\tldFlagAll = ldFlagVersion + \" \" + ldFlagBuildDate + \" \" + ldFlagGopRoot\n)\n\nfunc loadFlags(env *XGoEnv) string {\n\treturn fmt.Sprintf(ldFlagAll, env.Version, env.BuildDate, env.Root)\n}\n\nfunc appendLdflags(exargs []string, env *XGoEnv) []string {\n\tif env == nil {\n\t\tenv = xgoenv.Get()\n\t}\n\treturn append(exargs, \"-ldflags\", loadFlags(env))\n}\n\n// -----------------------------------------------------------------------------\n\n// Name returns name of the go command.\n// It returns value of environment variable `XGO_GOCMD` if not empty.\n// If not found, it returns `go`.\nfunc Name() string {\n\tgoCmd := os.Getenv(\"XGO_GOCMD\")\n\tif goCmd == \"\" {\n\t\tgoCmd = \"go\"\n\t}\n\treturn goCmd\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/gocmd/run.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage gocmd\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"syscall\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype RunConfig = Config\n\n// RunDir runs a Go project by specified directory.\n// If buildDir is not empty, it means split `go run` into `go build`\n// in buildDir and run the built app in current directory.\nfunc RunDir(buildDir, dir string, args []string, conf *RunConfig) (err error) {\n\tfis, err := os.ReadDir(dir)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar files []string\n\tfor _, fi := range fis {\n\t\tif !fi.IsDir() {\n\t\t\tif fname := fi.Name(); filterRunFname(fname) {\n\t\t\t\tfiles = append(files, filepath.Join(dir, fname))\n\t\t\t}\n\t\t}\n\t}\n\treturn RunFiles(buildDir, files, args, conf)\n}\n\nfunc filterRunFname(fname string) bool {\n\treturn strings.HasSuffix(fname, \".go\") &&\n\t\t!(strings.HasSuffix(fname, \"_test.go\") || strings.HasPrefix(fname, \"_\"))\n}\n\n// -----------------------------------------------------------------------------\n\n// RunFiles runs a Go project by specified files.\n// If buildDir is not empty, it means split `go run` into `go build`\n// in buildDir and run the built app in current directory.\nfunc RunFiles(buildDir string, files []string, args []string, conf *RunConfig) (err error) {\n\tif len(files) == 0 {\n\t\treturn syscall.ENOENT\n\t}\n\tif buildDir == \"\" {\n\t\targs = append(files, args...)\n\t\treturn doWithArgs(\"\", \"run\", conf, args...)\n\t}\n\n\tabsFiles := make([]string, len(files))\n\tfor i, file := range files {\n\t\tabsFiles[i], _ = filepath.Abs(file)\n\t}\n\n\tf, err := os.CreateTemp(\"\", \"gobuild\")\n\tif err != nil {\n\t\treturn\n\t}\n\ttempf := f.Name()\n\tf.Close()\n\tos.Remove(tempf)\n\tdefer os.Remove(tempf)\n\n\tbuildArgs := append([]string{\"-o\", tempf}, absFiles...)\n\tif err = doWithArgs(buildDir, \"build\", conf, buildArgs...); err != nil {\n\t\treturn\n\t}\n\n\tcmd := exec.Command(tempf, args...)\n\treturn runCmd(cmd)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/jsonrpc2/conn.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage jsonrpc2\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\n// Binder builds a connection configuration.\n// This may be used in servers to generate a new configuration per connection.\n// ConnectionOptions itself implements Binder returning itself unmodified, to\n// allow for the simple cases where no per connection information is needed.\ntype Binder interface {\n\t// Bind returns the ConnectionOptions to use when establishing the passed-in\n\t// Connection.\n\t//\n\t// The connection is not ready to use when Bind is called,\n\t// but Bind may close it without reading or writing to it.\n\tBind(context.Context, *Connection) ConnectionOptions\n}\n\n// A BinderFunc implements the Binder interface for a standalone Bind function.\ntype BinderFunc func(context.Context, *Connection) ConnectionOptions\n\nfunc (f BinderFunc) Bind(ctx context.Context, c *Connection) ConnectionOptions {\n\treturn f(ctx, c)\n}\n\nvar _ Binder = BinderFunc(nil)\n\n// ConnectionOptions holds the options for new connections.\ntype ConnectionOptions struct {\n\t// Framer allows control over the message framing and encoding.\n\t// If nil, HeaderFramer will be used.\n\tFramer Framer\n\t// Preempter allows registration of a pre-queue message handler.\n\t// If nil, no messages will be preempted.\n\tPreempter Preempter\n\t// Handler is used as the queued message handler for inbound messages.\n\t// If nil, all responses will be ErrNotHandled.\n\tHandler Handler\n\t// OnInternalError, if non-nil, is called with any internal errors that occur\n\t// while serving the connection, such as protocol errors or invariant\n\t// violations. (If nil, internal errors result in panics.)\n\tOnInternalError func(error)\n}\n\n// Connection manages the jsonrpc2 protocol, connecting responses back to their\n// calls.\n// Connection is bidirectional; it does not have a designated server or client\n// end.\ntype Connection struct {\n\tseq int64 // must only be accessed using atomic operations\n\n\tstateMu sync.Mutex\n\tstate   inFlightState // accessed only in updateInFlight\n\tdone    chan struct{} // closed (under stateMu) when state.closed is true and all goroutines have completed\n\n\twriter chan Writer // 1-buffered; stores the writer when not in use\n\n\thandler Handler\n\n\tonInternalError func(error)\n\tonDone          func()\n}\n\n// inFlightState records the state of the incoming and outgoing calls on a\n// Connection.\ntype inFlightState struct {\n\tconnClosing bool  // true when the Connection's Close method has been called\n\treading     bool  // true while the readIncoming goroutine is running\n\treadErr     error // non-nil when the readIncoming goroutine exits (typically io.EOF)\n\twriteErr    error // non-nil if a call to the Writer has failed with a non-canceled Context\n\n\t// closer shuts down and cleans up the Reader and Writer state, ideally\n\t// interrupting any Read or Write call that is currently blocked. It is closed\n\t// when the state is idle and one of: connClosing is true, readErr is non-nil,\n\t// or writeErr is non-nil.\n\t//\n\t// After the closer has been invoked, the closer field is set to nil\n\t// and the closeErr field is simultaneously set to its result.\n\tcloser   io.Closer\n\tcloseErr error // error returned from closer.Close\n\n\toutgoingCalls         map[ID]*AsyncCall // calls only\n\toutgoingNotifications int               // # of notifications awaiting \"write\"\n\n\t// incoming stores the total number of incoming calls and notifications\n\t// that have not yet written or processed a result.\n\tincoming int\n\n\tincomingByID map[ID]*incomingRequest // calls only\n\n\t// handlerQueue stores the backlog of calls and notifications that were not\n\t// already handled by a preempter.\n\t// The queue does not include the request currently being handled (if any).\n\thandlerQueue   []*incomingRequest\n\thandlerRunning bool\n}\n\n// updateInFlight locks the state of the connection's in-flight requests, allows\n// f to mutate that state, and closes the connection if it is idle and either\n// is closing or has a read or write error.\nfunc (c *Connection) updateInFlight(f func(*inFlightState)) {\n\tc.stateMu.Lock()\n\tdefer c.stateMu.Unlock()\n\n\ts := &c.state\n\n\tf(s)\n\n\tselect {\n\tcase <-c.done:\n\t\t// The connection was already completely done at the start of this call to\n\t\t// updateInFlight, so it must remain so. (The call to f should have noticed\n\t\t// that and avoided making any updates that would cause the state to be\n\t\t// non-idle.)\n\t\tif !s.idle() {\n\t\t\tpanic(\"jsonrpc2_v2: updateInFlight transitioned to non-idle when already done\")\n\t\t}\n\t\treturn\n\tdefault:\n\t}\n\n\tif s.idle() && s.shuttingDown(ErrUnknown) != nil {\n\t\tif Verbose {\n\t\t\tlog.Println(\"==> Connection.updateInFlight: shuttingDown\")\n\t\t}\n\t\tif s.closer != nil {\n\t\t\ts.closeErr = s.closer.Close()\n\t\t\ts.closer = nil // prevent duplicate Close calls\n\t\t}\n\t\tif Verbose {\n\t\t\tlog.Println(\"==> Connection.updateInFlight: s.reading -\", s.reading)\n\t\t}\n\t\tif s.reading {\n\t\t\t// The readIncoming goroutine is still running. Our call to Close should\n\t\t\t// cause it to exit soon, at which point it will make another call to\n\t\t\t// updateInFlight, set s.reading to false, and mark the Connection done.\n\t\t} else {\n\t\t\t// The readIncoming goroutine has exited, or never started to begin with.\n\t\t\t// Since everything else is idle, we're completely done.\n\t\t\tif c.onDone != nil {\n\t\t\t\tc.onDone()\n\t\t\t}\n\t\t\tclose(c.done)\n\t\t}\n\t}\n}\n\n// idle reports whether the connection is in a state with no pending calls or\n// notifications.\n//\n// If idle returns true, the readIncoming goroutine may still be running,\n// but no other goroutines are doing work on behalf of the connection.\nfunc (s *inFlightState) idle() bool {\n\treturn len(s.outgoingCalls) == 0 && s.outgoingNotifications == 0 && s.incoming == 0 && !s.handlerRunning\n}\n\n// shuttingDown reports whether the connection is in a state that should\n// disallow new (incoming and outgoing) calls. It returns either nil or\n// an error that is or wraps the provided errClosing.\nfunc (s *inFlightState) shuttingDown(errClosing error) error {\n\tif s.connClosing {\n\t\t// If Close has been called explicitly, it doesn't matter what state the\n\t\t// Reader and Writer are in: we shouldn't be starting new work because the\n\t\t// caller told us not to start new work.\n\t\treturn errClosing\n\t}\n\tif s.readErr != nil {\n\t\t// If the read side of the connection is broken, we cannot read new call\n\t\t// requests, and cannot read responses to our outgoing calls.\n\t\treturn fmt.Errorf(\"%w: %v\", errClosing, s.readErr)\n\t}\n\tif s.writeErr != nil {\n\t\t// If the write side of the connection is broken, we cannot write responses\n\t\t// for incoming calls, and cannot write requests for outgoing calls.\n\t\treturn fmt.Errorf(\"%w: %v\", errClosing, s.writeErr)\n\t}\n\treturn nil\n}\n\n// incomingRequest is used to track an incoming request as it is being handled\ntype incomingRequest struct {\n\t*Request // the request being processed\n\tctx      context.Context\n\tcancel   context.CancelFunc\n}\n\n// newConnection creates a new connection and runs it.\n//\n// This is used by the Dial and Serve functions to build the actual connection.\n//\n// The connection is closed automatically (and its resources cleaned up) when\n// the last request has completed after the underlying ReadWriteCloser breaks,\n// but it may be stopped earlier by calling Close (for a clean shutdown).\nfunc newConnection(bindCtx context.Context, rwc io.ReadWriteCloser, binder Binder, onDone func()) *Connection {\n\t// TODO: Should we create a new event span here?\n\t// This will propagate cancellation from ctx; should it?\n\tctx := notDone{bindCtx}\n\n\tc := &Connection{\n\t\tstate:  inFlightState{closer: rwc},\n\t\tdone:   make(chan struct{}),\n\t\twriter: make(chan Writer, 1),\n\t\tonDone: onDone,\n\t}\n\t// It's tempting to set a finalizer on c to verify that the state has gone\n\t// idle when the connection becomes unreachable. Unfortunately, the Binder\n\t// interface makes that unsafe: it allows the Handler to close over the\n\t// Connection, which could create a reference cycle that would cause the\n\t// Connection to become uncollectable.\n\n\toptions := binder.Bind(bindCtx, c)\n\tframer := options.Framer\n\tif framer == nil {\n\t\tframer = HeaderFramer()\n\t}\n\tc.handler = options.Handler\n\tif c.handler == nil {\n\t\tc.handler = defaultHandler{}\n\t}\n\tc.onInternalError = options.OnInternalError\n\tif c.onInternalError == nil {\n\t\tc.onInternalError = defaultHandleError\n\t}\n\n\tc.writer <- framer.Writer(rwc)\n\treader := framer.Reader(rwc)\n\n\tc.updateInFlight(func(s *inFlightState) {\n\t\tselect {\n\t\tcase <-c.done:\n\t\t\t// Bind already closed the connection; don't start a goroutine to read it.\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\n\t\t// The goroutine started here will continue until the underlying stream is closed.\n\t\t//\n\t\t// (If the Binder closed the Connection already, this should error out and\n\t\t// return almost immediately.)\n\t\ts.reading = true\n\t\tgo c.readIncoming(ctx, reader, options.Preempter)\n\t})\n\treturn c\n}\n\n// Notify invokes the target method but does not wait for a response.\n// The params will be marshaled to JSON before sending over the wire, and will\n// be handed to the method invoked.\nfunc (c *Connection) Notify(ctx context.Context, method string, params any) (err error) {\n\tattempted := false\n\n\tdefer func() {\n\t\tif attempted {\n\t\t\tc.updateInFlight(func(s *inFlightState) {\n\t\t\t\ts.outgoingNotifications--\n\t\t\t})\n\t\t}\n\t}()\n\n\tif debugCall {\n\t\tlog.Println(\"Notify\", method, \"params:\", params)\n\t}\n\n\tc.updateInFlight(func(s *inFlightState) {\n\t\t// If the connection is shutting down, allow outgoing notifications only if\n\t\t// there is at least one call still in flight. The number of calls in flight\n\t\t// cannot increase once shutdown begins, and allowing outgoing notifications\n\t\t// may permit notifications that will cancel in-flight calls.\n\t\tif len(s.outgoingCalls) == 0 && len(s.incomingByID) == 0 {\n\t\t\terr = s.shuttingDown(ErrClientClosing)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\ts.outgoingNotifications++\n\t\tattempted = true\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnotify, err := NewNotification(method, params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"marshaling notify parameters: %v\", err)\n\t}\n\n\terr = c.write(ctx, notify)\n\tif debugCall {\n\t\tlog.Println(\"Connection.write\", notify.Method, err)\n\t}\n\treturn\n}\n\n// Call invokes the target method and returns an object that can be used to await the response.\n// The params will be marshaled to JSON before sending over the wire, and will\n// be handed to the method invoked.\n// You do not have to wait for the response, it can just be ignored if not needed.\n// If sending the call failed, the response will be ready and have the error in it.\nfunc (c *Connection) Call(ctx context.Context, method string, params any) *AsyncCall {\n\tif debugCall {\n\t\tlog.Println(\"Call\", method, \"params:\", params)\n\t}\n\t// Generate a new request identifier.\n\tid := Int64ID(atomic.AddInt64(&c.seq, 1))\n\tac := &AsyncCall{\n\t\tid:    id,\n\t\tready: make(chan struct{}),\n\t}\n\t// When this method returns, either ac is retired, or the request has been\n\t// written successfully and the call is awaiting a response (to be provided by\n\t// the readIncoming goroutine).\n\n\tcall, err := NewCall(ac.id, method, params)\n\tif err != nil {\n\t\tac.retire(&Response{ID: id, Error: fmt.Errorf(\"marshaling call parameters: %w\", err)})\n\t\treturn ac\n\t}\n\n\tc.updateInFlight(func(s *inFlightState) {\n\t\terr = s.shuttingDown(ErrClientClosing)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif s.outgoingCalls == nil {\n\t\t\ts.outgoingCalls = make(map[ID]*AsyncCall)\n\t\t}\n\t\ts.outgoingCalls[ac.id] = ac\n\t})\n\tif err != nil {\n\t\tac.retire(&Response{ID: id, Error: err})\n\t\treturn ac\n\t}\n\n\terr = c.write(ctx, call)\n\tif debugCall {\n\t\tlog.Println(\"Connection.write\", call.ID, call.Method, err)\n\t}\n\tif err != nil {\n\t\t// Sending failed. We will never get a response, so deliver a fake one if it\n\t\t// wasn't already retired by the connection breaking.\n\t\tc.updateInFlight(func(s *inFlightState) {\n\t\t\tif s.outgoingCalls[ac.id] == ac {\n\t\t\t\tdelete(s.outgoingCalls, ac.id)\n\t\t\t\tac.retire(&Response{ID: id, Error: err})\n\t\t\t} else {\n\t\t\t\t// ac was already retired by the readIncoming goroutine:\n\t\t\t\t// perhaps our write raced with the Read side of the connection breaking.\n\t\t\t\t_ = 0\n\t\t\t}\n\t\t})\n\t}\n\treturn ac\n}\n\ntype AsyncCall struct {\n\tid       ID\n\tready    chan struct{} // closed after response has been set\n\tresponse *Response\n}\n\n// ID used for this call.\n// This can be used to cancel the call if needed.\nfunc (ac *AsyncCall) ID() ID { return ac.id }\n\n// IsReady can be used to check if the result is already prepared.\n// This is guaranteed to return true on a result for which Await has already\n// returned, or a call that failed to send in the first place.\nfunc (ac *AsyncCall) IsReady() bool {\n\tselect {\n\tcase <-ac.ready:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// retire processes the response to the call.\nfunc (ac *AsyncCall) retire(response *Response) {\n\tselect {\n\tcase <-ac.ready:\n\t\tpanic(fmt.Sprintf(\"jsonrpc2: retire called twice for ID %v\", ac.id))\n\tdefault:\n\t}\n\n\tac.response = response\n\tclose(ac.ready)\n}\n\n// Await waits for (and decodes) the results of a Call.\n// The response will be unmarshaled from JSON into the result.\nfunc (ac *AsyncCall) Await(ctx context.Context, result any) error {\n\tif Verbose {\n\t\tlog.Println(\"==> AsyncCall.Await\", ac.id)\n\t}\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tcase <-ac.ready:\n\t}\n\tif e := ac.response.Error; e != nil {\n\t\tif Verbose {\n\t\t\tlog.Println(\"==> AsyncCall.Await error:\", ac.id, e)\n\t\t}\n\t\treturn e\n\t}\n\tret := ac.response.Result\n\tif Verbose {\n\t\tlog.Println(\"==> AsyncCall.Await ret:\", ac.id, string(ret))\n\t}\n\tif result == nil {\n\t\treturn nil\n\t}\n\treturn json.Unmarshal(ret, result)\n}\n\n// Respond delivers a response to an incoming Call.\n//\n// Respond must be called exactly once for any message for which a handler\n// returns ErrAsyncResponse. It must not be called for any other message.\nfunc (c *Connection) Respond(id ID, result any, err error) error {\n\tvar req *incomingRequest\n\tc.updateInFlight(func(s *inFlightState) {\n\t\treq = s.incomingByID[id]\n\t})\n\tif req == nil {\n\t\treturn c.internalErrorf(\"Request not found for ID %v\", id)\n\t}\n\n\tif err == ErrAsyncResponse {\n\t\t// Respond is supposed to supply the asynchronous response, so it would be\n\t\t// confusing to call Respond with an error that promises to call Respond\n\t\t// again.\n\t\terr = c.internalErrorf(\"Respond called with ErrAsyncResponse for %q\", req.Method)\n\t}\n\treturn c.processResult(\"Respond\", req, result, err)\n}\n\n// Cancel cancels the Context passed to the Handle call for the inbound message\n// with the given ID.\n//\n// Cancel will not complain if the ID is not a currently active message, and it\n// will not cause any messages that have not arrived yet with that ID to be\n// cancelled.\nfunc (c *Connection) Cancel(id ID) {\n\tvar req *incomingRequest\n\tc.updateInFlight(func(s *inFlightState) {\n\t\treq = s.incomingByID[id]\n\t})\n\tif req != nil {\n\t\treq.cancel()\n\t}\n}\n\n// Wait blocks until the connection is fully closed, but does not close it.\nfunc (c *Connection) Wait() error {\n\tif Verbose {\n\t\tlog.Println(\"==> Connection.Wait start\")\n\t}\n\tvar err error\n\t<-c.done\n\tc.updateInFlight(func(s *inFlightState) {\n\t\terr = s.closeErr\n\t})\n\tif Verbose {\n\t\tlog.Println(\"==> Connection.Wait end:\", err)\n\t}\n\treturn err\n}\n\n// Close stops accepting new requests, waits for in-flight requests and enqueued\n// Handle calls to complete, and then closes the underlying stream.\n//\n// After the start of a Close, notification requests (that lack IDs and do not\n// receive responses) will continue to be passed to the Preempter, but calls\n// with IDs will receive immediate responses with ErrServerClosing, and no new\n// requests (not even notifications!) will be enqueued to the Handler.\nfunc (c *Connection) Close() error {\n\t// Stop handling new requests, and interrupt the reader (by closing the\n\t// connection) as soon as the active requests finish.\n\tc.updateInFlight(func(s *inFlightState) { s.connClosing = true })\n\n\treturn c.Wait()\n}\n\n// readIncoming collects inbound messages from the reader and delivers them, either responding\n// to outgoing calls or feeding requests to the queue.\nfunc (c *Connection) readIncoming(ctx context.Context, reader Reader, preempter Preempter) {\n\tvar err error\n\tfor {\n\t\tvar (\n\t\t\tmsg Message\n\t\t\tn   int64\n\t\t)\n\t\tmsg, n, err = reader.Read(ctx)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\n\t\tswitch msg := msg.(type) {\n\t\tcase *Request:\n\t\t\tc.acceptRequest(ctx, msg, n, preempter)\n\n\t\tcase *Response:\n\t\t\tif Verbose {\n\t\t\t\tlog.Println(\"==> readIncoming Response:\", msg.ID)\n\t\t\t}\n\t\t\tc.updateInFlight(func(s *inFlightState) {\n\t\t\t\tif ac, ok := s.outgoingCalls[msg.ID]; ok {\n\t\t\t\t\tdelete(s.outgoingCalls, msg.ID)\n\t\t\t\t\tac.retire(msg)\n\t\t\t\t} else {\n\t\t\t\t\t// TODO: How should we report unexpected responses?\n\t\t\t\t\t_ = 0\n\t\t\t\t}\n\t\t\t})\n\t\t\tif Verbose {\n\t\t\t\tlog.Println(\"==> readIncoming: updateInFlight -\", msg.ID)\n\t\t\t}\n\n\t\tdefault:\n\t\t\tc.internalErrorf(\"Read returned an unexpected message of type %T\", msg)\n\t\t}\n\t}\n\n\tc.updateInFlight(func(s *inFlightState) {\n\t\tif Verbose {\n\t\t\tlog.Println(\"==> readIncoming: updateInFlight s.reading - false\")\n\t\t}\n\t\ts.reading = false\n\t\ts.readErr = err\n\n\t\t// Retire any outgoing requests that were still in flight: with the Reader no\n\t\t// longer being processed, they necessarily cannot receive a response.\n\t\tfor id, ac := range s.outgoingCalls {\n\t\t\tac.retire(&Response{ID: id, Error: err})\n\t\t}\n\t\ts.outgoingCalls = nil\n\t})\n}\n\n// acceptRequest either handles msg synchronously or enqueues it to be handled\n// asynchronously.\nfunc (c *Connection) acceptRequest(ctx context.Context, msg *Request, msgBytes int64, preempter Preempter) {\n\t// In theory notifications cannot be cancelled, but we build them a cancel\n\t// context anyway.\n\tctx, cancel := context.WithCancel(ctx)\n\treq := &incomingRequest{\n\t\tRequest: msg,\n\t\tctx:     ctx,\n\t\tcancel:  cancel,\n\t}\n\n\t// If the request is a call, add it to the incoming map so it can be\n\t// cancelled (or responded) by ID.\n\tvar err error\n\tc.updateInFlight(func(s *inFlightState) {\n\t\ts.incoming++\n\n\t\tif req.IsCall() {\n\t\t\tif s.incomingByID[req.ID] != nil {\n\t\t\t\terr = fmt.Errorf(\"%w: request ID %v already in use\", ErrInvalidRequest, req.ID)\n\t\t\t\treq.ID = ID{} // Don't misattribute this error to the existing request.\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif s.incomingByID == nil {\n\t\t\t\ts.incomingByID = make(map[ID]*incomingRequest)\n\t\t\t}\n\t\t\ts.incomingByID[req.ID] = req\n\n\t\t\t// When shutting down, reject all new Call requests, even if they could\n\t\t\t// theoretically be handled by the preempter. The preempter could return\n\t\t\t// ErrAsyncResponse, which would increase the amount of work in flight\n\t\t\t// when we're trying to ensure that it strictly decreases.\n\t\t\terr = s.shuttingDown(ErrServerClosing)\n\t\t}\n\t})\n\tif err != nil {\n\t\tc.processResult(\"acceptRequest\", req, nil, err)\n\t\treturn\n\t}\n\n\tif preempter != nil {\n\t\tresult, err := preempter.Preempt(req.ctx, req.Request)\n\n\t\tif req.IsCall() && errors.Is(err, ErrAsyncResponse) {\n\t\t\t// This request will remain in flight until Respond is called for it.\n\t\t\treturn\n\t\t}\n\n\t\tif !errors.Is(err, ErrNotHandled) {\n\t\t\tc.processResult(\"Preempt\", req, result, err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tc.updateInFlight(func(s *inFlightState) {\n\t\t// If the connection is shutting down, don't enqueue anything to the\n\t\t// handler — not even notifications. That ensures that if the handler\n\t\t// continues to make progress, it will eventually become idle and\n\t\t// close the connection.\n\t\terr = s.shuttingDown(ErrServerClosing)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// We enqueue requests that have not been preempted to an unbounded slice.\n\t\t// Unfortunately, we cannot in general limit the size of the handler\n\t\t// queue: we have to read every response that comes in on the wire\n\t\t// (because it may be responding to a request issued by, say, an\n\t\t// asynchronous handler), and in order to get to that response we have\n\t\t// to read all of the requests that came in ahead of it.\n\t\ts.handlerQueue = append(s.handlerQueue, req)\n\t\tif !s.handlerRunning {\n\t\t\t// We start the handleAsync goroutine when it has work to do, and let it\n\t\t\t// exit when the queue empties.\n\t\t\t//\n\t\t\t// Otherwise, in order to synchronize the handler we would need some other\n\t\t\t// goroutine (probably readIncoming?) to explicitly wait for handleAsync\n\t\t\t// to finish, and that would complicate error reporting: either the error\n\t\t\t// report from the goroutine would be blocked on the handler emptying its\n\t\t\t// queue (which was tried, and introduced a deadlock detected by\n\t\t\t// TestCloseCallRace), or the error would need to be reported separately\n\t\t\t// from synchronizing completion. Allowing the handler goroutine to exit\n\t\t\t// when idle seems simpler than trying to implement either of those\n\t\t\t// alternatives correctly.\n\t\t\ts.handlerRunning = true\n\t\t\tgo c.handleAsync()\n\t\t}\n\t})\n\tif err != nil {\n\t\tc.processResult(\"acceptRequest\", req, nil, err)\n\t}\n}\n\n// handleAsync invokes the handler on the requests in the handler queue\n// sequentially until the queue is empty.\nfunc (c *Connection) handleAsync() {\n\tfor {\n\t\tvar req *incomingRequest\n\t\tc.updateInFlight(func(s *inFlightState) {\n\t\t\tif len(s.handlerQueue) > 0 {\n\t\t\t\treq, s.handlerQueue = s.handlerQueue[0], s.handlerQueue[1:]\n\t\t\t} else {\n\t\t\t\ts.handlerRunning = false\n\t\t\t}\n\t\t})\n\t\tif req == nil {\n\t\t\treturn\n\t\t}\n\n\t\t// Only deliver to the Handler if not already canceled.\n\t\tif err := req.ctx.Err(); err != nil {\n\t\t\tc.updateInFlight(func(s *inFlightState) {\n\t\t\t\tif s.writeErr != nil {\n\t\t\t\t\t// Assume that req.ctx was canceled due to s.writeErr.\n\t\t\t\t\t// TODO(#51365): use a Context API to plumb this through req.ctx.\n\t\t\t\t\terr = fmt.Errorf(\"%w: %v\", ErrServerClosing, s.writeErr)\n\t\t\t\t}\n\t\t\t})\n\t\t\tc.processResult(\"handleAsync\", req, nil, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif debugCall {\n\t\t\treq := req.Request\n\t\t\tid := req.ID\n\t\t\tif !id.IsValid() {\n\t\t\t\tid = StringID(\"\")\n\t\t\t}\n\t\t\tlog.Println(\"handleAsync\", id, req.Method, string(req.Params))\n\t\t}\n\t\tresult, err := c.handler.Handle(req.ctx, req.Request)\n\t\tc.processResult(c.handler, req, result, err)\n\t}\n}\n\n// processResult processes the result of a request and, if appropriate, sends a response.\nfunc (c *Connection) processResult(from any, req *incomingRequest, result any, err error) error {\n\tswitch err {\n\tcase ErrAsyncResponse:\n\t\tif !req.IsCall() {\n\t\t\treturn c.internalErrorf(\"%#v returned ErrAsyncResponse for a %q Request without an ID\", from, req.Method)\n\t\t}\n\t\treturn nil // This request is still in flight, so don't record the result yet.\n\tcase ErrNotHandled, ErrMethodNotFound:\n\t\t// Add detail describing the unhandled method.\n\t\terr = fmt.Errorf(\"%w: %q\", ErrMethodNotFound, req.Method)\n\t}\n\n\tif result != nil && err != nil {\n\t\tc.internalErrorf(\"%#v returned a non-nil result with a non-nil error for %s:\\n%v\\n%#v\", from, req.Method, err, result)\n\t\tresult = nil // Discard the spurious result and respond with err.\n\t}\n\n\tif req.IsCall() {\n\t\tresponse, respErr := NewResponse(req.ID, result, err)\n\t\tif debugCall {\n\t\t\tlog.Println(\"processResult\", response.ID, string(response.Result), response.Error)\n\t\t}\n\n\t\t// The caller could theoretically reuse the request's ID as soon as we've\n\t\t// sent the response, so ensure that it is removed from the incoming map\n\t\t// before sending.\n\t\tc.updateInFlight(func(s *inFlightState) {\n\t\t\tdelete(s.incomingByID, req.ID)\n\t\t})\n\t\tif respErr == nil {\n\t\t\twriteErr := c.write(notDone{req.ctx}, response)\n\t\t\tif err == nil {\n\t\t\t\terr = writeErr\n\t\t\t}\n\t\t} else {\n\t\t\terr = c.internalErrorf(\"%#v returned a malformed result for %q: %w\", from, req.Method, respErr)\n\t\t}\n\t} else { // req is a notification\n\t\tif result != nil {\n\t\t\terr = c.internalErrorf(\"%#v returned a non-nil result for a %q Request without an ID\", from, req.Method)\n\t\t} else if err != nil {\n\t\t\terr = fmt.Errorf(\"%w: %q notification failed: %v\", ErrInternal, req.Method, err)\n\t\t}\n\t}\n\t_ = err\n\n\t// Cancel the request and finalize the event span to free any associated resources.\n\treq.cancel()\n\tc.updateInFlight(func(s *inFlightState) {\n\t\tif s.incoming == 0 {\n\t\t\tpanic(\"jsonrpc2_v2: processResult called when incoming count is already zero\")\n\t\t}\n\t\ts.incoming--\n\t})\n\treturn nil\n}\n\n// write is used by all things that write outgoing messages, including replies.\n// it makes sure that writes are atomic\nfunc (c *Connection) write(ctx context.Context, msg Message) error {\n\twriter := <-c.writer\n\tdefer func() { c.writer <- writer }()\n\t_, err := writer.Write(ctx, msg)\n\n\tif err != nil && ctx.Err() == nil {\n\t\t// The call to Write failed, and since ctx.Err() is nil we can't attribute\n\t\t// the failure (even indirectly) to Context cancellation. The writer appears\n\t\t// to be broken, and future writes are likely to also fail.\n\t\t//\n\t\t// If the read side of the connection is also broken, we might not even be\n\t\t// able to receive cancellation notifications. Since we can't reliably write\n\t\t// the results of incoming calls and can't receive explicit cancellations,\n\t\t// cancel the calls now.\n\t\tc.updateInFlight(func(s *inFlightState) {\n\t\t\tif s.writeErr == nil {\n\t\t\t\ts.writeErr = err\n\t\t\t\tfor _, r := range s.incomingByID {\n\t\t\t\t\tr.cancel()\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\treturn err\n}\n\n// internalErrorf reports an internal error. By default it panics, but if\n// c.onInternalError is non-nil it instead calls that and returns an error\n// wrapping ErrInternal.\nfunc (c *Connection) internalErrorf(format string, args ...any) error {\n\terr := fmt.Errorf(format, args...)\n\tc.onInternalError(err)\n\n\treturn fmt.Errorf(\"%w: %v\", ErrInternal, err)\n}\n\n// notDone is a context.Context wrapper that returns a nil Done channel.\ntype notDone struct{ ctx context.Context }\n\nfunc (ic notDone) Value(key any) any {\n\treturn ic.ctx.Value(key)\n}\n\nfunc (notDone) Done() <-chan struct{}       { return nil }\nfunc (notDone) Err() error                  { return nil }\nfunc (notDone) Deadline() (time.Time, bool) { return time.Time{}, false }\n"
  },
  {
    "path": "x/jsonrpc2/frame.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage jsonrpc2\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Reader abstracts the transport mechanics from the JSON RPC protocol.\n// A Conn reads messages from the reader it was provided on construction,\n// and assumes that each call to Read fully transfers a single message,\n// or returns an error.\n// A reader is not safe for concurrent use, it is expected it will be used by\n// a single Conn in a safe manner.\ntype Reader interface {\n\t// Read gets the next message from the stream.\n\tRead(context.Context) (Message, int64, error)\n}\n\n// Writer abstracts the transport mechanics from the JSON RPC protocol.\n// A Conn writes messages using the writer it was provided on construction,\n// and assumes that each call to Write fully transfers a single message,\n// or returns an error.\n// A writer is not safe for concurrent use, it is expected it will be used by\n// a single Conn in a safe manner.\ntype Writer interface {\n\t// Write sends a message to the stream.\n\tWrite(context.Context, Message) (int64, error)\n}\n\n// Framer wraps low level byte readers and writers into jsonrpc2 message\n// readers and writers.\n// It is responsible for the framing and encoding of messages into wire form.\ntype Framer interface {\n\t// Reader wraps a byte reader into a message reader.\n\tReader(rw io.Reader) Reader\n\t// Writer wraps a byte writer into a message writer.\n\tWriter(rw io.Writer) Writer\n}\n\n// HeaderFramer returns a new Framer.\n// The messages are sent with HTTP content length and MIME type headers.\n// This is the format used by LSP and others.\nfunc HeaderFramer() Framer { return headerFramer{} }\n\ntype headerFramer struct{}\ntype headerReader struct{ in *bufio.Reader }\ntype headerWriter struct{ out io.Writer }\n\nfunc (headerFramer) Reader(rw io.Reader) Reader {\n\treturn &headerReader{in: bufio.NewReader(rw)}\n}\n\nfunc (headerFramer) Writer(rw io.Writer) Writer {\n\treturn &headerWriter{out: rw}\n}\n\nfunc (r *headerReader) Read(ctx context.Context) (Message, int64, error) {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn nil, 0, ctx.Err()\n\tdefault:\n\t}\n\tvar total, length int64\n\t// read the header, stop on the first empty line\n\tfor {\n\t\tline, err := r.in.ReadString('\\n')\n\t\ttotal += int64(len(line))\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tif total == 0 {\n\t\t\t\t\treturn nil, 0, io.EOF\n\t\t\t\t}\n\t\t\t\terr = io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\treturn nil, total, fmt.Errorf(\"failed reading header line: %w\", err)\n\t\t}\n\t\tline = strings.TrimSpace(line)\n\t\t// check we have a header line\n\t\tif line == \"\" {\n\t\t\tbreak\n\t\t}\n\t\tcolon := strings.IndexRune(line, ':')\n\t\tif colon < 0 {\n\t\t\treturn nil, total, fmt.Errorf(\"invalid header line %q\", line)\n\t\t}\n\t\tname, value := line[:colon], strings.TrimSpace(line[colon+1:])\n\t\tswitch name {\n\t\tcase \"Content-Length\":\n\t\t\tif length, err = strconv.ParseInt(value, 10, 32); err != nil {\n\t\t\t\treturn nil, total, fmt.Errorf(\"failed parsing Content-Length: %v\", value)\n\t\t\t}\n\t\t\tif length <= 0 {\n\t\t\t\treturn nil, total, fmt.Errorf(\"invalid Content-Length: %v\", length)\n\t\t\t}\n\t\tdefault:\n\t\t\t// ignoring unknown headers\n\t\t}\n\t}\n\tif length == 0 {\n\t\treturn nil, total, fmt.Errorf(\"missing Content-Length header\")\n\t}\n\tdata := make([]byte, length)\n\tn, err := io.ReadFull(r.in, data)\n\ttotal += int64(n)\n\tif err != nil {\n\t\treturn nil, total, err\n\t}\n\tmsg, err := DecodeMessage(data)\n\treturn msg, total, err\n}\n\nfunc (w *headerWriter) Write(ctx context.Context, msg Message) (int64, error) {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn 0, ctx.Err()\n\tdefault:\n\t}\n\tdata, err := EncodeMessage(msg)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"marshaling message: %v\", err)\n\t}\n\tn, err := fmt.Fprintf(w.out, \"Content-Length: %v\\r\\n\\r\\n\", len(data))\n\ttotal := int64(n)\n\tif err == nil {\n\t\tn, err = w.out.Write(data)\n\t\ttotal += int64(n)\n\t}\n\treturn total, err\n}\n"
  },
  {
    "path": "x/jsonrpc2/internal/stack/parse.go",
    "content": "// Copyright 2020 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage stack\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"regexp\"\n\t\"strconv\"\n)\n\nvar (\n\treBlank     = regexp.MustCompile(`^\\s*$`)\n\treGoroutine = regexp.MustCompile(`^\\s*goroutine (\\d+) \\[([^\\]]*)\\]:\\s*$`)\n\treCall      = regexp.MustCompile(`^\\s*` +\n\t\t`(created by )?` + //marker\n\t\t`(([\\w/.]+/)?[\\w]+)\\.` + //package\n\t\t`(\\(([^:.)]*)\\)\\.)?` + //optional type\n\t\t`([\\w\\.]+)` + //function\n\t\t`(\\(.*\\))?` + // args\n\t\t`\\s*$`)\n\trePos = regexp.MustCompile(`^\\s*(.*):(\\d+)( .*)?$`)\n)\n\n// Scanner splits an input stream into lines in a way that is consumable by\n// the parser.\ntype Scanner struct {\n\tlines *bufio.Scanner\n\tdone  bool\n}\n\n// NewScanner creates a scanner on top of a reader.\nfunc NewScanner(r io.Reader) *Scanner {\n\ts := &Scanner{\n\t\tlines: bufio.NewScanner(r),\n\t}\n\ts.Skip() // prefill\n\treturn s\n}\n\n// Peek returns the next line without consuming it.\nfunc (s *Scanner) Peek() string {\n\tif s.done {\n\t\treturn \"\"\n\t}\n\treturn s.lines.Text()\n}\n\n// Skip consumes the next line without looking at it.\n// Normally used after it has already been looked at using Peek.\nfunc (s *Scanner) Skip() {\n\tif !s.lines.Scan() {\n\t\ts.done = true\n\t}\n}\n\n// Next consumes and returns the next line.\nfunc (s *Scanner) Next() string {\n\tline := s.Peek()\n\ts.Skip()\n\treturn line\n}\n\n// Done returns true if the scanner has reached the end of the underlying\n// stream.\nfunc (s *Scanner) Done() bool {\n\treturn s.done\n}\n\n// Err returns true if the scanner has reached the end of the underlying\n// stream.\nfunc (s *Scanner) Err() error {\n\treturn s.lines.Err()\n}\n\n// Match returns the submatchs of the regular expression against the next line.\n// If it matched the line is also consumed.\nfunc (s *Scanner) Match(re *regexp.Regexp) []string {\n\tif s.done {\n\t\treturn nil\n\t}\n\tmatch := re.FindStringSubmatch(s.Peek())\n\tif match != nil {\n\t\ts.Skip()\n\t}\n\treturn match\n}\n\n// SkipBlank skips any number of pure whitespace lines.\nfunc (s *Scanner) SkipBlank() {\n\tfor !s.done {\n\t\tline := s.Peek()\n\t\tif len(line) != 0 && !reBlank.MatchString(line) {\n\t\t\treturn\n\t\t}\n\t\ts.Skip()\n\t}\n}\n\n// Parse the current contiguous block of goroutine stack traces until the\n// scanned content no longer matches.\nfunc Parse(scanner *Scanner) (Dump, error) {\n\tdump := Dump{}\n\tfor {\n\t\tgr, ok := parseGoroutine(scanner)\n\t\tif !ok {\n\t\t\treturn dump, nil\n\t\t}\n\t\tdump = append(dump, gr)\n\t}\n}\n\nfunc parseGoroutine(scanner *Scanner) (Goroutine, bool) {\n\tmatch := scanner.Match(reGoroutine)\n\tif match == nil {\n\t\treturn Goroutine{}, false\n\t}\n\tid, _ := strconv.ParseInt(match[1], 0, 32)\n\tgr := Goroutine{\n\t\tID:    int(id),\n\t\tState: match[2],\n\t}\n\tfor {\n\t\tframe, ok := parseFrame(scanner)\n\t\tif !ok {\n\t\t\tscanner.SkipBlank()\n\t\t\treturn gr, true\n\t\t}\n\t\tif frame.Position.Filename != \"\" {\n\t\t\tgr.Stack = append(gr.Stack, frame)\n\t\t}\n\t}\n}\n\nfunc parseFrame(scanner *Scanner) (Frame, bool) {\n\tfun, ok := parseFunction(scanner)\n\tif !ok {\n\t\treturn Frame{}, false\n\t}\n\tframe := Frame{\n\t\tFunction: fun,\n\t}\n\tframe.Position, ok = parsePosition(scanner)\n\t// if ok is false, then this is a broken state.\n\t// we got the func but not the file that must follow\n\t// the consumed line can be recovered from the frame\n\t//TODO: push back the fun raw\n\treturn frame, ok\n}\n\nfunc parseFunction(scanner *Scanner) (Function, bool) {\n\tmatch := scanner.Match(reCall)\n\tif match == nil {\n\t\treturn Function{}, false\n\t}\n\treturn Function{\n\t\tPackage: match[2],\n\t\tType:    match[5],\n\t\tName:    match[6],\n\t}, true\n}\n\nfunc parsePosition(scanner *Scanner) (Position, bool) {\n\tmatch := scanner.Match(rePos)\n\tif match == nil {\n\t\treturn Position{}, false\n\t}\n\tline, _ := strconv.ParseInt(match[2], 0, 32)\n\treturn Position{Filename: match[1], Line: int(line)}, true\n}\n"
  },
  {
    "path": "x/jsonrpc2/internal/stack/process.go",
    "content": "// Copyright 2020 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage stack\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"runtime\"\n\t\"sort\"\n)\n\n// Capture get the current stack traces from the runtime.\nfunc Capture() Dump {\n\tbuf := make([]byte, 2<<20)\n\tbuf = buf[:runtime.Stack(buf, true)]\n\tscanner := NewScanner(bytes.NewReader(buf))\n\tdump, _ := Parse(scanner)\n\treturn dump\n}\n\n// Summarize a dump for easier consumption.\n// This collates goroutines with equivalent stacks.\nfunc Summarize(dump Dump) Summary {\n\ts := Summary{\n\t\tTotal: len(dump),\n\t}\n\tfor _, gr := range dump {\n\t\ts.addGoroutine(gr)\n\t}\n\treturn s\n}\n\n// Process and input stream to an output stream, summarizing any stacks that\n// are detected in place.\nfunc Process(out io.Writer, in io.Reader) error {\n\tscanner := NewScanner(in)\n\tfor {\n\t\tdump, err := Parse(scanner)\n\t\tsummary := Summarize(dump)\n\t\tswitch {\n\t\tcase len(dump) > 0:\n\t\t\tfmt.Fprintf(out, \"%+v\\n\\n\", summary)\n\t\tcase err != nil:\n\t\t\treturn err\n\t\tcase scanner.Done():\n\t\t\treturn scanner.Err()\n\t\tdefault:\n\t\t\t// must have been a line that is not part of a dump\n\t\t\tfmt.Fprintln(out, scanner.Next())\n\t\t}\n\t}\n}\n\n// Diff calculates the delta between two dumps.\nfunc Diff(before, after Dump) Delta {\n\tresult := Delta{}\n\tprocessed := make(map[int]bool)\n\tfor _, gr := range before {\n\t\tprocessed[gr.ID] = false\n\t}\n\tfor _, gr := range after {\n\t\tif _, found := processed[gr.ID]; found {\n\t\t\tresult.Shared = append(result.Shared, gr)\n\t\t} else {\n\t\t\tresult.After = append(result.After, gr)\n\t\t}\n\t\tprocessed[gr.ID] = true\n\t}\n\tfor _, gr := range before {\n\t\tif done := processed[gr.ID]; !done {\n\t\t\tresult.Before = append(result.Before, gr)\n\t\t}\n\t}\n\treturn result\n}\n\n// TODO: do we want to allow contraction of stacks before comparison?\nfunc (s *Summary) addGoroutine(gr Goroutine) {\n\tindex := sort.Search(len(s.Calls), func(i int) bool {\n\t\treturn !s.Calls[i].Stack.less(gr.Stack)\n\t})\n\tif index >= len(s.Calls) || !s.Calls[index].Stack.equal(gr.Stack) {\n\t\t// insert new stack, first increase the length\n\t\ts.Calls = append(s.Calls, Call{})\n\t\t// move the top part upward to make space\n\t\tcopy(s.Calls[index+1:], s.Calls[index:])\n\t\t// insert the new call\n\t\ts.Calls[index] = Call{\n\t\t\tStack: gr.Stack,\n\t\t}\n\t}\n\t// merge the goroutine into the matched call\n\ts.Calls[index].merge(gr)\n}\n\n// TODO: do we want other grouping strategies?\nfunc (c *Call) merge(gr Goroutine) {\n\tfor i := range c.Groups {\n\t\tcanditate := &c.Groups[i]\n\t\tif canditate.State == gr.State {\n\t\t\tcanditate.Goroutines = append(canditate.Goroutines, gr)\n\t\t\treturn\n\t\t}\n\t}\n\tc.Groups = append(c.Groups, Group{\n\t\tState:      gr.State,\n\t\tGoroutines: []Goroutine{gr},\n\t})\n}\n"
  },
  {
    "path": "x/jsonrpc2/internal/stack/stack.go",
    "content": "// Copyright 2020 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package stack provides support for parsing standard goroutine stack traces.\npackage stack\n\nimport (\n\t\"fmt\"\n\t\"text/tabwriter\"\n)\n\n// Dump is a raw set of goroutines and their stacks.\ntype Dump []Goroutine\n\n// Goroutine is a single parsed goroutine dump.\ntype Goroutine struct {\n\tState string // state that the goroutine is in.\n\tID    int    // id of the goroutine.\n\tStack Stack  // call frames that make up the stack\n}\n\n// Stack is a set of frames in a callstack.\ntype Stack []Frame\n\n// Frame is a point in a call stack.\ntype Frame struct {\n\tFunction Function\n\tPosition Position\n}\n\n// Function is the function called at a frame.\ntype Function struct {\n\tPackage string // package name of function if known\n\tType    string // if set function is a method of this type\n\tName    string // function name of the frame\n}\n\n// Position is the file position for a frame.\ntype Position struct {\n\tFilename string // source filename\n\tLine     int    // line number within file\n}\n\n// Summary is a set of stacks processed and collated into Calls.\ntype Summary struct {\n\tTotal int    // the total count of goroutines in the summary\n\tCalls []Call // the collated stack traces\n}\n\n// Call is set of goroutines that all share the same callstack.\n// They will be grouped by state.\ntype Call struct {\n\tStack  Stack   // the shared callstack information\n\tGroups []Group // the sets of goroutines with the same state\n}\n\n// Group is a set of goroutines with the same stack that are in the same state.\ntype Group struct {\n\tState      string      // the shared state of the goroutines\n\tGoroutines []Goroutine // the set of goroutines in this group\n}\n\n// Delta represents the difference between two stack dumps.\ntype Delta struct {\n\tBefore Dump // The goroutines that were only in the before set.\n\tShared Dump // The goroutines that were in both sets.\n\tAfter  Dump // The goroutines that were only in the after set.\n}\n\nfunc (s Stack) equal(other Stack) bool {\n\tif len(s) != len(other) {\n\t\treturn false\n\t}\n\tfor i, frame := range s {\n\t\tif !frame.equal(other[i]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (s Stack) less(other Stack) bool {\n\tfor i, frame := range s {\n\t\tif i >= len(other) {\n\t\t\treturn false\n\t\t}\n\t\tif frame.less(other[i]) {\n\t\t\treturn true\n\t\t}\n\t\tif !frame.equal(other[i]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn len(s) < len(other)\n}\n\nfunc (f Frame) equal(other Frame) bool {\n\treturn f.Position.equal(other.Position)\n}\n\nfunc (f Frame) less(other Frame) bool {\n\treturn f.Position.less(other.Position)\n}\n\nfunc (p Position) equal(other Position) bool {\n\treturn p.Filename == other.Filename && p.Line == other.Line\n}\n\nfunc (p Position) less(other Position) bool {\n\tif p.Filename < other.Filename {\n\t\treturn true\n\t}\n\tif p.Filename > other.Filename {\n\t\treturn false\n\t}\n\treturn p.Line < other.Line\n}\n\nfunc (s Summary) Format(w fmt.State, r rune) {\n\ttw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)\n\tfor i, c := range s.Calls {\n\t\tif i > 0 {\n\t\t\tfmt.Fprintf(tw, \"\\n\\n\")\n\t\t\ttw.Flush()\n\t\t}\n\t\tfmt.Fprint(tw, c)\n\t}\n\ttw.Flush()\n\tif s.Total > 0 && w.Flag('+') {\n\t\tfmt.Fprintf(w, \"\\n\\n%d goroutines, %d unique\", s.Total, len(s.Calls))\n\t}\n}\n\nfunc (c Call) Format(w fmt.State, r rune) {\n\tfor i, g := range c.Groups {\n\t\tif i > 0 {\n\t\t\tfmt.Fprint(w, \" \")\n\t\t}\n\t\tfmt.Fprint(w, g)\n\t}\n\tfor _, f := range c.Stack {\n\t\tfmt.Fprintf(w, \"\\n%v\", f)\n\t}\n}\n\nfunc (g Group) Format(w fmt.State, r rune) {\n\tfmt.Fprintf(w, \"[%v]: \", g.State)\n\tfor i, gr := range g.Goroutines {\n\t\tif i > 0 {\n\t\t\tfmt.Fprint(w, \", \")\n\t\t}\n\t\tfmt.Fprintf(w, \"$%d\", gr.ID)\n\t}\n}\n\nfunc (f Frame) Format(w fmt.State, c rune) {\n\tfmt.Fprintf(w, \"%v:\\t%v\", f.Position, f.Function)\n}\n\nfunc (f Function) Format(w fmt.State, c rune) {\n\tif f.Type != \"\" {\n\t\tfmt.Fprintf(w, \"(%v).\", f.Type)\n\t}\n\tfmt.Fprintf(w, \"%v\", f.Name)\n}\n\nfunc (p Position) Format(w fmt.State, c rune) {\n\tfmt.Fprintf(w, \"%v:%v\", p.Filename, p.Line)\n}\n"
  },
  {
    "path": "x/jsonrpc2/internal/stack/stacktest/stacktest.go",
    "content": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage stacktest\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/goplus/xgo/x/jsonrpc2/internal/stack\"\n)\n\n// this is only needed to support pre 1.14 when testing.TB did not have Cleanup\ntype withCleanup interface {\n\tCleanup(func())\n}\n\n// the maximum amount of time to wait for goroutines to clean themselves up.\nconst maxWait = time.Second\n\n// NoLeak checks that a test (or benchmark) does not leak any goroutines.\nfunc NoLeak(t testing.TB) {\n\tc, ok := t.(withCleanup)\n\tif !ok {\n\t\treturn\n\t}\n\tbefore := stack.Capture()\n\tc.Cleanup(func() {\n\t\tvar delta stack.Delta\n\t\tstart := time.Now()\n\t\tdelay := time.Millisecond\n\t\tfor {\n\t\t\tafter := stack.Capture()\n\t\t\tdelta = stack.Diff(before, after)\n\t\t\tif len(delta.After) == 0 {\n\t\t\t\t// no leaks\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif time.Since(start) > maxWait {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\ttime.Sleep(delay)\n\t\t\tdelay *= 2\n\t\t}\n\t\t// it's been long enough, and leaks are still present\n\t\tsummary := stack.Summarize(delta.After)\n\t\tt.Errorf(\"goroutine leak detected:\\n%+v\", summary)\n\t})\n}\n"
  },
  {
    "path": "x/jsonrpc2/jsonrpc2.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec.\n// https://www.jsonrpc.org/specification\n// It is intended to be compatible with other implementations at the wire level.\npackage jsonrpc2\n\nimport (\n\t\"context\"\n\t\"errors\"\n)\n\ntype dbgFlags int\n\nconst (\n\tDbgFlagVerbose dbgFlags = 1 << iota\n\tDbgFlagCall\n\tDbgFlagAll = DbgFlagVerbose | DbgFlagCall\n)\n\nvar (\n\tVerbose   bool\n\tdebugCall bool\n)\n\n// SetDebug sets debug flags.\nfunc SetDebug(flags dbgFlags) {\n\tVerbose = (flags & DbgFlagVerbose) != 0\n\tdebugCall = (flags & (DbgFlagCall | DbgFlagVerbose)) != 0\n}\n\nvar (\n\t// ErrIdleTimeout is returned when serving timed out waiting for new connections.\n\tErrIdleTimeout = errors.New(\"timed out waiting for new connections\")\n\n\t// ErrNotHandled is returned from a Handler or Preempter to indicate it did\n\t// not handle the request.\n\t//\n\t// If a Handler returns ErrNotHandled, the server replies with\n\t// ErrMethodNotFound.\n\tErrNotHandled = errors.New(\"JSON RPC not handled\")\n\n\t// ErrAsyncResponse is returned from a handler to indicate it will generate a\n\t// response asynchronously.\n\t//\n\t// ErrAsyncResponse must not be returned for notifications,\n\t// which do not receive responses.\n\tErrAsyncResponse = errors.New(\"JSON RPC asynchronous response\")\n)\n\n// Preempter handles messages on a connection before they are queued to the main\n// handler.\n// Primarily this is used for cancel handlers or notifications for which out of\n// order processing is not an issue.\ntype Preempter interface {\n\t// Preempt is invoked for each incoming request before it is queued for handling.\n\t//\n\t// If Preempt returns ErrNotHandled, the request will be queued,\n\t// and eventually passed to a Handle call.\n\t//\n\t// Otherwise, the result and error are processed as if returned by Handle.\n\t//\n\t// Preempt must not block. (The Context passed to it is for Values only.)\n\tPreempt(ctx context.Context, req *Request) (result any, err error)\n}\n\n// A PreempterFunc implements the Preempter interface for a standalone Preempt function.\ntype PreempterFunc func(ctx context.Context, req *Request) (any, error)\n\nfunc (f PreempterFunc) Preempt(ctx context.Context, req *Request) (any, error) {\n\treturn f(ctx, req)\n}\n\nvar _ Preempter = PreempterFunc(nil)\n\n// Handler handles messages on a connection.\ntype Handler interface {\n\t// Handle is invoked sequentially for each incoming request that has not\n\t// already been handled by a Preempter.\n\t//\n\t// If the Request has a nil ID, Handle must return a nil result,\n\t// and any error may be logged but will not be reported to the caller.\n\t//\n\t// If the Request has a non-nil ID, Handle must return either a\n\t// non-nil, JSON-marshalable result, or a non-nil error.\n\t//\n\t// The Context passed to Handle will be canceled if the\n\t// connection is broken or the request is canceled or completed.\n\t// (If Handle returns ErrAsyncResponse, ctx will remain uncanceled\n\t// until either Cancel or Respond is called for the request's ID.)\n\tHandle(ctx context.Context, req *Request) (result any, err error)\n}\n\ntype defaultHandler struct{}\n\nfunc (defaultHandler) Preempt(context.Context, *Request) (any, error) {\n\treturn nil, ErrNotHandled\n}\n\nfunc (defaultHandler) Handle(context.Context, *Request) (any, error) {\n\treturn nil, ErrNotHandled\n}\n\n// A HandlerFunc implements the Handler interface for a standalone Handle function.\ntype HandlerFunc func(ctx context.Context, req *Request) (any, error)\n\nfunc (f HandlerFunc) Handle(ctx context.Context, req *Request) (any, error) {\n\treturn f(ctx, req)\n}\n\nvar _ Handler = HandlerFunc(nil)\n\nfunc defaultHandleError(err error) {\n\tpanic(\"jsonrpc2: \" + err.Error())\n}\n\n// async is a small helper for operations with an asynchronous result that you\n// can wait for.\ntype async struct {\n\tready    chan struct{} // closed when done\n\tfirstErr chan error    // 1-buffered; contains either nil or the first non-nil error\n}\n\nfunc newAsync() *async {\n\tvar a async\n\ta.ready = make(chan struct{})\n\ta.firstErr = make(chan error, 1)\n\ta.firstErr <- nil\n\treturn &a\n}\n\nfunc (a *async) done() {\n\tclose(a.ready)\n}\n\nfunc (a *async) wait() error {\n\t<-a.ready\n\terr := <-a.firstErr\n\ta.firstErr <- err\n\treturn err\n}\n\nfunc (a *async) setError(err error) {\n\tstoredErr := <-a.firstErr\n\tif storedErr == nil {\n\t\tstoredErr = err\n\t}\n\ta.firstErr <- storedErr\n}\n"
  },
  {
    "path": "x/jsonrpc2/jsonrpc2test/cases/testcase.go",
    "content": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage cases\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"path\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/goplus/xgo/x/jsonrpc2\"\n\t\"github.com/goplus/xgo/x/jsonrpc2/internal/stack/stacktest\"\n)\n\nvar callTests = []invoker{\n\tcall{\"no_args\", nil, true},\n\tcall{\"one_string\", \"fish\", \"got:fish\"},\n\tcall{\"one_number\", 10, \"got:10\"},\n\tcall{\"join\", []string{\"a\", \"b\", \"c\"}, \"a/b/c\"},\n\tsequence{\"notify\", []invoker{\n\t\tnotify{\"set\", 3},\n\t\tnotify{\"add\", 5},\n\t\tcall{\"get\", nil, 8},\n\t}},\n\tsequence{\"preempt\", []invoker{\n\t\tasync{\"a\", \"wait\", \"a\"},\n\t\tnotify{\"unblock\", \"a\"},\n\t\tcollect{\"a\", true, false},\n\t}},\n\tsequence{\"basic cancel\", []invoker{\n\t\tasync{\"b\", \"wait\", \"b\"},\n\t\tcancel{\"b\"},\n\t\tcollect{\"b\", nil, true},\n\t}},\n\tsequence{\"queue\", []invoker{\n\t\tasync{\"a\", \"wait\", \"a\"},\n\t\tnotify{\"set\", 1},\n\t\tnotify{\"add\", 2},\n\t\tnotify{\"add\", 3},\n\t\tnotify{\"add\", 4},\n\t\t// call{\"peek\", nil, 0}, // accumulator will not have any adds yet\n\t\tnotify{\"unblock\", \"a\"},\n\t\tcollect{\"a\", true, false},\n\t\tcall{\"get\", nil, 10}, // accumulator now has all the adds\n\t}},\n\tsequence{\"fork\", []invoker{\n\t\tasync{\"a\", \"fork\", \"a\"},\n\t\tnotify{\"set\", 1},\n\t\tnotify{\"add\", 2},\n\t\tnotify{\"add\", 3},\n\t\tnotify{\"add\", 4},\n\t\tcall{\"get\", nil, 10}, // fork will not have blocked the adds\n\t\tnotify{\"unblock\", \"a\"},\n\t\tcollect{\"a\", true, false},\n\t}},\n\tsequence{\"concurrent\", []invoker{\n\t\tasync{\"a\", \"fork\", \"a\"},\n\t\tnotify{\"unblock\", \"a\"},\n\t\tasync{\"b\", \"fork\", \"b\"},\n\t\tnotify{\"unblock\", \"b\"},\n\t\tcollect{\"a\", true, false},\n\t\tcollect{\"b\", true, false},\n\t}},\n}\n\ntype binder struct {\n\tframer  jsonrpc2.Framer\n\trunTest func(*handler)\n}\n\ntype handler struct {\n\tconn        *jsonrpc2.Connection\n\taccumulator int\n\n\tmutex   sync.Mutex\n\twaiters map[string]chan struct{}\n\n\tcalls map[string]*jsonrpc2.AsyncCall\n}\n\ntype invoker interface {\n\tName() string\n\tInvoke(t *testing.T, ctx context.Context, h *handler)\n}\n\ntype notify struct {\n\tmethod string\n\tparams any\n}\n\ntype call struct {\n\tmethod string\n\tparams any\n\texpect any\n}\n\ntype async struct {\n\tname   string\n\tmethod string\n\tparams any\n}\n\ntype collect struct {\n\tname   string\n\texpect any\n\tfails  bool\n}\n\ntype cancel struct {\n\tname string\n}\n\ntype sequence struct {\n\tname  string\n\ttests []invoker\n}\n\ntype echo call\n\ntype cancelParams struct{ ID int64 }\n\nfunc Test(t *testing.T, ctx context.Context, listener jsonrpc2.Listener, framer jsonrpc2.Framer, noLeak bool) {\n\tif noLeak {\n\t\tstacktest.NoLeak(t)\n\t}\n\tserver := jsonrpc2.NewServer(ctx, listener, binder{framer, nil})\n\tdefer func() {\n\t\tlistener.Close()\n\t\tif noLeak {\n\t\t\tserver.Wait()\n\t\t}\n\t}()\n\tfor _, test := range callTests {\n\t\tt.Run(test.Name(), func(t *testing.T) {\n\t\t\tclient, err := jsonrpc2.Dial(ctx,\n\t\t\t\tlistener.Dialer(), binder{framer, func(h *handler) {\n\t\t\t\t\tdefer h.conn.Close()\n\t\t\t\t\tctx := context.Background()\n\t\t\t\t\ttest.Invoke(t, ctx, h)\n\t\t\t\t\tif call, ok := test.(*call); ok {\n\t\t\t\t\t\t// also run all simple call tests in echo mode\n\t\t\t\t\t\t(*echo)(call).Invoke(t, ctx, h)\n\t\t\t\t\t}\n\t\t\t\t}}, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tclient.Wait()\n\t\t})\n\t}\n}\n\nfunc (test notify) Name() string { return test.method }\nfunc (test notify) Invoke(t *testing.T, ctx context.Context, h *handler) {\n\tif err := h.conn.Notify(ctx, test.method, test.params); err != nil {\n\t\tt.Fatalf(\"%v:Notify failed: %v\", test.method, err)\n\t}\n}\n\nfunc (test call) Name() string { return test.method }\nfunc (test call) Invoke(t *testing.T, ctx context.Context, h *handler) {\n\tresults := newResults(test.expect)\n\tif err := h.conn.Call(ctx, test.method, test.params).Await(ctx, results); err != nil {\n\t\tt.Fatalf(\"%v:Call failed: %v\", test.method, err)\n\t}\n\tverifyResults(t, test.method, results, test.expect)\n}\n\nfunc (test echo) Invoke(t *testing.T, ctx context.Context, h *handler) {\n\tresults := newResults(test.expect)\n\tif err := h.conn.Call(ctx, \"echo\", []any{test.method, test.params}).Await(ctx, results); err != nil {\n\t\tt.Fatalf(\"%v:Echo failed: %v\", test.method, err)\n\t}\n\tverifyResults(t, test.method, results, test.expect)\n}\n\nfunc (test async) Name() string { return test.name }\nfunc (test async) Invoke(t *testing.T, ctx context.Context, h *handler) {\n\th.calls[test.name] = h.conn.Call(ctx, test.method, test.params)\n}\n\nfunc (test collect) Name() string { return test.name }\nfunc (test collect) Invoke(t *testing.T, ctx context.Context, h *handler) {\n\to := h.calls[test.name]\n\tresults := newResults(test.expect)\n\terr := o.Await(ctx, results)\n\tswitch {\n\tcase test.fails && err == nil:\n\t\tt.Fatalf(\"%v:Collect was supposed to fail\", test.name)\n\tcase !test.fails && err != nil:\n\t\tt.Fatalf(\"%v:Collect failed: %v\", test.name, err)\n\t}\n\tverifyResults(t, test.name, results, test.expect)\n}\n\nfunc (test cancel) Name() string { return test.name }\nfunc (test cancel) Invoke(t *testing.T, ctx context.Context, h *handler) {\n\to := h.calls[test.name]\n\tif err := h.conn.Notify(ctx, \"cancel\", &cancelParams{o.ID().Raw().(int64)}); err != nil {\n\t\tt.Fatalf(\"%v:Collect failed: %v\", test.name, err)\n\t}\n}\n\nfunc (test sequence) Name() string { return test.name }\nfunc (test sequence) Invoke(t *testing.T, ctx context.Context, h *handler) {\n\tfor _, child := range test.tests {\n\t\tchild.Invoke(t, ctx, h)\n\t}\n}\n\n// newResults makes a new empty copy of the expected type to put the results into\nfunc newResults(expect any) any {\n\tswitch e := expect.(type) {\n\tcase []any:\n\t\tvar r []any\n\t\tfor _, v := range e {\n\t\t\tr = append(r, reflect.New(reflect.TypeOf(v)).Interface())\n\t\t}\n\t\treturn r\n\tcase nil:\n\t\treturn nil\n\tdefault:\n\t\treturn reflect.New(reflect.TypeOf(expect)).Interface()\n\t}\n}\n\n// verifyResults compares the results to the expected values\nfunc verifyResults(t *testing.T, method string, results any, expect any) {\n\tif expect == nil {\n\t\tif results != nil {\n\t\t\tt.Errorf(\"%v:Got results %+v where none expeted\", method, expect)\n\t\t}\n\t\treturn\n\t}\n\tval := reflect.Indirect(reflect.ValueOf(results)).Interface()\n\tif !reflect.DeepEqual(val, expect) {\n\t\tt.Errorf(\"%v:Results are incorrect, got %+v expect %+v\", method, val, expect)\n\t}\n}\n\nfunc (b binder) Bind(ctx context.Context, conn *jsonrpc2.Connection) jsonrpc2.ConnectionOptions {\n\th := &handler{\n\t\tconn:    conn,\n\t\twaiters: make(map[string]chan struct{}),\n\t\tcalls:   make(map[string]*jsonrpc2.AsyncCall),\n\t}\n\tif b.runTest != nil {\n\t\tgo b.runTest(h)\n\t}\n\treturn jsonrpc2.ConnectionOptions{\n\t\tFramer:    b.framer,\n\t\tPreempter: h,\n\t\tHandler:   h,\n\t}\n}\n\nfunc (h *handler) waiter(name string) chan struct{} {\n\tlog.Println(\"waiter:\", name)\n\th.mutex.Lock()\n\tdefer h.mutex.Unlock()\n\twaiter := make(chan struct{})\n\th.waiters[name] = waiter\n\treturn waiter\n}\n\nfunc (h *handler) closeWaiter(name string) {\n\tlog.Println(\"closeWaiter:\", name)\n\tfor !h.tryCloseWaiter(name) {\n\t\ttime.Sleep(time.Millisecond)\n\t}\n}\n\nfunc (h *handler) tryCloseWaiter(name string) (ok bool) {\n\th.mutex.Lock()\n\tdefer h.mutex.Unlock()\n\twaiter, ok := h.waiters[name]\n\tif ok {\n\t\tdelete(h.waiters, name)\n\t\tclose(waiter)\n\t}\n\treturn\n}\n\nfunc (h *handler) Preempt(ctx context.Context, req *jsonrpc2.Request) (any, error) {\n\tswitch req.Method {\n\tcase \"unblock\":\n\t\tvar name string\n\t\tif err := json.Unmarshal(req.Params, &name); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\th.closeWaiter(name)\n\t\treturn nil, nil\n\tcase \"peek\":\n\t\tif len(req.Params) > 0 {\n\t\t\treturn nil, fmt.Errorf(\"%w: expected no params\", jsonrpc2.ErrInvalidParams)\n\t\t}\n\t\treturn h.accumulator, nil\n\tcase \"cancel\":\n\t\tvar params cancelParams\n\t\tif err := json.Unmarshal(req.Params, &params); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\th.conn.Cancel(jsonrpc2.Int64ID(params.ID))\n\t\treturn nil, nil\n\tdefault:\n\t\treturn nil, jsonrpc2.ErrNotHandled\n\t}\n}\n\nfunc (h *handler) Handle(ctx context.Context, req *jsonrpc2.Request) (any, error) {\n\tswitch req.Method {\n\tcase \"no_args\":\n\t\tif len(req.Params) > 0 {\n\t\t\treturn nil, fmt.Errorf(\"%w: expected no params\", jsonrpc2.ErrInvalidParams)\n\t\t}\n\t\treturn true, nil\n\tcase \"one_string\":\n\t\tvar v string\n\t\tif err := json.Unmarshal(req.Params, &v); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\treturn \"got:\" + v, nil\n\tcase \"one_number\":\n\t\tvar v int\n\t\tif err := json.Unmarshal(req.Params, &v); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\treturn fmt.Sprintf(\"got:%d\", v), nil\n\tcase \"set\":\n\t\tvar v int\n\t\tif err := json.Unmarshal(req.Params, &v); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\th.accumulator = v\n\t\treturn nil, nil\n\tcase \"add\":\n\t\tvar v int\n\t\tif err := json.Unmarshal(req.Params, &v); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\th.accumulator += v\n\t\treturn nil, nil\n\tcase \"get\":\n\t\tif len(req.Params) > 0 {\n\t\t\treturn nil, fmt.Errorf(\"%w: expected no params\", jsonrpc2.ErrInvalidParams)\n\t\t}\n\t\treturn h.accumulator, nil\n\tcase \"join\":\n\t\tvar v []string\n\t\tif err := json.Unmarshal(req.Params, &v); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\treturn path.Join(v...), nil\n\tcase \"echo\":\n\t\tvar v []any\n\t\tif err := json.Unmarshal(req.Params, &v); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\tvar result any\n\t\terr := h.conn.Call(ctx, v[0].(string), v[1]).Await(ctx, &result)\n\t\treturn result, err\n\tcase \"wait\":\n\t\tvar name string\n\t\tif err := json.Unmarshal(req.Params, &name); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\tselect {\n\t\tcase <-h.waiter(name):\n\t\t\treturn true, nil\n\t\tcase <-ctx.Done():\n\t\t\treturn nil, ctx.Err()\n\t\t}\n\tcase \"fork\":\n\t\tvar name string\n\t\tif err := json.Unmarshal(req.Params, &name); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s\", jsonrpc2.ErrParse, err)\n\t\t}\n\t\twaitFor := h.waiter(name)\n\t\tgo func() {\n\t\t\tselect {\n\t\t\tcase <-waitFor:\n\t\t\t\th.conn.Respond(req.ID, true, nil)\n\t\t\tcase <-ctx.Done():\n\t\t\t\th.conn.Respond(req.ID, nil, ctx.Err())\n\t\t\t}\n\t\t}()\n\t\treturn nil, jsonrpc2.ErrAsyncResponse\n\tdefault:\n\t\treturn nil, jsonrpc2.ErrNotHandled\n\t}\n}\n"
  },
  {
    "path": "x/jsonrpc2/jsonrpc2test/jsonrpc2_test.go",
    "content": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage jsonrpc2test_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/x/jsonrpc2\"\n\t\"github.com/goplus/xgo/x/jsonrpc2/jsonrpc2test\"\n\t\"github.com/goplus/xgo/x/jsonrpc2/jsonrpc2test/cases\"\n)\n\nfunc TestNetPipe(t *testing.T) {\n\tjsonrpc2.SetDebug(jsonrpc2.DbgFlagCall)\n\tctx := context.Background()\n\tlistener := jsonrpc2test.NetPipeListener()\n\tcases.Test(t, ctx, listener, jsonrpc2.HeaderFramer(), true)\n}\n"
  },
  {
    "path": "x/jsonrpc2/jsonrpc2test/pipe.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage jsonrpc2test\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/goplus/xgo/x/jsonrpc2\"\n)\n\n// NetPipeListener returns a new Listener that listens using net.Pipe.\n// It is only possibly to connect to it using the Dialer returned by the\n// Dialer method, each call to that method will generate a new pipe the other\n// side of which will be returned from the Accept call.\nfunc NetPipeListener() jsonrpc2.Listener {\n\treturn &netPiper{\n\t\tdone:   make(chan struct{}),\n\t\tdialed: make(chan io.ReadWriteCloser),\n\t}\n}\n\n// netPiper is the implementation of Listener build on top of net.Pipes.\ntype netPiper struct {\n\tdone   chan struct{}\n\tdialed chan io.ReadWriteCloser\n}\n\n// Accept blocks waiting for an incoming connection to the listener.\nfunc (l *netPiper) Accept(context.Context) (io.ReadWriteCloser, error) {\n\t// Block until the pipe is dialed or the listener is closed,\n\t// preferring the latter if already closed at the start of Accept.\n\tselect {\n\tcase <-l.done:\n\t\treturn nil, net.ErrClosed\n\tdefault:\n\t}\n\tselect {\n\tcase rwc := <-l.dialed:\n\t\treturn rwc, nil\n\tcase <-l.done:\n\t\treturn nil, net.ErrClosed\n\t}\n}\n\n// Close will cause the listener to stop listening. It will not close any connections that have\n// already been accepted.\nfunc (l *netPiper) Close() error {\n\t// unblock any accept calls that are pending\n\tclose(l.done)\n\treturn nil\n}\n\nfunc (l *netPiper) Dialer() jsonrpc2.Dialer {\n\treturn l\n}\n\nfunc (l *netPiper) Dial(ctx context.Context) (io.ReadWriteCloser, error) {\n\tclient, server := net.Pipe()\n\n\tselect {\n\tcase l.dialed <- server:\n\t\treturn client, nil\n\n\tcase <-l.done:\n\t\tclient.Close()\n\t\tserver.Close()\n\t\treturn nil, net.ErrClosed\n\t}\n}\n"
  },
  {
    "path": "x/jsonrpc2/jsonrpc2test/pipe_test.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jsonrpc2test\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"testing\"\n)\n\nfunc TestNetPipeDone(t *testing.T) {\n\tnp := &netPiper{\n\t\tdone:   make(chan struct{}, 1),\n\t\tdialed: make(chan io.ReadWriteCloser),\n\t}\n\tnp.done <- struct{}{}\n\tif f, err := np.Accept(context.Background()); f != nil || err != net.ErrClosed {\n\t\tt.Fatal(\"np.Accept:\", f, err)\n\t}\n\tnp.done <- struct{}{}\n\tif f, err := np.Dial(context.Background()); f != nil || err != net.ErrClosed {\n\t\tt.Fatal(\"np.Dial:\", f, err)\n\t}\n}\n"
  },
  {
    "path": "x/jsonrpc2/messages.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage jsonrpc2\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n)\n\n// ID is a Request identifier.\ntype ID struct {\n\tvalue any\n}\n\n// Message is the interface to all jsonrpc2 message types.\n// They share no common functionality, but are a closed set of concrete types\n// that are allowed to implement this interface. The message types are *Request\n// and *Response.\ntype Message interface {\n\t// marshal builds the wire form from the API form.\n\t// It is private, which makes the set of Message implementations closed.\n\tmarshal(to *wireCombined)\n}\n\n// Request is a Message sent to a peer to request behavior.\n// If it has an ID it is a call, otherwise it is a notification.\ntype Request struct {\n\t// ID of this request, used to tie the Response back to the request.\n\t// This will be nil for notifications.\n\tID ID\n\t// Method is a string containing the method name to invoke.\n\tMethod string\n\t// Params is either a struct or an array with the parameters of the method.\n\tParams json.RawMessage\n}\n\n// Response is a Message used as a reply to a call Request.\n// It will have the same ID as the call it is a response to.\ntype Response struct {\n\t// result is the content of the response.\n\tResult json.RawMessage\n\t// err is set only if the call failed.\n\tError error\n\t// id of the request this is a response to.\n\tID ID\n}\n\n// StringID creates a new string request identifier.\nfunc StringID(s string) ID { return ID{value: s} }\n\n// Int64ID creates a new integer request identifier.\nfunc Int64ID(i int64) ID { return ID{value: i} }\n\n// IsValid returns true if the ID is a valid identifier.\n// The default value for ID will return false.\nfunc (id ID) IsValid() bool { return id.value != nil }\n\n// Raw returns the underlying value of the ID.\nfunc (id ID) Raw() any { return id.value }\n\n// NewNotification constructs a new Notification message for the supplied\n// method and parameters.\nfunc NewNotification(method string, params any) (*Request, error) {\n\tp, merr := marshalToRaw(params)\n\treturn &Request{Method: method, Params: p}, merr\n}\n\n// NewCall constructs a new Call message for the supplied ID, method and\n// parameters.\nfunc NewCall(id ID, method string, params any) (*Request, error) {\n\tp, merr := marshalToRaw(params)\n\treturn &Request{ID: id, Method: method, Params: p}, merr\n}\n\nfunc (msg *Request) IsCall() bool { return msg.ID.IsValid() }\n\nfunc (msg *Request) marshal(to *wireCombined) {\n\tto.ID = msg.ID.value\n\tto.Method = msg.Method\n\tto.Params = msg.Params\n}\n\n// NewResponse constructs a new Response message that is a reply to the\n// supplied. If err is set result may be ignored.\nfunc NewResponse(id ID, result any, rerr error) (*Response, error) {\n\tr, merr := marshalToRaw(result)\n\treturn &Response{ID: id, Result: r, Error: rerr}, merr\n}\n\nfunc (msg *Response) marshal(to *wireCombined) {\n\tto.ID = msg.ID.value\n\tto.Error = toWireError(msg.Error)\n\tto.Result = msg.Result\n}\n\nfunc toWireError(err error) *wireError {\n\tif err == nil {\n\t\t// no error, the response is complete\n\t\treturn nil\n\t}\n\tif err, ok := err.(*wireError); ok {\n\t\t// already a wire error, just use it\n\t\treturn err\n\t}\n\tresult := &wireError{Message: err.Error()}\n\tvar wrapped *wireError\n\tif errors.As(err, &wrapped) {\n\t\t// if we wrapped a wire error, keep the code from the wrapped error\n\t\t// but the message from the outer error\n\t\tresult.Code = wrapped.Code\n\t}\n\treturn result\n}\n\nfunc EncodeMessage(msg Message) ([]byte, error) {\n\twire := wireCombined{VersionTag: wireVersion}\n\tmsg.marshal(&wire)\n\tdata, err := json.Marshal(&wire)\n\tif err != nil {\n\t\treturn data, fmt.Errorf(\"marshaling jsonrpc message: %w\", err)\n\t}\n\treturn data, nil\n}\n\nfunc DecodeMessage(data []byte) (Message, error) {\n\tmsg := wireCombined{}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshaling jsonrpc message: %w\", err)\n\t}\n\tif msg.VersionTag != wireVersion {\n\t\treturn nil, fmt.Errorf(\"invalid message version tag %s expected %s\", msg.VersionTag, wireVersion)\n\t}\n\tid := ID{}\n\tswitch v := msg.ID.(type) {\n\tcase nil:\n\tcase float64:\n\t\t// coerce the id type to int64 if it is float64, the spec does not allow fractional parts\n\t\tid = Int64ID(int64(v))\n\tcase int64:\n\t\tid = Int64ID(v)\n\tcase string:\n\t\tid = StringID(v)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"invalid message id type <%T>%v\", v, v)\n\t}\n\tif msg.Method != \"\" {\n\t\t// has a method, must be a call\n\t\treturn &Request{\n\t\t\tMethod: msg.Method,\n\t\t\tID:     id,\n\t\t\tParams: msg.Params,\n\t\t}, nil\n\t}\n\t// no method, should be a response\n\tif !id.IsValid() {\n\t\treturn nil, ErrInvalidRequest\n\t}\n\tresp := &Response{\n\t\tID:     id,\n\t\tResult: msg.Result,\n\t}\n\t// we have to check if msg.Error is nil to avoid a typed error\n\tif msg.Error != nil {\n\t\tresp.Error = msg.Error\n\t}\n\treturn resp, nil\n}\n\nfunc marshalToRaw(obj any) (json.RawMessage, error) {\n\tif obj == nil {\n\t\treturn nil, nil\n\t}\n\tdata, err := json.Marshal(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn json.RawMessage(data), nil\n}\n"
  },
  {
    "path": "x/jsonrpc2/serve.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Copyright 2020 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage jsonrpc2\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\n// Listener is implemented by protocols to accept new inbound connections.\ntype Listener interface {\n\t// Accept accepts an inbound connection to a server.\n\t// It blocks until either an inbound connection is made, or the listener is closed.\n\tAccept(context.Context) (io.ReadWriteCloser, error)\n\n\t// Close closes the listener.\n\t// Any blocked Accept or Dial operations will unblock and return errors.\n\tClose() error\n\n\t// Dialer returns a dialer that can be used to connect to this listener\n\t// locally.\n\t// If a listener does not implement this it will return nil.\n\tDialer() Dialer\n}\n\n// Dialer is used by clients to dial a server.\ntype Dialer interface {\n\t// Dial returns a new communication byte stream to a listening server.\n\tDial(ctx context.Context) (io.ReadWriteCloser, error)\n}\n\n// Server is a running server that is accepting incoming connections.\ntype Server struct {\n\tlistener Listener\n\tbinder   Binder\n\tasync    *async\n\n\tshutdownOnce sync.Once\n\tclosing      int32 // atomic: set to nonzero when Shutdown is called\n}\n\n// Dial uses the dialer to make a new connection, wraps the returned\n// reader and writer using the framer to make a stream, and then builds\n// a connection on top of that stream using the binder.\n//\n// The returned Connection will operate independently using the Preempter and/or\n// Handler provided by the Binder, and will release its own resources when the\n// connection is broken, but the caller may Close it earlier to stop accepting\n// (or sending) new requests.\nfunc Dial(ctx context.Context, dialer Dialer, binder Binder, onDone func()) (*Connection, error) {\n\t// dial a server\n\trwc, err := dialer.Dial(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn newConnection(ctx, rwc, binder, onDone), nil\n}\n\n// NewServer starts a new server listening for incoming connections and returns\n// it.\n// This returns a fully running and connected server, it does not block on\n// the listener.\n// You can call Wait to block on the server, or Shutdown to get the sever to\n// terminate gracefully.\n// To notice incoming connections, use an intercepting Binder.\nfunc NewServer(ctx context.Context, listener Listener, binder Binder) *Server {\n\tserver := &Server{\n\t\tlistener: listener,\n\t\tbinder:   binder,\n\t\tasync:    newAsync(),\n\t}\n\tgo server.run(ctx)\n\treturn server\n}\n\n// Wait returns only when the server has shut down.\nfunc (s *Server) Wait() (err error) {\n\tif Verbose {\n\t\tlog.Println(\"==> Server.Wait start\")\n\t}\n\terr = s.async.wait()\n\tif Verbose {\n\t\tlog.Println(\"==> Server.Wait end:\", err)\n\t}\n\treturn\n}\n\n// Shutdown informs the server to stop accepting new connections.\nfunc (s *Server) Shutdown() {\n\ts.shutdownOnce.Do(func() {\n\t\tatomic.StoreInt32(&s.closing, 1)\n\t\ts.listener.Close()\n\t})\n}\n\n// run accepts incoming connections from the listener,\n// If IdleTimeout is non-zero, run exits after there are no clients for this\n// duration, otherwise it exits only on error.\nfunc (s *Server) run(ctx context.Context) {\n\tdefer s.async.done()\n\n\tvar activeConns sync.WaitGroup\n\tfor {\n\t\trwc, err := s.listener.Accept(ctx)\n\t\tif err != nil {\n\t\t\t// Only Shutdown closes the listener. If we get an error after Shutdown is\n\t\t\t// called, assume that was the cause and don't report the error;\n\t\t\t// otherwise, report the error in case it is unexpected.\n\t\t\tif atomic.LoadInt32(&s.closing) == 0 {\n\t\t\t\ts.async.setError(err)\n\t\t\t}\n\t\t\t// We are done generating new connections for good.\n\t\t\tbreak\n\t\t}\n\n\t\t// A new inbound connection.\n\t\tactiveConns.Add(1)\n\t\t_ = newConnection(ctx, rwc, s.binder, activeConns.Done) // unregisters itself when done\n\t}\n\tactiveConns.Wait()\n}\n\n// NewIdleListener wraps a listener with an idle timeout.\n//\n// When there are no active connections for at least the timeout duration,\n// calls to Accept will fail with ErrIdleTimeout.\n//\n// A connection is considered inactive as soon as its Close method is called.\nfunc NewIdleListener(timeout time.Duration, wrap Listener) Listener {\n\tl := &idleListener{\n\t\twrapped:   wrap,\n\t\ttimeout:   timeout,\n\t\tactive:    make(chan int, 1),\n\t\ttimedOut:  make(chan struct{}),\n\t\tidleTimer: make(chan *time.Timer, 1),\n\t}\n\tl.idleTimer <- time.AfterFunc(l.timeout, l.timerExpired)\n\treturn l\n}\n\ntype idleListener struct {\n\twrapped Listener\n\ttimeout time.Duration\n\n\t// Only one of these channels is receivable at any given time.\n\tactive    chan int         // count of active connections; closed when Close is called if not timed out\n\ttimedOut  chan struct{}    // closed when the idle timer expires\n\tidleTimer chan *time.Timer // holds the timer only when idle\n}\n\n// Accept accepts an incoming connection.\n//\n// If an incoming connection is accepted concurrent to the listener being closed\n// due to idleness, the new connection is immediately closed.\nfunc (l *idleListener) Accept(ctx context.Context) (io.ReadWriteCloser, error) {\n\trwc, err := l.wrapped.Accept(ctx)\n\n\tselect {\n\tcase n, ok := <-l.active:\n\t\tif err != nil {\n\t\t\tif ok {\n\t\t\t\tl.active <- n\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\t\tif ok {\n\t\t\tl.active <- n + 1\n\t\t} else {\n\t\t\t// l.wrapped.Close Close has been called, but Accept returned a\n\t\t\t// connection. This race can occur with concurrent Accept and Close calls\n\t\t\t// with any net.Listener, and it is benign: since the listener was closed\n\t\t\t// explicitly, it can't have also timed out.\n\t\t\t_ = 0\n\t\t}\n\t\treturn l.newConn(rwc), nil\n\n\tcase <-l.timedOut:\n\t\tif err == nil {\n\t\t\t// Keeping the connection open would leave the listener simultaneously\n\t\t\t// active and closed due to idleness, which would be contradictory and\n\t\t\t// confusing. Close the connection and pretend that it never happened.\n\t\t\trwc.Close()\n\t\t} else {\n\t\t\t// In theory the timeout could have raced with an unrelated error return\n\t\t\t// from Accept. However, ErrIdleTimeout is arguably still valid (since we\n\t\t\t// would have closed due to the timeout independent of the error), and the\n\t\t\t// harm from returning a spurious ErrIdleTimeout is negligible anyway.\n\t\t\t_ = 0\n\t\t}\n\t\treturn nil, ErrIdleTimeout\n\n\tcase timer := <-l.idleTimer:\n\t\tif err != nil {\n\t\t\t// The idle timer doesn't run until it receives itself from the idleTimer\n\t\t\t// channel, so it can't have called l.wrapped.Close yet and thus err can't\n\t\t\t// be ErrIdleTimeout. Leave the idle timer as it was and return whatever\n\t\t\t// error we got.\n\t\t\tl.idleTimer <- timer\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif !timer.Stop() {\n\t\t\t// Failed to stop the timer — the timer goroutine is in the process of\n\t\t\t// firing. Send the timer back to the timer goroutine so that it can\n\t\t\t// safely close the timedOut channel, and then wait for the listener to\n\t\t\t// actually be closed before we return ErrIdleTimeout.\n\t\t\tl.idleTimer <- timer\n\t\t\trwc.Close()\n\t\t\t<-l.timedOut\n\t\t\treturn nil, ErrIdleTimeout\n\t\t}\n\n\t\tl.active <- 1\n\t\treturn l.newConn(rwc), nil\n\t}\n}\n\nfunc (l *idleListener) Close() error {\n\tselect {\n\tcase _, ok := <-l.active:\n\t\tif ok {\n\t\t\tclose(l.active)\n\t\t}\n\n\tcase <-l.timedOut:\n\t\t// Already closed by the timer; take care not to double-close if the caller\n\t\t// only explicitly invokes this Close method once, since the io.Closer\n\t\t// interface explicitly leaves doubled Close calls undefined.\n\t\treturn ErrIdleTimeout\n\n\tcase timer := <-l.idleTimer:\n\t\tif !timer.Stop() {\n\t\t\t// Couldn't stop the timer. It shouldn't take long to run, so just wait\n\t\t\t// (so that the Listener is guaranteed to be closed before we return)\n\t\t\t// and pretend that this call happened afterward.\n\t\t\t// That way we won't leak any timers or goroutines when Close returns.\n\t\t\tl.idleTimer <- timer\n\t\t\t<-l.timedOut\n\t\t\treturn ErrIdleTimeout\n\t\t}\n\t\tclose(l.active)\n\t}\n\n\treturn l.wrapped.Close()\n}\n\nfunc (l *idleListener) Dialer() Dialer {\n\treturn l.wrapped.Dialer()\n}\n\nfunc (l *idleListener) timerExpired() {\n\tselect {\n\tcase n, ok := <-l.active:\n\t\tif ok {\n\t\t\tpanic(fmt.Sprintf(\"jsonrpc2: idleListener idle timer fired with %d connections still active\", n))\n\t\t} else {\n\t\t\tpanic(\"jsonrpc2: Close finished with idle timer still running\")\n\t\t}\n\n\tcase <-l.timedOut:\n\t\tpanic(\"jsonrpc2: idleListener idle timer fired more than once\")\n\n\tcase <-l.idleTimer:\n\t\t// The timer for this very call!\n\t}\n\n\t// Close the Listener with all channels still blocked to ensure that this call\n\t// to l.wrapped.Close doesn't race with the one in l.Close.\n\tdefer close(l.timedOut)\n\tl.wrapped.Close()\n}\n\nfunc (l *idleListener) connClosed() {\n\tselect {\n\tcase n, ok := <-l.active:\n\t\tif !ok {\n\t\t\t// l is already closed, so it can't close due to idleness,\n\t\t\t// and we don't need to track the number of active connections any more.\n\t\t\treturn\n\t\t}\n\t\tn--\n\t\tif n == 0 {\n\t\t\tl.idleTimer <- time.AfterFunc(l.timeout, l.timerExpired)\n\t\t} else {\n\t\t\tl.active <- n\n\t\t}\n\n\tcase <-l.timedOut:\n\t\tpanic(\"jsonrpc2: idleListener idle timer fired before last active connection was closed\")\n\n\tcase <-l.idleTimer:\n\t\tpanic(\"jsonrpc2: idleListener idle timer active before last active connection was closed\")\n\t}\n}\n\ntype idleListenerConn struct {\n\twrapped   io.ReadWriteCloser\n\tl         *idleListener\n\tcloseOnce sync.Once\n}\n\nfunc (l *idleListener) newConn(rwc io.ReadWriteCloser) *idleListenerConn {\n\tc := &idleListenerConn{\n\t\twrapped: rwc,\n\t\tl:       l,\n\t}\n\n\t// A caller that forgets to call Close may disrupt the idleListener's\n\t// accounting, even though the file descriptor for the underlying connection\n\t// may eventually be garbage-collected anyway.\n\t//\n\t// Set a (best-effort) finalizer to verify that a Close call always occurs.\n\t// (We will clear the finalizer explicitly in Close.)\n\truntime.SetFinalizer(c, func(c *idleListenerConn) {\n\t\tpanic(\"jsonrpc2: IdleListener connection became unreachable without a call to Close\")\n\t})\n\n\treturn c\n}\n\nfunc (c *idleListenerConn) Read(p []byte) (int, error)  { return c.wrapped.Read(p) }\nfunc (c *idleListenerConn) Write(p []byte) (int, error) { return c.wrapped.Write(p) }\n\nfunc (c *idleListenerConn) Close() error {\n\tdefer c.closeOnce.Do(func() {\n\t\tc.l.connClosed()\n\t\truntime.SetFinalizer(c, nil)\n\t})\n\treturn c.wrapped.Close()\n}\n"
  },
  {
    "path": "x/jsonrpc2/stdio/server.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage stdio\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"sync/atomic\"\n\n\t\"github.com/goplus/xgo/x/fakenet\"\n\t\"github.com/goplus/xgo/x/jsonrpc2\"\n\t\"github.com/goplus/xgo/x/jsonrpc2/jsonrpc2test\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype listener struct {\n}\n\nfunc (p *listener) Close() error {\n\treturn nil\n}\n\n// Accept blocks waiting for an incoming connection to the listener.\nfunc (p *listener) Accept(context.Context) (io.ReadWriteCloser, error) {\n\tconnCnt := &connCnt[server]\n\tif atomic.AddInt32(connCnt, 1) != 1 {\n\t\tatomic.AddInt32(connCnt, -1)\n\t\treturn nil, ErrTooManyConnections\n\t}\n\treturn fakenet.NewConn(\"stdio\", os.Stdin, os.Stdout), nil\n}\n\nfunc (p *listener) Dialer() jsonrpc2.Dialer {\n\treturn nil\n}\n\n// Listener returns a jsonrpc2.Listener based on stdin and stdout.\nfunc Listener(allowLocalDail bool) jsonrpc2.Listener {\n\tif allowLocalDail {\n\t\treturn jsonrpc2test.NetPipeListener()\n\t}\n\treturn &listener{}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/jsonrpc2/stdio/stdio.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage stdio\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"sync/atomic\"\n\n\t\"github.com/goplus/xgo/x/fakenet\"\n\t\"github.com/goplus/xgo/x/jsonrpc2\"\n)\n\nvar (\n\tErrTooManyConnections = errors.New(\"too many connections\")\n)\n\nconst (\n\tclient = iota\n\tserver\n)\n\nvar (\n\tconnCnt [2]int32\n)\n\n// -----------------------------------------------------------------------------\n\ntype dialer struct {\n\tin  io.ReadCloser\n\tout io.WriteCloser\n}\n\n// Dial returns a new communication byte stream to a listening server.\nfunc (p *dialer) Dial(ctx context.Context) (io.ReadWriteCloser, error) {\n\tdailCnt := &connCnt[client]\n\tif atomic.AddInt32(dailCnt, 1) != 1 {\n\t\tatomic.AddInt32(dailCnt, -1)\n\t\treturn nil, ErrTooManyConnections\n\t}\n\treturn fakenet.NewConn(\"stdio.dialer\", p.in, p.out), nil\n}\n\n// Dialer returns a jsonrpc2.Dialer based on in and out.\nfunc Dialer(in io.ReadCloser, out io.WriteCloser) jsonrpc2.Dialer {\n\treturn &dialer{in: in, out: out}\n}\n\n// Dial makes a new connection based on in and out, wraps the returned\n// reader and writer using the framer to make a stream, and then builds a\n// connection on top of that stream using the binder.\n//\n// The returned Connection will operate independently using the Preempter and/or\n// Handler provided by the Binder, and will release its own resources when the\n// connection is broken, but the caller may Close it earlier to stop accepting\n// (or sending) new requests.\nfunc Dial(in io.ReadCloser, out io.WriteCloser, binder jsonrpc2.Binder, onDone func()) (*jsonrpc2.Connection, error) {\n\treturn jsonrpc2.Dial(context.Background(), Dialer(in, out), binder, onDone)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/jsonrpc2/wire.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage jsonrpc2\n\nimport (\n\t\"encoding/json\"\n)\n\n// This file contains the go forms of the wire specification.\n// see http://www.jsonrpc.org/specification for details\n\nvar (\n\t// ErrParse is used when invalid JSON was received by the server.\n\tErrParse = NewError(-32700, \"JSON RPC parse error\")\n\t// ErrInvalidRequest is used when the JSON sent is not a valid Request object.\n\tErrInvalidRequest = NewError(-32600, \"JSON RPC invalid request\")\n\t// ErrMethodNotFound should be returned by the handler when the method does\n\t// not exist / is not available.\n\tErrMethodNotFound = NewError(-32601, \"JSON RPC method not found\")\n\t// ErrInvalidParams should be returned by the handler when method\n\t// parameter(s) were invalid.\n\tErrInvalidParams = NewError(-32602, \"JSON RPC invalid params\")\n\t// ErrInternal indicates a failure to process a call correctly\n\tErrInternal = NewError(-32603, \"JSON RPC internal error\")\n\n\t// The following errors are not part of the json specification, but\n\t// compliant extensions specific to this implementation.\n\n\t// ErrServerOverloaded is returned when a message was refused due to a\n\t// server being temporarily unable to accept any new messages.\n\tErrServerOverloaded = NewError(-32000, \"JSON RPC overloaded\")\n\t// ErrUnknown should be used for all non coded errors.\n\tErrUnknown = NewError(-32001, \"JSON RPC unknown error\")\n\t// ErrServerClosing is returned for calls that arrive while the server is closing.\n\tErrServerClosing = NewError(-32002, \"JSON RPC server is closing\")\n\t// ErrClientClosing is a dummy error returned for calls initiated while the client is closing.\n\tErrClientClosing = NewError(-32003, \"JSON RPC client is closing\")\n)\n\nconst wireVersion = \"2.0\"\n\n// wireCombined has all the fields of both Request and Response.\n// We can decode this and then work out which it is.\ntype wireCombined struct {\n\tVersionTag string          `json:\"jsonrpc\"`\n\tID         any             `json:\"id,omitempty\"`\n\tMethod     string          `json:\"method,omitempty\"`\n\tParams     json.RawMessage `json:\"params,omitempty\"`\n\tResult     json.RawMessage `json:\"result,omitempty\"`\n\tError      *wireError      `json:\"error,omitempty\"`\n}\n\n// wireError represents a structured error in a Response.\ntype wireError struct {\n\t// Code is an error code indicating the type of failure.\n\tCode int64 `json:\"code\"`\n\t// Message is a short description of the error.\n\tMessage string `json:\"message\"`\n\t// Data is optional structured data containing additional information about the error.\n\tData json.RawMessage `json:\"data,omitempty\"`\n}\n\n// NewError returns an error that will encode on the wire correctly.\n// The standard codes are made available from this package, this function should\n// only be used to build errors for application specific codes as allowed by the\n// specification.\nfunc NewError(code int64, message string) error {\n\treturn &wireError{\n\t\tCode:    code,\n\t\tMessage: message,\n\t}\n}\n\nfunc (err *wireError) Error() string {\n\treturn err.Message\n}\n\nfunc (err *wireError) Is(other error) bool {\n\tw, ok := other.(*wireError)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn err.Code == w.Code\n}\n"
  },
  {
    "path": "x/langserver/client.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage langserver\n\nimport (\n\t\"context\"\n\n\t\"github.com/goplus/xgo/x/jsonrpc2\"\n)\n\n// -----------------------------------------------------------------------------\n\nconst (\n\tmethodGenGo   = \"gengo\"\n\tmethodChanged = \"changed\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Dialer is used by clients to dial a server.\ntype Dialer = jsonrpc2.Dialer\n\ntype AsyncCall = jsonrpc2.AsyncCall\n\n// Client is a client of the language server.\ntype Client struct {\n\tconn *jsonrpc2.Connection\n}\n\n// Open uses the dialer to make a new connection and returns a client of the LangServer\n// based on the connection.\nfunc Open(ctx context.Context, dialer Dialer, onDone func()) (ret Client, err error) {\n\tc, err := jsonrpc2.Dial(ctx, dialer, jsonrpc2.BinderFunc(\n\t\tfunc(ctx context.Context, c *jsonrpc2.Connection) (ret jsonrpc2.ConnectionOptions) {\n\t\t\treturn\n\t\t}), onDone)\n\tif err != nil {\n\t\treturn\n\t}\n\tret = Client{c}\n\treturn\n}\n\nfunc (p Client) Close() error {\n\treturn p.conn.Close()\n}\n\nfunc (p Client) AsyncGenGo(ctx context.Context, pattern ...string) *AsyncCall {\n\treturn p.conn.Call(ctx, methodGenGo, pattern)\n}\n\nfunc (p Client) GenGo(ctx context.Context, pattern ...string) (err error) {\n\treturn p.AsyncGenGo(ctx, pattern...).Await(ctx, nil)\n}\n\nfunc (p Client) Changed(ctx context.Context, files ...string) (err error) {\n\treturn p.conn.Notify(ctx, methodChanged, files)\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/langserver/serve_dial.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage langserver\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"io/fs\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/goplus/xgo/x/jsonrpc2/stdio\"\n)\n\nfunc fatal(err error) {\n\tlog.Fatalln(err)\n}\n\n// -----------------------------------------------------------------------------\n\n// ServeAndDialConfig represents the configuration of ServeAndDial.\ntype ServeAndDialConfig struct {\n\t// OnError is to customize how to process errors (optional).\n\t// It should panic in any case.\n\tOnError func(err error)\n}\n\nconst (\n\tlogPrefix = \"serve-\"\n\tlogSuffix = \".log\"\n)\n\nfunc logFileOf(xgoDir string, pid int) string {\n\treturn xgoDir + logPrefix + strconv.Itoa(pid) + logSuffix\n}\n\nfunc isLog(fname string) bool {\n\treturn strings.HasSuffix(fname, logSuffix) && strings.HasPrefix(fname, logPrefix)\n}\n\nfunc pidByName(fname string) int {\n\tpidText := fname[len(logPrefix) : len(fname)-len(logSuffix)]\n\tif ret, err := strconv.ParseInt(pidText, 10, 0); err == nil {\n\t\treturn int(ret)\n\t}\n\treturn -1\n}\n\nfunc killByPid(pid int) {\n\tif pid < 0 {\n\t\treturn\n\t}\n\tif proc, err := os.FindProcess(pid); err == nil {\n\t\tproc.Kill()\n\t}\n}\n\nfunc tooOld(d fs.DirEntry) bool {\n\tif fi, e := d.Info(); e == nil {\n\t\tlastHour := time.Now().Add(-time.Hour)\n\t\treturn fi.ModTime().Before(lastHour)\n\t}\n\treturn false\n}\n\n// ServeAndDial executes a command as a LangServer, makes a new connection to it\n// and returns a client of the LangServer based on the connection.\nfunc ServeAndDial(conf *ServeAndDialConfig, xgoCmd string, args ...string) Client {\n\tif conf == nil {\n\t\tconf = new(ServeAndDialConfig)\n\t}\n\tonErr := conf.OnError\n\tif onErr == nil {\n\t\tonErr = fatal\n\t}\n\n\thome, err := os.UserHomeDir()\n\tif err != nil {\n\t\tonErr(err)\n\t}\n\txgoDir := home + \"/.xgo/\"\n\terr = os.MkdirAll(xgoDir, 0755)\n\tif err != nil {\n\t\tonErr(err)\n\t}\n\n\t// logFile is where the LangServer application log saves to.\n\t// default is ~/.xgo/serve-{pid}.log\n\tlogFile := logFileOf(xgoDir, os.Getpid())\n\n\t// clean too old logfiles, and kill old LangServer processes\n\tgo func() {\n\t\tif fis, e := os.ReadDir(xgoDir); e == nil {\n\t\t\tfor _, fi := range fis {\n\t\t\t\tif fi.IsDir() {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif fname := fi.Name(); isLog(fname) && tooOld(fi) {\n\t\t\t\t\tos.Remove(xgoDir + fname)\n\t\t\t\t\tkillByPid(pidByName(fname))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\tin, w := io.Pipe()\n\tr, out := io.Pipe()\n\n\tvar cmd *exec.Cmd\n\tgo func() {\n\t\tdefer r.Close()\n\t\tdefer w.Close()\n\n\t\tf, err := os.Create(logFile)\n\t\tif err != nil {\n\t\t\tonErr(err)\n\t\t}\n\t\tdefer f.Close()\n\n\t\tcmd = exec.Command(xgoCmd, args...)\n\t\tcmd.Stdin = r\n\t\tcmd.Stdout = w\n\t\tcmd.Stderr = f\n\t\terr = cmd.Start()\n\t\tif err != nil {\n\t\t\tonErr(err)\n\t\t}\n\n\t\tnewLogFile := logFileOf(xgoDir, cmd.Process.Pid)\n\t\tos.Rename(logFile, newLogFile)\n\n\t\tcmd.Wait()\n\t}()\n\n\tc, err := Open(context.Background(), stdio.Dialer(in, out), func() {\n\t\tif cmd == nil {\n\t\t\treturn\n\t\t}\n\t\tif proc := cmd.Process; proc != nil {\n\t\t\tlog.Println(\"==> ServeAndDial: kill\", proc.Pid, xgoCmd, args)\n\t\t\tproc.Kill()\n\t\t}\n\t})\n\tif err != nil {\n\t\tonErr(err)\n\t}\n\treturn c\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/langserver/server.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage langserver\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"path/filepath\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/jsonrpc2\"\n\t\"github.com/goplus/xgo/x/xgoprojs\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Listener is implemented by protocols to accept new inbound connections.\ntype Listener = jsonrpc2.Listener\n\n// Server is a running server that is accepting incoming connections.\ntype Server = jsonrpc2.Server\n\n// Config holds the options for new connections.\ntype Config struct {\n\t// Framer allows control over the message framing and encoding.\n\t// If nil, HeaderFramer will be used.\n\tFramer jsonrpc2.Framer\n}\n\n// NewServer creates a new LangServer and returns it.\nfunc NewServer(ctx context.Context, listener Listener, conf *Config) (ret *Server) {\n\th := newHandle()\n\tret = jsonrpc2.NewServer(ctx, listener, jsonrpc2.BinderFunc(\n\t\tfunc(ctx context.Context, c *jsonrpc2.Connection) (ret jsonrpc2.ConnectionOptions) {\n\t\t\tif conf != nil {\n\t\t\t\tret.Framer = conf.Framer\n\t\t\t}\n\t\t\tret.Handler = h\n\t\t\t// ret.OnInternalError = h.OnInternalError\n\t\t\treturn\n\t\t}))\n\th.server = ret\n\tgo h.runLoop()\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\ntype none = struct{}\n\ntype handler struct {\n\tmutex sync.Mutex\n\tdirty map[string]none\n\n\tserver *Server\n}\n\nfunc newHandle() *handler {\n\treturn &handler{\n\t\tdirty: make(map[string]none),\n\t}\n}\n\n/*\nfunc (p *handler) OnInternalError(err error) {\n\tpanic(\"jsonrpc2: \" + err.Error())\n}\n*/\n\nfunc (p *handler) runLoop() {\n\tconst (\n\t\tduration = time.Second / 100\n\t)\n\tfor {\n\t\tvar dir string\n\t\tp.mutex.Lock()\n\t\tfor dir = range p.dirty {\n\t\t\tdelete(p.dirty, dir)\n\t\t\tbreak\n\t\t}\n\t\tp.mutex.Unlock()\n\t\tif dir == \"\" {\n\t\t\ttime.Sleep(duration)\n\t\t\tcontinue\n\t\t}\n\t\ttool.GenGoEx(dir, nil, true, tool.GenFlagPrompt)\n\t}\n}\n\nfunc (p *handler) Changed(files []string) {\n\tp.mutex.Lock()\n\tdefer p.mutex.Unlock()\n\n\tfor _, file := range files {\n\t\tdir := filepath.Dir(file)\n\t\tp.dirty[dir] = none{}\n\t}\n}\n\nfunc (p *handler) Handle(ctx context.Context, req *jsonrpc2.Request) (result any, err error) {\n\tswitch req.Method {\n\tcase methodChanged:\n\t\tvar files []string\n\t\terr = json.Unmarshal(req.Params, &files)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tp.Changed(files)\n\tcase methodGenGo:\n\t\tvar pattern []string\n\t\terr = json.Unmarshal(req.Params, &pattern)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = GenGo(pattern...)\n\t}\n\treturn\n}\n\nfunc GenGo(pattern ...string) (err error) {\n\tprojs, err := xgoprojs.ParseAll(pattern...)\n\tif err != nil {\n\t\treturn\n\t}\n\tconf, _ := tool.NewDefaultConf(\".\", 0)\n\tif conf != nil {\n\t\tdefer conf.UpdateCache()\n\t}\n\tfor _, proj := range projs {\n\t\tswitch v := proj.(type) {\n\t\tcase *xgoprojs.DirProj:\n\t\t\ttool.GenGoEx(v.Dir, conf, true, 0)\n\t\tcase *xgoprojs.PkgPathProj:\n\t\t\tif v.Path == \"builtin\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttool.GenGoPkgPathEx(\"\", v.Path, conf, true, 0)\n\t\t}\n\t}\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/typesutil/api.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\nimport (\n\t\"go/types\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// A CheckConfig specifies the configuration for type checking.\n// The zero value for Config is a ready-to-use default configuration.\ntype CheckConfig types.Config\n\n// Check type-checks a package and returns the resulting package object and\n// the first error if any. Additionally, if info != nil, Check populates each\n// of the non-nil maps in the Info struct.\n//\n// The package is marked as complete if no errors occurred, otherwise it is\n// incomplete. See Config.Error for controlling behavior in the presence of\n// errors.\n//\n// The package is specified by a list of *ast.Files and corresponding\n// file set, and the package path the package is identified with.\n// The clean path must not be empty or dot (\".\").\nfunc (conf *CheckConfig) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (ret *types.Package, err error) {\n\tret = types.NewPackage(path, \"\")\n\tc := NewChecker((*types.Config)(conf), &Config{Types: ret, Fset: fset}, nil, info)\n\terr = c.Files(nil, files)\n\treturn\n}\n"
  },
  {
    "path": "x/typesutil/builtin_test.go",
    "content": "package typesutil\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/goplus/gogen\"\n)\n\nfunc TestConvErr(t *testing.T) {\n\te := errors.New(\"foo\")\n\tif ret, ok := convErr(nil, &gogen.ImportError{Err: e}); !ok || ret.Msg != \"foo\" {\n\t\tt.Fatal(\"convErr:\", ret, ok)\n\t}\n\tif _, ok := convErr(nil, e); ok {\n\t\tt.Fatal(\"convErr: ok?\")\n\t}\n}\n"
  },
  {
    "path": "x/typesutil/check.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\nimport (\n\tgoast \"go/ast\"\n\t\"go/types\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/mod/xgomod\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/goplus/xgo/x/typesutil/internal/typesutil\"\n\t\"github.com/qiniu/x/errors\"\n\t\"github.com/qiniu/x/log\"\n)\n\n// -----------------------------------------------------------------------------\n\ntype dbgFlags int\n\nconst (\n\tDbgFlagVerbose dbgFlags = 1 << iota\n\tDbgFlagPrintError\n\tDbgFlagDisableRecover\n\tDbgFlagDefault = DbgFlagVerbose | DbgFlagPrintError\n\tDbgFlagAll     = DbgFlagDefault | DbgFlagDisableRecover\n)\n\nvar (\n\tdebugVerbose  bool\n\tdebugPrintErr bool\n)\n\nfunc SetDebug(flags dbgFlags) {\n\tdebugVerbose = (flags & DbgFlagVerbose) != 0\n\tdebugPrintErr = (flags & DbgFlagPrintError) != 0\n\tif (flags & DbgFlagDisableRecover) != 0 {\n\t\tcl.SetDisableRecover(true)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\ntype Project = cl.Project\n\ntype Config struct {\n\t// Types provides type information for the package (required).\n\tTypes *types.Package\n\n\t// Fset provides source position information for syntax trees and types (required).\n\tFset *token.FileSet\n\n\t// WorkingDir is the directory in which to run xgo compiler (optional).\n\t// If WorkingDir is not set, os.Getwd() is used.\n\tWorkingDir string\n\n\t// Mod represents an XGo module (optional).\n\tMod *xgomod.Module\n\n\t// If IgnoreFuncBodies is set, skip compiling function bodies (optional).\n\tIgnoreFuncBodies bool\n\n\t// If UpdateGoTypesOverload is set, update go types overload data (optional).\n\tUpdateGoTypesOverload bool\n}\n\n// A Checker maintains the state of the type checker.\n// It must be created with NewChecker.\ntype Checker struct {\n\tconf    *types.Config\n\topts    *Config\n\tgoInfo  *types.Info\n\txgoInfo *Info\n}\n\n// NewChecker returns a new Checker instance for a given package.\n// Package files may be added incrementally via checker.Files.\nfunc NewChecker(conf *types.Config, opts *Config, goInfo *types.Info, xgoInfo *Info) *Checker {\n\treturn &Checker{conf, opts, goInfo, xgoInfo}\n}\n\n// Files checks the provided files as part of the checker's package.\nfunc (p *Checker) Files(goFiles []*goast.File, xgoFiles []*ast.File) (err error) {\n\topts := p.opts\n\tpkgTypes := opts.Types\n\tfset := opts.Fset\n\tconf := p.conf\n\n\t// Save original Error handler and restore it on function exit\n\torigError := conf.Error\n\tdefer func() {\n\t\tconf.Error = origError\n\t}()\n\n\tif len(xgoFiles) == 0 {\n\t\tonErr := conf.Error\n\t\tif onErr != nil {\n\t\t\tconf.Error = func(err error) {\n\t\t\t\tif e, ok := convGoErr(err); ok {\n\t\t\t\t\tonErr(e)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tchecker := types.NewChecker(conf, fset, pkgTypes, p.goInfo)\n\t\treturn checker.Files(goFiles)\n\t}\n\tfiles := make([]*goast.File, 0, len(goFiles))\n\tgofs := make(map[string]*goast.File)\n\txgofs := make(map[string]*ast.File)\n\tfor _, goFile := range goFiles {\n\t\tf := fset.File(goFile.Pos())\n\t\tif f == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfile := f.Name()\n\t\tfname := filepath.Base(file)\n\t\tif strings.HasPrefix(fname, \"xgo_autogen\") {\n\t\t\tcontinue\n\t\t}\n\t\tgofs[file] = goFile\n\t\tfiles = append(files, goFile)\n\t}\n\tfor _, xgoFile := range xgoFiles {\n\t\tf := fset.File(xgoFile.Pos())\n\t\tif f == nil {\n\t\t\tcontinue\n\t\t}\n\t\txgofs[f.Name()] = xgoFile\n\t}\n\tif debugVerbose {\n\t\tlog.Println(\"typesutil.Check:\", pkgTypes.Path(), \"xgoFiles =\", len(xgofs), \"goFiles =\", len(gofs))\n\t}\n\tpkg := &ast.Package{\n\t\tName:    pkgTypes.Name(),\n\t\tFiles:   xgofs,\n\t\tGoFiles: gofs,\n\t}\n\tmod := opts.Mod\n\tif mod == nil {\n\t\tmod = xgomod.Default\n\t}\n\t_, err = cl.NewPackage(pkgTypes.Path(), pkg, &cl.Config{\n\t\tTypes:          pkgTypes,\n\t\tFset:           fset,\n\t\tLookupClass:    mod.LookupClass,\n\t\tImporter:       conf.Importer,\n\t\tRecorder:       NewRecorder(p.xgoInfo),\n\t\tNoFileLine:     true,\n\t\tNoAutoGenMain:  true,\n\t\tNoSkipConstant: true,\n\t\tOutline:        opts.IgnoreFuncBodies,\n\t})\n\tif err != nil {\n\t\tif onErr := conf.Error; onErr != nil {\n\t\t\tif list, ok := err.(errors.List); ok {\n\t\t\t\tfor _, e := range list {\n\t\t\t\t\tif ce, ok := convErr(fset, e); ok {\n\t\t\t\t\t\tonErr(ce)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if ce, ok := convErr(fset, err); ok {\n\t\t\t\tonErr(ce)\n\t\t\t} else {\n\t\t\t\tonErr(err)\n\t\t\t}\n\t\t}\n\t\tif debugPrintErr {\n\t\t\tlog.Println(\"typesutil.Check err:\", err)\n\t\t\tlog.SingleStack()\n\t\t}\n\t\t// don't return even if err != nil\n\t}\n\tif len(files) > 0 {\n\t\tonErr := conf.Error\n\t\tif onErr != nil {\n\t\t\tconf.Error = func(err error) {\n\t\t\t\tif e, ok := convGoErr(err); ok {\n\t\t\t\t\tonErr(e)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tscope := pkgTypes.Scope()\n\t\tobjMap := DeleteObjects(scope, files)\n\t\tchecker := types.NewChecker(conf, fset, pkgTypes, p.goInfo)\n\t\terr = checker.Files(files)\n\t\t// TODO(xsw): how to process error?\n\t\tCorrectTypesInfo(scope, objMap, p.xgoInfo.Uses)\n\t\tif opts.UpdateGoTypesOverload {\n\t\t\tgogen.InitXGoPackage(pkgTypes)\n\t\t}\n\t}\n\treturn\n}\n\ntype astIdent interface {\n\tcomparable\n\tast.Node\n}\n\ntype objMapT = map[types.Object]types.Object\n\n// CorrectTypesInfo corrects types info to avoid there are two instances for the same Go object.\nfunc CorrectTypesInfo[Ident astIdent](scope *types.Scope, objMap objMapT, uses map[Ident]types.Object) {\n\tfor o := range objMap {\n\t\tobjMap[o] = scope.Lookup(o.Name())\n\t}\n\tfor id, old := range uses {\n\t\tif new := objMap[old]; new != nil {\n\t\t\tuses[id] = new\n\t\t}\n\t}\n}\n\n// DeleteObjects deletes all objects defined in Go files and returns deleted objects.\nfunc DeleteObjects(scope *types.Scope, files []*goast.File) objMapT {\n\tobjMap := make(objMapT)\n\tfor _, f := range files {\n\t\tfor _, decl := range f.Decls {\n\t\t\tswitch v := decl.(type) {\n\t\t\tcase *goast.GenDecl:\n\t\t\t\tfor _, spec := range v.Specs {\n\t\t\t\t\tswitch v := spec.(type) {\n\t\t\t\t\tcase *goast.ValueSpec:\n\t\t\t\t\t\tfor _, name := range v.Names {\n\t\t\t\t\t\t\tscopeDelete(objMap, scope, name.Name)\n\t\t\t\t\t\t}\n\t\t\t\t\tcase *goast.TypeSpec:\n\t\t\t\t\t\tscopeDelete(objMap, scope, v.Name.Name)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase *goast.FuncDecl:\n\t\t\t\tif v.Recv == nil {\n\t\t\t\t\tscopeDelete(objMap, scope, v.Name.Name)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn objMap\n}\n\nfunc convErr(fset *token.FileSet, e error) (ret Error, ok bool) {\n\tswitch v := e.(type) {\n\tcase *gogen.CodeError:\n\t\tret.Pos, ret.End, ret.Msg = v.Pos, v.End, v.Msg\n\tcase *gogen.MatchError:\n\t\tif v.Src != nil {\n\t\t\tret.Pos, ret.End = v.Src.Pos(), v.Src.End()\n\t\t}\n\t\tret.Msg = v.Message(\"\")\n\tcase *gogen.ImportError:\n\t\tret.Pos, ret.End, ret.Msg = v.Pos, v.End, v.Err.Error()\n\tcase *gogen.BoundTypeError:\n\t\tret.Pos, ret.End, ret.Msg = v.Pos, v.End, v.Error()\n\tdefault:\n\t\treturn\n\t}\n\tret.Fset, ok = fset, true\n\treturn\n}\n\nfunc convGoErr(e error) (ret Error, ok bool) {\n\tif v, ok := e.(types.Error); ok {\n\t\tret.Fset, ret.Pos, ret.Msg, ret.Soft = v.Fset, v.Pos, v.Msg, v.Soft\n\t\tcode, _, end, ok := typesutil.GetErrorGo116(&v)\n\t\tif ok {\n\t\t\tret.Code = Code(code)\n\t\t\tret.End = end\n\t\t}\n\t}\n\treturn ret, true\n}\n\nfunc scopeDelete(objMap map[types.Object]types.Object, scope *types.Scope, name string) {\n\tif o := typesutil.ScopeDelete(scope, name); o != nil {\n\t\tobjMap[o] = nil\n\t}\n}\n"
  },
  {
    "path": "x/typesutil/check_test.go",
    "content": "package typesutil_test\n\nimport (\n\tgoast \"go/ast\"\n\t\"go/importer\"\n\tgoparser \"go/parser\"\n\t\"go/types\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/goplus/mod/xgomod\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/goplus/xgo/x/typesutil\"\n\t\"github.com/qiniu/x/errors\"\n)\n\nfunc init() {\n\tif os.Getenv(\"XGOROOT\") == \"\" {\n\t\tdir, _ := os.Getwd()\n\t\tos.Setenv(\"XGOROOT\", filepath.Clean(filepath.Join(dir, \"./../..\")))\n\t}\n\ttypesutil.SetDebug(typesutil.DbgFlagDefault)\n}\n\nfunc loadFiles(fset *token.FileSet, file string, src any, goxfile string, goxsrc any, gofile string, gosrc any) ([]*ast.File, []*goast.File, error) {\n\tvar files []*ast.File\n\tvar gofiles []*goast.File\n\tif file != \"\" {\n\t\tf, err := parser.ParseFile(fset, file, src, 0)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tfiles = append(files, f)\n\t}\n\tif goxfile != \"\" {\n\t\tf, err := parser.ParseFile(fset, goxfile, goxsrc, parser.ParseXGoClass)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tfiles = append(files, f)\n\t}\n\tif gofile != \"\" {\n\t\tf, err := goparser.ParseFile(fset, gofile, gosrc, 0)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tgofiles = append(gofiles, f)\n\t}\n\treturn files, gofiles, nil\n}\n\nfunc checkFiles(fset *token.FileSet, file string, src any, goxfile string, goxsrc any, gofile string, gosrc any) (*typesutil.Info, *types.Info, error) {\n\tfiles, gofiles, err := loadFiles(fset, file, src, goxfile, goxsrc, gofile, gosrc)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn checkInfo(fset, files, gofiles, nil)\n}\n\nfunc checkFilesWithErrorHandler(fset *token.FileSet, file string, src any, goxfile string, goxsrc any, gofile string, gosrc any, handleErr func(error)) (*typesutil.Info, *types.Info, error) {\n\tfiles, gofiles, err := loadFiles(fset, file, src, goxfile, goxsrc, gofile, gosrc)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn checkInfo(fset, files, gofiles, handleErr)\n}\n\nfunc checkInfo(fset *token.FileSet, files []*ast.File, gofiles []*goast.File, handleErr func(error)) (*typesutil.Info, *types.Info, error) {\n\tconf := &types.Config{}\n\tconf.Importer = importer.Default()\n\tif handleErr == nil {\n\t\thandleErr = func(err error) {\n\t\t\tlog.Println(err)\n\t\t}\n\t}\n\tconf.Error = handleErr\n\tchkOpts := &typesutil.Config{\n\t\tTypes: types.NewPackage(\"main\", \"main\"),\n\t\tFset:  fset,\n\t\tMod:   xgomod.Default,\n\t}\n\tinfo := &typesutil.Info{\n\t\tTypes:      make(map[ast.Expr]types.TypeAndValue),\n\t\tDefs:       make(map[*ast.Ident]types.Object),\n\t\tUses:       make(map[*ast.Ident]types.Object),\n\t\tImplicits:  make(map[ast.Node]types.Object),\n\t\tSelections: make(map[*ast.SelectorExpr]*types.Selection),\n\t\tScopes:     make(map[ast.Node]*types.Scope),\n\t\tOverloads:  make(map[*ast.Ident]types.Object),\n\t}\n\tginfo := &types.Info{\n\t\tTypes:      make(map[goast.Expr]types.TypeAndValue),\n\t\tDefs:       make(map[*goast.Ident]types.Object),\n\t\tUses:       make(map[*goast.Ident]types.Object),\n\t\tImplicits:  make(map[goast.Node]types.Object),\n\t\tSelections: make(map[*goast.SelectorExpr]*types.Selection),\n\t\tScopes:     make(map[goast.Node]*types.Scope),\n\t}\n\tcheck := typesutil.NewChecker(conf, chkOpts, ginfo, info)\n\terr := check.Files(gofiles, files)\n\treturn info, ginfo, err\n}\n\nfunc TestCheckFiles(t *testing.T) {\n\tfset := token.NewFileSet()\n\tinfo, ginfo, err := checkFiles(fset, \"main.xgo\", `\nimport \"fmt\"\n\ntype Point struct {\n\tx int\n\ty int\n}\npt := &Point{}\npt.x = 100\npt.y = 200\nfmt.Println(pt)\n\ngopt := GoPoint{100,200}\ngopt.Test()\ngotest()\nfmt.Println(GoValue)\nfmt.Println(&Rect{100,200})\n`, \"Rect.gox\", `\nvar (\n\tx int\n\ty int\n)\n`, \"util.go\", `package main\nvar GoValue string\ntype GoPoint struct {\n\tx int\n\ty int\n}\nfunc (p *GoPoint) Test() {\n}\nfunc gotest() {\n}\n`)\n\tif err != nil || info == nil || ginfo == nil {\n\t\tt.Fatalf(\"check failed: %v\", err)\n\t}\n\n\tfor def, obj := range info.Defs {\n\t\to := info.ObjectOf(def)\n\t\tif o != obj {\n\t\t\tt.Fatal(\"bad obj\", o)\n\t\t}\n\t}\n\tfor use, obj := range info.Uses {\n\t\to := info.ObjectOf(use)\n\t\tif o.String() != obj.String() {\n\t\t\tt.Fatal(\"bad obj\", o)\n\t\t}\n\t\ttyp := info.TypeOf(use)\n\t\tif typ.String() != obj.Type().String() {\n\t\t\tt.Fatal(\"bad typ\", typ)\n\t\t}\n\t}\n\n}\n\nfunc TestCheckGoFiles(t *testing.T) {\n\tfset := token.NewFileSet()\n\tinfo, ginfo, err := checkFiles(fset, \"\", \"\", \"\", \"\", \"main.go\", `package main\ntype GoPoint struct {\n\tx int\n\ty int\n}\nfunc main() {\n}\n`)\n\tif err != nil || info == nil || ginfo == nil {\n\t\tt.Fatalf(\"check failed: %v\", err)\n\t}\n}\n\nfunc TestCheckError(t *testing.T) {\n\tfset := token.NewFileSet()\n\t_, _, err := checkFiles(fset, \"main.xgo\", `\ntype Point struct {\n\tx int\n\ty int\n}\npt := &Point1{}\nprintln(pt)\n`, \"\", \"\", \"\", \"\")\n\tif err == nil {\n\t\tt.Fatal(\"no error\")\n\t}\n\t_, _, err = checkFiles(fset, \"main.xgo\", `\nvar i int = \"hello\"\n`, \"\", \"\", \"\", \"\")\n\tif err == nil {\n\t\tt.Fatal(\"no error\")\n\t}\n\t_, _, err = checkFiles(fset, \"main.xgo\", `\nvar nums []int\nnums = append(nums, \"NaN\")\n`, \"\", \"\", \"\", \"\")\n\tif err == nil {\n\t\tt.Fatal(\"no error\")\n\t}\n}\n\nfunc TestBadFile(t *testing.T) {\n\tconf := &types.Config{}\n\topt := &typesutil.Config{}\n\topt.Fset = token.NewFileSet()\n\topt.Types = types.NewPackage(\"\", \"main\")\n\tchecker := typesutil.NewChecker(conf, opt, nil, nil)\n\t_ = checker.Files([]*goast.File{{Name: goast.NewIdent(\"main\")}},\n\t\t[]*ast.File{{Name: ast.NewIdent(\"main\")}})\n}\n\nfunc TestCheckOverload(t *testing.T) {\n\tfset := token.NewFileSet()\n\tinfo, ginfo, err := checkFiles(fset, \"main.xgo\", `\ntype foo struct {\n}\n\nfunc (a *foo) mulInt(b int) *foo {\n\treturn a\n}\n\nfunc (a *foo) mulFoo(b *foo) *foo {\n\treturn a\n}\n\nfunc (foo).mul = (\n\t(foo).mulInt\n\t(foo).mulFoo\n)\nfunc addInt0() {\n}\n\nfunc addInt1(i int) {\n}\n\nfunc addInt2(i, j int) {\n}\n\nvar addInt3 = func(i, j, k int) {\n}\n\nfunc add = (\n\taddInt0\n\taddInt1\n\taddInt2\n\taddInt3\n\tfunc(a, b string) string {\n\t\treturn a + b\n\t}\n)\n\nvar a, b *foo\nvar c = a.mul(100)\nvar d = a.mul(c)\n\nfunc init() {\n\tadd 100, 200\n\tadd 100, 200, 300\n\tadd(\"hello\", \"world\")\n}\n`, \"\", \"\", \"\", \"\")\n\tif err != nil || info == nil || ginfo == nil {\n\t\tt.Fatalf(\"check failed: %v\", err)\n\t}\n\tfor use, ovDeclObj := range info.Overloads {\n\t\to := info.ObjectOf(use)\n\t\tdeclObj, ovObjs := info.OverloadOf(use)\n\t\tif ovDeclObj != declObj {\n\t\t\tt.Fatal(\"bad overload\", o)\n\t\t}\n\t\tfound := false\n\t\tfor _, ovObj := range ovObjs {\n\t\t\tif o == ovObj {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tt.Fatal(\"bad overload\", o)\n\t\t}\n\t}\n\tfor use, o := range info.Uses {\n\t\tdeclObj, ovObjs := info.OverloadOf(use)\n\t\tif declObj != nil && ovObjs != nil {\n\t\t\tif info.Overloads[use] == nil {\n\t\t\t\tt.Fatal(\"bad overload\", o)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestCheckError2(t *testing.T) {\n\tvar checkerErrs errors.List\n\tfset := token.NewFileSet()\n\tcheckFilesWithErrorHandler(fset, \"main.xgo\", `\ntype Foo struct {\n\tB bool\n}\n\nbar := 2\ngotbar := false\nboolBar := false\ngg := &Foo{B: true}\nif bar == 2 && !gotbar {\n\tprintln(\"wow!\")\n} else if bar == 1 && gotbar {\n\tif !boolBar {\n\t\tboolBar = true\n\t\tfor i := 0; i < 20; i++ {\n\t\t\tif gg.P {\n\t\t\t\tprintln(\"wow 2!\")\n\t\t\t}\n\t\t}\n\t}\n}\n`, \"\", \"\", \"\", \"\", func(err error) {\n\t\tcheckerErrs.Add(err)\n\t})\n\n\tif len(checkerErrs) > 1 {\n\t\tt.Fatal(\"too many errors\")\n\t}\n}\n\nfunc TestCheckError3(t *testing.T) {\n\tvar checkerErrs errors.List\n\tfset := token.NewFileSet()\n\tcheckFilesWithErrorHandler(fset, \"main.xgo\", `\ntype Foo struct {\n\tB []string\n}\n\nbar := 2\ngotbar := false\nboolBar := false\ngg := &Foo{B: []string{\"hello\", \"world\"}}\nif bar == 2 && !gotbar {\n\tprintln(\"Double jump activated!\")\n} else if bar == 1 && gotbar {\n\tif !boolBar {\n\t\tboolBar = true\n\t\tfor item := range gg.P {\n\t\t\tprintln(\"i am item\", item)\n\t\t}\n\t}\n}\n`, \"\", \"\", \"\", \"\", func(err error) {\n\t\tcheckerErrs.Add(err)\n\t})\n\n\tif len(checkerErrs) > 1 {\n\t\tt.Fatal(\"too many errors\")\n\t}\n}\n\nfunc TestCheckWithGoFiles(t *testing.T) {\n\tfset := token.NewFileSet()\n\tinfo, ginfo, err := checkFiles(fset, \"main.xgo\", `\nimport \"fmt\"\n\nfmt.Println(add(1, 2))\nfmt.Println(gofunc())\n`, \"\", \"\", \"util.go\", `package main\n\nfunc add(a, b int) int {\n\treturn a + b\n}\n\nfunc gofunc() string {\n\treturn \"hello from go\"\n}\n`)\n\tif err != nil || info == nil || ginfo == nil {\n\t\tt.Fatalf(\"check failed: %v\", err)\n\t}\n\n\t// Verify that Go file definitions are accessible from XGo\n\tfoundAdd := false\n\tfoundGofunc := false\n\tfor _, obj := range info.Uses {\n\t\tif obj.Name() == \"add\" {\n\t\t\tfoundAdd = true\n\t\t}\n\t\tif obj.Name() == \"gofunc\" {\n\t\t\tfoundGofunc = true\n\t\t}\n\t}\n\tif !foundAdd || !foundGofunc {\n\t\tt.Fatal(\"Go file functions not found in XGo uses\")\n\t}\n\n\t// Verify that Go file definitions are in ginfo\n\tfoundAddDef := false\n\tfoundGofuncDef := false\n\tfor _, obj := range ginfo.Defs {\n\t\tif obj != nil {\n\t\t\tif obj.Name() == \"add\" {\n\t\t\t\tfoundAddDef = true\n\t\t\t}\n\t\t\tif obj.Name() == \"gofunc\" {\n\t\t\t\tfoundGofuncDef = true\n\t\t\t}\n\t\t}\n\t}\n\tif !foundAddDef || !foundGofuncDef {\n\t\tt.Fatal(\"Go file functions not found in ginfo defs\")\n\t}\n}\n"
  },
  {
    "path": "x/typesutil/code_string.go",
    "content": "// Code generated by \"stringer -type Code codes.go\"; DO NOT EDIT.\n\npackage typesutil\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[InvalidSyntaxTree - -1]\n\t_ = x[Test-1]\n\t_ = x[BlankPkgName-2]\n\t_ = x[MismatchedPkgName-3]\n\t_ = x[InvalidPkgUse-4]\n\t_ = x[BadImportPath-5]\n\t_ = x[BrokenImport-6]\n\t_ = x[ImportCRenamed-7]\n\t_ = x[UnusedImport-8]\n\t_ = x[InvalidInitCycle-9]\n\t_ = x[DuplicateDecl-10]\n\t_ = x[InvalidDeclCycle-11]\n\t_ = x[InvalidTypeCycle-12]\n\t_ = x[InvalidConstInit-13]\n\t_ = x[InvalidConstVal-14]\n\t_ = x[InvalidConstType-15]\n\t_ = x[UntypedNilUse-16]\n\t_ = x[WrongAssignCount-17]\n\t_ = x[UnassignableOperand-18]\n\t_ = x[NoNewVar-19]\n\t_ = x[MultiValAssignOp-20]\n\t_ = x[InvalidIfaceAssign-21]\n\t_ = x[InvalidChanAssign-22]\n\t_ = x[IncompatibleAssign-23]\n\t_ = x[UnaddressableFieldAssign-24]\n\t_ = x[NotAType-25]\n\t_ = x[InvalidArrayLen-26]\n\t_ = x[BlankIfaceMethod-27]\n\t_ = x[IncomparableMapKey-28]\n\t_ = x[InvalidPtrEmbed-30]\n\t_ = x[BadRecv-31]\n\t_ = x[InvalidRecv-32]\n\t_ = x[DuplicateFieldAndMethod-33]\n\t_ = x[DuplicateMethod-34]\n\t_ = x[InvalidBlank-35]\n\t_ = x[InvalidIota-36]\n\t_ = x[MissingInitBody-37]\n\t_ = x[InvalidInitSig-38]\n\t_ = x[InvalidInitDecl-39]\n\t_ = x[InvalidMainDecl-40]\n\t_ = x[TooManyValues-41]\n\t_ = x[NotAnExpr-42]\n\t_ = x[TruncatedFloat-43]\n\t_ = x[NumericOverflow-44]\n\t_ = x[UndefinedOp-45]\n\t_ = x[MismatchedTypes-46]\n\t_ = x[DivByZero-47]\n\t_ = x[NonNumericIncDec-48]\n\t_ = x[UnaddressableOperand-49]\n\t_ = x[InvalidIndirection-50]\n\t_ = x[NonIndexableOperand-51]\n\t_ = x[InvalidIndex-52]\n\t_ = x[SwappedSliceIndices-53]\n\t_ = x[NonSliceableOperand-54]\n\t_ = x[InvalidSliceExpr-55]\n\t_ = x[InvalidShiftCount-56]\n\t_ = x[InvalidShiftOperand-57]\n\t_ = x[InvalidReceive-58]\n\t_ = x[InvalidSend-59]\n\t_ = x[DuplicateLitKey-60]\n\t_ = x[MissingLitKey-61]\n\t_ = x[InvalidLitIndex-62]\n\t_ = x[OversizeArrayLit-63]\n\t_ = x[MixedStructLit-64]\n\t_ = x[InvalidStructLit-65]\n\t_ = x[MissingLitField-66]\n\t_ = x[DuplicateLitField-67]\n\t_ = x[UnexportedLitField-68]\n\t_ = x[InvalidLitField-69]\n\t_ = x[UntypedLit-70]\n\t_ = x[InvalidLit-71]\n\t_ = x[AmbiguousSelector-72]\n\t_ = x[UndeclaredImportedName-73]\n\t_ = x[UnexportedName-74]\n\t_ = x[UndeclaredName-75]\n\t_ = x[MissingFieldOrMethod-76]\n\t_ = x[BadDotDotDotSyntax-77]\n\t_ = x[NonVariadicDotDotDot-78]\n\t_ = x[MisplacedDotDotDot-79]\n\t_ = x[InvalidDotDotDot-81]\n\t_ = x[UncalledBuiltin-82]\n\t_ = x[InvalidAppend-83]\n\t_ = x[InvalidCap-84]\n\t_ = x[InvalidClose-85]\n\t_ = x[InvalidCopy-86]\n\t_ = x[InvalidComplex-87]\n\t_ = x[InvalidDelete-88]\n\t_ = x[InvalidImag-89]\n\t_ = x[InvalidLen-90]\n\t_ = x[SwappedMakeArgs-91]\n\t_ = x[InvalidMake-92]\n\t_ = x[InvalidReal-93]\n\t_ = x[InvalidAssert-94]\n\t_ = x[ImpossibleAssert-95]\n\t_ = x[InvalidConversion-96]\n\t_ = x[InvalidUntypedConversion-97]\n\t_ = x[BadOffsetofSyntax-98]\n\t_ = x[InvalidOffsetof-99]\n\t_ = x[UnusedExpr-100]\n\t_ = x[UnusedVar-101]\n\t_ = x[MissingReturn-102]\n\t_ = x[WrongResultCount-103]\n\t_ = x[OutOfScopeResult-104]\n\t_ = x[InvalidCond-105]\n\t_ = x[InvalidPostDecl-106]\n\t_ = x[InvalidIterVar-108]\n\t_ = x[InvalidRangeExpr-109]\n\t_ = x[MisplacedBreak-110]\n\t_ = x[MisplacedContinue-111]\n\t_ = x[MisplacedFallthrough-112]\n\t_ = x[DuplicateCase-113]\n\t_ = x[DuplicateDefault-114]\n\t_ = x[BadTypeKeyword-115]\n\t_ = x[InvalidTypeSwitch-116]\n\t_ = x[InvalidExprSwitch-117]\n\t_ = x[InvalidSelectCase-118]\n\t_ = x[UndeclaredLabel-119]\n\t_ = x[DuplicateLabel-120]\n\t_ = x[MisplacedLabel-121]\n\t_ = x[UnusedLabel-122]\n\t_ = x[JumpOverDecl-123]\n\t_ = x[JumpIntoBlock-124]\n\t_ = x[InvalidMethodExpr-125]\n\t_ = x[WrongArgCount-126]\n\t_ = x[InvalidCall-127]\n\t_ = x[UnusedResults-128]\n\t_ = x[InvalidDefer-129]\n\t_ = x[InvalidGo-130]\n\t_ = x[BadDecl-131]\n\t_ = x[RepeatedDecl-132]\n\t_ = x[InvalidUnsafeAdd-133]\n\t_ = x[InvalidUnsafeSlice-134]\n\t_ = x[UnsupportedFeature-135]\n\t_ = x[NotAGenericType-136]\n\t_ = x[WrongTypeArgCount-137]\n\t_ = x[CannotInferTypeArgs-138]\n\t_ = x[InvalidTypeArg-139]\n\t_ = x[InvalidInstanceCycle-140]\n\t_ = x[InvalidUnion-141]\n\t_ = x[MisplacedConstraintIface-142]\n\t_ = x[InvalidMethodTypeParams-143]\n\t_ = x[MisplacedTypeParam-144]\n\t_ = x[InvalidUnsafeSliceData-145]\n\t_ = x[InvalidUnsafeString-146]\n\t_ = x[InvalidClear-148]\n\t_ = x[TypeTooLarge-149]\n\t_ = x[InvalidMinMaxOperand-150]\n\t_ = x[TooNew-151]\n}\n\nconst (\n\t_Code_name_0 = \"InvalidSyntaxTree\"\n\t_Code_name_1 = \"TestBlankPkgNameMismatchedPkgNameInvalidPkgUseBadImportPathBrokenImportImportCRenamedUnusedImportInvalidInitCycleDuplicateDeclInvalidDeclCycleInvalidTypeCycleInvalidConstInitInvalidConstValInvalidConstTypeUntypedNilUseWrongAssignCountUnassignableOperandNoNewVarMultiValAssignOpInvalidIfaceAssignInvalidChanAssignIncompatibleAssignUnaddressableFieldAssignNotATypeInvalidArrayLenBlankIfaceMethodIncomparableMapKey\"\n\t_Code_name_2 = \"InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDot\"\n\t_Code_name_3 = \"InvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDecl\"\n\t_Code_name_4 = \"InvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString\"\n\t_Code_name_5 = \"InvalidClearTypeTooLargeInvalidMinMaxOperandTooNew\"\n)\n\nvar (\n\t_Code_index_1 = [...]uint16{0, 4, 16, 33, 46, 59, 71, 85, 97, 113, 126, 142, 158, 174, 189, 205, 218, 234, 253, 261, 277, 295, 312, 330, 354, 362, 377, 393, 411}\n\t_Code_index_2 = [...]uint16{0, 15, 22, 33, 56, 71, 83, 94, 109, 123, 138, 153, 166, 175, 189, 204, 215, 230, 239, 255, 275, 293, 312, 324, 343, 362, 378, 395, 414, 428, 439, 454, 467, 482, 498, 512, 528, 543, 560, 578, 593, 603, 613, 630, 652, 666, 680, 700, 718, 738, 756}\n\t_Code_index_3 = [...]uint16{0, 16, 31, 44, 54, 66, 77, 91, 104, 115, 125, 140, 151, 162, 175, 191, 208, 232, 249, 264, 274, 283, 296, 312, 328, 339, 354}\n\t_Code_index_4 = [...]uint16{0, 14, 30, 44, 61, 81, 94, 110, 124, 141, 158, 175, 190, 204, 218, 229, 241, 254, 271, 284, 295, 308, 320, 329, 336, 348, 364, 382, 400, 415, 432, 451, 465, 485, 497, 521, 544, 562, 584, 603}\n\t_Code_index_5 = [...]uint8{0, 12, 24, 44, 50}\n)\n\nfunc (i Code) String() string {\n\tswitch {\n\tcase i == -1:\n\t\treturn _Code_name_0\n\tcase 1 <= i && i <= 28:\n\t\ti -= 1\n\t\treturn _Code_name_1[_Code_index_1[i]:_Code_index_1[i+1]]\n\tcase 30 <= i && i <= 79:\n\t\ti -= 30\n\t\treturn _Code_name_2[_Code_index_2[i]:_Code_index_2[i+1]]\n\tcase 81 <= i && i <= 106:\n\t\ti -= 81\n\t\treturn _Code_name_3[_Code_index_3[i]:_Code_index_3[i+1]]\n\tcase 108 <= i && i <= 146:\n\t\ti -= 108\n\t\treturn _Code_name_4[_Code_index_4[i]:_Code_index_4[i+1]]\n\tcase 148 <= i && i <= 151:\n\t\ti -= 148\n\t\treturn _Code_name_5[_Code_index_5[i]:_Code_index_5[i+1]]\n\tdefault:\n\t\treturn \"Code(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\n"
  },
  {
    "path": "x/typesutil/codes.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\n//go:generate go run golang.org/x/tools/cmd/stringer@latest -type Code codes.go\n\ntype Code int\n\n// This file defines the error codes that can be produced during type-checking.\n// Collectively, these codes provide an identifier that may be used to\n// implement special handling for certain types of errors.\n//\n// Error code values should not be changed: add new codes at the end.\n//\n// Error codes should be fine-grained enough that the exact nature of the error\n// can be easily determined, but coarse enough that they are not an\n// implementation detail of the type checking algorithm. As a rule-of-thumb,\n// errors should be considered equivalent if there is a theoretical refactoring\n// of the type checker in which they are emitted in exactly one place. For\n// example, the type checker emits different error messages for \"too many\n// arguments\" and \"too few arguments\", but one can imagine an alternative type\n// checker where this check instead just emits a single \"wrong number of\n// arguments\", so these errors should have the same code.\n//\n// Error code names should be as brief as possible while retaining accuracy and\n// distinctiveness. In most cases names should start with an adjective\n// describing the nature of the error (e.g. \"invalid\", \"unused\", \"misplaced\"),\n// and end with a noun identifying the relevant language object. For example,\n// \"_DuplicateDecl\" or \"_InvalidSliceExpr\". For brevity, naming follows the\n// convention that \"bad\" implies a problem with syntax, and \"invalid\" implies a\n// problem with types.\n\nconst (\n\t// InvalidSyntaxTree occurs if an invalid syntax tree is provided\n\t// to the type checker. It should never happen.\n\tInvalidSyntaxTree Code = -1\n)\n\nconst (\n\t// The zero Code value indicates an unset (invalid) error code.\n\t_ Code = iota\n\n\t// Test is reserved for errors that only apply while in self-test mode.\n\tTest\n\n\t// BlankPkgName occurs when a package name is the blank identifier \"_\".\n\t//\n\t// Per the spec:\n\t//  \"The PackageName must not be the blank identifier.\"\n\t//\n\t// Example:\n\t//  package _\n\tBlankPkgName\n\n\t// MismatchedPkgName occurs when a file's package name doesn't match the\n\t// package name already established by other files.\n\tMismatchedPkgName\n\n\t// InvalidPkgUse occurs when a package identifier is used outside of a\n\t// selector expression.\n\t//\n\t// Example:\n\t//  import \"fmt\"\n\t//\n\t//  var _ = fmt\n\tInvalidPkgUse\n\n\t// BadImportPath occurs when an import path is not valid.\n\tBadImportPath\n\n\t// BrokenImport occurs when importing a package fails.\n\t//\n\t// Example:\n\t//  import \"amissingpackage\"\n\tBrokenImport\n\n\t// ImportCRenamed occurs when the special import \"C\" is renamed. \"C\" is a\n\t// pseudo-package, and must not be renamed.\n\t//\n\t// Example:\n\t//  import _ \"C\"\n\tImportCRenamed\n\n\t// UnusedImport occurs when an import is unused.\n\t//\n\t// Example:\n\t//  import \"fmt\"\n\t//\n\t//  func main() {}\n\tUnusedImport\n\n\t// InvalidInitCycle occurs when an invalid cycle is detected within the\n\t// initialization graph.\n\t//\n\t// Example:\n\t//  var x int = f()\n\t//\n\t//  func f() int { return x }\n\tInvalidInitCycle\n\n\t// DuplicateDecl occurs when an identifier is declared multiple times.\n\t//\n\t// Example:\n\t//  var x = 1\n\t//  var x = 2\n\tDuplicateDecl\n\n\t// InvalidDeclCycle occurs when a declaration cycle is not valid.\n\t//\n\t// Example:\n\t//  type S struct {\n\t//  \tS\n\t//  }\n\t//\n\tInvalidDeclCycle\n\n\t// InvalidTypeCycle occurs when a cycle in type definitions results in a\n\t// type that is not well-defined.\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  type T [unsafe.Sizeof(T{})]int\n\tInvalidTypeCycle\n\n\t// InvalidConstInit occurs when a const declaration has a non-constant\n\t// initializer.\n\t//\n\t// Example:\n\t//  var x int\n\t//  const _ = x\n\tInvalidConstInit\n\n\t// InvalidConstVal occurs when a const value cannot be converted to its\n\t// target type.\n\t//\n\t// TODO(findleyr): this error code and example are not very clear. Consider\n\t// removing it.\n\t//\n\t// Example:\n\t//  const _ = 1 << \"hello\"\n\tInvalidConstVal\n\n\t// InvalidConstType occurs when the underlying type in a const declaration\n\t// is not a valid constant type.\n\t//\n\t// Example:\n\t//  const c *int = 4\n\tInvalidConstType\n\n\t// UntypedNilUse occurs when the predeclared (untyped) value nil is used to\n\t// initialize a variable declared without an explicit type.\n\t//\n\t// Example:\n\t//  var x = nil\n\tUntypedNilUse\n\n\t// WrongAssignCount occurs when the number of values on the right-hand side\n\t// of an assignment or initialization expression does not match the number\n\t// of variables on the left-hand side.\n\t//\n\t// Example:\n\t//  var x = 1, 2\n\tWrongAssignCount\n\n\t// UnassignableOperand occurs when the left-hand side of an assignment is\n\t// not assignable.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tconst c = 1\n\t//  \tc = 2\n\t//  }\n\tUnassignableOperand\n\n\t// NoNewVar occurs when a short variable declaration (':=') does not declare\n\t// new variables.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tx := 1\n\t//  \tx := 2\n\t//  }\n\tNoNewVar\n\n\t// MultiValAssignOp occurs when an assignment operation (+=, *=, etc) does\n\t// not have single-valued left-hand or right-hand side.\n\t//\n\t// Per the spec:\n\t//  \"In assignment operations, both the left- and right-hand expression lists\n\t//  must contain exactly one single-valued expression\"\n\t//\n\t// Example:\n\t//  func f() int {\n\t//  \tx, y := 1, 2\n\t//  \tx, y += 1\n\t//  \treturn x + y\n\t//  }\n\tMultiValAssignOp\n\n\t// InvalidIfaceAssign occurs when a value of type T is used as an\n\t// interface, but T does not implement a method of the expected interface.\n\t//\n\t// Example:\n\t//  type I interface {\n\t//  \tf()\n\t//  }\n\t//\n\t//  type T int\n\t//\n\t//  var x I = T(1)\n\tInvalidIfaceAssign\n\n\t// InvalidChanAssign occurs when a chan assignment is invalid.\n\t//\n\t// Per the spec, a value x is assignable to a channel type T if:\n\t//  \"x is a bidirectional channel value, T is a channel type, x's type V and\n\t//  T have identical element types, and at least one of V or T is not a\n\t//  defined type.\"\n\t//\n\t// Example:\n\t//  type T1 chan int\n\t//  type T2 chan int\n\t//\n\t//  var x T1\n\t//  // Invalid assignment because both types are named\n\t//  var _ T2 = x\n\tInvalidChanAssign\n\n\t// IncompatibleAssign occurs when the type of the right-hand side expression\n\t// in an assignment cannot be assigned to the type of the variable being\n\t// assigned.\n\t//\n\t// Example:\n\t//  var x []int\n\t//  var _ int = x\n\tIncompatibleAssign\n\n\t// UnaddressableFieldAssign occurs when trying to assign to a struct field\n\t// in a map value.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tm := make(map[string]struct{i int})\n\t//  \tm[\"foo\"].i = 42\n\t//  }\n\tUnaddressableFieldAssign\n\n\t// NotAType occurs when the identifier used as the underlying type in a type\n\t// declaration or the right-hand side of a type alias does not denote a type.\n\t//\n\t// Example:\n\t//  var S = 2\n\t//\n\t//  type T S\n\tNotAType\n\n\t// InvalidArrayLen occurs when an array length is not a constant value.\n\t//\n\t// Example:\n\t//  var n = 3\n\t//  var _ = [n]int{}\n\tInvalidArrayLen\n\n\t// BlankIfaceMethod occurs when a method name is '_'.\n\t//\n\t// Per the spec:\n\t//  \"The name of each explicitly specified method must be unique and not\n\t//  blank.\"\n\t//\n\t// Example:\n\t//  type T interface {\n\t//  \t_(int)\n\t//  }\n\tBlankIfaceMethod\n\n\t// IncomparableMapKey occurs when a map key type does not support the == and\n\t// != operators.\n\t//\n\t// Per the spec:\n\t//  \"The comparison operators == and != must be fully defined for operands of\n\t//  the key type; thus the key type must not be a function, map, or slice.\"\n\t//\n\t// Example:\n\t//  var x map[T]int\n\t//\n\t//  type T []int\n\tIncomparableMapKey\n\n\t// InvalidIfaceEmbed occurs when a non-interface type is embedded in an\n\t// interface (for go 1.17 or earlier).\n\t_ // not used anymore\n\n\t// InvalidPtrEmbed occurs when an embedded field is of the pointer form *T,\n\t// and T itself is itself a pointer, an unsafe.Pointer, or an interface.\n\t//\n\t// Per the spec:\n\t//  \"An embedded field must be specified as a type name T or as a pointer to\n\t//  a non-interface type name *T, and T itself may not be a pointer type.\"\n\t//\n\t// Example:\n\t//  type T *int\n\t//\n\t//  type S struct {\n\t//  \t*T\n\t//  }\n\tInvalidPtrEmbed\n\n\t// BadRecv occurs when a method declaration does not have exactly one\n\t// receiver parameter.\n\t//\n\t// Example:\n\t//  func () _() {}\n\tBadRecv\n\n\t// InvalidRecv occurs when a receiver type expression is not of the form T\n\t// or *T, or T is a pointer type.\n\t//\n\t// Example:\n\t//  type T struct {}\n\t//\n\t//  func (**T) m() {}\n\tInvalidRecv\n\n\t// DuplicateFieldAndMethod occurs when an identifier appears as both a field\n\t// and method name.\n\t//\n\t// Example:\n\t//  type T struct {\n\t//  \tm int\n\t//  }\n\t//\n\t//  func (T) m() {}\n\tDuplicateFieldAndMethod\n\n\t// DuplicateMethod occurs when two methods on the same receiver type have\n\t// the same name.\n\t//\n\t// Example:\n\t//  type T struct {}\n\t//  func (T) m() {}\n\t//  func (T) m(i int) int { return i }\n\tDuplicateMethod\n\n\t// InvalidBlank occurs when a blank identifier is used as a value or type.\n\t//\n\t// Per the spec:\n\t//  \"The blank identifier may appear as an operand only on the left-hand side\n\t//  of an assignment.\"\n\t//\n\t// Example:\n\t//  var x = _\n\tInvalidBlank\n\n\t// InvalidIota occurs when the predeclared identifier iota is used outside\n\t// of a constant declaration.\n\t//\n\t// Example:\n\t//  var x = iota\n\tInvalidIota\n\n\t// MissingInitBody occurs when an init function is missing its body.\n\t//\n\t// Example:\n\t//  func init()\n\tMissingInitBody\n\n\t// InvalidInitSig occurs when an init function declares parameters or\n\t// results.\n\t//\n\t// Deprecated: no longer emitted by the type checker. _InvalidInitDecl is\n\t// used instead.\n\tInvalidInitSig\n\n\t// InvalidInitDecl occurs when init is declared as anything other than a\n\t// function.\n\t//\n\t// Example:\n\t//  var init = 1\n\t//\n\t// Example:\n\t//  func init() int { return 1 }\n\tInvalidInitDecl\n\n\t// InvalidMainDecl occurs when main is declared as anything other than a\n\t// function, in a main package.\n\tInvalidMainDecl\n\n\t// TooManyValues occurs when a function returns too many values for the\n\t// expression context in which it is used.\n\t//\n\t// Example:\n\t//  func ReturnTwo() (int, int) {\n\t//  \treturn 1, 2\n\t//  }\n\t//\n\t//  var x = ReturnTwo()\n\tTooManyValues\n\n\t// NotAnExpr occurs when a type expression is used where a value expression\n\t// is expected.\n\t//\n\t// Example:\n\t//  type T struct {}\n\t//\n\t//  func f() {\n\t//  \tT\n\t//  }\n\tNotAnExpr\n\n\t// TruncatedFloat occurs when a float constant is truncated to an integer\n\t// value.\n\t//\n\t// Example:\n\t//  var _ int = 98.6\n\tTruncatedFloat\n\n\t// NumericOverflow occurs when a numeric constant overflows its target type.\n\t//\n\t// Example:\n\t//  var x int8 = 1000\n\tNumericOverflow\n\n\t// UndefinedOp occurs when an operator is not defined for the type(s) used\n\t// in an operation.\n\t//\n\t// Example:\n\t//  var c = \"a\" - \"b\"\n\tUndefinedOp\n\n\t// MismatchedTypes occurs when operand types are incompatible in a binary\n\t// operation.\n\t//\n\t// Example:\n\t//  var a = \"hello\"\n\t//  var b = 1\n\t//  var c = a - b\n\tMismatchedTypes\n\n\t// DivByZero occurs when a division operation is provable at compile\n\t// time to be a division by zero.\n\t//\n\t// Example:\n\t//  const divisor = 0\n\t//  var x int = 1/divisor\n\tDivByZero\n\n\t// NonNumericIncDec occurs when an increment or decrement operator is\n\t// applied to a non-numeric value.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tvar c = \"c\"\n\t//  \tc++\n\t//  }\n\tNonNumericIncDec\n\n\t// UnaddressableOperand occurs when the & operator is applied to an\n\t// unaddressable expression.\n\t//\n\t// Example:\n\t//  var x = &1\n\tUnaddressableOperand\n\n\t// InvalidIndirection occurs when a non-pointer value is indirected via the\n\t// '*' operator.\n\t//\n\t// Example:\n\t//  var x int\n\t//  var y = *x\n\tInvalidIndirection\n\n\t// NonIndexableOperand occurs when an index operation is applied to a value\n\t// that cannot be indexed.\n\t//\n\t// Example:\n\t//  var x = 1\n\t//  var y = x[1]\n\tNonIndexableOperand\n\n\t// InvalidIndex occurs when an index argument is not of integer type,\n\t// negative, or out-of-bounds.\n\t//\n\t// Example:\n\t//  var s = [...]int{1,2,3}\n\t//  var x = s[5]\n\t//\n\t// Example:\n\t//  var s = []int{1,2,3}\n\t//  var _ = s[-1]\n\t//\n\t// Example:\n\t//  var s = []int{1,2,3}\n\t//  var i string\n\t//  var _ = s[i]\n\tInvalidIndex\n\n\t// SwappedSliceIndices occurs when constant indices in a slice expression\n\t// are decreasing in value.\n\t//\n\t// Example:\n\t//  var _ = []int{1,2,3}[2:1]\n\tSwappedSliceIndices\n\n\t// NonSliceableOperand occurs when a slice operation is applied to a value\n\t// whose type is not sliceable, or is unaddressable.\n\t//\n\t// Example:\n\t//  var x = [...]int{1, 2, 3}[:1]\n\t//\n\t// Example:\n\t//  var x = 1\n\t//  var y = 1[:1]\n\tNonSliceableOperand\n\n\t// InvalidSliceExpr occurs when a three-index slice expression (a[x:y:z]) is\n\t// applied to a string.\n\t//\n\t// Example:\n\t//  var s = \"hello\"\n\t//  var x = s[1:2:3]\n\tInvalidSliceExpr\n\n\t// InvalidShiftCount occurs when the right-hand side of a shift operation is\n\t// either non-integer, negative, or too large.\n\t//\n\t// Example:\n\t//  var (\n\t//  \tx string\n\t//  \ty int = 1 << x\n\t//  )\n\tInvalidShiftCount\n\n\t// InvalidShiftOperand occurs when the shifted operand is not an integer.\n\t//\n\t// Example:\n\t//  var s = \"hello\"\n\t//  var x = s << 2\n\tInvalidShiftOperand\n\n\t// InvalidReceive occurs when there is a channel receive from a value that\n\t// is either not a channel, or is a send-only channel.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tvar x = 1\n\t//  \t<-x\n\t//  }\n\tInvalidReceive\n\n\t// InvalidSend occurs when there is a channel send to a value that is not a\n\t// channel, or is a receive-only channel.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tvar x = 1\n\t//  \tx <- \"hello!\"\n\t//  }\n\tInvalidSend\n\n\t// DuplicateLitKey occurs when an index is duplicated in a slice, array, or\n\t// map literal.\n\t//\n\t// Example:\n\t//  var _ = []int{0:1, 0:2}\n\t//\n\t// Example:\n\t//  var _ = map[string]int{\"a\": 1, \"a\": 2}\n\tDuplicateLitKey\n\n\t// MissingLitKey occurs when a map literal is missing a key expression.\n\t//\n\t// Example:\n\t//  var _ = map[string]int{1}\n\tMissingLitKey\n\n\t// InvalidLitIndex occurs when the key in a key-value element of a slice or\n\t// array literal is not an integer constant.\n\t//\n\t// Example:\n\t//  var i = 0\n\t//  var x = []string{i: \"world\"}\n\tInvalidLitIndex\n\n\t// OversizeArrayLit occurs when an array literal exceeds its length.\n\t//\n\t// Example:\n\t//  var _ = [2]int{1,2,3}\n\tOversizeArrayLit\n\n\t// MixedStructLit occurs when a struct literal contains a mix of positional\n\t// and named elements.\n\t//\n\t// Example:\n\t//  var _ = struct{i, j int}{i: 1, 2}\n\tMixedStructLit\n\n\t// InvalidStructLit occurs when a positional struct literal has an incorrect\n\t// number of values.\n\t//\n\t// Example:\n\t//  var _ = struct{i, j int}{1,2,3}\n\tInvalidStructLit\n\n\t// MissingLitField occurs when a struct literal refers to a field that does\n\t// not exist on the struct type.\n\t//\n\t// Example:\n\t//  var _ = struct{i int}{j: 2}\n\tMissingLitField\n\n\t// DuplicateLitField occurs when a struct literal contains duplicated\n\t// fields.\n\t//\n\t// Example:\n\t//  var _ = struct{i int}{i: 1, i: 2}\n\tDuplicateLitField\n\n\t// UnexportedLitField occurs when a positional struct literal implicitly\n\t// assigns an unexported field of an imported type.\n\tUnexportedLitField\n\n\t// InvalidLitField occurs when a field name is not a valid identifier.\n\t//\n\t// Example:\n\t//  var _ = struct{i int}{1: 1}\n\tInvalidLitField\n\n\t// UntypedLit occurs when a composite literal omits a required type\n\t// identifier.\n\t//\n\t// Example:\n\t//  type outer struct{\n\t//  \tinner struct { i int }\n\t//  }\n\t//\n\t//  var _ = outer{inner: {1}}\n\tUntypedLit\n\n\t// InvalidLit occurs when a composite literal expression does not match its\n\t// type.\n\t//\n\t// Example:\n\t//  type P *struct{\n\t//  \tx int\n\t//  }\n\t//  var _ = P {}\n\tInvalidLit\n\n\t// AmbiguousSelector occurs when a selector is ambiguous.\n\t//\n\t// Example:\n\t//  type E1 struct { i int }\n\t//  type E2 struct { i int }\n\t//  type T struct { E1; E2 }\n\t//\n\t//  var x T\n\t//  var _ = x.i\n\tAmbiguousSelector\n\n\t// UndeclaredImportedName occurs when a package-qualified identifier is\n\t// undeclared by the imported package.\n\t//\n\t// Example:\n\t//  import \"go/types\"\n\t//\n\t//  var _ = types.NotAnActualIdentifier\n\tUndeclaredImportedName\n\n\t// UnexportedName occurs when a selector refers to an unexported identifier\n\t// of an imported package.\n\t//\n\t// Example:\n\t//  import \"reflect\"\n\t//\n\t//  type _ reflect.flag\n\tUnexportedName\n\n\t// UndeclaredName occurs when an identifier is not declared in the current\n\t// scope.\n\t//\n\t// Example:\n\t//  var x T\n\tUndeclaredName\n\n\t// MissingFieldOrMethod occurs when a selector references a field or method\n\t// that does not exist.\n\t//\n\t// Example:\n\t//  type T struct {}\n\t//\n\t//  var x = T{}.f\n\tMissingFieldOrMethod\n\n\t// BadDotDotDotSyntax occurs when a \"...\" occurs in a context where it is\n\t// not valid.\n\t//\n\t// Example:\n\t//  var _ = map[int][...]int{0: {}}\n\tBadDotDotDotSyntax\n\n\t// NonVariadicDotDotDot occurs when a \"...\" is used on the final argument to\n\t// a non-variadic function.\n\t//\n\t// Example:\n\t//  func printArgs(s []string) {\n\t//  \tfor _, a := range s {\n\t//  \t\tprintln(a)\n\t//  \t}\n\t//  }\n\t//\n\t//  func f() {\n\t//  \ts := []string{\"a\", \"b\", \"c\"}\n\t//  \tprintArgs(s...)\n\t//  }\n\tNonVariadicDotDotDot\n\n\t// MisplacedDotDotDot occurs when a \"...\" is used somewhere other than the\n\t// final argument in a function declaration.\n\t//\n\t// Example:\n\t// \tfunc f(...int, int)\n\tMisplacedDotDotDot\n\n\t_ // InvalidDotDotDotOperand was removed.\n\n\t// InvalidDotDotDot occurs when a \"...\" is used in a non-variadic built-in\n\t// function.\n\t//\n\t// Example:\n\t//  var s = []int{1, 2, 3}\n\t//  var l = len(s...)\n\tInvalidDotDotDot\n\n\t// UncalledBuiltin occurs when a built-in function is used as a\n\t// function-valued expression, instead of being called.\n\t//\n\t// Per the spec:\n\t//  \"The built-in functions do not have standard Go types, so they can only\n\t//  appear in call expressions; they cannot be used as function values.\"\n\t//\n\t// Example:\n\t//  var _ = copy\n\tUncalledBuiltin\n\n\t// InvalidAppend occurs when append is called with a first argument that is\n\t// not a slice.\n\t//\n\t// Example:\n\t//  var _ = append(1, 2)\n\tInvalidAppend\n\n\t// InvalidCap occurs when an argument to the cap built-in function is not of\n\t// supported type.\n\t//\n\t// See https://golang.org/ref/spec#Length_and_capacity for information on\n\t// which underlying types are supported as arguments to cap and len.\n\t//\n\t// Example:\n\t//  var s = 2\n\t//  var x = cap(s)\n\tInvalidCap\n\n\t// InvalidClose occurs when close(...) is called with an argument that is\n\t// not of channel type, or that is a receive-only channel.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tvar x int\n\t//  \tclose(x)\n\t//  }\n\tInvalidClose\n\n\t// InvalidCopy occurs when the arguments are not of slice type or do not\n\t// have compatible type.\n\t//\n\t// See https://golang.org/ref/spec#Appending_and_copying_slices for more\n\t// information on the type requirements for the copy built-in.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tvar x []int\n\t//  \ty := []int64{1,2,3}\n\t//  \tcopy(x, y)\n\t//  }\n\tInvalidCopy\n\n\t// InvalidComplex occurs when the complex built-in function is called with\n\t// arguments with incompatible types.\n\t//\n\t// Example:\n\t//  var _ = complex(float32(1), float64(2))\n\tInvalidComplex\n\n\t// InvalidDelete occurs when the delete built-in function is called with a\n\t// first argument that is not a map.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tm := \"hello\"\n\t//  \tdelete(m, \"e\")\n\t//  }\n\tInvalidDelete\n\n\t// InvalidImag occurs when the imag built-in function is called with an\n\t// argument that does not have complex type.\n\t//\n\t// Example:\n\t//  var _ = imag(int(1))\n\tInvalidImag\n\n\t// InvalidLen occurs when an argument to the len built-in function is not of\n\t// supported type.\n\t//\n\t// See https://golang.org/ref/spec#Length_and_capacity for information on\n\t// which underlying types are supported as arguments to cap and len.\n\t//\n\t// Example:\n\t//  var s = 2\n\t//  var x = len(s)\n\tInvalidLen\n\n\t// SwappedMakeArgs occurs when make is called with three arguments, and its\n\t// length argument is larger than its capacity argument.\n\t//\n\t// Example:\n\t//  var x = make([]int, 3, 2)\n\tSwappedMakeArgs\n\n\t// InvalidMake occurs when make is called with an unsupported type argument.\n\t//\n\t// See https://golang.org/ref/spec#Making_slices_maps_and_channels for\n\t// information on the types that may be created using make.\n\t//\n\t// Example:\n\t//  var x = make(int)\n\tInvalidMake\n\n\t// InvalidReal occurs when the real built-in function is called with an\n\t// argument that does not have complex type.\n\t//\n\t// Example:\n\t//  var _ = real(int(1))\n\tInvalidReal\n\n\t// InvalidAssert occurs when a type assertion is applied to a\n\t// value that is not of interface type.\n\t//\n\t// Example:\n\t//  var x = 1\n\t//  var _ = x.(float64)\n\tInvalidAssert\n\n\t// ImpossibleAssert occurs for a type assertion x.(T) when the value x of\n\t// interface cannot have dynamic type T, due to a missing or mismatching\n\t// method on T.\n\t//\n\t// Example:\n\t//  type T int\n\t//\n\t//  func (t *T) m() int { return int(*t) }\n\t//\n\t//  type I interface { m() int }\n\t//\n\t//  var x I\n\t//  var _ = x.(T)\n\tImpossibleAssert\n\n\t// InvalidConversion occurs when the argument type cannot be converted to the\n\t// target.\n\t//\n\t// See https://golang.org/ref/spec#Conversions for the rules of\n\t// convertibility.\n\t//\n\t// Example:\n\t//  var x float64\n\t//  var _ = string(x)\n\tInvalidConversion\n\n\t// InvalidUntypedConversion occurs when there is no valid implicit\n\t// conversion from an untyped value satisfying the type constraints of the\n\t// context in which it is used.\n\t//\n\t// Example:\n\t//  var _ = 1 + []int{}\n\tInvalidUntypedConversion\n\n\t// BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument\n\t// that is not a selector expression.\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  var x int\n\t//  var _ = unsafe.Offsetof(x)\n\tBadOffsetofSyntax\n\n\t// InvalidOffsetof occurs when unsafe.Offsetof is called with a method\n\t// selector, rather than a field selector, or when the field is embedded via\n\t// a pointer.\n\t//\n\t// Per the spec:\n\t//\n\t//  \"If f is an embedded field, it must be reachable without pointer\n\t//  indirections through fields of the struct. \"\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  type T struct { f int }\n\t//  type S struct { *T }\n\t//  var s S\n\t//  var _ = unsafe.Offsetof(s.f)\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  type S struct{}\n\t//\n\t//  func (S) m() {}\n\t//\n\t//  var s S\n\t//  var _ = unsafe.Offsetof(s.m)\n\tInvalidOffsetof\n\n\t// UnusedExpr occurs when a side-effect free expression is used as a\n\t// statement. Such a statement has no effect.\n\t//\n\t// Example:\n\t//  func f(i int) {\n\t//  \ti*i\n\t//  }\n\tUnusedExpr\n\n\t// UnusedVar occurs when a variable is declared but unused.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tx := 1\n\t//  }\n\tUnusedVar\n\n\t// MissingReturn occurs when a function with results is missing a return\n\t// statement.\n\t//\n\t// Example:\n\t//  func f() int {}\n\tMissingReturn\n\n\t// WrongResultCount occurs when a return statement returns an incorrect\n\t// number of values.\n\t//\n\t// Example:\n\t//  func ReturnOne() int {\n\t//  \treturn 1, 2\n\t//  }\n\tWrongResultCount\n\n\t// OutOfScopeResult occurs when the name of a value implicitly returned by\n\t// an empty return statement is shadowed in a nested scope.\n\t//\n\t// Example:\n\t//  func factor(n int) (i int) {\n\t//  \tfor i := 2; i < n; i++ {\n\t//  \t\tif n%i == 0 {\n\t//  \t\t\treturn\n\t//  \t\t}\n\t//  \t}\n\t//  \treturn 0\n\t//  }\n\tOutOfScopeResult\n\n\t// InvalidCond occurs when an if condition is not a boolean expression.\n\t//\n\t// Example:\n\t//  func checkReturn(i int) {\n\t//  \tif i {\n\t//  \t\tpanic(\"non-zero return\")\n\t//  \t}\n\t//  }\n\tInvalidCond\n\n\t// InvalidPostDecl occurs when there is a declaration in a for-loop post\n\t// statement.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tfor i := 0; i < 10; j := 0 {}\n\t//  }\n\tInvalidPostDecl\n\n\t_ // InvalidChanRange was removed.\n\n\t// InvalidIterVar occurs when two iteration variables are used while ranging\n\t// over a channel.\n\t//\n\t// Example:\n\t//  func f(c chan int) {\n\t//  \tfor k, v := range c {\n\t//  \t\tprintln(k, v)\n\t//  \t}\n\t//  }\n\tInvalidIterVar\n\n\t// InvalidRangeExpr occurs when the type of a range expression is not\n\t// a valid type for use with a range loop.\n\t//\n\t// Example:\n\t//  func f(f float64) {\n\t//  \tfor j := range f {\n\t//  \t\tprintln(j)\n\t//  \t}\n\t//  }\n\tInvalidRangeExpr\n\n\t// MisplacedBreak occurs when a break statement is not within a for, switch,\n\t// or select statement of the innermost function definition.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tbreak\n\t//  }\n\tMisplacedBreak\n\n\t// MisplacedContinue occurs when a continue statement is not within a for\n\t// loop of the innermost function definition.\n\t//\n\t// Example:\n\t//  func sumeven(n int) int {\n\t//  \tproceed := func() {\n\t//  \t\tcontinue\n\t//  \t}\n\t//  \tsum := 0\n\t//  \tfor i := 1; i <= n; i++ {\n\t//  \t\tif i % 2 != 0 {\n\t//  \t\t\tproceed()\n\t//  \t\t}\n\t//  \t\tsum += i\n\t//  \t}\n\t//  \treturn sum\n\t//  }\n\tMisplacedContinue\n\n\t// MisplacedFallthrough occurs when a fallthrough statement is not within an\n\t// expression switch.\n\t//\n\t// Example:\n\t//  func typename(i interface{}) string {\n\t//  \tswitch i.(type) {\n\t//  \tcase int64:\n\t//  \t\tfallthrough\n\t//  \tcase int:\n\t//  \t\treturn \"int\"\n\t//  \t}\n\t//  \treturn \"unsupported\"\n\t//  }\n\tMisplacedFallthrough\n\n\t// DuplicateCase occurs when a type or expression switch has duplicate\n\t// cases.\n\t//\n\t// Example:\n\t//  func printInt(i int) {\n\t//  \tswitch i {\n\t//  \tcase 1:\n\t//  \t\tprintln(\"one\")\n\t//  \tcase 1:\n\t//  \t\tprintln(\"One\")\n\t//  \t}\n\t//  }\n\tDuplicateCase\n\n\t// DuplicateDefault occurs when a type or expression switch has multiple\n\t// default clauses.\n\t//\n\t// Example:\n\t//  func printInt(i int) {\n\t//  \tswitch i {\n\t//  \tcase 1:\n\t//  \t\tprintln(\"one\")\n\t//  \tdefault:\n\t//  \t\tprintln(\"One\")\n\t//  \tdefault:\n\t//  \t\tprintln(\"1\")\n\t//  \t}\n\t//  }\n\tDuplicateDefault\n\n\t// BadTypeKeyword occurs when a .(type) expression is used anywhere other\n\t// than a type switch.\n\t//\n\t// Example:\n\t//  type I interface {\n\t//  \tm()\n\t//  }\n\t//  var t I\n\t//  var _ = t.(type)\n\tBadTypeKeyword\n\n\t// InvalidTypeSwitch occurs when .(type) is used on an expression that is\n\t// not of interface type.\n\t//\n\t// Example:\n\t//  func f(i int) {\n\t//  \tswitch x := i.(type) {}\n\t//  }\n\tInvalidTypeSwitch\n\n\t// InvalidExprSwitch occurs when a switch expression is not comparable.\n\t//\n\t// Example:\n\t//  func _() {\n\t//  \tvar a struct{ _ func() }\n\t//  \tswitch a /* ERROR cannot switch on a */ {\n\t//  \t}\n\t//  }\n\tInvalidExprSwitch\n\n\t// InvalidSelectCase occurs when a select case is not a channel send or\n\t// receive.\n\t//\n\t// Example:\n\t//  func checkChan(c <-chan int) bool {\n\t//  \tselect {\n\t//  \tcase c:\n\t//  \t\treturn true\n\t//  \tdefault:\n\t//  \t\treturn false\n\t//  \t}\n\t//  }\n\tInvalidSelectCase\n\n\t// UndeclaredLabel occurs when an undeclared label is jumped to.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  \tgoto L\n\t//  }\n\tUndeclaredLabel\n\n\t// DuplicateLabel occurs when a label is declared more than once.\n\t//\n\t// Example:\n\t//  func f() int {\n\t//  L:\n\t//  L:\n\t//  \treturn 1\n\t//  }\n\tDuplicateLabel\n\n\t// MisplacedLabel occurs when a break or continue label is not on a for,\n\t// switch, or select statement.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  L:\n\t//  \ta := []int{1,2,3}\n\t//  \tfor _, e := range a {\n\t//  \t\tif e > 10 {\n\t//  \t\t\tbreak L\n\t//  \t\t}\n\t//  \t\tprintln(a)\n\t//  \t}\n\t//  }\n\tMisplacedLabel\n\n\t// UnusedLabel occurs when a label is declared and not used.\n\t//\n\t// Example:\n\t//  func f() {\n\t//  L:\n\t//  }\n\tUnusedLabel\n\n\t// JumpOverDecl occurs when a label jumps over a variable declaration.\n\t//\n\t// Example:\n\t//  func f() int {\n\t//  \tgoto L\n\t//  \tx := 2\n\t//  L:\n\t//  \tx++\n\t//  \treturn x\n\t//  }\n\tJumpOverDecl\n\n\t// JumpIntoBlock occurs when a forward jump goes to a label inside a nested\n\t// block.\n\t//\n\t// Example:\n\t//  func f(x int) {\n\t//  \tgoto L\n\t//  \tif x > 0 {\n\t//  \tL:\n\t//  \t\tprint(\"inside block\")\n\t//  \t}\n\t// }\n\tJumpIntoBlock\n\n\t// InvalidMethodExpr occurs when a pointer method is called but the argument\n\t// is not addressable.\n\t//\n\t// Example:\n\t//  type T struct {}\n\t//\n\t//  func (*T) m() int { return 1 }\n\t//\n\t//  var _ = T.m(T{})\n\tInvalidMethodExpr\n\n\t// WrongArgCount occurs when too few or too many arguments are passed by a\n\t// function call.\n\t//\n\t// Example:\n\t//  func f(i int) {}\n\t//  var x = f()\n\tWrongArgCount\n\n\t// InvalidCall occurs when an expression is called that is not of function\n\t// type.\n\t//\n\t// Example:\n\t//  var x = \"x\"\n\t//  var y = x()\n\tInvalidCall\n\n\t// UnusedResults occurs when a restricted expression-only built-in function\n\t// is suspended via go or defer. Such a suspension discards the results of\n\t// these side-effect free built-in functions, and therefore is ineffectual.\n\t//\n\t// Example:\n\t//  func f(a []int) int {\n\t//  \tdefer len(a)\n\t//  \treturn i\n\t//  }\n\tUnusedResults\n\n\t// InvalidDefer occurs when a deferred expression is not a function call,\n\t// for example if the expression is a type conversion.\n\t//\n\t// Example:\n\t//  func f(i int) int {\n\t//  \tdefer int32(i)\n\t//  \treturn i\n\t//  }\n\tInvalidDefer\n\n\t// InvalidGo occurs when a go expression is not a function call, for example\n\t// if the expression is a type conversion.\n\t//\n\t// Example:\n\t//  func f(i int) int {\n\t//  \tgo int32(i)\n\t//  \treturn i\n\t//  }\n\tInvalidGo\n\n\t// All codes below were added in Go 1.17.\n\n\t// BadDecl occurs when a declaration has invalid syntax.\n\tBadDecl\n\n\t// RepeatedDecl occurs when an identifier occurs more than once on the left\n\t// hand side of a short variable declaration.\n\t//\n\t// Example:\n\t//  func _() {\n\t//  \tx, y, y := 1, 2, 3\n\t//  }\n\tRepeatedDecl\n\n\t// InvalidUnsafeAdd occurs when unsafe.Add is called with a\n\t// length argument that is not of integer type.\n\t// It also occurs if it is used in a package compiled for a\n\t// language version before go1.17.\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  var p unsafe.Pointer\n\t//  var _ = unsafe.Add(p, float64(1))\n\tInvalidUnsafeAdd\n\n\t// InvalidUnsafeSlice occurs when unsafe.Slice is called with a\n\t// pointer argument that is not of pointer type or a length argument\n\t// that is not of integer type, negative, or out of bounds.\n\t// It also occurs if it is used in a package compiled for a language\n\t// version before go1.17.\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  var x int\n\t//  var _ = unsafe.Slice(x, 1)\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  var x int\n\t//  var _ = unsafe.Slice(&x, float64(1))\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  var x int\n\t//  var _ = unsafe.Slice(&x, -1)\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  var x int\n\t//  var _ = unsafe.Slice(&x, uint64(1) << 63)\n\tInvalidUnsafeSlice\n\n\t// All codes below were added in Go 1.18.\n\n\t// UnsupportedFeature occurs when a language feature is used that is not\n\t// supported at this Go version.\n\tUnsupportedFeature\n\n\t// NotAGenericType occurs when a non-generic type is used where a generic\n\t// type is expected: in type or function instantiation.\n\t//\n\t// Example:\n\t//  type T int\n\t//\n\t//  var _ T[int]\n\tNotAGenericType\n\n\t// WrongTypeArgCount occurs when a type or function is instantiated with an\n\t// incorrect number of type arguments, including when a generic type or\n\t// function is used without instantiation.\n\t//\n\t// Errors involving failed type inference are assigned other error codes.\n\t//\n\t// Example:\n\t//  type T[p any] int\n\t//\n\t//  var _ T[int, string]\n\t//\n\t// Example:\n\t//  func f[T any]() {}\n\t//\n\t//  var x = f\n\tWrongTypeArgCount\n\n\t// CannotInferTypeArgs occurs when type or function type argument inference\n\t// fails to infer all type arguments.\n\t//\n\t// Example:\n\t//  func f[T any]() {}\n\t//\n\t//  func _() {\n\t//  \tf()\n\t//  }\n\tCannotInferTypeArgs\n\n\t// InvalidTypeArg occurs when a type argument does not satisfy its\n\t// corresponding type parameter constraints.\n\t//\n\t// Example:\n\t//  type T[P ~int] struct{}\n\t//\n\t//  var _ T[string]\n\tInvalidTypeArg // arguments? InferenceFailed\n\n\t// InvalidInstanceCycle occurs when an invalid cycle is detected\n\t// within the instantiation graph.\n\t//\n\t// Example:\n\t//  func f[T any]() { f[*T]() }\n\tInvalidInstanceCycle\n\n\t// InvalidUnion occurs when an embedded union or approximation element is\n\t// not valid.\n\t//\n\t// Example:\n\t//  type _ interface {\n\t//   \t~int | interface{ m() }\n\t//  }\n\tInvalidUnion\n\n\t// MisplacedConstraintIface occurs when a constraint-type interface is used\n\t// outside of constraint position.\n\t//\n\t// Example:\n\t//   type I interface { ~int }\n\t//\n\t//   var _ I\n\tMisplacedConstraintIface\n\n\t// InvalidMethodTypeParams occurs when methods have type parameters.\n\t//\n\t// It cannot be encountered with an AST parsed using go/parser.\n\tInvalidMethodTypeParams\n\n\t// MisplacedTypeParam occurs when a type parameter is used in a place where\n\t// it is not permitted.\n\t//\n\t// Example:\n\t//  type T[P any] P\n\t//\n\t// Example:\n\t//  type T[P any] struct{ *P }\n\tMisplacedTypeParam\n\n\t// InvalidUnsafeSliceData occurs when unsafe.SliceData is called with\n\t// an argument that is not of slice type. It also occurs if it is used\n\t// in a package compiled for a language version before go1.20.\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  var x int\n\t//  var _ = unsafe.SliceData(x)\n\tInvalidUnsafeSliceData\n\n\t// InvalidUnsafeString occurs when unsafe.String is called with\n\t// a length argument that is not of integer type, negative, or\n\t// out of bounds. It also occurs if it is used in a package\n\t// compiled for a language version before go1.20.\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  var b [10]byte\n\t//  var _ = unsafe.String(&b[0], -1)\n\tInvalidUnsafeString\n\n\t// InvalidUnsafeStringData occurs if it is used in a package\n\t// compiled for a language version before go1.20.\n\t_ // not used anymore\n\n\t// InvalidClear occurs when clear is called with an argument\n\t// that is not of map or slice type.\n\t//\n\t// Example:\n\t//  func _(x int) {\n\t//  \tclear(x)\n\t//  }\n\tInvalidClear\n\n\t// TypeTooLarge occurs if unsafe.Sizeof or unsafe.Offsetof is\n\t// called with an expression whose type is too large.\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  type E [1 << 31 - 1]int\n\t//  var a [1 << 31]E\n\t//  var _ = unsafe.Sizeof(a)\n\t//\n\t// Example:\n\t//  import \"unsafe\"\n\t//\n\t//  type E [1 << 31 - 1]int\n\t//  var s struct {\n\t//  \t_ [1 << 31]E\n\t//  \tx int\n\t//  }\n\t// var _ = unsafe.Offsetof(s.x)\n\tTypeTooLarge\n\n\t// InvalidMinMaxOperand occurs if min or max is called\n\t// with an operand that cannot be ordered because it\n\t// does not support the < operator.\n\t//\n\t// Example:\n\t//  const _ = min(true)\n\t//\n\t// Example:\n\t//  var s, t []byte\n\t//  var _ = max(s, t)\n\tInvalidMinMaxOperand\n\n\t// TooNew indicates that, through build tags or a go.mod file,\n\t// a source file requires a version of Go that is newer than\n\t// the logic of the type checker. As a consequence, the type\n\t// checker may produce spurious errors or fail to report real\n\t// errors. The solution is to rebuild the application with a\n\t// newer Go release.\n\tTooNew\n)\n"
  },
  {
    "path": "x/typesutil/eval.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\nimport (\n\t\"go/types\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\n// CheckExpr type checks the expression expr as if it had appeared at position\n// pos of package pkg. Type information about the expression is recorded in\n// info. The expression may be an identifier denoting an uninstantiated generic\n// function or type.\n//\n// If pkg == nil, the Universe scope is used and the provided\n// position pos is ignored. If pkg != nil, and pos is invalid,\n// the package scope is used. Otherwise, pos must belong to the\n// package.\n//\n// An error is returned if pos is not within the package or\n// if the node cannot be type-checked.\n//\n// Note: Eval and CheckExpr should not be used instead of running Check\n// to compute types and values, but in addition to Check, as these\n// functions ignore the context in which an expression is used (e.g., an\n// assignment). Thus, top-level untyped constants will return an\n// untyped type rather than the respective context-specific type.\nfunc CheckExpr(fset *token.FileSet, pkg *types.Package, pos token.Pos, expr ast.Expr, info *Info) (err error) {\n\tpanic(\"todo\")\n}\n"
  },
  {
    "path": "x/typesutil/exprstring.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// This file implements printing of expressions.\n\npackage typesutil\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/x/typesutil/typeparams\"\n)\n\n// ExprString returns the (possibly shortened) string representation for x.\n// Shortened representations are suitable for user interfaces but may not\n// necessarily follow Go syntax.\nfunc ExprString(x ast.Expr) string {\n\tvar buf bytes.Buffer\n\tWriteExpr(&buf, x)\n\treturn buf.String()\n}\n\n// WriteExpr writes the (possibly shortened) string representation for x to buf.\n// Shortened representations are suitable for user interfaces but may not\n// necessarily follow Go syntax.\nfunc WriteExpr(buf *bytes.Buffer, x ast.Expr) {\n\t// The AST preserves source-level parentheses so there is\n\t// no need to introduce them here to correct for different\n\t// operator precedences. (This assumes that the AST was\n\t// generated by a Go parser.)\n\n\tswitch x := x.(type) {\n\tdefault:\n\t\tfmt.Fprintf(buf, \"(ast: %T)\", x) // nil, ast.BadExpr, ast.KeyValueExpr\n\n\tcase *ast.Ident:\n\t\tbuf.WriteString(x.Name)\n\n\tcase *ast.Ellipsis:\n\t\tbuf.WriteString(\"...\")\n\t\tif x.Elt != nil {\n\t\t\tWriteExpr(buf, x.Elt)\n\t\t}\n\n\tcase *ast.BasicLit:\n\t\tbuf.WriteString(x.Value)\n\n\tcase *ast.FuncLit:\n\t\tbuf.WriteByte('(')\n\t\tWriteExpr(buf, x.Type)\n\t\tbuf.WriteString(\" literal)\") // shortened\n\n\tcase *ast.CompositeLit:\n\t\tWriteExpr(buf, x.Type)\n\t\tbuf.WriteByte('{')\n\t\tif len(x.Elts) > 0 {\n\t\t\tbuf.WriteString(\"…\")\n\t\t}\n\t\tbuf.WriteByte('}')\n\n\tcase *ast.ParenExpr:\n\t\tbuf.WriteByte('(')\n\t\tWriteExpr(buf, x.X)\n\t\tbuf.WriteByte(')')\n\n\tcase *ast.SelectorExpr:\n\t\tWriteExpr(buf, x.X)\n\t\tbuf.WriteByte('.')\n\t\tbuf.WriteString(x.Sel.Name)\n\n\tcase *ast.IndexExpr, *ast.IndexListExpr:\n\t\tix := typeparams.UnpackIndexExpr(x)\n\t\tWriteExpr(buf, ix.X)\n\t\tbuf.WriteByte('[')\n\t\twriteExprList(buf, ix.Indices)\n\t\tbuf.WriteByte(']')\n\n\tcase *ast.SliceExpr:\n\t\tWriteExpr(buf, x.X)\n\t\tbuf.WriteByte('[')\n\t\tif x.Low != nil {\n\t\t\tWriteExpr(buf, x.Low)\n\t\t}\n\t\tbuf.WriteByte(':')\n\t\tif x.High != nil {\n\t\t\tWriteExpr(buf, x.High)\n\t\t}\n\t\tif x.Slice3 {\n\t\t\tbuf.WriteByte(':')\n\t\t\tif x.Max != nil {\n\t\t\t\tWriteExpr(buf, x.Max)\n\t\t\t}\n\t\t}\n\t\tbuf.WriteByte(']')\n\n\tcase *ast.TypeAssertExpr:\n\t\tWriteExpr(buf, x.X)\n\t\tbuf.WriteString(\".(\")\n\t\tWriteExpr(buf, x.Type)\n\t\tbuf.WriteByte(')')\n\n\tcase *ast.CallExpr:\n\t\tWriteExpr(buf, x.Fun)\n\t\tbuf.WriteByte('(')\n\t\twriteExprList(buf, x.Args)\n\t\tif x.Ellipsis.IsValid() {\n\t\t\tbuf.WriteString(\"...\")\n\t\t}\n\t\tbuf.WriteByte(')')\n\n\tcase *ast.StarExpr:\n\t\tbuf.WriteByte('*')\n\t\tWriteExpr(buf, x.X)\n\n\tcase *ast.UnaryExpr:\n\t\tbuf.WriteString(x.Op.String())\n\t\tWriteExpr(buf, x.X)\n\n\tcase *ast.BinaryExpr:\n\t\tWriteExpr(buf, x.X)\n\t\tbuf.WriteByte(' ')\n\t\tbuf.WriteString(x.Op.String())\n\t\tbuf.WriteByte(' ')\n\t\tWriteExpr(buf, x.Y)\n\n\tcase *ast.ArrayType:\n\t\tbuf.WriteByte('[')\n\t\tif x.Len != nil {\n\t\t\tWriteExpr(buf, x.Len)\n\t\t}\n\t\tbuf.WriteByte(']')\n\t\tWriteExpr(buf, x.Elt)\n\n\tcase *ast.StructType:\n\t\tbuf.WriteString(\"struct{\")\n\t\twriteFieldList(buf, x.Fields.List, \"; \", false)\n\t\tbuf.WriteByte('}')\n\n\tcase *ast.FuncType:\n\t\tbuf.WriteString(\"func\")\n\t\twriteSigExpr(buf, x)\n\n\tcase *ast.InterfaceType:\n\t\tbuf.WriteString(\"interface{\")\n\t\twriteFieldList(buf, x.Methods.List, \"; \", true)\n\t\tbuf.WriteByte('}')\n\n\tcase *ast.MapType:\n\t\tbuf.WriteString(\"map[\")\n\t\tWriteExpr(buf, x.Key)\n\t\tbuf.WriteByte(']')\n\t\tWriteExpr(buf, x.Value)\n\n\tcase *ast.ChanType:\n\t\tvar s string\n\t\tswitch x.Dir {\n\t\tcase ast.SEND:\n\t\t\ts = \"chan<- \"\n\t\tcase ast.RECV:\n\t\t\ts = \"<-chan \"\n\t\tdefault:\n\t\t\ts = \"chan \"\n\t\t}\n\t\tbuf.WriteString(s)\n\t\tWriteExpr(buf, x.Value)\n\t}\n}\n\nfunc writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {\n\tbuf.WriteByte('(')\n\twriteFieldList(buf, sig.Params.List, \", \", false)\n\tbuf.WriteByte(')')\n\n\tres := sig.Results\n\tn := res.NumFields()\n\tif n == 0 {\n\t\t// no result\n\t\treturn\n\t}\n\n\tbuf.WriteByte(' ')\n\tif n == 1 && len(res.List[0].Names) == 0 {\n\t\t// single unnamed result\n\t\tWriteExpr(buf, res.List[0].Type)\n\t\treturn\n\t}\n\n\t// multiple or named result(s)\n\tbuf.WriteByte('(')\n\twriteFieldList(buf, res.List, \", \", false)\n\tbuf.WriteByte(')')\n}\n\nfunc writeFieldList(buf *bytes.Buffer, list []*ast.Field, sep string, iface bool) {\n\tfor i, f := range list {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(sep)\n\t\t}\n\n\t\t// field list names\n\t\twriteIdentList(buf, f.Names)\n\n\t\t// types of interface methods consist of signatures only\n\t\tif sig, _ := f.Type.(*ast.FuncType); sig != nil && iface {\n\t\t\twriteSigExpr(buf, sig)\n\t\t\tcontinue\n\t\t}\n\n\t\t// named fields are separated with a blank from the field type\n\t\tif len(f.Names) > 0 {\n\t\t\tbuf.WriteByte(' ')\n\t\t}\n\n\t\tWriteExpr(buf, f.Type)\n\n\t\t// ignore tag\n\t}\n}\n\nfunc writeIdentList(buf *bytes.Buffer, list []*ast.Ident) {\n\tfor i, x := range list {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\tbuf.WriteString(x.Name)\n\t}\n}\n\nfunc writeExprList(buf *bytes.Buffer, list []ast.Expr) {\n\tfor i, x := range list {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\tWriteExpr(buf, x)\n\t}\n}\n"
  },
  {
    "path": "x/typesutil/exprstring_test.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage typesutil_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/x/typesutil\"\n)\n\ntype testEntry struct {\n\tsrc, str string\n}\n\n// dup returns a testEntry where both src and str are the same.\nfunc dup(s string) testEntry {\n\treturn testEntry{s, s}\n}\n\nvar testExprs = []testEntry{\n\t// basic type literals\n\tdup(\"x\"),\n\tdup(\"true\"),\n\tdup(\"42\"),\n\tdup(\"3.1415\"),\n\tdup(\"2.71828i\"),\n\tdup(`'a'`),\n\tdup(`\"foo\"`),\n\tdup(\"`bar`\"),\n\n\t// func and composite literals\n\t{\"func(){}\", \"(func() literal)\"},\n\t{\"func(x int) complex128 {}\", \"(func(x int) complex128 literal)\"},\n\t{\"[]int{1, 2, 3}\", \"[]int{…}\"},\n\n\t// type expressions\n\tdup(\"[1 << 10]byte\"),\n\tdup(\"[]int\"),\n\tdup(\"*int\"),\n\tdup(\"struct{x int}\"),\n\tdup(\"func()\"),\n\tdup(\"func(int, float32) string\"),\n\tdup(\"interface{m()}\"),\n\tdup(\"interface{m() string; n(x int)}\"),\n\n\tdup(\"map[string]int\"),\n\tdup(\"chan E\"),\n\tdup(\"<-chan E\"),\n\tdup(\"chan<- E\"),\n\n\t// non-type expressions\n\tdup(\"(x)\"),\n\tdup(\"x.f\"),\n\tdup(\"a[i]\"),\n\n\tdup(\"s[:]\"),\n\tdup(\"s[i:]\"),\n\tdup(\"s[:j]\"),\n\tdup(\"s[i:j]\"),\n\tdup(\"s[:j:k]\"),\n\tdup(\"s[i:j:k]\"),\n\n\tdup(\"x.(T)\"),\n\n\tdup(\"x.([10]int)\"),\n\tdup(\"x.([...]int)\"),\n\n\tdup(\"x.(struct{})\"),\n\tdup(\"x.(struct{x int; y, z float32; E})\"),\n\n\tdup(\"x.(func())\"),\n\tdup(\"x.(func(x int))\"),\n\tdup(\"x.(func() int)\"),\n\tdup(\"x.(func(x, y int, z float32) (r int))\"),\n\tdup(\"x.(func(a, b, c int))\"),\n\tdup(\"x.(func(x ...T))\"),\n\n\tdup(\"x.(interface{})\"),\n\tdup(\"x.(interface{m(); n(x int); E})\"),\n\tdup(\"x.(interface{m(); n(x int) T; E; F})\"),\n\n\tdup(\"x.(map[K]V)\"),\n\n\tdup(\"x.(chan E)\"),\n\tdup(\"x.(<-chan E)\"),\n\tdup(\"x.(chan<- chan int)\"),\n\tdup(\"x.(chan<- <-chan int)\"),\n\tdup(\"x.(<-chan chan int)\"),\n\tdup(\"x.(chan (<-chan int))\"),\n\n\tdup(\"f()\"),\n\tdup(\"f(x)\"),\n\tdup(\"int(x)\"),\n\tdup(\"f(x, x + y)\"),\n\tdup(\"f(s...)\"),\n\tdup(\"f(a, s...)\"),\n\n\tdup(\"*x\"),\n\tdup(\"&x\"),\n\tdup(\"x + y\"),\n\tdup(\"x + y << (2 * s)\"),\n}\n\nfunc TestExprString(t *testing.T) {\n\tfor _, test := range testExprs {\n\t\tx, err := parser.ParseExpr(test.src)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %s\", test.src, err)\n\t\t\tcontinue\n\t\t}\n\t\tif got := typesutil.ExprString(x); got != test.str {\n\t\t\tt.Errorf(\"%s: got %s, want %s\", test.src, got, test.str)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "x/typesutil/gopinfo.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/cl\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/qiniu/x/log\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Info holds result type information for a type-checked package.\n// Only the information for which a map is provided is collected.\n// If the package has type errors, the collected information may\n// be incomplete.\ntype Info struct {\n\t// Types maps expressions to their types, and for constant\n\t// expressions, also their values. Invalid expressions are\n\t// omitted.\n\t//\n\t// For (possibly parenthesized) identifiers denoting built-in\n\t// functions, the recorded signatures are call-site specific:\n\t// if the call result is not a constant, the recorded type is\n\t// an argument-specific signature. Otherwise, the recorded type\n\t// is invalid.\n\t//\n\t// The Types map does not record the type of every identifier,\n\t// only those that appear where an arbitrary expression is\n\t// permitted. For instance, the identifier f in a selector\n\t// expression x.f is found only in the Selections map, the\n\t// identifier z in a variable declaration 'var z int' is found\n\t// only in the Defs map, and identifiers denoting packages in\n\t// qualified identifiers are collected in the Uses map.\n\tTypes map[ast.Expr]types.TypeAndValue\n\n\t// Instances maps identifiers denoting generic types or functions to their\n\t// type arguments and instantiated type.\n\t//\n\t// For example, Instances will map the identifier for 'T' in the type\n\t// instantiation T[int, string] to the type arguments [int, string] and\n\t// resulting instantiated *Named type. Given a generic function\n\t// func F[A any](A), Instances will map the identifier for 'F' in the call\n\t// expression F(int(1)) to the inferred type arguments [int], and resulting\n\t// instantiated *Signature.\n\t//\n\t// Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs\n\t// results in an equivalent of Instances[id].Type.\n\tInstances map[*ast.Ident]types.Instance\n\n\t// Defs maps identifiers to the objects they define (including\n\t// package names, dots \".\" of dot-imports, and blank \"_\" identifiers).\n\t// For identifiers that do not denote objects (e.g., the package name\n\t// in package clauses, or symbolic variables t in t := x.(type) of\n\t// type switch headers), the corresponding objects are nil.\n\t//\n\t// For an embedded field, Defs returns the field *Var it defines.\n\t//\n\t// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()\n\tDefs map[*ast.Ident]types.Object\n\n\t// Uses maps identifiers to the objects they denote.\n\t//\n\t// For an embedded field, Uses returns the *TypeName it denotes.\n\t//\n\t// Invariant: Uses[id].Pos() != id.Pos()\n\tUses map[*ast.Ident]types.Object\n\n\t// Implicits maps nodes to their implicitly declared objects, if any.\n\t// The following node and object types may appear:\n\t//\n\t//     node               declared object\n\t//\n\t//     *ast.ImportSpec    *PkgName for imports without renames\n\t//     *ast.CaseClause    type-specific *Var for each type switch case clause (incl. default)\n\t//     *ast.Field         anonymous parameter *Var (incl. unnamed results)\n\t//     *ast.FunLit        function literal in *ast.OverloadFuncDecl\n\t//\n\tImplicits map[ast.Node]types.Object\n\n\t// Selections maps selector expressions (excluding qualified identifiers)\n\t// to their corresponding selections.\n\tSelections map[*ast.SelectorExpr]*types.Selection\n\n\t// Scopes maps ast.Nodes to the scopes they define. Package scopes are not\n\t// associated with a specific node but with all files belonging to a package.\n\t// Thus, the package scope can be found in the type-checked Package object.\n\t// Scopes nest, with the Universe scope being the outermost scope, enclosing\n\t// the package scope, which contains (one or more) files scopes, which enclose\n\t// function scopes which in turn enclose statement and function literal scopes.\n\t// Note that even though package-level functions are declared in the package\n\t// scope, the function scopes are embedded in the file scope of the file\n\t// containing the function declaration.\n\t//\n\t// The following node types may appear in Scopes:\n\t//\n\t//     *ast.File\n\t//     *ast.FuncType\n\t//     *ast.TypeSpec\n\t//     *ast.BlockStmt\n\t//     *ast.IfStmt\n\t//     *ast.SwitchStmt\n\t//     *ast.TypeSwitchStmt\n\t//     *ast.CaseClause\n\t//     *ast.CommClause\n\t//     *ast.ForStmt\n\t//     *ast.RangeStmt\n\t//     *ast.ForPhraseStmt\n\t//     *ast.ForPhrase\n\t//     *ast.LambdaExpr\n\t//     *ast.LambdaExpr2\n\t//\n\tScopes map[ast.Node]*types.Scope\n\n\t// InitOrder is the list of package-level initializers in the order in which\n\t// they must be executed. Initializers referring to variables related by an\n\t// initialization dependency appear in topological order, the others appear\n\t// in source order. Variables without an initialization expression do not\n\t// appear in this list.\n\t// InitOrder []*Initializer\n\n\t// Overloads maps identifiers to the overload decl object.\n\tOverloads map[*ast.Ident]types.Object\n}\n\n// ObjectOf returns the object denoted by the specified id,\n// or nil if not found.\n//\n// If id is an embedded struct field, ObjectOf returns the field (*Var)\n// it defines, not the type (*TypeName) it uses.\n//\n// Precondition: the Uses and Defs maps are populated.\nfunc (info *Info) ObjectOf(id *ast.Ident) types.Object {\n\tif obj := info.Defs[id]; obj != nil {\n\t\treturn obj\n\t}\n\treturn info.Uses[id]\n}\n\n// TypeOf returns the type of expression e, or nil if not found.\n// Precondition: the Types, Uses and Defs maps are populated.\nfunc (info *Info) TypeOf(e ast.Expr) types.Type {\n\tif t, ok := info.Types[e]; ok {\n\t\treturn t.Type\n\t}\n\tif id, _ := e.(*ast.Ident); id != nil {\n\t\tif obj := info.ObjectOf(id); obj != nil {\n\t\t\treturn obj.Type()\n\t\t}\n\t}\n\treturn nil\n}\n\n// Returns the overloaded function declaration corresponding to the ident and its overloaded function members\nfunc (info *Info) OverloadOf(id *ast.Ident) (types.Object, []types.Object) {\n\tif obj := info.Overloads[id]; obj != nil {\n\t\tif sig, ok := obj.Type().(*types.Signature); ok {\n\t\t\tif _, objs := gogen.CheckSigFuncExObjects(sig); len(objs) > 1 {\n\t\t\t\treturn obj, objs\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n\n// -----------------------------------------------------------------------------\n\ntype xgoRecorder struct {\n\t*Info\n}\n\n// NewRecorder creates a new recorder for cl.NewPackage.\nfunc NewRecorder(info *Info) cl.Recorder {\n\treturn xgoRecorder{info}\n}\n\n// Type maps expressions to their types, and for constant\n// expressions, also their values. Invalid expressions are\n// omitted.\n//\n// For (possibly parenthesized) identifiers denoting built-in\n// functions, the recorded signatures are call-site specific:\n// if the call result is not a constant, the recorded type is\n// an argument-specific signature. Otherwise, the recorded type\n// is invalid.\n//\n// The Types map does not record the type of every identifier,\n// only those that appear where an arbitrary expression is\n// permitted. For instance, the identifier f in a selector\n// expression x.f is found only in the Selections map, the\n// identifier z in a variable declaration 'var z int' is found\n// only in the Defs map, and identifiers denoting packages in\n// qualified identifiers are collected in the Uses map.\nfunc (info xgoRecorder) Type(e ast.Expr, tv types.TypeAndValue) {\n\tif debugVerbose {\n\t\tlog.Println(\"==> Type:\", e, tv.Type)\n\t}\n\tif info.Types != nil {\n\t\tinfo.Types[e] = tv\n\t}\n}\n\n// Instantiate maps identifiers denoting generic types or functions to their\n// type arguments and instantiated type.\n//\n// For example, Instantiate will map the identifier for 'T' in the type\n// instantiation T[int, string] to the type arguments [int, string] and\n// resulting instantiated *Named type. Given a generic function\n// func F[A any](A), Instances will map the identifier for 'F' in the call\n// expression F(int(1)) to the inferred type arguments [int], and resulting\n// instantiated *Signature.\n//\n// Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs\n// results in an equivalent of Instances[id].Type.\nfunc (info xgoRecorder) Instantiate(id *ast.Ident, inst types.Instance) {\n\tif info.Instances != nil {\n\t\tinfo.Instances[id] = inst\n\t}\n}\n\n// Def maps identifiers to the objects they define (including\n// package names, dots \".\" of dot-imports, and blank \"_\" identifiers).\n// For identifiers that do not denote objects (e.g., the package name\n// in package clauses, or symbolic variables t in t := x.(type) of\n// type switch headers), the corresponding objects are nil.\n//\n// For an embedded field, Def maps the field *Var it defines.\n//\n// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()\nfunc (info xgoRecorder) Def(id *ast.Ident, obj types.Object) {\n\tif debugVerbose {\n\t\tlog.Println(\"==> Def:\", id, obj)\n\t}\n\tif info.Defs != nil {\n\t\tinfo.Defs[id] = obj\n\t}\n}\n\n// Use maps identifiers to the objects they denote.\n//\n// For an embedded field, Use maps the *TypeName it denotes.\n//\n// Invariant: Uses[id].Pos() != id.Pos()\nfunc (info xgoRecorder) Use(id *ast.Ident, obj types.Object) {\n\tif debugVerbose {\n\t\tlog.Println(\"==> Use:\", id, obj)\n\t}\n\tif info.Uses != nil {\n\t\tinfo.Uses[id] = obj\n\t}\n\tif info.Overloads != nil {\n\t\tif sig, ok := obj.Type().(*types.Signature); ok {\n\t\t\tif ext, ok := gogen.CheckSigFuncEx(sig); ok {\n\t\t\t\tif debugVerbose {\n\t\t\t\t\tlog.Println(\"==> Overloads:\", id, ext)\n\t\t\t\t}\n\t\t\t\tinfo.Overloads[id] = obj\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Implicit maps nodes to their implicitly declared objects, if any.\n// The following node and object types may appear:\n//\n//\tnode               declared object\n//\n//\t*ast.ImportSpec    *PkgName for imports without renames\n//\t*ast.CaseClause    type-specific *Var for each type switch case clause (incl. default)\n//\t*ast.Field         anonymous parameter *Var (incl. unnamed results)\nfunc (info xgoRecorder) Implicit(node ast.Node, obj types.Object) {\n\tif debugVerbose {\n\t\tlog.Println(\"==> Implicit:\", obj)\n\t}\n\tif info.Implicits != nil {\n\t\tinfo.Implicits[node] = obj\n\t}\n}\n\n// Select maps selector expressions (excluding qualified identifiers)\n// to their corresponding selections.\nfunc (info xgoRecorder) Select(e *ast.SelectorExpr, sel *types.Selection) {\n\tif info.Selections != nil {\n\t\tinfo.Selections[e] = sel\n\t}\n}\n\n// Scope maps ast.Nodes to the scopes they define. Package scopes are not\n// associated with a specific node but with all files belonging to a package.\n// Thus, the package scope can be found in the type-checked Package object.\n// Scopes nest, with the Universe scope being the outermost scope, enclosing\n// the package scope, which contains (one or more) files scopes, which enclose\n// function scopes which in turn enclose statement and function literal scopes.\n// Note that even though package-level functions are declared in the package\n// scope, the function scopes are embedded in the file scope of the file\n// containing the function declaration.\n//\n// The following node types may appear in Scopes:\n//\n//\t*ast.File\n//\t*ast.FuncType\n//\t*ast.TypeSpec\n//\t*ast.BlockStmt\n//\t*ast.IfStmt\n//\t*ast.SwitchStmt\n//\t*ast.TypeSwitchStmt\n//\t*ast.CaseClause\n//\t*ast.CommClause\n//\t*ast.ForStmt\n//\t*ast.RangeStmt\nfunc (info xgoRecorder) Scope(n ast.Node, scope *types.Scope) {\n\tif debugVerbose {\n\t\tlog.Println(\"==> Scope:\", scope)\n\t}\n\tif info.Scopes != nil {\n\t\tinfo.Scopes[n] = scope\n\t}\n}\n\n// -----------------------------------------------------------------------------\n\n// An Error describes a type-checking error; it implements the error interface.\n// A \"soft\" error is an error that still permits a valid interpretation of a\n// package (such as \"unused variable\"); \"hard\" errors may lead to unpredictable\n// behavior if ignored.\ntype Error struct {\n\tFset     *token.FileSet // file set for interpretation of Pos\n\tPos, End token.Pos      // error position\n\tMsg      string         // error message\n\tCode     Code           // error code\n\tSoft     bool           // if set, error is \"soft\"\n}\n\n// Error returns an error string formatted as follows:\n// filename:line:column: message\nfunc (err Error) Error() string {\n\treturn fmt.Sprintf(\"%s: %s\", err.Fset.Position(err.Pos), err.Msg)\n}\n"
  },
  {
    "path": "x/typesutil/info_test.go",
    "content": "package typesutil_test\n\nimport (\n\t\"fmt\"\n\n\tgoast \"go/ast\"\n\tgoformat \"go/format\"\n\tgoparser \"go/parser\"\n\n\t\"go/constant\"\n\t\"go/importer\"\n\t\"go/types\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/goplus/gogen\"\n\t\"github.com/goplus/mod/env\"\n\t\"github.com/goplus/mod/modfile\"\n\t\"github.com/goplus/mod/modload\"\n\t\"github.com/goplus/mod/xgomod\"\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/format\"\n\t\"github.com/goplus/xgo/parser\"\n\t\"github.com/goplus/xgo/token\"\n\t\"github.com/goplus/xgo/tool\"\n\t\"github.com/goplus/xgo/x/typesutil\"\n)\n\nvar spxProject = &modfile.Project{\n\tExt: \".tgmx\", Class: \"*MyGame\",\n\tWorks:    []*modfile.Class{{Ext: \".tspx\", Class: \"Sprite\"}},\n\tPkgPaths: []string{\"github.com/goplus/xgo/cl/internal/spx\", \"math\"}}\n\nvar spxMod *xgomod.Module\n\nfunc init() {\n\tspxMod = xgomod.New(modload.Default)\n\tspxMod.Opt.Projects = append(spxMod.Opt.Projects, spxProject)\n\tspxMod.ImportClasses()\n}\n\nfunc lookupClass(ext string) (c *modfile.Project, ok bool) {\n\tswitch ext {\n\tcase \".tgmx\", \".tspx\":\n\t\treturn spxProject, true\n\t}\n\treturn\n}\n\nfunc spxParserConf() parser.Config {\n\treturn parser.Config{\n\t\tClassKind: func(fname string) (isProj bool, ok bool) {\n\t\t\text := modfile.ClassExt(fname)\n\t\t\tc, ok := lookupClass(ext)\n\t\t\tif ok {\n\t\t\t\tisProj = c.IsProj(ext, fname)\n\t\t\t}\n\t\t\treturn\n\t\t},\n\t}\n}\n\nfunc parseMixedSource(mod *xgomod.Module, fset *token.FileSet, name, src string, goname string, gosrc string, parserConf parser.Config, updateGoTypesOverload bool) (*types.Package, *typesutil.Info, *types.Info, error) {\n\tf, err := parser.ParseEntry(fset, name, src, parserConf)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tvar gofiles []*goast.File\n\tif len(gosrc) > 0 {\n\t\tf, err := goparser.ParseFile(fset, goname, gosrc, goparser.ParseComments)\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\t\tgofiles = append(gofiles, f)\n\t}\n\n\tconf := &types.Config{}\n\tconf.Importer = tool.NewImporter(nil, &env.XGo{Root: \"../..\", Version: \"1.0\"}, fset)\n\tchkOpts := &typesutil.Config{\n\t\tTypes:                 types.NewPackage(\"main\", f.Name.Name),\n\t\tFset:                  fset,\n\t\tMod:                   mod,\n\t\tUpdateGoTypesOverload: updateGoTypesOverload,\n\t}\n\tinfo := &typesutil.Info{\n\t\tTypes:      make(map[ast.Expr]types.TypeAndValue),\n\t\tDefs:       make(map[*ast.Ident]types.Object),\n\t\tUses:       make(map[*ast.Ident]types.Object),\n\t\tImplicits:  make(map[ast.Node]types.Object),\n\t\tSelections: make(map[*ast.SelectorExpr]*types.Selection),\n\t\tScopes:     make(map[ast.Node]*types.Scope),\n\t\tOverloads:  make(map[*ast.Ident]types.Object),\n\t}\n\tginfo := &types.Info{\n\t\tTypes:      make(map[goast.Expr]types.TypeAndValue),\n\t\tDefs:       make(map[*goast.Ident]types.Object),\n\t\tUses:       make(map[*goast.Ident]types.Object),\n\t\tImplicits:  make(map[goast.Node]types.Object),\n\t\tSelections: make(map[*goast.SelectorExpr]*types.Selection),\n\t\tScopes:     make(map[goast.Node]*types.Scope),\n\t}\n\tcheck := typesutil.NewChecker(conf, chkOpts, ginfo, info)\n\terr = check.Files(gofiles, []*ast.File{f})\n\treturn chkOpts.Types, info, ginfo, err\n}\nfunc parseSource(fset *token.FileSet, filename string, src any, mode parser.Mode) (*types.Package, *typesutil.Info, error) {\n\tf, err := parser.ParseEntry(fset, filename, src, parser.Config{\n\t\tMode: mode,\n\t})\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tpkg := types.NewPackage(\"\", f.Name.Name)\n\tconf := &types.Config{}\n\txgo := &env.XGo{Version: \"1.0\"}\n\tconf.Importer = tool.NewImporter(nil, xgo, fset)\n\tchkOpts := &typesutil.Config{\n\t\tTypes: pkg,\n\t\tFset:  fset,\n\t\tMod:   xgomod.Default,\n\t}\n\tinfo := &typesutil.Info{\n\t\tTypes:      make(map[ast.Expr]types.TypeAndValue),\n\t\tDefs:       make(map[*ast.Ident]types.Object),\n\t\tUses:       make(map[*ast.Ident]types.Object),\n\t\tImplicits:  make(map[ast.Node]types.Object),\n\t\tSelections: make(map[*ast.SelectorExpr]*types.Selection),\n\t\tScopes:     make(map[ast.Node]*types.Scope),\n\t\tOverloads:  make(map[*ast.Ident]types.Object),\n\t}\n\tcheck := typesutil.NewChecker(conf, chkOpts, nil, info)\n\terr = check.Files(nil, []*ast.File{f})\n\treturn pkg, info, err\n}\n\nfunc parseGoSource(fset *token.FileSet, filename string, src any, mode goparser.Mode) (*types.Package, *types.Info, error) {\n\tf, err := goparser.ParseFile(fset, filename, src, mode)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tconf := &types.Config{}\n\tconf.Importer = importer.Default()\n\tinfo := &types.Info{\n\t\tTypes:      make(map[goast.Expr]types.TypeAndValue),\n\t\tDefs:       make(map[*goast.Ident]types.Object),\n\t\tUses:       make(map[*goast.Ident]types.Object),\n\t\tImplicits:  make(map[goast.Node]types.Object),\n\t\tSelections: make(map[*goast.SelectorExpr]*types.Selection),\n\t\tScopes:     make(map[goast.Node]*types.Scope),\n\t}\n\tpkg := types.NewPackage(\"\", f.Name.Name)\n\tcheck := types.NewChecker(conf, fset, pkg, info)\n\terr = check.Files([]*goast.File{f})\n\treturn pkg, info, err\n}\n\nfunc testXGoInfo(t *testing.T, src string, gosrc string, expect string) {\n\ttestXGoInfoEx(t, xgomod.Default, \"main.xgo\", src, \"main.go\", gosrc, expect, parser.Config{})\n}\n\nfunc testSpxInfo(t *testing.T, name string, src string, expect string) {\n\ttestXGoInfoEx(t, spxMod, name, src, \"main.go\", \"\", expect, spxParserConf())\n}\n\nfunc testXGoInfoEx(t *testing.T, mod *xgomod.Module, name string, src string, goname string, gosrc string, expect string, parseConf parser.Config) {\n\tfset := token.NewFileSet()\n\t_, info, _, err := parseMixedSource(mod, fset, name, src, goname, gosrc, parseConf, false)\n\tif err != nil {\n\t\tt.Fatal(\"parserMixedSource error\", err)\n\t}\n\tvar list []string\n\tlist = append(list, \"== types ==\")\n\tlist = append(list, typesList(fset, info.Types, false)...)\n\tlist = append(list, \"== defs ==\")\n\tlist = append(list, defsList(fset, info.Defs, true)...)\n\tlist = append(list, \"== uses ==\")\n\tlist = append(list, usesList(fset, info.Uses)...)\n\tif len(info.Overloads) > 0 {\n\t\tlist = append(list, \"== overloads ==\")\n\t\tlist = append(list, overloadsList(fset, info.Overloads)...)\n\t}\n\tresult := strings.Join(list, \"\\n\")\n\tt.Log(result)\n\tif result != expect {\n\t\tt.Fatal(\"bad expect\\n\", expect)\n\t}\n}\n\nfunc testInfo(t *testing.T, src any) {\n\tfset := token.NewFileSet()\n\t_, info, err := parseSource(fset, \"main.xgo\", src, parser.ParseComments)\n\tif err != nil {\n\t\tt.Fatal(\"parserSource error\", err)\n\t}\n\t_, goinfo, err := parseGoSource(fset, \"main.go\", src, goparser.ParseComments)\n\tif err != nil {\n\t\tt.Fatal(\"parserGoSource error\", err)\n\t}\n\ttestItems(t, \"types\", typesList(fset, info.Types, true), goTypesList(fset, goinfo.Types, true))\n\ttestItems(t, \"defs\", defsList(fset, info.Defs, true), goDefsList(fset, goinfo.Defs, true))\n\ttestItems(t, \"uses\", usesList(fset, info.Uses), goUsesList(fset, goinfo.Uses))\n\t// TODO check selections\n\t//testItems(t, \"selections\", selectionList(fset, info.Selections), goSelectionList(fset, goinfo.Selections))\n}\n\nfunc testItems(t *testing.T, name string, items []string, goitems []string) {\n\ttext := strings.Join(items, \"\\n\")\n\tgotext := strings.Join(goitems, \"\\n\")\n\tif len(items) != len(goitems) || text != gotext {\n\t\tt.Errorf(`====== check %v error (XGo count: %v, Go count %v) ====== \n------ XGo ------\n%v\n------ Go ------\n%v\n`,\n\t\t\tname, len(items), len(goitems),\n\t\t\ttext, gotext)\n\t} else {\n\t\tt.Logf(`====== check %v pass (count: %v) ======\n%v\n`, name, len(items), text)\n\t}\n}\n\nfunc sortItems(items []string) []string {\n\tsort.Strings(items)\n\tfor i := 0; i < len(items); i++ {\n\t\titems[i] = fmt.Sprintf(\"%03v: %v\", i, items[i])\n\t}\n\treturn items\n}\n\nfunc typesList(fset *token.FileSet, types map[ast.Expr]types.TypeAndValue, skipBasicLit bool) []string {\n\tvar items []string\n\tfor expr, tv := range types {\n\t\tvar buf strings.Builder\n\t\tposn := fset.Position(expr.Pos())\n\t\ttvstr := tv.Type.String()\n\t\tif skipBasicLit {\n\t\t\tif t, ok := expr.(*ast.BasicLit); ok {\n\t\t\t\ttvstr = t.Kind.String()\n\t\t\t}\n\t\t}\n\t\tif tv.Value != nil {\n\t\t\ttvstr += \" = \" + tv.Value.String()\n\t\t}\n\t\t// line:col | expr | mode : type = value\n\t\tfmt.Fprintf(&buf, \"%2d:%2d | %-19s %-30T | %-7s : %s | %v\",\n\t\t\tposn.Line, posn.Column, exprString(fset, expr), expr,\n\t\t\tmode(tv), tvstr, (*TypeAndValue)(unsafe.Pointer(&tv)).mode)\n\t\titems = append(items, buf.String())\n\t}\n\treturn sortItems(items)\n}\n\nfunc goTypesList(fset *token.FileSet, types map[goast.Expr]types.TypeAndValue, skipBasicLit bool) []string {\n\tvar items []string\n\tfor expr, tv := range types {\n\t\tvar buf strings.Builder\n\t\tposn := fset.Position(expr.Pos())\n\t\ttvstr := tv.Type.String()\n\t\tif skipBasicLit {\n\t\t\tif t, ok := expr.(*goast.BasicLit); ok {\n\t\t\t\ttvstr = t.Kind.String()\n\t\t\t}\n\t\t}\n\t\tif tv.Value != nil {\n\t\t\ttvstr += \" = \" + tv.Value.String()\n\t\t}\n\t\t// line:col | expr | mode : type = value\n\t\tfmt.Fprintf(&buf, \"%2d:%2d | %-19s %-30T | %-7s : %s | %v\",\n\t\t\tposn.Line, posn.Column, goexprString(fset, expr), expr,\n\t\t\tmode(tv), tvstr, (*TypeAndValue)(unsafe.Pointer(&tv)).mode)\n\t\titems = append(items, buf.String())\n\t}\n\treturn sortItems(items)\n}\n\nfunc defsList(fset *token.FileSet, uses map[*ast.Ident]types.Object, skipNil bool) []string {\n\tvar items []string\n\tfor expr, obj := range uses {\n\t\tif skipNil && obj == nil {\n\t\t\tcontinue\n\t\t}\n\t\tvar buf strings.Builder\n\t\tposn := fset.Position(expr.Pos())\n\t\t// line:col | expr | mode : type = value\n\t\tfmt.Fprintf(&buf, \"%2d:%2d | %-19s | %s\",\n\t\t\tposn.Line, posn.Column, expr,\n\t\t\tobj)\n\t\titems = append(items, buf.String())\n\t}\n\treturn sortItems(items)\n}\n\nfunc goDefsList(fset *token.FileSet, uses map[*goast.Ident]types.Object, skipNil bool) []string {\n\tvar items []string\n\tfor expr, obj := range uses {\n\t\tif skipNil && obj == nil {\n\t\t\tcontinue // skip nil object\n\t\t}\n\t\tvar buf strings.Builder\n\t\tposn := fset.Position(expr.Pos())\n\t\t// line:col | expr | mode : type = value\n\t\tfmt.Fprintf(&buf, \"%2d:%2d | %-19s | %s\",\n\t\t\tposn.Line, posn.Column, expr,\n\t\t\tobj)\n\t\titems = append(items, buf.String())\n\t}\n\treturn sortItems(items)\n}\n\nfunc usesList(fset *token.FileSet, uses map[*ast.Ident]types.Object) []string {\n\tvar items []string\n\tfor expr, obj := range uses {\n\t\tvar buf strings.Builder\n\t\tposn := fset.Position(expr.Pos())\n\t\t// line:col | expr | mode : type = value\n\t\tfmt.Fprintf(&buf, \"%2d:%2d | %-19s | %s\",\n\t\t\tposn.Line, posn.Column, expr,\n\t\t\tobj)\n\t\titems = append(items, buf.String())\n\t}\n\treturn sortItems(items)\n}\n\nfunc goUsesList(fset *token.FileSet, uses map[*goast.Ident]types.Object) []string {\n\tvar items []string\n\tfor expr, obj := range uses {\n\t\tif obj == nil {\n\t\t\tcontinue // skip nil object\n\t\t}\n\t\tvar buf strings.Builder\n\t\tposn := fset.Position(expr.Pos())\n\t\t// line:col | expr | mode : type = value\n\t\tfmt.Fprintf(&buf, \"%2d:%2d | %-19s | %s\",\n\t\t\tposn.Line, posn.Column, expr,\n\t\t\tobj)\n\t\titems = append(items, buf.String())\n\t}\n\treturn sortItems(items)\n}\n\nfunc overloadsList(fset *token.FileSet, overloads map[*ast.Ident]types.Object) []string {\n\tvar items []string\n\tfor expr, obj := range overloads {\n\t\tvar buf strings.Builder\n\t\tposn := fset.Position(expr.Pos())\n\t\t// line:col | expr | mode : type = value\n\t\tfmt.Fprintf(&buf, \"%2d:%2d | %-19s | %s\",\n\t\t\tposn.Line, posn.Column, expr,\n\t\t\tobj)\n\t\titems = append(items, buf.String())\n\t}\n\treturn sortItems(items)\n}\n\n/*\nfunc selectionList(fset *token.FileSet, sels map[*ast.SelectorExpr]*types.Selection) []string {\n\tvar items []string\n\tfor expr, sel := range sels {\n\t\tvar buf strings.Builder\n\t\tposn := fset.Position(expr.Pos())\n\t\t// line:col | expr | mode : type = value\n\t\tfmt.Fprintf(&buf, \"%2d:%2d | %-19s | %s\",\n\t\t\tposn.Line, posn.Column, exprString(fset, expr),\n\t\t\tsel)\n\t\titems = append(items, buf.String())\n\t}\n\treturn sortItems(items)\n}\n\nfunc goSelectionList(fset *token.FileSet, sels map[*goast.SelectorExpr]*types.Selection) []string {\n\tvar items []string\n\tfor expr, sel := range sels {\n\t\tvar buf strings.Builder\n\t\tposn := fset.Position(expr.Pos())\n\t\t// line:col | expr | mode : type = value\n\t\tfmt.Fprintf(&buf, \"%2d:%2d | %-19s | %s\",\n\t\t\tposn.Line, posn.Column, goexprString(fset, expr),\n\t\t\tsel)\n\t\titems = append(items, buf.String())\n\t}\n\treturn sortItems(items)\n}\n*/\n\nfunc mode(tv types.TypeAndValue) string {\n\tswitch {\n\tcase tv.IsVoid():\n\t\treturn \"void\"\n\tcase tv.IsType():\n\t\treturn \"type\"\n\tcase tv.IsBuiltin():\n\t\treturn \"builtin\"\n\tcase tv.IsNil():\n\t\treturn \"nil\"\n\tcase tv.Assignable():\n\t\tif tv.Addressable() {\n\t\t\treturn \"var\"\n\t\t}\n\t\treturn \"mapindex\"\n\tcase tv.IsValue():\n\t\treturn \"value\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n\nfunc exprString(fset *token.FileSet, expr ast.Expr) string {\n\tvar buf strings.Builder\n\tformat.Node(&buf, fset, expr)\n\treturn buf.String()\n}\n\nfunc goexprString(fset *token.FileSet, expr goast.Expr) string {\n\tvar buf strings.Builder\n\tgoformat.Node(&buf, fset, expr)\n\treturn buf.String()\n}\n\ntype operandMode byte\n\nconst (\n\tinvalid   operandMode = iota // operand is invalid\n\tnovalue                      // operand represents no value (result of a function call w/o result)\n\tbuiltin                      // operand is a built-in function\n\ttypexpr                      // operand is a type\n\tconstant_                    // operand is a constant; the operand's typ is a Basic type\n\tvariable                     // operand is an addressable variable\n\tmapindex                     // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)\n\tvalue                        // operand is a computed value\n\tcommaok                      // like value, but operand may be used in a comma,ok expression\n\tcommaerr                     // like commaok, but second value is error, not boolean\n\tcgofunc                      // operand is a cgo function\n)\n\nfunc (v operandMode) String() string {\n\treturn operandModeString[int(v)]\n}\n\nvar operandModeString = [...]string{\n\tinvalid:   \"invalid operand\",\n\tnovalue:   \"no value\",\n\tbuiltin:   \"built-in\",\n\ttypexpr:   \"type\",\n\tconstant_: \"constant\",\n\tvariable:  \"variable\",\n\tmapindex:  \"map index expression\",\n\tvalue:     \"value\",\n\tcommaok:   \"comma, ok expression\",\n\tcommaerr:  \"comma, error expression\",\n\tcgofunc:   \"cgo function\",\n}\n\ntype TypeAndValue struct {\n\tmode  operandMode\n\tType  types.Type\n\tValue constant.Value\n}\n\nfunc TestVarTypes(t *testing.T) {\n\ttestInfo(t, `package main\ntype T struct {\n\tx int\n\ty int\n}\nvar v *int = nil\nvar v1 []int\nvar v2 map[int8]string\nvar v3 struct{}\nvar v4 *T = &T{100,200}\nvar v5 = [6]int{}\nvar v6 = v5[0]\nvar v7 = [6]int{}[0]\nvar m map[int]string\nfunc init() {\n\tv5[0] = 100\n\t_ = v5[:][0]\n\tm[0] = \"hello\"\n\t_ = m[0]\n\t_ = map[int]string{}[0]\n\t_ = &v3\n\t_ = *(&v3)\n\ta := []int{1,2,3,4,5}[0]\n\t_ = a\n}\n`)\n}\n\nfunc TestStruct(t *testing.T) {\n\ttestInfo(t, `package main\nimport \"fmt\"\n\ntype Person struct {\n\tname string\n\tage  int8\n}\n\nfunc test() {\n\tp := Person{\n\t\tname: \"jack\",\n\t}\n\t_ = p.name\n\tp.name = \"name\"\n\tfmt.Println(p)\n}\n`)\n}\n\nfunc TestTypeAssert(t *testing.T) {\n\ttestInfo(t, `package main\n\nfunc test() {\n\tvar a interface{} = 100\n\tif n, ok := a.(int); ok {\n\t\t_ = n\n\t}\n}\n`)\n}\n\nfunc TestChan(t *testing.T) {\n\ttestInfo(t, `package main\n\nfunc test() {\n\tvar ch chan int\n\tselect {\n\tcase n, ok := <-ch:\n\t\t_ = n\n\t\t_ = ok\n\t\tbreak\n\t}\n}\n`)\n}\n\nfunc TestRange(t *testing.T) {\n\ttestInfo(t, `package main\nfunc test() {\n\ta := []int{100,200}\n\tfor k, v := range a {\n\t\t_ = k\n\t\t_ = v\n\t}\n\tvar m map[int]string\n\tfor k, v := range m {\n\t\t_ = k\n\t\t_ = v\n\t}\n\tfor v := range m {\n\t\t_ = v\n\t}\n}\n`)\n}\n\nfunc TestFuncLit(t *testing.T) {\n\ttestInfo(t, `package main\nfunc test() {\n\tadd := func(n1 int, n2 int) int {\n\t\treturn n1+n2\n\t}\n\t_ = add(1,2)\n\n\tgo func(n int) {\n\t\t_ = n+100\n\t}(100)\n}\n`)\n}\n\nfunc TestSliceLit(t *testing.T) {\n\ttestXGoInfo(t, `\na := [100,200]\nprintln a\n`, ``, `== types ==\n000:  2: 7 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n001:  2:11 | 200                 *ast.BasicLit                  | value   : untyped int = 200 | constant\n002:  3: 1 | println             *ast.Ident                     | value   : func(a ...any) (n int, err error) | value\n003:  3: 1 | println a           *ast.CallExpr                  | value   : (n int, err error) | value\n004:  3: 9 | a                   *ast.Ident                     | var     : []int | variable\n== defs ==\n000:  2: 1 | a                   | var a []int\n001:  2: 1 | main                | func main.main()\n== uses ==\n000:  3: 1 | println             | func fmt.Println(a ...any) (n int, err error)\n001:  3: 9 | a                   | var a []int`)\n}\n\nfunc TestForPhrase1(t *testing.T) {\n\ttestXGoInfo(t, `\nsum := 0\nfor x <- [1, 3, 5, 7, 11, 13, 17], x > 3 {\n\tsum = sum + x\n}\nprintln sum\n`, ``, `== types ==\n000:  2: 8 | 0                   *ast.BasicLit                  | value   : untyped int = 0 | constant\n001:  3:11 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n002:  3:14 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant\n003:  3:17 | 5                   *ast.BasicLit                  | value   : untyped int = 5 | constant\n004:  3:20 | 7                   *ast.BasicLit                  | value   : untyped int = 7 | constant\n005:  3:23 | 11                  *ast.BasicLit                  | value   : untyped int = 11 | constant\n006:  3:27 | 13                  *ast.BasicLit                  | value   : untyped int = 13 | constant\n007:  3:31 | 17                  *ast.BasicLit                  | value   : untyped int = 17 | constant\n008:  3:36 | x                   *ast.Ident                     | var     : int | variable\n009:  3:36 | x > 3               *ast.BinaryExpr                | value   : untyped bool | value\n010:  3:40 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant\n011:  4: 2 | sum                 *ast.Ident                     | var     : int | variable\n012:  4: 8 | sum                 *ast.Ident                     | var     : int | variable\n013:  4: 8 | sum + x             *ast.BinaryExpr                | value   : int | value\n014:  4:14 | x                   *ast.Ident                     | var     : int | variable\n015:  6: 1 | println             *ast.Ident                     | value   : func(a ...any) (n int, err error) | value\n016:  6: 1 | println sum         *ast.CallExpr                  | value   : (n int, err error) | value\n017:  6: 9 | sum                 *ast.Ident                     | var     : int | variable\n== defs ==\n000:  2: 1 | main                | func main.main()\n001:  2: 1 | sum                 | var sum int\n002:  3: 5 | x                   | var x int\n== uses ==\n000:  3:36 | x                   | var x int\n001:  4: 2 | sum                 | var sum int\n002:  4: 8 | sum                 | var sum int\n003:  4:14 | x                   | var x int\n004:  6: 1 | println             | func fmt.Println(a ...any) (n int, err error)\n005:  6: 9 | sum                 | var sum int`)\n}\n\nfunc TestForPhrase2(t *testing.T) {\n\ttestXGoInfo(t, `\nsum := 0\nfor i, x <- [1, 3, 5, 7, 11, 13, 17], i%2 == 1 && x > 3 {\n\tsum = sum + x\n}\nprintln sum\n`, ``, `== types ==\n000:  2: 8 | 0                   *ast.BasicLit                  | value   : untyped int = 0 | constant\n001:  3:14 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n002:  3:17 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant\n003:  3:20 | 5                   *ast.BasicLit                  | value   : untyped int = 5 | constant\n004:  3:23 | 7                   *ast.BasicLit                  | value   : untyped int = 7 | constant\n005:  3:26 | 11                  *ast.BasicLit                  | value   : untyped int = 11 | constant\n006:  3:30 | 13                  *ast.BasicLit                  | value   : untyped int = 13 | constant\n007:  3:34 | 17                  *ast.BasicLit                  | value   : untyped int = 17 | constant\n008:  3:39 | i                   *ast.Ident                     | var     : int | variable\n009:  3:39 | i % 2               *ast.BinaryExpr                | value   : int | value\n010:  3:39 | i%2 == 1            *ast.BinaryExpr                | value   : untyped bool | value\n011:  3:39 | i%2 == 1 && x > 3   *ast.BinaryExpr                | value   : untyped bool | value\n012:  3:41 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant\n013:  3:46 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n014:  3:51 | x                   *ast.Ident                     | var     : int | variable\n015:  3:51 | x > 3               *ast.BinaryExpr                | value   : untyped bool | value\n016:  3:55 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant\n017:  4: 2 | sum                 *ast.Ident                     | var     : int | variable\n018:  4: 8 | sum                 *ast.Ident                     | var     : int | variable\n019:  4: 8 | sum + x             *ast.BinaryExpr                | value   : int | value\n020:  4:14 | x                   *ast.Ident                     | var     : int | variable\n021:  6: 1 | println             *ast.Ident                     | value   : func(a ...any) (n int, err error) | value\n022:  6: 1 | println sum         *ast.CallExpr                  | value   : (n int, err error) | value\n023:  6: 9 | sum                 *ast.Ident                     | var     : int | variable\n== defs ==\n000:  2: 1 | main                | func main.main()\n001:  2: 1 | sum                 | var sum int\n002:  3: 5 | i                   | var i int\n003:  3: 8 | x                   | var x int\n== uses ==\n000:  3:39 | i                   | var i int\n001:  3:51 | x                   | var x int\n002:  4: 2 | sum                 | var sum int\n003:  4: 8 | sum                 | var sum int\n004:  4:14 | x                   | var x int\n005:  6: 1 | println             | func fmt.Println(a ...any) (n int, err error)\n006:  6: 9 | sum                 | var sum int`)\n}\n\nfunc TestMapComprehension(t *testing.T) {\n\ttestXGoInfo(t, `\ny := {x: i for i, x <- [\"1\", \"3\", \"5\", \"7\", \"11\"]}\nprintln y\n`, ``, `== types ==\n000:  2: 7 | x                   *ast.Ident                     | var     : string | variable\n001:  2:10 | i                   *ast.Ident                     | var     : int | variable\n002:  2:25 | \"1\"                 *ast.BasicLit                  | value   : untyped string = \"1\" | constant\n003:  2:30 | \"3\"                 *ast.BasicLit                  | value   : untyped string = \"3\" | constant\n004:  2:35 | \"5\"                 *ast.BasicLit                  | value   : untyped string = \"5\" | constant\n005:  2:40 | \"7\"                 *ast.BasicLit                  | value   : untyped string = \"7\" | constant\n006:  2:45 | \"11\"                *ast.BasicLit                  | value   : untyped string = \"11\" | constant\n007:  3: 1 | println             *ast.Ident                     | value   : func(a ...any) (n int, err error) | value\n008:  3: 1 | println y           *ast.CallExpr                  | value   : (n int, err error) | value\n009:  3: 9 | y                   *ast.Ident                     | var     : map[string]int | variable\n== defs ==\n000:  2: 1 | main                | func main.main()\n001:  2: 1 | y                   | var y map[string]int\n002:  2:16 | i                   | var i int\n003:  2:19 | x                   | var x string\n== uses ==\n000:  2: 7 | x                   | var x string\n001:  2:10 | i                   | var i int\n002:  3: 1 | println             | func fmt.Println(a ...any) (n int, err error)\n003:  3: 9 | y                   | var y map[string]int`)\n}\n\nfunc TestListComprehension(t *testing.T) {\n\ttestXGoInfo(t, `\na := [1, 3.4, 5]\nb := [x*x for x <- a]\n_ = b\n`, ``, `== types ==\n000:  2: 7 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n001:  2:10 | 3.4                 *ast.BasicLit                  | value   : untyped float = 3.4 | constant\n002:  2:15 | 5                   *ast.BasicLit                  | value   : untyped int = 5 | constant\n003:  3: 7 | x                   *ast.Ident                     | var     : float64 | variable\n004:  3: 7 | x * x               *ast.BinaryExpr                | value   : float64 | value\n005:  3: 9 | x                   *ast.Ident                     | var     : float64 | variable\n006:  3:20 | a                   *ast.Ident                     | var     : []float64 | variable\n007:  4: 5 | b                   *ast.Ident                     | var     : []float64 | variable\n== defs ==\n000:  2: 1 | a                   | var a []float64\n001:  2: 1 | main                | func main.main()\n002:  3: 1 | b                   | var b []float64\n003:  3:15 | x                   | var x float64\n== uses ==\n000:  3: 7 | x                   | var x float64\n001:  3: 9 | x                   | var x float64\n002:  3:20 | a                   | var a []float64\n003:  4: 5 | b                   | var b []float64`)\n}\n\nfunc TestListComprehensionMultiLevel(t *testing.T) {\n\ttestXGoInfo(t, `\narr := [1, 2, 3, 4.1, 5, 6]\nx := [[a, b] for a <- arr, a < b for b <- arr, b > 2]\nprintln(\"x:\", x)\n`, ``, `== types ==\n000:  2: 9 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n001:  2:12 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant\n002:  2:15 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant\n003:  2:18 | 4.1                 *ast.BasicLit                  | value   : untyped float = 4.1 | constant\n004:  2:23 | 5                   *ast.BasicLit                  | value   : untyped int = 5 | constant\n005:  2:26 | 6                   *ast.BasicLit                  | value   : untyped int = 6 | constant\n006:  3: 8 | a                   *ast.Ident                     | var     : float64 | variable\n007:  3:11 | b                   *ast.Ident                     | var     : float64 | variable\n008:  3:23 | arr                 *ast.Ident                     | var     : []float64 | variable\n009:  3:28 | a                   *ast.Ident                     | var     : float64 | variable\n010:  3:28 | a < b               *ast.BinaryExpr                | value   : untyped bool | value\n011:  3:32 | b                   *ast.Ident                     | var     : float64 | variable\n012:  3:43 | arr                 *ast.Ident                     | var     : []float64 | variable\n013:  3:48 | b                   *ast.Ident                     | var     : float64 | variable\n014:  3:48 | b > 2               *ast.BinaryExpr                | value   : untyped bool | value\n015:  3:52 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant\n016:  4: 1 | println             *ast.Ident                     | value   : func(a ...any) (n int, err error) | value\n017:  4: 1 | println(\"x:\", x)    *ast.CallExpr                  | value   : (n int, err error) | value\n018:  4: 9 | \"x:\"                *ast.BasicLit                  | value   : untyped string = \"x:\" | constant\n019:  4:15 | x                   *ast.Ident                     | var     : [][]float64 | variable\n== defs ==\n000:  2: 1 | arr                 | var arr []float64\n001:  2: 1 | main                | func main.main()\n002:  3: 1 | x                   | var x [][]float64\n003:  3:18 | a                   | var a float64\n004:  3:38 | b                   | var b float64\n== uses ==\n000:  3: 8 | a                   | var a float64\n001:  3:11 | b                   | var b float64\n002:  3:23 | arr                 | var arr []float64\n003:  3:28 | a                   | var a float64\n004:  3:32 | b                   | var b float64\n005:  3:43 | arr                 | var arr []float64\n006:  3:48 | b                   | var b float64\n007:  4: 1 | println             | func fmt.Println(a ...any) (n int, err error)\n008:  4:15 | x                   | var x [][]float64`)\n}\n\nfunc TestFileEnumLines(t *testing.T) {\n\ttestXGoInfo(t, `\nimport \"os\"\n\nfor line <- os.Stdin {\n\tprintln line\n}\n`, ``, `== types ==\n000:  4:13 | os.Stdin            *ast.SelectorExpr              | var     : *os.File | variable\n001:  5: 2 | println             *ast.Ident                     | value   : func(a ...any) (n int, err error) | value\n002:  5: 2 | println line        *ast.CallExpr                  | value   : (n int, err error) | value\n003:  5:10 | line                *ast.Ident                     | var     : string | variable\n== defs ==\n000:  4: 1 | main                | func main.main()\n001:  4: 5 | line                | var line string\n== uses ==\n000:  4:13 | os                  | package os\n001:  4:16 | Stdin               | var os.Stdin *os.File\n002:  5: 2 | println             | func fmt.Println(a ...any) (n int, err error)\n003:  5:10 | line                | var line string`)\n}\n\nfunc TestLambdaExpr(t *testing.T) {\n\ttestXGoInfo(t, `package main\nfunc Map(c []float64, t func(float64) float64) {\n\t// ...\n}\n\nfunc Map2(c []float64, t func(float64) (float64, float64)) {\n\t// ...\n}\n\nMap([1.2, 3.5, 6], x => x * x)\nMap2([1.2, 3.5, 6], x => (x * x, x + x))\n`, ``, `== types ==\n000:  2:12 | []float64           *ast.ArrayType                 | type    : []float64 | type\n001:  2:14 | float64             *ast.Ident                     | type    : float64 | type\n002:  2:25 | func(float64) float64 *ast.FuncType                  | type    : func(float64) float64 | type\n003:  2:30 | float64             *ast.Ident                     | type    : float64 | type\n004:  2:39 | float64             *ast.Ident                     | type    : float64 | type\n005:  6:13 | []float64           *ast.ArrayType                 | type    : []float64 | type\n006:  6:15 | float64             *ast.Ident                     | type    : float64 | type\n007:  6:26 | func(float64) (float64, float64) *ast.FuncType                  | type    : func(float64) (float64, float64) | type\n008:  6:31 | float64             *ast.Ident                     | type    : float64 | type\n009:  6:41 | float64             *ast.Ident                     | type    : float64 | type\n010:  6:50 | float64             *ast.Ident                     | type    : float64 | type\n011: 10: 1 | Map                 *ast.Ident                     | value   : func(c []float64, t func(float64) float64) | value\n012: 10: 1 | Map([1.2, 3.5, 6], x => x * x) *ast.CallExpr                  | void    : () | no value\n013: 10: 6 | 1.2                 *ast.BasicLit                  | value   : untyped float = 1.2 | constant\n014: 10:11 | 3.5                 *ast.BasicLit                  | value   : untyped float = 3.5 | constant\n015: 10:16 | 6                   *ast.BasicLit                  | value   : untyped int = 6 | constant\n016: 10:25 | x                   *ast.Ident                     | var     : float64 | variable\n017: 10:25 | x * x               *ast.BinaryExpr                | value   : float64 | value\n018: 10:29 | x                   *ast.Ident                     | var     : float64 | variable\n019: 11: 1 | Map2                *ast.Ident                     | value   : func(c []float64, t func(float64) (float64, float64)) | value\n020: 11: 1 | Map2([1.2, 3.5, 6], x => (x * x, x + x)) *ast.CallExpr                  | void    : () | no value\n021: 11: 7 | 1.2                 *ast.BasicLit                  | value   : untyped float = 1.2 | constant\n022: 11:12 | 3.5                 *ast.BasicLit                  | value   : untyped float = 3.5 | constant\n023: 11:17 | 6                   *ast.BasicLit                  | value   : untyped int = 6 | constant\n024: 11:27 | x                   *ast.Ident                     | var     : float64 | variable\n025: 11:27 | x * x               *ast.BinaryExpr                | value   : float64 | value\n026: 11:31 | x                   *ast.Ident                     | var     : float64 | variable\n027: 11:34 | x                   *ast.Ident                     | var     : float64 | variable\n028: 11:34 | x + x               *ast.BinaryExpr                | value   : float64 | value\n029: 11:38 | x                   *ast.Ident                     | var     : float64 | variable\n== defs ==\n000:  2: 6 | Map                 | func main.Map(c []float64, t func(float64) float64)\n001:  2:10 | c                   | var c []float64\n002:  2:23 | t                   | var t func(float64) float64\n003:  6: 6 | Map2                | func main.Map2(c []float64, t func(float64) (float64, float64))\n004:  6:11 | c                   | var c []float64\n005:  6:24 | t                   | var t func(float64) (float64, float64)\n006: 10: 1 | main                | func main.main()\n007: 10:20 | x                   | var x float64\n008: 11:21 | x                   | var x float64\n== uses ==\n000:  2:14 | float64             | type float64\n001:  2:30 | float64             | type float64\n002:  2:39 | float64             | type float64\n003:  6:15 | float64             | type float64\n004:  6:31 | float64             | type float64\n005:  6:41 | float64             | type float64\n006:  6:50 | float64             | type float64\n007: 10: 1 | Map                 | func main.Map(c []float64, t func(float64) float64)\n008: 10:25 | x                   | var x float64\n009: 10:29 | x                   | var x float64\n010: 11: 1 | Map2                | func main.Map2(c []float64, t func(float64) (float64, float64))\n011: 11:27 | x                   | var x float64\n012: 11:31 | x                   | var x float64\n013: 11:34 | x                   | var x float64\n014: 11:38 | x                   | var x float64`)\n}\n\nfunc TestLambdaExpr2(t *testing.T) {\n\ttestXGoInfo(t, `package main\nfunc Map(c []float64, t func(float64) float64) {\n\t// ...\n}\n\nfunc Map2(c []float64, t func(float64) (float64, float64)) {\n\t// ...\n}\n\nMap([1.2, 3.5, 6], x => {\n\treturn x * x\n})\nMap2([1.2, 3.5, 6], x => {\n\treturn x * x, x + x\n})\n`, ``, `== types ==\n000:  2:12 | []float64           *ast.ArrayType                 | type    : []float64 | type\n001:  2:14 | float64             *ast.Ident                     | type    : float64 | type\n002:  2:25 | func(float64) float64 *ast.FuncType                  | type    : func(float64) float64 | type\n003:  2:30 | float64             *ast.Ident                     | type    : float64 | type\n004:  2:39 | float64             *ast.Ident                     | type    : float64 | type\n005:  6:13 | []float64           *ast.ArrayType                 | type    : []float64 | type\n006:  6:15 | float64             *ast.Ident                     | type    : float64 | type\n007:  6:26 | func(float64) (float64, float64) *ast.FuncType                  | type    : func(float64) (float64, float64) | type\n008:  6:31 | float64             *ast.Ident                     | type    : float64 | type\n009:  6:41 | float64             *ast.Ident                     | type    : float64 | type\n010:  6:50 | float64             *ast.Ident                     | type    : float64 | type\n011: 10: 1 | Map                 *ast.Ident                     | value   : func(c []float64, t func(float64) float64) | value\n012: 10: 1 | Map([1.2, 3.5, 6], x => {\n\treturn x * x\n}) *ast.CallExpr                  | void    : () | no value\n013: 10: 6 | 1.2                 *ast.BasicLit                  | value   : untyped float = 1.2 | constant\n014: 10:11 | 3.5                 *ast.BasicLit                  | value   : untyped float = 3.5 | constant\n015: 10:16 | 6                   *ast.BasicLit                  | value   : untyped int = 6 | constant\n016: 11: 9 | x                   *ast.Ident                     | var     : float64 | variable\n017: 11: 9 | x * x               *ast.BinaryExpr                | value   : float64 | value\n018: 11:13 | x                   *ast.Ident                     | var     : float64 | variable\n019: 13: 1 | Map2                *ast.Ident                     | value   : func(c []float64, t func(float64) (float64, float64)) | value\n020: 13: 1 | Map2([1.2, 3.5, 6], x => {\n\treturn x * x, x + x\n}) *ast.CallExpr                  | void    : () | no value\n021: 13: 7 | 1.2                 *ast.BasicLit                  | value   : untyped float = 1.2 | constant\n022: 13:12 | 3.5                 *ast.BasicLit                  | value   : untyped float = 3.5 | constant\n023: 13:17 | 6                   *ast.BasicLit                  | value   : untyped int = 6 | constant\n024: 14: 9 | x                   *ast.Ident                     | var     : float64 | variable\n025: 14: 9 | x * x               *ast.BinaryExpr                | value   : float64 | value\n026: 14:13 | x                   *ast.Ident                     | var     : float64 | variable\n027: 14:16 | x                   *ast.Ident                     | var     : float64 | variable\n028: 14:16 | x + x               *ast.BinaryExpr                | value   : float64 | value\n029: 14:20 | x                   *ast.Ident                     | var     : float64 | variable\n== defs ==\n000:  2: 6 | Map                 | func main.Map(c []float64, t func(float64) float64)\n001:  2:10 | c                   | var c []float64\n002:  2:23 | t                   | var t func(float64) float64\n003:  6: 6 | Map2                | func main.Map2(c []float64, t func(float64) (float64, float64))\n004:  6:11 | c                   | var c []float64\n005:  6:24 | t                   | var t func(float64) (float64, float64)\n006: 10: 1 | main                | func main.main()\n007: 10:20 | x                   | var x float64\n008: 13:21 | x                   | var x float64\n== uses ==\n000:  2:14 | float64             | type float64\n001:  2:30 | float64             | type float64\n002:  2:39 | float64             | type float64\n003:  6:15 | float64             | type float64\n004:  6:31 | float64             | type float64\n005:  6:41 | float64             | type float64\n006:  6:50 | float64             | type float64\n007: 10: 1 | Map                 | func main.Map(c []float64, t func(float64) float64)\n008: 11: 9 | x                   | var x float64\n009: 11:13 | x                   | var x float64\n010: 13: 1 | Map2                | func main.Map2(c []float64, t func(float64) (float64, float64))\n011: 14: 9 | x                   | var x float64\n012: 14:13 | x                   | var x float64\n013: 14:16 | x                   | var x float64\n014: 14:20 | x                   | var x float64`)\n}\n\nfunc TestMixedOverload1(t *testing.T) {\n\ttestXGoInfo(t, `\ntype Mesh struct {\n}\n\nfunc (p *Mesh) Name() string {\n\treturn \"hello\"\n}\n\nvar (\n\tm1 = &Mesh{}\n\tm2 = &Mesh{}\n)\n\nOnKey \"hello\", => {\n}\nOnKey \"hello\", key => {\n}\nOnKey [\"1\"], => {\n}\nOnKey [\"2\"], key => {\n}\nOnKey [m1, m2], => {\n}\nOnKey [m1, m2], key => {\n}\nOnKey [\"a\"], [\"b\"], key => {\n}\nOnKey [\"a\"], [m1, m2], key => {\n}\nOnKey [\"a\"], nil, key => {\n}\nOnKey 100, 200\nOnKey \"a\", \"b\", x => x * x, x => {\n\treturn x * 2\n}\nOnKey \"a\", \"b\", 1, 2, 3\nOnKey(\"a\", \"b\", [1, 2, 3]...)\n`, `\npackage main\n\ntype Mesher interface {\n\tName() string\n}\n\ntype N struct {\n}\n\nfunc (m *N) OnKey__0(a string, fn func()) {\n}\n\nfunc (m *N) OnKey__1(a string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__2(a []string, fn func()) {\n}\n\nfunc (m *N) OnKey__3(a []string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__4(a []Mesher, fn func()) {\n}\n\nfunc (m *N) OnKey__5(a []Mesher, fn func(key Mesher)) {\n}\n\nfunc (m *N) OnKey__6(a []string, b []string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__7(a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__8(x int, y int) {\n}\n\n\nfunc OnKey__0(a string, fn func()) {\n}\n\nfunc OnKey__1(a string, fn func(key string)) {\n}\n\nfunc OnKey__2(a []string, fn func()) {\n}\n\nfunc OnKey__3(a []string, fn func(key string)) {\n}\n\nfunc OnKey__4(a []Mesher, fn func()) {\n}\n\nfunc OnKey__5(a []Mesher, fn func(key Mesher)) {\n}\n\nfunc OnKey__6(a []string, b []string, fn func(key string)) {\n}\n\nfunc OnKey__7(a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc OnKey__8(x int, y int) {\n}\n\nfunc OnKey__9(a, b string, fn ...func(x int) int) {\n}\n\nfunc OnKey__a(a, b string, v ...int) {\n}\n`, `== types ==\n000:  2:11 | struct {\n}          *ast.StructType                | type    : struct{} | type\n001:  5:10 | Mesh                *ast.Ident                     | type    : main.Mesh | type\n002:  5:23 | string              *ast.Ident                     | type    : string | type\n003:  6: 9 | \"hello\"             *ast.BasicLit                  | value   : untyped string = \"hello\" | constant\n004: 10: 7 | &Mesh{}             *ast.UnaryExpr                 | value   : *main.Mesh | value\n005: 10: 8 | Mesh                *ast.Ident                     | type    : main.Mesh | type\n006: 10: 8 | Mesh{}              *ast.CompositeLit              | value   : main.Mesh | value\n007: 11: 7 | &Mesh{}             *ast.UnaryExpr                 | value   : *main.Mesh | value\n008: 11: 8 | Mesh                *ast.Ident                     | type    : main.Mesh | type\n009: 11: 8 | Mesh{}              *ast.CompositeLit              | value   : main.Mesh | value\n010: 14: 1 | OnKey               *ast.Ident                     | value   : func(a string, fn func()) | value\n011: 14: 1 | OnKey \"hello\", => {\n} *ast.CallExpr                  | void    : () | no value\n012: 14: 7 | \"hello\"             *ast.BasicLit                  | value   : untyped string = \"hello\" | constant\n013: 16: 1 | OnKey               *ast.Ident                     | value   : func(a string, fn func(key string)) | value\n014: 16: 1 | OnKey \"hello\", key => {\n} *ast.CallExpr                  | void    : () | no value\n015: 16: 7 | \"hello\"             *ast.BasicLit                  | value   : untyped string = \"hello\" | constant\n016: 18: 1 | OnKey               *ast.Ident                     | value   : func(a []string, fn func()) | value\n017: 18: 1 | OnKey [\"1\"], => {\n} *ast.CallExpr                  | void    : () | no value\n018: 18: 8 | \"1\"                 *ast.BasicLit                  | value   : untyped string = \"1\" | constant\n019: 20: 1 | OnKey               *ast.Ident                     | value   : func(a []string, fn func(key string)) | value\n020: 20: 1 | OnKey [\"2\"], key => {\n} *ast.CallExpr                  | void    : () | no value\n021: 20: 8 | \"2\"                 *ast.BasicLit                  | value   : untyped string = \"2\" | constant\n022: 22: 1 | OnKey               *ast.Ident                     | value   : func(a []main.Mesher, fn func()) | value\n023: 22: 1 | OnKey [m1, m2], => {\n} *ast.CallExpr                  | void    : () | no value\n024: 22: 8 | m1                  *ast.Ident                     | var     : *main.Mesh | variable\n025: 22:12 | m2                  *ast.Ident                     | var     : *main.Mesh | variable\n026: 24: 1 | OnKey               *ast.Ident                     | value   : func(a []main.Mesher, fn func(key main.Mesher)) | value\n027: 24: 1 | OnKey [m1, m2], key => {\n} *ast.CallExpr                  | void    : () | no value\n028: 24: 8 | m1                  *ast.Ident                     | var     : *main.Mesh | variable\n029: 24:12 | m2                  *ast.Ident                     | var     : *main.Mesh | variable\n030: 26: 1 | OnKey               *ast.Ident                     | value   : func(a []string, b []string, fn func(key string)) | value\n031: 26: 1 | OnKey [\"a\"], [\"b\"], key => {\n} *ast.CallExpr                  | void    : () | no value\n032: 26: 8 | \"a\"                 *ast.BasicLit                  | value   : untyped string = \"a\" | constant\n033: 26:15 | \"b\"                 *ast.BasicLit                  | value   : untyped string = \"b\" | constant\n034: 28: 1 | OnKey               *ast.Ident                     | value   : func(a []string, b []main.Mesher, fn func(key string)) | value\n035: 28: 1 | OnKey [\"a\"], [m1, m2], key => {\n} *ast.CallExpr                  | void    : () | no value\n036: 28: 8 | \"a\"                 *ast.BasicLit                  | value   : untyped string = \"a\" | constant\n037: 28:15 | m1                  *ast.Ident                     | var     : *main.Mesh | variable\n038: 28:19 | m2                  *ast.Ident                     | var     : *main.Mesh | variable\n039: 30: 1 | OnKey               *ast.Ident                     | value   : func(a []string, b []string, fn func(key string)) | value\n040: 30: 1 | OnKey [\"a\"], nil, key => {\n} *ast.CallExpr                  | void    : () | no value\n041: 30: 8 | \"a\"                 *ast.BasicLit                  | value   : untyped string = \"a\" | constant\n042: 30:14 | nil                 *ast.Ident                     | nil     : untyped nil | value\n043: 32: 1 | OnKey               *ast.Ident                     | value   : func(x int, y int) | value\n044: 32: 1 | OnKey 100, 200      *ast.CallExpr                  | void    : () | no value\n045: 32: 7 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n046: 32:12 | 200                 *ast.BasicLit                  | value   : untyped int = 200 | constant\n047: 33: 1 | OnKey               *ast.Ident                     | value   : func(a string, b string, fn ...func(x int) int) | value\n048: 33: 1 | OnKey \"a\", \"b\", x => x * x, x => {\n\treturn x * 2\n} *ast.CallExpr                  | void    : () | no value\n049: 33: 7 | \"a\"                 *ast.BasicLit                  | value   : untyped string = \"a\" | constant\n050: 33:12 | \"b\"                 *ast.BasicLit                  | value   : untyped string = \"b\" | constant\n051: 33:22 | x                   *ast.Ident                     | var     : int | variable\n052: 33:22 | x * x               *ast.BinaryExpr                | value   : int | value\n053: 33:26 | x                   *ast.Ident                     | var     : int | variable\n054: 34: 9 | x                   *ast.Ident                     | var     : int | variable\n055: 34: 9 | x * 2               *ast.BinaryExpr                | value   : int | value\n056: 34:13 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant\n057: 36: 1 | OnKey               *ast.Ident                     | value   : func(a string, b string, v ...int) | value\n058: 36: 1 | OnKey \"a\", \"b\", 1, 2, 3 *ast.CallExpr                  | void    : () | no value\n059: 36: 7 | \"a\"                 *ast.BasicLit                  | value   : untyped string = \"a\" | constant\n060: 36:12 | \"b\"                 *ast.BasicLit                  | value   : untyped string = \"b\" | constant\n061: 36:17 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n062: 36:20 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant\n063: 36:23 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant\n064: 37: 1 | OnKey               *ast.Ident                     | value   : func(a string, b string, v ...int) | value\n065: 37: 1 | OnKey(\"a\", \"b\", [1, 2, 3]...) *ast.CallExpr                  | void    : () | no value\n066: 37: 7 | \"a\"                 *ast.BasicLit                  | value   : untyped string = \"a\" | constant\n067: 37:12 | \"b\"                 *ast.BasicLit                  | value   : untyped string = \"b\" | constant\n068: 37:18 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n069: 37:21 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant\n070: 37:24 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant\n== defs ==\n000:  2: 6 | Mesh                | type main.Mesh struct{}\n001:  5: 7 | p                   | var p *main.Mesh\n002:  5:16 | Name                | func (*main.Mesh).Name() string\n003: 10: 2 | m1                  | var main.m1 *main.Mesh\n004: 11: 2 | m2                  | var main.m2 *main.Mesh\n005: 14: 1 | main                | func main.main()\n006: 16:16 | key                 | var key string\n007: 20:14 | key                 | var key string\n008: 24:17 | key                 | var key main.Mesher\n009: 26:21 | key                 | var key string\n010: 28:24 | key                 | var key string\n011: 30:19 | key                 | var key string\n012: 33:17 | x                   | var x int\n013: 33:29 | x                   | var x int\n== uses ==\n000:  5:10 | Mesh                | type main.Mesh struct{}\n001:  5:23 | string              | type string\n002: 10: 8 | Mesh                | type main.Mesh struct{}\n003: 11: 8 | Mesh                | type main.Mesh struct{}\n004: 14: 1 | OnKey               | func main.OnKey__0(a string, fn func())\n005: 16: 1 | OnKey               | func main.OnKey__1(a string, fn func(key string))\n006: 18: 1 | OnKey               | func main.OnKey__2(a []string, fn func())\n007: 20: 1 | OnKey               | func main.OnKey__3(a []string, fn func(key string))\n008: 22: 1 | OnKey               | func main.OnKey__4(a []main.Mesher, fn func())\n009: 22: 8 | m1                  | var main.m1 *main.Mesh\n010: 22:12 | m2                  | var main.m2 *main.Mesh\n011: 24: 1 | OnKey               | func main.OnKey__5(a []main.Mesher, fn func(key main.Mesher))\n012: 24: 8 | m1                  | var main.m1 *main.Mesh\n013: 24:12 | m2                  | var main.m2 *main.Mesh\n014: 26: 1 | OnKey               | func main.OnKey__6(a []string, b []string, fn func(key string))\n015: 28: 1 | OnKey               | func main.OnKey__7(a []string, b []main.Mesher, fn func(key string))\n016: 28:15 | m1                  | var main.m1 *main.Mesh\n017: 28:19 | m2                  | var main.m2 *main.Mesh\n018: 30: 1 | OnKey               | func main.OnKey__6(a []string, b []string, fn func(key string))\n019: 30:14 | nil                 | nil\n020: 32: 1 | OnKey               | func main.OnKey__8(x int, y int)\n021: 33: 1 | OnKey               | func main.OnKey__9(a string, b string, fn ...func(x int) int)\n022: 33:22 | x                   | var x int\n023: 33:26 | x                   | var x int\n024: 34: 9 | x                   | var x int\n025: 36: 1 | OnKey               | func main.OnKey__a(a string, b string, v ...int)\n026: 37: 1 | OnKey               | func main.OnKey__a(a string, b string, v ...int)\n== overloads ==\n000: 14: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n001: 16: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n002: 18: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n003: 20: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n004: 22: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n005: 24: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n006: 26: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n007: 28: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n008: 30: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n009: 32: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n010: 33: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n011: 36: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})\n012: 37: 1 | OnKey               | func main.OnKey(__xgo_overload_args__ interface{_()})`)\n}\n\nfunc TestMixedOverload2(t *testing.T) {\n\ttestXGoInfo(t, `\ntype Mesh struct {\n}\n\nfunc (p *Mesh) Name() string {\n\treturn \"hello\"\n}\n\nvar (\n\tm1 = &Mesh{}\n\tm2 = &Mesh{}\n)\n\nn := &N{}\nn.onKey \"hello\", => {\n}\nn.onKey \"hello\", key => {\n}\nn.onKey [\"1\"], => {\n}\nn.onKey [\"2\"], key => {\n}\nn.onKey [m1, m2], => {\n}\nn.onKey [m1, m2], key => {\n}\nn.onKey [\"a\"], [\"b\"], key => {\n}\nn.onKey [\"a\"], [m1, m2], key => {\n}\nn.onKey [\"a\"], nil, key => {\n}\nn.onKey 100, 200\n`, `\npackage main\n\ntype Mesher interface {\n\tName() string\n}\n\ntype N struct {\n}\n\nfunc (m *N) OnKey__0(a string, fn func()) {\n}\n\nfunc (m *N) OnKey__1(a string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__2(a []string, fn func()) {\n}\n\nfunc (m *N) OnKey__3(a []string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__4(a []Mesher, fn func()) {\n}\n\nfunc (m *N) OnKey__5(a []Mesher, fn func(key Mesher)) {\n}\n\nfunc (m *N) OnKey__6(a []string, b []string, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__7(a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc (m *N) OnKey__8(x int, y int) {\n}\n\n\nfunc OnKey__0(a string, fn func()) {\n}\n\nfunc OnKey__1(a string, fn func(key string)) {\n}\n\nfunc OnKey__2(a []string, fn func()) {\n}\n\nfunc OnKey__3(a []string, fn func(key string)) {\n}\n\nfunc OnKey__4(a []Mesher, fn func()) {\n}\n\nfunc OnKey__5(a []Mesher, fn func(key Mesher)) {\n}\n\nfunc OnKey__6(a []string, b []string, fn func(key string)) {\n}\n\nfunc OnKey__7(a []string, b []Mesher, fn func(key string)) {\n}\n\nfunc OnKey__8(x int, y int) {\n}\n\nfunc OnKey__9(a, b string, fn ...func(x int) int) {\n}\n\nfunc OnKey__a(a, b string, v ...int) {\n}\n`, `== types ==\n000:  2:11 | struct {\n}          *ast.StructType                | type    : struct{} | type\n001:  5:10 | Mesh                *ast.Ident                     | type    : main.Mesh | type\n002:  5:23 | string              *ast.Ident                     | type    : string | type\n003:  6: 9 | \"hello\"             *ast.BasicLit                  | value   : untyped string = \"hello\" | constant\n004: 10: 7 | &Mesh{}             *ast.UnaryExpr                 | value   : *main.Mesh | value\n005: 10: 8 | Mesh                *ast.Ident                     | type    : main.Mesh | type\n006: 10: 8 | Mesh{}              *ast.CompositeLit              | value   : main.Mesh | value\n007: 11: 7 | &Mesh{}             *ast.UnaryExpr                 | value   : *main.Mesh | value\n008: 11: 8 | Mesh                *ast.Ident                     | type    : main.Mesh | type\n009: 11: 8 | Mesh{}              *ast.CompositeLit              | value   : main.Mesh | value\n010: 14: 6 | &N{}                *ast.UnaryExpr                 | value   : *main.N | value\n011: 14: 7 | N                   *ast.Ident                     | type    : main.N | type\n012: 14: 7 | N{}                 *ast.CompositeLit              | value   : main.N | value\n013: 15: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n014: 15: 1 | n.onKey             *ast.SelectorExpr              | value   : func(a string, fn func()) | value\n015: 15: 1 | n.onKey \"hello\", => {\n} *ast.CallExpr                  | void    : () | no value\n016: 15: 9 | \"hello\"             *ast.BasicLit                  | value   : untyped string = \"hello\" | constant\n017: 17: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n018: 17: 1 | n.onKey             *ast.SelectorExpr              | value   : func(a string, fn func(key string)) | value\n019: 17: 1 | n.onKey \"hello\", key => {\n} *ast.CallExpr                  | void    : () | no value\n020: 17: 9 | \"hello\"             *ast.BasicLit                  | value   : untyped string = \"hello\" | constant\n021: 19: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n022: 19: 1 | n.onKey             *ast.SelectorExpr              | value   : func(a []string, fn func()) | value\n023: 19: 1 | n.onKey [\"1\"], => {\n} *ast.CallExpr                  | void    : () | no value\n024: 19:10 | \"1\"                 *ast.BasicLit                  | value   : untyped string = \"1\" | constant\n025: 21: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n026: 21: 1 | n.onKey             *ast.SelectorExpr              | value   : func(a []string, fn func(key string)) | value\n027: 21: 1 | n.onKey [\"2\"], key => {\n} *ast.CallExpr                  | void    : () | no value\n028: 21:10 | \"2\"                 *ast.BasicLit                  | value   : untyped string = \"2\" | constant\n029: 23: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n030: 23: 1 | n.onKey             *ast.SelectorExpr              | value   : func(a []main.Mesher, fn func()) | value\n031: 23: 1 | n.onKey [m1, m2], => {\n} *ast.CallExpr                  | void    : () | no value\n032: 23:10 | m1                  *ast.Ident                     | var     : *main.Mesh | variable\n033: 23:14 | m2                  *ast.Ident                     | var     : *main.Mesh | variable\n034: 25: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n035: 25: 1 | n.onKey             *ast.SelectorExpr              | value   : func(a []main.Mesher, fn func(key main.Mesher)) | value\n036: 25: 1 | n.onKey [m1, m2], key => {\n} *ast.CallExpr                  | void    : () | no value\n037: 25:10 | m1                  *ast.Ident                     | var     : *main.Mesh | variable\n038: 25:14 | m2                  *ast.Ident                     | var     : *main.Mesh | variable\n039: 27: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n040: 27: 1 | n.onKey             *ast.SelectorExpr              | value   : func(a []string, b []string, fn func(key string)) | value\n041: 27: 1 | n.onKey [\"a\"], [\"b\"], key => {\n} *ast.CallExpr                  | void    : () | no value\n042: 27:10 | \"a\"                 *ast.BasicLit                  | value   : untyped string = \"a\" | constant\n043: 27:17 | \"b\"                 *ast.BasicLit                  | value   : untyped string = \"b\" | constant\n044: 29: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n045: 29: 1 | n.onKey             *ast.SelectorExpr              | value   : func(a []string, b []main.Mesher, fn func(key string)) | value\n046: 29: 1 | n.onKey [\"a\"], [m1, m2], key => {\n} *ast.CallExpr                  | void    : () | no value\n047: 29:10 | \"a\"                 *ast.BasicLit                  | value   : untyped string = \"a\" | constant\n048: 29:17 | m1                  *ast.Ident                     | var     : *main.Mesh | variable\n049: 29:21 | m2                  *ast.Ident                     | var     : *main.Mesh | variable\n050: 31: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n051: 31: 1 | n.onKey             *ast.SelectorExpr              | value   : func(a []string, b []string, fn func(key string)) | value\n052: 31: 1 | n.onKey [\"a\"], nil, key => {\n} *ast.CallExpr                  | void    : () | no value\n053: 31:10 | \"a\"                 *ast.BasicLit                  | value   : untyped string = \"a\" | constant\n054: 31:16 | nil                 *ast.Ident                     | nil     : untyped nil | value\n055: 33: 1 | n                   *ast.Ident                     | var     : *main.N | variable\n056: 33: 1 | n.onKey             *ast.SelectorExpr              | value   : func(x int, y int) | value\n057: 33: 1 | n.onKey 100, 200    *ast.CallExpr                  | void    : () | no value\n058: 33: 9 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n059: 33:14 | 200                 *ast.BasicLit                  | value   : untyped int = 200 | constant\n== defs ==\n000:  2: 6 | Mesh                | type main.Mesh struct{}\n001:  5: 7 | p                   | var p *main.Mesh\n002:  5:16 | Name                | func (*main.Mesh).Name() string\n003: 10: 2 | m1                  | var main.m1 *main.Mesh\n004: 11: 2 | m2                  | var main.m2 *main.Mesh\n005: 14: 1 | main                | func main.main()\n006: 14: 1 | n                   | var n *main.N\n007: 17:18 | key                 | var key string\n008: 21:16 | key                 | var key string\n009: 25:19 | key                 | var key main.Mesher\n010: 27:23 | key                 | var key string\n011: 29:26 | key                 | var key string\n012: 31:21 | key                 | var key string\n== uses ==\n000:  5:10 | Mesh                | type main.Mesh struct{}\n001:  5:23 | string              | type string\n002: 10: 8 | Mesh                | type main.Mesh struct{}\n003: 11: 8 | Mesh                | type main.Mesh struct{}\n004: 14: 7 | N                   | type main.N struct{}\n005: 15: 1 | n                   | var n *main.N\n006: 15: 3 | onKey               | func (*main.N).OnKey__0(a string, fn func())\n007: 17: 1 | n                   | var n *main.N\n008: 17: 3 | onKey               | func (*main.N).OnKey__1(a string, fn func(key string))\n009: 19: 1 | n                   | var n *main.N\n010: 19: 3 | onKey               | func (*main.N).OnKey__2(a []string, fn func())\n011: 21: 1 | n                   | var n *main.N\n012: 21: 3 | onKey               | func (*main.N).OnKey__3(a []string, fn func(key string))\n013: 23: 1 | n                   | var n *main.N\n014: 23: 3 | onKey               | func (*main.N).OnKey__4(a []main.Mesher, fn func())\n015: 23:10 | m1                  | var main.m1 *main.Mesh\n016: 23:14 | m2                  | var main.m2 *main.Mesh\n017: 25: 1 | n                   | var n *main.N\n018: 25: 3 | onKey               | func (*main.N).OnKey__5(a []main.Mesher, fn func(key main.Mesher))\n019: 25:10 | m1                  | var main.m1 *main.Mesh\n020: 25:14 | m2                  | var main.m2 *main.Mesh\n021: 27: 1 | n                   | var n *main.N\n022: 27: 3 | onKey               | func (*main.N).OnKey__6(a []string, b []string, fn func(key string))\n023: 29: 1 | n                   | var n *main.N\n024: 29: 3 | onKey               | func (*main.N).OnKey__7(a []string, b []main.Mesher, fn func(key string))\n025: 29:17 | m1                  | var main.m1 *main.Mesh\n026: 29:21 | m2                  | var main.m2 *main.Mesh\n027: 31: 1 | n                   | var n *main.N\n028: 31: 3 | onKey               | func (*main.N).OnKey__6(a []string, b []string, fn func(key string))\n029: 31:16 | nil                 | nil\n030: 33: 1 | n                   | var n *main.N\n031: 33: 3 | onKey               | func (*main.N).OnKey__8(x int, y int)\n== overloads ==\n000: 15: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})\n001: 17: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})\n002: 19: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})\n003: 21: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})\n004: 23: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})\n005: 25: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})\n006: 27: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})\n007: 29: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})\n008: 31: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})\n009: 33: 3 | onKey               | func (main.N).OnKey(__xgo_overload_args__ interface{_()})`)\n}\n\nfunc TestMixedOverload3(t *testing.T) {\n\ttestXGoInfo(t, `\nTest\nTest 100\nvar n N\nn.test\nn.test 100\n`, `\npackage main\n\nfunc Test__0() {\n}\nfunc Test__1(n int) {\n}\ntype N struct {\n}\nfunc (p *N) Test__0() {\n}\nfunc (p *N) Test__1(n int) {\n}\n`, `== types ==\n000:  2: 1 | Test                *ast.Ident                     | value   : func() | value\n001:  3: 1 | Test                *ast.Ident                     | value   : func(n int) | value\n002:  3: 1 | Test 100            *ast.CallExpr                  | void    : () | no value\n003:  3: 6 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n004:  4: 7 | N                   *ast.Ident                     | type    : main.N | type\n005:  5: 1 | n                   *ast.Ident                     | var     : main.N | variable\n006:  5: 1 | n.test              *ast.SelectorExpr              | value   : func() | value\n007:  6: 1 | n                   *ast.Ident                     | var     : main.N | variable\n008:  6: 1 | n.test              *ast.SelectorExpr              | value   : func(n int) | value\n009:  6: 1 | n.test 100          *ast.CallExpr                  | void    : () | no value\n010:  6: 8 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n== defs ==\n000:  2: 1 | main                | func main.main()\n001:  4: 5 | n                   | var n main.N\n== uses ==\n000:  2: 1 | Test                | func main.Test__0()\n001:  3: 1 | Test                | func main.Test__1(n int)\n002:  4: 7 | N                   | type main.N struct{}\n003:  5: 1 | n                   | var n main.N\n004:  5: 3 | test                | func (*main.N).Test__0()\n005:  6: 1 | n                   | var n main.N\n006:  6: 3 | test                | func (*main.N).Test__1(n int)\n== overloads ==\n000:  2: 1 | Test                | func main.Test(__xgo_overload_args__ interface{_()})\n001:  3: 1 | Test                | func main.Test(__xgo_overload_args__ interface{_()})\n002:  5: 3 | test                | func (main.N).Test(__xgo_overload_args__ interface{_()})\n003:  6: 3 | test                | func (main.N).Test(__xgo_overload_args__ interface{_()})`)\n}\n\nfunc TestOverloadNamed(t *testing.T) {\n\ttestXGoInfo(t, `\nimport \"github.com/goplus/xgo/cl/internal/overload/bar\"\n\nvar a bar.Var[int]\nvar b bar.Var[bar.M]\nc := bar.Var(string)\nd := bar.Var(bar.M)\n`, ``, `== types ==\n000:  4: 7 | bar.Var             *ast.SelectorExpr              | type    : github.com/goplus/xgo/cl/internal/overload/bar.Var__0[int] | type\n001:  4: 7 | bar.Var[int]        *ast.IndexExpr                 | type    : github.com/goplus/xgo/cl/internal/overload/bar.Var__0[int] | type\n002:  4:15 | int                 *ast.Ident                     | type    : int | type\n003:  5: 7 | bar.Var             *ast.SelectorExpr              | type    : github.com/goplus/xgo/cl/internal/overload/bar.Var__1[github.com/goplus/xgo/cl/internal/overload/bar.M] | type\n004:  5: 7 | bar.Var[bar.M]      *ast.IndexExpr                 | type    : github.com/goplus/xgo/cl/internal/overload/bar.Var__1[github.com/goplus/xgo/cl/internal/overload/bar.M] | type\n005:  5:15 | bar.M               *ast.SelectorExpr              | type    : github.com/goplus/xgo/cl/internal/overload/bar.M | type\n006:  6: 6 | bar.Var             *ast.SelectorExpr              | value   : func[T github.com/goplus/xgo/cl/internal/overload/bar.basetype]() *github.com/goplus/xgo/cl/internal/overload/bar.Var__0[T] | value\n007:  6: 6 | bar.Var(string)     *ast.CallExpr                  | value   : *github.com/goplus/xgo/cl/internal/overload/bar.Var__0[string] | value\n008:  6:14 | string              *ast.Ident                     | type    : string | type\n009:  7: 6 | bar.Var             *ast.SelectorExpr              | value   : func[T map[string]any]() *github.com/goplus/xgo/cl/internal/overload/bar.Var__1[T] | value\n010:  7: 6 | bar.Var(bar.M)      *ast.CallExpr                  | value   : *github.com/goplus/xgo/cl/internal/overload/bar.Var__1[github.com/goplus/xgo/cl/internal/overload/bar.M] | value\n011:  7:14 | bar.M               *ast.SelectorExpr              | var     : github.com/goplus/xgo/cl/internal/overload/bar.M | variable\n== defs ==\n000:  4: 5 | a                   | var main.a github.com/goplus/xgo/cl/internal/overload/bar.Var__0[int]\n001:  5: 5 | b                   | var main.b github.com/goplus/xgo/cl/internal/overload/bar.Var__1[github.com/goplus/xgo/cl/internal/overload/bar.M]\n002:  6: 1 | c                   | var c *github.com/goplus/xgo/cl/internal/overload/bar.Var__0[string]\n003:  6: 1 | main                | func main.main()\n004:  7: 1 | d                   | var d *github.com/goplus/xgo/cl/internal/overload/bar.Var__1[github.com/goplus/xgo/cl/internal/overload/bar.M]\n== uses ==\n000:  4: 7 | bar                 | package bar (\"github.com/goplus/xgo/cl/internal/overload/bar\")\n001:  4:11 | Var                 | type github.com/goplus/xgo/cl/internal/overload/bar.Var__0[T github.com/goplus/xgo/cl/internal/overload/bar.basetype] struct{val T}\n002:  4:15 | int                 | type int\n003:  5: 7 | bar                 | package bar (\"github.com/goplus/xgo/cl/internal/overload/bar\")\n004:  5:11 | Var                 | type github.com/goplus/xgo/cl/internal/overload/bar.Var__1[T map[string]any] struct{val T}\n005:  5:15 | bar                 | package bar (\"github.com/goplus/xgo/cl/internal/overload/bar\")\n006:  5:19 | M                   | type github.com/goplus/xgo/cl/internal/overload/bar.M = map[string]any\n007:  6: 6 | bar                 | package bar (\"github.com/goplus/xgo/cl/internal/overload/bar\")\n008:  6:10 | Var                 | func github.com/goplus/xgo/cl/internal/overload/bar.XGox_Var_Cast__0[T github.com/goplus/xgo/cl/internal/overload/bar.basetype]() *github.com/goplus/xgo/cl/internal/overload/bar.Var__0[T]\n009:  6:14 | string              | type string\n010:  7: 6 | bar                 | package bar (\"github.com/goplus/xgo/cl/internal/overload/bar\")\n011:  7:10 | Var                 | func github.com/goplus/xgo/cl/internal/overload/bar.XGox_Var_Cast__1[T map[string]any]() *github.com/goplus/xgo/cl/internal/overload/bar.Var__1[T]\n012:  7:14 | bar                 | package bar (\"github.com/goplus/xgo/cl/internal/overload/bar\")\n013:  7:18 | M                   | type github.com/goplus/xgo/cl/internal/overload/bar.M = map[string]any\n== overloads ==\n000:  4:11 | Var                 | type github.com/goplus/xgo/cl/internal/overload/bar.Var = func(__xgo_overload_args__ interface{_()})\n001:  5:11 | Var                 | type github.com/goplus/xgo/cl/internal/overload/bar.Var = func(__xgo_overload_args__ interface{_()})\n002:  6:10 | Var                 | type github.com/goplus/xgo/cl/internal/overload/bar.Var = func(__xgo_overload_args__ interface{_()})\n003:  7:10 | Var                 | type github.com/goplus/xgo/cl/internal/overload/bar.Var = func(__xgo_overload_args__ interface{_()})`)\n}\n\nfunc TestMixedOverloadNamed(t *testing.T) {\n\ttestXGoInfo(t, `\nvar a Var[int]\nvar b Var[M]\nc := Var(string)\nd := Var(M)\n`, `\npackage main\n\ntype M = map[string]any\n\ntype basetype interface {\n\tstring | int | bool | float64\n}\n\ntype Var__0[T basetype] struct {\n\tval T\n}\n\ntype Var__1[T map[string]any] struct {\n\tval T\n}\n\nfunc XGox_Var_Cast__0[T basetype]() *Var__0[T] {\n\treturn new(Var__0[T])\n}\n\nfunc XGox_Var_Cast__1[T map[string]any]() *Var__1[T] {\n\treturn new(Var__1[T])\n}\n`, `== types ==\n000:  2: 7 | Var                 *ast.Ident                     | type    : main.Var__0[int] | type\n001:  2: 7 | Var[int]            *ast.IndexExpr                 | type    : main.Var__0[int] | type\n002:  2:11 | int                 *ast.Ident                     | type    : int | type\n003:  3: 7 | Var                 *ast.Ident                     | type    : main.Var__1[main.M] | type\n004:  3: 7 | Var[M]              *ast.IndexExpr                 | type    : main.Var__1[main.M] | type\n005:  3:11 | M                   *ast.Ident                     | type    : main.M | type\n006:  4: 6 | Var                 *ast.Ident                     | value   : func[T main.basetype]() *main.Var__0[T] | value\n007:  4: 6 | Var(string)         *ast.CallExpr                  | value   : *main.Var__0[string] | value\n008:  4:10 | string              *ast.Ident                     | type    : string | type\n009:  5: 6 | Var                 *ast.Ident                     | value   : func[T map[string]interface{}]() *main.Var__1[T] | value\n010:  5: 6 | Var(M)              *ast.CallExpr                  | value   : *main.Var__1[main.M] | value\n011:  5:10 | M                   *ast.Ident                     | type    : main.M | type\n== defs ==\n000:  2: 5 | a                   | var main.a main.Var__0[int]\n001:  3: 5 | b                   | var main.b main.Var__1[main.M]\n002:  4: 1 | c                   | var c *main.Var__0[string]\n003:  4: 1 | main                | func main.main()\n004:  5: 1 | d                   | var d *main.Var__1[main.M]\n== uses ==\n000:  2: 7 | Var                 | type main.Var__0[T main.basetype] struct{val T}\n001:  2:11 | int                 | type int\n002:  3: 7 | Var                 | type main.Var__1[T map[string]any] struct{val T}\n003:  3:11 | M                   | type main.M = map[string]any\n004:  4: 6 | Var                 | func main.XGox_Var_Cast__0[T main.basetype]() *main.Var__0[T]\n005:  4:10 | string              | type string\n006:  5: 6 | Var                 | func main.XGox_Var_Cast__1[T map[string]any]() *main.Var__1[T]\n007:  5:10 | M                   | type main.M = map[string]any\n== overloads ==\n000:  2: 7 | Var                 | type main.Var = func(__xgo_overload_args__ interface{_()})\n001:  3: 7 | Var                 | type main.Var = func(__xgo_overload_args__ interface{_()})\n002:  4: 6 | Var                 | type main.Var = func(__xgo_overload_args__ interface{_()})\n003:  5: 6 | Var                 | type main.Var = func(__xgo_overload_args__ interface{_()})`)\n}\n\nfunc TestMixedRawNamed(t *testing.T) {\n\ttestXGoInfo(t, `\nvar a Var__0[int]\nvar b Var__1[M]\nc := XGox_Var_Cast__0[string]\nd := XGox_Var_Cast__1[M]\n`, `\npackage main\n\ntype M = map[string]any\n\ntype basetype interface {\n\tstring | int | bool | float64\n}\n\ntype Var__0[T basetype] struct {\n\tval T\n}\n\ntype Var__1[T map[string]any] struct {\n\tval T\n}\n\nfunc XGox_Var_Cast__0[T basetype]() *Var__0[T] {\n\treturn new(Var__0[T])\n}\n\nfunc XGox_Var_Cast__1[T map[string]any]() *Var__1[T] {\n\treturn new(Var__1[T])\n}\n`, `== types ==\n000:  2: 7 | Var__0              *ast.Ident                     | type    : main.Var__0[int] | type\n001:  2: 7 | Var__0[int]         *ast.IndexExpr                 | type    : main.Var__0[int] | type\n002:  2:14 | int                 *ast.Ident                     | type    : int | type\n003:  3: 7 | Var__1              *ast.Ident                     | type    : main.Var__1[main.M] | type\n004:  3: 7 | Var__1[M]           *ast.IndexExpr                 | type    : main.Var__1[main.M] | type\n005:  3:14 | M                   *ast.Ident                     | type    : main.M | type\n006:  4: 6 | XGox_Var_Cast__0    *ast.Ident                     | value   : func[T main.basetype]() *main.Var__0[T] | value\n007:  4: 6 | XGox_Var_Cast__0[string] *ast.IndexExpr                 | var     : func() *main.Var__0[string] | variable\n008:  4:23 | string              *ast.Ident                     | type    : string | type\n009:  5: 6 | XGox_Var_Cast__1    *ast.Ident                     | value   : func[T map[string]interface{}]() *main.Var__1[T] | value\n010:  5: 6 | XGox_Var_Cast__1[M] *ast.IndexExpr                 | var     : func() *main.Var__1[main.M] | variable\n011:  5:23 | M                   *ast.Ident                     | type    : main.M | type\n== defs ==\n000:  2: 5 | a                   | var main.a main.Var__0[int]\n001:  3: 5 | b                   | var main.b main.Var__1[main.M]\n002:  4: 1 | c                   | var c func() *main.Var__0[string]\n003:  4: 1 | main                | func main.main()\n004:  5: 1 | d                   | var d func() *main.Var__1[main.M]\n== uses ==\n000:  2: 7 | Var__0              | type main.Var__0[T main.basetype] struct{val T}\n001:  2:14 | int                 | type int\n002:  3: 7 | Var__1              | type main.Var__1[T map[string]any] struct{val T}\n003:  3:14 | M                   | type main.M = map[string]any\n004:  4: 6 | XGox_Var_Cast__0    | func main.XGox_Var_Cast__0[T main.basetype]() *main.Var__0[T]\n005:  4:23 | string              | type string\n006:  5: 6 | XGox_Var_Cast__1    | func main.XGox_Var_Cast__1[T map[string]any]() *main.Var__1[T]\n007:  5:23 | M                   | type main.M = map[string]any`)\n}\n\nfunc TestSpxInfo(t *testing.T) {\n\ttestSpxInfo(t, \"Kai.tspx\", `\nvar (\n\ta int\n)\n\ntype info struct {\n\tx int\n\ty int\n}\n\nfunc onInit() {\n\ta = 1\n\tclone\n\tclone info{1,2}\n\tclone &info{1,2}\n}\n\nfunc onCloned() {\n\tsay(\"Hi\")\n}\n`, `== types ==\n000:  0: 0 | MyGame              *ast.Ident                     | type    : main.MyGame | type\n001:  1: 1 | Kai                 *ast.Ident                     | type    : main.Kai | type\n002:  3: 4 | int                 *ast.Ident                     | type    : int | type\n003:  6:11 | struct {\n\tx int\n\ty int\n} *ast.StructType                | type    : struct{x int; y int} | type\n004:  7: 4 | int                 *ast.Ident                     | type    : int | type\n005:  8: 4 | int                 *ast.Ident                     | type    : int | type\n006: 12: 2 | a                   *ast.Ident                     | var     : int | variable\n007: 12: 6 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n008: 13: 2 | clone               *ast.Ident                     | value   : func(sprite any) | value\n009: 14: 2 | clone               *ast.Ident                     | value   : func(sprite any, data any) | value\n010: 14: 2 | clone info{1, 2}    *ast.CallExpr                  | void    : () | no value\n011: 14: 8 | info                *ast.Ident                     | type    : main.info | type\n012: 14: 8 | info{1, 2}          *ast.CompositeLit              | value   : main.info | value\n013: 14:13 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n014: 14:15 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant\n015: 15: 2 | clone               *ast.Ident                     | value   : func(sprite any, data any) | value\n016: 15: 2 | clone &info{1, 2}   *ast.CallExpr                  | void    : () | no value\n017: 15: 8 | &info{1, 2}         *ast.UnaryExpr                 | value   : *main.info | value\n018: 15: 9 | info                *ast.Ident                     | type    : main.info | type\n019: 15: 9 | info{1, 2}          *ast.CompositeLit              | value   : main.info | value\n020: 15:14 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant\n021: 15:16 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant\n022: 19: 2 | say                 *ast.Ident                     | value   : func(msg string, secs ...float64) | value\n023: 19: 2 | say(\"Hi\")           *ast.CallExpr                  | void    : () | no value\n024: 19: 6 | \"Hi\"                *ast.BasicLit                  | value   : untyped string = \"Hi\" | constant\n== defs ==\n000:  0: 0 | Main                | func (*main.Kai).Main()\n001:  1: 1 | this                | var this *main.Kai\n002:  3: 2 | a                   | field a int\n003:  6: 6 | info                | type main.info struct{x int; y int}\n004:  7: 2 | x                   | field x int\n005:  8: 2 | y                   | field y int\n006: 11: 6 | onInit              | func (*main.Kai).onInit()\n007: 18: 6 | onCloned            | func (*main.Kai).onCloned()\n== uses ==\n000:  0: 0 | MyGame              | type main.MyGame struct{*github.com/goplus/xgo/cl/internal/spx.MyGame}\n001:  1: 1 | Kai                 | type main.Kai struct{github.com/goplus/xgo/cl/internal/spx.Sprite; *main.MyGame; a int}\n002:  3: 4 | int                 | type int\n003:  7: 4 | int                 | type int\n004:  8: 4 | int                 | type int\n005: 12: 2 | a                   | field a int\n006: 13: 2 | clone               | func github.com/goplus/xgo/cl/internal/spx.Gopt_Sprite_Clone__0(sprite any)\n007: 14: 2 | clone               | func github.com/goplus/xgo/cl/internal/spx.Gopt_Sprite_Clone__1(sprite any, data any)\n008: 14: 8 | info                | type main.info struct{x int; y int}\n009: 15: 2 | clone               | func github.com/goplus/xgo/cl/internal/spx.Gopt_Sprite_Clone__1(sprite any, data any)\n010: 15: 9 | info                | type main.info struct{x int; y int}\n011: 19: 2 | say                 | func (*github.com/goplus/xgo/cl/internal/spx.Sprite).Say(msg string, secs ...float64)\n== overloads ==\n000: 13: 2 | clone               | func (github.com/goplus/xgo/cl/internal/spx.Sprite).Clone(__xgo_overload_args__ interface{_()})\n001: 14: 2 | clone               | func (github.com/goplus/xgo/cl/internal/spx.Sprite).Clone(__xgo_overload_args__ interface{_()})\n002: 15: 2 | clone               | func (github.com/goplus/xgo/cl/internal/spx.Sprite).Clone(__xgo_overload_args__ interface{_()})`)\n}\n\nfunc TestScopesInfo(t *testing.T) {\n\tvar tests = []struct {\n\t\tsrc    string\n\t\tscopes []string // list of scope descriptors of the form kind:varlist\n\t}{\n\t\t{`package p0`, []string{\n\t\t\t\"file:\",\n\t\t}},\n\t\t{`package p1; import ( \"fmt\"; m \"math\"; _ \"os\" ); var ( _ = fmt.Println; _ = m.Pi )`, []string{\n\t\t\t\"file:fmt m\",\n\t\t}},\n\t\t{`package p2; func _() {}`, []string{\n\t\t\t\"file:\", \"func:\",\n\t\t}},\n\t\t{`package p3; func _(x, y int) {}`, []string{\n\t\t\t\"file:\", \"func:x y\",\n\t\t}},\n\t\t{`package p4; func _(x, y int) { x, z := 1, 2; _ = z }`, []string{\n\t\t\t\"file:\", \"func:x y z\", // redeclaration of x\n\t\t}},\n\t\t{`package p5; func _(x, y int) (u, _ int) { return }`, []string{\n\t\t\t\"file:\", \"func:u x y\",\n\t\t}},\n\t\t{`package p6; func _() { { var x int; _ = x } }`, []string{\n\t\t\t\"file:\", \"func:\", \"block:x\",\n\t\t}},\n\t\t{`package p7; func _() { if true {} }`, []string{\n\t\t\t\"file:\", \"func:\", \"if:\", \"block:\",\n\t\t}},\n\t\t{`package p8; func _() { if x := 0; x < 0 { y := x; _ = y } }`, []string{\n\t\t\t\"file:\", \"func:\", \"if:x\", \"block:y\",\n\t\t}},\n\t\t{`package p9; func _() { switch x := 0; x {} }`, []string{\n\t\t\t\"file:\", \"func:\", \"switch:x\",\n\t\t}},\n\t\t{`package p10; func _() { switch x := 0; x { case 1: y := x; _ = y; default: }}`, []string{\n\t\t\t\"file:\", \"func:\", \"switch:x\", \"case:y\", \"case:\",\n\t\t}},\n\t\t{`package p11; func _(t interface{}) { switch t.(type) {} }`, []string{\n\t\t\t\"file:\", \"func:t\", \"type switch:\",\n\t\t}},\n\t\t{`package p12; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{\n\t\t\t\"file:\", \"func:t\", \"type switch:t\",\n\t\t}},\n\t\t{`package p13; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{\n\t\t\t\"file:\", \"func:t\", \"type switch:\", \"case:x\", // x implicitly declared\n\t\t}},\n\t\t{`package p14; func _() { select{} }`, []string{\n\t\t\t\"file:\", \"func:\",\n\t\t}},\n\t\t{`package p15; func _(c chan int) { select{ case <-c: } }`, []string{\n\t\t\t\"file:\", \"func:c\", \"comm:\",\n\t\t}},\n\t\t{`package p16; func _(c chan int) { select{ case i := <-c: x := i; _ = x} }`, []string{\n\t\t\t\"file:\", \"func:c\", \"comm:i x\",\n\t\t}},\n\t\t{`package p17; func _() { for{} }`, []string{\n\t\t\t\"file:\", \"func:\", \"for:\", \"block:\",\n\t\t}},\n\t\t{`package p18; func _(n int) { for i := 0; i < n; i++ { _ = i } }`, []string{\n\t\t\t\"file:\", \"func:n\", \"for:i\", \"block:\",\n\t\t}},\n\t\t{`package p19; func _(a []int) { for i := range a { _ = i} }`, []string{\n\t\t\t\"file:\", \"func:a\", \"range:i\", \"block:\",\n\t\t}},\n\t\t{`package p20; var s int; func _(a []int) { for i, x := range a { s += x; _ = i } }`, []string{\n\t\t\t\"file:\", \"func:a\", \"range:i x\", \"block:\",\n\t\t}},\n\t\t{`package p21; var s int; func _(a []int) { for i, x := range a { c := i; println(c) } }`, []string{\n\t\t\t\"file:\", \"func:a\", \"range:i x\", \"block:c\",\n\t\t}},\n\t\t{`package p22; func _(){ sum := 0; for x <- [1, 3, 5, 7, 11, 13, 17], x > 3 { sum = sum + x; c := sum; _ = c } }`, []string{\n\t\t\t\"file:\", \"func:sum\", \"for phrase:x\", \"block:c\",\n\t\t}},\n\t\t{`package p23; func _(){ sum := 0; for x <- [1, 3, 5, 7, 11, 13, 17] { sum = sum + x; c := sum; _ = c } }`, []string{\n\t\t\t\"file:\", \"func:sum\", \"for phrase:x\", \"block:c\",\n\t\t}},\n\t\t{`package p23; func test(fn func(int)int){};func _(){test(func(x int) int { y := x*x; return y } ) }`, []string{\n\t\t\t\"file:test\", \"func:fn\", \"func:\", \"func:x y\",\n\t\t}},\n\t\t{`package p24; func test(fn func(int)int){};func _(){test( x => x*x );}`, []string{\n\t\t\t\"file:test\", \"func:fn\", \"func:\", \"lambda:x\",\n\t\t}},\n\t\t{`package p25; func test(fn func(int)int){};func _(){test( x => { y := x*x; return y } ) }`, []string{\n\t\t\t\"file:test\", \"func:fn\", \"func:\", \"lambda:x y\",\n\t\t}},\n\t\t{`package p26; func _(){ b := {for x <- [\"1\", \"3\", \"5\", \"7\", \"11\"], x == \"5\"}; _ = b }`, []string{\n\t\t\t\"file:\", \"func:b\", \"for phrase:x\",\n\t\t}},\n\t\t{`package p27; func _(){ b, ok := {i for i, x <- [\"1\", \"3\", \"5\", \"7\", \"11\"], x == \"5\"}; _ = b; _ = ok }`, []string{\n\t\t\t\"file:\", \"func:b ok\", \"for phrase:i x\",\n\t\t}},\n\t\t{`package p28; func _(){ a := [x*x for x <- [1, 3.4, 5] if x > 2 ]; _ = a }`, []string{\n\t\t\t\"file:\", \"func:a\", \"for phrase:x\",\n\t\t}},\n\t\t{`package p29; func _(){ arr := [1, 2, 3, 4.1, 5, 6];x := [[a, b] for a <- arr, a < b for b <- arr, b > 2]; _ = x }`, []string{\n\t\t\t\"file:\", \"func:arr x\", \"for phrase:b\", \"for phrase:a\",\n\t\t}},\n\t\t{`package p30; func _(){ y := {x: i for i, x <- [\"1\", \"3\", \"5\", \"7\", \"11\"]}; _ = y }`, []string{\n\t\t\t\"file:\", \"func:y\", \"for phrase:i x\",\n\t\t}},\n\t\t{`package p31; func _(){ z := {v: k for k, v <- {\"Hello\": 1, \"Hi\": 3, \"xsw\": 5, \"XGo\": 7}, v > 3}; _ = z }`, []string{\n\t\t\t\"file:\", \"func:z\", \"for phrase:k v\",\n\t\t}},\n\t}\n\n\tfor _, test := range tests {\n\t\tpkg, info, err := parseSource(token.NewFileSet(), \"src.xgo\", test.src, 0)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"parse source failed: %v\", test.src)\n\t\t}\n\t\tname := pkg.Name()\n\t\t// number of scopes must match\n\t\tif len(info.Scopes) != len(test.scopes) {\n\t\t\tt.Errorf(\"package %s: got %d scopes; want %d\\n%v\\n%v\", name, len(info.Scopes), len(test.scopes),\n\t\t\t\ttest.scopes, info.Scopes)\n\t\t}\n\n\t\t// scope descriptions must match\n\t\tfor node, scope := range info.Scopes {\n\t\t\tkind := \"<unknown node kind>\"\n\t\t\tswitch node.(type) {\n\t\t\tcase *ast.File:\n\t\t\t\tkind = \"file\"\n\t\t\tcase *ast.FuncType:\n\t\t\t\tkind = \"func\"\n\t\t\tcase *ast.BlockStmt:\n\t\t\t\tkind = \"block\"\n\t\t\tcase *ast.IfStmt:\n\t\t\t\tkind = \"if\"\n\t\t\tcase *ast.SwitchStmt:\n\t\t\t\tkind = \"switch\"\n\t\t\tcase *ast.TypeSwitchStmt:\n\t\t\t\tkind = \"type switch\"\n\t\t\tcase *ast.CaseClause:\n\t\t\t\tkind = \"case\"\n\t\t\tcase *ast.CommClause:\n\t\t\t\tkind = \"comm\"\n\t\t\tcase *ast.ForStmt:\n\t\t\t\tkind = \"for\"\n\t\t\tcase *ast.RangeStmt:\n\t\t\t\tkind = \"range\"\n\t\t\tcase *ast.ForPhraseStmt:\n\t\t\t\tkind = \"for phrase\"\n\t\t\tcase *ast.LambdaExpr:\n\t\t\t\tkind = \"lambda\"\n\t\t\tcase *ast.LambdaExpr2:\n\t\t\t\tkind = \"lambda\"\n\t\t\tcase *ast.ForPhrase:\n\t\t\t\tkind = \"for phrase\"\n\t\t\tdefault:\n\t\t\t\tkind = fmt.Sprintf(\"<unknown node kind> %T\", node)\n\t\t\t}\n\n\t\t\t// look for matching scope description\n\t\t\tdesc := kind + \":\" + strings.Join(scope.Names(), \" \")\n\t\t\tfound := false\n\t\t\tfor _, d := range test.scopes {\n\t\t\t\tif desc == d {\n\t\t\t\t\tfound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !found {\n\t\t\t\tt.Errorf(\"package %s: no matching scope found for %s\", name, desc)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestAddress(t *testing.T) {\n\ttestInfo(t, `package address\n\ntype foo struct{ c int; p *int }\n\nfunc (f foo) ptr() *foo { return &f }\nfunc (f foo) clone() foo { return f }\n\ntype nested struct {\n\tf foo\n\ta [2]foo\n\ts []foo\n\tm map[int]foo\n}\n\nfunc _() {\n\tgetNested := func() nested { return nested{} }\n\tgetNestedPtr := func() *nested { return &nested{} }\n\n\t_ = getNested().f.c\n\t_ = getNested().a[0].c\n\t_ = getNested().s[0].c\n\t_ = getNested().m[0].c\n\t_ = getNested().f.ptr().c\n\t_ = getNested().f.clone().c\n\t_ = getNested().f.clone().ptr().c\n\n\t_ = getNestedPtr().f.c\n\t_ = getNestedPtr().a[0].c\n\t_ = getNestedPtr().s[0].c\n\t_ = getNestedPtr().m[0].c\n\t_ = getNestedPtr().f.ptr().c\n\t_ = getNestedPtr().f.clone().c\n\t_ = getNestedPtr().f.clone().ptr().c\n}\n`)\n}\n\nfunc TestAddress2(t *testing.T) {\n\ttestInfo(t, `package load\nimport \"os\"\n\nfunc _() { _ = os.Stdout }\nfunc _() {\n\tvar a int\n\t_ = a\n}\nfunc _() {\n\tvar p *int\n\t_ = *p\n}\nfunc _() {\n\tvar s []int\n\t_ = s[0]\n}\nfunc _() {\n\tvar s struct{f int}\n\t_ = s.f\n}\nfunc _() {\n\tvar a [10]int\n\t_ = a[0]\n}\nfunc _(x int) {\n\t_ = x\n}\nfunc _()(x int) {\n\t_ = x\n\treturn\n}\ntype T int\nfunc (x T) _() {\n\t_ = x\n}\n\nfunc _() {\n\tvar a, b int\n\t_ = a + b\n}\nfunc _() {\n\t_ = &[]int{1}\n}\nfunc _() {\n\t_ = func(){}\n}\nfunc f() { _ = f }\nfunc _() {\n\tvar m map[int]int\n\t_ = m[0]\n\t_, _ = m[0]\n}\nfunc _() {\n\tvar ch chan int\n\t_ = <-ch\n\t_, _ = <-ch\n}\n`)\n}\n\nfunc TestMixedPackage(t *testing.T) {\n\tfset := token.NewFileSet()\n\tpkg, _, _, err := parseMixedSource(xgomod.Default, fset, \"main.xgo\", `\nTest\nTest 100\nvar n N\nn.test\nn.test 100\n`, \"main.go\", `\npackage main\n\nfunc Test__0() {\n}\nfunc Test__1(n int) {\n}\ntype N struct {\n}\nfunc (p *N) Test__0() {\n}\nfunc (p *N) Test__1(n int) {\n}`, parser.Config{}, true)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tobj := pkg.Scope().Lookup(\"N\")\n\tnamed := obj.Type().(*types.Named)\n\tif named.NumMethods() == 2 {\n\t\tt.Fatal(\"found overload method failed\")\n\t}\n\text, ok := gogen.CheckFuncEx(named.Method(2).Type().(*types.Signature))\n\tif !ok {\n\t\tt.Fatal(\"checkFuncEx failed\")\n\t}\n\tm, ok := ext.(*gogen.TyOverloadMethod)\n\tif !ok || len(m.Methods) != 2 {\n\t\tt.Fatal(\"check TyOverloadMethod failed\")\n\t}\n}\n\nfunc TestGopOverloadUses(t *testing.T) {\n\ttestXGoInfo(t, `\nfunc MulInt(a, b int) int {\n\treturn a * b\n}\n\nfunc MulFloat(a, b float64) float64 {\n\treturn a * b\n}\n\nfunc Mul = (\n\tMulInt\n\tMulFloat\n\tfunc(x, y, z int) int {\n\t\treturn x * y * z\n\t}\n)\n\nMul 100,200\nMul 100,200,300\n`, ``, `== types ==\n000:  0: 0 | \"MulInt,MulFloat,\"  *ast.BasicLit                  | value   : untyped string = \"MulInt,MulFloat,\" | constant\n001:  2:18 | int                 *ast.Ident                     | type    : int | type\n002:  2:23 | int                 *ast.Ident                     | type    : int | type\n003:  3: 9 | a                   *ast.Ident                     | var     : int | variable\n004:  3: 9 | a * b               *ast.BinaryExpr                | value   : int | value\n005:  3:13 | b                   *ast.Ident                     | var     : int | variable\n006:  6:20 | float64             *ast.Ident                     | type    : float64 | type\n007:  6:29 | float64             *ast.Ident                     | type    : float64 | type\n008:  7: 9 | a                   *ast.Ident                     | var     : float64 | variable\n009:  7: 9 | a * b               *ast.BinaryExpr                | value   : float64 | value\n010:  7:13 | b                   *ast.Ident                     | var     : float64 | variable\n011: 13: 2 | func(x, y, z int) int *ast.FuncType                  | type    : func(x int, y int, z int) int | type\n012: 13: 2 | func(x, y, z int) int {\n\treturn x * y * z\n} *ast.FuncLit                   | value   : func(x int, y int, z int) int | value\n013: 13:15 | int                 *ast.Ident                     | type    : int | type\n014: 13:20 | int                 *ast.Ident                     | type    : int | type\n015: 14:10 | x                   *ast.Ident                     | var     : int | variable\n016: 14:10 | x * y               *ast.BinaryExpr                | value   : int | value\n017: 14:10 | x * y * z           *ast.BinaryExpr                | value   : int | value\n018: 14:14 | y                   *ast.Ident                     | var     : int | variable\n019: 14:18 | z                   *ast.Ident                     | var     : int | variable\n020: 18: 1 | Mul                 *ast.Ident                     | value   : func(a int, b int) int | value\n021: 18: 1 | Mul 100, 200        *ast.CallExpr                  | value   : int | value\n022: 18: 5 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n023: 18: 9 | 200                 *ast.BasicLit                  | value   : untyped int = 200 | constant\n024: 19: 1 | Mul                 *ast.Ident                     | value   : func(x int, y int, z int) int | value\n025: 19: 1 | Mul 100, 200, 300   *ast.CallExpr                  | value   : int | value\n026: 19: 5 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n027: 19: 9 | 200                 *ast.BasicLit                  | value   : untyped int = 200 | constant\n028: 19:13 | 300                 *ast.BasicLit                  | value   : untyped int = 300 | constant\n== defs ==\n000:  0: 0 | XGoo_Mul            | const main.XGoo_Mul untyped string\n001:  2: 6 | MulInt              | func main.MulInt(a int, b int) int\n002:  2:13 | a                   | var a int\n003:  2:16 | b                   | var b int\n004:  6: 6 | MulFloat            | func main.MulFloat(a float64, b float64) float64\n005:  6:15 | a                   | var a float64\n006:  6:18 | b                   | var b float64\n007: 10: 6 | Mul                 | func main.Mul(__xgo_overload_args__ interface{_()})\n008: 13: 2 | Mul__2              | func main.Mul__2(x int, y int, z int) int\n009: 13: 7 | x                   | var x int\n010: 13:10 | y                   | var y int\n011: 13:13 | z                   | var z int\n012: 18: 1 | main                | func main.main()\n== uses ==\n000:  2:18 | int                 | type int\n001:  2:23 | int                 | type int\n002:  3: 9 | a                   | var a int\n003:  3:13 | b                   | var b int\n004:  6:20 | float64             | type float64\n005:  6:29 | float64             | type float64\n006:  7: 9 | a                   | var a float64\n007:  7:13 | b                   | var b float64\n008: 11: 2 | MulInt              | func main.MulInt(a int, b int) int\n009: 12: 2 | MulFloat            | func main.MulFloat(a float64, b float64) float64\n010: 13:15 | int                 | type int\n011: 13:20 | int                 | type int\n012: 14:10 | x                   | var x int\n013: 14:14 | y                   | var y int\n014: 14:18 | z                   | var z int\n015: 18: 1 | Mul                 | func main.MulInt(a int, b int) int\n016: 19: 1 | Mul                 | func main.Mul__2(x int, y int, z int) int\n== overloads ==\n000: 18: 1 | Mul                 | func main.Mul(__xgo_overload_args__ interface{_()})\n001: 19: 1 | Mul                 | func main.Mul(__xgo_overload_args__ interface{_()})`)\n\n\ttestXGoInfo(t, `\ntype foo struct {\n}\n\nfunc (a *foo) mulInt(b int) *foo {\n\treturn a\n}\n\nfunc (a *foo) mulFoo(b *foo) *foo {\n\treturn a\n}\n\nfunc (foo).mul = (\n\t(foo).mulInt\n\t(foo).mulFoo\n)\n\nvar a, b *foo\nvar c = a.mul(100)\nvar d = a.mul(c)\n`, ``, `== types ==\n000:  0: 0 | \".mulInt,.mulFoo\"   *ast.BasicLit                  | value   : untyped string = \".mulInt,.mulFoo\" | constant\n001:  2:10 | struct {\n}          *ast.StructType                | type    : struct{} | type\n002:  5:10 | foo                 *ast.Ident                     | type    : main.foo | type\n003:  5:24 | int                 *ast.Ident                     | type    : int | type\n004:  5:29 | *foo                *ast.StarExpr                  | type    : *main.foo | type\n005:  5:30 | foo                 *ast.Ident                     | type    : main.foo | type\n006:  6: 9 | a                   *ast.Ident                     | var     : *main.foo | variable\n007:  9:10 | foo                 *ast.Ident                     | type    : main.foo | type\n008:  9:24 | *foo                *ast.StarExpr                  | type    : *main.foo | type\n009:  9:25 | foo                 *ast.Ident                     | type    : main.foo | type\n010:  9:30 | *foo                *ast.StarExpr                  | type    : *main.foo | type\n011:  9:31 | foo                 *ast.Ident                     | type    : main.foo | type\n012: 10: 9 | a                   *ast.Ident                     | var     : *main.foo | variable\n013: 18:10 | *foo                *ast.StarExpr                  | type    : *main.foo | type\n014: 18:11 | foo                 *ast.Ident                     | type    : main.foo | type\n015: 19: 9 | a                   *ast.Ident                     | var     : *main.foo | variable\n016: 19: 9 | a.mul               *ast.SelectorExpr              | value   : func(b int) *main.foo | value\n017: 19: 9 | a.mul(100)          *ast.CallExpr                  | value   : *main.foo | value\n018: 19:15 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n019: 20: 9 | a                   *ast.Ident                     | var     : *main.foo | variable\n020: 20: 9 | a.mul               *ast.SelectorExpr              | value   : func(b *main.foo) *main.foo | value\n021: 20: 9 | a.mul(c)            *ast.CallExpr                  | value   : *main.foo | value\n022: 20:15 | c                   *ast.Ident                     | var     : *main.foo | variable\n== defs ==\n000:  0: 0 | XGoo_foo_mul        | const main.XGoo_foo_mul untyped string\n001:  2: 6 | foo                 | type main.foo struct{}\n002:  5: 7 | a                   | var a *main.foo\n003:  5:15 | mulInt              | func (*main.foo).mulInt(b int) *main.foo\n004:  5:22 | b                   | var b int\n005:  9: 7 | a                   | var a *main.foo\n006:  9:15 | mulFoo              | func (*main.foo).mulFoo(b *main.foo) *main.foo\n007:  9:22 | b                   | var b *main.foo\n008: 13:12 | mul                 | func (main.foo).mul(__xgo_overload_args__ interface{_()})\n009: 18: 5 | a                   | var main.a *main.foo\n010: 18: 8 | b                   | var main.b *main.foo\n011: 19: 5 | c                   | var main.c *main.foo\n012: 20: 5 | d                   | var main.d *main.foo\n== uses ==\n000:  5:10 | foo                 | type main.foo struct{}\n001:  5:24 | int                 | type int\n002:  5:30 | foo                 | type main.foo struct{}\n003:  6: 9 | a                   | var a *main.foo\n004:  9:10 | foo                 | type main.foo struct{}\n005:  9:25 | foo                 | type main.foo struct{}\n006:  9:31 | foo                 | type main.foo struct{}\n007: 10: 9 | a                   | var a *main.foo\n008: 13: 7 | foo                 | type main.foo struct{}\n009: 14: 3 | foo                 | type main.foo struct{}\n010: 14: 8 | mulInt              | func (*main.foo).mulInt(b int) *main.foo\n011: 15: 3 | foo                 | type main.foo struct{}\n012: 15: 8 | mulFoo              | func (*main.foo).mulFoo(b *main.foo) *main.foo\n013: 18:11 | foo                 | type main.foo struct{}\n014: 19: 9 | a                   | var main.a *main.foo\n015: 19:11 | mul                 | func (*main.foo).mulInt(b int) *main.foo\n016: 20: 9 | a                   | var main.a *main.foo\n017: 20:11 | mul                 | func (*main.foo).mulFoo(b *main.foo) *main.foo\n018: 20:15 | c                   | var main.c *main.foo\n== overloads ==\n000: 19:11 | mul                 | func (main.foo).mul(__xgo_overload_args__ interface{_()})\n001: 20:11 | mul                 | func (main.foo).mul(__xgo_overload_args__ interface{_()})`)\n\n}\n\nfunc TestGopOverloadDecl(t *testing.T) {\n\ttestXGoInfo(t, `\nfunc addInt0() {\n}\n\nfunc addInt1(i int) {\n}\n\nfunc addInt2(i, j int) {\n}\n\nvar addInt3 = func(i, j, k int) {\n}\n\nfunc add = (\n\taddInt0\n\taddInt1\n\taddInt2\n\taddInt3\n\tfunc(a, b string) string {\n\t\treturn a + b\n\t}\n)\n\nfunc init() {\n\tadd 100, 200\n\tadd 100, 200, 300\n\tadd(\"hello\", \"world\")\n}\n`, ``, `== types ==\n000:  0: 0 | \"addInt0,addInt1,addInt2,addInt3,\" *ast.BasicLit                  | value   : untyped string = \"addInt0,addInt1,addInt2,addInt3,\" | constant\n001:  5:16 | int                 *ast.Ident                     | type    : int | type\n002:  8:19 | int                 *ast.Ident                     | type    : int | type\n003: 11:15 | func(i, j, k int)   *ast.FuncType                  | type    : func(i int, j int, k int) | type\n004: 11:15 | func(i, j, k int) {\n} *ast.FuncLit                   | value   : func(i int, j int, k int) | value\n005: 11:28 | int                 *ast.Ident                     | type    : int | type\n006: 19: 2 | func(a, b string) string *ast.FuncType                  | type    : func(a string, b string) string | type\n007: 19: 2 | func(a, b string) string {\n\treturn a + b\n} *ast.FuncLit                   | value   : func(a string, b string) string | value\n008: 19:12 | string              *ast.Ident                     | type    : string | type\n009: 19:20 | string              *ast.Ident                     | type    : string | type\n010: 20:10 | a                   *ast.Ident                     | var     : string | variable\n011: 20:10 | a + b               *ast.BinaryExpr                | value   : string | value\n012: 20:14 | b                   *ast.Ident                     | var     : string | variable\n013: 25: 2 | add                 *ast.Ident                     | value   : func(i int, j int) | value\n014: 25: 2 | add 100, 200        *ast.CallExpr                  | void    : () | no value\n015: 25: 6 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n016: 25:11 | 200                 *ast.BasicLit                  | value   : untyped int = 200 | constant\n017: 26: 2 | add                 *ast.Ident                     | var     : func(i int, j int, k int) | variable\n018: 26: 2 | add 100, 200, 300   *ast.CallExpr                  | void    : () | no value\n019: 26: 6 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n020: 26:11 | 200                 *ast.BasicLit                  | value   : untyped int = 200 | constant\n021: 26:16 | 300                 *ast.BasicLit                  | value   : untyped int = 300 | constant\n022: 27: 2 | add                 *ast.Ident                     | value   : func(a string, b string) string | value\n023: 27: 2 | add(\"hello\", \"world\") *ast.CallExpr                  | value   : string | value\n024: 27: 6 | \"hello\"             *ast.BasicLit                  | value   : untyped string = \"hello\" | constant\n025: 27:15 | \"world\"             *ast.BasicLit                  | value   : untyped string = \"world\" | constant\n== defs ==\n000:  0: 0 | XGoo_add            | const main.XGoo_add untyped string\n001:  2: 6 | addInt0             | func main.addInt0()\n002:  5: 6 | addInt1             | func main.addInt1(i int)\n003:  5:14 | i                   | var i int\n004:  8: 6 | addInt2             | func main.addInt2(i int, j int)\n005:  8:14 | i                   | var i int\n006:  8:17 | j                   | var j int\n007: 11: 5 | addInt3             | var main.addInt3 func(i int, j int, k int)\n008: 11:20 | i                   | var i int\n009: 11:23 | j                   | var j int\n010: 11:26 | k                   | var k int\n011: 14: 6 | add                 | func main.add(__xgo_overload_args__ interface{_()})\n012: 19: 2 | add__4              | func main.add__4(a string, b string) string\n013: 19: 7 | a                   | var a string\n014: 19:10 | b                   | var b string\n015: 24: 6 | init                | func main.init()\n== uses ==\n000:  5:16 | int                 | type int\n001:  8:19 | int                 | type int\n002: 11:28 | int                 | type int\n003: 15: 2 | addInt0             | func main.addInt0()\n004: 16: 2 | addInt1             | func main.addInt1(i int)\n005: 17: 2 | addInt2             | func main.addInt2(i int, j int)\n006: 18: 2 | addInt3             | var main.addInt3 func(i int, j int, k int)\n007: 19:12 | string              | type string\n008: 19:20 | string              | type string\n009: 20:10 | a                   | var a string\n010: 20:14 | b                   | var b string\n011: 25: 2 | add                 | func main.addInt2(i int, j int)\n012: 26: 2 | add                 | var main.addInt3 func(i int, j int, k int)\n013: 27: 2 | add                 | func main.add__4(a string, b string) string\n== overloads ==\n000: 25: 2 | add                 | func main.add(__xgo_overload_args__ interface{_()})\n001: 26: 2 | add                 | func main.add(__xgo_overload_args__ interface{_()})\n002: 27: 2 | add                 | func main.add(__xgo_overload_args__ interface{_()})`)\n\n\ttestXGoInfo(t, `\nfunc add = (\n\tfunc(a, b int) int {\n\t\treturn a + b\n\t}\n\tfunc(a, b string) string {\n\t\treturn a + b\n\t}\n)\n\nfunc init() {\n\tadd 100, 200\n\tadd \"hello\", \"world\"\n}\n`, ``, `== types ==\n000:  3: 2 | func(a, b int) int  *ast.FuncType                  | type    : func(a int, b int) int | type\n001:  3: 2 | func(a, b int) int {\n\treturn a + b\n} *ast.FuncLit                   | value   : func(a int, b int) int | value\n002:  3:12 | int                 *ast.Ident                     | type    : int | type\n003:  3:17 | int                 *ast.Ident                     | type    : int | type\n004:  4:10 | a                   *ast.Ident                     | var     : int | variable\n005:  4:10 | a + b               *ast.BinaryExpr                | value   : int | value\n006:  4:14 | b                   *ast.Ident                     | var     : int | variable\n007:  6: 2 | func(a, b string) string *ast.FuncType                  | type    : func(a string, b string) string | type\n008:  6: 2 | func(a, b string) string {\n\treturn a + b\n} *ast.FuncLit                   | value   : func(a string, b string) string | value\n009:  6:12 | string              *ast.Ident                     | type    : string | type\n010:  6:20 | string              *ast.Ident                     | type    : string | type\n011:  7:10 | a                   *ast.Ident                     | var     : string | variable\n012:  7:10 | a + b               *ast.BinaryExpr                | value   : string | value\n013:  7:14 | b                   *ast.Ident                     | var     : string | variable\n014: 12: 2 | add                 *ast.Ident                     | value   : func(a int, b int) int | value\n015: 12: 2 | add 100, 200        *ast.CallExpr                  | value   : int | value\n016: 12: 6 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant\n017: 12:11 | 200                 *ast.BasicLit                  | value   : untyped int = 200 | constant\n018: 13: 2 | add                 *ast.Ident                     | value   : func(a string, b string) string | value\n019: 13: 2 | add \"hello\", \"world\" *ast.CallExpr                  | value   : string | value\n020: 13: 6 | \"hello\"             *ast.BasicLit                  | value   : untyped string = \"hello\" | constant\n021: 13:15 | \"world\"             *ast.BasicLit                  | value   : untyped string = \"world\" | constant\n== defs ==\n000:  2: 6 | add                 | func main.add(__xgo_overload_args__ interface{_()})\n001:  3: 2 | add__0              | func main.add__0(a int, b int) int\n002:  3: 7 | a                   | var a int\n003:  3:10 | b                   | var b int\n004:  6: 2 | add__1              | func main.add__1(a string, b string) string\n005:  6: 7 | a                   | var a string\n006:  6:10 | b                   | var b string\n007: 11: 6 | init                | func main.init()\n== uses ==\n000:  3:12 | int                 | type int\n001:  3:17 | int                 | type int\n002:  4:10 | a                   | var a int\n003:  4:14 | b                   | var b int\n004:  6:12 | string              | type string\n005:  6:20 | string              | type string\n006:  7:10 | a                   | var a string\n007:  7:14 | b                   | var b string\n008: 12: 2 | add                 | func main.add__0(a int, b int) int\n009: 13: 2 | add                 | func main.add__1(a string, b string) string\n== overloads ==\n000: 12: 2 | add                 | func main.add(__xgo_overload_args__ interface{_()})\n001: 13: 2 | add                 | func main.add(__xgo_overload_args__ interface{_()})`)\n}\n\nfunc TestGoxOverloadInfo(t *testing.T) {\n\ttestSpxInfo(t, \"Rect.gox\", `\nfunc addInt(a, b int) int {\n\treturn a + b\n}\n\nfunc addString(a, b string) string {\n\treturn a + b\n}\n\nfunc add = (\n\taddInt\n\tfunc(a, b float64) float64 {\n\t\treturn a + b\n\t}\n\taddString\n)\n`, `== types ==\n000:  0: 0 | \".addInt,,.addString\" *ast.BasicLit                  | value   : untyped string = \".addInt,,.addString\" | constant\n001:  1: 1 | Rect                *ast.Ident                     | type    : main.Rect | type\n002:  2:18 | int                 *ast.Ident                     | type    : int | type\n003:  2:23 | int                 *ast.Ident                     | type    : int | type\n004:  3: 9 | a                   *ast.Ident                     | var     : int | variable\n005:  3: 9 | a + b               *ast.BinaryExpr                | value   : int | value\n006:  3:13 | b                   *ast.Ident                     | var     : int | variable\n007:  6:21 | string              *ast.Ident                     | type    : string | type\n008:  6:29 | string              *ast.Ident                     | type    : string | type\n009:  7: 9 | a                   *ast.Ident                     | var     : string | variable\n010:  7: 9 | a + b               *ast.BinaryExpr                | value   : string | value\n011:  7:13 | b                   *ast.Ident                     | var     : string | variable\n012: 12:12 | float64             *ast.Ident                     | type    : float64 | type\n013: 12:21 | float64             *ast.Ident                     | type    : float64 | type\n014: 13:10 | a                   *ast.Ident                     | var     : float64 | variable\n015: 13:10 | a + b               *ast.BinaryExpr                | value   : float64 | value\n016: 13:14 | b                   *ast.Ident                     | var     : float64 | variable\n== defs ==\n000:  0: 0 | XGoo_Rect_add       | const main.XGoo_Rect_add untyped string\n001:  1: 1 | this                | var this *main.Rect\n002:  2: 6 | addInt              | func (*main.Rect).addInt(a int, b int) int\n003:  2:13 | a                   | var a int\n004:  2:16 | b                   | var b int\n005:  6: 6 | addString           | func (*main.Rect).addString(a string, b string) string\n006:  6:16 | a                   | var a string\n007:  6:19 | b                   | var b string\n008: 10: 6 | add                 | func (main.Rect).add(__xgo_overload_args__ interface{_()})\n009: 12: 2 | add__1              | func (*main.Rect).add__1(a float64, b float64) float64\n010: 12: 7 | a                   | var a float64\n011: 12:10 | b                   | var b float64\n== uses ==\n000:  1: 1 | Rect                | type main.Rect struct{}\n001:  2:18 | int                 | type int\n002:  2:23 | int                 | type int\n003:  3: 9 | a                   | var a int\n004:  3:13 | b                   | var b int\n005:  6:21 | string              | type string\n006:  6:29 | string              | type string\n007:  7: 9 | a                   | var a string\n008:  7:13 | b                   | var b string\n009: 11: 2 | addInt              | func (*main.Rect).addInt(a int, b int) int\n010: 12:12 | float64             | type float64\n011: 12:21 | float64             | type float64\n012: 13:10 | a                   | var a float64\n013: 13:14 | b                   | var b float64\n014: 15: 2 | addString           | func (*main.Rect).addString(a string, b string) string`)\n}\n\nfunc TestTypesAlias(t *testing.T) {\n\ttestInfo(t, `package main\nimport \"fmt\"\n\ntype T = int\nvar v T\nfunc demo(v T) {\n\tfmt.Println(v)\n}\nfunc main() {\n\tdemo(100)\n}\n`)\n}\n"
  },
  {
    "path": "x/typesutil/internal/typesutil/types.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage typesutil\n\nimport (\n\t\"go/token\"\n\t\"go/types\"\n\t\"unsafe\"\n)\n\n// -----------------------------------------------------------------------------\n\n// A Scope maintains a set of objects and links to its containing\n// (parent) and contained (children) scopes. Objects may be inserted\n// and looked up by name. The zero value for Scope is a ready-to-use\n// empty scope.\ntype Scope struct {\n\tparent   *Scope\n\tchildren []*Scope\n\tnumber   int                     // parent.children[number-1] is this scope; 0 if there is no parent\n\telems    map[string]types.Object // lazily allocated\n\tpos, end token.Pos               // scope extent; may be invalid\n\tcomment  string                  // for debugging only\n\tisFunc   bool                    // set if this is a function scope (internal use only)\n}\n\n// ScopeDelete deletes an object from specified scope by its name.\nfunc ScopeDelete(s *types.Scope, name string) types.Object {\n\telems := (*Scope)(unsafe.Pointer(s)).elems\n\tif o, ok := elems[name]; ok {\n\t\tdelete(elems, name)\n\t\treturn o\n\t}\n\treturn nil\n}\n\n// -----------------------------------------------------------------------------\n\n// An Error describes a type-checking error; it implements the error interface.\n// A \"soft\" error is an error that still permits a valid interpretation of a\n// package (such as \"unused variable\"); \"hard\" errors may lead to unpredictable\n// behavior if ignored.\ntype Error struct {\n\tFset *token.FileSet // file set for interpretation of Pos\n\tPos  token.Pos      // error position\n\tMsg  string         // error message\n\tSoft bool           // if set, error is \"soft\"\n\n\t// go116code is a future API, unexported as the set of error codes is large\n\t// and likely to change significantly during experimentation. Tools wishing\n\t// to preview this feature may read go116code using reflection (see\n\t// errorcodes_test.go), but beware that there is no guarantee of future\n\t// compatibility.\n\tgo116code  int\n\tgo116start token.Pos\n\tgo116end   token.Pos\n}\n\nfunc SetErrorGo116(ret *types.Error, code int, start, end token.Pos) {\n\te := (*Error)(unsafe.Pointer(ret))\n\te.go116code = code\n\te.go116start = start\n\te.go116end = end\n}\n\n// GetErrorGo116 extracts the go116 fields from types.Error\nfunc GetErrorGo116(err *types.Error) (code int, start, end token.Pos, ok bool) {\n\tdefer func() {\n\t\tif recover() != nil {\n\t\t\tcode = 0\n\t\t\tstart = token.NoPos\n\t\t\tend = token.NoPos\n\t\t\tok = false\n\t\t}\n\t}()\n\n\te := (*Error)(unsafe.Pointer(err))\n\tcode = e.go116code\n\tstart = e.go116start\n\tend = e.go116end\n\tok = true\n\treturn\n}\n\n// -----------------------------------------------------------------------------\n\nfunc init() {\n\tif unsafe.Sizeof(Scope{}) != unsafe.Sizeof(types.Scope{}) {\n\t\tpanic(\"unexpected sizeof types.Scope\")\n\t}\n\tif unsafe.Sizeof(Error{}) != unsafe.Sizeof(types.Error{}) {\n\t\tpanic(\"unexpected sizeof types.Error\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/typesutil/typeparams/typeparams.go",
    "content": "// Copyright 2021 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage typeparams\n\nimport (\n\t\"github.com/goplus/xgo/ast\"\n\t\"github.com/goplus/xgo/token\"\n)\n\nfunc PackIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token.Pos) ast.Expr {\n\tswitch len(exprs) {\n\tcase 0:\n\t\tpanic(\"internal error: PackIndexExpr with empty expr slice\")\n\tcase 1:\n\t\treturn &ast.IndexExpr{\n\t\t\tX:      x,\n\t\t\tLbrack: lbrack,\n\t\t\tIndex:  exprs[0],\n\t\t\tRbrack: rbrack,\n\t\t}\n\tdefault:\n\t\treturn &ast.IndexListExpr{\n\t\t\tX:       x,\n\t\t\tLbrack:  lbrack,\n\t\t\tIndices: exprs,\n\t\t\tRbrack:  rbrack,\n\t\t}\n\t}\n}\n\n// IndexExpr wraps an ast.IndexExpr or ast.IndexListExpr.\n//\n// Orig holds the original ast.Expr from which this IndexExpr was derived.\ntype IndexExpr struct {\n\tOrig ast.Expr // the wrapped expr, which may be distinct from the IndexListExpr below.\n\t*ast.IndexListExpr\n}\n\nfunc UnpackIndexExpr(n ast.Node) *IndexExpr {\n\tswitch e := n.(type) {\n\tcase *ast.IndexExpr:\n\t\treturn &IndexExpr{e, &ast.IndexListExpr{\n\t\t\tX:       e.X,\n\t\t\tLbrack:  e.Lbrack,\n\t\t\tIndices: []ast.Expr{e.Index},\n\t\t\tRbrack:  e.Rbrack,\n\t\t}}\n\tcase *ast.IndexListExpr:\n\t\treturn &IndexExpr{e, e}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/watcher/changes.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage watcher\n\nimport (\n\t\"io/fs\"\n\t\"log\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/goplus/mod/xgomod\"\n)\n\n// -----------------------------------------------------------------------------------------\n\ntype module struct {\n\texts []string\n}\n\nfunc (p *module) ignore(fname string) bool {\n\text := path.Ext(fname)\n\tfor _, v := range p.exts {\n\t\tif ext == v {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// -----------------------------------------------------------------------------------------\n\ntype none struct{}\n\ntype Changes struct {\n\tchanged map[string]none\n\tmods    map[string]*module\n\tmutex   sync.Mutex\n\tcond    sync.Cond\n\n\troot string\n}\n\nfunc NewChanges(root string) *Changes {\n\tchanged := make(map[string]none)\n\tmods := make(map[string]*module)\n\troot, _ = filepath.Abs(root)\n\tc := &Changes{changed: changed, mods: mods, root: root + \"/\"}\n\tc.cond.L = &c.mutex\n\treturn c\n}\n\nfunc (p *Changes) doLookupMod(name string) *module {\n\tmod, ok := p.mods[name]\n\tif !ok {\n\t\tmod = new(module)\n\t\tmod.exts = make([]string, 0, 8)\n\t\tm, e := xgomod.Load(p.root + name)\n\t\tif e == nil {\n\t\t\tm.ImportClasses(func(c *xgomod.Project) {\n\t\t\t\tmod.exts = append(mod.exts, c.Ext)\n\t\t\t\tfor _, w := range c.Works {\n\t\t\t\t\tif w.Ext != c.Ext {\n\t\t\t\t\t\tmod.exts = append(mod.exts, w.Ext)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\tmod.exts = append(mod.exts, \".xgo\", \".gop\", \".go\", \".gox\", \".gmx\")\n\t\tp.mods[name] = mod\n\t\tif debugMod {\n\t\t\tlog.Println(\"Mod:\", name, \"Exts:\", mod.exts)\n\t\t}\n\t}\n\treturn mod\n}\n\nfunc (p *Changes) lookupMod(name string) *module {\n\tname = strings.TrimSuffix(name, \"/\")\n\tp.mutex.Lock()\n\tmod := p.doLookupMod(name)\n\tp.mutex.Unlock()\n\treturn mod\n}\n\nfunc (p *Changes) deleteMod(dir string) {\n\tp.mutex.Lock()\n\tdelete(p.mods, dir)\n\tp.mutex.Unlock()\n}\n\nfunc (p *Changes) Fetch(fullPath bool) (dir string) {\n\tp.mutex.Lock()\n\tfor len(p.changed) == 0 {\n\t\tp.cond.Wait()\n\t}\n\tfor dir = range p.changed {\n\t\tdelete(p.changed, dir)\n\t\tbreak\n\t}\n\tp.mutex.Unlock()\n\tif fullPath {\n\t\tdir = p.root + dir\n\t}\n\treturn\n}\n\nfunc (p *Changes) Ignore(name string, isDir bool) bool {\n\tdir, fname := path.Split(name)\n\tif strings.HasPrefix(fname, \"_\") {\n\t\treturn true\n\t}\n\treturn !isDir && (isHiddenTemp(fname) || isAutogen(fname) || p.lookupMod(dir).ignore(fname))\n}\n\nfunc (p *Changes) FileChanged(name string) {\n\tdir := path.Dir(name)\n\tp.mutex.Lock()\n\tn := len(p.changed)\n\tp.changed[dir] = none{}\n\tp.mutex.Unlock()\n\tif n == 0 {\n\t\tp.cond.Broadcast()\n\t}\n}\n\nfunc (p *Changes) EntryDeleted(name string, isDir bool) {\n\tif isDir {\n\t\tp.deleteMod(name)\n\t} else {\n\t\tp.FileChanged(name)\n\t}\n}\n\nfunc (p *Changes) DirAdded(name string) {\n\tdir := p.root + name\n\tfilepath.WalkDir(dir, func(entry string, d fs.DirEntry, err error) error {\n\t\tif err != nil || d.IsDir() {\n\t\t\treturn err\n\t\t}\n\t\tentry, _ = filepath.Rel(dir, entry)\n\t\tentry = filepath.ToSlash(entry)\n\t\tif !p.Ignore(entry, false) {\n\t\t\tp.FileChanged(entry)\n\t\t}\n\t\treturn nil\n\t})\n}\n\n// pattern: xgo_autogen*.go\nfunc isAutogen(fname string) bool {\n\treturn strings.HasPrefix(fname, \"xgo_autogen\") && strings.HasSuffix(fname, \".go\")\n}\n\n// pattern: .* or *~\nfunc isHiddenTemp(fname string) bool {\n\treturn strings.HasPrefix(fname, \".\") || strings.HasSuffix(fname, \"~\")\n}\n\n// -----------------------------------------------------------------------------------------\n"
  },
  {
    "path": "x/watcher/watch.go",
    "content": "/*\n * Copyright (c) 2023 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Package watcher monitors code changes in an XGo workspace.\npackage watcher\n\nimport (\n\t\"github.com/goplus/xgo/x/fsnotify\"\n)\n\nvar (\n\tdebugMod bool\n)\n\nconst (\n\tDbgFlagMod = 1 << iota\n\tDbgFlagAll = DbgFlagMod\n)\n\nfunc SetDebug(dbgFlags int) {\n\tdebugMod = (dbgFlags & DbgFlagMod) != 0\n}\n\n// -----------------------------------------------------------------------------------------\n\ntype Runner struct {\n\tw fsnotify.Watcher\n\tc *Changes\n}\n\nfunc New(root string) Runner {\n\tw := fsnotify.New()\n\tc := NewChanges(root)\n\treturn Runner{w: w, c: c}\n}\n\nfunc (p Runner) Fetch(fullPath bool) (dir string) {\n\treturn p.c.Fetch(fullPath)\n}\n\nfunc (p Runner) Run() error {\n\troot := p.c.root\n\troot = root[:len(root)-1]\n\treturn p.w.Run(root, p.c, p.c.Ignore)\n}\n\nfunc (p *Runner) Close() error {\n\treturn p.w.Close()\n}\n\n// -----------------------------------------------------------------------------------------\n"
  },
  {
    "path": "x/xgoenv/env.go",
    "content": "/*\n * Copyright (c) 2022 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xgoenv\n\nimport (\n\tmodenv \"github.com/goplus/mod/env\"\n\t\"github.com/goplus/xgo/env\"\n)\n\nfunc Get() *modenv.XGo {\n\treturn &modenv.XGo{\n\t\tVersion:   env.Version(),\n\t\tBuildDate: env.BuildDate(),\n\t\tRoot:      env.XGOROOT(),\n\t}\n}\n"
  },
  {
    "path": "x/xgoprojs/proj.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xgoprojs\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"syscall\"\n)\n\n// -----------------------------------------------------------------------------\n\n// Proj is the interface for a project\ntype Proj = interface {\n\tprojObj()\n}\n\n// FilesProj represents a project with files\ntype FilesProj struct {\n\tFiles []string\n}\n\n// PkgPathProj represents a project with a package path\ntype PkgPathProj struct {\n\tPath string\n}\n\n// DirProj represents a project with a directory\ntype DirProj struct {\n\tDir string\n}\n\nfunc (p *FilesProj) projObj()   {}\nfunc (p *PkgPathProj) projObj() {}\nfunc (p *DirProj) projObj()     {}\n\n// -----------------------------------------------------------------------------\n\n// ParseOne parses the first argument and returns a Proj object\n// If the first argument is a file, it continues to parse subsequent arguments.\nfunc ParseOne(args ...string) (proj Proj, next []string, err error) {\n\tif len(args) == 0 {\n\t\treturn nil, nil, syscall.ENOENT\n\t}\n\targ := args[0]\n\tif isFile(arg) {\n\t\tn := 1\n\t\tfor n < len(args) && isFile(args[n]) {\n\t\t\tn++\n\t\t}\n\t\treturn &FilesProj{Files: args[:n]}, args[n:], nil\n\t} else if isLocal(arg) {\n\t\treturn &DirProj{Dir: arg}, args[1:], nil\n\t}\n\treturn &PkgPathProj{Path: arg}, args[1:], nil\n}\n\nfunc isFile(fname string) bool {\n\tn := len(filepath.Ext(fname))\n\tif n == 0 {\n\t\treturn false\n\t}\n\tif info, err := os.Stat(fname); err != nil || info.IsDir() {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc isLocal(ns string) bool {\n\tif len(ns) > 0 {\n\t\tswitch c := ns[0]; c {\n\t\tcase '/', '\\\\', '.':\n\t\t\treturn true\n\t\tdefault:\n\t\t\treturn len(ns) >= 2 && ns[1] == ':' && ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z')\n\t\t}\n\t}\n\treturn false\n}\n\n// -----------------------------------------------------------------------------\n\n// ParseAll parses all arguments and returns a slice of Proj objects\nfunc ParseAll(args ...string) (projs []Proj, err error) {\n\tvar hasFiles, hasNotFiles bool\n\tfor {\n\t\tproj, next, e := ParseOne(args...)\n\t\tif e != nil {\n\t\t\tif hasFiles && hasNotFiles {\n\t\t\t\treturn nil, ErrMixedFilesProj\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif _, ok := proj.(*FilesProj); ok {\n\t\t\thasFiles = true\n\t\t} else {\n\t\t\thasNotFiles = true\n\t\t}\n\t\tprojs = append(projs, proj)\n\t\targs = next\n\t}\n}\n\nvar (\n\t// ErrMixedFilesProj is returned when a project contains both files and non-files\n\tErrMixedFilesProj = errors.New(\"mixed files project\")\n)\n\n// -----------------------------------------------------------------------------\n"
  },
  {
    "path": "x/xgoprojs/proj_test.go",
    "content": "/*\n * Copyright (c) 2021 The XGo Authors (xgo.dev). All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage xgoprojs\n\nimport (\n\t\"testing\"\n)\n\n// -----------------------------------------------------------------------------\n\nfunc TestIsLocal(t *testing.T) {\n\tif !isLocal(\".\") || !isLocal(\"/\") {\n\t\tt.Fatal(`isLocal(\".\") || isLocal(\"/\")`)\n\t}\n\tif !isLocal(\"c:/foo\") {\n\t\tt.Fatal(`isLocal(\"c:/foo\")`)\n\t}\n\tif !isLocal(\"C:/foo\") {\n\t\tt.Fatal(`isLocal(\"C:/foo\")`)\n\t}\n\tif isLocal(\"\") {\n\t\tt.Fatal(`isLocal(\"\")`)\n\t}\n}\n\nfunc TestParseOne(t *testing.T) {\n\tproj, next, err := ParseOne(\"proj.go\", \"proj_test.go\", \"abc\")\n\tif err != nil || len(next) != 1 || next[0] != \"abc\" {\n\t\tt.Fatal(\"ParseOne failed:\", proj, next, err)\n\t}\n}\n\nfunc TestParseAll_wildcard1(t *testing.T) {\n\tprojs, err := ParseAll(\"proj_test.go\")\n\tif err != nil || len(projs) != 1 {\n\t\tt.Fatal(\"ParseAll failed:\", projs, err)\n\t}\n\tif proj, ok := projs[0].(*FilesProj); !ok || len(proj.Files) != 1 || proj.Files[0] != \"proj_test.go\" {\n\t\tt.Fatal(\"ParseAll failed:\", projs)\n\t}\n}\n\nfunc TestParseAll_wildcard2(t *testing.T) {\n\tprojs, err := ParseAll(\"../xgoenv/env.go\")\n\tif err != nil || len(projs) != 1 {\n\t\tt.Fatal(\"ParseAll failed:\", projs, err)\n\t}\n\tif proj, ok := projs[0].(*FilesProj); !ok || len(proj.Files) != 1 || proj.Files[0] != \"../xgoenv/env.go\" {\n\t\tt.Fatal(\"ParseAll failed:\", projs)\n\t}\n}\n\nfunc TestParseAll_multiFiles(t *testing.T) {\n\tprojs, err := ParseAll(\"proj.go\", \"proj_test.go\")\n\tif err != nil || len(projs) != 1 {\n\t\tt.Fatal(\"ParseAll failed:\", projs, err)\n\t}\n\tif proj, ok := projs[0].(*FilesProj); !ok || len(proj.Files) != 2 || proj.Files[0] != \"proj.go\" {\n\t\tt.Fatal(\"ParseAll failed:\", proj)\n\t}\n\tprojs[0].projObj()\n}\n\nfunc TestParseAll_multiProjs(t *testing.T) {\n\tprojs, err := ParseAll(\"a/...\", \"./a/...\", \"/a\")\n\tif err != nil || len(projs) != 3 {\n\t\tt.Fatal(\"ParseAll failed:\", projs, err)\n\t}\n\tif proj, ok := projs[0].(*PkgPathProj); !ok || proj.Path != \"a/...\" {\n\t\tt.Fatal(\"ParseAll failed:\", proj)\n\t}\n\tif proj, ok := projs[1].(*DirProj); !ok || proj.Dir != \"./a/...\" {\n\t\tt.Fatal(\"ParseAll failed:\", proj)\n\t}\n\tif proj, ok := projs[2].(*DirProj); !ok || proj.Dir != \"/a\" {\n\t\tt.Fatal(\"ParseAll failed:\", proj)\n\t}\n\tfor _, proj := range projs {\n\t\tproj.projObj()\n\t}\n}\n\nfunc TestParseAllErr(t *testing.T) {\n\t_, err := ParseAll(\"a/...\", \"./a/...\", \"/a\", \"proj_test.go\")\n\tif err != ErrMixedFilesProj {\n\t\tt.Fatal(\"ParseAll:\", err)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n"
  }
]