├── .codecov.yml ├── .editorconfig ├── .github └── workflows │ ├── default.yml │ ├── deploy-pages.yml │ ├── pr_info_intro.yml │ ├── pr_info_post.yml │ └── pr_info_untrusted.yml ├── .gitignore ├── .gitmodules ├── DGrammar ├── .gitignore ├── begin.txt ├── end.txt └── generate_html_from_libdparse.sh ├── LICENSE_1_0.txt ├── README.md ├── ci ├── summary_comment.sh └── summary_comment_diff.sh ├── doc-src ├── index.ddoc └── macros.ddoc ├── dub.json ├── img ├── logo.kra └── logo.png ├── macros.ddoc ├── meson.build ├── src ├── dparse │ ├── ast.d │ ├── astprinter.d │ ├── entities.d │ ├── formatter.d │ ├── lexer.d │ ├── parser.d │ ├── rollback_allocator.d │ ├── stack_buffer.d │ ├── strings.d │ └── trivia.d └── std │ └── experimental │ └── lexer.d └── test ├── ast_checks ├── assert_args.d ├── assert_args.txt ├── errorRecovery.d ├── errorRecovery.txt ├── file1.d ├── file1.txt ├── foreach.d ├── foreach.txt ├── interpolated_string.d ├── interpolated_string.txt ├── issue428.d ├── issue428.txt ├── issue471.d ├── issue471.txt ├── moduleDec4.d ├── moduleDec4.txt ├── named_arguments.d ├── named_arguments.txt ├── oneLineFunctionDoc.d ├── oneLineFunctionDoc.txt ├── scope_exit.d ├── scope_exit.txt ├── shortenedFunction.d ├── shortenedFunction.txt ├── switch_condition.d ├── switch_condition.txt ├── throw_expressions.d ├── throw_expressions.txt ├── while_condition.d └── while_condition.txt ├── fail_files ├── aliases.d ├── asm-gcc.d ├── bad_asm.d ├── bad_enum_1.d ├── bad_enum_2.d ├── bad_enum_3.d ├── bad_enums.d ├── bad_parens.d ├── better_block_leave.d ├── contracts.d ├── dcd_tricks.d ├── ifConditions.d ├── incompleteStatement198_0.d ├── incompleteStatement198_1.d ├── incompleteStatement198_2.d ├── incompleteStatement198_3.d ├── incompleteStatement198_4.d ├── incompleteStatement198_5.d ├── incompleteStatement198_6.d ├── incompleteStatement198_7.d ├── issue0078.d ├── issue0158.d ├── issue0171.d ├── issue0176.d ├── issue0179.d ├── issue0227.d ├── issue095.d ├── issue245.d ├── issue459.d ├── killer.d ├── killer2.d ├── misc_coverage.d ├── pragma_exp_bound.d ├── shortenedMethod.d ├── sillyparse.d ├── throwattribute.d ├── varargs-attributes.d └── with_better_dcd.d ├── fuzzer.d ├── fuzzer.sh ├── pass_files ├── ae.d ├── alias_assign.d ├── aliases.d ├── asm-gcc.d ├── asm.d ├── attributes.d ├── auto_declarations.d ├── bitfields.d ├── body_as_ident.d ├── casts.d ├── classes.d ├── compiler_2_104_0.d ├── constructors_destructors.d ├── crazy1.d ├── declarations.d ├── dip1000.d ├── dip25.d ├── do_body.d ├── enums.d ├── exceptions.d ├── expressions.d ├── function_aliases.d ├── function_declarations.d ├── function_literals.d ├── helloworld.d ├── ifConditions.d ├── imports.d ├── innerclass.d ├── interfaces.d ├── issue00114.d ├── issue0042.d ├── issue0047.d ├── issue0067.d ├── issue0070.d ├── issue0095.d ├── issue0106.d ├── issue0156.d ├── issue0158.d ├── issue0291.d ├── issue0413.d ├── lexical.d ├── linkageAttributes.d ├── misc_coverage.d ├── mixin_types.d ├── moduleDec1.d ├── moduleDec2.d ├── moduleDec3.d ├── moduleDec4.d ├── much_recursion.d ├── named_arguments.d ├── pragmas.d ├── ptrntab.d ├── shortenedMethods.d ├── single_line_with_decl.d ├── statements.d ├── structs.d ├── switch_condition_assignment.d ├── templates.d ├── test8898.d ├── throwattribute.d ├── unions.d ├── varargs-attributes.d └── while_condition_assignment.d ├── run_tests.sh ├── tester.d └── watcher.sh /.codecov.yml: -------------------------------------------------------------------------------- 1 | # Documentation: https://github.com/codecov/support/wiki/codecov.yml 2 | codecov: 3 | coverage: 4 | precision: 3 5 | round: down 6 | 7 | status: 8 | # Learn more at https://codecov.io/docs#yaml_default_commit_status 9 | project: true 10 | patch: true 11 | changes: false 12 | 13 | comment: false 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | indent_size = 4 5 | tab_width = 8 6 | trim_trailing_whitespace = true 7 | indent_style = space 8 | -------------------------------------------------------------------------------- /.github/workflows/default.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | pull_request: 5 | 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | main: 12 | name: Run all tests 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | include: 17 | - compiler: dmd-latest 18 | dmd: dmd 19 | 20 | - compiler: ldc-latest 21 | dmd: ldmd2 22 | 23 | - compiler: gdc-latest 24 | dmd: gdmd 25 | 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | # Clone repo + submodules 30 | - name: Checkout repo 31 | uses: actions/checkout@v2 32 | with: 33 | submodules: 'recursive' 34 | 35 | # Update packages and install test dependencies 36 | - name: Install required dependencies 37 | run: | 38 | sudo apt-get update 39 | sudo apt-get install libxml2-utils -y 40 | 41 | # Install the host compiler (DMD or LDC) 42 | - name: Install ${{ matrix.compiler }} 43 | if: ${{ matrix.compiler != 'gdc-latest' }} 44 | uses: dlang-community/setup-dlang@v1 45 | with: 46 | compiler: ${{ matrix.compiler }} 47 | 48 | # GDC not yet supported by setup-dlang 49 | - name: Install GDC 50 | if: ${{ matrix.compiler == 'gdc-latest' }} 51 | run: | 52 | sudo apt-get install gdc-12 gdmd -y 53 | # hack: 54 | sudo rm /usr/bin/gdc 55 | sudo ln -s /usr/bin/gdc-12 /usr/bin/gdc 56 | gdc --version 57 | 58 | # Execute all other tests (parsing, XPath, ...) 59 | - name: Run run_tests.sh 60 | env: 61 | DMD: ${{ matrix.dmd }} 62 | shell: bash 63 | working-directory: test 64 | run: source run_tests.sh 65 | 66 | # Upload coverage reports to CodeCov 67 | - uses: codecov/codecov-action@v2 68 | if: ${{ matrix.compiler == 'ldc-latest' }} 69 | with: 70 | directory: test/coverage 71 | -------------------------------------------------------------------------------- /.github/workflows/deploy-pages.yml: -------------------------------------------------------------------------------- 1 | name: deploy-pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - deploy-pages 7 | - master 8 | 9 | jobs: 10 | main: 11 | name: Deploy to ghpages 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | # Clone repo + submodules 17 | - name: Checkout source repo 18 | uses: actions/checkout@v3 19 | with: 20 | submodules: 'recursive' 21 | path: main 22 | 23 | - name: Checkout ghpages repo 24 | uses: actions/checkout@v3 25 | with: 26 | submodules: 'recursive' 27 | ref: gh-pages 28 | path: gh-pages 29 | 30 | - name: Install DMD 31 | uses: dlang-community/setup-dlang@v1 32 | with: 33 | compiler: dmd-latest 34 | 35 | - name: Build HTML 36 | shell: bash 37 | run: | 38 | cd main 39 | dub upgrade 40 | dub build -b ddox -v 41 | ./DGrammar/generate_html_from_libdparse.sh 42 | cp ./DGrammar/grammar.html docs/grammar.html 43 | cp -r docs/* ../gh-pages 44 | cd ../gh-pages 45 | 46 | - uses: stefanzweifel/git-auto-commit-action@v4 47 | with: 48 | branch: gh-pages 49 | repository: gh-pages 50 | -------------------------------------------------------------------------------- /.github/workflows/pr_info_intro.yml: -------------------------------------------------------------------------------- 1 | name: PR Info (pre-comment) 2 | 3 | on: 4 | # NOTE: high probability for security vulnerabilities if doing ANYTHING in 5 | # this file other than commenting something! 6 | pull_request_target: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | intro_comment: 12 | name: Make intro comment 13 | runs-on: ubuntu-20.04 14 | steps: 15 | - name: 'Prepare sticky comment' 16 | # commit of v2.5.0 17 | # same one used again at the bottom of the file to update the comment. 18 | uses: marocchino/sticky-pull-request-comment@3d60a5b2dae89d44e0c6ddc69dd7536aec2071cd 19 | with: 20 | message: | 21 | Thanks for your Pull Request and making D better! 22 | 23 | This comment will automatically be updated to summarize some statistics in a few minutes. 24 | only_create: true 25 | -------------------------------------------------------------------------------- /.github/workflows/pr_info_post.yml: -------------------------------------------------------------------------------- 1 | name: PR Info (comment) 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["PR Info"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | comment: 11 | name: PR Info 12 | runs-on: ubuntu-20.04 13 | if: > 14 | github.event.workflow_run.event == 'pull_request' && 15 | github.event.workflow_run.conclusion == 'success' 16 | steps: 17 | # from https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ 18 | - name: 'Download artifact' 19 | uses: actions/github-script@v3.1.0 20 | with: 21 | script: | 22 | var artifacts = await github.actions.listWorkflowRunArtifacts({ 23 | owner: context.repo.owner, 24 | repo: context.repo.repo, 25 | run_id: ${{github.event.workflow_run.id }}, 26 | }); 27 | var matchArtifact = artifacts.data.artifacts.filter((artifact) => { 28 | return artifact.name == "pr" 29 | })[0]; 30 | var download = await github.actions.downloadArtifact({ 31 | owner: context.repo.owner, 32 | repo: context.repo.repo, 33 | artifact_id: matchArtifact.id, 34 | archive_format: 'zip', 35 | }); 36 | var fs = require('fs'); 37 | fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); 38 | - run: unzip pr.zip 39 | 40 | - name: Set variable 41 | run: | 42 | PR_ID=$(cat ./NR) 43 | echo "PR_ID=$PR_ID" >> $GITHUB_ENV 44 | 45 | - name: Update GitHub comment 46 | uses: marocchino/sticky-pull-request-comment@3d60a5b2dae89d44e0c6ddc69dd7536aec2071cd 47 | with: 48 | path: ./comment.txt 49 | number: ${{ env.PR_ID }} 50 | -------------------------------------------------------------------------------- /.github/workflows/pr_info_untrusted.yml: -------------------------------------------------------------------------------- 1 | name: PR Info 2 | 3 | # This workflow builds the whole project once and: 4 | # - comments build deprecations/warnings (highlighting new ones since last tested PR) 5 | 6 | on: 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | pr_info: 13 | name: PR Info 14 | runs-on: ubuntu-latest 15 | steps: 16 | # Compiler to test with 17 | - name: Prepare compiler 18 | uses: dlang-community/setup-dlang@v1 19 | with: 20 | compiler: dmd-latest 21 | 22 | - name: Prepare compiler 23 | uses: dlang-community/setup-dlang@v1 24 | with: 25 | compiler: ldc-latest 26 | 27 | - name: Checkout 28 | uses: actions/checkout@v3 29 | with: 30 | fetch-depth: 0 31 | 32 | - name: Checkout old stuff, with new comment script 33 | run: | 34 | git checkout ${{ github.base_ref }} 35 | git checkout ${{ github.sha }} -- ./ci/summary_comment.sh ./ci/summary_comment_diff.sh 36 | 37 | # first dump old info 38 | 39 | - name: Check pre-PR status 40 | run: ./ci/summary_comment.sh | tee ../OLD_OUTPUT.txt 41 | 42 | - name: Checkout PR target 43 | run: | 44 | git checkout ${{ github.sha }} 45 | git clean -fd 46 | git reset --hard 47 | 48 | - name: Evaluate PR 49 | run: ./ci/summary_comment.sh | tee ../NEW_OUTPUT.txt 50 | 51 | - name: Generate comment 52 | run: ./ci/summary_comment_diff.sh ../OLD_OUTPUT.txt ../NEW_OUTPUT.txt | tee comment.txt 53 | 54 | - name: Prepare comment for upload 55 | run: | 56 | mkdir -p ./pr 57 | mv comment.txt pr 58 | echo ${{ github.event.number }} > ./pr/NR 59 | 60 | - name: upload comment to high-trust action making the comment 61 | uses: actions/upload-artifact@v4 62 | with: 63 | name: pr 64 | path: pr/ 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.[oa] 5 | *.obj 6 | test/coverage/* 7 | test/tester 8 | test/fuzzer 9 | test/output.txt 10 | test/tokens-newlines.txt 11 | test/tokens.txt 12 | test/unittests 13 | dub.selections.json 14 | *.lst 15 | 16 | # Perf reports 17 | perf.data 18 | perf.data.old 19 | 20 | # Valgrind reports 21 | callgrind.* 22 | massif.* 23 | 24 | # D profiling tools 25 | profilegc.log 26 | 27 | # GDB temp files 28 | .gdb_history 29 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlang-community/libdparse/f8a6c28589aae180532fb460a1b22e92a0978292/.gitmodules -------------------------------------------------------------------------------- /DGrammar/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | pup* 3 | -------------------------------------------------------------------------------- /DGrammar/begin.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | D Grammar 6 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /DGrammar/end.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /DGrammar/generate_html_from_libdparse.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | LIBDPARSE_PATH=${DIR}/.. 7 | cd $DIR 8 | 9 | dmd -D ${LIBDPARSE_PATH}/src/dparse/parser.d\ 10 | -I${LIBDPARSE_PATH}/src\ 11 | ${LIBDPARSE_PATH}/macros.ddoc -c -o- 12 | 13 | # install pup (if not existent) 14 | pup=pup 15 | if [[ $(command -v "$pup" 2>&1 > /dev/null || echo 131) -eq 131 ]] ; then 16 | if [ -f $DIR/pup ] ; then 17 | pup="./pup" 18 | else 19 | version="v0.4.0" 20 | shasum="c1706d13aa04f3665b4a3a73958870268b41c672" 21 | out="pup_${version}_linux_amd64.zip" 22 | wget "https://github.com/ericchiang/pup/releases/download/${version}/pup_${version}_linux_amd64.zip" -O "${out}" 23 | echo "${shasum} ${out}" | sha1sum -c - 24 | unzip pup_${version}_linux_amd64.zip 25 | pup="./pup" 26 | fi 27 | fi 28 | 29 | cat begin.txt > grammar.html 30 | cat parser.html | "$pup" 'pre[class="grammar"]' --pre >> grammar.html 31 | cat end.txt >> grammar.html 32 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libdparse 2 | ========= 3 | Library for lexing and parsing D source code. 4 | 5 | # Documentation 6 | 7 | Online documentation is available [here](http://libdparse.dlang.io). 8 | 9 | A HTML version of libdparse's grammar is also [automatically generated](http://libdparse.dlang.io/grammar.html). 10 | 11 | # Testing 12 | [![CI Status](https://travis-ci.org/dlang-community/libdparse.svg)](https://travis-ci.org/dlang-community/libdparse) 13 | 14 | Tests are present in the test directory. To run them execute the run\_tests.sh 15 | script. Running the tests on Windows is not currently supported. 16 | 17 | # Differences with the official grammar 18 | * [Static array initialization syntax](http://dlang.org/arrays.html#static-init-static). Due to ambiguities they are supported when the expression that gives the elements indexes is not an array. In the opposite case they are parsed as associative array literals. 19 | 20 | # Unsupported Syntax 21 | * [Class allocators](http://dlang.org/class.html#allocators). These are deprecated in D2. 22 | * [Class deallocators](http://dlang.org/class.html#deallocators). These are deprecated in D2. 23 | 24 | # Example 25 | 26 | ```d 27 | /+dub.sdl: 28 | dependency "libdparse" version="~>0.7" 29 | +/ 30 | import dparse.ast; 31 | import std.stdio, std.range; 32 | 33 | class TestVisitor : ASTVisitor 34 | { 35 | alias visit = ASTVisitor.visit; 36 | int indentLevel; 37 | 38 | override void visit(const FunctionDeclaration decl) 39 | { 40 | writeln(' '.repeat(indentLevel * 4), decl.name.text); 41 | indentLevel++; 42 | scope (exit) indentLevel--; 43 | decl.accept(this); 44 | } 45 | } 46 | 47 | void main() 48 | { 49 | import dparse.lexer; 50 | import dparse.parser : parseModule; 51 | import dparse.rollback_allocator : RollbackAllocator; 52 | 53 | auto sourceCode = q{ 54 | void foo() @safe { 55 | void bar(); 56 | } 57 | }; 58 | LexerConfig config; 59 | auto cache = StringCache(StringCache.defaultBucketCount); 60 | auto tokens = getTokensForParser(sourceCode, config, &cache); 61 | 62 | RollbackAllocator rba; 63 | auto m = parseModule(tokens, "test.d", &rba); 64 | auto visitor = new TestVisitor(); 65 | visitor.visit(m); 66 | } 67 | ``` 68 | [![Open on run.dlang.io](https://img.shields.io/badge/run.dlang.io-open-blue.svg)](https://run.dlang.io/is/qZsGDD) 69 | -------------------------------------------------------------------------------- /ci/summary_comment.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -u 4 | 5 | # Output from this script is piped to a file by CI, being run from before a 6 | # change has been made and after a change has been made. Then both outputs are 7 | # compared using summary_comment_diff.sh 8 | 9 | # cd to git folder, just in case this is manually run: 10 | ROOT_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")/../" && pwd )" 11 | cd ${ROOT_DIR} 12 | 13 | dub --version 14 | ldc2 --version 15 | 16 | # fetch missing packages before timing 17 | dub upgrade --missing-only 18 | 19 | start=`date +%s` 20 | dub build --build=release --force 2>&1 || echo "LIB BUILD FAILED" 21 | end=`date +%s` 22 | build_time=$( echo "$end - $start" | bc -l ) 23 | 24 | strip libdparse.a 25 | 26 | echo "STAT:------ libdparse statistics ------" 27 | echo "STAT:" 28 | echo "STAT:statistics (-before, +after)" 29 | echo "STAT:library size=$(wc -c libdparse.a)" 30 | echo "STAT:rough build time=${build_time}s" 31 | echo "STAT:" 32 | 33 | cleanup() { 34 | rm -rf "$ROOT_DIR/DCD" 35 | } 36 | trap cleanup EXIT 37 | 38 | git clone https://github.com/dlang-community/DCD.git 39 | cd DCD 40 | echo "STAT:" 41 | echo "STAT:------ DCD statistics ------" 42 | echo "STAT:" 43 | 44 | sed -E -i 's/"libdparse":\s*"[^"]+"/"libdparse": {"path":".."}/' dub.json 45 | cat dub.json 46 | sed -E -i 's/"libdparse":\s*"[^"]+"/"libdparse": {"path":".."}/' dub.selections.json 47 | cat dub.selections.json 48 | 49 | ./ci/summary_comment.sh 2>&1 | grep -E "^STAT:|DCD BUILD FAILED|unix:tc|\d+ tests passed and \d+ failed|No such file|[Ee]rror" 50 | -------------------------------------------------------------------------------- /ci/summary_comment_diff.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -u 4 | 5 | EMPTY=1 6 | 7 | ADDED=$(diff --new-line-format='%L' --old-line-format='' --unchanged-line-format='' "$1" "$2") 8 | REMOVED=$(diff --new-line-format='' --old-line-format='%L' --unchanged-line-format='' "$1" "$2") 9 | TOTAL=$(cat "$2") 10 | 11 | STATS_OLD=$(grep -E '^STAT:' "$1" | sed -E 's/^STAT://') 12 | STATS_NEW=$(grep -E '^STAT:' "$2" | sed -E 's/^STAT://') 13 | 14 | STATS_DIFFED=$(diff --new-line-format='+%L' --old-line-format='-%L' --unchanged-line-format=' %L' <(echo "$STATS_OLD") <(echo "$STATS_NEW")) 15 | 16 | ADDED_DEPRECATIONS=$(grep -Pi '\b(deprecation|deprecated)\b' <<< "$ADDED") 17 | REMOVED_DEPRECATIONS=$(grep -Pi '\b(deprecation|deprecated)\b' <<< "$REMOVED") 18 | ADDED_WARNINGS=$(grep -Pi '\b(warn|warning)\b' <<< "$ADDED") 19 | REMOVED_WARNINGS=$(grep -Pi '\b(warn|warning)\b' <<< "$REMOVED") 20 | 21 | DEPRECATION_COUNT=$(grep -Pi '\b(deprecation|deprecated)\b' <<< "$TOTAL" | wc -l) 22 | WARNING_COUNT=$(grep -Pi '\b(warn|warning)\b' <<< "$TOTAL" | wc -l) 23 | 24 | if [ -z "$ADDED_DEPRECATIONS" ]; then 25 | # no new deprecations 26 | true 27 | else 28 | echo "⚠️ This PR introduces new deprecations:" 29 | echo 30 | echo '```' 31 | echo "$ADDED_DEPRECATIONS" 32 | echo '```' 33 | echo 34 | EMPTY=0 35 | fi 36 | 37 | if [ -z "$ADDED_WARNINGS" ]; then 38 | # no new deprecations 39 | true 40 | else 41 | echo "⚠️ This PR introduces new warnings:" 42 | echo 43 | echo '```' 44 | echo "$ADDED_WARNINGS" 45 | echo '```' 46 | echo 47 | EMPTY=0 48 | fi 49 | 50 | if grep "LIB BUILD FAILED" <<< "$TOTAL"; then 51 | echo '❌ Basic `dub build` failed! Please check your changes again.' 52 | echo 53 | elif grep "DCD BUILD FAILED" <<< "$TOTAL"; then 54 | echo '❌ `dub build` of DCD has failed with these changes! Please check your changes again.' 55 | echo 56 | else 57 | if [ -z "$REMOVED_DEPRECATIONS" ]; then 58 | # no removed deprecations 59 | true 60 | else 61 | echo "✅ This PR fixes following deprecations:" 62 | echo 63 | echo '```' 64 | echo "$REMOVED_DEPRECATIONS" 65 | echo '```' 66 | echo 67 | EMPTY=0 68 | fi 69 | 70 | if [ -z "$REMOVED_WARNINGS" ]; then 71 | # no removed warnings 72 | true 73 | else 74 | echo "✅ This PR fixes following warnings:" 75 | echo 76 | echo '```' 77 | echo "$REMOVED_WARNINGS" 78 | echo '```' 79 | echo 80 | EMPTY=0 81 | fi 82 | 83 | if [ $EMPTY == 1 ]; then 84 | echo "✅ PR OK, no changes in deprecations or warnings" 85 | echo 86 | fi 87 | 88 | echo "Total deprecations: $DEPRECATION_COUNT" 89 | echo 90 | echo "Total warnings: $WARNING_COUNT" 91 | echo 92 | fi 93 | 94 | if [ -z "$STATS_DIFFED" ]; then 95 | # no statistics? 96 | true 97 | else 98 | echo "Build statistics:" 99 | echo 100 | echo '```diff' 101 | echo "$STATS_DIFFED" 102 | echo '```' 103 | echo 104 | fi 105 | 106 | echo '
' 107 | echo 108 | echo 'Full build output' 109 | echo 110 | echo '```' 111 | echo "$TOTAL" 112 | echo '```' 113 | echo 114 | echo '
' 115 | -------------------------------------------------------------------------------- /doc-src/index.ddoc: -------------------------------------------------------------------------------- 1 | $(B libdparse documentation) 2 | 3 | Documentation for individual modules is available through the links on the left. 4 | -------------------------------------------------------------------------------- /doc-src/macros.ddoc: -------------------------------------------------------------------------------- 1 | GRAMMAR =
$0
2 | RULEDEF = $0 3 | RULE = $0 4 | LITERAL = $0 5 | LREF = $0 6 | D_KEYWORD = $(B $0) 7 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libdparse", 3 | "targetName": "dparse", 4 | "targetType": "library", 5 | "description": "Library for lexing and parsing D source code", 6 | "license": "BSL-1.0", 7 | "-ddoxFilterArgs": [ 8 | "--unittest-examples", 9 | "--min-protection=Protected" 10 | ], 11 | "-ddoxTool": "scod" 12 | } 13 | -------------------------------------------------------------------------------- /img/logo.kra: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlang-community/libdparse/f8a6c28589aae180532fb460a1b22e92a0978292/img/logo.kra -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlang-community/libdparse/f8a6c28589aae180532fb460a1b22e92a0978292/img/logo.png -------------------------------------------------------------------------------- /macros.ddoc: -------------------------------------------------------------------------------- 1 | GRAMMAR =
$0
2 | RULEDEF = $(B $(DDOC_ANCHOR $0) $0) 3 | RULE = $(LINK2 #$0, $(B $0)) 4 | LITERAL = $(D_STRING $(I $0)) 5 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('dparse', 'd', 2 | meson_version: '>=0.44', 3 | license: 'BSL-1.0', 4 | version: '0.10.4' 5 | ) 6 | 7 | project_soversion = '0' 8 | 9 | pkgc = import('pkgconfig') 10 | 11 | # 12 | # Sources 13 | # 14 | dparse_src = [ 15 | 'src/dparse/ast.d', 16 | 'src/dparse/entities.d', 17 | 'src/dparse/formatter.d', 18 | 'src/dparse/lexer.d', 19 | 'src/dparse/parser.d', 20 | 'src/dparse/rollback_allocator.d', 21 | 'src/dparse/stack_buffer.d', 22 | 'src/dparse/trivia.d', 23 | 'src/std/experimental/lexer.d', 24 | ] 25 | 26 | src_dir = include_directories('src/') 27 | 28 | # 29 | # Targets 30 | # 31 | dparse_lib = library('dparse', 32 | [dparse_src], 33 | include_directories: [src_dir], 34 | install: true, 35 | version: meson.project_version(), 36 | soversion: project_soversion 37 | ) 38 | 39 | pkgc.generate(name: 'dparse', 40 | libraries: [dparse_lib], 41 | subdirs: 'd/dparse', 42 | version: meson.project_version(), 43 | description: 'Library for lexing and parsing D source code.' 44 | ) 45 | 46 | # for use by others which embed this as subproject 47 | dparse_dep = declare_dependency( 48 | link_with: [dparse_lib], 49 | include_directories: [src_dir] 50 | ) 51 | 52 | # 53 | # Tests 54 | # 55 | if meson.get_compiler('d').get_id() == 'llvm' 56 | extra_args = ['-main', '-link-defaultlib-shared'] 57 | else 58 | extra_args = ['-main'] 59 | endif 60 | 61 | dparse_test_exe = executable('test_dparse', 62 | [dparse_src], 63 | include_directories: [src_dir], 64 | d_unittest: true, 65 | link_args: extra_args, 66 | ) 67 | test('test_dparse', dparse_test_exe) 68 | 69 | # 70 | # Install 71 | # 72 | install_subdir('src/dparse/', install_dir: 'include/d/dparse/') 73 | install_subdir('src/std/', install_dir: 'include/d/dparse/') 74 | -------------------------------------------------------------------------------- /src/dparse/rollback_allocator.d: -------------------------------------------------------------------------------- 1 | module dparse.rollback_allocator; 2 | 3 | import core.memory : GC; 4 | 5 | //version = debug_rollback_allocator; 6 | 7 | /** 8 | * Pointer-bump allocator with rollback functionality. 9 | */ 10 | struct RollbackAllocator 11 | { 12 | public: 13 | 14 | // must be multiple of 8 15 | enum memoryAlignment = 16u; 16 | 17 | @disable this(this); 18 | 19 | ~this() 20 | { 21 | while (first !is null) 22 | deallocateNode(); 23 | } 24 | 25 | /** 26 | * Allocates `size` bytes of memory. 27 | */ 28 | void[] allocate(const size_t size) 29 | out (arr) 30 | { 31 | assert(arr.length == size); 32 | } 33 | do 34 | { 35 | import std.algorithm.comparison : min; 36 | 37 | if (first is null) 38 | allocateNode(size); 39 | 40 | // Memory align the size 41 | immutable size_t s = size & ~(cast(size_t) memoryAlignment - 1); 42 | immutable size_t s2 = s == size ? size : s + memoryAlignment; 43 | 44 | size_t fu = first.used; 45 | size_t end = fu + s2; 46 | //assert(end >= fu + size); 47 | //assert(end % 8 == 0); 48 | if (end > first.mem.length) 49 | { 50 | allocateNode(size); 51 | fu = first.used; 52 | end = fu + s2; 53 | } 54 | //assert((cast(size_t) first.mem.ptr) % 8 == 0); 55 | //assert(((cast(size_t) first.mem.ptr) + end) % 8 == 0); 56 | void[] m = first.mem[fu .. fu + size]; 57 | // alignment can make our size here bigger than what we actually have, so we clamp down to the used amount 58 | first.used = min(end, first.mem.length); 59 | return m; 60 | } 61 | 62 | /** 63 | * Rolls back the allocator to the given checkpoint. 64 | */ 65 | void rollback(size_t point) 66 | { 67 | import std.stdio : stderr; 68 | 69 | if (point == 0) 70 | { 71 | while (first) 72 | deallocateNode(); 73 | return; 74 | } 75 | else 76 | assert(contains(point), "Attepmted to roll back to a point not in the allocator."); 77 | 78 | // while `first !is null` is always going to pass after the contains(point) check, it may no longer pass after deallocateNode 79 | while (first !is null && !first.contains(point)) 80 | deallocateNode(); 81 | assert(first !is null); 82 | 83 | immutable begin = point - cast(size_t) first.mem.ptr; 84 | version (debug_rollback_allocator) 85 | (cast(ubyte[]) first.mem)[begin .. $] = 0; 86 | first.used = begin; 87 | assert(cast(size_t) first.mem.ptr + first.used == point); 88 | } 89 | 90 | /** 91 | * Get a checkpoint for the allocator. 92 | */ 93 | size_t setCheckpoint() const nothrow @nogc 94 | { 95 | assert(first is null || first.used <= first.mem.length); 96 | return first is null ? 0 : cast(size_t) first.mem.ptr + first.used; 97 | } 98 | 99 | /** 100 | * Allocates a T and returns a pointer to it 101 | */ 102 | auto make(T, Args...)(auto ref Args args) 103 | { 104 | import std.algorithm.comparison : max; 105 | import std.experimental.allocator : stateSize; 106 | import std.conv : emplace; 107 | 108 | void[] mem = allocate(max(stateSize!T, 1)); 109 | if (mem.ptr is null) 110 | return null; 111 | static if (is(T == class)) 112 | return emplace!T(mem, args); 113 | else 114 | return emplace(cast(T*) mem.ptr, args); 115 | } 116 | 117 | private: 118 | 119 | // Used for debugging 120 | bool contains(size_t point) const 121 | { 122 | for (const(Node)* n = first; n !is null; n = n.next) 123 | if (n.contains(point)) 124 | return true; 125 | return false; 126 | } 127 | 128 | static struct Node 129 | { 130 | Node* next; 131 | size_t used; 132 | ubyte[] mem; 133 | 134 | bool contains(size_t p) const pure nothrow @nogc @safe 135 | { 136 | return p >= cast(size_t) mem.ptr && p <= cast(size_t) mem.ptr + mem.length; 137 | } 138 | } 139 | 140 | void allocateNode(size_t size) 141 | { 142 | import core.exception : onOutOfMemoryError; 143 | import std.algorithm : max; 144 | import std.conv : emplace; 145 | import std.experimental.allocator.mallocator : AlignedMallocator; 146 | 147 | enum ALLOC_SIZE = 1024 * 8; 148 | 149 | ubyte[] m = cast(ubyte[]) AlignedMallocator.instance.alignedAllocate(max(size + Node.sizeof, ALLOC_SIZE), memoryAlignment); 150 | if (m is null) 151 | onOutOfMemoryError(); 152 | GC.addRange(m.ptr, m.length); 153 | 154 | version (debug_rollback_allocator) 155 | m[] = 0; 156 | Node* n = emplace!Node(cast(Node*) m.ptr, first, 0, m[Node.sizeof .. $]); 157 | assert((cast(size_t) n.mem.ptr) % 8 == 0, "The memoriez!"); 158 | first = n; 159 | } 160 | 161 | void deallocateNode() 162 | { 163 | assert(first !is null); 164 | import std.experimental.allocator.mallocator : AlignedMallocator; 165 | 166 | Node* next = first.next; 167 | ubyte[] mem = (cast(ubyte*) first)[0 .. Node.sizeof + first.mem.length]; 168 | version (debug_rollback_allocator) 169 | mem[] = 0; 170 | GC.removeRange(mem.ptr); 171 | AlignedMallocator.instance.deallocate(mem); 172 | first = next; 173 | } 174 | 175 | Node* first; 176 | } 177 | 178 | @("most simple usage, including memory across multiple pointers") 179 | unittest 180 | { 181 | RollbackAllocator rba; 182 | size_t[10] checkpoint; 183 | foreach (i; 0 .. 10) 184 | { 185 | checkpoint[i] = rba.setCheckpoint(); 186 | rba.allocate(4000); 187 | } 188 | 189 | foreach_reverse (i; 0 .. 10) 190 | { 191 | rba.rollback(checkpoint[i]); 192 | } 193 | } 194 | 195 | @("many allocates and frees while leaking memory") 196 | unittest 197 | { 198 | RollbackAllocator rba; 199 | foreach (i; 0 .. 10) 200 | { 201 | size_t[3] checkpoint; 202 | foreach (n; 0 .. 3) 203 | { 204 | checkpoint[n] = rba.setCheckpoint(); 205 | rba.allocate(4000); 206 | } 207 | foreach_reverse (n; 1 .. 3) 208 | { 209 | rba.rollback(checkpoint[n]); 210 | } 211 | } 212 | } 213 | 214 | @("allocating overly big") 215 | unittest 216 | { 217 | import std.stdio : stderr; 218 | 219 | RollbackAllocator rba; 220 | size_t[200] checkpoint; 221 | size_t cp; 222 | foreach (i; 1024 * 8 - 100 .. 1024 * 8 + 100) 223 | { 224 | try 225 | { 226 | checkpoint[cp++] = rba.setCheckpoint(); 227 | rba.allocate(i); 228 | } 229 | catch (Error e) 230 | { 231 | stderr.writeln("Unittest: crashed in allocating ", i, " bytes"); 232 | throw e; 233 | } 234 | } 235 | 236 | foreach_reverse (i, c; checkpoint[0 .. cp]) 237 | { 238 | try 239 | { 240 | rba.rollback(c); 241 | } 242 | catch (Error e) 243 | { 244 | stderr.writeln("Unittest: crashed in rolling back ", i, " (address ", c, ")"); 245 | throw e; 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/dparse/stack_buffer.d: -------------------------------------------------------------------------------- 1 | module dparse.stack_buffer; 2 | 3 | import core.memory : GC; 4 | 5 | import std.traits; 6 | 7 | //version = debug_stack_allocator; 8 | 9 | struct StackBuffer 10 | { 11 | bool put(T)(T t) 12 | { 13 | import std.experimental.allocator.mallocator : Mallocator; 14 | 15 | static if (is(T == class) || isPointer!T) 16 | if (t is null) 17 | return false; 18 | 19 | if (_length == 0 && arr is null) 20 | arr = stackSpace[]; 21 | 22 | static if (is(T == class)) 23 | static assert(T.sizeof == size_t.sizeof); 24 | 25 | static if (hasIndirections!T) 26 | while (_length % size_t.sizeof != 0) 27 | put(ubyte(1)); 28 | 29 | if (arr.ptr != stackSpace.ptr) 30 | { 31 | if (_length + T.sizeof > arr.length) 32 | { 33 | size_t newLength = arr.length << 1; 34 | while (_length + T.sizeof > newLength) 35 | newLength <<= 1; 36 | auto oldPtr = arr.ptr; 37 | Mallocator.instance.reallocate(arr, newLength); 38 | GC.removeRange(oldPtr); 39 | GC.addRange(arr.ptr, arr.length); 40 | version (debug_stack_allocator) 41 | (cast(ubyte[]) arr)[_length .. $] = 0; 42 | } 43 | } 44 | else if (_length + T.sizeof > stackSpace.length) 45 | { 46 | size_t newLength = stackSpace.length << 1; 47 | while (_length + T.sizeof > newLength) 48 | newLength <<= 1; 49 | arr = Mallocator.instance.allocate(newLength); 50 | GC.addRange(arr.ptr, arr.length); 51 | version (debug_stack_allocator) 52 | (cast(ubyte[]) arr)[] = 0; 53 | arr[0 .. stackSpace.length] = stackSpace[]; 54 | } 55 | arr[_length .. _length + T.sizeof] = (cast(void*) &t)[0 .. T.sizeof]; 56 | _length += T.sizeof; 57 | return true; 58 | } 59 | 60 | ~this() 61 | { 62 | import std.experimental.allocator.mallocator:Mallocator; 63 | 64 | version (debug_stack_allocator) 65 | (cast(ubyte[]) arr)[] = 0; 66 | if (arr.ptr !is stackSpace.ptr) 67 | { 68 | GC.removeRange(arr.ptr); 69 | Mallocator.instance.deallocate(arr); 70 | } 71 | } 72 | 73 | inout(void[]) opSlice() inout pure nothrow @nogc @safe return scope 74 | { 75 | return arr[0 .. _length]; 76 | } 77 | 78 | @disable this(this); 79 | 80 | uint length() const pure nothrow @nogc @safe @property 81 | { 82 | return _length; 83 | } 84 | 85 | alias opDollar = length; 86 | 87 | void clear() 88 | { 89 | _length = 0; 90 | } 91 | 92 | private: 93 | 94 | void[8 * 16] stackSpace; 95 | void[] arr; 96 | uint _length; 97 | } 98 | 99 | unittest 100 | { 101 | StackBuffer sb; 102 | ubyte[80] u; 103 | sb.put(u); 104 | assert(sb.length == 80); 105 | static struct S 106 | { 107 | void[100] u; 108 | } 109 | 110 | S s; 111 | sb.put(s); 112 | 113 | class B 114 | { 115 | int b; 116 | } 117 | 118 | class D : B 119 | { 120 | double d; 121 | } 122 | 123 | B b = new B; 124 | sb.put(b); 125 | 126 | B d = new D; 127 | sb.put(d); 128 | } 129 | 130 | package struct TypedStackBuffer(T) 131 | if (__traits(isPOD, T)) 132 | { 133 | void push(T value) @trusted 134 | { 135 | sb.put(value); 136 | } 137 | 138 | void opOpAssign(string op : "~")(T value) 139 | { 140 | push(value); 141 | } 142 | 143 | ref inout(T) back() inout pure nothrow @nogc @safe @property 144 | { 145 | return (cast(inout(T)[])sb.opSlice[$ - T.sizeof .. $])[0]; 146 | } 147 | 148 | void popBack() pure nothrow @nogc @safe @property 149 | { 150 | assert(!empty); 151 | sb._length -= T.sizeof; 152 | } 153 | 154 | bool empty() const pure nothrow @nogc @safe @property 155 | { 156 | return sb.length == 0; 157 | } 158 | 159 | uint length() const pure nothrow @nogc @safe @property 160 | { 161 | return sb.length / T.sizeof; 162 | } 163 | 164 | private: 165 | StackBuffer sb; 166 | } 167 | -------------------------------------------------------------------------------- /src/dparse/strings.d: -------------------------------------------------------------------------------- 1 | /// Utility for unescaping D string literals of any kind 2 | module dparse.strings; 3 | 4 | import std.algorithm; 5 | import std.array; 6 | import std.ascii : isAlphaNum, isHexDigit, isWhite; 7 | import std.conv; 8 | import std.range; 9 | import std.string; 10 | import std.utf; 11 | 12 | /** 13 | * Checks if a string literal input has correct start/end sequences (quotes) to 14 | * be any kind of D string literal. 15 | * 16 | * Bugs: doesn't check for validity of token strings. 17 | * 18 | * Standards: $(LINK https://dlang.org/spec/lex.html#string_literals) 19 | */ 20 | bool isStringLiteral(const(char)[] literal, out char stringCloseChar, 21 | out bool hasPostfix, out bool parseEscapes, out int prefixLength) 22 | { 23 | // there are no 1 character strings 24 | if (literal.length < 2) 25 | return false; 26 | 27 | // check for valid start 28 | bool allowPostfix; 29 | switch (literal[0]) 30 | { 31 | case 'r': // WysiwygString 32 | case 'x': // HexString 33 | if (literal[1] != '"') 34 | return false; 35 | stringCloseChar = '"'; 36 | allowPostfix = true; 37 | prefixLength = 2; 38 | break; 39 | case 'q': // DelimitedString 40 | if (literal[1] == '{') 41 | stringCloseChar = '}'; 42 | else if (literal[1] == '"') 43 | stringCloseChar = '"'; 44 | else 45 | return false; 46 | 47 | allowPostfix = false; 48 | prefixLength = 2; 49 | break; 50 | case '`': 51 | case '"': 52 | stringCloseChar = literal[0]; 53 | allowPostfix = true; 54 | parseEscapes = stringCloseChar == '"'; 55 | prefixLength = 1; 56 | break; 57 | default: 58 | return false; 59 | } 60 | 61 | if (allowPostfix && literal[$ - 1].among!('c', 'w', 'd')) 62 | { 63 | hasPostfix = true; 64 | literal = literal[0 .. $ - 1]; 65 | } 66 | 67 | if (literal.length <= prefixLength || literal[$ - 1] != stringCloseChar) 68 | return false; 69 | 70 | if (parseEscapes) 71 | { 72 | size_t countBackslashes = 0; 73 | foreach_reverse (dchar c; literal[0 .. $ - 1]) 74 | { 75 | if (c != '\\') 76 | break; 77 | countBackslashes++; 78 | } 79 | 80 | // check if end escapes the quote, making this an invalid string 81 | if ((countBackslashes % 2) != 0) 82 | return false; // uneven backslash count -> invalid end 83 | } 84 | 85 | return true; 86 | } 87 | 88 | /// ditto 89 | bool isStringLiteral(const(char)[] literal) 90 | { 91 | char stringCloseChar; 92 | bool hasPostfix, parseEscapes; 93 | int prefixLength; 94 | return isStringLiteral(literal, stringCloseChar, hasPostfix, parseEscapes, 95 | prefixLength); 96 | } 97 | 98 | /// 99 | unittest 100 | { 101 | assert(isStringLiteral(`"hello"`)); 102 | assert(isStringLiteral(`"ñ"`)); 103 | assert(isStringLiteral(`"hello world!"`)); 104 | assert(isStringLiteral(`r"hello world!"c`)); 105 | assert(isStringLiteral(`r"hello world!"d`)); 106 | assert(isStringLiteral(`q{cool}`)); 107 | assert(isStringLiteral(`q{cool\}`)); 108 | assert(isStringLiteral(`"\\"`)); 109 | assert(!isStringLiteral(`"\\\"`)); 110 | assert(isStringLiteral(`"\\\\"`)); 111 | assert(isStringLiteral(`"a\\\\"`)); 112 | assert(!isStringLiteral(`"ñ\"`)); 113 | assert(isStringLiteral(`"ñ\\"`)); 114 | assert(isStringLiteral(`""`)); 115 | assert(isStringLiteral(`q""`)); 116 | assert(isStringLiteral(`x""`)); 117 | assert(!isStringLiteral(``)); 118 | assert(!isStringLiteral(`"`)); 119 | assert(!isStringLiteral(`w""`)); 120 | assert(!isStringLiteral(`hello"`)); 121 | assert(!isStringLiteral(`"hello`)); 122 | assert(!isStringLiteral(`"hello world`)); 123 | assert(!isStringLiteral(`hello world`)); 124 | assert(!isStringLiteral(`r"`)); 125 | assert(!isStringLiteral(`rr"ok"`)); 126 | assert(!isStringLiteral(`x"`)); 127 | assert(!isStringLiteral(`x" `)); 128 | assert(!isStringLiteral(`qqqq`)); 129 | } 130 | 131 | /// Defines different handler types what to do when invalid escape sequences are 132 | /// found inside $(LREF unescapeString). 133 | enum InvalidEscapeAction 134 | { 135 | /// keep the backslash character as well as the escape characters in the 136 | /// string like in the input string. 137 | keep = 0, 138 | /// Ignore and skip offending characters, drop them from the output. Named 139 | /// character entities are still being included like $(LREF keep) as they 140 | /// are not currently implemented. 141 | skip, 142 | /// Throw a ConvException on invalid escape sequences. Does not throw 143 | /// anything on unknown named character entities as they are not currently 144 | /// implemented but instead treats them like $(LREF keep). 145 | error 146 | } 147 | 148 | /** 149 | * Unescapes a D string, effectively being the same as mixing in the string into 150 | * some function call, but only for single string literals. 151 | * 152 | * Strips quotes, prefixes and suffixes, interprets escape sequences in normal 153 | * double quoted strings and interprets hex strings. Returns simple slices for 154 | * non-escaped strings. 155 | * 156 | * It's undefined how invalid/malformed strings are evaluated. 157 | * 158 | * Bugs: doesn't check for validity of token strings, doesn't interpret named 159 | * character entity escape sequences, (HTML-kind escape sequences) doesn't check 160 | * nesting level of delimited strings. 161 | * 162 | * Standards: $(LINK https://dlang.org/spec/lex.html#string_literals) 163 | */ 164 | string unescapeString( 165 | InvalidEscapeAction invalidEscapeAction = InvalidEscapeAction.error 166 | )( 167 | string input 168 | ) 169 | in 170 | { 171 | assert(isStringLiteral(input)); 172 | } 173 | do 174 | { 175 | char stringCloseChar; 176 | bool hasPostfix, parseEscapes; 177 | int prefixLength; 178 | isStringLiteral(input, stringCloseChar, hasPostfix, parseEscapes, 179 | prefixLength); 180 | 181 | if (hasPostfix) 182 | input = input[0 .. $ - 1]; 183 | 184 | auto content = input[prefixLength .. $ - 1]; 185 | 186 | if (!content.length) 187 | return content; 188 | 189 | if (input[0] == 'x') 190 | { 191 | // hex string, obsolete but still implemented 192 | return parseHexStringContent!invalidEscapeAction(content); 193 | } 194 | else if (input[0] == 'q' && input[1] == '"') 195 | { 196 | content = content.normalizeNewLines; 197 | if (isIdentifierChar(content[0])) 198 | { 199 | auto ln = content.indexOf('\n'); 200 | if (ln == -1) 201 | { 202 | final switch (invalidEscapeAction) 203 | { 204 | case InvalidEscapeAction.keep: 205 | return content; 206 | case InvalidEscapeAction.skip: 207 | return null; 208 | case InvalidEscapeAction.error: 209 | throw new ConvException("Invalid delimited escape string"); 210 | } 211 | } 212 | auto delimiter = content[0 .. ln]; 213 | content = content[ln + 1 .. $]; 214 | if (!content.endsWith(chain("\n", delimiter))) 215 | { 216 | final switch (invalidEscapeAction) 217 | { 218 | case InvalidEscapeAction.keep: 219 | return content; 220 | case InvalidEscapeAction.skip: 221 | auto lastNl = content.lastIndexOf('\n'); 222 | if (lastNl == -1) 223 | return content; 224 | else 225 | return content[0 .. lastNl]; 226 | case InvalidEscapeAction.error: 227 | throw new ConvException("Delimited escape string not ending correctly"); 228 | } 229 | } 230 | return content[0 .. $ - delimiter.length]; 231 | } 232 | else 233 | { 234 | char delimiterChar = content[0]; 235 | char endChar; 236 | switch (delimiterChar) 237 | { 238 | case '[': endChar = ']'; break; 239 | case '(': endChar = ')'; break; 240 | case '<': endChar = '>'; break; 241 | case '{': endChar = '}'; break; 242 | default: endChar = delimiterChar; break; 243 | } 244 | 245 | if (content[1 .. $].endsWith(endChar)) 246 | return content[1 .. $ - 1]; 247 | else 248 | { 249 | final switch (invalidEscapeAction) 250 | { 251 | case InvalidEscapeAction.keep: 252 | return content; 253 | case InvalidEscapeAction.skip: 254 | return content[1 .. $]; 255 | case InvalidEscapeAction.error: 256 | throw new ConvException("Invalid delimited escape string"); 257 | } 258 | } 259 | } 260 | } 261 | else 262 | { 263 | if (!parseEscapes) 264 | return content.normalizeNewLines; 265 | else 266 | return unescapeDoubleQuotedContent!invalidEscapeAction( 267 | content.normalizeNewLines); 268 | } 269 | } 270 | 271 | /// 272 | unittest 273 | { 274 | assert(unescapeString(q{r"I am Oz"}) == r"I am Oz"); 275 | assert(unescapeString(q{r"c:\games\Sudoku.exe"}) == r"c:\games\Sudoku.exe"); 276 | assert(unescapeString(q{r"ab\n"}) == r"ab\n"); 277 | 278 | assert(unescapeString(q{`the Great and Powerful.`}) == `the Great and Powerful.`); 279 | assert(unescapeString(q{`c:\games\Empire.exe`}) == `c:\games\Empire.exe`); 280 | assert(unescapeString(q{`The "lazy" dog`}) == `The "lazy" dog`); 281 | assert(unescapeString(q{`a"b\n`}) == `a"b\n`); 282 | 283 | assert(unescapeString(q{"Who are you?"}) == "Who are you?"); 284 | assert(unescapeString(q{"c:\\games\\Doom.exe"}) == "c:\\games\\Doom.exe"); 285 | assert(unescapeString(q{"ab\n"}) == "ab\n"); 286 | 287 | assert(unescapeString(`x"0A"`) == hexString!"0A"); 288 | assert(unescapeString(`x"00 FBCD 32FD 0A"`) == hexString!"00 FBCD 32FD 0A"); 289 | 290 | assert(unescapeString(`q"(foo(xxx))"`) == q"(foo(xxx))"); 291 | assert(unescapeString(`q"[foo{]"`) == q"[foo{]"); 292 | assert(unescapeString(`q""`) == q""); 293 | assert(unescapeString(`q"{foo(}"`) == q"{foo(}"); 294 | assert(unescapeString(`q"EOS 295 | This 296 | is a multi-line 297 | heredoc string 298 | EOS"`) == q"EOS 299 | This 300 | is a multi-line 301 | heredoc string 302 | EOS"); 303 | assert(unescapeString(`q"/foo]/"`) == `foo]`); 304 | 305 | assert(unescapeString(`q{this is the voice of}`) == q{this is the voice of}); 306 | assert(unescapeString(`q{/*}*/ }`) == q{/*}*/ }); 307 | assert(unescapeString(`q{ world(q{control}); }`) == q{ world(q{control}); }); 308 | assert(unescapeString(`q{ __TIME__ }`) == q{ __TIME__ }); 309 | 310 | assert(unescapeString(q{"hello"c}) == "hello"); 311 | assert(unescapeString(q{"hello"w}) == "hello"); 312 | assert(unescapeString(q{"hello"d}) == "hello"); 313 | 314 | assert(unescapeString(`""`) == ""); 315 | assert(unescapeString(`"hello\'world\"cool\""`) == "hello\'world\"cool\""); 316 | assert(unescapeString(`"\x0A"`) == "\x0A"); 317 | assert(unescapeString(`"\u200b"`) == "\u200b"); 318 | assert(unescapeString(`"\U0001F4A9"`) == "\U0001F4A9"); 319 | assert(unescapeString(`"\0"`) == "\0"); 320 | assert(unescapeString(`"\1"`) == "\1"); 321 | assert(unescapeString(`"\12"`) == "\12"); 322 | assert(unescapeString(`"\127"`) == "\127"); 323 | assert(unescapeString(`"\1278"`) == "\1278"); 324 | assert(unescapeString(`"\12a8"`) == "\12a8"); 325 | assert(unescapeString(`"\1a28"`) == "\1a28"); 326 | assert(unescapeString(`x"afDE"`) == "\xaf\xDE"); 327 | assert(unescapeString("\"hello\nworld\rfoo\r\nbar\u2028ok\u2029\"") 328 | == "hello\nworld\nfoo\nbar\nok\n"); 329 | } 330 | 331 | unittest 332 | { 333 | import std.exception : assertThrown; 334 | 335 | // unimplemented named characters 336 | assert(unescapeString(`"\&foo;"`) == "\\&foo;"); 337 | 338 | assertThrown!ConvException(unescapeString(`"\&foo"`)); 339 | assert(unescapeString!(InvalidEscapeAction.keep)(`"\&foo"`) == "\\&foo"); 340 | assert(unescapeString!(InvalidEscapeAction.skip)(`"\&foo"`) == ""); 341 | } 342 | 343 | unittest 344 | { 345 | import std.exception : assertThrown; 346 | 347 | assertThrown!ConvException(unescapeString(`q"EOS"`)); 348 | assert(unescapeString!(InvalidEscapeAction.keep)(`q"EOS"`) == "EOS"); 349 | assert(unescapeString!(InvalidEscapeAction.skip)(`q"EOS"`) == ""); 350 | 351 | assertThrown!ConvException(unescapeString(`q"EOS 352 | hello"`)); 353 | assert(unescapeString!(InvalidEscapeAction.keep)(`q"EOS 354 | hello"`) == "hello"); 355 | assert(unescapeString!(InvalidEscapeAction.skip)(`q"EOS 356 | hello"`) == "hello"); 357 | assert(unescapeString!(InvalidEscapeAction.skip)(`q"EOS 358 | hello 359 | world"`) == "hello"); 360 | 361 | assertThrown!ConvException(unescapeString(`q"/xd"`)); 362 | assert(unescapeString!(InvalidEscapeAction.keep)(`q"/xd"`) == "/xd"); 363 | assert(unescapeString!(InvalidEscapeAction.skip)(`q"/xd"`) == "xd"); 364 | 365 | assertThrown!ConvException(unescapeString(`"\x"`)); 366 | assert(unescapeString!(InvalidEscapeAction.keep)(`"\x"`) == "\\x"); 367 | assert(unescapeString!(InvalidEscapeAction.skip)(`"\x"`) == ""); 368 | 369 | assertThrown!ConvException(unescapeString(`"\u0"`)); 370 | assert(unescapeString!(InvalidEscapeAction.keep)(`"\u0"`) == "\\u0"); 371 | assert(unescapeString!(InvalidEscapeAction.skip)(`"\u0"`) == ""); 372 | 373 | assertThrown!ConvException(unescapeString(`"\U0000000"`)); 374 | assert(unescapeString!(InvalidEscapeAction.keep)(`"\U0000000"`) == "\\U0000000"); 375 | assert(unescapeString!(InvalidEscapeAction.skip)(`"\U0000000"`) == ""); 376 | 377 | assertThrown!ConvException(unescapeString(`"\xAG"`)); 378 | assert(unescapeString!(InvalidEscapeAction.keep)(`"\xAG"`) == "\\xAG"); 379 | assert(unescapeString!(InvalidEscapeAction.skip)(`"\xAG"`) == ""); 380 | 381 | assertThrown!ConvException(unescapeString(`"\u00AG"`)); 382 | assert(unescapeString!(InvalidEscapeAction.keep)(`"\u00AG"`) == "\\u00AG"); 383 | assert(unescapeString!(InvalidEscapeAction.skip)(`"\u00AG"`) == ""); 384 | 385 | assertThrown!ConvException(unescapeDoubleQuotedContent(`a\`)); 386 | assert(unescapeDoubleQuotedContent!(InvalidEscapeAction.keep)(`a\`) == "a\\"); 387 | assert(unescapeDoubleQuotedContent!(InvalidEscapeAction.skip)(`a\`) == "a"); 388 | 389 | assertThrown!ConvException(unescapeString(`"\z"`)); 390 | assert(unescapeString!(InvalidEscapeAction.keep)(`"\z"`) == "\\z"); 391 | assert(unescapeString!(InvalidEscapeAction.skip)(`"\z"`) == "z"); 392 | 393 | assert(parseHexStringContent("") == ""); 394 | 395 | assertThrown!ConvException(unescapeString(`x"AG"`)); 396 | assert(unescapeString!(InvalidEscapeAction.keep)(`x"AG"`) == "AG"); 397 | assert(unescapeString!(InvalidEscapeAction.skip)(`x"AG"`) == ""); 398 | 399 | assertThrown!ConvException(unescapeString(`x"A"`)); 400 | assert(unescapeString!(InvalidEscapeAction.keep)(`x"A"`) == "A"); 401 | assert(unescapeString!(InvalidEscapeAction.skip)(`x"A"`) == ""); 402 | } 403 | 404 | private string unescapeDoubleQuotedContent( 405 | InvalidEscapeAction invalidEscapeAction = InvalidEscapeAction.error 406 | )( 407 | string input 408 | ) 409 | { 410 | auto escape = input.indexOf('\\'); 411 | if (escape == -1) 412 | return input; 413 | 414 | auto ret = appender!string; 415 | ret.reserve(input.length); 416 | size_t start = 0; 417 | 418 | bool requireMinLength(size_t length) 419 | { 420 | if (escape + length >= input.length) 421 | { 422 | final switch (invalidEscapeAction) 423 | { 424 | case InvalidEscapeAction.keep: 425 | ret ~= input[start .. $]; 426 | start = input.length; 427 | return false; 428 | case InvalidEscapeAction.skip: 429 | start = input.length; 430 | return false; 431 | case InvalidEscapeAction.error: 432 | throw new ConvException("Unfinished escape at end of string"); 433 | } 434 | } 435 | else 436 | { 437 | return true; 438 | } 439 | } 440 | 441 | void errorInvalidCharacter(size_t continueAt) 442 | { 443 | final switch (invalidEscapeAction) 444 | { 445 | case InvalidEscapeAction.keep: 446 | ret ~= input[start .. continueAt]; 447 | start = continueAt; 448 | break; 449 | case InvalidEscapeAction.skip: 450 | start = continueAt; 451 | break; 452 | case InvalidEscapeAction.error: 453 | throw new ConvException("Invalid escape character before index " 454 | ~ continueAt.to!string); 455 | } 456 | } 457 | 458 | bool parseUnicode(size_t length) 459 | { 460 | auto c = input[escape + 2 .. escape + 2 + length]; 461 | if (!c.all!isHexDigit) 462 | { 463 | errorInvalidCharacter(escape + 2 + length); 464 | return false; 465 | } 466 | dchar ch = cast(dchar) c.to!uint(16); 467 | char[4] buf; 468 | auto size = encode(buf, ch); 469 | ret ~= buf[0 .. size]; 470 | start = escape + 2 + length; 471 | return true; 472 | } 473 | 474 | Loop: while (escape != -1) 475 | { 476 | ret ~= input[start .. escape]; 477 | start = escape; 478 | 479 | if (!requireMinLength(1)) 480 | break; 481 | 482 | Switch: 483 | switch (input[escape + 1]) 484 | { 485 | case '\'': 486 | case '"': 487 | case '?': 488 | case '\\': 489 | ret ~= input[escape + 1]; 490 | start = escape + 2; 491 | break; 492 | 493 | case 'a': ret ~= '\a'; start = escape + 2; break; 494 | case 'b': ret ~= '\b'; start = escape + 2; break; 495 | case 'f': ret ~= '\f'; start = escape + 2; break; 496 | case 'n': ret ~= '\n'; start = escape + 2; break; 497 | case 'r': ret ~= '\r'; start = escape + 2; break; 498 | case 't': ret ~= '\t'; start = escape + 2; break; 499 | case 'v': ret ~= '\v'; start = escape + 2; break; 500 | 501 | case 'x': 502 | if (!requireMinLength(3)) 503 | break Loop; 504 | char a = input[escape + 2]; 505 | char b = input[escape + 3]; 506 | if (!a.isHexDigit || !b.isHexDigit) 507 | { 508 | errorInvalidCharacter(escape + 4); 509 | break; 510 | } 511 | ret ~= cast(char)(a.parseHexChar << 4 | b.parseHexChar); 512 | start = escape + 4; 513 | break; 514 | case 'u': 515 | if (!requireMinLength(1 + 4)) 516 | break Loop; 517 | parseUnicode(4); 518 | break; 519 | case 'U': 520 | if (!requireMinLength(1 + 8)) 521 | break Loop; 522 | parseUnicode(8); 523 | break; 524 | case '0': .. case '7': 525 | int length = 1; 526 | foreach (n; 2 .. 4) 527 | { 528 | if (escape + 1 + n > input.length) 529 | break; 530 | char c = input[escape + n]; 531 | if (c >= '0' && c <= '7') 532 | length = n; 533 | else 534 | break; 535 | } 536 | int c = input[escape + 1 .. escape + 1 + length].to!int(8); 537 | ret ~= cast(char) c; 538 | start = escape + 1 + length; 539 | break; 540 | case '&': 541 | auto end = input.indexOf(';', escape + 2); 542 | if (end == -1) 543 | { 544 | errorInvalidCharacter(input.length); 545 | } 546 | else 547 | { 548 | ret ~= input[escape .. end + 1]; 549 | start = end + 1; 550 | } 551 | break; 552 | default: 553 | errorInvalidCharacter(escape + 1); 554 | break; 555 | } 556 | 557 | escape = input.indexOf('\\', start); 558 | } 559 | ret ~= input[start .. $]; 560 | return ret.data; 561 | } 562 | 563 | unittest 564 | { 565 | assert(unescapeDoubleQuotedContent(`hello world`) == "hello world"); 566 | assert(unescapeDoubleQuotedContent(`hello\nworld`) == "hello\nworld"); 567 | assert(unescapeDoubleQuotedContent(`hello\tworld`) == "hello\tworld"); 568 | assert(unescapeDoubleQuotedContent(`hello\u200bworld`) == "hello\u200bworld"); 569 | assert(unescapeDoubleQuotedContent(`hello \"\\ok`) == "hello \"\\ok"); 570 | assert(unescapeDoubleQuotedContent(`こんにちは \"\\ñ`) == "こんにちは \"\\ñ"); 571 | } 572 | 573 | private string parseHexStringContent( 574 | InvalidEscapeAction invalidEscapeAction = InvalidEscapeAction.error 575 | )( 576 | string input 577 | ) 578 | { 579 | if (!input.length) 580 | return input; 581 | 582 | auto ret = appender!string; 583 | ret.reserve(input.length / 3); 584 | char buf; 585 | foreach (i, char c; input) 586 | { 587 | if (c.isWhite) 588 | continue; 589 | 590 | if (!c.isHexDigit) 591 | { 592 | final switch (invalidEscapeAction) 593 | { 594 | case InvalidEscapeAction.keep: 595 | if (buf != char.init) 596 | { 597 | ret ~= buf; 598 | buf = char.init; 599 | } 600 | ret ~= c; 601 | break; 602 | case InvalidEscapeAction.skip: 603 | break; 604 | case InvalidEscapeAction.error: 605 | throw new ConvException("Invalid hex character at index " 606 | ~ i.to!string); 607 | } 608 | } 609 | else 610 | { 611 | if (buf == char.init) 612 | { 613 | buf = c; 614 | } 615 | else 616 | { 617 | ret ~= cast(char)(buf.parseHexChar << 4 | c.parseHexChar); 618 | buf = char.init; 619 | } 620 | } 621 | } 622 | 623 | if (buf != char.init) 624 | { 625 | final switch (invalidEscapeAction) 626 | { 627 | case InvalidEscapeAction.keep: 628 | ret ~= buf; 629 | break; 630 | case InvalidEscapeAction.skip: 631 | break; 632 | case InvalidEscapeAction.error: 633 | throw new ConvException("Unterminated hex character at end of string"); 634 | } 635 | } 636 | 637 | return ret.data; 638 | } 639 | 640 | private int parseHexChar(char c) 641 | in 642 | { 643 | assert(c.isHexDigit); 644 | assert('a' > 'A' && 'A' > '0'); // just checking that ASCII doesn't suddenly change 645 | } 646 | do 647 | { 648 | // can omit range ends and digit check because of function preconditions 649 | if (c >= 'a') 650 | return (c - 'a') + 10; 651 | else if (c >= 'A') 652 | return (c - 'A') + 10; 653 | else 654 | return c - '0'; 655 | } 656 | 657 | private bool isIdentifierChar(char c) 658 | { 659 | return isAlphaNum(c) || c == '_'; 660 | } 661 | 662 | /// normalizes all line endings with \n, as parsed in D strings 663 | private string normalizeNewLines(string text) 664 | { 665 | import std.utf : codeLength; 666 | 667 | enum exoticLineBreakLength = codeLength!char('\u2028'); 668 | static immutable dchar[] nlCharacters = ['\r', '\u2028', '\u2029']; 669 | 670 | auto end = text.indexOfAny(nlCharacters); 671 | if (end == -1) 672 | return text; 673 | auto ret = appender!string; 674 | ret.reserve(text.length); 675 | size_t start = 0; 676 | while (end != -1) 677 | { 678 | ret ~= text[start .. end]; 679 | ret ~= '\n'; 680 | if (end + 1 < text.length && text[end] == '\r' && text[end + 1] == '\n') 681 | end++; 682 | else if (text[end] != '\r') 683 | end += exoticLineBreakLength - 1; 684 | start = end + 1; 685 | end = text.indexOfAny(nlCharacters, start); 686 | } 687 | ret ~= text[start .. $]; 688 | return ret.data; 689 | } 690 | 691 | /// 692 | unittest 693 | { 694 | string testNoChange = "hello\nworld!"; 695 | assert(normalizeNewLines(testNoChange).ptr is testNoChange.ptr); 696 | 697 | assert(normalizeNewLines("hello\rworld") == "hello\nworld"); 698 | assert(normalizeNewLines("hello\r\nworld") == "hello\nworld"); 699 | assert(normalizeNewLines("hello\r\n\nworld") == "hello\n\nworld"); 700 | assert(normalizeNewLines("hello\u2028\nworld") == "hello\n\nworld"); 701 | assert(normalizeNewLines("hello\u2029\nworld") == "hello\n\nworld"); 702 | assert(normalizeNewLines("hello\r") == "hello\n"); 703 | } 704 | -------------------------------------------------------------------------------- /src/dparse/trivia.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Module to work with trivia tokens (`comment`, `whitespace`, 3 | * `specialTokenSequence`) which are attached to tokens near them when source 4 | * code gets tokenized. 5 | */ 6 | module dparse.trivia; 7 | 8 | import std.algorithm; 9 | import std.array; 10 | import std.range; 11 | import std.string; 12 | import std.traits; 13 | 14 | import dparse.lexer; 15 | 16 | enum CommentType : ubyte 17 | { 18 | none, 19 | docLine, 20 | docBlock, 21 | normalLine, 22 | normalBlock, 23 | } 24 | 25 | CommentType determineCommentType(string comment) pure nothrow @safe 26 | { 27 | auto bytes = comment.representation; 28 | auto index = bytes.startsWith( 29 | "//".representation, 30 | "/+".representation, 31 | "/*".representation 32 | ); 33 | bool isDoc = bytes.length >= 3 && bytes[1] == bytes[2]; 34 | switch (index) 35 | { 36 | case 1: 37 | // Don't treat "////////...." comments as doc comments 38 | isDoc = isDoc && (bytes.length == 3 || bytes[3..$].any!(c => c != '/')); 39 | return isDoc ? CommentType.docLine : CommentType.normalLine; 40 | case 2: 41 | case 3: 42 | return isDoc ? CommentType.docBlock : CommentType.normalBlock; 43 | default: 44 | return CommentType.none; 45 | } 46 | } 47 | 48 | /// 49 | unittest 50 | { 51 | assert (determineCommentType("/// hello") == CommentType.docLine); 52 | assert (determineCommentType("/++ hello") == CommentType.docBlock); 53 | assert (determineCommentType("/** hello") == CommentType.docBlock); 54 | assert (determineCommentType("// hello") == CommentType.normalLine); 55 | assert (determineCommentType("/+ hello") == CommentType.normalBlock); 56 | assert (determineCommentType("/* hello") == CommentType.normalBlock); 57 | assert (determineCommentType("/ hello") == CommentType.none); 58 | assert (determineCommentType("/") == CommentType.none); 59 | 60 | assert (determineCommentType("////////////////////") == CommentType.normalLine); 61 | assert (determineCommentType("///") == CommentType.docLine); 62 | assert (determineCommentType("/// ") == CommentType.docLine); 63 | } 64 | 65 | bool isDocComment(CommentType type) @safe nothrow pure 66 | { 67 | return type == CommentType.docLine || type == CommentType.docBlock; 68 | } 69 | 70 | /** 71 | * Removes "decoration" such as leading whitespace, leading + and * characters, 72 | * and places the result into the given output range 73 | */ 74 | public void unDecorateComment(T)(string comment, auto ref T outputRange) 75 | if (isOutputRange!(T, string)) 76 | in 77 | { 78 | assert (comment.length >= 3); 79 | } 80 | do 81 | { 82 | import std.string : chompPrefix, KeepTerminator, lineSplitter, stripRight; 83 | 84 | string leadingChars; 85 | 86 | enum LineType { none, normal, strange } 87 | LineType prevLineType; 88 | 89 | switch (comment[0 .. 3]) 90 | { 91 | case "///": 92 | foreach (line; lineSplitter!(KeepTerminator.yes)(comment)) 93 | { 94 | if (leadingChars.empty) 95 | { 96 | size_t k = 3; 97 | while (k < line.length && (line[k] == ' ' || line[k] == '\t')) 98 | k++; 99 | leadingChars = line[0 .. k]; 100 | } 101 | outputRange.put(line.chompPrefix(leadingChars)); 102 | } 103 | break; 104 | case "/++": 105 | case "/**": 106 | alias CL = MultiLineCommentHelper!(ElementEncodingType!(typeof(comment))); 107 | CL cl = CL(comment); 108 | cl.process(outputRange); 109 | break; 110 | default: 111 | outputRange.put(comment); 112 | } 113 | } 114 | 115 | /// 116 | unittest 117 | { 118 | import std.array:array, appender; 119 | import std.stdio:stderr; 120 | stderr.writeln("Running unittest for unDecorateComment..."); 121 | 122 | string[] inputs = [ 123 | "/***************\n*******************/", 124 | "/***************\n *\n ******************/", 125 | "/**\n*/", 126 | "/** */", 127 | "/***/", 128 | "/******/", 129 | "/** abcde1 */", 130 | "/// abcde2\n/// abcde2", 131 | "/**\n * stuff1\n */", 132 | "/**\n *\n * stuff2\n */", 133 | "/**\n *\n * stuff3\n *\n */", 134 | "/**\n *\n * stuff4\n *\n*/", 135 | "/**\n * abcde3\n * abcde3 \n */", 136 | "/**\n * abcde4\n *\n * abcde4\n */", 137 | "/**abcde5\n*abcde5\n*/", 138 | "/** abcde6\n * abcde6\n*/", 139 | "/**\n1\n\n\n\n*/", 140 | "/**\r\n1\r\n\r\n\r\n\r\n*/", 141 | "/**\na1\n\na2\n\n*/", 142 | "/**b1\n*b2\n*b3*/", 143 | "/**c1\n *c2\n *c3*/", 144 | "/**d1\n *d2\n *d3\n*/", 145 | "///a\fbc\n///def" 146 | ]; 147 | string[] outputs = [ 148 | "", 149 | "", 150 | "", 151 | "", 152 | "", 153 | "", 154 | "abcde1", 155 | "abcde2\nabcde2", 156 | "stuff1", 157 | "stuff2", 158 | "stuff3", 159 | "stuff4", 160 | "abcde3\n abcde3", 161 | "abcde4\n\nabcde4", 162 | "abcde5\nabcde5", 163 | "abcde6\nabcde6", 164 | "1", 165 | "1", 166 | "a1\n\na2", 167 | "b1\nb2\nb3", 168 | "c1\nc2\nc3", 169 | "d1\nd2\nd3", 170 | "a\fbc\ndef" 171 | ]; 172 | 173 | // tests where * and + are not interchangeable 174 | string[2][] np = 175 | [ 176 | ["/**\n * d1\n d2\n */", "* d1\nd2"], 177 | ["/**\n + d1\n d2\n */", "+ d1\nd2"], 178 | ["/**d1\n\n\n*d2\n*/", "d1\n\n*d2"], 179 | ]; 180 | 181 | assert(inputs.length == outputs.length); 182 | foreach (pair; zip(inputs, outputs)) 183 | { 184 | foreach (b; [true, false]) 185 | { 186 | auto app = appender!string(); 187 | unDecorateComment(b ? pair[0] : pair[0].replace("*", "+"), app); 188 | assert(pair[1] == app.data, "[[" ~ pair[0] ~ "]] => [[" ~ app.data ~ "]]"); 189 | } 190 | } 191 | foreach (pair; np) 192 | { 193 | auto app = appender!string(); 194 | unDecorateComment(pair[0], app); 195 | assert(pair[1] == app.data, "[[" ~ pair[0] ~ "]] => [[" ~ app.data ~ "]]"); 196 | } 197 | stderr.writeln("Unittest for unDecorateComment passed."); 198 | } 199 | 200 | /** Gives a line per line view on DDOC comments of type `/++` and `/**` which 201 | * makes easier to remove the decoration and in an almost 100% nogc way. */ 202 | private struct MultiLineCommentHelper(CharType : const(char)) 203 | { 204 | // this struct is more used as a 'function with nested functions' would. 205 | this() @disable; 206 | this(this) @disable; 207 | auto opAssign(T)(T t) @disable; 208 | 209 | private: 210 | 211 | char[][] lines; 212 | // either lines.length or lines.length-1, depending on if last line only closes 213 | size_t lastLineInBlockPlusOne; 214 | // either '*' or '+' 215 | const(char) commentChar; 216 | // either 0 or 1, depending on if first line only opens 217 | ubyte firstLineInBlock; 218 | 219 | import std.ascii : isWhite; 220 | 221 | void stripIndent() @safe @nogc pure nothrow 222 | { 223 | if (lines.length < 2) 224 | return; 225 | size_t count; 226 | foreach (const j; 0 .. lines[1].length) 227 | if (!(lines[1][j]).isWhite) 228 | { 229 | count = j; 230 | break; 231 | } 232 | if (count < 2) 233 | return; 234 | foreach (ref line; lines[1 .. $]) 235 | { 236 | foreach (const j; 0 .. line.length) 237 | { 238 | if (!(line[j]).isWhite) 239 | break; 240 | if (j == count - 1) 241 | { 242 | line = line[j .. $]; 243 | break; 244 | } 245 | } 246 | } 247 | } 248 | 249 | void processFirstLine() @safe @nogc pure nothrow 250 | { 251 | assert(lines.length); 252 | if (lines[0].length > 3) 253 | { 254 | foreach (const i; 1..lines[0].length) 255 | { 256 | if (lines[0][i] == commentChar) 257 | { 258 | if (i < lines[0].length - 2) 259 | continue; 260 | if (i == lines[0].length - 2 && lines[0][i+1] == '/') 261 | { 262 | lines[0][] = ' '; 263 | break; 264 | } 265 | if (i == lines[0].length - 1) 266 | { 267 | lines[0][] = ' '; 268 | break; 269 | } 270 | } 271 | else 272 | { 273 | lines[0][0..i] = ' '; 274 | break; 275 | } 276 | } 277 | } 278 | lines[0][0..3] = " "; 279 | if (lines.length == 1 && 280 | lines[0][$-2] == commentChar && lines[0][$-1] == '/') 281 | { 282 | lines[0][$-2..$] = " "; 283 | } 284 | foreach (const i; 0..lines[0].length) 285 | if (!(lines[0][i].isWhite)) 286 | return; 287 | firstLineInBlock = 1; 288 | } 289 | 290 | void processLastLine() @safe @nogc pure nothrow 291 | { 292 | lastLineInBlockPlusOne = lines.length; 293 | if (lines.length == 1) 294 | return; 295 | size_t closeStartIndex = size_t.max; 296 | foreach (const i; 0..lines[$-1].length) 297 | { 298 | if (lines[$-1][i] == commentChar) 299 | { 300 | if (closeStartIndex == size_t.max) 301 | closeStartIndex = i; 302 | if (i == lines[$-1].length - 2) 303 | { 304 | // see the FIXME note in unDecorate() 305 | lastLineInBlockPlusOne = closeStartIndex == 0 ? lines.length-1 : lines.length; 306 | 307 | lines[$-1][closeStartIndex..$] = ' '; 308 | break; 309 | } 310 | } 311 | else 312 | { 313 | closeStartIndex = size_t.max; 314 | lastLineInBlockPlusOne = lines.length; 315 | } 316 | } 317 | } 318 | 319 | void unDecorate() @safe @nogc pure nothrow 320 | { 321 | if (lines.length == 1 || lines.length == 2 && lines[$-1].length == 0) 322 | return; 323 | bool allDecorated; 324 | static immutable char[2][2] pattern = [[' ', '*'],[' ', '+']]; 325 | const ubyte patternIndex = commentChar == '+'; 326 | // first line is never decorated 327 | const size_t lo = 1; 328 | // although very uncommon, the last line can be decorated e.g in `* lastline */`: 329 | // the first '*' is a deco if all prev lines are also decorated. 330 | // FIXME: `hi` should be set to `lastLineInBlockPlusOne`... 331 | const size_t hi = (lines[$-1].length > 1 && 332 | (lines[$-1][0] == commentChar || lines[$-1][0..2] == pattern[patternIndex])) 333 | ? lines.length : lines.length-1; 334 | // deco with a leading white 335 | foreach (const i; lo .. hi) 336 | { 337 | if (lines[i].length < 2) 338 | break; 339 | else if (lines[i][0..2] != pattern[patternIndex]) 340 | break; 341 | else if (i == hi-1) 342 | allDecorated = true; 343 | } 344 | // deco w/o leading white 345 | if (!allDecorated) 346 | foreach (const i; lo .. hi) 347 | { 348 | if (lines[i].length == 0) 349 | break; 350 | if (lines[i][0] != commentChar) 351 | break; 352 | else if (i == hi-1) 353 | allDecorated = true; 354 | } 355 | if (!allDecorated) 356 | return; 357 | 358 | const size_t indexToChange = (lines[lo][0] == commentChar) ? 0 : 1; 359 | foreach (ref line; lines[lo .. hi]) 360 | line[indexToChange] = ' '; 361 | } 362 | 363 | void stripLeft() @safe @nogc pure nothrow 364 | { 365 | foreach (const i; 0 .. lines[0].length) 366 | if (!(lines[0][i]).isWhite) 367 | { 368 | lines[0] = lines[0][i..$]; 369 | break; 370 | } 371 | if (lines.length == 1) 372 | return; 373 | while (true) 374 | { 375 | bool processColumn; 376 | foreach (ref line; lines[1 .. lastLineInBlockPlusOne]) 377 | { 378 | if (line.length == 0) 379 | continue; 380 | if (!(line[0]).isWhite) 381 | return; 382 | processColumn = true; 383 | } 384 | if (!processColumn) 385 | return; 386 | foreach (ref line; lines[1 .. lastLineInBlockPlusOne]) 387 | { 388 | if (line.length == 0) 389 | continue; 390 | line = line[1..$]; 391 | } 392 | } 393 | } 394 | 395 | void stripRight() @safe @nogc pure nothrow 396 | { 397 | foreach (ref line; lines[0 .. lines.length]) 398 | { 399 | if (line.length == 0) 400 | continue; 401 | if ((line[$-1]).isWhite) 402 | { 403 | size_t firstWhite = line.length; 404 | while (firstWhite > 0 && (line[firstWhite-1]).isWhite) 405 | firstWhite--; 406 | line = line[0..firstWhite]; 407 | } 408 | } 409 | } 410 | 411 | void run() @safe @nogc pure nothrow 412 | { 413 | stripIndent(); 414 | processFirstLine(); 415 | processLastLine(); 416 | unDecorate(); 417 | stripLeft(); 418 | stripRight(); 419 | } 420 | 421 | public: 422 | 423 | this(CharType[] text) @safe pure nothrow 424 | { 425 | assert(text.length >= 3 && text[0] == '/', 426 | "MultiLineCommentHelper text must start with a comment in form /++ or /**"); 427 | 428 | commentChar = text[1]; 429 | size_t startIndex, i; 430 | Appender!(char[][]) linesApp; 431 | linesApp.reserve(512); 432 | 433 | void storeLine(size_t endIndexPlusOne) 434 | { 435 | static if (isMutable!CharType) 436 | linesApp ~= text[startIndex..endIndexPlusOne]; 437 | else 438 | linesApp ~= text[startIndex..endIndexPlusOne].dup; 439 | } 440 | 441 | // if we go over text length (in \r\n) we already stored the line, so just exit there 442 | while (i < text.length) 443 | { 444 | // check if next char is going to be end of text, store until then & break 445 | if (i + 1 == text.length) 446 | { 447 | storeLine(text.length); 448 | break; 449 | } 450 | if (text[i] == '\n') 451 | { 452 | storeLine(i); 453 | startIndex = i + 1; 454 | } 455 | else if (i + 1 < text.length && text[i .. i+2] == "\r\n") 456 | { 457 | storeLine(i); 458 | i++; 459 | startIndex = i + 1; 460 | } 461 | i++; 462 | } 463 | lines = linesApp.data; 464 | } 465 | 466 | void process(T)(ref T outbuffer) 467 | { 468 | run(); 469 | outbuffer.reserve(lines.length * 90); 470 | bool prevWritten, empties; 471 | foreach (ref line; lines[firstLineInBlock .. lines.length]) 472 | { 473 | if (line.length != 0) 474 | { 475 | // close preceeding line 476 | if (prevWritten) 477 | outbuffer ~= "\n"; 478 | // insert new empty line 479 | if (prevWritten && empties) 480 | outbuffer ~= "\n"; 481 | 482 | outbuffer ~= line; 483 | prevWritten = true; 484 | empties = false; 485 | } 486 | else empties = true; 487 | } 488 | } 489 | } 490 | 491 | unittest 492 | { 493 | import std.conv : to; 494 | 495 | alias SC = MultiLineCommentHelper!(immutable(char)); 496 | 497 | // checks full comment processing on the given string and compares the generated lines 498 | void check(string comment, string[] lines, size_t lineNo = __LINE__) 499 | { 500 | auto sc = SC(comment); 501 | sc.run(); 502 | assert(sc.lines == lines, sc.lines.to!string ~ " != " ~ lines.to!string 503 | ~ " (for check on line " ~ lineNo.to!string ~ ")"); 504 | } 505 | 506 | // check common cases while typing 507 | check("/++", [""]); 508 | check("/++\r", [""]); 509 | check("/++\n", [""]); 510 | check("/++\r\n", [""]); 511 | check("/++\r\n+", ["", "+"]); 512 | check("/++\r\n+ ok", ["", "ok"]); 513 | check("/++\r\n+ ok\r\n+/", ["", "ok", ""]); 514 | check("/++/", [""]); 515 | } 516 | 517 | /// Extracts and combines ddoc comments from trivia comments. 518 | string extractDdocFromTrivia(Tokens)(Tokens tokens) pure nothrow @safe 519 | if (isInputRange!Tokens && (is(ElementType!Tokens : Token) || is(ElementType!Tokens : TriviaToken))) 520 | { 521 | bool hasDoc; 522 | auto ret = appender!string; 523 | foreach (trivia; tokens) 524 | { 525 | if (trivia.type == tok!"comment" 526 | && trivia.text.determineCommentType.isDocComment) 527 | { 528 | hasDoc = true; 529 | if (!ret.data.empty) 530 | ret.put('\n'); 531 | unDecorateComment(trivia.text, ret); 532 | } 533 | } 534 | 535 | if (ret.data.length) 536 | return ret.data; 537 | else 538 | return hasDoc ? "" : null; 539 | } 540 | 541 | unittest 542 | { 543 | Token[] tokens = [ 544 | Token(cast(ubyte) tok!"whitespace", "\n\n", 0, 0, 0), 545 | Token(cast(ubyte) tok!"comment", "///", 0, 0, 0), 546 | Token(cast(ubyte) tok!"whitespace", "\n", 0, 0, 0) 547 | ]; 548 | 549 | // Empty comment is non-null 550 | auto comment = extractDdocFromTrivia(tokens); 551 | assert(comment !is null); 552 | assert(comment == ""); 553 | 554 | // Missing comment is null 555 | comment = extractDdocFromTrivia(tokens[0 .. 1]); 556 | assert(comment is null); 557 | assert(comment == ""); 558 | } 559 | 560 | string extractLeadingDdoc(const Token token) pure nothrow @safe 561 | { 562 | return extractDdocFromTrivia(token.leadingTrivia); 563 | } 564 | 565 | string extractTrailingDdoc(const Token token) pure nothrow @safe 566 | { 567 | return extractDdocFromTrivia(token.trailingTrivia.filter!(a => a.line == token.line)); 568 | } 569 | 570 | // test token trivia members 571 | unittest 572 | { 573 | import std.conv : to; 574 | import std.exception : enforce; 575 | 576 | static immutable src = `/// this is a module. 577 | // mixed 578 | /// it can do stuff 579 | module foo.bar; 580 | 581 | // hello 582 | 583 | /** 584 | * some doc 585 | * hello 586 | */ 587 | int x; /// very nice 588 | 589 | // TODO: do stuff 590 | void main() { 591 | #line 40 592 | /// could be better 593 | writeln(":)"); 594 | } 595 | 596 | /// 597 | unittest {} 598 | 599 | /// end of file`; 600 | 601 | LexerConfig cf; 602 | StringCache ca = StringCache(16); 603 | 604 | const tokens = getTokensForParser(src, cf, &ca); 605 | 606 | assert(tokens.length == 22); 607 | 608 | assert(tokens[0].type == tok!"module"); 609 | assert(tokens[0].leadingTrivia.length == 6); 610 | assert(tokens[0].leadingTrivia[0].type == tok!"comment"); 611 | assert(tokens[0].leadingTrivia[0].text == "/// this is a module."); 612 | assert(tokens[0].leadingTrivia[1].type == tok!"whitespace"); 613 | assert(tokens[0].leadingTrivia[1].text == "\n"); 614 | assert(tokens[0].leadingTrivia[2].type == tok!"comment"); 615 | assert(tokens[0].leadingTrivia[2].text == "// mixed"); 616 | assert(tokens[0].leadingTrivia[3].type == tok!"whitespace"); 617 | assert(tokens[0].leadingTrivia[3].text == "\n"); 618 | assert(tokens[0].leadingTrivia[4].type == tok!"comment"); 619 | assert(tokens[0].leadingTrivia[4].text == "/// it can do stuff"); 620 | assert(tokens[0].leadingTrivia[5].type == tok!"whitespace"); 621 | assert(tokens[0].leadingTrivia[5].text == "\n"); 622 | assert(tokens[0].trailingTrivia.length == 1); 623 | assert(tokens[0].trailingTrivia[0].type == tok!"whitespace"); 624 | assert(tokens[0].trailingTrivia[0].text == " "); 625 | 626 | assert(tokens[1].type == tok!"identifier"); 627 | assert(tokens[1].text == "foo"); 628 | assert(!tokens[1].leadingTrivia.length); 629 | assert(!tokens[1].trailingTrivia.length); 630 | 631 | assert(tokens[2].type == tok!"."); 632 | assert(!tokens[2].leadingTrivia.length); 633 | assert(!tokens[2].trailingTrivia.length); 634 | 635 | assert(tokens[3].type == tok!"identifier"); 636 | assert(tokens[3].text == "bar"); 637 | assert(!tokens[3].leadingTrivia.length); 638 | assert(!tokens[3].trailingTrivia.length); 639 | 640 | assert(tokens[4].type == tok!";"); 641 | assert(!tokens[4].leadingTrivia.length); 642 | assert(tokens[4].trailingTrivia.length == 1); 643 | assert(tokens[4].trailingTrivia[0].type == tok!"whitespace"); 644 | assert(tokens[4].trailingTrivia[0].text == "\n\n"); 645 | 646 | assert(tokens[5].type == tok!"int"); 647 | assert(tokens[5].leadingTrivia.length == 4); 648 | assert(tokens[5].leadingTrivia[0].text == "// hello"); 649 | assert(tokens[5].leadingTrivia[1].text == "\n\n"); 650 | assert(tokens[5].leadingTrivia[2].text == "/**\n * some doc\n * hello\n */"); 651 | assert(tokens[5].leadingTrivia[3].text == "\n"); 652 | assert(tokens[5].trailingTrivia.length == 1); 653 | assert(tokens[5].trailingTrivia[0].text == " "); 654 | 655 | assert(tokens[6].type == tok!"identifier"); 656 | assert(tokens[6].text == "x"); 657 | assert(!tokens[6].leadingTrivia.length); 658 | assert(!tokens[6].trailingTrivia.length); 659 | 660 | assert(tokens[7].type == tok!";"); 661 | assert(!tokens[7].leadingTrivia.length); 662 | assert(tokens[7].trailingTrivia.length == 3); 663 | assert(tokens[7].trailingTrivia[0].text == " "); 664 | assert(tokens[7].trailingTrivia[1].text == "/// very nice"); 665 | assert(tokens[7].trailingTrivia[2].text == "\n\n"); 666 | 667 | assert(tokens[8].type == tok!"void"); 668 | assert(tokens[8].leadingTrivia.length == 2); 669 | assert(tokens[8].leadingTrivia[0].text == "// TODO: do stuff"); 670 | assert(tokens[8].leadingTrivia[1].text == "\n"); 671 | assert(tokens[8].trailingTrivia.length == 1); 672 | assert(tokens[8].trailingTrivia[0].text == " "); 673 | 674 | assert(tokens[9].type == tok!"identifier"); 675 | assert(tokens[9].text == "main"); 676 | assert(!tokens[9].leadingTrivia.length); 677 | assert(!tokens[9].trailingTrivia.length); 678 | 679 | assert(tokens[10].type == tok!"("); 680 | assert(!tokens[10].leadingTrivia.length); 681 | assert(!tokens[10].trailingTrivia.length); 682 | 683 | assert(tokens[11].type == tok!")"); 684 | assert(!tokens[11].leadingTrivia.length); 685 | assert(tokens[11].trailingTrivia.length == 1); 686 | assert(tokens[11].trailingTrivia[0].text == " "); 687 | 688 | assert(tokens[12].type == tok!"{"); 689 | assert(!tokens[12].leadingTrivia.length); 690 | assert(tokens[12].trailingTrivia.length == 1); 691 | assert(tokens[12].trailingTrivia[0].text == "\n "); 692 | 693 | assert(tokens[13].type == tok!"identifier"); 694 | assert(tokens[13].text == "writeln"); 695 | assert(tokens[13].leadingTrivia.length == 4); 696 | assert(tokens[13].leadingTrivia[0].type == tok!"specialTokenSequence"); 697 | assert(tokens[13].leadingTrivia[0].text == "#line 40"); 698 | assert(tokens[13].leadingTrivia[1].type == tok!"whitespace"); 699 | assert(tokens[13].leadingTrivia[1].text == "\n "); 700 | assert(tokens[13].leadingTrivia[2].type == tok!"comment"); 701 | assert(tokens[13].leadingTrivia[2].text == "/// could be better"); 702 | assert(tokens[13].leadingTrivia[3].type == tok!"whitespace"); 703 | assert(tokens[13].leadingTrivia[3].text == "\n "); 704 | assert(!tokens[13].trailingTrivia.length); 705 | 706 | assert(tokens[14].type == tok!"("); 707 | assert(!tokens[14].leadingTrivia.length); 708 | assert(!tokens[14].trailingTrivia.length); 709 | 710 | assert(tokens[15].type == tok!"stringLiteral"); 711 | assert(!tokens[15].leadingTrivia.length); 712 | assert(!tokens[15].trailingTrivia.length); 713 | 714 | assert(tokens[16].type == tok!")"); 715 | assert(!tokens[16].leadingTrivia.length); 716 | assert(!tokens[16].trailingTrivia.length); 717 | 718 | assert(tokens[17].type == tok!";"); 719 | assert(!tokens[17].leadingTrivia.length); 720 | assert(tokens[17].trailingTrivia.length == 1); 721 | assert(tokens[17].trailingTrivia[0].text == "\n"); 722 | 723 | assert(tokens[18].type == tok!"}"); 724 | assert(!tokens[18].leadingTrivia.length); 725 | assert(tokens[18].trailingTrivia.length == 1); 726 | assert(tokens[18].trailingTrivia[0].type == tok!"whitespace"); 727 | assert(tokens[18].trailingTrivia[0].text == "\n\n"); 728 | 729 | assert(tokens[19].type == tok!"unittest"); 730 | assert(tokens[19].leadingTrivia.length == 2); 731 | assert(tokens[19].leadingTrivia[0].type == tok!"comment"); 732 | assert(tokens[19].leadingTrivia[0].text == "///"); 733 | assert(tokens[19].leadingTrivia[1].type == tok!"whitespace"); 734 | assert(tokens[19].leadingTrivia[1].text == "\n"); 735 | 736 | assert(tokens[19].trailingTrivia.length == 1); 737 | assert(tokens[19].trailingTrivia[0].type == tok!"whitespace"); 738 | assert(tokens[19].trailingTrivia[0].text == " "); 739 | 740 | assert(tokens[20].type == tok!"{"); 741 | assert(!tokens[20].leadingTrivia.length); 742 | assert(!tokens[20].trailingTrivia.length); 743 | 744 | assert(tokens[21].type == tok!"}"); 745 | assert(!tokens[21].leadingTrivia.length); 746 | assert(tokens[21].trailingTrivia.length == 2); 747 | assert(tokens[21].trailingTrivia[0].type == tok!"whitespace"); 748 | assert(tokens[21].trailingTrivia[0].text == "\n\n"); 749 | assert(tokens[21].trailingTrivia[1].type == tok!"comment"); 750 | assert(tokens[21].trailingTrivia[1].text == "/// end of file"); 751 | } 752 | -------------------------------------------------------------------------------- /test/ast_checks/assert_args.d: -------------------------------------------------------------------------------- 1 | static assert(foo, "a", b, "c"); 2 | -------------------------------------------------------------------------------- /test/ast_checks/assert_args.txt: -------------------------------------------------------------------------------- 1 | ./module/declaration/staticAssertDeclaration//assertArguments/unaryExpression[1]//identifierOrTemplateInstance/identifier[text()="foo"] 2 | ./module/declaration/staticAssertDeclaration//assertArguments/unaryExpression[2]/primaryExpression/stringLiteral[text()='"a"'] 3 | ./module/declaration/staticAssertDeclaration//assertArguments/unaryExpression[3]//identifierOrTemplateInstance/identifier[text()="b"] 4 | ./module/declaration/staticAssertDeclaration//assertArguments/unaryExpression[4]/primaryExpression/stringLiteral[text()='"c"'] 5 | -------------------------------------------------------------------------------- /test/ast_checks/errorRecovery.d: -------------------------------------------------------------------------------- 1 | // things that test that they abort properly, but not emit any AST: 2 | 3 | void asm1() { 4 | asm { 5 | align true; 6 | } 7 | } 8 | 9 | void asm2() { 10 | asm const { 11 | } 12 | } 13 | 14 | void randomContinue() { 15 | continue 4; 16 | } 17 | 18 | void randomBreak() { 19 | break 4; 20 | } 21 | 22 | void randomGoto() { 23 | goto 4; 24 | } 25 | 26 | void randomDebug() { 27 | debug = true; 28 | } 29 | 30 | void randomDebug2() { 31 | debug (true) 32 | { 33 | } 34 | } 35 | 36 | void foo3() 37 | in(x > 4) 38 | 39 | void thisIsCurrentlyEaten() {} 40 | 41 | struct 5 42 | { 43 | void ignoredAsWell() {} 44 | } 45 | 46 | void brokenSwitch() 47 | { 48 | switch (;) 49 | { 50 | } 51 | } 52 | 53 | void brokenCall() 54 | { 55 | foo( 56 | } 57 | 58 | void randomVersion() { 59 | version (true) 60 | { 61 | } 62 | } 63 | 64 | void randomVersion2() { 65 | version = x(""); 66 | } 67 | 68 | class X : Sub 69 | if (x) 70 | void ignored() {} 71 | void afterClass() {} 72 | 73 | version; 74 | 75 | void versionWorks() {} 76 | 77 | mixin = 4; 78 | 79 | void mixinWorks() {} 80 | 81 | version = true; 82 | 83 | void discardedVersion() {} 84 | 85 | // things that test that they abort properly and emit partial or corrected AST: 86 | 87 | void foo() out(x > 4) {} 88 | void foo2() out(true; x > 4) {} 89 | 90 | version = 1 91 | 92 | void bar() 93 | { 94 | do { 95 | 96 | } while (x) 97 | } 98 | 99 | void baz() 100 | { 101 | import a 102 | import b; 103 | } 104 | 105 | @ -------------------------------------------------------------------------------- /test/ast_checks/errorRecovery.txt: -------------------------------------------------------------------------------- 1 | INCLUDES_PARSE_ERROR 2 | ./module/declaration/functionDeclaration[name = 'asm1'] 3 | ./module/declaration/functionDeclaration[name = 'asm2'] 4 | ./module/declaration/functionDeclaration[name = 'randomContinue'] 5 | ./module/declaration/functionDeclaration[name = 'randomBreak'] 6 | ./module/declaration/functionDeclaration[name = 'randomGoto'] 7 | ./module/declaration/functionDeclaration[name = 'randomDebug'] 8 | ./module/declaration/functionDeclaration[name = 'randomDebug2'] 9 | ./module/declaration/functionDeclaration[name = 'brokenSwitch'] 10 | ./module/declaration/functionDeclaration[name = 'brokenCall'] 11 | ./module/declaration/functionDeclaration[name = 'randomVersion'] 12 | ./module/declaration/functionDeclaration[name = 'randomVersion2'] 13 | ./module/declaration/functionDeclaration[name = 'afterClass'] 14 | ./module/declaration/functionDeclaration[name = 'versionWorks'] 15 | ./module/declaration/functionDeclaration[name = 'mixinWorks'] 16 | ./module/declaration/functionDeclaration[name = 'discardedVersion'] 17 | ./module/declaration/functionDeclaration[name = 'foo']//outContractExpression/assertArguments/cmpExpression 18 | ./module/declaration/functionDeclaration[name = 'foo2']//outContractExpression/assertArguments/cmpExpression 19 | ./module/declaration/versionSpecification[intLiteral = '1'] 20 | ./module/declaration/functionDeclaration[name = 'bar']/functionBody//doStatement 21 | ./module/declaration/functionDeclaration[name = 'baz']/functionBody//importDeclaration/singleImport/identifierChain[identifier = 'a'] 22 | ./module/declaration/functionDeclaration[name = 'baz']/functionBody//importDeclaration/singleImport/identifierChain[identifier = 'b'] 23 | -------------------------------------------------------------------------------- /test/ast_checks/file1.d: -------------------------------------------------------------------------------- 1 | struct UselessStruct 2 | { 3 | static if (true) 4 | { 5 | unittest {} 6 | } 7 | 8 | private: 9 | 10 | } 11 | 12 | int someNumber; 13 | -------------------------------------------------------------------------------- /test/ast_checks/file1.txt: -------------------------------------------------------------------------------- 1 | ./module//structBody//compileCondition 2 | ./module//structBody//attributeDeclaration 3 | ./module/declaration/variableDeclaration//type2[text()="int"] 4 | -------------------------------------------------------------------------------- /test/ast_checks/foreach.d: -------------------------------------------------------------------------------- 1 | void foo(T)(T[] arr) 2 | { 3 | foreach (enum ref scope const inout alias f; arr) {} 4 | } 5 | -------------------------------------------------------------------------------- /test/ast_checks/foreach.txt: -------------------------------------------------------------------------------- 1 | //functionDeclaration//foreachStatement//foreachType/alias 2 | //functionDeclaration//foreachStatement//foreachType/enum 3 | //functionDeclaration//foreachStatement//foreachType/ref 4 | //functionDeclaration//foreachStatement//foreachType/scope 5 | //functionDeclaration//foreachStatement//foreachType/typeConstructor[text()='const'] 6 | //functionDeclaration//foreachStatement//foreachType/typeConstructor[text()='inout'] 7 | -------------------------------------------------------------------------------- /test/ast_checks/interpolated_string.d: -------------------------------------------------------------------------------- 1 | void foo() 2 | { 3 | writeln(i"Hello name, you have $$(wealth) in your account right now"); 4 | } 5 | -------------------------------------------------------------------------------- /test/ast_checks/interpolated_string.txt: -------------------------------------------------------------------------------- 1 | //interpolatedString[@startQuote='i"'] 2 | //interpolatedString[@endQuote='"'] 3 | //interpolatedString/text[text()='Hello name, you have $'] 4 | //interpolatedString/text[text()=' in your account right now'] 5 | //interpolatedString/expression/unaryExpression 6 | -------------------------------------------------------------------------------- /test/ast_checks/issue428.d: -------------------------------------------------------------------------------- 1 | debug: 2 | const a = 1; 3 | -------------------------------------------------------------------------------- /test/ast_checks/issue428.txt: -------------------------------------------------------------------------------- 1 | //conditionalDeclaration//trueDeclarations[@style="colon"] 2 | -------------------------------------------------------------------------------- /test/ast_checks/issue471.d: -------------------------------------------------------------------------------- 1 | void foo() 2 | { 3 | auto bar1 = () => 4; 4 | auto bar2 = ref () => 4; 5 | auto bar3 = auto ref () => 4; 6 | auto bar4 = function () => 4; 7 | auto bar5 = function ref () => 4; 8 | auto bar6 = function auto ref () => 4; 9 | } -------------------------------------------------------------------------------- /test/ast_checks/issue471.txt: -------------------------------------------------------------------------------- 1 | //variableDeclaration//autoDeclarationPart//name[text()="bar1"]/../initializer//functionLiteralExpression 2 | not(//variableDeclaration//autoDeclarationPart//name[text()="bar1"]/../initializer//functionLiteralExpression[@ref]) 3 | //variableDeclaration//autoDeclarationPart//name[text()="bar2"]/../initializer//functionLiteralExpression[@ref] 4 | //variableDeclaration//autoDeclarationPart//name[text()="bar2"]/../initializer//functionLiteralExpression[@ref="ref"] 5 | //variableDeclaration//autoDeclarationPart//name[text()="bar3"]/../initializer//functionLiteralExpression[@ref="auto ref"] 6 | //variableDeclaration//autoDeclarationPart//name[text()="bar4"]/../initializer//functionLiteralExpression 7 | not(//variableDeclaration//autoDeclarationPart//name[text()="bar4"]/../initializer//functionLiteralExpression[@ref]) 8 | //variableDeclaration//autoDeclarationPart//name[text()="bar5"]/../initializer//functionLiteralExpression[@ref] 9 | //variableDeclaration//autoDeclarationPart//name[text()="bar5"]/../initializer//functionLiteralExpression[@ref="ref"] 10 | //variableDeclaration//autoDeclarationPart//name[text()="bar6"]/../initializer//functionLiteralExpression[@ref="auto ref"] 11 | -------------------------------------------------------------------------------- /test/ast_checks/moduleDec4.d: -------------------------------------------------------------------------------- 1 | @something deprecated module moduleDec3; 2 | -------------------------------------------------------------------------------- /test/ast_checks/moduleDec4.txt: -------------------------------------------------------------------------------- 1 | /module/moduleDeclaration/atAttribute/identifier[text()="something"] 2 | /module/moduleDeclaration/deprecated 3 | -------------------------------------------------------------------------------- /test/ast_checks/named_arguments.d: -------------------------------------------------------------------------------- 1 | void main() 2 | { 3 | a(100); 4 | b(x: 100); 5 | c(x: 100, y: 200); 6 | d!(a: "a", b: "b")("c"); 7 | } 8 | -------------------------------------------------------------------------------- /test/ast_checks/named_arguments.txt: -------------------------------------------------------------------------------- 1 | //functionCallExpression[unaryExpression//identifier='b']//namedArgument[identifier='x'] 2 | //functionCallExpression[unaryExpression//identifier='c']//namedArgument[identifier='x'] 3 | //functionCallExpression[unaryExpression//identifier='c']//namedArgument[identifier='y'] 4 | //functionCallExpression[unaryExpression//identifier='d']//namedTemplateArgument[identifier='a'] 5 | //functionCallExpression[unaryExpression//identifier='d']//namedTemplateArgument[identifier='b'] 6 | -------------------------------------------------------------------------------- /test/ast_checks/oneLineFunctionDoc.d: -------------------------------------------------------------------------------- 1 | void fun() {} /// Test 2 | -------------------------------------------------------------------------------- /test/ast_checks/oneLineFunctionDoc.txt: -------------------------------------------------------------------------------- 1 | //functionDeclaration/ddoc 2 | -------------------------------------------------------------------------------- /test/ast_checks/scope_exit.d: -------------------------------------------------------------------------------- 1 | void foo() { 2 | scope (exit) 3 | int hi; 4 | } 5 | 6 | void bar() { 7 | // wtf does this even mean, why does this work with DMD?! 8 | int x = 1; 9 | switch (x) 10 | { 11 | case 1: 12 | break; 13 | case 2: 14 | break; 15 | scope (exit) 16 | default: 17 | foo(); 18 | break; 19 | } 20 | } -------------------------------------------------------------------------------- /test/ast_checks/scope_exit.txt: -------------------------------------------------------------------------------- 1 | ./module/declaration[1]/functionDeclaration/name[text()="foo"] 2 | ./module/declaration[1]/functionDeclaration//scopeGuardStatement/declarationOrStatement/declaration/variableDeclaration 3 | ./module/declaration[2]/functionDeclaration/name[text()="bar"] 4 | ./module/declaration[2]/functionDeclaration//switchStatement/statement//declarationOrStatement[1]/statement/caseStatement//intLiteral[text()="1"] 5 | ./module/declaration[2]/functionDeclaration//switchStatement/statement//declarationOrStatement[2]/statement/caseStatement//intLiteral[text()="2"] 6 | # scope (exit) becomes part of `case 2:`, which may be wrong, since DMD doesn't handle it like so, but the grammar is specified like so 7 | ./module/declaration[2]/functionDeclaration//switchStatement/statement//declarationOrStatement[2]/statement/caseStatement//scopeGuardStatement 8 | -------------------------------------------------------------------------------- /test/ast_checks/shortenedFunction.d: -------------------------------------------------------------------------------- 1 | int abcdef(int a) => a * 3; 2 | @property propy() in(true || false) out(r; r == 4) => _x; 3 | -------------------------------------------------------------------------------- /test/ast_checks/shortenedFunction.txt: -------------------------------------------------------------------------------- 1 | ./module/declaration[1]/functionDeclaration/name[text()="abcdef"] 2 | ./module/declaration[1]/functionDeclaration//functionBody/shortenedFunctionBody 3 | ./module/declaration[1]/functionDeclaration//functionBody/shortenedFunctionBody//mulExpression 4 | ./module/declaration[2]/functionDeclaration/name[text()="propy"] 5 | ./module/declaration[2]/functionDeclaration/storageClass/atAttribute/identifier[text()="property"] 6 | ./module/declaration[2]/functionDeclaration//functionBody/shortenedFunctionBody 7 | ./module/declaration[2]/functionDeclaration//inOutContractExpression[1]//inContractExpression 8 | ./module/declaration[2]/functionDeclaration//inOutContractExpression[2]//outContractExpression 9 | -------------------------------------------------------------------------------- /test/ast_checks/switch_condition.d: -------------------------------------------------------------------------------- 1 | void a() 2 | { 3 | switch (auto line = readln) 4 | { 5 | default: 6 | line.strip; 7 | } 8 | } 9 | 10 | void b() 11 | { 12 | switch (scope line = readln) 13 | { 14 | default: 15 | line.strip; 16 | } 17 | } 18 | 19 | void c() 20 | { 21 | switch (const line = readln) 22 | { 23 | default: 24 | line.strip; 25 | } 26 | } 27 | 28 | void d() 29 | { 30 | switch (const inout string line = readln) 31 | { 32 | default: 33 | line.strip; 34 | } 35 | } -------------------------------------------------------------------------------- /test/ast_checks/switch_condition.txt: -------------------------------------------------------------------------------- 1 | //functionDeclaration[name = 'a']//functionBody//switchStatement/condition/auto 2 | not(//functionDeclaration[name = 'a']//functionBody//switchStatement/condition/scope) 3 | //functionDeclaration[name = 'a']//functionBody//switchStatement/condition/identifier[text()='line'] 4 | //functionDeclaration[name = 'a']//functionBody//switchStatement/condition/unaryExpression//identifier[text()='readln'] 5 | //functionDeclaration[name = 'a']//functionBody//switchStatement/statement//defaultStatement//identifier[text()='line'] 6 | //functionDeclaration[name = 'a']//functionBody//switchStatement/statement//defaultStatement//identifier[text()='strip'] 7 | not(//functionDeclaration[name = 'b']//functionBody//switchStatement/condition/auto) 8 | //functionDeclaration[name = 'b']//functionBody//switchStatement/condition/scope 9 | //functionDeclaration[name = 'b']//functionBody//switchStatement/condition/identifier[text()='line'] 10 | //functionDeclaration[name = 'b']//functionBody//switchStatement/condition/unaryExpression//identifier[text()='readln'] 11 | //functionDeclaration[name = 'b']//functionBody//switchStatement/statement//defaultStatement//identifier[text()='line'] 12 | //functionDeclaration[name = 'b']//functionBody//switchStatement/statement//defaultStatement//identifier[text()='strip'] 13 | not(//functionDeclaration[name = 'c']//functionBody//switchStatement/condition/auto) 14 | not(//functionDeclaration[name = 'c']//functionBody//switchStatement/condition/scope) 15 | //functionDeclaration[name = 'c']//functionBody//switchStatement/condition/typeConstructor[text()='const'] 16 | //functionDeclaration[name = 'c']//functionBody//switchStatement/condition/identifier[text()='line'] 17 | //functionDeclaration[name = 'c']//functionBody//switchStatement/condition/unaryExpression//identifier[text()='readln'] 18 | //functionDeclaration[name = 'c']//functionBody//switchStatement/statement//defaultStatement//identifier[text()='line'] 19 | //functionDeclaration[name = 'c']//functionBody//switchStatement/statement//defaultStatement//identifier[text()='strip'] 20 | not(//functionDeclaration[name = 'd']//functionBody//switchStatement/condition/auto) 21 | not(//functionDeclaration[name = 'd']//functionBody//switchStatement/condition/scope) 22 | //functionDeclaration[name = 'd']//functionBody//switchStatement/condition/typeConstructor[text()='const'] 23 | //functionDeclaration[name = 'd']//functionBody//switchStatement/condition/typeConstructor[text()='inout'] 24 | //functionDeclaration[name = 'd']//functionBody//switchStatement/condition/type[@pretty='string'] 25 | //functionDeclaration[name = 'd']//functionBody//switchStatement/condition/identifier[text()='line'] 26 | //functionDeclaration[name = 'd']//functionBody//switchStatement/condition/unaryExpression//identifier[text()='readln'] 27 | //functionDeclaration[name = 'd']//functionBody//switchStatement/statement//defaultStatement//identifier[text()='line'] 28 | //functionDeclaration[name = 'd']//functionBody//switchStatement/statement//defaultStatement//identifier[text()='strip'] 29 | -------------------------------------------------------------------------------- /test/ast_checks/throw_expressions.d: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | foo(throw someThrowable, bar); 4 | 5 | return foo ? bar : throw new Exception("Hello, World!"); 6 | } 7 | -------------------------------------------------------------------------------- /test/ast_checks/throw_expressions.txt: -------------------------------------------------------------------------------- 1 | //functionDeclaration[name = 'main'] 2 | //functionCallExpression/unaryExpression/primaryExpression/identifierOrTemplateInstance[identifier='foo'] 3 | //functionCallExpression/arguments/namedArgumentList/namedArgument[1]/unaryExpression/throwExpression//*[identifier='someThrowable'] 4 | //functionCallExpression/arguments/namedArgumentList/namedArgument[2]/unaryExpression/primaryExpression/*[identifier='bar'] 5 | //ternaryExpression/*[1]//*[identifier='foo'] 6 | //ternaryExpression/*[2]//*[identifier='bar'] 7 | //ternaryExpression/*[3]/throwExpression/unaryExpression/newExpression/type[@pretty='Exception'] 8 | //ternaryExpression/*[3]/throwExpression/unaryExpression/newExpression/arguments//stringLiteral 9 | -------------------------------------------------------------------------------- /test/ast_checks/while_condition.d: -------------------------------------------------------------------------------- 1 | void a() 2 | { 3 | while (auto line = readln) 4 | { 5 | line.strip; 6 | } 7 | } 8 | 9 | void b() 10 | { 11 | while (scope line = readln) 12 | { 13 | line.strip; 14 | } 15 | } 16 | 17 | void c() 18 | { 19 | while (const line = readln) 20 | { 21 | line.strip; 22 | } 23 | } 24 | 25 | void d() 26 | { 27 | while (const inout string line = readln) 28 | { 29 | line.strip; 30 | } 31 | } -------------------------------------------------------------------------------- /test/ast_checks/while_condition.txt: -------------------------------------------------------------------------------- 1 | //functionDeclaration[name = 'a']//functionBody//whileStatement/condition/auto 2 | not(//functionDeclaration[name = 'a']//functionBody//whileStatement/condition/scope) 3 | //functionDeclaration[name = 'a']//functionBody//whileStatement/condition/identifier[text()='line'] 4 | //functionDeclaration[name = 'a']//functionBody//whileStatement/condition/unaryExpression//identifier[text()='readln'] 5 | //functionDeclaration[name = 'a']//functionBody//whileStatement/declarationOrStatement//identifier[text()='line'] 6 | //functionDeclaration[name = 'a']//functionBody//whileStatement/declarationOrStatement//identifier[text()='strip'] 7 | not(//functionDeclaration[name = 'b']//functionBody//whileStatement/condition/auto) 8 | //functionDeclaration[name = 'b']//functionBody//whileStatement/condition/scope 9 | //functionDeclaration[name = 'b']//functionBody//whileStatement/condition/identifier[text()='line'] 10 | //functionDeclaration[name = 'b']//functionBody//whileStatement/condition/unaryExpression//identifier[text()='readln'] 11 | //functionDeclaration[name = 'b']//functionBody//whileStatement/declarationOrStatement//identifier[text()='line'] 12 | //functionDeclaration[name = 'b']//functionBody//whileStatement/declarationOrStatement//identifier[text()='strip'] 13 | not(//functionDeclaration[name = 'c']//functionBody//whileStatement/condition/auto) 14 | not(//functionDeclaration[name = 'c']//functionBody//whileStatement/condition/scope) 15 | //functionDeclaration[name = 'c']//functionBody//whileStatement/condition/typeConstructor[text()='const'] 16 | //functionDeclaration[name = 'c']//functionBody//whileStatement/condition/identifier[text()='line'] 17 | //functionDeclaration[name = 'c']//functionBody//whileStatement/condition/unaryExpression//identifier[text()='readln'] 18 | //functionDeclaration[name = 'c']//functionBody//whileStatement/declarationOrStatement//identifier[text()='line'] 19 | //functionDeclaration[name = 'c']//functionBody//whileStatement/declarationOrStatement//identifier[text()='strip'] 20 | not(//functionDeclaration[name = 'd']//functionBody//whileStatement/condition/auto) 21 | not(//functionDeclaration[name = 'd']//functionBody//whileStatement/condition/scope) 22 | //functionDeclaration[name = 'd']//functionBody//whileStatement/condition/typeConstructor[text()='const'] 23 | //functionDeclaration[name = 'd']//functionBody//whileStatement/condition/typeConstructor[text()='inout'] 24 | //functionDeclaration[name = 'd']//functionBody//whileStatement/condition/type[@pretty='string'] 25 | //functionDeclaration[name = 'd']//functionBody//whileStatement/condition/identifier[text()='line'] 26 | //functionDeclaration[name = 'd']//functionBody//whileStatement/condition/unaryExpression//identifier[text()='readln'] 27 | //functionDeclaration[name = 'd']//functionBody//whileStatement/declarationOrStatement//identifier[text()='line'] 28 | //functionDeclaration[name = 'd']//functionBody//whileStatement/declarationOrStatement//identifier[text()='strip'] 29 | -------------------------------------------------------------------------------- /test/fail_files/aliases.d: -------------------------------------------------------------------------------- 1 | alias Fun(T T T) oops; 2 | alias Fun(T) = const(const()) oops(; 3 | alias Fun(T) = oops(; 4 | alias Fun(T) = int(oops(; 5 | alias Fun(T) == int(oops(; 6 | 7 | -------------------------------------------------------------------------------- /test/fail_files/asm-gcc.d: -------------------------------------------------------------------------------- 1 | void main() 2 | { 3 | asm 4 | { 5 | "mov A B"; 6 | ; 7 | "mov A B" : (a); 8 | "mov A B" : xx "rw" (a); 9 | 10 | "mov A B" : "rw" (a), ; 11 | "mov A B" : "rw" (a), : ; 12 | "mov A B" : "rw" (a) : , : ; 13 | 14 | "mov A B" : "rw" (a) : "r" (b) : this; 15 | 16 | 17 | "mov A B" : "rw" (a) : "r" (b) : "xxx" : 0; 18 | 19 | "mov A B" : "rw" (a) : "r" (b) : "xxx" : LEnd ; 20 | } 21 | } -------------------------------------------------------------------------------- /test/fail_files/bad_asm.d: -------------------------------------------------------------------------------- 1 | void main(){ asm{ db cast(ubyte[]) "é"; } } 2 | -------------------------------------------------------------------------------- /test/fail_files/bad_enum_1.d: -------------------------------------------------------------------------------- 1 | enum : float 2 | { 3 | int x = 10, 4 | } 5 | -------------------------------------------------------------------------------- /test/fail_files/bad_enum_2.d: -------------------------------------------------------------------------------- 1 | enum : int; 2 | -------------------------------------------------------------------------------- /test/fail_files/bad_enum_3.d: -------------------------------------------------------------------------------- 1 | enum A 2 | { 3 | a; 4 | } 5 | -------------------------------------------------------------------------------- /test/fail_files/bad_enums.d: -------------------------------------------------------------------------------- 1 | enum : int; 2 | -------------------------------------------------------------------------------- /test/fail_files/bad_parens.d: -------------------------------------------------------------------------------- 1 | unittest 2 | { 3 | ushort r = bswap(*(cast(ushort*) (bytes.ptr + index)); 4 | } 5 | -------------------------------------------------------------------------------- /test/fail_files/better_block_leave.d: -------------------------------------------------------------------------------- 1 | void foo() 2 | { 3 | c = 4 | } 5 | 6 | alias b this; 7 | -------------------------------------------------------------------------------- /test/fail_files/contracts.d: -------------------------------------------------------------------------------- 1 | void foo() out(true) do {} 2 | void foo() out(;true, "false") out {} {} 3 | void foo() out(;true, "false",,) do {} 4 | void foo() out( 5 | void foo() out$ 6 | void foo() out 7 | void foo() in( 8 | void foo() in$ 9 | void foo() in 10 | void foo() { in } 11 | void foo() { out } 12 | 13 | struct Fpp{invariant()} 14 | -------------------------------------------------------------------------------- /test/fail_files/dcd_tricks.d: -------------------------------------------------------------------------------- 1 | struct A { int a; } 2 | struct B { int b; } 3 | 4 | B node; 5 | 6 | void foo(A node) 7 | { 8 | void bar(B node) 9 | { 10 | node. 11 | } 12 | node. 13 | } 14 | 15 | node. 16 | -------------------------------------------------------------------------------- /test/fail_files/ifConditions.d: -------------------------------------------------------------------------------- 1 | void foo() 2 | { 3 | if (auto const(Type)* data = call()){} 4 | if (const const a = call()){} 5 | if (auto auto a = call()){} 6 | if (Type!(0) = expr()){} 7 | if (Type!(0) i){} 8 | } 9 | -------------------------------------------------------------------------------- /test/fail_files/incompleteStatement198_0.d: -------------------------------------------------------------------------------- 1 | a[{break 2 | -------------------------------------------------------------------------------- /test/fail_files/incompleteStatement198_1.d: -------------------------------------------------------------------------------- 1 | a[{continue 2 | -------------------------------------------------------------------------------- /test/fail_files/incompleteStatement198_2.d: -------------------------------------------------------------------------------- 1 | a[{do 2 | -------------------------------------------------------------------------------- /test/fail_files/incompleteStatement198_3.d: -------------------------------------------------------------------------------- 1 | a[{for 2 | -------------------------------------------------------------------------------- /test/fail_files/incompleteStatement198_4.d: -------------------------------------------------------------------------------- 1 | a[{foreach 2 | -------------------------------------------------------------------------------- /test/fail_files/incompleteStatement198_5.d: -------------------------------------------------------------------------------- 1 | a[{if 2 | -------------------------------------------------------------------------------- /test/fail_files/incompleteStatement198_6.d: -------------------------------------------------------------------------------- 1 | a[{synchronized 2 | -------------------------------------------------------------------------------- /test/fail_files/incompleteStatement198_7.d: -------------------------------------------------------------------------------- 1 | a[{while 2 | -------------------------------------------------------------------------------- /test/fail_files/issue0078.d: -------------------------------------------------------------------------------- 1 | { 2 | int foo(); 3 | } 4 | {} 5 | -------------------------------------------------------------------------------- /test/fail_files/issue0158.d: -------------------------------------------------------------------------------- 1 | T[++const() null @NotAnAssignExpr / immutable].Y y; 2 | -------------------------------------------------------------------------------- /test/fail_files/issue0171.d: -------------------------------------------------------------------------------- 1 | class Foo{a} 2 | struct Foo{int} -------------------------------------------------------------------------------- /test/fail_files/issue0176.d: -------------------------------------------------------------------------------- 1 | void foo(){ asm{ align 8); }} -------------------------------------------------------------------------------- /test/fail_files/issue0179.d: -------------------------------------------------------------------------------- 1 | struct id { this() { foreach( a;1..2) { switch (id) goto 2 | -------------------------------------------------------------------------------- /test/fail_files/issue0227.d: -------------------------------------------------------------------------------- 1 | static if (true) 2 | { 3 | template A() 4 | { 5 | // Err mssg Used to be gagged and parse OK 6 | enum a = 1 7 | enum b = 0; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/fail_files/issue095.d: -------------------------------------------------------------------------------- 1 | enum A 2 | { 3 | a,, 4 | } 5 | -------------------------------------------------------------------------------- /test/fail_files/issue245.d: -------------------------------------------------------------------------------- 1 | void f() { 2 | a[b 3 | -------------------------------------------------------------------------------- /test/fail_files/issue459.d: -------------------------------------------------------------------------------- 1 | void main() 2 | { 3 | pragma(msg, "add a `" ~ A.stringof ~ "`for " ~ t.stringof"); 4 | } -------------------------------------------------------------------------------- /test/fail_files/killer.d: -------------------------------------------------------------------------------- 1 | // Evil block of invalid code that used to kill the parser 2 | unittest 3 | { 4 | a = b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!(b!( 5 | occRange = [ 6 | OteKaostk!(CRowjOLnk!("01","100x", RCOJE!(QTEURLE(7,132468,0)0)), 7 | OteKaostk!(CRowjOLnk!("01","210x", RCOJE!(QTEURLE(54,2641896,0)0)), 8 | OteKaostk!(CRowjOLnk!("01","220x", RCOJE!(QTEURLE(671,31636308,0)0)), 9 | OteKaostk!(CRowjOLnk!("01","564a", RCOJE!(QTEURLE(195,4825080,0)0)), 10 | OteKaostk!(CRowjOLnk!("01","564b", RCOJE!(QTEURLE(624,15223104,0)0)), 11 | OteKaostk!(CRowjOLnk!("01","XXXX", RCOJE!(QTEURLE(664,14892192,0)0)), 12 | OteKaostk!(CRowjOLnk!("04","0", RCOJE!(QTEURLE(46593.000002000015,1248158357.3444164,0)))), 13 | OteKaostk!(CRowjOLnk!("04","210x", RCOJE!(QTEURLE(8,254208,0)0)), 14 | OteKaostk!(CRowjOLnk!("04","220x", RCOJE!(QTEURLE(157,6447048,0)0)), 15 | OteKaostk!(CRowjOLnk!("04","564a", RCOJE!(QTEURLE(85,2196060,0)0)), 16 | OteKaostk!(CRowjOLnk!("04","564b", RCOJE!(QTEURLE(105,2465820,0)0)), 17 | OteKaostk!(CRowjOLnk!("04","XXXX", RCOJE!(QTEURLE(217,4291392,0)0)), 18 | OteKaostk!(CRowjOLnk!("971","0", RCOJE!(QTEURLE(114821.00000200002,3605356322.8689,0)))), 19 | OteKaostk!(CRowjOLnk!("971","100x", RCOJE!(QTEURLE(0.117114,3260.45376,1)))), 20 | OteKaostk!(CRowjOLnk!("971","210x", RCOJE!(QTEURLE(18,509328,0)0)), 21 | OteKaostk!(CRowjOLnk!("971","220x", RCOJE!(QTEURLE(334,13999944,0)0)), 22 | OteKaostk!(CRowjOLnk!("971","564a", RCOJE!(QTEURLE(95,2471520,0)0)), 23 | OteKaostk!(CRowjOLnk!("971","564b", RCOJE!(QTEURLE(493,11299560,0)0)), 24 | OteKaostk!(CRowjOLnk!("971","XXXX", RCOJE!(QTEURLE(885,23098500,0)0)) 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /test/fail_files/killer2.d: -------------------------------------------------------------------------------- 1 | unittest 2 | { 3 | version (53056)[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 4 | union {} 5 | Klse 6 | gosh ÿ 'A+ SPL : tape]; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/fail_files/misc_coverage.d: -------------------------------------------------------------------------------- 1 | module misc_coverage; 2 | 3 | uint a[*!*]; 4 | 5 | const extern([Zzzz]) a = 8; 6 | 7 | auto a(8) = 8; 8 | 9 | void foo() 10 | { 11 | foreach(a;z) break ++ZzZ--; 12 | } 13 | 14 | class Base : List, ++ZzZ--, Of, Base {} 15 | 16 | void foo() 17 | { 18 | switch(foo) 19 | { 20 | case low: .. case high ++ZzZ--; 21 | case arg,list ++ZzZ--; 22 | default ++ZzZ--; 23 | } 24 | switch(foo) 25 | { 26 | case ++ 27 | } 28 | switch(foo) 29 | { 30 | case one: final a; 31 | } 32 | switch(foo) 33 | { 34 | case one: static a; 35 | } 36 | } 37 | 38 | enum E 39 | { 40 | a, 41 | b = ++ZzZ[*!*]ZzZ--; 42 | } 43 | 44 | alias a = 0, b = a; 45 | 46 | version ++ZzZ--; 47 | 48 | void foo() 49 | { 50 | for (i; cond(); i++) } 51 | } 52 | 53 | alias @++ const void null; 54 | 55 | alias int fun() @++; 56 | 57 | alias T = @++ U; 58 | 59 | alias T = extern ++ZzZ-- U; 60 | 61 | int[8] a = [+o+,+o+]; 62 | 63 | import ++; 64 | 65 | __vector( a; 66 | __vector) a; 67 | __vector() a; 68 | __vector(__vector) a; 69 | 70 | class A : SomeDynarray[] {} 71 | 72 | //keep at the end 73 | void foo(){a += 74 | -------------------------------------------------------------------------------- /test/fail_files/pragma_exp_bound.d: -------------------------------------------------------------------------------- 1 | void main() { 2 | pragma(msg F 3 | } 4 | -------------------------------------------------------------------------------- /test/fail_files/shortenedMethod.d: -------------------------------------------------------------------------------- 1 | int bar() in { assert(true); } => 1; 2 | -------------------------------------------------------------------------------- /test/fail_files/sillyparse.d: -------------------------------------------------------------------------------- 1 | void main() 2 | { 3 | if () 4 | } 5 | 6 | void main(@) int); 7 | -------------------------------------------------------------------------------- /test/fail_files/throwattribute.d: -------------------------------------------------------------------------------- 1 | void doThings(throw int x) 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /test/fail_files/varargs-attributes.d: -------------------------------------------------------------------------------- 1 | int printf(const(char)*, ref ...); 2 | -------------------------------------------------------------------------------- /test/fail_files/with_better_dcd.d: -------------------------------------------------------------------------------- 1 | module a; 2 | void foo(){with(A)} 3 | -------------------------------------------------------------------------------- /test/fuzzer.d: -------------------------------------------------------------------------------- 1 | import dparse.lexer; 2 | import std.random; 3 | import std.stdio; 4 | 5 | void main() 6 | { 7 | foreach (i; 0 .. 100) 8 | writeRandomToken(); 9 | } 10 | 11 | void writeRandomToken() 12 | { 13 | again: 14 | IdType i = cast(IdType) uniform(1, IdType.max); 15 | switch (i) 16 | { 17 | case tok!"int": 18 | case tok!"uint": 19 | case tok!"double": 20 | case tok!"idouble": 21 | case tok!"float": 22 | case tok!"ifloat": 23 | case tok!"short": 24 | case tok!"ushort": 25 | case tok!"long": 26 | case tok!"ulong": 27 | case tok!"char": 28 | case tok!"wchar": 29 | case tok!"dchar": 30 | case tok!"bool": 31 | case tok!"void": 32 | case tok!"cent": 33 | case tok!"ucent": 34 | case tok!"real": 35 | case tok!"ireal": 36 | case tok!"byte": 37 | case tok!"ubyte": 38 | case tok!"cdouble": 39 | case tok!"cfloat": 40 | case tok!"creal": 41 | case tok!",": 42 | case tok!".": 43 | case tok!"..": 44 | case tok!"...": 45 | case tok!"/": 46 | case tok!"/=": 47 | case tok!"!": 48 | case tok!"!<": 49 | case tok!"!<=": 50 | case tok!"!<>": 51 | case tok!"!<>=": 52 | case tok!"!=": 53 | case tok!"!>": 54 | case tok!"!>=": 55 | case tok!"$": 56 | case tok!"%": 57 | case tok!"%=": 58 | case tok!"&": 59 | case tok!"&&": 60 | case tok!"&=": 61 | case tok!"(": 62 | case tok!")": 63 | case tok!"*": 64 | case tok!"*=": 65 | case tok!"+": 66 | case tok!"++": 67 | case tok!"+=": 68 | case tok!"-": 69 | case tok!"--": 70 | case tok!"-=": 71 | case tok!":": 72 | case tok!";": 73 | case tok!"<": 74 | case tok!"<<": 75 | case tok!"<<=": 76 | case tok!"<=": 77 | case tok!"<>": 78 | case tok!"<>=": 79 | case tok!"=": 80 | case tok!"==": 81 | case tok!"=>": 82 | case tok!">": 83 | case tok!">=": 84 | case tok!">>": 85 | case tok!">>=": 86 | case tok!">>>": 87 | case tok!">>>=": 88 | case tok!"?": 89 | case tok!"@": 90 | case tok!"[": 91 | case tok!"]": 92 | case tok!"^": 93 | case tok!"^=": 94 | case tok!"^^": 95 | case tok!"^^=": 96 | case tok!"{": 97 | case tok!"|": 98 | case tok!"|=": 99 | case tok!"||": 100 | case tok!"}": 101 | case tok!"~": 102 | case tok!"~=": 103 | case tok!"abstract": 104 | case tok!"alias": 105 | case tok!"align": 106 | case tok!"asm": 107 | case tok!"assert": 108 | case tok!"auto": 109 | case tok!"break": 110 | case tok!"case": 111 | case tok!"cast": 112 | case tok!"const": 113 | case tok!"continue": 114 | case tok!"debug": 115 | case tok!"default": 116 | case tok!"delegate": 117 | case tok!"delete": 118 | case tok!"deprecated": 119 | case tok!"do": 120 | case tok!"else": 121 | case tok!"enum": 122 | case tok!"extern": 123 | case tok!"false": 124 | case tok!"final": 125 | case tok!"finally": 126 | case tok!"function": 127 | case tok!"goto": 128 | case tok!"immutable": 129 | case tok!"import": 130 | case tok!"in": 131 | case tok!"inout": 132 | case tok!"invariant": 133 | case tok!"is": 134 | case tok!"lazy": 135 | case tok!"macro": 136 | case tok!"mixin": 137 | case tok!"module": 138 | case tok!"new": 139 | case tok!"nothrow": 140 | case tok!"null": 141 | case tok!"out": 142 | case tok!"override": 143 | case tok!"pragma": 144 | case tok!"pure": 145 | case tok!"ref": 146 | case tok!"return": 147 | case tok!"scope": 148 | case tok!"shared": 149 | case tok!"static": 150 | case tok!"super": 151 | case tok!"template": 152 | case tok!"this": 153 | case tok!"throw": 154 | case tok!"true": 155 | case tok!"try": 156 | case tok!"typedef": 157 | case tok!"typeid": 158 | case tok!"typeof": 159 | case tok!"unittest": 160 | case tok!"version": 161 | case tok!"__DATE__": 162 | case tok!"__EOF__": 163 | case tok!"__FILE__": 164 | case tok!"__FUNCTION__": 165 | case tok!"__gshared": 166 | case tok!"__LINE__": 167 | case tok!"__MODULE__": 168 | case tok!"__parameters": 169 | case tok!"__PRETTY_FUNCTION__": 170 | case tok!"__TIME__": 171 | case tok!"__TIMESTAMP__": 172 | case tok!"__traits": 173 | case tok!"__vector": 174 | case tok!"__VENDOR__": 175 | case tok!"__VERSION__": 176 | case tok!"export": 177 | case tok!"package": 178 | case tok!"private": 179 | case tok!"public": 180 | case tok!"protected": 181 | case tok!"synchronized": 182 | write(str(i), " "); 183 | break; 184 | case tok!"class": 185 | case tok!"interface": 186 | case tok!"union": 187 | case tok!"struct": 188 | write(str(i), " "); 189 | if (uniform(0, 9) > 3) 190 | write("ident "); 191 | break; 192 | case tok!"switch": 193 | case tok!"if": 194 | case tok!"for": 195 | case tok!"foreach": 196 | case tok!"foreach_reverse": 197 | case tok!"while": 198 | case tok!"with": 199 | case tok!"catch": 200 | write(str(i), " "); 201 | if (uniform(0, 9) > 3) 202 | write("("); 203 | break; 204 | case tok!"doubleLiteral": 205 | write("1.0 "); 206 | break; 207 | case tok!"floatLiteral": 208 | write("1.0f "); 209 | break; 210 | case tok!"intLiteral": 211 | write("1 "); 212 | break; 213 | case tok!"longLiteral": 214 | write("1L "); 215 | break; 216 | case tok!"uintLiteral": 217 | write("1U "); 218 | break; 219 | case tok!"ulongLiteral": 220 | write("1UL "); 221 | break; 222 | case tok!"idoubleLiteral": 223 | case tok!"ifloatLiteral": 224 | case tok!"realLiteral": 225 | case tok!"irealLiteral": 226 | break; 227 | case tok!"dstringLiteral": 228 | case tok!"stringLiteral": 229 | case tok!"wstringLiteral": 230 | writeStringLiteral(); 231 | break; 232 | case tok!"identifier": 233 | write("ident "); 234 | break; 235 | default: 236 | goto again; 237 | } 238 | } 239 | 240 | void writeStringLiteral() 241 | { 242 | switch (uniform(0, 6)) 243 | { 244 | case 0: writeDoubleQuoteStringLiteral(); break; 245 | case 1: writeHeredocStringLiteral(); break; 246 | case 2: writeDelimitedStringLiteral(); break; 247 | case 3: writeBacktickStringLiteral(); break; 248 | case 4: writeRDoubleQuoteStringLiteral(); break; 249 | case 5: writeCharLiteral(); break; 250 | default: break; 251 | } 252 | } 253 | 254 | void writeDoubleQuoteStringLiteral() 255 | { 256 | write('"'); 257 | auto length = uniform(0, 30); 258 | foreach (i; 0 .. length) 259 | { 260 | again: 261 | auto j = uniform(0, 128); 262 | switch (j) 263 | { 264 | case 0: .. case 6: goto again; 265 | case 7: write(`\a`); break; 266 | case 8: write(`\b`); break; 267 | case 9: write(`\t`); break; 268 | case 10: write(`\n`); break; 269 | case 11: write(`\v`); break; 270 | case 12: write(`\f`); break; 271 | case 13: write(`\r`); break; 272 | case 14: .. case 31: goto again; 273 | case 32: .. case 33: 274 | case 35: .. case 91: 275 | case 93: .. case 176: 276 | write(cast(char) j); 277 | break; 278 | case 34: write(`\"`); break; 279 | case 92: write(`\\`); break; 280 | default: goto again; 281 | } 282 | } 283 | write('"'); 284 | write([' ', 'c', 'w', 'd'][uniform(0, 4)]); 285 | write(' '); 286 | } 287 | 288 | void writeHeredocStringLiteral() 289 | { 290 | 291 | } 292 | 293 | void writeDelimitedStringLiteral() 294 | { 295 | 296 | } 297 | 298 | void writeBacktickStringLiteral() 299 | { 300 | 301 | } 302 | 303 | void writeRDoubleQuoteStringLiteral() 304 | { 305 | 306 | } 307 | 308 | void writeCharLiteral() 309 | { 310 | 311 | } 312 | -------------------------------------------------------------------------------- /test/fuzzer.sh: -------------------------------------------------------------------------------- 1 | TOTALRUNS=10000 2 | 3 | FILES=$(find ../experimental_allocator/src/ ../src/dparse/ ../src/std/experimental/ -name "*.d") 4 | 5 | echo "Compiling fuzzer" 6 | dmd fuzzer.d -I../src/ ${FILES} || exit 1 7 | echo "Done" 8 | echo "Compiling parser" 9 | dmd -O -inline -g tester.d -I../src/ ${FILES} || exit 1 10 | echo "Done" 11 | printf " " 12 | for i in $(seq $TOTALRUNS); do 13 | printf "\b\b\b\b\b\b\b\b\b\b\b" 14 | printf "%5d/%5d" $i $TOTALRUNS 15 | ./fuzzer > tokens.txt 16 | ./tester tokens.txt > output.txt 2>&1 17 | if [ $? -eq 139 ]; then echo "Segfaulted..."; sed -e "s/ /\\n/g" tokens.txt > tokens-newlines.txt; exit 1; fi 18 | grep ception output.txt && sed -e "s/ /\\n/g" tokens.txt > tokens-newlines.txt && exit 1; 19 | done 20 | printf "\n" 21 | -------------------------------------------------------------------------------- /test/pass_files/ae.d: -------------------------------------------------------------------------------- 1 | void httpRequest(HttpRequest request, void delegate(Data) resultHandler, void delegate(string) errorHandler, int redirectCount = 0) 2 | { 3 | 4 | void responseHandler(HttpResponse response, string disconnectReason) 5 | { 6 | if (!response) 7 | if (errorHandler) 8 | errorHandler(disconnectReason); 9 | else 10 | throw new Exception(disconnectReason); 11 | else 12 | if (response.status >= 300 && response.status < 400 && "Location" in response.headers) 13 | { 14 | if (redirectCount == 15) 15 | throw new Exception("HTTP redirect loop: " ~ request.url); 16 | request.resource = applyRelativeURL(request.url, response.headers["Location"]); 17 | if (response.status == HttpStatusCode.SeeOther) 18 | { 19 | request.method = "GET"; 20 | request.data = null; 21 | } 22 | httpRequest(request, resultHandler, errorHandler, redirectCount+1); 23 | } 24 | else 25 | if (errorHandler) 26 | try 27 | resultHandler(response.getContent()); 28 | catch (Exception e) 29 | errorHandler(e.msg); 30 | else 31 | resultHandler(response.getContent()); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /test/pass_files/alias_assign.d: -------------------------------------------------------------------------------- 1 | template T(X) 2 | { 3 | alias A = int; 4 | A = X; 5 | struct B 6 | { 7 | A t; 8 | } 9 | } 10 | 11 | void fun() 12 | { 13 | auto b = T!float.B(); 14 | } 15 | 16 | // https://github.com/dlang/phobos/pull/8033 17 | template EraseAll(args...) 18 | if (args.length >= 1) 19 | { 20 | alias EraseAll = AliasSeq!(); 21 | static foreach (arg; args[1 .. $]) 22 | static if (!isSame!(args[0], arg)) 23 | EraseAll = AliasSeq!(EraseAll, arg); 24 | } 25 | 26 | // https://github.com/dlang/phobos/pull/8034 27 | template NoDuplicates(args...) 28 | { 29 | alias NoDuplicates = AliasSeq!(); 30 | static foreach (arg; args) 31 | NoDuplicates = AppendUnique!(NoDuplicates, arg); 32 | } 33 | 34 | // https://github.com/dlang/phobos/pull/8036 35 | template ReplaceAll(args...) 36 | { 37 | alias ReplaceAll = AliasSeq!(); 38 | static foreach (arg; args[2 .. $]) 39 | { 40 | static if (isSame!(args[0], arg)) 41 | ReplaceAll = AliasSeq!(ReplaceAll, args[1]); 42 | else 43 | ReplaceAll = AliasSeq!(ReplaceAll, arg); 44 | } 45 | } 46 | 47 | // https://github.com/dlang/phobos/pull/8037 48 | template Reverse(args...) 49 | { 50 | alias Reverse = AliasSeq!(); 51 | static foreach_reverse (arg; args) 52 | Reverse = AliasSeq!(Reverse, arg); 53 | } 54 | 55 | // https://github.com/dlang/phobos/pull/8038 56 | template MostDerived(T, TList...) 57 | { 58 | import std.traits : Select; 59 | alias MostDerived = T; 60 | static foreach (U; TList) 61 | MostDerived = Select!(is(U : MostDerived), U, MostDerived); 62 | } 63 | 64 | // https://github.com/dlang/phobos/pull/8044 65 | template Repeat(size_t n, items...) 66 | { 67 | static if (n == 0) 68 | { 69 | alias Repeat = AliasSeq!(); 70 | } 71 | else 72 | { 73 | alias Repeat = items; 74 | enum log2n = 75 | { 76 | uint result = 0; 77 | auto x = n; 78 | while (x >>= 1) 79 | ++result; 80 | return result; 81 | }(); 82 | static foreach (i; 0 .. log2n) 83 | { 84 | Repeat = AliasSeq!(Repeat, Repeat); 85 | } 86 | Repeat = AliasSeq!(Repeat, Repeat!(n - (1u << log2n), items)); 87 | } 88 | } 89 | 90 | // https://github.com/dlang/phobos/pull/8047 91 | template Stride(int stepSize, Args...) 92 | if (stepSize != 0) 93 | { 94 | alias Stride = AliasSeq!(); 95 | static if (stepSize > 0) 96 | { 97 | static foreach (i; 0 .. (Args.length + stepSize - 1) / stepSize) 98 | Stride = AliasSeq!(Stride, Args[i * stepSize]); 99 | } 100 | else 101 | { 102 | static foreach (i; 0 .. (Args.length - stepSize - 1) / -stepSize) 103 | Stride = AliasSeq!(Stride, Args[$ - 1 + i * stepSize]); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /test/pass_files/aliases.d: -------------------------------------------------------------------------------- 1 | alias a = b; 2 | alias b a; 3 | alias int b; 4 | alias int b, c, d, e; 5 | alias c = int; 6 | alias x = y, w = z; 7 | alias const immutable int constInt; 8 | alias shared(int) sharedInt; 9 | alias a(b) = c; 10 | alias a(b) = c, d = e, f = g; 11 | alias a(b) = const c, d = int; 12 | alias a(b) = const c, d = double[int[string]]; 13 | alias a = c.d.e; 14 | struct S 15 | { 16 | int a; 17 | alias a this; 18 | } 19 | class Foo 20 | { 21 | void bar() 22 | { 23 | alias self = this; 24 | } 25 | } 26 | 27 | // C-style aliases 28 | alias int GetterType() @property; 29 | alias int SetterType(int) @property; 30 | alias string SetterType(int, string) @nogc; 31 | alias string SetterType(int, string) @safe; 32 | alias int F1(); 33 | alias @property int F2(); 34 | alias string F3(); 35 | alias nothrow @trusted uint F4(); 36 | alias int F5(Object); 37 | alias bool F6(Object); 38 | alias int F1(); 39 | alias int F2() pure nothrow; 40 | alias int F3() @safe; 41 | alias int F23() @safe pure nothrow; 42 | 43 | // return type covariance 44 | alias long F4(); 45 | class C {} 46 | class D : C {} 47 | alias C F5(); 48 | alias D F6(); 49 | alias typeof(null) F7(); 50 | alias int[] F8(); 51 | alias int* F9(); 52 | 53 | // variadic type equality 54 | alias int F10(int); 55 | alias int F11(int...); 56 | alias int F12(int, ...); 57 | 58 | // linkage equality 59 | alias extern(C) int F13(int); 60 | alias extern(D) int F14(int); 61 | alias extern(Windows) int F15(int); 62 | 63 | // ref & @property equality 64 | alias int F16(int); 65 | alias ref int F17(int); 66 | alias @property int F18(int); 67 | 68 | // function types with '=' 69 | alias Fun1(T) = T(T t) @safe; 70 | alias Fun2 = void(int,int,int) pure nothrow; 71 | alias Fun3 = const void(int,int,int) @trusted; 72 | alias Fun4(T...) = shared const(Foo!Bar)(T t) const; 73 | -------------------------------------------------------------------------------- /test/pass_files/asm-gcc.d: -------------------------------------------------------------------------------- 1 | module asm_gcc; 2 | 3 | ref T store(T)(); 4 | 5 | enum asm1 = "mov %0, %0;"; 6 | string asm2(int i) { return "mov %0, %0;"; } 7 | 8 | void main() 9 | { 10 | int var1, var2, var3, var4; 11 | int* ptr1; 12 | 13 | asm 14 | { 15 | // Some tests as found in dmd's iasmgcc.d 16 | "nop"; 17 | asm1; 18 | asm2(1); 19 | mixin(`"repne"`, `~ "scasb"`); 20 | 21 | // GCC examples 22 | "notl %[iov]" : [iov] "=r" (var1) : "0" (var2) ; 23 | ; 24 | "mov %1, %0\n\t" 25 | "add $1, %0" : "=r" (var1) : "r" (var2) ; 26 | 27 | // DRuntime 28 | "cpuid" : "=a" (var1), "=c" (var2), "=d" (var3) : "a" (0x8000_0006) : "ebx" ; 29 | 30 | // Deprecated: Missing parens 31 | "cpuid" : "=a" var1, "=b" var2 : "a" 0x8000_001E : "ecx", "edx"; 32 | 33 | "str x29, %0" : "=m" (var1) ; 34 | 35 | "mrs %0, cntvct_el0" : "=r" *ptr1; 36 | 37 | "mov %1, %0" : : "r" (var2) : "cc" : LCarry ; 38 | "mov %0, %0" : : "r" (var2) : "cc" ; 39 | 40 | "mov %0, %0" : "=r" (*ptr1) : "r" (store!int = 1) : "cc"; 41 | } 42 | 43 | LCarry: 44 | asm /*goto*/ { 45 | "btl %1, %0\n\t" 46 | "jc %l2" 47 | : /* No outputs. */ 48 | : "r" (var1), "r" (var2) 49 | : "cc" 50 | : LCarry 51 | ; 52 | } 53 | 54 | asm { 55 | ; 56 | ; 57 | "jmp LCarry" : : : : LCarry ; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/pass_files/asm.d: -------------------------------------------------------------------------------- 1 | module hasasm; 2 | 3 | void doStuff() 4 | { 5 | asm nothrow @safe @nogc 6 | { 7 | xor RAX, RAX; 8 | } 9 | asm 10 | { 11 | mov EAX, 10u; 12 | mov RAX, 10UL; 13 | mov RAX, a; 14 | ret; 15 | mov RAX, a[100]; 16 | mov RAX, [a + 100]; 17 | mov RAX, a ? b : c; 18 | align 100; 19 | align whatever; 20 | label: 21 | mov RAX, RCX; 22 | db "test"; 23 | mov RAX, 100; 24 | mov RAX, 10.0f; 25 | mov RAX, 10.0; 26 | mov ST(0), 1; 27 | add near ptr [EAX], 3; 28 | add byte ptr [EAX], 3; 29 | mov RAX, a.b.c; 30 | mov RAX, ~a; 31 | mov RAX, !a; 32 | mov RAX, -a; 33 | mov RAX, +a; 34 | mov RAX, offsetof a; 35 | mov EAX, FS:4; 36 | mov EAX, FS:CL; 37 | push dword ptr FS:[0]; 38 | jge short L_largepositive; 39 | lea EDX,[ECX][ECX*8]; 40 | in AL,6; 41 | out AL,6; 42 | int 3; 43 | } 44 | asm 45 | { 46 | align 4 ; 47 | LABEL: ; 48 | ; 49 | 50 | mov EAX, this; 51 | mov ECX, __LOCAL_SIZE; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/pass_files/attributes.d: -------------------------------------------------------------------------------- 1 | extern int d; 2 | @uda int e; 3 | @("uda") int f; 4 | @uda(42) int g; 5 | @uda() int h; 6 | @uda!() int i; 7 | @uda!(int) int j; 8 | private int k; 9 | public int l; 10 | protected int m; 11 | shared int n; 12 | export int o; 13 | pragma(whatever) int p; 14 | @uda: 15 | public: 16 | int q; 17 | deprecated double r; 18 | deprecated("NEVAR USE THIS") double s; 19 | deprecated("NEVAR USE THIS" ~ " IT AM BAD") double s; 20 | @templateName!int(123) int t; 21 | @templateName(123) int u; 22 | extern(System) int v; 23 | align int a; 24 | align(8) int b; 25 | align(8 + c) int c; 26 | -------------------------------------------------------------------------------- /test/pass_files/auto_declarations.d: -------------------------------------------------------------------------------- 1 | package(std) 2 | ref intersect() 3 | { 4 | } 5 | 6 | package(std) 7 | auto intersect() 8 | { 9 | } 10 | 11 | package(std) 12 | const intersect() 13 | { 14 | } 15 | public static immutable ctRegex(alias pattern, alias flags=[]) = ctRegexImpl!(pattern, flags).nr; 16 | struct S(T) { static T t = 0; } 17 | immutable a(A) = S!A.t, b(B) = S!B.t; 18 | const c(C) = S!C.t, d = S!int.t; 19 | -------------------------------------------------------------------------------- /test/pass_files/bitfields.d: -------------------------------------------------------------------------------- 1 | struct B 2 | { 3 | int x:3, y:2; 4 | } 5 | -------------------------------------------------------------------------------- /test/pass_files/body_as_ident.d: -------------------------------------------------------------------------------- 1 | void body() 2 | in 3 | { 4 | } 5 | body 6 | { 7 | Corpulence body; 8 | alias b = body; 9 | } 10 | 11 | struct Foo(Body) 12 | { 13 | static if (is(Body)){} 14 | } 15 | 16 | enum Body; 17 | 18 | @Body void foo(); 19 | -------------------------------------------------------------------------------- /test/pass_files/casts.d: -------------------------------------------------------------------------------- 1 | auto a = cast() b; 2 | auto a = cast(const) b; 3 | auto a = cast(const shared) b; 4 | auto a = cast(immutable) b; 5 | auto a = cast(inout) b; 6 | auto a = cast(inout shared) b; 7 | auto a = cast(shared) b; 8 | auto a = cast(shared const) b; 9 | auto a = cast(shared inout) b; 10 | auto a = cast(shared inout int*) b; 11 | auto a = cast(int) b; 12 | -------------------------------------------------------------------------------- /test/pass_files/classes.d: -------------------------------------------------------------------------------- 1 | class A; 2 | class B {} 3 | class C : B {} 4 | class C : TList[0] {} 5 | class C : TList[0].TList[0].Prop {} 6 | class D(T) if (Z) : B {} 7 | class E(T) : B if (Z) {} 8 | class F(T); 9 | class G(T) if (Z); 10 | class H : public A {} 11 | class H : typeof(A).B {} 12 | class I { int x; alias y = this.x; } 13 | class J : K { int x; alias y = super.x; } 14 | -------------------------------------------------------------------------------- /test/pass_files/compiler_2_104_0.d: -------------------------------------------------------------------------------- 1 | @int void f(); 2 | 3 | @"my test" unittest 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /test/pass_files/constructors_destructors.d: -------------------------------------------------------------------------------- 1 | static this() {} 2 | shared static this() {} 3 | static ~this() {} 4 | shared static ~this() {} 5 | 6 | static this() @system {} 7 | shared static this() @system {} 8 | static ~this() @system {} 9 | shared static ~this() @system {} 10 | 11 | static this(); 12 | shared static this(); 13 | static ~this(); 14 | shared static ~this(); 15 | 16 | static this() @system; 17 | shared static this() @system; 18 | static ~this() @system; 19 | shared static ~this() @system; 20 | 21 | struct SomeStruct 22 | { 23 | @disable this(); 24 | shared this() {} 25 | this() {} 26 | this(int a) {} 27 | ~this() {} 28 | shared ~this() {} 29 | } 30 | -------------------------------------------------------------------------------- /test/pass_files/crazy1.d: -------------------------------------------------------------------------------- 1 | package mixin template phone() if (new class ulong { 2 | import omg = ouchMyFinger.pineapple; 3 | } 4 | & knife!__DATE__(phone is +33890, 'G', raspberry !is ~76988.5, cast (shared const) -import (assert ('q'))) &= 's' || raspberry) 5 | { 6 | import phone = aaargh.grape.aaargh : ouchMyFinger; 7 | unittest 8 | { 9 | } 10 | __gshared static ~this() 11 | in 12 | { 13 | asm 14 | { 15 | } 16 | 17 | } 18 | out (aaargh) 19 | { 20 | } 21 | body 22 | { 23 | immutable uint(assert (--32106[shared(creal).apple], omg)); 24 | } 25 | } 26 | 27 | alias eggplant() = const creal; 28 | enum pineapple() = 100; 29 | enum remote(boom)(...) {} 30 | 31 | align tape()(bool* boom...) 32 | { 33 | } 34 | 35 | debug: 36 | version = 20571; 37 | 38 | enum apple(...) @carton 39 | { 40 | } 41 | 42 | creal[] eggplant = { 43 | return; 44 | } 45 | ; 46 | 47 | immutable drywall.orange plastic = { 48 | __VENDOR__; 49 | } 50 | ; 51 | 52 | synchronized gosh(omg..., boom)() 53 | { 54 | } 55 | 56 | enum phone(alias shared short acrylic : immutable ireal* = 78419) = 'P'; 57 | 58 | unittest 59 | { 60 | version (53056) 61 | union {} 62 | else 63 | gosh %= 'A'; 64 | } 65 | 66 | @carton template nanny_ogg(eggplant, orange) if (33586.8 !in (uint).knife) 67 | { 68 | } 69 | 70 | override scope class discworld(alias angelic.cocaine.items portal) 71 | { 72 | } 73 | 74 | shared static ~this() 75 | { 76 | synchronized 77 | if (auto boom = knife) 78 | unittest 79 | { 80 | } 81 | 82 | } 83 | 84 | abstract alias aaargh(alias grape knife : byte = &52133.9) = double; 85 | 86 | unittest 87 | { 88 | version (88876) 89 | default: 90 | synchronized invariant 91 | { 92 | switch ("migrane" ^^ new class ubyte { 93 | enum : ireal 94 | { 95 | zelda = 4488, carton = delete 27010.9 96 | } 97 | enum discworld; 98 | }) 99 | remote == --'K'; 100 | } 101 | else 102 | static plastic = void, slash = {}; 103 | 104 | } 105 | 106 | package class acrylic : cfloat 107 | { 108 | override this(this omg : .pineapple!85919.grape = ulong)(ulong[&34679 = knife .. 48093] emerge..., ...)shared 109 | { 110 | asm 111 | { 112 | ds word 79944 < ~ + + byte ptr word ptr 67048.8 + 13589.6 & orange.knife.items >= ~real ptr zelda >= angelic ^ 50597.6; 113 | ds [remote.drywall ? + SPL : tape : 0]; 114 | } 115 | } 116 | } 117 | 118 | deprecated ("apple") unittest 119 | { 120 | debug: 121 | union {} 122 | } 123 | 124 | unittest 125 | { 126 | const(knife!__DATE__.grape).boom == 'N'; 127 | const(int).init == 0; 128 | } 129 | 130 | unittest 131 | { 132 | inout .phone!__VENDOR__ remote(wchar ...); 133 | } 134 | 135 | unittest 136 | { 137 | foreach ( 138 | shared typeof (return).raspberry.apple!__FILE__* omg, shared discworld; 139 | 'W' ? *nanny_ogg, 89077 : 'D' ? 46910[emerge, 93690.9, 'f'] : ++"plastic") 140 | (ubyte).slash--.ouchMyFinger != *"zelda" >>>= ++zelda; 141 | } 142 | 143 | unittest 144 | { 145 | synchronized slash(shared creal acrylic : *49515)() if (cast (shared uint) "pineapple") 146 | { 147 | } 148 | 149 | do 150 | switch (__traits(gosh, pineapple) !in 84501) 151 | default: 152 | struct acrylic 153 | { 154 | auto ouchMyFinger = 'F', discworld = void; 155 | } 156 | while (__VENDOR__); 157 | } 158 | debug phone.tape knife = { immutable freedom = void, aaargh = void; }; 159 | 160 | unittest 161 | { 162 | foreach_reverse (shared .gosh.eggplant!aaargh.discworld angelic; 163 | new real[*__TIME__ !is ~8633][__DATE__]) 164 | { 165 | void plastic = void; 166 | } 167 | } 168 | 169 | 170 | -------------------------------------------------------------------------------- /test/pass_files/declarations.d: -------------------------------------------------------------------------------- 1 | deprecated("this code is older than the dinosaurs") module declarations; 2 | int a; 3 | int[] a; 4 | int[string] a; 5 | int a, b; 6 | int a = .b.c; 7 | a.b c = d; 8 | .a.b c = d; 9 | typeof(a) c = d; 10 | typeof(a).b c = d; 11 | align int a; 12 | align(8) int a; 13 | align(8) align int a; 14 | int[] a = []; 15 | auto a = [1, 2, 3]; 16 | auto a = [a:1, b:2, c:3]; 17 | auto a = b, c = d; 18 | static if (true) 19 | int a; 20 | else 21 | int b; 22 | 23 | debug void foo(); 24 | debug(something) void foo(); 25 | debug(100) void foo(); 26 | debug = 101; 27 | debug = identifier; 28 | 29 | version(AArch64) enum x = 100; 30 | version = coverage; 31 | 32 | static if (true): 33 | mixin ("int a;"); 34 | mixin something; 35 | mixin something!A; 36 | mixin duff!(i, j, delegate { foo13(i); }); 37 | mixin typeof(something!A).x; 38 | template mix(){ 39 | int x; 40 | } 41 | mixin .mix; 42 | __vector(int[4]) intVector; 43 | ; 44 | 45 | enum a = 1; 46 | SomeStruct a = { a : 10, b : 20 }; 47 | int[a .. b] c; 48 | int function(int) a; 49 | int function(int) const a; 50 | int delegate(int) a; 51 | int a = typeid(int).alignof; 52 | int a = typeid(10).alignof; 53 | int a = (int).sizeof; 54 | enum string STRING_CONSTANT = "abc"; 55 | Size[][] minSizes = new Size[][](cols, rows); 56 | version(StdDdoc) 57 | { 58 | struct DirEntry 59 | { 60 | version (Windows) 61 | { 62 | } 63 | else version (Posix) 64 | { 65 | private this(string path); 66 | } 67 | } 68 | } 69 | idouble a = 4Li; 70 | idouble a = 4i; 71 | ifloat a = 4fi; 72 | ifloat a = 4Fi; 73 | 74 | static foreach (n; ['a', 'b', 'c']) 75 | { 76 | mixin("char " ~ n ~ ";"); 77 | } 78 | 79 | static foreach_reverse (i; '0' .. '5') 80 | { 81 | mixin("int _" ~ i ~ ";"); 82 | } 83 | 84 | static foreach (enum i, alias T; AliasSeq!(int, bool)) 85 | { 86 | T a = i; 87 | } 88 | 89 | struct Foo(T); 90 | union Foo(T); 91 | class Foo(T); 92 | interface Foo(T); 93 | 94 | mixin("auto a = 1 + ", 1, ";"); 95 | 96 | __traits(getMember, Foo, "Bar") fooBar; 97 | const(__traits(getMember, Foo, "Bar")) fooBar; 98 | alias FooBar = __traits(getMember, Foo, "Bar"); 99 | const fooBar = cast(__traits(getMember, Foo, "Bar")) __traits(getMember, Foo, "bar"); 100 | int twice(int x) = 2 * x; 101 | const int twice(int x) = 2 * x; 102 | immutable int twice(int x) = 2 * x; 103 | 104 | void foo() 105 | { 106 | __traits(getMember, Foo, "Bar") fooBar; 107 | immutable int twice(int x) = 2 * x; 108 | } 109 | 110 | alias Mt1 = mixin("foo", ".", "bar"); 111 | alias Mt2 = mixin("int"); 112 | const(mixin("int")) globalInt1; 113 | shared const(mixin("int")) globalInt2; 114 | const(mixin("int"))[][] globalIntMtx1; 115 | 116 | -------------------------------------------------------------------------------- /test/pass_files/dip1000.d: -------------------------------------------------------------------------------- 1 | class Foo 2 | { 3 | @property auto front() scope 4 | {return new Foo;} 5 | } -------------------------------------------------------------------------------- /test/pass_files/dip25.d: -------------------------------------------------------------------------------- 1 | @safe struct S { 2 | static int a; 3 | int b; 4 | ref int fun() { return a; } // fine, callers assume infinite lifetime 5 | ref int gun() { return b; } // ERROR! Cannot return a direct member 6 | ref int hun() return { return b; } // fine, result is scoped within this 7 | @safe ref int fun(ref return float x); 8 | } 9 | @safe struct S { 10 | private int x; 11 | ref int get() return { return x; } // should work, see next section 12 | } 13 | -------------------------------------------------------------------------------- /test/pass_files/do_body.d: -------------------------------------------------------------------------------- 1 | void foo(int x) 2 | in 3 | { 4 | assert(x > 0); 5 | } 6 | do 7 | { 8 | 9 | } 10 | 11 | int foo2(int x) 12 | in 13 | { 14 | assert(x > 0); 15 | } 16 | out(r) 17 | { 18 | assert(r > 0); 19 | } 20 | do 21 | { 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /test/pass_files/enums.d: -------------------------------------------------------------------------------- 1 | enum A { a } 2 | enum B { a, b, c } 3 | enum C { a = 10, b = 20, d = int.init, e } 4 | enum D : int { a } 5 | enum : int { a } 6 | enum { int b = 100 } 7 | enum 8 | { 9 | /// doc for a 10 | a, 11 | b, // doc for b 12 | c // doc for c 13 | } 14 | 15 | enum E 16 | { 17 | @disable member, 18 | @A @B deprecated("meep") member, 19 | deprecated("meep") member 20 | } 21 | 22 | // https://github.com/dlang-community/libdparse/issues/390 23 | enum F; 24 | enum G : long; 25 | -------------------------------------------------------------------------------- /test/pass_files/exceptions.d: -------------------------------------------------------------------------------- 1 | void foo() 2 | { 3 | try { 4 | doSomething(); 5 | } catch { 6 | doSomethingElse(); 7 | } 8 | 9 | try { 10 | doSomething(); 11 | } catch (Exception) { 12 | doSomethingElse(); 13 | } 14 | 15 | try { 16 | doSomething(); 17 | } catch (Exception e) { 18 | doSomethingElse(); 19 | } 20 | 21 | try 22 | doSomething(); 23 | finally 24 | doSomethingElse(); 25 | } 26 | -------------------------------------------------------------------------------- /test/pass_files/expressions.d: -------------------------------------------------------------------------------- 1 | auto a = 1 * 2; 2 | auto a = 1 + 2; 3 | auto a = 1 % 2; 4 | auto a = 1 || 2; 5 | auto a = 1 && 2; 6 | auto a = 1 << 2; 7 | auto a = 1 >> 2; 8 | auto a = 1 < 2; 9 | auto a = 1 > 2; 10 | auto a = 1 >>> 2; 11 | auto a = cast() a; 12 | auto a = cast(int) a; 13 | auto a = a || b || c && d; 14 | auto a = b++; 15 | auto a = ++b; 16 | auto a = b[10]; 17 | auto a = b[10 .. $]; 18 | auto a = b[$]; 19 | auto a = b[1 .. 2]; 20 | auto a = [10] ~ [20]; 21 | auto a = [[10], [20]]; 22 | auto a = [{1}]; 23 | auto a = "string"; 24 | auto a = "string" "concatenated"; 25 | auto a = void.sizeof; 26 | auto a = int.mangleof; 27 | auto a = .x; 28 | auto a = const(int).mangleof; 29 | auto a = function(int a) { return a; }; 30 | auto a = delegate(int a) { return a; }; 31 | auto a = function() { return 100; }; 32 | auto a = delegate() { return 100; }; 33 | auto a = function { return 100; }; 34 | auto a = delegate { return 100; }; 35 | auto a = mixin("1 + ", 1); 36 | enum a = is(_execinfo == module); 37 | enum a = is(_execinfo == package); 38 | enum a = is(_execinfo == __vector); 39 | 40 | void foo() 41 | { 42 | a = b; 43 | a >>>= b; 44 | a >>= b; 45 | a <<= b; 46 | a += b; 47 | a -= b; 48 | a *= b; 49 | a %= b; 50 | a &= b; 51 | a /= b; 52 | a |= b; 53 | a ^^= b; 54 | a ^= b; 55 | a ~= b; 56 | auto a = b <= c; 57 | auto a = b == c; 58 | auto a = b is c; 59 | auto a = b !is c; 60 | auto a = b in c; 61 | auto a = b !in c; 62 | delete a; 63 | auto a = new b; 64 | auto a = new b(); 65 | auto a = new b(); 66 | auto a = const c(); 67 | auto a = const(c)(); 68 | auto a = import("foo.txt"); 69 | auto a = b([a:1, b:2]); 70 | auto a = b((c)); 71 | auto a = b((a) => a * 10_000); 72 | auto a = b((int a) {return a + 2;}); 73 | enum a = typeof(b); 74 | enum a = typeid(b); 75 | enum a = is(_execinfo == module); 76 | enum a = is(_execinfo == package); 77 | enum a = is(_execinfo == __vector); 78 | enum a = is(a : b); 79 | enum a = __traits(classInstanceSize, b); 80 | enum a = mixin("something"); 81 | enum a = mixin(nothing); 82 | pragma(startaddress, b); 83 | auto a = new int[100]; 84 | auto a = new class {}; 85 | auto a = new (x, y, z) class {}; 86 | auto a = new (x, y, z) class (c, d) {}; 87 | auto a = new (x, y, z) class A, B {}; 88 | assert (a); 89 | assert (a,); 90 | assert (a, "b"); 91 | assert (a, "b",); 92 | assert((lower & upper).empty); 93 | auto ptr = cast(const shared int*) &val; 94 | (cast(Node*) data.ptr).next = null; 95 | int[int[string]] aa5 = [["a":1]:2, ["b":3]:4]; 96 | if (is(typeof(a) == __vector)){} 97 | Hand h = body.hands.left; 98 | } 99 | -------------------------------------------------------------------------------- /test/pass_files/function_aliases.d: -------------------------------------------------------------------------------- 1 | alias aaa = a => a * 2; 2 | alias bbb = (a) => a * 2; 3 | alias ccc = (a) nothrow => a * 2; 4 | alias ddd = function void(a) => a * 2; 5 | alias eee = function void(a) nothrow => a * 2; 6 | alias fff = function void(a) { return 100; }; 7 | alias ggg = function void(a) in { } out { } body { return 100; }; 8 | alias hhh = delegate void(a) => a * 2; 9 | alias iii = delegate void(a) nothrow => a * 2; 10 | alias jjj = delegate void(a) { return 100; }; 11 | alias kkk = delegate void(a) in { } out { } body { return 100; }; 12 | alias lll = { return 100; }; 13 | alias mmm = () { return 100; }; 14 | alias nnn = () @nogc { return 100; }; 15 | alias fun = (@(1) inout @(1) a) => {}; 16 | 17 | // The following is valid according to D's grammar, but rejected by dmd 18 | /+unittest 19 | { 20 | alias abc = in { } out { } body { return 100; }; 21 | alias abc = () in{ } out { } body { return 100; }; 22 | alias abc = () @nogc in{ } out { } body { return 100; }; 23 | doThings(in { } out { } body { return 100; }); 24 | auto a = in { } out { } body { return 100; }; 25 | } 26 | +/ 27 | -------------------------------------------------------------------------------- /test/pass_files/function_declarations.d: -------------------------------------------------------------------------------- 1 | void foo(int a ...) {} 2 | void foo(...) in {} body {} 3 | void foo(double) in {} out {} body {} 4 | void foo(const int) in {} out(a) {} body {} 5 | void foo(lazy real) out(a) {} in {} body {} 6 | 7 | void foo(T)(); 8 | void foo(T)() in {} body {} 9 | void foo(T)() in {} out {} body {} 10 | void foo(T)() in {} out(a) {} body {} 11 | void foo(T)() out(a) {} in {} body {} 12 | 13 | void foo(T)(immutable(T) t) if (something) {} 14 | void foo(T)(in int great) if (something) in {} body {} 15 | void foo(T)(final void* param) if (something) in {} out {} body {} 16 | void foo(T)(char c = 'a') if (something) in {} out(a) {} body {} 17 | void foo(T)(char s[]) if (something) out(a) {} in {} body {} 18 | 19 | auto foo(int ...) { return 1; } 20 | auto ref foo() { return 1; } 21 | ref auto foo() { return 1; } 22 | const foo() { return 1; } 23 | auto inout foo() { return 1; } 24 | inout auto foo() { return 1; } 25 | 26 | int foo() pure { return 1; } 27 | int foo() const { return 1; } 28 | int foo() inout { return 1; } 29 | int foo() immutable { return 1; } 30 | int foo() shared { return 1; } 31 | int foo() const @safe { return 1; } 32 | int foo() const @safe nothrow { return 1; } 33 | 34 | auto a = function int (int a) { return a * 2; }; 35 | auto a = function int (int a) pure { return a * 2; }; 36 | auto a = function int (int a) @whatever { return a * 2; }; 37 | auto a = function (int a) => a * 2; 38 | auto a = (int a) => a * 2; 39 | auto a = function int (int a) => a * 2; 40 | void bar() 41 | { 42 | doStuff(function int(int a) { return a / 2; }); 43 | doStuff(function int(int a) body { return a / 2; }); 44 | doStuff(function int(int a) in { assert (a > 10); } body { return a / 2; }); 45 | } 46 | 47 | void foo(@(1) const @UDA int p); 48 | 49 | void cVarArg(int, ...); 50 | enum bool isInputRange = is(typeof((inout int = 0){})); 51 | auto a = b => b * 2; 52 | 53 | int typesafeVarArg(int a = 1, int[] rest = [] ...) { return 1; } 54 | 55 | void foo() in(true) do {} 56 | void foo() in(true,) do {} 57 | void foo() in(true,"false") do {} 58 | void foo() in(true,"false",) do {} 59 | void foo() in(true) in(true) do {} 60 | void foo() in(true) {} 61 | void foo() in(true) in(true) {} 62 | void foo() in(true) in {} do {} 63 | void foo() in {} in {} do {} 64 | 65 | void foo() out(;true) do {} 66 | void foo() out(;true, "false") do {} 67 | void foo() out(;true, "false",) do {} 68 | void foo() out(;true) out(;true) do {} 69 | void foo() out(;true,) {} 70 | void foo() out(;true) out(;true) {} 71 | void foo() out(;true) out {} do {} 72 | void foo() out {} out {} do {} 73 | 74 | void foo() in(true) out(;true) do {} 75 | void foo() out(; true) in(true) do {} 76 | void foo() out(result; true) in(true) do {} 77 | void foo() in(true) out(result; true) in(true) {} 78 | void foo() in(true) out(result; true) in(true); 79 | -------------------------------------------------------------------------------- /test/pass_files/function_literals.d: -------------------------------------------------------------------------------- 1 | void main() 2 | { 3 | auto foo1() pure immutable 4 | { 5 | return 0; 6 | } 7 | 8 | auto foo2() pure const 9 | { 10 | return 0; 11 | } 12 | } 13 | 14 | void main() 15 | { 16 | auto dg1 = () pure immutable 17 | { 18 | return 0; 19 | }; 20 | auto dg2 = () pure const 21 | { 22 | return 0; 23 | }; 24 | } 25 | 26 | void testRefReturns() 27 | { 28 | alias flit11 = function ref int () { return a; }; 29 | alias flit12 = delegate ref int () { return a; }; 30 | alias flit13 = ref () {return a; }; 31 | 32 | auto aflit11 = function ref int () { return a; }; 33 | auto aflit12 = delegate ref int () { return a; }; 34 | auto aflit13 = ref () {return a; }; 35 | 36 | alias flit21 = function ref int () => a; 37 | alias flit22 = delegate ref int () => a; 38 | alias flit23 = ref () => a; 39 | 40 | auto aflit21 = function ref int () => a; 41 | auto aflit22 = delegate ref int () => a; 42 | auto aflit23 = ref () => a; 43 | 44 | (ref () => x )() = 1; 45 | assert((funa16271!( ref (ref a) => a)(x) += 1) == 7 ); 46 | assert((funa16271!(function ref (ref a) => a)(x) += 1) == 8 ); 47 | assert((funa16271!(function ref int(ref a) => a)(x) += 1) == 9 ); 48 | assert((funa16271!(delegate ref (ref a) => a)(x) += 1) == 10); 49 | assert((funa16271!(delegate ref int(ref a) => a)(x) += 1) == 11); 50 | } 51 | -------------------------------------------------------------------------------- /test/pass_files/helloworld.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | void main() 4 | { 5 | writeln("Hello World!"); 6 | } 7 | -------------------------------------------------------------------------------- /test/pass_files/ifConditions.d: -------------------------------------------------------------------------------- 1 | void foo() 2 | { 3 | if (const(Type)* data = call()){} 4 | if (const a = call()){} 5 | if (const shared a = call()){} 6 | if (auto a = call()){} 7 | if (immutable shared(Type) a = call()){} 8 | if (a) {} 9 | if (T t = T.init) {} 10 | if (T!0 t = T.init) {} 11 | if (true) {} 12 | } 13 | 14 | void main() 15 | { 16 | if ((a && b) || c) {} 17 | if (a && b || c) {} 18 | } 19 | -------------------------------------------------------------------------------- /test/pass_files/imports.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | import foo, bar; 3 | import io = std.stdio; 4 | import std.stdio: writefln, foo = writef; 5 | import io = std.stdio : foo = writefln; 6 | import foo, bar, baz; 7 | import core.stdc.stdio, std.string : KeepTerminator; 8 | -------------------------------------------------------------------------------- /test/pass_files/innerclass.d: -------------------------------------------------------------------------------- 1 | class Foo 2 | { 3 | class Bar 4 | { 5 | class Foobar { void method() {} } 6 | } 7 | 8 | public Bar bar; 9 | } 10 | 11 | void main () 12 | { 13 | auto foo = new Foo; 14 | Foo.Bar bar = foo.new Bar; 15 | auto foo_bar = foo.new Bar; 16 | auto foobar = foo.bar.new Foobar; 17 | auto foobar = (foo.bar).new Foobar; 18 | (foo.new Bar).method; 19 | } 20 | -------------------------------------------------------------------------------- /test/pass_files/interfaces.d: -------------------------------------------------------------------------------- 1 | interface A; 2 | interface B {} 3 | interface C : B {} 4 | interface D(T) if (Z) : B {} 5 | interface E(T) : B if (Z) {} 6 | interface F(T); 7 | interface G(T) if (Z); 8 | interface H{ void hat() in {} } 9 | -------------------------------------------------------------------------------- /test/pass_files/issue00114.d: -------------------------------------------------------------------------------- 1 | void main () 2 | { 3 | int a, b; 4 | int[string] aa = [ "hello" : a = a, "world" : b = b ]; 5 | } 6 | -------------------------------------------------------------------------------- /test/pass_files/issue0042.d: -------------------------------------------------------------------------------- 1 | /// Ditto 2 | template octal(alias s) 3 | if (isIntegral!(typeof(s))) 4 | { 5 | enum auto octal = octal!(typeof(s), to!string(s)); 6 | } 7 | -------------------------------------------------------------------------------- /test/pass_files/issue0047.d: -------------------------------------------------------------------------------- 1 | unittest 2 | { 3 | { 4 | { 5 | if (1) 6 | { 7 | } 8 | else 9 | { 10 | x; 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/pass_files/issue0067.d: -------------------------------------------------------------------------------- 1 | auto a = b[1, 2..3]; 2 | auto a = b[2..3]; 3 | -------------------------------------------------------------------------------- /test/pass_files/issue0070.d: -------------------------------------------------------------------------------- 1 | version (Foo) 2 | { 3 | version(D_Version2) 4 | { 5 | public import core.memory; 6 | } 7 | else: 8 | } 9 | 10 | version (Bar) 11 | int foo (); 12 | else: 13 | int foo(int); 14 | -------------------------------------------------------------------------------- /test/pass_files/issue0095.d: -------------------------------------------------------------------------------- 1 | enum A 2 | { 3 | a, 4 | } 5 | -------------------------------------------------------------------------------- /test/pass_files/issue0106.d: -------------------------------------------------------------------------------- 1 | void foo1(){if (const a = 0){}} 2 | void foo2(){if (const shared a = 0){}} 3 | void foo3(){if (immutable shared a = 0){}} -------------------------------------------------------------------------------- /test/pass_files/issue0156.d: -------------------------------------------------------------------------------- 1 | module a; 2 | 3 | void foo() 4 | { 5 | const a = ["a":"b"]; 6 | const a = [1:1]; 7 | const a = [[1]:"a"]; 8 | const a = [[1]:[1]]; 9 | } -------------------------------------------------------------------------------- /test/pass_files/issue0158.d: -------------------------------------------------------------------------------- 1 | T[1].Y y; 2 | T.T y; 3 | T.T y; 4 | T[] y; 5 | T* y; 6 | T[0].Y y; 7 | T.T[] y; 8 | T.T[8] y; 9 | T.T[8].T y; 10 | .T!"af" taf; 11 | .T!0[] t; 12 | T!(0)[] t; 13 | T!(0)[dim] t; 14 | alias SubList = T[lo.lo .. hi.hi]; 15 | -------------------------------------------------------------------------------- /test/pass_files/issue0291.d: -------------------------------------------------------------------------------- 1 | module issue0291; 2 | 3 | void foo() 4 | in { } // "so you need do{}?" 5 | out (; true) // No you don't, but libdparse thinks you still do. 6 | { } 7 | -------------------------------------------------------------------------------- /test/pass_files/issue0413.d: -------------------------------------------------------------------------------- 1 | @("") export auto a() { } 2 | -------------------------------------------------------------------------------- /test/pass_files/lexical.d: -------------------------------------------------------------------------------- 1 | #!/usr/bin/rdmd 2 | 3 | /// single line comment 4 | /* StarSlash */ 5 | /***** ******/ 6 | /** 7 | * 123 8 | */ 9 | /+ StarPlus +/ 10 | /++ 11 | + 456 12 | +/ 13 | /+ StarPlus /+ Nested +/ +/ 14 | 15 | #line 10 "whatever.d" 16 | 17 | auto a = 1u; 18 | auto a = 1U; 19 | auto a = 1uL; 20 | auto a = 1UL; 21 | auto a = 1L; 22 | auto a = 1Lu; 23 | auto a = 1LU; 24 | auto a = 1i; 25 | auto a = 1.0; 26 | auto a = 1.0L; 27 | auto a = 1e10; 28 | auto a = 1.e10; 29 | auto a = 1e1L; 30 | auto a = 1e1f; 31 | auto a = 1e1F; 32 | auto a = 1e1i; 33 | auto a = 1e+1; 34 | auto a = 1e+_1; 35 | auto a = 1e-1; 36 | auto a = 1e-_1; 37 | auto a = b[1..2]; 38 | auto a = 0x1p10; 39 | auto a = 0x1_2u; 40 | auto a = 0x1_2.fi; 41 | auto a = 0x1_2L; 42 | auto a = 0b1_0; 43 | auto a = 0b1u; 44 | auto a = 0b1L; 45 | auto a = 0b1UL; 46 | auto a = 0b1U; 47 | auto a = 0b1LU; 48 | string a = .1; 49 | string a = 'a'; 50 | string a = '\0'; 51 | string a = '\01'; 52 | string a = '\012'; 53 | string a = '\123'; 54 | string a = '\u12aA'; 55 | string a = '\U12aA5678'; 56 | string a = "\"\?\\\0\a\b\f\n\r\t\v\xff\1\u1234\UaA345678"; 57 | string a = "str"c; 58 | string a = "str"d; 59 | string a = "str"w; 60 | string a = `str`; 61 | string a = `str''""`; 62 | string a = r"\a\b\c"c; 63 | string a = x"12 34 56 78 90 ab cd ef AB CD EF"; 64 | string a = x"AB"c; 65 | string a = x"AB"d; 66 | string a = x"AB"w; 67 | string a = q{int a;}; 68 | string a = q{{int a;}}; 69 | string a = q{int a;}c; 70 | string a = q{int a;}d; 71 | string a = q{int a;}w; 72 | string a = q"( " )"; 73 | string a = q"( (") )"; 74 | string a = q"< " >"; 75 | string a = q"< <"> >"; 76 | string a = q"{ " }"; 77 | string a = q"{ {"} }"; 78 | string a = q"[ " ]"; 79 | string a = q"[ ["] ]"; 80 | string a = q"IDENTIFIER 81 | " 82 | IDENTIFIER"; 83 | 84 | -------------------------------------------------------------------------------- /test/pass_files/linkageAttributes.d: -------------------------------------------------------------------------------- 1 | private: 2 | extern(C) int a; 3 | extern(D) int b; 4 | extern(Windows) int c; 5 | extern(Pascal) int d; 6 | extern(System) int e; 7 | extern(Objective-C) int f ; 8 | extern(C++) int g; 9 | extern(C++, a.b.c) int h; 10 | extern(C++, struct) int i; 11 | extern(C++, class) int j; 12 | 13 | extern(C++, "abc") int k; 14 | extern(C++, "a", "b", "c") int k; 15 | extern(C++, (abc)) int m; 16 | extern(C++, ("abc")) int n; 17 | -------------------------------------------------------------------------------- /test/pass_files/misc_coverage.d: -------------------------------------------------------------------------------- 1 | module misc_coverage; 2 | 3 | uint a[][]; 4 | 5 | alias T = deprecated U; 6 | 7 | alias T = extern const U; 8 | -------------------------------------------------------------------------------- /test/pass_files/mixin_types.d: -------------------------------------------------------------------------------- 1 | mixin("int") variableName; 2 | void foo(mixin("int") arg) { 3 | mixin("int") localVar; 4 | } 5 | struct S { 6 | mixin("int") foo; 7 | } 8 | -------------------------------------------------------------------------------- /test/pass_files/moduleDec1.d: -------------------------------------------------------------------------------- 1 | deprecated module moduleDec1; 2 | -------------------------------------------------------------------------------- /test/pass_files/moduleDec2.d: -------------------------------------------------------------------------------- 1 | deprecated("Don't use this") module moduleDec2; 2 | -------------------------------------------------------------------------------- /test/pass_files/moduleDec3.d: -------------------------------------------------------------------------------- 1 | @something module moduleDec3; 2 | -------------------------------------------------------------------------------- /test/pass_files/moduleDec4.d: -------------------------------------------------------------------------------- 1 | @something deprecated module moduleDec3; 2 | -------------------------------------------------------------------------------- /test/pass_files/much_recursion.d: -------------------------------------------------------------------------------- 1 | template Foo() { 2 | static if (a) { 3 | } else static if (b) { 4 | } else static if (b) { 5 | } else static if (b) { 6 | } else static if (b) { 7 | } else static if (b) { 8 | } else static if (b) { 9 | } else static if (b) { 10 | } else static if (b) { 11 | } else static if (b) { 12 | } else static if (b) { 13 | } else static if (b) { 14 | } else static if (b) { 15 | } else static if (b) { 16 | } else static if (b) { 17 | } else static if (b) { 18 | } else static if (b) { 19 | } else static if (b) { 20 | } else static if (b) { 21 | } else static if (b) { 22 | } else static if (b) { 23 | } else static if (b) { 24 | } else static if (b) { 25 | } else static if (b) { 26 | } else static if (b) { 27 | } else static if (b) { 28 | } else static if (b) { 29 | } else static if (b) { 30 | } else static if (b) { 31 | } else static if (b) { 32 | } else static if (b) { 33 | } else static if (b) { 34 | } else static if (b) { 35 | } else static if (b) { 36 | } else static if (b) { 37 | } else static if (b) { 38 | } else static if (b) { 39 | } else static if (b) { 40 | } else static if (b) { 41 | } else static if (b) { 42 | } else static if (b) { 43 | } else static if (b) { 44 | } else static if (b) { 45 | } else static if (b) { 46 | } else static if (b) { 47 | } else static if (b) { 48 | } else static if (b) { 49 | } else static if (b) { 50 | } else static if (b) { 51 | } else static if (b) { 52 | } else static if (b) { 53 | } else static if (b) { 54 | } else static if (b) { 55 | } else static if (b) { 56 | } else static if (b) { 57 | } else static if (b) { 58 | } else static if (b) { 59 | } else static if (b) { 60 | } else static if (b) { 61 | } else static if (b) { 62 | } else static if (b) { 63 | } else static if (b) { 64 | } else static if (b) { 65 | } else static if (b) { 66 | } else static if (b) { 67 | } else static if (b) { 68 | } else static if (b) { 69 | } else static if (b) { 70 | } else static if (b) { 71 | } else static if (b) { 72 | } else static if (b) { 73 | } else static if (b) { 74 | } else static if (b) { 75 | } else static if (b) { 76 | } else static if (b) { 77 | } else static if (b) { 78 | } else static if (b) { 79 | } else static if (b) { 80 | } else static if (b) { 81 | } else static if (b) { 82 | } else static if (b) { 83 | } else static if (b) { 84 | } else static if (b) { 85 | } else static if (b) { 86 | } else static if (b) { 87 | } else static if (b) { 88 | } else static if (b) { 89 | } else static if (b) { 90 | } else static if (b) { 91 | } else static if (b) { 92 | } else static if (b) { 93 | } else static if (b) { 94 | } else static if (b) { 95 | } else static if (b) { 96 | } else static if (b) { 97 | } else static if (b) { 98 | } else static if (b) { 99 | } else static if (b) { 100 | } else static if (b) { 101 | } else static if (b) { 102 | } else static if (b) { 103 | } else static if (b) { 104 | } else static if (b) { 105 | } else static if (b) { 106 | } else static if (b) { 107 | } else static if (b) { 108 | } else static if (b) { 109 | } else static if (b) { 110 | } else static if (b) { 111 | } else static if (b) { 112 | } else static if (b) { 113 | } else static if (b) { 114 | } else static if (b) { 115 | } else static if (b) { 116 | } else static if (b) { 117 | } else static if (b) { 118 | } else static if (b) { 119 | } else static if (b) { 120 | } else static if (b) { 121 | } else static if (b) { 122 | } else static if (b) { 123 | } else static if (b) { 124 | } else static if (b) { 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /test/pass_files/named_arguments.d: -------------------------------------------------------------------------------- 1 | void main() 2 | { 3 | a(100); 4 | b(x: 100); 5 | c(x: 100, y: 200); 6 | d!(a: "a", b: "b")("c"); 7 | } 8 | -------------------------------------------------------------------------------- /test/pass_files/pragmas.d: -------------------------------------------------------------------------------- 1 | module a; 2 | 3 | void foo() 4 | { 5 | pragma(bar) baz = 8; 6 | 7 | pragma(bar) 8 | { 9 | baz = 8; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/pass_files/ptrntab.d: -------------------------------------------------------------------------------- 1 | OP[] optab = 2 | [ 3 | { "sym", 0, { null } }, 4 | { "sym", 0, { null } }, 5 | { "sym", 0, { null } }, 6 | { "sym", 0, { null } }, 7 | { "sym", 0, { null } }, 8 | { "sym", 0, { null } }, 9 | { "sym", 0, { null } }, 10 | { "sym", 0, { null } }, 11 | { "sym", 0, { null } }, 12 | { "sym", 0, { null } }, 13 | { "sym", 0, { null } }, 14 | { "sym", 0, { null } }, 15 | { "sym", 0, { null } }, 16 | { "sym", 0, { null } }, 17 | { "sym", 0, { null } }, 18 | { "sym", 0, { null } }, 19 | { "sym", 0, { null } }, 20 | { "sym", 0, { null } }, 21 | { "sym", 0, { null } }, 22 | { "sym", 0, { null } }, 23 | { "sym", 0, { null } }, 24 | { "sym", 0, { null } }, 25 | { "sym", 0, { null } }, 26 | { "sym", 0, { null } }, 27 | { "sym", 0, { null } }, 28 | { "sym", 0, { null } }, 29 | { "sym", 0, { null } }, 30 | { "sym", 0, { null } }, 31 | { "sym", 0, { null } }, 32 | { "sym", 0, { null } }, 33 | { "sym", 0, { null } }, 34 | { "sym", 0, { null } }, 35 | { "sym", 0, { null } }, 36 | { "sym", 0, { null } }, 37 | { "sym", 0, { null } }, 38 | { "sym", 0, { null } }, 39 | { "sym", 0, { null } }, 40 | { "sym", 0, { null } }, 41 | { "sym", 0, { null } }, 42 | { "sym", 0, { null } }, 43 | { "sym", 0, { null } }, 44 | { "sym", 0, { null } }, 45 | { "sym", 0, { null } }, 46 | { "sym", 0, { null } }, 47 | { "sym", 0, { null } }, 48 | { "sym", 0, { null } }, 49 | { "sym", 0, { null } }, 50 | { "sym", 0, { null } }, 51 | { "sym", 0, { null } }, 52 | { "sym", 0, { null } }, 53 | { "sym", 0, { null } }, 54 | { "sym", 0, { null } }, 55 | { "sym", 0, { null } }, 56 | { "sym", 0, { null } }, 57 | { "sym", 0, { null } }, 58 | { "sym", 0, { null } }, 59 | { "sym", 0, { null } }, 60 | { "sym", 0, { null } }, 61 | { "sym", 0, { null } }, 62 | { "sym", 0, { null } }, 63 | { "sym", 0, { null } }, 64 | { "sym", 0, { null } }, 65 | { "sym", 0, { null } }, 66 | { "sym", 0, { null } }, 67 | { "sym", 0, { null } }, 68 | { "sym", 0, { null } }, 69 | { "sym", 0, { null } }, 70 | { "sym", 0, { null } }, 71 | { "sym", 0, { null } }, 72 | { "sym", 0, { null } }, 73 | { "sym", 0, { null } }, 74 | { "sym", 0, { null } }, 75 | { "sym", 0, { null } }, 76 | { "sym", 0, { null } }, 77 | { "sym", 0, { null } }, 78 | { "sym", 0, { null } }, 79 | { "sym", 0, { null } }, 80 | { "sym", 0, { null } }, 81 | { "sym", 0, { null } }, 82 | { "sym", 0, { null } }, 83 | { "sym", 0, { null } }, 84 | { "sym", 0, { null } }, 85 | { "sym", 0, { null } }, 86 | { "sym", 0, { null } }, 87 | { "sym", 0, { null } }, 88 | { "sym", 0, { null } }, 89 | { "sym", 0, { null } }, 90 | { "sym", 0, { null } }, 91 | { "sym", 0, { null } }, 92 | { "sym", 0, { null } }, 93 | { "sym", 0, { null } }, 94 | { "sym", 0, { null } }, 95 | { "sym", 0, { null } }, 96 | { "sym", 0, { null } }, 97 | { "sym", 0, { null } }, 98 | { "sym", 0, { null } }, 99 | { "sym", 0, { null } }, 100 | { "sym", 0, { null } }, 101 | { "sym", 0, { null } }, 102 | { "sym", 0, { null } }, 103 | { "sym", 0, { null } }, 104 | { "sym", 0, { null } }, 105 | { "sym", 0, { null } }, 106 | { "sym", 0, { null } }, 107 | { "sym", 0, { null } }, 108 | { "sym", 0, { null } }, 109 | { "sym", 0, { null } }, 110 | ]; 111 | -------------------------------------------------------------------------------- /test/pass_files/shortenedMethods.d: -------------------------------------------------------------------------------- 1 | // these 2 are equivalent 2 | int foo() { return 1; } 3 | int bar() => 1; 4 | 5 | // https://github.com/dlang/dmd/blob/52844d4b1e9d6714bfd2e535f25a72074a046209/test/compilable/shortened_methods.d 6 | class A { 7 | int _x = 34; 8 | // short syntax works in all contexts 9 | @property x() => _x; 10 | @property x(int v) => _x = v; 11 | 12 | // including with contracts 13 | @property y() in(true) => _x; 14 | 15 | // or other auto returns 16 | auto foo() @safe => assert(0); 17 | 18 | // or normal method defintions 19 | bool isNull() => this is null; 20 | } 21 | 22 | class B : A{ 23 | // short syntax also overrides the same as long syntax 24 | override bool isNull() => this !is null; 25 | } 26 | 27 | static assert((new A).x == 34); 28 | 29 | string test() => "hello"; // works at any scope 30 | 31 | static assert(test() == "hello"); // works normally 32 | static assert(is(typeof(&test) == string function())); // same normal type 33 | 34 | void func() { 35 | int a; 36 | int nested() => a; // and at nested scopes too 37 | } -------------------------------------------------------------------------------- /test/pass_files/single_line_with_decl.d: -------------------------------------------------------------------------------- 1 | module a; 2 | 3 | enum A{a0} 4 | 5 | void foo() 6 | { 7 | with(A) const i = 0; 8 | with(A) 9 | { 10 | const i = 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/pass_files/statements.d: -------------------------------------------------------------------------------- 1 | deprecated void foo() 2 | { 3 | if (a) b(); 4 | if (a) b(); else c(); 5 | if (auto a = i == 10) b(); 6 | if (int i = a < 100) b(); 7 | switch (x) {} 8 | switch (x) { case 10: break; } 9 | switch (x) { case 10: break; default: break; } 10 | label: switch (x) { 11 | case 1: break label; 12 | case 2: goto default; 13 | case 3: goto label2; 14 | case 4: goto case 3; 15 | case 5: .. case 100: break; 16 | } 17 | while (true) { continue; } 18 | while (true) { continue onAndOn; } 19 | label2: while (true) { break label2; } 20 | do nothing(); while(false); 21 | do { nothing(); } while(false); 22 | for (int i = 0; i < 100; i++) {} 23 | for (; i < 100; i++) {} 24 | for (; i < 100;) {} 25 | for (int i = 0; ; i++) {} 26 | foreach (a; b) {} 27 | foreach (int a; b) {} 28 | foreach (a, b; c) {} 29 | foreach (a; c .. d) {} 30 | foreach (ref a; c .. d) {} 31 | foreach (ref const a; c .. d) {} 32 | foreach (const ref a; c .. d) {} 33 | foreach (inout a; c .. d) {} 34 | scope(failure) complain(); 35 | scope(success) celebrate(); 36 | scope(success) {celebrate();} 37 | synchronized (swimming) {} 38 | throw aParty!tonight; 39 | with (great.power) comes(great.responsibility); 40 | final switch (x) {} 41 | version(ARM_HardFloat) 42 | a = b.c; 43 | else 44 | a = c.d; 45 | debug 46 | a = 100; 47 | static if (someCondition) 48 | a = 9; 49 | version(graphviz_debugging) 50 | { 51 | File f = File("graph%04d.dot".format(i), "w"); 52 | tsTree.print(f); 53 | } 54 | 55 | version(Alpha) 56 | { 57 | } 58 | else 59 | { 60 | i++; 61 | } 62 | label: 63 | 64 | 65 | static foreach (n; ['a', 'b', 'c']) 66 | {{ 67 | mixin(n ~ "++;"); 68 | }} 69 | 70 | static foreach_reverse (i; 1 .. 10) 71 | { 72 | assert(i-- > 0); 73 | } 74 | mixin("auto a = 1 +", 1); 75 | } 76 | -------------------------------------------------------------------------------- /test/pass_files/structs.d: -------------------------------------------------------------------------------- 1 | struct S 2 | { 3 | @disable this(this); 4 | this() { this.y++; } 5 | this(int a) { this.y++; } 6 | this() const { this.y++; } 7 | this(T)() { this.y++; } 8 | this(T)() if (U) { this.y++; } 9 | this(this) { this.y++; } 10 | this(this) @whatever { this.y++; } 11 | ~this(); 12 | ~this() const {} 13 | invariant() 14 | { 15 | assert (x == 10); 16 | } 17 | invariant(true); 18 | invariant(true, "false"); 19 | } 20 | struct S; 21 | struct S {} 22 | struct S(T) {} 23 | struct S(T) if (U) {} 24 | -------------------------------------------------------------------------------- /test/pass_files/switch_condition_assignment.d: -------------------------------------------------------------------------------- 1 | import std : writeln, format; 2 | 3 | int get() 4 | { 5 | return 1; 6 | } 7 | 8 | void main() 9 | { 10 | switch (auto x = get()) { 11 | default: 12 | writeln("inside switch: ", x); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/pass_files/templates.d: -------------------------------------------------------------------------------- 1 | template a(b) {} 2 | template a(b, c) {} 3 | template a(b, c) if (d) {} 4 | template a(alias int a : 10) {} 5 | template a(alias int a : int) {} 6 | template a(alias int a : 10) {} 7 | template a(alias int a : int = 10) {} 8 | template a(alias int a : int) {} 9 | template a(alias int a = 10) {} 10 | template a(alias int a = int) {} 11 | template a(alias b) {} 12 | template a(alias b, c : d) {} 13 | template a(alias b, c : int) {} 14 | template a(alias b, c : int[]) {} 15 | template a(b) if (is (a : struct)) {} 16 | template a(b) if (is (a : union)) {} 17 | template a(b) if (is (a : class)) {} 18 | template a(b) if (is (a : interface)) {} 19 | template a(b) if (is (a : enum)) {} 20 | template a(b) if (is (a : function)) {} 21 | template a(b) if (is (a : delegate)) {} 22 | template a(b) if (is (a : super)) {} 23 | template a(b) if (is (a : return)) {} 24 | template a(b) if (is (a : __parameters)) {} 25 | template a(b) if (is (a : const)) {} 26 | template a(b) if (is (a : immutable)) {} 27 | template a(b) if (is (a : inout)) {} 28 | template a(b) if (is (a : shared)) {} 29 | enum A(T) = A; 30 | template A(T) if (is (T u : v, W)) {} 31 | mixin template A(T) { T t; } 32 | size_t replicateBits(size_t , )() {} 33 | -------------------------------------------------------------------------------- /test/pass_files/test8898.d: -------------------------------------------------------------------------------- 1 | // REQUIRED_ARGS: -w 2 | // PERMUTE_ARGS: 3 | 4 | static if (true): 5 | 6 | version (Foo) 7 | { 8 | } 9 | else 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /test/pass_files/throwattribute.d: -------------------------------------------------------------------------------- 1 | void doStuff() throw { 2 | throw void doThings() 3 | { 4 | 5 | } 6 | throw new Exception("OH NO!"); 7 | } 8 | 9 | throw: 10 | extern void throwsSomething(); 11 | -------------------------------------------------------------------------------- /test/pass_files/unions.d: -------------------------------------------------------------------------------- 1 | union { int a; char b; } 2 | union SomeUnion; 3 | union SomeUnion { int a; int b; } 4 | union SomeUnion(T) { int a; int b; } 5 | union SomeUnion(T) if (z) { int a; int b; } 6 | 7 | -------------------------------------------------------------------------------- /test/pass_files/varargs-attributes.d: -------------------------------------------------------------------------------- 1 | int printf(const(char)*, const scope shared return ...); 2 | int printf2(const scope shared return ...); 3 | int printf3(const(char)*, ...); 4 | -------------------------------------------------------------------------------- /test/pass_files/while_condition_assignment.d: -------------------------------------------------------------------------------- 1 | import std : writeln, format; 2 | 3 | void main() 4 | { 5 | int i = 10; 6 | // Silly example I know 7 | while (int not_four = (i != 4)) { 8 | writeln(format("%d is not 4!", i)); 9 | i--; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | PASS_FILES=$(find pass_files -name "*.d" | sort) 6 | FAIL_FILES=$(find fail_files -name "*.d" | sort) 7 | PASS_COUNT=0 8 | FAIL_COUNT=0 9 | NORMAL="\033[01;0m" 10 | GREEN="\033[32m" 11 | RED="\033[31m" 12 | YELLOW="\033[33m" 13 | DMD=${DMD:=dmd} 14 | SOURCE_FILES="../src/std/experimental/*.d ../src/dparse/*.d " 15 | IMPORT_PATHS="-I../src/" 16 | 17 | echo -en "Compiling parse tester... " 18 | ${DMD} tester.d -debug $SOURCE_FILES -g $IMPORT_PATHS 19 | echo -e "${GREEN}DONE${NORMAL}" 20 | 21 | test_fail() { 22 | set +e 23 | ./tester "$@" 24 | test_fail_status=$? 25 | set -e 26 | if [ $test_fail_status -eq 1 ]; then 27 | return 0 28 | else 29 | return 1 30 | fi 31 | } 32 | 33 | # if tester segfaults, it's most likely due to a stack overflow 34 | # check the maxStackSize variable in tester.d in that case 35 | # (increasing it should be avoided if it's possible to implement tail recursion or other stack saving techniques) 36 | 37 | for i in $PASS_FILES; do 38 | echo -en "Parsing $i..." 39 | if ./tester "$i" 2>/dev/null 1>/dev/null; then 40 | echo -e "\t${GREEN}PASS${NORMAL}" 41 | ((PASS_COUNT=PASS_COUNT+1)) 42 | else 43 | echo -e "\t${RED}FAIL${NORMAL}" 44 | ((FAIL_COUNT=FAIL_COUNT+1)) 45 | ./tester "$i" 46 | fi 47 | done 48 | 49 | for i in $FAIL_FILES; do 50 | echo -en "Parsing $i..." 51 | if test_fail "$i" 2>/dev/null 1>/dev/null; then 52 | echo -e "\t${GREEN}PASS${NORMAL}" 53 | ((PASS_COUNT=PASS_COUNT+1)) 54 | else 55 | echo -e "\t${RED}FAIL${NORMAL}" 56 | ((FAIL_COUNT=FAIL_COUNT+1)) 57 | fi 58 | done 59 | 60 | echo 61 | if [ "$FAIL_COUNT" -eq 0 ]; then 62 | echo -e "${GREEN}${PASS_COUNT} parse test(s) passed and ${FAIL_COUNT} failed.${NORMAL}" 63 | else 64 | echo -e "${RED}${PASS_COUNT} parse test(s) passed and ${FAIL_COUNT} failed.${NORMAL}" 65 | exit 1 66 | fi 67 | 68 | if [[ ${BUILDKITE:-} != "true" ]]; then 69 | PASS_COUNT=0 70 | FAIL_COUNT=0 71 | echo 72 | for file in ast_checks/*.d; do 73 | echo -en "Running AST match tests on ${file}..." 74 | # The query file has the same base name as its corresponding D file, but 75 | # with a txt extension. It contains XPath expressions, one per line, that 76 | # must match nodes in the generated AST. 77 | queryFile=ast_checks/$(basename "$file" .d).txt 78 | lineCount=1 79 | currentPasses=0 80 | currentFailures=0 81 | expectParseFailure=0 82 | set +e 83 | AST="$(./tester --ast "$file" 2>/dev/null)" 84 | test_fail_status=$? 85 | set -e 86 | while read -r line || [ -n "$line" ]; do 87 | if [[ "$line" == "INCLUDES_PARSE_ERROR" ]]; then 88 | expectParseFailure=1 89 | elif [[ "$line" =~ ^# ]]; then 90 | true # comment line 91 | elif echo "$AST" | xmllint --xpath "${line}" - 2>&1 | grep 'XPath set is empty' >/dev/null; then 92 | echo 93 | echo -e " ${RED}Check on line $lineCount of $queryFile failed.${NORMAL}" 94 | ((currentFailures=currentFailures+1)) 95 | else 96 | ((currentPasses=currentPasses+1)) 97 | fi 98 | ((lineCount=lineCount+1)) 99 | done < "$queryFile" 100 | 101 | if [[ $expectParseFailure -eq 0 ]]; then 102 | if [[ $test_fail_status -ne 0 ]]; then 103 | echo -e " ${RED}D parsing of $queryFile failed in general.${NORMAL}" 104 | ./tester --ast "$file" >/dev/null 105 | ((currentFailures=currentFailures+1)) 106 | fi 107 | fi 108 | 109 | if [[ $currentFailures -gt 0 ]]; then 110 | echo -e " ${RED}${currentPasses} check(s) passed and ${currentFailures} check(s) failed${NORMAL}" 111 | ((FAIL_COUNT=FAIL_COUNT+1)) 112 | 113 | if [ -z "${VERBOSE:-}" ]; then 114 | echo -e " Run with VERBOSE=1 to print AST XML" 115 | else 116 | echo 117 | ./tester --ast "$file" | xmllint --format - 118 | echo 119 | fi 120 | else 121 | echo -e " ${GREEN}${currentPasses} check(s) passed and ${currentFailures} check(s) failed${NORMAL}" 122 | ((PASS_COUNT=PASS_COUNT+1)) 123 | fi 124 | done 125 | echo 126 | if [ "$FAIL_COUNT" -eq 0 ]; then 127 | echo -e "${GREEN}${PASS_COUNT} AST test(s) passed and ${FAIL_COUNT} failed.${NORMAL}" 128 | else 129 | echo -e "${RED}${PASS_COUNT} AST test(s) passed and ${FAIL_COUNT} failed.${NORMAL}" 130 | exit 1 131 | fi 132 | else 133 | echo 134 | echo -e "${YELLOW}Skipping AST checks in Buildkite CI${NORMAL}" 135 | fi 136 | 137 | if [[ ${DMD} == "gdmd" ]] 138 | then 139 | echo "GDC / GDMD does not support -cov" 140 | exit 0; 141 | fi 142 | 143 | echo 144 | find . -name "*.lst" -exec rm -f {} \; 145 | echo -en "Generating coverage reports... " 146 | ${DMD} tester.d -debug -cov -unittest $SOURCE_FILES $IMPORT_PATHS 147 | # if tester segfaults, it's most likely due to a stack overflow 148 | # check the maxStackSize variable in tester.d in that case 149 | # (increasing it should be avoided if it's possible to implement tail recursion or other stack saving techniques) 150 | ./tester --ast --DRT-testmode=run-main $PASS_FILES $FAIL_FILES ast_checks/*.d > /dev/null 151 | rm -rf coverage/ 152 | mkdir coverage/ 153 | find . -name "*.lst" | while read -r i; do 154 | dest=$(echo "$i" | sed -e "s/\\.\\.\\-//") 155 | mv "$i" "coverage/$dest"; 156 | done 157 | echo -e "${GREEN}DONE${NORMAL}" 158 | for i in coverage/*.lst; do 159 | tail -n1 "$i" 160 | done 161 | 162 | rm -f tester.o 163 | -------------------------------------------------------------------------------- /test/tester.d: -------------------------------------------------------------------------------- 1 | import core.thread; 2 | import dparse.ast; 3 | import dparse.astprinter; 4 | import dparse.lexer; 5 | import dparse.parser; 6 | import dparse.rollback_allocator : RollbackAllocator; 7 | import std.array; 8 | import std.exception; 9 | import std.file; 10 | import std.getopt; 11 | import std.stdio; 12 | 13 | int errorCount = 0; 14 | int warningCount = 0; 15 | 16 | void messageFunction(string fileName, size_t line, size_t column, 17 | string message, bool isError) 18 | { 19 | if (isError) 20 | { 21 | errorCount++; 22 | version (D_Coverage) {} 23 | else 24 | stderr.writefln("%s(%d:%d)[error]: %s", fileName, line, column, message); 25 | } 26 | else 27 | { 28 | version (D_Coverage) {} 29 | else 30 | stderr.writefln("%s(%d:%d)[warn ]: %s", fileName, line, column, message); 31 | warningCount++; 32 | } 33 | } 34 | 35 | void testTokenChecks() 36 | { 37 | foreach (IdType i; 0 .. IdType.max) 38 | { 39 | switch (i) 40 | { 41 | case tok!"int": 42 | case tok!"uint": 43 | case tok!"double": 44 | case tok!"idouble": 45 | case tok!"float": 46 | case tok!"ifloat": 47 | case tok!"short": 48 | case tok!"ushort": 49 | case tok!"long": 50 | case tok!"ulong": 51 | case tok!"char": 52 | case tok!"wchar": 53 | case tok!"dchar": 54 | case tok!"bool": 55 | case tok!"void": 56 | case tok!"cent": 57 | case tok!"ucent": 58 | case tok!"real": 59 | case tok!"ireal": 60 | case tok!"byte": 61 | case tok!"ubyte": 62 | case tok!"cdouble": 63 | case tok!"cfloat": 64 | case tok!"creal": 65 | assert (isBasicType(i)); 66 | assert (!isNumberLiteral(i)); 67 | assert (!isIntegerLiteral(i)); 68 | assert (!isOperator(i)); 69 | assert (!isKeyword(i)); 70 | assert (!isStringLiteral(i)); 71 | assert (!isProtection(i)); 72 | break; 73 | case tok!"doubleLiteral": 74 | case tok!"floatLiteral": 75 | case tok!"idoubleLiteral": 76 | case tok!"ifloatLiteral": 77 | case tok!"realLiteral": 78 | case tok!"irealLiteral": 79 | assert (!isBasicType(i)); 80 | assert (isNumberLiteral(i)); 81 | assert (!isIntegerLiteral(i)); 82 | assert (!isOperator(i)); 83 | assert (!isKeyword(i)); 84 | assert (!isStringLiteral(i)); 85 | assert (!isProtection(i)); 86 | break; 87 | case tok!"intLiteral": 88 | case tok!"longLiteral": 89 | case tok!"uintLiteral": 90 | case tok!"ulongLiteral": 91 | assert (!isBasicType(i)); 92 | assert (isNumberLiteral(i)); 93 | assert (isIntegerLiteral(i)); 94 | assert (!isOperator(i)); 95 | assert (!isKeyword(i)); 96 | assert (!isStringLiteral(i)); 97 | assert (!isProtection(i)); 98 | break; 99 | case tok!",": 100 | case tok!".": 101 | case tok!"..": 102 | case tok!"...": 103 | case tok!"/": 104 | case tok!"/=": 105 | case tok!"!": 106 | case tok!"!<": 107 | case tok!"!<=": 108 | case tok!"!<>": 109 | case tok!"!<>=": 110 | case tok!"!=": 111 | case tok!"!>": 112 | case tok!"!>=": 113 | case tok!"$": 114 | case tok!"%": 115 | case tok!"%=": 116 | case tok!"&": 117 | case tok!"&&": 118 | case tok!"&=": 119 | case tok!"(": 120 | case tok!")": 121 | case tok!"*": 122 | case tok!"*=": 123 | case tok!"+": 124 | case tok!"++": 125 | case tok!"+=": 126 | case tok!"-": 127 | case tok!"--": 128 | case tok!"-=": 129 | case tok!":": 130 | case tok!";": 131 | case tok!"<": 132 | case tok!"<<": 133 | case tok!"<<=": 134 | case tok!"<=": 135 | case tok!"<>": 136 | case tok!"<>=": 137 | case tok!"=": 138 | case tok!"==": 139 | case tok!"=>": 140 | case tok!">": 141 | case tok!">=": 142 | case tok!">>": 143 | case tok!">>=": 144 | case tok!">>>": 145 | case tok!">>>=": 146 | case tok!"?": 147 | case tok!"@": 148 | case tok!"[": 149 | case tok!"]": 150 | case tok!"^": 151 | case tok!"^=": 152 | case tok!"^^": 153 | case tok!"^^=": 154 | case tok!"{": 155 | case tok!"|": 156 | case tok!"|=": 157 | case tok!"||": 158 | case tok!"}": 159 | case tok!"~": 160 | case tok!"~=": 161 | assert (!isBasicType(i)); 162 | assert (!isNumberLiteral(i)); 163 | assert (!isIntegerLiteral(i)); 164 | assert (isOperator(i)); 165 | assert (!isKeyword(i)); 166 | assert (!isStringLiteral(i)); 167 | assert (!isProtection(i)); 168 | break; 169 | case tok!"abstract": 170 | case tok!"alias": 171 | case tok!"align": 172 | case tok!"asm": 173 | case tok!"assert": 174 | case tok!"auto": 175 | case tok!"break": 176 | case tok!"case": 177 | case tok!"cast": 178 | case tok!"catch": 179 | case tok!"class": 180 | case tok!"const": 181 | case tok!"continue": 182 | case tok!"debug": 183 | case tok!"default": 184 | case tok!"delegate": 185 | case tok!"delete": 186 | case tok!"deprecated": 187 | case tok!"do": 188 | case tok!"else": 189 | case tok!"enum": 190 | case tok!"extern": 191 | case tok!"false": 192 | case tok!"final": 193 | case tok!"finally": 194 | case tok!"for": 195 | case tok!"foreach": 196 | case tok!"foreach_reverse": 197 | case tok!"function": 198 | case tok!"goto": 199 | case tok!"if": 200 | case tok!"immutable": 201 | case tok!"import": 202 | case tok!"in": 203 | case tok!"inout": 204 | case tok!"interface": 205 | case tok!"invariant": 206 | case tok!"is": 207 | case tok!"lazy": 208 | case tok!"macro": 209 | case tok!"mixin": 210 | case tok!"module": 211 | case tok!"new": 212 | case tok!"nothrow": 213 | case tok!"null": 214 | case tok!"out": 215 | case tok!"override": 216 | case tok!"pragma": 217 | case tok!"pure": 218 | case tok!"ref": 219 | case tok!"return": 220 | case tok!"scope": 221 | case tok!"shared": 222 | case tok!"static": 223 | case tok!"struct": 224 | case tok!"super": 225 | case tok!"switch": 226 | case tok!"synchronized": 227 | case tok!"template": 228 | case tok!"this": 229 | case tok!"throw": 230 | case tok!"true": 231 | case tok!"try": 232 | case tok!"typedef": 233 | case tok!"typeid": 234 | case tok!"typeof": 235 | case tok!"union": 236 | case tok!"unittest": 237 | case tok!"version": 238 | case tok!"while": 239 | case tok!"with": 240 | case tok!"__DATE__": 241 | case tok!"__EOF__": 242 | case tok!"__FILE__": 243 | case tok!"__FILE_FULL_PATH__": 244 | case tok!"__FUNCTION__": 245 | case tok!"__gshared": 246 | case tok!"__LINE__": 247 | case tok!"__MODULE__": 248 | case tok!"__parameters": 249 | case tok!"__PRETTY_FUNCTION__": 250 | case tok!"__TIME__": 251 | case tok!"__TIMESTAMP__": 252 | case tok!"__traits": 253 | case tok!"__vector": 254 | case tok!"__VENDOR__": 255 | case tok!"__VERSION__": 256 | assert (!isBasicType(i)); 257 | assert (!isNumberLiteral(i)); 258 | assert (!isIntegerLiteral(i)); 259 | assert (!isOperator(i)); 260 | assert (isKeyword(i)); 261 | assert (!isStringLiteral(i)); 262 | assert (!isProtection(i)); 263 | break; 264 | case tok!"dstringLiteral": 265 | case tok!"stringLiteral": 266 | case tok!"wstringLiteral": 267 | assert (!isBasicType(i)); 268 | assert (!isNumberLiteral(i)); 269 | assert (!isIntegerLiteral(i)); 270 | assert (!isOperator(i)); 271 | assert (!isKeyword(i)); 272 | assert (isStringLiteral(i)); 273 | assert (!isProtection(i)); 274 | break; 275 | case tok!"export": 276 | case tok!"package": 277 | case tok!"private": 278 | case tok!"public": 279 | case tok!"protected": 280 | assert (!isBasicType(i)); 281 | assert (!isNumberLiteral(i)); 282 | assert (!isIntegerLiteral(i)); 283 | assert (!isOperator(i)); 284 | assert (isKeyword(i)); 285 | assert (!isStringLiteral(i)); 286 | assert (isProtection(i)); 287 | break; 288 | default: 289 | assert (!isBasicType(i)); 290 | assert (!isNumberLiteral(i)); 291 | assert (!isIntegerLiteral(i)); 292 | assert (!isOperator(i)); 293 | assert (!isKeyword(i)); 294 | assert (!isStringLiteral(i)); 295 | assert (!isProtection(i)); 296 | break; 297 | } 298 | } 299 | } 300 | 301 | void testArbitraryASTs() 302 | { 303 | StringCache cache = StringCache(StringCache.defaultBucketCount); 304 | LexerConfig config; 305 | config.stringBehavior = StringBehavior.source; 306 | RollbackAllocator rba; 307 | string[] errors; 308 | void msgDelegate(string fileName, size_t line, size_t column, string message, bool isError) 309 | { 310 | errors ~= message; 311 | } 312 | auto parser = new Parser(); 313 | parser.messageDelegate = &msgDelegate; 314 | parser.allocator = &rba; 315 | 316 | parser.tokens = getTokensForParser("struct S {}", config, &cache); 317 | assert(parser.parseCompileCondition() is null); 318 | assert(errors == ["`version`, `debug`, or `static` expected (found token `struct`)"]); 319 | errors = null; 320 | 321 | parser = new Parser(); 322 | parser.messageDelegate = &msgDelegate; 323 | parser.allocator = &rba; 324 | parser.tokens = getTokensForParser("~", config, &cache); 325 | assert(parser.parseDestructor() is null); 326 | assert(errors == ["`this` expected instead of EOF"]); 327 | errors = null; 328 | 329 | parser = new Parser(); 330 | parser.messageDelegate = &msgDelegate; 331 | parser.allocator = &rba; 332 | parser.tokens = getTokensForParser("for (x; y; z) {}", config, &cache); 333 | assert(parser.parseForeach() is null); 334 | assert(errors == ["`foreach` or `foreach_reverse` expected (found token `for`)"]); 335 | errors = null; 336 | 337 | parser = new Parser(); 338 | parser.messageDelegate = &msgDelegate; 339 | parser.allocator = &rba; 340 | parser.tokens = getTokensForParser("version =", config, &cache); 341 | assert(parser.parseVersionSpecification() is null); 342 | assert(errors == ["Identifier or integer literal expected (found EOF)"]); 343 | errors = null; 344 | 345 | } 346 | 347 | int main(string[] args) 348 | { 349 | version (D_Coverage) testTokenChecks(); 350 | version (D_Coverage) testArbitraryASTs(); 351 | 352 | bool ast; 353 | getopt(args, "ast", &ast); 354 | 355 | enforce(args.length > 1, "Must specifiy at least one D file"); 356 | auto printer = new XMLPrinter; 357 | printer.output = stdout; 358 | bool done; 359 | 360 | // increase stack size in case of segfault: 361 | // stack usage in debug / non-optimized mode is _much_ higher 362 | version (D_Coverage) 363 | enum maxStackSize = 256 * 4096; 364 | else debug 365 | enum maxStackSize = 256 * 4096; 366 | else 367 | enum maxStackSize = 40 * 4096; 368 | 369 | // use a fiber to limit stack size 370 | new Fiber({ 371 | foreach (arg; args[1 .. $]) 372 | { 373 | auto f = File(arg); 374 | immutable ulong fileSize = f.size(); 375 | ubyte[] fileBytes = new ubyte[](fileSize); 376 | enforce(f.rawRead(fileBytes).length == fileSize); 377 | StringCache cache = StringCache(fileSize.optimalBucketCount); 378 | LexerConfig config; 379 | config.stringBehavior = StringBehavior.source; 380 | config.fileName = arg; 381 | const(Token)[] tokens = getTokensForParser(fileBytes, config, &cache); 382 | RollbackAllocator rba; 383 | auto mod = parseModule(ParserConfig(tokens, arg, &rba, &messageFunction)); 384 | if (ast && mod !is null) 385 | printer.visit(mod); 386 | } 387 | done = true; 388 | }, maxStackSize).call(); 389 | assert(done); 390 | if (!ast) 391 | writefln("Finished parsing with %d errors and %d warnings.", 392 | errorCount, warningCount); 393 | 394 | version (D_Coverage) 395 | return 0; // we don't care about error count in coverage mode 396 | else 397 | return errorCount == 0 ? 0 : 1; 398 | } 399 | -------------------------------------------------------------------------------- /test/watcher.sh: -------------------------------------------------------------------------------- 1 | while $(true); do 2 | inotifywait -emodify -r ../src/ pass_files/ fail_files/; 3 | ./run_tests.sh; 4 | done 5 | --------------------------------------------------------------------------------