diff --git a/.gitattributes b/.gitattributes index 176a458..5802459 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ * text=auto +*.pwn linguist-language=Pawn +*.inc linguist-language=Pawn diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index bafd4db..67f59ca 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,36 +1,37 @@ + + -**Is this a BUG REPORT, FEATURE REQUEST or QUESTION?**: +### Issue description: + -* [ ] Bug Report -* [ ] Feature Request -* [ ] Question +### Minimal complete verifiable example (MCVE): + -**How to reproduce it (as minimally and precisely as possible)**: +``` +``` -**Anything else we need to know?**: + -**Environment**: +### Workspace Information: -* Operating System -* Compiler version -* How are you invoking the compiler? Pawno, Sublime, vscode, sampctl or command-line? -* If using sampctl, the version number +* Compiler version: +* Command line arguments provided (or sampctl version): +* Operating System: diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..c015ee6 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,14 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 90 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 9000 +# Issues with these labels will never be considered stale +exemptLabels: + - "state: discuss" +# Label to use when marking an issue as stale +staleLabel: "state: stale" +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had recent activity. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/.gitignore b/.gitignore index 14b6cae..becd9cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ build .vs .vscode -source/compiler/tests/*.amx diff --git a/.travis.yml b/.travis.yml index 19667c9..5851319 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,11 @@ addons: - cmake before_script: - - cmake source/compiler -DCMAKE_BUILD_TYPE=RelWithDebInfo - -DCMAKE_C_FLAGS="-m32 -Werror -Wno-address-of-packed-member" - -DCPACK_GENERATOR="TGZ;ZIP" + - mkdir build && cd build + - cmake ../source/compiler + -DCMAKE_BUILD_TYPE=RelWithDebInfo + -DCMAKE_C_FLAGS="-m32 -Werror -Wno-address-of-packed-member" + -DCPACK_GENERATOR="TGZ;ZIP" script: - make @@ -32,10 +34,10 @@ deploy: secure: gDAuRNlF2uVhVHyZtJrX6MwNxnkfkQzohrC/6UcKAgqt+NKs4vZyq5FzTUceiDAkB0se70ZVx08e9ibAiXP/b7D1MPkAEiRxt9J6Vu3x6Bi1kPPuK5RfjFeT3gc1SbrULAP8Nz0NdU0chUhei6/V5NGhTegwp925DJOISq7+Ibw= file_glob: true file: - - 'pawnc-*-linux.tar.gz' - - 'pawnc-*-macos.zip' + - 'build/pawnc-*-linux.tar.gz' + - 'build/pawnc-*-macos.zip' draft: true skip_cleanup: true on: tags: true - repo: Zeex/pawn + repo: pawn-lang/compiler diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..530f772 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @pawn-lang/compiler diff --git a/appveyor.yml b/appveyor.yml index bdcd9a5..70a6428 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,8 @@ configuration: - RelWithDebInfo before_build: - - cmake -G "Visual Studio 15 2017" source/compiler -DCPACK_GENERATOR=ZIP -DCMAKE_GENERATOR_TOOLSET=v100 + - mkdir build && cd build + - cmake ../source/compiler -G "Visual Studio 14 2015" -DCPACK_GENERATOR=ZIP -DCMAKE_GENERATOR_TOOLSET=v100 build_script: - cmake --build . --config %CONFIGURATION% @@ -15,17 +16,17 @@ test_script: - ctest --build-config %CONFIGURATION% on_failure: - - type Testing\Temporary\LastTest.log + - type Testing\Temporary\LastTest.log artifacts: - - path: pawnc-*-windows.zip + - path: build/pawnc-*-windows.zip name: Binary package deploy: description: '' provider: GitHub auth_token: - secure: ++mk9GhXTpN/hdVjIIi/nHpu0gYpDybMJNDPB3lld8r1UyfoPbz08SVGeSS84HjR + secure: dEyljMC1z2hb032HYJQY+CIZIdQ382cuHU/3LJA96nbqOl5xfGYPVP/TGbJANbtz artifact: /pawnc-.*-windows\.zip/ draft: true prerelease: true diff --git a/readme.md b/readme.md index db0ba47..d420f29 100644 --- a/readme.md +++ b/readme.md @@ -1,135 +1,119 @@ -# Pawn +# Pawn Community Compiler -[![Build Status][build_status]][build] [![Build Status - Windows][build_status_win]][build_win] +[![Build Status][build_status_linux]][build_linux] +[![Build Status - Windows][build_status_win]][build_win] -[Original readme](readme_compuphase.txt) +## What -## What is this? +This is a modified version of the Pawn 3.2.3664 compiler with many bug fixes and +enhancements. -This is a modified copy of the Pawn compiler version 3.2.3664 by Compuphase that fixes some bugs and adds a few features. +This project was originally started by Zeex but on 2017-12-31, the project was +taken over by some members of the SA:MP community. Zeex still contributes to the +project, along with the [Compiler Team][team]. -This project was originally maintained and managed by Zeex who left the project due to lack of time on 2017-12-31. Thank you for all your hard work on keeping this project alive Zeex! +The original readme is available [here][original_readme] -The project is now maintained by the [Compiler Team](https://github.com/orgs/pawn-lang/teams/compiler) here at the pawn-lang GitHub org. +## Why -## List of changes +This project exists to: -See [Known compiler bugs](../../wiki/Known-compiler-bugs) for the list of fixed bugs and [What's new](../../wiki/What's-new) for the list of features and other notable changes. +- Fix known bugs with the original compiler +- Provide a better development experience for the SA:MP community -[Release notes](https://github.com/pawn-lang/compiler/releases) descibe changes in each version of the compiler. +If you find problem, you can [open an issue][issues] and contributors can work +on a fix. This isn't true with the original compiler that comes with the SA:MP +server distribution. -## Download +There are also new features that enhance the development experience, such as +telling you which tags are involved in a "tag mismatch" or showing you where +that pesky "symbol is never used" is actually declared. -Binary packages can be downloads from [Releases](https://github.com/pawn-lang/compiler/releases). +There are plenty of features and fixes that are documented, see below for links: -You can also get the latest development binaries for Windows on [AppVeyor](https://ci.appveyor.com/project/pawn-lang/compiler/branch/master/artifacts). This archive is built automatically on every Git commit and can be pretty unstable, so use at your own risk. +- [Known compiler bugs][bugs] contains a list of bugs that the team are aware of + with their status. -## Installation on Windows +- [What's new][new] contains features and other notable changes. -Download the ZIP archive and extract `pawnc.dll`, `pawncc.exe`, `pawndisasmsm.exe` to your pawno directory or another directory of your choice if you're using a different editor. +- [Release notes][releases] list of all official releases of the compiler + binaries. -## Installation on openSUSE/SLES +## How to Use -There is an installation package available for openSUSE/SLES users so that you can easily install the compiler on your distribution. Please follow these steps: +Binary packages can be downloaded from [Releases][releases], see the below +sections for platform-specific installation instructions. -1. Go to https://build.opensuse.org/package/show/home:mschnitzer/pawncc -2. On the right side, select your distribution (only if it's not disabled!) -3. Click "Go to download repository" -4. Copy the link and enter in sudo mode in your shell: `zypper ar $COPIED_LINK home:mschnitzer` -5. Again as root, type: `zypper ref` -6. Install the package with `zypper in pawncc` -7. Run `pawncc` in your shell to test if it's working +**Note:** You will _probably_ get warnings/errors/fatals when you first build +with this compiler — you need to add the `-Z` flag to your build configuration +or add `#pragma compat`. See [this page][compat] for more information. -## Building from source code +You can also get the latest development binaries for Windows on +[AppVeyor][artifacts]. This archive is built automatically on every Git commit +and can be pretty unstable, so use at your own risk. -In general you will need [CMake](https://cmake.org/) and a C compiller to build Pawn from source code. +### Windows -### Building on Windows +If you just use an editor configured to run `pawncc` such as Pawno, Sublime Text +or VS Code you can simply delete your existing `pawncc.exe` and replace it with +the new one. -* Clone this repo: `git clone https://github.com/Zeex/pawn.git C:\pawn` (you can use another directory instead of `C:\Pawn`, but make sure the path doesn't have spaces). -* Install [Visual Studio Community](https://www.visualstudio.com/vs/community/), it's free. -* Install [CMake](https://cmake.org/). +Download the ZIP archive and extract `pawnc.dll`, `pawncc.exe`, +`pawndisasmsm.exe` to your original `pawncc` directory. If you use Pawno, this +will be in the `pawno/` folder that's inside your server directory. - When installing make sure to check "Add CMake to system PATH" to make your life easier. +### openSUSE/SLES -* Generate a Visual Studio project. +There is an installation package available for openSUSE/SLES users so that you +can easily install the compiler on your distribution. Please follow these steps: - In Command promprt or Powershell execute the following: +1. Go to +2. On the right side, select your distribution (only if it's not disabled!) +3. Click "Go to download repository" +4. Copy the link and enter in sudo mode in your shell: + `zypper ar $COPIED_LINK home:mschnitzer` +5. Again as root, type: `zypper ref` +6. Install the package with `zypper in pawncc` +7. Run `pawncc` in your shell to test if it's working - ```cmd - cd C:\Pawn - mkdir build && cd build - cmake ..\source\compiler -G "Visual Studio 15 2017" - ``` +### With sampctl -* From the same directory as in the previous step run: +If you are a [sampctl][sampctl] user, you are already using this compiler! - ``` - cmake --build . --config Release - ``` +### Build From Source - or open the pawnc.sln in Visual Studio and build from there (but make sure to choose the "Release" configuration). - - This will create `pawnc.dll` and `pawncc.exe` in the `Release` folder. You can now copy these files to your `pawno` folder for convenience or put them in a separate folder and configure your code editor accordingly. - -### Building on Linux - -Use your distribution's package manager to install the required dependencies. For example, in Ubuntu you would do: - -```sh -sudo apt install gcc gcc-multilib make cmake -``` - -`gcc-multilib` is needed for compiling a 32-bit binary (64-bit is not supported). - -Now you can clone this repo and build the compiler: - -```sh -git clone https://github.com/Zeex/pawn.git ~/pawn -cd ~/pawn -mkdir build && cd build -cmake ../source/compiler -DCMAKE_C_FLAGS=-m32 -DCMAKE_BUILD_TYPE=Release -make -``` - -Replace "Release" with "Debug" if you want to build a debug executable for development or submitting bugs. - -### Building on macOS - -* Install Xcode: https://developer.apple.com/xcode/ - -* Install Command Line Tools for Xcode: - -```sh -xcode-select --install -``` - -* Install CMake: - -```sh -brew install cmake -``` - -* Now you can clone this repo and build the compiler: - -```sh -git clone https://github.com/Zeex/pawn.git ~/pawn -cd ~/pawn -mkdir build && cd build -cmake ../source/compiler -DCMAKE_C_FLAGS=-m32 -DCMAKE_BUILD_TYPE=Release -make -``` +If you are interested in contributing or just using a specific version, check +out [this page][build_source] for instructions for compiling for your platform. ## Background -The project was originally started as a set of patches aimed to create a compiler that would be compatible with the compiler used in [SA-MP (San Andreas Multiplayer)](http://sa-mp.com/). +The project was originally started as a set of patches aimed to create a +compiler that would be compatible with the compiler used in +[SA-MP (San Andreas Multiplayer)](http://sa-mp.com/). -SA-MP uses a modified version of Pawn 3.2.3664 [1] with Windows-only executables, and the developers said that they lost the source code for it which means it can't be ported to other platforms (e.g. Linux) and newly discovered bugs can't be fixed. So the main goal of the project is to re-create changes that were previously made by the devs as well as fix all known compiler bugs. +SA-MP uses a modified version of Pawn 3.2.3664 [1] with Windows-only +executables, and the developers said that they lost the source code for it which +means it can't be ported to other platforms (e.g. Linux) and newly discovered +bugs can't be fixed. So the main goal of the project is to re-create changes +that were previously made by the devs as well as fix all known compiler bugs. -[1] It's worth noting that the version of the AMX embedded into the SA-MP server seems to be based on an older release of Pawn. +[1] It's worth noting that the version of the AMX embedded into the SA-MP server +seems to be based on an older release of Pawn. -[build]: https://travis-ci.org/pawn-lang/compiler -[build_status]: https://travis-ci.org/pawn-lang/compiler.svg?branch=master +[build_linux]: https://travis-ci.org/pawn-lang/compiler +[build_status_linux]: https://travis-ci.org/pawn-lang/compiler.svg?branch=master [build_win]: https://ci.appveyor.com/project/Southclaws/compiler/branch/master -[build_status_win]: https://ci.appveyor.com/api/projects/status/k112tbr1afrkif0n?svg=true - +[build_status_win]: + https://ci.appveyor.com/api/projects/status/k112tbr1afrkif0n?svg=true +[team]: https://github.com/pawn-lang/compiler/graphs/contributors +[original_readme]: + https://github.com/pawn-lang/compiler/tree/master/readme_compuphase.txt +[issues]: https://github.com/pawn-lang/compiler/issues +[bugs]: https://github.com/pawn-lang/compiler/wiki/Known-compiler-bugs +[new]: https://github.com/pawn-lang/compiler/wiki/What's-new +[releases]: https://github.com/pawn-lang/compiler/releases +[artifacts]: + https://ci.appveyor.com/project/Southclaws/compiler/branch/master/artifacts +[compat]: https://github.com/pawn-lang/compiler/wiki/Compatibility-mode +[sampctl]: http://bit.ly/sampctl +[build_source]: https://github.com/pawn-lang/compiler/wiki/Building-From-Source diff --git a/source/amx/CMakeLists.txt b/source/amx/CMakeLists.txt index a3bfb0f..bc68454 100644 --- a/source/amx/CMakeLists.txt +++ b/source/amx/CMakeLists.txt @@ -68,6 +68,7 @@ if(WIN32) set_target_properties(amxDGram PROPERTIES LINK_FLAGS "/export:amx_DGramInit /export:amx_DGramCleanup") endif() + target_link_libraries(amxDGram ws2_32) endif() # amxFile diff --git a/source/amx/amx.c b/source/amx/amx.c index 78ab989..675576f 100644 --- a/source/amx/amx.c +++ b/source/amx/amx.c @@ -1860,7 +1860,7 @@ int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char * supports this too. */ -#define NEXT(cip) goto **cip++ +#define NEXT(cip) goto **(void **)cip++ int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) { @@ -2047,14 +2047,14 @@ static const void * const amx_opcodelist[] = { NEXT(cip); op_load_i: /* verify address */ - if (pri>=hea && pri=(ucell)amx->stp) + if ((pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri=_R(data,pri); NEXT(cip); op_lodb_i: GETPARAM(offs); /* verify address */ - if (pri>=hea && pri=(ucell)amx->stp) + if ((pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); switch (offs) { case 1: @@ -2120,14 +2120,14 @@ static const void * const amx_opcodelist[] = { NEXT(cip); op_stor_i: /* verify address */ - if (alt>=hea && alt=(ucell)amx->stp) + if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); _W(data,alt,pri); NEXT(cip); op_strb_i: GETPARAM(offs); /* verify address */ - if (alt>=hea && alt=(ucell)amx->stp) + if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); switch (offs) { case 1: @@ -2144,7 +2144,7 @@ static const void * const amx_opcodelist[] = { op_lidx: offs=pri*sizeof(cell)+alt; /* implicit shift value for a cell */ /* verify address */ - if (offs>=hea && offs=(ucell)amx->stp) + if ((offs>=hea && offs=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri=_R(data,offs); NEXT(cip); @@ -2152,7 +2152,7 @@ static const void * const amx_opcodelist[] = { GETPARAM(offs); offs=(pri << (int)offs)+alt; /* verify address */ - if (offs>=hea && offs=(ucell)amx->stp) + if ((offs>=hea && offs=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri=_R(data,offs); NEXT(cip); @@ -2641,13 +2641,13 @@ static const void * const amx_opcodelist[] = { /* verify top & bottom memory addresses, for both source and destination * addresses */ - if (pri>=hea && pri=(ucell)amx->stp) + if ((pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); - if ((pri+offs)>hea && (pri+offs)(ucell)amx->stp) + if (((pri+offs)>hea && (pri+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); - if (alt>=hea && alt=(ucell)amx->stp) + if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); - if ((alt+offs)>hea && (alt+offs)(ucell)amx->stp) + if (((alt+offs)>hea && (alt+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); #if defined _R_DEFAULT memcpy(data+(int)alt, data+(int)pri, (int)offs); @@ -2667,13 +2667,13 @@ static const void * const amx_opcodelist[] = { /* verify top & bottom memory addresses, for both source and destination * addresses */ - if (pri>=hea && pri=(ucell)amx->stp) + if ((pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); - if ((pri+offs)>hea && (pri+offs)(ucell)amx->stp) + if (((pri+offs)>hea && (pri+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); - if (alt>=hea && alt=(ucell)amx->stp) + if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); - if ((alt+offs)>hea && (alt+offs)(ucell)amx->stp) + if (((alt+offs)>hea && (alt+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); #if defined _R_DEFAULT pri=memcmp(data+(int)alt, data+(int)pri, (int)offs); @@ -2688,9 +2688,9 @@ static const void * const amx_opcodelist[] = { op_fill: GETPARAM(offs); /* verify top & bottom memory addresses */ - if (alt>=hea && alt=(ucell)amx->stp) + if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); - if ((alt+offs)>hea && (alt+offs)(ucell)amx->stp) + if (((alt+offs)>hea && (alt+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); for (i=(int)alt; offs>=(int)sizeof(cell); i+=sizeof(cell), offs-=sizeof(cell)) _W32(data,i,pri); @@ -4242,7 +4242,7 @@ int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr) data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; assert(phys_addr!=NULL); - if (amx_addr>=amx->hea && amx_addrstk || amx_addr<0 || amx_addr>=amx->stp) { + if ((amx_addr>=amx->hea && amx_addrstk) || amx_addr<0 || amx_addr>=amx->stp) { *phys_addr=NULL; return AMX_ERR_MEMACCESS; } /* if */ @@ -4491,19 +4491,19 @@ static const long utf8_lowmark[5] = { 0x80, 0x800, 0x10000L, 0x200000L, 0x400000 switch (followup) { case 4: if (((c=*string++) & 0xc0) != 0x80) goto error; - result = (result << 6) | c & 0x3f; + result = (result << 6) | (c & 0x3f); case 3: if (((c=*string++) & 0xc0) != 0x80) goto error; - result = (result << 6) | c & 0x3f; + result = (result << 6) | (c & 0x3f); case 2: if (((c=*string++) & 0xc0) != 0x80) goto error; - result = (result << 6) | c & 0x3f; + result = (result << 6) | (c & 0x3f); case 1: if (((c=*string++) & 0xc0) != 0x80) goto error; - result = (result << 6) | c & 0x3f; + result = (result << 6) | (c & 0x3f); case 0: if (((c=*string++) & 0xc0) != 0x80) goto error; - result = (result << 6) | c & 0x3f; + result = (result << 6) | (c & 0x3f); } /* switch */ /* Do additional checks: shortest encoding & reserved positions. The * lowmark limits also depends on the code length; it can be read from @@ -4511,7 +4511,7 @@ static const long utf8_lowmark[5] = { 0x80, 0x800, 0x10000L, 0x200000L, 0x400000 */ if (result=0xd800 && result<=0xdfff || result==0xfffe || result==0xffff) + if ((result>=0xd800 && result<=0xdfff) || result==0xfffe || result==0xffff) goto error; } /* if */ @@ -4546,40 +4546,40 @@ int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value) } else if (value<0x800) { /* 110xxxxx 10xxxxxx */ if (maxchars < 2) goto error; - *string++ = (char)((value>>6) & 0x1f | 0xc0); - *string++ = (char)(value & 0x3f | 0x80); + *string++ = (char)(((value>>6) & 0x1f) | 0xc0); + *string++ = (char)((value & 0x3f) | 0x80); } else if (value<0x10000) { /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ if (maxchars < 3) goto error; - if (value>=0xd800 && value<=0xdfff || value==0xfffe || value==0xffff) + if ((value>=0xd800 && value<=0xdfff) || value==0xfffe || value==0xffff) goto error; /* surrogate pairs and invalid characters */ - *string++ = (char)((value>>12) & 0x0f | 0xe0); - *string++ = (char)((value>>6) & 0x3f | 0x80); - *string++ = (char)(value & 0x3f | 0x80); + *string++ = (char)(((value>>12) & 0x0f) | 0xe0); + *string++ = (char)(((value>>6) & 0x3f) | 0x80); + *string++ = (char)((value & 0x3f) | 0x80); } else if (value<0x200000) { /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ if (maxchars < 4) goto error; - *string++ = (char)((value>>18) & 0x07 | 0xf0); - *string++ = (char)((value>>12) & 0x3f | 0x80); - *string++ = (char)((value>>6) & 0x3f | 0x80); - *string++ = (char)(value & 0x3f | 0x80); + *string++ = (char)(((value>>18) & 0x07) | 0xf0); + *string++ = (char)(((value>>12) & 0x3f) | 0x80); + *string++ = (char)(((value>>6) & 0x3f) | 0x80); + *string++ = (char)((value & 0x3f) | 0x80); } else if (value<0x4000000) { /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ if (maxchars < 5) goto error; - *string++ = (char)((value>>24) & 0x03 | 0xf8); - *string++ = (char)((value>>18) & 0x3f | 0x80); - *string++ = (char)((value>>12) & 0x3f | 0x80); - *string++ = (char)((value>>6) & 0x3f | 0x80); - *string++ = (char)(value & 0x3f | 0x80); + *string++ = (char)(((value>>24) & 0x03) | 0xf8); + *string++ = (char)(((value>>18) & 0x3f) | 0x80); + *string++ = (char)(((value>>12) & 0x3f) | 0x80); + *string++ = (char)(((value>>6) & 0x3f) | 0x80); + *string++ = (char)((value & 0x3f) | 0x80); } else { /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ if (maxchars < 6) goto error; - *string++ = (char)((value>>30) & 0x01 | 0xfc); - *string++ = (char)((value>>24) & 0x3f | 0x80); - *string++ = (char)((value>>18) & 0x3f | 0x80); - *string++ = (char)((value>>12) & 0x3f | 0x80); - *string++ = (char)((value>>6) & 0x3f | 0x80); - *string++ = (char)(value & 0x3f | 0x80); + *string++ = (char)(((value>>30) & 0x01) | 0xfc); + *string++ = (char)(((value>>24) & 0x3f) | 0x80); + *string++ = (char)(((value>>18) & 0x3f) | 0x80); + *string++ = (char)(((value>>12) & 0x3f) | 0x80); + *string++ = (char)(((value>>6) & 0x3f) | 0x80); + *string++ = (char)((value & 0x3f) | 0x80); } /* if */ if (endptr!=NULL) diff --git a/source/amx/amxargs.c b/source/amx/amxargs.c index c73c744..2a99062 100644 --- a/source/amx/amxargs.c +++ b/source/amx/amxargs.c @@ -16,6 +16,7 @@ * * Version: $Id: amxargs.c 5588 2016-10-25 11:13:28Z $ */ + #if defined _UNICODE || defined __UNICODE__ || defined UNICODE # if !defined UNICODE /* for Windows */ # define UNICODE diff --git a/source/amx/amxaux.c b/source/amx/amxaux.c index 6a35bab..10a92ea 100644 --- a/source/amx/amxaux.c +++ b/source/amx/amxaux.c @@ -20,6 +20,7 @@ * * Version: $Id: amxaux.c 3612 2006-07-22 09:59:46Z thiadmer $ */ + #include #include #include @@ -29,12 +30,15 @@ size_t AMXAPI aux_ProgramSize(char *filename) { FILE *fp; + size_t size; AMX_HEADER hdr; if ((fp=fopen(filename,"rb")) == NULL) return 0; - fread(&hdr, sizeof hdr, 1, fp); + size = fread(&hdr, sizeof hdr, 1, fp); fclose(fp); + if (size < 1) + return 0; amx_Align16(&hdr.magic); amx_Align32((uint32_t *)&hdr.stp); @@ -44,13 +48,18 @@ size_t AMXAPI aux_ProgramSize(char *filename) int AMXAPI aux_LoadProgram(AMX *amx, char *filename, void *memblock) { FILE *fp; + size_t size; AMX_HEADER hdr; int result, didalloc; /* open the file, read and check the header */ if ((fp = fopen(filename, "rb")) == NULL) return AMX_ERR_NOTFOUND; - fread(&hdr, sizeof hdr, 1, fp); + size = fread(&hdr, sizeof hdr, 1, fp); + if (size < 1) { + fclose(fp); + return AMX_ERR_FORMAT; + } /* if */ amx_Align16(&hdr.magic); amx_Align32((uint32_t *)&hdr.size); amx_Align32((uint32_t *)&hdr.stp); @@ -72,8 +81,10 @@ int AMXAPI aux_LoadProgram(AMX *amx, char *filename, void *memblock) /* read in the file */ rewind(fp); - fread(memblock, 1, (size_t)hdr.size, fp); + size = fread(memblock, 1, (size_t)hdr.size, fp); fclose(fp); + if (size < (size_t)hdr.size) + return AMX_ERR_FORMAT; /* initialize the abstract machine */ memset(amx, 0, sizeof *amx); diff --git a/source/amx/amxaux.h b/source/amx/amxaux.h index 08d9efd..b9d2148 100644 --- a/source/amx/amxaux.h +++ b/source/amx/amxaux.h @@ -20,6 +20,7 @@ * * Version: $Id: amxaux.h 3612 2006-07-22 09:59:46Z thiadmer $ */ + #ifndef AMXAUX_H_INCLUDED #define AMXAUX_H_INCLUDED diff --git a/source/amx/amxcons.c b/source/amx/amxcons.c index b1d9a7a..127a446 100644 --- a/source/amx/amxcons.c +++ b/source/amx/amxcons.c @@ -1283,7 +1283,7 @@ static cell AMX_NATIVE_CALL n_getvalue(AMX *amx,const cell *params) if (c==EOL_CHAR && inlist(amx,'\r',params+2,(int)params[0]/sizeof(cell)-1)!=0) c='\r'; #endif - if ((chars>1 || chars>0 && sign>0) + if ((chars>1 || (chars>0 && sign>0)) && (n=inlist(amx,c,params+2,(int)params[0]/sizeof(cell)-1))!=0) { if (n>0) diff --git a/source/amx/amxcore.c b/source/amx/amxcore.c index 0c83f06..488b99b 100644 --- a/source/amx/amxcore.c +++ b/source/amx/amxcore.c @@ -16,6 +16,7 @@ * * Version: $Id: amxcore.c 5504 2016-05-15 13:42:30Z $ */ + #if defined _UNICODE || defined __UNICODE__ || defined UNICODE # if !defined UNICODE /* for Windows */ # define UNICODE @@ -181,7 +182,7 @@ static cell AMX_NATIVE_CALL setarg(AMX *amx,const cell *params) /* adjust the address in "value" in case of an array access */ value+=params[2]*sizeof(cell); /* verify the address */ - if (value<0 || value>=amx->hea && valuestk) + if (value<0 || (value>=amx->hea && valuestk)) return 0; /* set the value indirectly */ * (cell *)(data+(int)value) = params[3]; diff --git a/source/amx/amxdbg.c b/source/amx/amxdbg.c index 69dff00..b0adcd6 100644 --- a/source/amx/amxdbg.c +++ b/source/amx/amxdbg.c @@ -22,6 +22,7 @@ * * Version: $Id: amxdbg.c 3612 2006-07-22 09:59:46Z thiadmer $ */ + #include #include #include diff --git a/source/amx/amxdgram.c b/source/amx/amxdgram.c index d1636a5..51345a2 100644 --- a/source/amx/amxdgram.c +++ b/source/amx/amxdgram.c @@ -22,6 +22,7 @@ * * Version: $Id: amxdgram.c 3664 2006-11-08 12:09:25Z thiadmer $ */ + #include #include #if defined LINUX diff --git a/source/amx/amxfile.c b/source/amx/amxfile.c index 0f69e47..d356a5d 100644 --- a/source/amx/amxfile.c +++ b/source/amx/amxfile.c @@ -16,6 +16,7 @@ * * Version: $Id: amxfile.c 5588 2016-10-25 11:13:28Z $ */ + #if defined _UNICODE || defined __UNICODE__ || defined UNICODE # if !defined UNICODE /* for Windows */ # define UNICODE diff --git a/source/amx/amxfloat.c b/source/amx/amxfloat.c index d22ff62..0ae2e7e 100644 --- a/source/amx/amxfloat.c +++ b/source/amx/amxfloat.c @@ -17,6 +17,7 @@ * 2004-01-09: Adaptions for 64-bit cells (using "double precision"), by * Thiadmer Riemersma */ + #include /* for atof() */ #include /* for NULL */ #include diff --git a/source/amx/amxgc.c b/source/amx/amxgc.c index 06a729b..e1dc1f0 100644 --- a/source/amx/amxgc.c +++ b/source/amx/amxgc.c @@ -20,6 +20,7 @@ * * Version: $Id: amxgc.c 3660 2006-11-05 13:05:09Z thiadmer $ */ + #include #include #include /* for malloc()/free() */ diff --git a/source/amx/amxprocess.c b/source/amx/amxprocess.c index 1c2cb65..c5c7cf6 100644 --- a/source/amx/amxprocess.c +++ b/source/amx/amxprocess.c @@ -20,6 +20,7 @@ * * Version: $Id: amxprocess.c 3664 2006-11-08 12:09:25Z thiadmer $ */ + #if defined _UNICODE || defined __UNICODE__ || defined UNICODE # if !defined UNICODE /* for Windows */ # define UNICODE diff --git a/source/amx/amxstring.c b/source/amx/amxstring.c index 45927df..03bb5bf 100644 --- a/source/amx/amxstring.c +++ b/source/amx/amxstring.c @@ -16,6 +16,7 @@ * * Version: $Id: amxstring.c 5514 2016-05-20 14:26:51Z $ */ + #include #include #include diff --git a/source/amx/amxtime.c b/source/amx/amxtime.c index 4fe79ce..81c5a32 100644 --- a/source/amx/amxtime.c +++ b/source/amx/amxtime.c @@ -16,6 +16,7 @@ * * Version: $Id: amxtime.c 5588 2016-10-25 11:13:28Z $ */ + #include #include #include "amx.h" diff --git a/source/amx/fixed.c b/source/amx/fixed.c index c5365cd..b5707b9 100644 --- a/source/amx/fixed.c +++ b/source/amx/fixed.c @@ -24,6 +24,7 @@ * * Version: $Id: fixed.c 3662 2006-11-07 08:44:33Z thiadmer $ */ + #include #include /* for NULL */ #include "amx.h" diff --git a/source/amx/pawndbg.c b/source/amx/pawndbg.c index cde9942..76ada77 100644 --- a/source/amx/pawndbg.c +++ b/source/amx/pawndbg.c @@ -50,6 +50,7 @@ * needs only to transfer a file: * pawndbg myprog.amx -rs232=1 -transfer -quit */ + #include #include #include diff --git a/source/amx/pawnrun.c b/source/amx/pawnrun.c index 9bc9b9d..a345c4f 100644 --- a/source/amx/pawnrun.c +++ b/source/amx/pawnrun.c @@ -20,6 +20,7 @@ * * Version: $Id: pawnrun.c 3615 2006-07-27 07:49:08Z thiadmer $ */ + #include #include #include diff --git a/source/amx/pawnrun/prun1.c b/source/amx/pawnrun/prun1.c index d9bb5ae..c37e929 100644 --- a/source/amx/pawnrun/prun1.c +++ b/source/amx/pawnrun/prun1.c @@ -4,6 +4,7 @@ * * This file may be freely used. No warranties of any kind. */ + #include #include /* for exit() */ #include diff --git a/source/amx/pawnrun/prun2.c b/source/amx/pawnrun/prun2.c index e3b0864..465c4c5 100644 --- a/source/amx/pawnrun/prun2.c +++ b/source/amx/pawnrun/prun2.c @@ -4,6 +4,7 @@ * * This file may be freely used. No warranties of any kind. */ + #include #include #include diff --git a/source/amx/pawnrun/prun3.c b/source/amx/pawnrun/prun3.c index 657b8df..878dff1 100644 --- a/source/amx/pawnrun/prun3.c +++ b/source/amx/pawnrun/prun3.c @@ -4,6 +4,7 @@ * * This file may be freely used. No warranties of any kind. */ + #include #include /* for exit() */ #include diff --git a/source/amx/pawnrun/prun4.c b/source/amx/pawnrun/prun4.c index f308ddd..e88305c 100644 --- a/source/amx/pawnrun/prun4.c +++ b/source/amx/pawnrun/prun4.c @@ -4,6 +4,7 @@ * * This file may be freely used. No warranties of any kind. */ + #include #include /* for exit() */ #include /* for memset() (on some compilers) */ diff --git a/source/amx/pawnrun/prun5.c b/source/amx/pawnrun/prun5.c index 9301e8b..c84c09a 100644 --- a/source/amx/pawnrun/prun5.c +++ b/source/amx/pawnrun/prun5.c @@ -5,6 +5,7 @@ * * This file may be freely used. No warranties of any kind. */ + #include #include #include diff --git a/source/amx/pawnrun/prun_jit.c b/source/amx/pawnrun/prun_jit.c index 7c5e12e..7ac4ab4 100644 --- a/source/amx/pawnrun/prun_jit.c +++ b/source/amx/pawnrun/prun_jit.c @@ -19,6 +19,7 @@ * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ + #include #include #include diff --git a/source/compiler/CMakeLists.txt b/source/compiler/CMakeLists.txt index 146d8d9..b2aa8a9 100644 --- a/source/compiler/CMakeLists.txt +++ b/source/compiler/CMakeLists.txt @@ -5,7 +5,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake) set(VERSION_MAJOR 3) set(VERSION_MINOR 10) -set(VERSION_BUILD 6) +set(VERSION_BUILD 9) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_BUILD}) set(VERSION_STR ${VERSION}) math(EXPR VERSION_INT "${VERSION_MAJOR} << 8 | ${VERSION_MINOR}") @@ -139,6 +139,9 @@ target_link_libraries(pawncc pawnc) # The Pawn disassembler set(PAWNDISASM_SRCS pawndisasm.c + sc.h + ../amx/amx.h + ../amx/amxdbg.h ../amx/amxdbg.c ) add_executable(pawndisasm ${PAWNDISASM_SRCS}) @@ -160,10 +163,30 @@ if(MSVC) DESTINATION bin) endif() -# Generate tests (only if enabled, i.e. BUILD_TESTING=ON) +# Generate targets for running compiler tests include(CTest) if(BUILD_TESTING) - enable_testing() + set(PAWNRUNS_SRCS + pawnruns.c + ../amx/amx.c + ../amx/amx.h + ../amx/amxaux.c + ../amx/amxaux.h + ../amx/amxcons.c + ../amx/amxcore.c + ) + if(UNIX) + set(PAWNRUNS_SRCS + ${PAWNRUNS_SRCS} + ../linux/getch.c + ../linux/getch.h + ../linux/binreloc.c + ) + endif() + add_executable(pawnruns ${PAWNRUNS_SRCS}) + if(UNIX) + target_link_libraries(pawnruns dl) + endif() add_subdirectory(tests) endif() diff --git a/source/compiler/libpawnc.c b/source/compiler/libpawnc.c index b3d56c8..33ddc1a 100644 --- a/source/compiler/libpawnc.c +++ b/source/compiler/libpawnc.c @@ -22,6 +22,7 @@ * * Version: $Id: libpawnc.c 3612 2006-07-22 09:59:46Z thiadmer $ */ + #include #include #include diff --git a/source/compiler/lstring.h b/source/compiler/lstring.h index 9d011ec..5216390 100644 --- a/source/compiler/lstring.h +++ b/source/compiler/lstring.h @@ -7,10 +7,10 @@ #define HAVE_SAFESTR #endif -#if defined strlcpy +#if defined strlcpy && !defined HAVE_STRLCPY #define HAVE_STRLCPY #endif -#if defined strlcat +#if defined strlcat && !defined HAVE_STRLCAT #define HAVE_STRLCAT #endif diff --git a/source/compiler/pawncc.c b/source/compiler/pawncc.c index a6d19c5..bd52518 100644 --- a/source/compiler/pawncc.c +++ b/source/compiler/pawncc.c @@ -22,6 +22,7 @@ * * Version: $Id: pawncc.c 3612 2006-07-22 09:59:46Z thiadmer $ */ + #include "sc.h" int main(int argc, char *argv[]) diff --git a/source/compiler/pawndisasm.c b/source/compiler/pawndisasm.c index f5ec6f4..7d6532f 100644 --- a/source/compiler/pawndisasm.c +++ b/source/compiler/pawndisasm.c @@ -20,6 +20,7 @@ * * Version: $Id$ */ + #include #include #include @@ -221,7 +222,8 @@ static OPCODE opcodelist[] = { void print_opcode(FILE *ftxt,cell opcode,cell cip) { - fprintf(ftxt,"%08"PRIxC" %s ",cip,opcodelist[(int)(opcode &0x0000ffff)].name); + fprintf(ftxt,"%08"PRIxC" %s", + cip,opcodelist[(int)(opcode &0x0000ffff)].name); } void print_funcname(FILE *ftxt,cell address) @@ -263,21 +265,21 @@ cell parm0(FILE *ftxt,const cell *params,cell opcode,cell cip) cell parm1(FILE *ftxt,const cell *params,cell opcode,cell cip) { print_opcode(ftxt,opcode,cip); - fprintf(ftxt,"%08"PRIxC"\n",*params); + fprintf(ftxt," %08"PRIxC"\n",*params); return 2; } cell parm2(FILE *ftxt,const cell *params,cell opcode,cell cip) { print_opcode(ftxt,opcode,cip); - fprintf(ftxt,"%08"PRIxC" %08"PRIxC"\n",params[0],params[1]); + fprintf(ftxt," %08"PRIxC" %08"PRIxC"\n",params[0],params[1]); return 3; } cell parm3(FILE *ftxt,const cell *params,cell opcode,cell cip) { print_opcode(ftxt,opcode,cip); - fprintf(ftxt,"%08"PRIxC" %08"PRIxC" %08"PRIxC"\n", + fprintf(ftxt," %08"PRIxC" %08"PRIxC" %08"PRIxC"\n", params[0],params[1],params[2]); return 4; } @@ -285,7 +287,7 @@ cell parm3(FILE *ftxt,const cell *params,cell opcode,cell cip) cell parm4(FILE *ftxt,const cell *params,cell opcode,cell cip) { print_opcode(ftxt,opcode,cip); - fprintf(ftxt,"%08"PRIxC" %08"PRIxC" %08"PRIxC" %08"PRIxC"\n", + fprintf(ftxt," %08"PRIxC" %08"PRIxC" %08"PRIxC" %08"PRIxC"\n", params[0],params[1],params[2],params[3]); return 5; } @@ -293,7 +295,7 @@ cell parm4(FILE *ftxt,const cell *params,cell opcode,cell cip) cell parm5(FILE *ftxt,const cell *params,cell opcode,cell cip) { print_opcode(ftxt,opcode,cip); - fprintf(ftxt,"%08"PRIxC" %08"PRIxC" %08"PRIxC" %08"PRIxC" %08"PRIxC"\n", + fprintf(ftxt," %08"PRIxC" %08"PRIxC" %08"PRIxC" %08"PRIxC" %08"PRIxC"\n", params[0],params[1],params[2],params[3],params[4]); return 6; } @@ -309,7 +311,7 @@ cell do_proc(FILE *ftxt,const cell *params,cell opcode,cell cip) cell do_call(FILE *ftxt,const cell *params,cell opcode,cell cip) { print_opcode(ftxt,opcode,cip); - fprintf(ftxt,"%08"PRIxC,*params); + fprintf(ftxt," %08"PRIxC,*params); print_funcname(ftxt,*params); fputs("\n",ftxt); return 2; @@ -318,7 +320,7 @@ cell do_call(FILE *ftxt,const cell *params,cell opcode,cell cip) cell do_jump(FILE *ftxt,const cell *params,cell opcode,cell cip) { print_opcode(ftxt,opcode,cip); - fprintf(ftxt,"%08"PRIxC"\n",*params); + fprintf(ftxt," %08"PRIxC"\n",*params); return 2; } @@ -345,7 +347,7 @@ cell do_sysreq(FILE *ftxt,const cell *params,cell opcode,cell cip) } /* if */ print_opcode(ftxt,opcode,cip); - fprintf(ftxt,"%08"PRIxC,*params); + fprintf(ftxt," %08"PRIxC,*params); if (namelen>0) fprintf(ftxt,"\t; %s",name); fprintf(ftxt,"\n"); @@ -355,7 +357,7 @@ cell do_sysreq(FILE *ftxt,const cell *params,cell opcode,cell cip) cell do_switch(FILE *ftxt,const cell *params,cell opcode,cell cip) { print_opcode(ftxt,opcode,cip); - fprintf(ftxt,"%08"PRIxC"\n",*params); + fprintf(ftxt," %08"PRIxC"\n",*params); return 2; } @@ -366,7 +368,7 @@ cell casetbl(FILE *ftxt,const cell *params,cell opcode,cell cip) print_opcode(ftxt,opcode,cip); num=params[0]+1; - fprintf(ftxt,"%08"PRIxC" %08"PRIxC"\n",params[0],params[1]); + fprintf(ftxt," %08"PRIxC" %08"PRIxC"\n",params[0],params[1]); for (idx=1; idx +#include /* for exit() */ +#include +#include /* for memset() (on some compilers) */ +#include "../amx/amx.h" +#include "../amx/amxaux.h" + +static void ErrorExit(AMX *amx, int errorcode) +{ + printf("Run time error %d: \"%s\"\n", errorcode, aux_StrError(errorcode)); + exit(1); +} + +static void PrintUsage(char *program) +{ + printf("Usage: %s \n is a compiled script.\n", program); + exit(1); +} + +int main(int argc,char *argv[]) +{ + extern AMX_NATIVE_INFO console_Natives[]; + extern AMX_NATIVE_INFO core_Natives[]; + + AMX amx; + cell ret = 0; + int err; + + if (argc != 2) + PrintUsage(argv[0]); + + err = aux_LoadProgram(&amx, argv[1], NULL); + if (err != AMX_ERR_NONE) + ErrorExit(&amx, err); + + amx_Register(&amx, console_Natives, -1); + err = amx_Register(&amx, core_Natives, -1); + if (err != AMX_ERR_NONE) + ErrorExit(&amx, err); + + err = amx_Exec(&amx, &ret, AMX_EXEC_MAIN); + if (err != AMX_ERR_NONE) + ErrorExit(&amx, err); + printf("%s returns %ld\n", argv[1], (long)ret); + + aux_FreeProgram(&amx); + return 0; +} diff --git a/source/compiler/sc.h b/source/compiler/sc.h index 48d43fd..e417ae9 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -27,6 +27,7 @@ * * Version: $Id: sc.h 3660 2006-11-05 13:05:09Z thiadmer $ */ + #ifndef SC_H_INCLUDED #define SC_H_INCLUDED #include @@ -113,6 +114,10 @@ typedef struct s_constvalue { * tag for enumeration lists */ } constvalue; +typedef struct s_constvalue_root { + constvalue *first,*last; +} constvalue_root; + /* Symbol table format * * The symbol name read from the input file is stored in "name", the @@ -148,13 +153,13 @@ typedef struct s_symbol { } x; /* 'x' for 'extra' */ union { arginfo *arglist; /* types of all parameters for functions */ - constvalue *enumlist;/* list of names for the "root" of an enumeration */ + constvalue_root *enumlist;/* list of names for the "root" of an enumeration */ struct { cell length; /* arrays: length (size) */ short level; /* number of dimensions below this level */ } array; } dim; /* for 'dimension', both functions and arrays */ - constvalue *states; /* list of state function/state variable ids + addresses */ + constvalue_root *states;/* list of state function/state variable ids + addresses */ int fnumber; /* static global variables: file number in which the declaration is visible */ int lnumber; /* line number (in the current source file) for the declaration */ struct s_symbol **refer; /* referrer list, functions that "use" this symbol */ @@ -303,6 +308,11 @@ typedef struct s_valuepair { #define opcodes(n) ((n)*sizeof(cell)) /* opcode size */ #define opargs(n) ((n)*sizeof(cell)) /* size of typical argument */ +/* general purpose macros */ +#if !defined makelong + #define makelong(low,high) ((long)(low) | ((long)(high) << (sizeof(long)*4))) +#endif + /* Tokens recognized by lex() * Some of these constants are assigned as well to the variable "lastst" (see SC1.C) */ @@ -398,14 +408,16 @@ typedef struct s_valuepair { #define tLABEL 337 #define tSTRING 338 /* argument types for emit/__emit */ -#define teNUMERIC 339 /* integer/rational number */ -#define teDATA 340 /* data (variable name or address) */ -#define teLOCAL 341 /* local variable (name or offset) */ -#define teFUNCTN 342 /* Pawn function */ -#define teNATIVE 343 /* native function */ +#define teANY 339 /* any value */ +#define teNUMERIC 340 /* integer/rational number */ +#define teDATA 341 /* data (variable name or address) */ +#define teLOCAL 342 /* local variable (name or offset) */ +#define teFUNCTN 343 /* Pawn function */ +#define teNATIVE 344 /* native function */ +#define teNONNEG 345 /* nonnegative integer */ /* for assigment to "lastst" only (see SC1.C) */ -#define tEXPR 344 -#define tENDLESS 345 /* endless loop */ +#define tEXPR 346 +#define tENDLESS 347 /* endless loop */ /* (reversed) evaluation of staging buffer */ #define sSTARTREORDER 0x01 @@ -475,6 +487,35 @@ typedef enum s_optmark { #define MAX_INSTR_LEN 30 +#define eotNUMBER 0 +#define eotFUNCTION 1 +#define eotLABEL 2 +typedef struct s_emit_outval { + int type; + union { + ucell ucell; + const char *string; + } value; +} emit_outval; + +/* constants for error_suggest() */ +#define MAX_EDIT_DIST 2 /* allow two mis-typed characters; when there are more, + * the names are too different, and no match is returned */ +enum { /* identifier types */ + estSYMBOL = 0, + estNONSYMBOL, + estAUTOMATON, + estSTATE +}; +enum { /* symbol types */ + essNONLABEL, /* find symbols of any type but labels */ + essVARCONST, /* array, single variable or named constant */ + essARRAY, + essCONST, + essFUNCTN, + essLABEL +}; + /* interface functions */ #if defined __cplusplus extern "C" { @@ -549,13 +590,14 @@ long pc_lengthbin(void *handle); /* return the length of the file */ SC_FUNC void set_extension(char *filename,char *extension,int force); SC_FUNC symbol *fetchfunc(char *name,int tag); SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag); +SC_FUNC void check_index_tagmismatch(char *symname,int expectedtag,int actualtag,int allowcoerce,int errline); SC_FUNC void check_tagmismatch(int formaltag,int actualtag,int allowcoerce,int errline); SC_FUNC void check_tagmismatch_multiple(int formaltags[],int numtags,int actualtag,int errline); SC_FUNC char *funcdisplayname(char *dest,char *funcname); SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr); -SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index); -SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index); -SC_FUNC void delete_consttable(constvalue *table); +SC_FUNC constvalue *append_constval(constvalue_root *table,const char *name,cell val,int index); +SC_FUNC constvalue *find_constval(constvalue_root *table,char *name,int index); +SC_FUNC void delete_consttable(constvalue_root *table); SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag); SC_FUNC symbol *add_builtin_constant(char *name,cell val,int vclass,int tag); SC_FUNC symbol *add_builtin_string_constant(char *name,const char *val,int vclass); @@ -622,7 +664,7 @@ SC_FUNC void setlinedirect(int line); SC_FUNC void setlineconst(int line); SC_FUNC void setlabel(int index); SC_FUNC void markexpr(optmark type,const char *name,cell offset); -SC_FUNC void startfunc(char *fname); +SC_FUNC void startfunc(char *fname,int generateproc); SC_FUNC void endfunc(void); SC_FUNC void alignframe(int numbytes); SC_FUNC void rvalue(value *lval); @@ -699,11 +741,12 @@ SC_FUNC void dec(value *lval); SC_FUNC void jmp_ne0(int number); SC_FUNC void jmp_eq0(int number); SC_FUNC void outval(cell val,int newline); -SC_FUNC void outinstr(const char *name,ucell args[],int numargs); +SC_FUNC void outinstr(const char *name,emit_outval params[],int numparams); /* function prototypes in SC5.C */ -SC_FUNC int error(int number,...); +SC_FUNC int error(long number,...); SC_FUNC void errorset(int code,int line); +SC_FUNC int error_suggest(int error,const char *name,const char *name2,int type,int subtype); /* function prototypes in SC6.C */ SC_FUNC int assemble(FILE *fout,FILE *fin); @@ -801,8 +844,8 @@ SC_VDECL symbol *line_sym; SC_VDECL cell *litq; /* the literal queue */ SC_VDECL unsigned char pline[]; /* the line read from the input file */ SC_VDECL const unsigned char *lptr;/* points to the current position in "pline" */ -SC_VDECL constvalue tagname_tab;/* tagname table */ -SC_VDECL constvalue libname_tab;/* library table (#pragma library "..." syntax) */ +SC_VDECL constvalue_root tagname_tab;/* tagname table */ +SC_VDECL constvalue_root libname_tab;/* library table (#pragma library "..." syntax) */ SC_VDECL constvalue *curlibrary;/* current library */ SC_VDECL int pc_addlibtable; /* is the library table added to the AMX file? */ SC_VDECL symbol *curfunc; /* pointer to current function */ @@ -859,8 +902,8 @@ SC_VDECL int pc_naked; /* if true mark following function as naked */ SC_VDECL int pc_compat; /* running in compatibility mode? */ SC_VDECL int pc_recursion; /* enable detailed recursion report? */ -SC_VDECL constvalue sc_automaton_tab; /* automaton table */ -SC_VDECL constvalue sc_state_tab; /* state table */ +SC_VDECL constvalue_root sc_automaton_tab; /* automaton table */ +SC_VDECL constvalue_root sc_state_tab; /* state table */ SC_VDECL FILE *inpf; /* file read from (source or include) */ SC_VDECL FILE *inpf_org; /* main source file */ diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index b1ad703..fcec5ec 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -22,6 +22,7 @@ * * Version: $Id: sc1.c 3664 2006-11-08 12:09:25Z thiadmer $ */ + #include #include #include @@ -92,14 +93,14 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic, static int declloc(int fstatic); static void decl_const(int table); static void decl_enum(int table,int fstatic); -static cell needsub(int *tag,constvalue **enumroot); +static cell needsub(int *tag,constvalue_root **enumroot); static void initials(int ident,int tag,cell *size,int dim[],int numdim, - constvalue *enumroot); + constvalue_root *enumroot); static cell initarray(int ident,int tag,int dim[],int numdim,int cur, - int startlit,int counteddim[],constvalue *lastdim, - constvalue *enumroot,int *errorfound); + int startlit,int counteddim[],constvalue_root *lastdim, + constvalue_root *enumroot,int *errorfound); static cell initvector(int ident,int tag,cell size,int startlit,int fillzero, - constvalue *enumroot,int *errorfound); + constvalue_root *enumroot,int *errorfound); static cell init(int ident,int *tag,int *errorfound); static int getstates(const char *funcname); static void attachstatelist(symbol *sym, int state_id); @@ -113,7 +114,7 @@ static void reduce_referrers(symbol *root); static long max_stacksize(symbol *root,int *recursion); static int testsymbols(symbol *root,int level,int testlabs,int testconst); static void destructsymbols(symbol *root,int level); -static constvalue *find_constval_byval(constvalue *table,cell val); +static constvalue *find_constval_byval(constvalue_root *table,cell val); static symbol *fetchlab(char *name); static void statement(int *lastindent,int allow_decl); static void compound(int stmt_sameline,int starttok); @@ -137,7 +138,6 @@ static void dostate(void); static void addwhile(int *ptr); static void delwhile(void); static int *readwhile(void); -static void doemit(void); typedef void (SC_FASTCALL *OPCODE_PROC)(char *name); typedef struct { @@ -842,7 +842,7 @@ int pc_addtag(char *name) assert(strchr(name,':')==NULL); /* colon should already have been stripped */ last=0; - ptr=tagname_tab.next; + ptr=tagname_tab.first; while (ptr!=NULL) { tag=(int)(ptr->value & TAGMASK); if (strcmp(name,ptr->name)==0) @@ -938,8 +938,8 @@ static void initglobals(void) glbtab.next=NULL; /* clear global variables/constants table */ loctab.next=NULL; /* " local " / " " */ hashtable_init(&symbol_cache_ht, sizeof(symbol *),(16384/3*2),NULL); /* 16384 slots */ - tagname_tab.next=NULL; /* tagname table */ - libname_tab.next=NULL; /* library table (#pragma library "..." syntax) */ + tagname_tab.first=tagname_tab.last=NULL; /* tagname table */ + libname_tab.first=libname_tab.last=NULL; /* library table (#pragma library "..." syntax) */ pline[0]='\0'; /* the line read from the input file */ lptr=NULL; /* points to the current position in "pline" */ @@ -994,7 +994,7 @@ static int toggle_option(const char *optptr, int option) { switch (*option_value(optptr)) { case '\0': - option=!option; + option=TRUE; break; case '-': option=FALSE; @@ -1065,7 +1065,8 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam if (chdir(ptr)==-1) ; /* silently ignore chdir() errors */ break; - case 'd': + case 'd': { + int debug; switch (*option_value(ptr)) { case '0': sc_debug=0; @@ -1084,7 +1085,14 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam default: about(); } /* switch */ + debug=0; + if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC))==(sCHKBOUNDS | sSYMBOLIC)) + debug=2; + else if ((sc_debug & sCHKBOUNDS)==sCHKBOUNDS) + debug=1; + add_builtin_constant("debug",debug,sGLOBAL,0); break; + } /* case */ case 'e': if (ename) strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */ @@ -1208,9 +1216,16 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam about(); } /* if */ break; - case 'Z': + case 'Z': { + symbol *sym; pc_compat=toggle_option(ptr,pc_compat); + sym=findconst("__compat",NULL); + if (sym!=NULL) { + assert(sym!=NULL); + sym->addr=pc_compat; + } /* if */ break; + } /* case */ case '\\': /* use \ instead for escape characters */ sc_ctrlchar='\\'; break; @@ -1983,7 +1998,7 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fst int numdim; short filenum; symbol *sym; - constvalue *enumroot; + constvalue_root *enumroot=NULL; #if !defined NDEBUG cell glbdecl=0; #endif @@ -2003,7 +2018,7 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fst } else { tag=pc_addtag(NULL); if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ - error(20,str); /* invalid symbol name */ + error_suggest(20,str,NULL,estSYMBOL,essFUNCTN); /* invalid symbol name */ assert(strlen(str)<=sNAMEMAX); strcpy(name,str); /* save symbol name */ } /* if */ @@ -2054,11 +2069,11 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fst */ assert(sym==NULL || sym->states==NULL && sc_curstates==0 - || sym->states!=NULL && sym->next!=NULL && sym->states->next->index==sc_curstates); + || sym->states!=NULL && sym->next!=NULL && sym->states->first->index==sc_curstates); /* a state variable may only have a single id in its list (so either this * variable has no states, or it has a single list) */ - assert(sym==NULL || sym->states==NULL || sym->states->next->next==NULL); + assert(sym==NULL || sym->states==NULL || sym->states->first->next==NULL); /* it is okay for the (global) variable to exist, as long as it belongs to * a different automaton */ @@ -2116,7 +2131,7 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fst continue; /* a function or a constant */ if ((sweep->usage & uDEFINE)==0) continue; /* undefined variable, ignore */ - if (fsa!=state_getfsa(sweep->states->next->index)) + if (fsa!=state_getfsa(sweep->states->first->index)) continue; /* wrong automaton */ /* when arrived here, this is a global variable, with states and * belonging to the same automaton as the variable we are declaring @@ -2138,7 +2153,7 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fst continue; /* a function or a constant */ if ((sweep->usage & uDEFINE)==0) continue; /* undefined variable, ignore */ - if (fsa!=state_getfsa(sweep->states->next->index)) + if (fsa!=state_getfsa(sweep->states->first->index)) continue; /* wrong automaton */ /* when arrived here, this is a global variable, with states and * belonging to the same automaton as the variable we are declaring @@ -2147,7 +2162,7 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fst * variable have a non-empty intersection, this is not a suitable * overlap point -> wipe the address range */ - if (state_conflict_id(sc_curstates,sweep->states->next->index)) { + if (state_conflict_id(sc_curstates,sweep->states->first->index)) { sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep); assert(sweep->addr % sizeof(cell) == 0); addr=sweep->addr/sizeof(cell); @@ -2194,7 +2209,7 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fst attachstatelist(sym,sc_curstates); } else { /* if declared but not yet defined, adjust the variable's address */ assert(sym->states==NULL && sc_curstates==0 - || sym->states->next!=NULL && sym->states->next->index==sc_curstates && sym->states->next->next==NULL); + || sym->states->first!=NULL && sym->states->first->index==sc_curstates && sym->states->first->next==NULL); sym->addr=address; sym->codeaddr=code_idx; sym->usage|=uDEFINE; @@ -2236,7 +2251,7 @@ static int declloc(int fstatic) int idxtag[sDIMEN_MAX]; char name[sNAMEMAX+1]; symbol *sym; - constvalue *enumroot; + constvalue_root *enumroot=NULL; cell val,size; char *str; value lval = {0}; @@ -2418,7 +2433,7 @@ static cell calc_arraysize(int dim[],int numdim,int cur) } static void adjust_indirectiontables(int dim[],int numdim,int startlit, - constvalue *lastdim,int *skipdim) + constvalue_root *lastdim,int *skipdim) { static int base; int cur; @@ -2444,7 +2459,7 @@ static int base; accum=0; for (i=0; inext; d<*skipdim; d++,ld=ld->next) { + for (d=0,ld=lastdim->first; d<*skipdim; d++,ld=ld->next) { assert(ld!=NULL); } /* for */ for (d=0; dnext : NULL; + constvalue *enumfield=(enumroot!=NULL) ? enumroot->first : NULL; do { int fieldlit=litidx; int matchbrace,i; @@ -2796,7 +2815,7 @@ static cell init(int ident,int *tag,int *errorfound) * * Get required array size */ -static cell needsub(int *tag,constvalue **enumroot) +static cell needsub(int *tag,constvalue_root **enumroot) { cell val; symbol *sym; @@ -2868,8 +2887,8 @@ static void decl_enum(int vclass,int fstatic) char *str; int tag,explicittag; cell increment,multiplier; - constvalue *enumroot; - symbol *enumsym; + constvalue_root *enumroot=NULL; + symbol *enumsym=NULL; short filenum; filenum=fcurrent; @@ -2922,12 +2941,9 @@ static void decl_enum(int vclass,int fstatic) enumsym->fnumber=filenum; } /* start a new list for the element names */ - if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL) + if ((enumroot=(constvalue_root*)malloc(sizeof(constvalue_root)))==NULL) error(103); /* insufficient memory (fatal error) */ - memset(enumroot,0,sizeof(constvalue)); - } else { - enumsym=NULL; - enumroot=NULL; + memset(enumroot,0,sizeof(constvalue_root)); } /* if */ needtoken('{'); @@ -3076,15 +3092,15 @@ static void attachstatelist(symbol *sym, int state_id) /* add the state list id */ constvalue *stateptr; if (sym->states==NULL) { - if ((sym->states=(constvalue*)malloc(sizeof(constvalue)))==NULL) + if ((sym->states=(constvalue_root*)malloc(sizeof(constvalue_root)))==NULL) error(103); /* insufficient memory (fatal error) */ - memset(sym->states,0,sizeof(constvalue)); + memset(sym->states,0,sizeof(constvalue_root)); } /* if */ /* see whether the id already exists (add new state only if it does not * yet exist */ assert(sym->states!=NULL); - for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next) + for (stateptr=sym->states->first; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next) /* nothing */; assert(state_id<=SHRT_MAX); if (stateptr==NULL) @@ -3099,7 +3115,7 @@ static void attachstatelist(symbol *sym, int state_id) if (state_id==-1 && sc_status!=statFIRST) { /* in the second round, all states should have been accumulated */ assert(sym->states!=NULL); - for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next) + for (stateptr=sym->states->first; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next) /* nothing */; if (stateptr==NULL) error(85,sym->name); /* no states are defined for this function */ @@ -3136,8 +3152,7 @@ SC_FUNC symbol *fetchfunc(char *name,int tag) sym=addsym(name,code_idx,iFUNCTN,sGLOBAL,tag,0); assert(sym!=NULL); /* fatal error 103 must be given on error */ /* assume no arguments */ - sym->dim.arglist=(arginfo*)malloc(1*sizeof(arginfo)); - sym->dim.arglist[0].ident=0; + sym->dim.arglist=(arginfo*)calloc(1,sizeof(arginfo)); /* set library ID to NULL (only for native functions) */ sym->x.lib=NULL; /* set the required stack size to zero (only for non-native functions) */ @@ -3396,14 +3411,36 @@ static constvalue *find_tag_byval(int tag) return tagsym; } +SC_FUNC void check_index_tagmismatch(char *symname,int expectedtag,int actualtag,int allowcoerce,int errline) +{ + assert(symname!=NULL); + if (!matchtag(expectedtag,actualtag,allowcoerce)) { + constvalue *tagsym; + char expected_tagname[sNAMEMAX+3]="none (\"_\"),",actual_tagname[sNAMEMAX+2]="none (\"_\")"; /* two extra characters for quotes */ + if(expectedtag!=0) { + tagsym=find_tag_byval(expectedtag); + sprintf(expected_tagname,"\"%s\",",(tagsym!=NULL) ? tagsym->name : "-unknown-"); + } /* if */ + if(actualtag!=0) { + tagsym=find_tag_byval(actualtag); + sprintf(actual_tagname,"\"%s\"",(tagsym!=NULL) ? tagsym->name : "-unknown-"); + } /* if */ + if(errline>0) + errorset(sSETPOS,errline); + error(229,symname,expected_tagname,actual_tagname); /* index tag mismatch */ + if(errline>0) + errorset(sSETPOS,-1); + } /* if */ +} + SC_FUNC void check_tagmismatch(int formaltag,int actualtag,int allowcoerce,int errline) { if (!matchtag(formaltag,actualtag,allowcoerce)) { constvalue *tagsym; - char formal_tagname[sNAMEMAX+3]="none (\"_\"),",actual_tagname[sNAMEMAX+2]="none (\"_\")"; /* two extra characters for quotes */ + char formal_tagname[sNAMEMAX+3]="none (\"_\"),",actual_tagname[sNAMEMAX+2]="none (\"_\")"; /* two extra characters for quotes */ if(formaltag!=0) { tagsym=find_tag_byval(formaltag); - sprintf(formal_tagname,"\"%s\",", (tagsym!=NULL) ? tagsym->name : "-unknown-"); + sprintf(formal_tagname,"\"%s\",",(tagsym!=NULL) ? tagsym->name : "-unknown-"); } /* if */ if(actualtag!=0) { tagsym=find_tag_byval(actualtag); @@ -3413,7 +3450,7 @@ SC_FUNC void check_tagmismatch(int formaltag,int actualtag,int allowcoerce,int e errorset(sSETPOS,errline); error(213,"tag",formal_tagname,actual_tagname); /* tag mismatch */ if(errline>0) - errorset(sSETPOS,-1); + errorset(sSETPOS,-1); } /* if */ } @@ -3424,7 +3461,7 @@ SC_FUNC void check_tagmismatch_multiple(int formaltags[],int numtags,int actualt constvalue *tagsym; char formal_tagnames[sLINEMAX]="",actual_tagname[sNAMEMAX+2]="none (\"_\")"; int notag_allowed=FALSE,add_comma=FALSE; - for (i=0; iname : "-unknown-"); + sprintf(formal_tagnames,"%s\"%s\"",formal_tagnames,(tagsym!=NULL) ? tagsym->name : "-unknown-"); } else { notag_allowed=TRUE; - } /* if */ + } /* if */ } /* for */ if(notag_allowed==TRUE) { if(add_comma==TRUE) @@ -3446,12 +3483,12 @@ SC_FUNC void check_tagmismatch_multiple(int formaltags[],int numtags,int actualt if(actualtag!=0) { tagsym=find_tag_byval(actualtag); sprintf(actual_tagname,"\"%s\"",(tagsym!=NULL) ? tagsym->name : "-unknown-"); - } /* if */ + } /* if */ if(errline>0) errorset(sSETPOS,errline); error(213,(numtags==1) ? "tag" : "tags",formal_tagnames,actual_tagname); /* tag mismatch */ if(errline>0) - errorset(sSETPOS,-1); + errorset(sSETPOS,-1); } /* if */ } @@ -3753,7 +3790,7 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc sym->usage &= ~uDEFINE; /* if the function has states, dump the label to the start of the function */ if (state_id!=0) { - constvalue *ptr=sym->states->next; + constvalue *ptr=sym->states->first; while (ptr!=NULL) { assert(sc_status!=statWRITE || strlen(ptr->name)>0); if (ptr->index==state_id) { @@ -3763,7 +3800,7 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc ptr=ptr->next; } /* while */ } /* if */ - startfunc(sym->name); /* creates stack frame */ + startfunc(sym->name,(sym->flags & flagNAKED)==0); /* creates stack frame */ insert_dbgline(funcline); setline(FALSE); if (sc_alignnext) { @@ -3793,7 +3830,7 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc sc_curstates=0; if ((rettype & uRETVALUE)!=0) sym->usage|=uRETVALUE; - if (declared!=0) { + if (declared!=0 && (curfunc->flags & flagNAKED)==0) { /* This happens only in a very special (and useless) case, where a function * has only a single statement in its body (no compound block) and that * statement declares a new variable @@ -3801,10 +3838,11 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc modstk((int)declared*sizeof(cell)); /* remove all local variables */ declared=0; } /* if */ - if ((lastst!=tRETURN) && (lastst!=tGOTO)){ + if ((lastst!=tRETURN) && (lastst!=tGOTO) && (sym->flags & flagNAKED)==0) { + destructsymbols(&loctab,0); ldconst(0,sPRI); ffret(strcmp(sym->name,uENTRYFUNC)!=0); - if ((sym->usage & uRETVALUE)!=0 && (sym->flags & flagNAKED)==0) { + if ((sym->usage & uRETVALUE)!=0) { char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ funcdisplayname(symname,sym->name); error(209,symname); /* function should return a value */ @@ -3917,6 +3955,8 @@ static int declargs(symbol *sym,int chkshadow) case '&': if (ident!=iVARIABLE || numtags>0) error(1,"-identifier-","&"); + if (fconst) + error(238, "const reference"); /* meaningless combination of class specifiers */ ident=iREFERENCE; break; case tCONST: @@ -3991,6 +4031,8 @@ static int declargs(symbol *sym,int chkshadow) case tELLIPS: if (ident!=iVARIABLE) error(10); /* illegal function or declaration */ + if (fconst) + error(238, "const variable arguments"); /* meaningless combination of class specifiers */ if (numtags==0) tags[numtags++]=0; /* default tag */ if ((sym->usage & uPROTOTYPED)==0) { @@ -4077,7 +4119,7 @@ static void doarg(char *name,int ident,int offset,int tags[],int numtags, int fpublic,int fconst,int chkshadow,arginfo *arg) { symbol *argsym; - constvalue *enumroot; + constvalue_root *enumroot=NULL; cell size; strcpy(arg->name,name); @@ -4162,7 +4204,7 @@ static void doarg(char *name,int ident,int offset,int tags[],int numtags, } else { constexpr(&arg->defvalue.val,&arg->defvalue_tag,NULL); assert(numtags>0); - check_tagmismatch(tags[0],arg->defvalue_tag,TRUE,-1); + check_tagmismatch(tags[0],arg->defvalue_tag,TRUE,-1); } /* if */ } /* if */ } /* if */ @@ -4183,8 +4225,12 @@ static void doarg(char *name,int ident,int offset,int tags[],int numtags, assert(numtags>0); argsym=addvariable(name,offset,ident,sLOCAL,tags[0], arg->dim,arg->numdim,arg->idxtag,0); - if (fpublic) + if (fpublic) { argsym->usage|=uREAD; /* arguments of public functions are always "used" */ + if(argsym->ident==iREFARRAY || argsym->ident==iREFERENCE) + argsym->usage|=uWRITTEN; + } + if (fconst) argsym->usage|=uCONST; } /* if */ @@ -4338,7 +4384,7 @@ static void make_report(symbol *root,FILE *log,char *sourcefile) int i,arg; symbol *sym,*ref; constvalue *tagsym; - constvalue *enumroot; + constvalue_root *enumroot; char *ptr; /* adapt the installation directory */ @@ -4392,11 +4438,11 @@ static void make_report(symbol *root,FILE *log,char *sourcefile) } /* if */ /* browse through all fields */ if ((enumroot=sym->dim.enumlist)!=NULL) { - enumroot=enumroot->next; /* skip root */ - while (enumroot!=NULL) { - fprintf(log,"\t\t\t\n",funcdisplayname(symname,enumroot->name),enumroot->value); + constvalue *cur=enumroot->first; /* skip root */ + while (cur!=NULL) { + fprintf(log,"\t\t\t\n",funcdisplayname(symname,cur->name),cur->value); /* find the constant with this name and get the tag */ - ref=findglb(enumroot->name,sGLOBAL); + ref=findglb(cur->name,sGLOBAL); if (ref!=NULL) { if (ref->x.tags.index!=0) { tagsym=find_tag_byval(ref->x.tags.index); @@ -4407,7 +4453,7 @@ static void make_report(symbol *root,FILE *log,char *sourcefile) fprintf(log,"\t\t\t\t\n",(long)ref->dim.array.length); } /* if */ fprintf(log,"\t\t\t\n"); - enumroot=enumroot->next; + cur=cur->next; } /* while */ } /* if */ assert(sym->refer!=NULL); @@ -4521,7 +4567,7 @@ static void make_report(symbol *root,FILE *log,char *sourcefile) if ((sym->usage & uNATIVE)==0) fprintf(log,"\t\t\t\n",(long)sym->x.stacksize); if (sym->states!=NULL) { - constvalue *stlist=sym->states->next; + constvalue *stlist=sym->states->first; assert(stlist!=NULL); /* there should be at least one state item */ while (stlist!=NULL && stlist->index==-1) stlist=stlist->next; @@ -4823,7 +4869,7 @@ static int testsymbols(symbol *root,int level,int testlabs,int testconst) case iLABEL: if (testlabs) { if ((sym->usage & uDEFINE)==0) { - error(19,sym->name); /* not a label: ... */ + error_suggest(19,sym->name,NULL,estSYMBOL,essLABEL); /* not a label: ... */ } else if ((sym->usage & uREAD)==0) { errorset(sSETPOS,sym->lnumber); error(203,sym->name); /* symbol isn't used: ... */ @@ -4862,12 +4908,21 @@ static int testsymbols(symbol *root,int level,int testlabs,int testconst) errorset(sSETPOS,sym->lnumber); error(204,sym->name); /* value assigned to symbol is never used */ errorset(sSETPOS,-1); -#if 0 // ??? not sure whether it is a good idea to force people use "const" } else if ((sym->usage & (uWRITTEN | uPUBLIC | uCONST))==0 && sym->ident==iREFARRAY) { - errorset(sSETPOS,sym->lnumber); - error(214,sym->name); /* make array argument "const" */ - errorset(sSETPOS,-1); -#endif + int warn = 1; + symbol* depend = finddepend(sym); + while (depend != NULL) { + if ((depend->usage & (uWRITTEN | uPUBLIC | uCONST)) != 0) { + warn = 0; + break; + } + depend = finddepend(depend); + } /* while */ + if (warn) { + errorset(sSETPOS, sym->lnumber); + error(214, sym->name); /* make array argument "const" */ + errorset(sSETPOS, -1); + } /* if */ } /* if */ /* also mark the variable (local or global) to the debug information */ if ((sym->usage & (uWRITTEN | uREAD))!=0 && (sym->usage & uNATIVE)==0) @@ -4891,7 +4946,7 @@ static cell calc_array_datasize(symbol *sym, cell *offset) if (offset!=NULL) *offset=length*(*offset+sizeof(cell)); if (sublength>0) - length*=length*sublength; + length*=sublength; else length=0; } else { @@ -4907,13 +4962,21 @@ static void destructsymbols(symbol *root,int level) int savepri=FALSE; symbol *sym=root->next; while (sym!=NULL && sym->compound>=level) { - if (sym->ident==iVARIABLE || sym->ident==iARRAY) { + if ((sym->ident==iVARIABLE || sym->ident==iARRAY) && !(sym->vclass==sSTATIC && sym->fnumber==-1)) { char symbolname[16]; symbol *opsym; cell elements; /* check that the '~' operator is defined for this tag */ operator_symname(symbolname,"~",sym->tag,0,1,0); if ((opsym=findglb(symbolname,sGLOBAL))!=NULL) { + if ((opsym->usage & uMISSING)!=0 || (opsym->usage & uPROTOTYPED)==0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + funcdisplayname(symname,opsym->name); + if ((opsym->usage & uMISSING)!=0) + error(4,symname); /* function not defined */ + if ((opsym->usage & uPROTOTYPED)==0) + error(71,symname); /* operator must be declared before use */ + } /* if */ /* save PRI, in case of a return statment */ if (!savepri) { pushreg(sPRI); /* right-hand operand is in PRI */ @@ -4921,6 +4984,15 @@ static void destructsymbols(symbol *root,int level) } /* if */ /* if the variable is an array, get the number of elements */ if (sym->ident==iARRAY) { + /* according to the PAWN Implementer Guide, the destructor + * should be triggered for the data of the array only; hence + * if the array is a part of a larger array, it must be ignored + * as it's parent would(or has already) trigger(ed) the destructor + */ + if (sym->parent!=NULL) { + sym=sym->next; + continue; + } /* if */ elements=calc_array_datasize(sym,&offset); /* "elements" can be zero when the variable is declared like * new mytag: myvar[2][] = { {1, 2}, {3, 4} } @@ -4953,8 +5025,8 @@ static void destructsymbols(symbol *root,int level) popreg(sPRI); } -static constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val, - int index) +static constvalue *insert_constval(constvalue *prev,constvalue *next, + const char *name,cell val,int index) { constvalue *cur; @@ -4968,23 +5040,29 @@ static constvalue *insert_constval(constvalue *prev,constvalue *next,const char cur->value=val; cur->index=index; cur->next=next; - prev->next=cur; + if (prev!=NULL) + prev->next=cur; return cur; } -SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index) +SC_FUNC constvalue *append_constval(constvalue_root *table,const char *name, + cell val,int index) { - constvalue *cur,*prev; + constvalue *newvalue; - /* find the end of the constant table */ - for (prev=table, cur=table->next; cur!=NULL; prev=cur, cur=cur->next) - /* nothing */; - return insert_constval(prev,NULL,name,val,index); + if (table->last!=NULL) { + newvalue=insert_constval(table->last,NULL,name,val,index); + } else { + newvalue=insert_constval(NULL,NULL,name,val,index); + table->first=newvalue; + } /* if */ + table->last=newvalue; + return newvalue; } -SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index) +SC_FUNC constvalue *find_constval(constvalue_root *table,char *name,int index) { - constvalue *ptr = table->next; + constvalue *ptr = table->first; while (ptr!=NULL) { if (strcmp(name,ptr->name)==0 && ptr->index==index) @@ -4994,9 +5072,9 @@ SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index) return NULL; } -static constvalue *find_constval_byval(constvalue *table,cell val) +static constvalue *find_constval_byval(constvalue_root *table,cell val) { - constvalue *ptr = table->next; + constvalue *ptr = table->first; while (ptr!=NULL) { if (ptr->value==val) @@ -5007,14 +5085,19 @@ static constvalue *find_constval_byval(constvalue *table,cell val) } #if 0 /* never used */ -static int delete_constval(constvalue *table,char *name) +static int delete_constval(constvalue_root *table,char *name) { - constvalue *prev = table; - constvalue *cur = prev->next; + constvalue *prev=NULL; + constvalue *cur=table->first; while (cur!=NULL) { if (strcmp(name,cur->name)==0) { - prev->next=cur->next; + if (prev!=NULL) + prev->next=cur->next; + else + table->first=cur->next; + if (table->last==cur) + table->last=prev; free(cur); return TRUE; } /* if */ @@ -5025,16 +5108,16 @@ static int delete_constval(constvalue *table,char *name) } #endif -SC_FUNC void delete_consttable(constvalue *table) +SC_FUNC void delete_consttable(constvalue_root *table) { - constvalue *cur=table->next, *next; + constvalue *cur=table->first, *next; while (cur!=NULL) { next=cur->next; free(cur); cur=next; } /* while */ - memset(table,0,sizeof(constvalue)); + memset(table,0,sizeof(constvalue_root)); } /* add_constant @@ -5286,7 +5369,6 @@ static void statement(int *lastindent,int allow_decl) extern char *sc_tokens[]; const unsigned char *bck_lptr=lptr-strlen(sc_tokens[tok-tFIRST]); if (matchtoken('{')) { - lexpush(); emit_flags |= efBLOCK; lastst=tEMIT; break; @@ -5295,7 +5377,7 @@ static void statement(int *lastindent,int allow_decl) lexclr(FALSE); tok=lex(&val,&st); } /* case */ - /* drop through */ + /* fallthrough */ default: /* non-empty expression */ sc_allowproccall=optproccall; lexpush(); /* analyze token later */ @@ -5351,9 +5433,11 @@ static void compound(int stmt_sameline,int starttok) } /* if */ } /* while */ if (lastst!=tRETURN) - destructsymbols(&loctab,nestlevel); + if (nestlevel >= 1 || (curfunc->flags & flagNAKED)==0) + destructsymbols(&loctab,nestlevel); if (lastst!=tRETURN && lastst!=tGOTO) - modstk((int)(declared-save_decl)*sizeof(cell)); /* delete local variable space */ + if (nestlevel >= 1 || (curfunc->flags & flagNAKED)==0) + modstk((int)(declared-save_decl)*sizeof(cell)); /* delete local variable space */ testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */ declared=save_decl; delete_symbols(&loctab,nestlevel,FALSE,TRUE); /* erase local symbols, but @@ -5709,8 +5793,8 @@ static void doswitch(void) int tok,endtok; cell val; char *str; - constvalue caselist = { NULL, "", 0, 0}; /* case list starts empty */ - constvalue *cse,*csp; + constvalue_root caselist = { NULL, NULL}; /* case list starts empty */ + constvalue *cse,*csp,*newval; char labelname[sNAMEMAX+1]; endtok= matchtoken('(') ? ')' : tDO; @@ -5758,7 +5842,7 @@ static void doswitch(void) * that advanced abstract machines can sift the case table with a * binary search). Check for duplicate case values at the same time. */ - for (csp=&caselist, cse=caselist.next; + for (csp=NULL, cse=caselist.first; cse!=NULL && cse->valuenext) /* nothing */; @@ -5771,9 +5855,10 @@ static void doswitch(void) #if sNAMEMAX < 8 #error Length of identifier (sNAMEMAX) too small. #endif - assert(csp!=NULL); - assert(csp->next==cse); - insert_constval(csp,cse,itoh(lbl_case),val,0); + assert(csp==NULL || csp->next==cse); + newval=insert_constval(csp,cse,itoh(lbl_case),val,0); + if (csp==NULL) + caselist.first=newval; if (matchtoken(tDBLDOT)) { cell end; constexpr(&end,NULL,NULL); @@ -5782,14 +5867,13 @@ static void doswitch(void) while (++val<=end) { casecount++; /* find the new insertion point */ - for (csp=&caselist, cse=caselist.next; + for (csp=NULL, cse=caselist.first; cse!=NULL && cse->valuenext) /* nothing */; if (cse!=NULL && cse->value==val) error(40,val); /* duplicate "case" label */ - assert(csp!=NULL); - assert(csp->next==cse); + assert(csp==NULL || csp->next==cse); insert_constval(csp,cse,itoh(lbl_case),val,0); } /* if */ } /* if */ @@ -5827,7 +5911,7 @@ static void doswitch(void) /* verify that the case table is sorted (unfortunatly, duplicates can * occur; there really shouldn't be duplicate cases, but the compiler * may not crash or drop into an assertion for a user error). */ - for (cse=caselist.next; cse!=NULL && cse->next!=NULL; cse=cse->next) + for (cse=caselist.first; cse!=NULL && cse->next!=NULL; cse=cse->next) assert(cse->value <= cse->next->value); #endif /* generate the table here, before lbl_exit (general jump target) */ @@ -5842,7 +5926,7 @@ static void doswitch(void) } /* if */ ffcase(casecount,labelname,TRUE); /* generate the rest of the table */ - for (cse=caselist.next; cse!=NULL; cse=cse->next) + for (cse=caselist.first; cse!=NULL; cse=cse->next) ffcase(cse->value,cse->name,FALSE); setlabel(lbl_exit); @@ -5889,7 +5973,7 @@ static void dogoto(void) // sym->compound (nesting level of the label) against nestlevel; // if sym->compound < nestlevel, call the destructor operator } else { - error(20,st); /* illegal symbol name */ + error_suggest(20,st,NULL,estSYMBOL,essLABEL); /* illegal symbol name */ } /* if */ needtoken(tTERM); } @@ -5929,7 +6013,7 @@ static symbol *fetchlab(char *name) sym=findloc(name); /* labels are local in scope */ if (sym) { if (sym->ident!=iLABEL) - error(19,sym->name); /* not a label: ... */ + error_suggest(19,sym->name,NULL,estSYMBOL,essLABEL); /* not a label: ... */ } else { sym=addsym(name,getlabel(),iLABEL,sLOCAL,0,0); assert(sym!=NULL); /* fatal error 103 must be given on error */ @@ -5953,23 +6037,26 @@ static void SC_FASTCALL emit_invalid_token(int expected_token,int found_token) } /* if */ } -static void SC_FASTCALL emit_param_any(ucell *p) +static int SC_FASTCALL emit_param_any_internal(emit_outval *p,int expected_tok, + int allow_nonint,int allow_expr) { char *str; cell val,cidx; symbol *sym; - extern char *sc_tokens[]; - int tok,neg,ident,index; + int tok,negate,ident,index; - neg=FALSE; + negate=FALSE; + p->type=eotNUMBER; fetchtok: tok=lex(&val,&str); switch (tok) { case tNUMBER: - *p=(neg!=FALSE) ? -val : val; + p->value.ucell=(ucell)(negate ? -val : val); break; case tRATIONAL: - *p=(neg!=FALSE) ? (val|((cell)1 << (PAWN_CELL_SIZE-1))) : val; + if (!allow_nonint) + goto invalid_token; + p->value.ucell=(ucell)(negate ? (val|((cell)1 << (PAWN_CELL_SIZE-1))) : val); break; case tSYMBOL: sym=findloc(str); @@ -5977,67 +6064,158 @@ fetchtok: sym=findglb(str,sSTATEVAR); if (sym==NULL || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0)) { error(17,str); /* undefined symbol */ - break; + return FALSE; } /* if */ if (sym->ident==iLABEL) { - tok=tLABEL; - goto invalid_token; - } /* if */ - if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) { + sym->usage|=uREAD; + if (negate) + goto invalid_token_neg; + if (!allow_nonint) { + tok=tLABEL; + goto invalid_token; + } /* if */ + p->type=eotLABEL; + p->value.ucell=(ucell)sym->addr; + } else if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) { + markusage(sym,uREAD); + if (negate) + goto invalid_token_neg; + if (!allow_nonint) { + tok=(sym->usage & uNATIVE) ? teNATIVE : teFUNCTN; + goto invalid_token; + } /* if */ if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)==0 && sym->addr>=0) sym->addr=ntv_funcid++; - markusage(sym,uREAD); + p->type=eotFUNCTION; + p->value.string=str; } else { markusage(sym,uREAD | uWRITTEN); + if (!allow_nonint && sym->ident!=iCONSTEXPR) { + tok=(sym->vclass==sLOCAL) ? teLOCAL : teDATA; + goto invalid_token; + } /* if */ + p->value.ucell=(ucell)(negate ? -sym->addr : sym->addr); } /* if */ - *p=(neg!=FALSE) ? -sym->addr : sym->addr; break; case '(': + if (!allow_expr) + goto invalid_token; if ((emit_flags & efEXPR)==0) stgset(TRUE); stgget(&index,&cidx); errorset(sEXPRMARK,0); ident=expression(&val,NULL,NULL,FALSE); - if (ident!=iCONSTEXPR) - error(8); /* must be constant expression */ errorset(sEXPRRELEASE,0); stgdel(index,cidx); if ((emit_flags & efEXPR)==0) stgset(FALSE); needtoken(')'); - *p=(neg!=FALSE) ? -val : val; + p->value.ucell=(ucell)(negate ? -val : val); + if (ident!=iCONSTEXPR) { + error(8); /* must be constant expression */ + return FALSE; + } /* if */ + break; + case ':': + tok=lex(&val,&str); + if (tok!=tSYMBOL) { + emit_invalid_token(tSYMBOL,tok); + return FALSE; + } /* if */ + sym=fetchlab(str); + sym->usage|=uREAD; + p->type=eotLABEL; + p->value.ucell=(ucell)sym->addr; break; case '-': - if (neg==FALSE) { - neg=TRUE; + if (!negate) { + negate=TRUE; goto fetchtok; } else { - char ival[sNAMEMAX+2]="-"; - strcpy(ival+1,str); - error(1,sc_tokens[tSYMBOL-tFIRST],ival); - break; + extern char *sc_tokens[]; + char ival[sNAMEMAX+2]; + invalid_token_neg: + sprintf(ival,"-%s",str); + error(1,sc_tokens[expected_tok-tFIRST],ival); + return FALSE; } /* if */ default: invalid_token: - emit_invalid_token(teNUMERIC,tok); + emit_invalid_token(expected_tok,tok); + return FALSE; } /* switch */ + return TRUE; } -static void SC_FASTCALL emit_param_data(ucell *p) +static void SC_FASTCALL emit_param_any(emit_outval *p) +{ + emit_param_any_internal(p,teANY,TRUE,TRUE); +} + +static void SC_FASTCALL emit_param_integer(emit_outval *p) +{ + emit_param_any_internal(p,tNUMBER,FALSE,TRUE); +} + +static void SC_FASTCALL emit_param_index(emit_outval *p,int isrange,const cell *valid_values,int numvalues) +{ + int i; + cell val; + + assert(isrange ? (numvalues==2) : (numvalues>0)); + if (!emit_param_any_internal(p,tNUMBER,FALSE,FALSE)) + return; + val=(cell)p->value.ucell; + if (isrange) { + if (valid_values[0]<=val && val<=valid_values[1]) + return; + } else { + for (i=0; ivalue.ucell<(cell)0) { + extern char *sc_tokens[]; +#if PAWN_CELL_SIZE==16 + char ival[7]; + sprintf(ival,"%ld",(long)p->value.ucell); +#elif PAWN_CELL_SIZE==32 + char ival[12]; + sprintf(ival,"%ld",(long)p->value.ucell); +#elif PAWN_CELL_SIZE==64 + char ival[21]; + sprintf(ival,"%lld",(long long)p->value.ucell); +#else + #error Unsupported cell size +#endif + error(1,sc_tokens[teNONNEG-tFIRST],ival); + } /* if */ +} + +static void SC_FASTCALL emit_param_data(emit_outval *p) { cell val; char *str; symbol *sym; int tok; + p->type=eotNUMBER; tok=lex(&val,&str); switch (tok) { case tNUMBER: - *p=val; + p->value.ucell=(ucell)val; break; case tSYMBOL: sym=findloc(str); if (sym!=NULL) { + markusage(sym,uREAD | uWRITTEN); if (sym->ident==iLABEL) { tok=tLABEL; goto invalid_token; @@ -6052,13 +6230,13 @@ static void SC_FASTCALL emit_param_data(ucell *p) error(17,str); /* undefined symbol */ break; } /* if */ + markusage(sym,uREAD | uWRITTEN); if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) { tok=((sym->usage & uNATIVE)!=0) ? teNATIVE : teFUNCTN; goto invalid_token; } /* if */ } /* if */ - markusage(sym,uREAD | uWRITTEN); - *p=sym->addr; + p->value.ucell=(ucell)sym->addr; break; default: invalid_token: @@ -6066,21 +6244,23 @@ static void SC_FASTCALL emit_param_data(ucell *p) } /* switch */ } -static void SC_FASTCALL emit_param_local(ucell *p) +static void SC_FASTCALL emit_param_local(emit_outval *p) { cell val; char *str; symbol *sym; int tok; + p->type=eotNUMBER; tok=lex(&val,&str); switch (tok) { case tNUMBER: - *p=val; + p->value.ucell=(ucell)val; break; case tSYMBOL: sym=findloc(str); if (sym!=NULL) { + markusage(sym,uREAD | uWRITTEN); if (sym->ident==iLABEL) { tok=tLABEL; goto invalid_token; @@ -6091,13 +6271,16 @@ static void SC_FASTCALL emit_param_local(ucell *p) } /* if */ } else { sym=findglb(str,sSTATEVAR); - if (sym==NULL || sym->ident!=iCONSTEXPR) { + if (sym==NULL) { + undefined_sym: error(17,str); /* undefined symbol */ break; } /* if */ + markusage(sym,uREAD | uWRITTEN); + if (sym->ident!=iCONSTEXPR) + goto undefined_sym; } /* if */ - markusage(sym,uREAD | uWRITTEN); - *p=sym->addr; + p->value.ucell=(ucell)sym->addr; break; default: invalid_token: @@ -6105,95 +6288,41 @@ static void SC_FASTCALL emit_param_local(ucell *p) } /* switch */ } -static void SC_FASTCALL emit_param_index(ucell *p,const cell *valid_values,int numvalues) +static void SC_FASTCALL emit_param_label(emit_outval *p) { cell val; char *str; symbol *sym; - int tok,i,global; - int neg=FALSE; + int tok; - assert(numvalues>0); -fetchtok: + p->type=eotNUMBER; tok=lex(&val,&str); - switch (tok) { - case tNUMBER: - if (neg!=FALSE) - val=-val; - break; - case tRATIONAL: - if (neg!=FALSE) - val=val|((cell)1 << (PAWN_CELL_SIZE-1)); - break; + switch (tok) + { + case ':': + tok=lex(&val,&str); + if (tok!=tSYMBOL) + goto invalid_token; + /* fallthrough */ case tSYMBOL: - global=FALSE; - sym=findloc(str); - if (sym==NULL) { - global=TRUE; - sym=findglb(str,sSTATEVAR); - } /* if */ - if (sym==NULL) { - error(17,str); /* undefined symbol */ - return; - } /* if */ - switch (sym->ident) { - case iCONSTEXPR: - break; - case iLABEL: - tok=tLABEL; - goto invalid_token; - case iFUNCTN: - case iREFFUNC: - tok=((sym->usage & uNATIVE)!=0) ? teNATIVE : teFUNCTN; - goto invalid_token; - default: - tok=(global==FALSE && sym->vclass!=sSTATIC) ? teLOCAL : teDATA; - goto invalid_token; - } /* switch */ - markusage(sym,uREAD); - val=(neg!=FALSE) ? -sym->addr : sym->addr; + sym=fetchlab(str); + sym->usage|=uREAD; + p->value.ucell=(ucell)sym->addr; break; - case '-': - if (neg==FALSE) { - neg=TRUE; - goto fetchtok; - } /* if */ - /* drop through */ default: invalid_token: - emit_invalid_token(teNUMERIC,tok); - return; - } /* switch */ - - *p=val; - for (i=0; iusage|=uREAD; - *p=*(ucell *)&sym->addr; + } } -static void SC_FASTCALL emit_param_function(ucell *p,int isnative) +static void SC_FASTCALL emit_param_function(emit_outval *p,int isnative) { cell val; char *str; symbol *sym; int tok; + p->type=eotNUMBER; tok=lex(&val,&str); switch (tok) { @@ -6203,6 +6332,7 @@ static void SC_FASTCALL emit_param_function(ucell *p,int isnative) error(17,str); /* undefined symbol */ return; } /* if */ + markusage(sym,uREAD); if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) { if (!!(sym->usage & uNATIVE)==isnative) break; @@ -6210,7 +6340,7 @@ static void SC_FASTCALL emit_param_function(ucell *p,int isnative) } else { tok=(sym->ident==iCONSTEXPR) ? teNUMERIC : teDATA; } /* if */ - /* drop through */ + /* fallthrough */ default: emit_invalid_token((isnative!=FALSE) ? teNATIVE : teFUNCTN,tok); return; @@ -6219,11 +6349,11 @@ static void SC_FASTCALL emit_param_function(ucell *p,int isnative) if (isnative!=FALSE) { if ((sym->usage & uREAD)==0 && sym->addr>=0) sym->addr=ntv_funcid++; - *p=sym->addr; + p->value.ucell=(ucell)sym->addr; } else { - *(char **)p=str; + p->type=eotFUNCTION; + p->value.string=str; } /* if */ - markusage(sym,uREAD); } static void SC_FASTCALL emit_noop(char *name) @@ -6238,15 +6368,40 @@ static void SC_FASTCALL emit_parm0(char *name) static void SC_FASTCALL emit_parm1_any(char *name) { - ucell p[1]; + emit_outval p[1]; emit_param_any(&p[0]); outinstr(name,p,(sizeof p / sizeof p[0])); } +static void SC_FASTCALL emit_parm1_integer(char *name) +{ + emit_outval p[1]; + + emit_param_integer(&p[0]); + outinstr(name,p,(sizeof p / sizeof p[0])); +} + +static void SC_FASTCALL emit_parm1_nonneg(char *name) +{ + emit_outval p[1]; + + emit_param_nonneg(&p[0]); + outinstr(name,p,(sizeof p / sizeof p[0])); +} + +static void SC_FASTCALL emit_parm1_shift(char *name) +{ + static const cell valid_values[] = { 0,sizeof(cell)*8-1 }; + emit_outval p[1]; + + emit_param_index(&p[0],TRUE,valid_values,(sizeof valid_values / sizeof valid_values[0])); + outinstr(name,p,(sizeof p / sizeof p[0])); +} + static void SC_FASTCALL emit_parm1_data(char *name) { - ucell p[1]; + emit_outval p[1]; emit_param_data(&p[0]); outinstr(name,p,(sizeof p / sizeof p[0])); @@ -6254,7 +6409,7 @@ static void SC_FASTCALL emit_parm1_data(char *name) static void SC_FASTCALL emit_parm1_local(char *name) { - ucell p[1]; + emit_outval p[1]; emit_param_local(&p[0]); outinstr(name,p,(sizeof p / sizeof p[0])); @@ -6262,7 +6417,7 @@ static void SC_FASTCALL emit_parm1_local(char *name) static void SC_FASTCALL emit_parm1_label(char *name) { - ucell p[1]; + emit_outval p[1]; emit_param_label(&p[0]); outinstr(name,p,(sizeof p / sizeof p[0])); @@ -6270,77 +6425,72 @@ static void SC_FASTCALL emit_parm1_label(char *name) static void SC_FASTCALL emit_do_casetbl(char *name) { - ucell p[2]; + emit_outval p[2]; (void)name; - emit_param_any(&p[0]); + emit_param_nonneg(&p[0]); emit_param_label(&p[1]); stgwrite("\tcasetbl\n"); - stgwrite("\tcase "); - outval(p[0],FALSE); - stgwrite(" "); - outval(p[1],TRUE); - code_idx+=opcodes(1)+opargs(2); + outinstr("case",p,(sizeof p / sizeof p[0])); } static void SC_FASTCALL emit_do_case(char *name) { - ucell p[2]; + emit_outval p[2]; emit_param_any(&p[0]); emit_param_label(&p[1]); - stgwrite("\tcase "); - outval(p[0],FALSE); - stgwrite(" "); - outval(p[1],TRUE); - code_idx+=opargs(2); + outinstr("case",p,(sizeof p / sizeof p[0])); + code_idx-=opcodes(1); } static void SC_FASTCALL emit_do_lodb_strb(char *name) { static const cell valid_values[] = { 1,2,4 }; - ucell p[1]; + emit_outval p[1]; - emit_param_index(&p[0],valid_values,(sizeof valid_values / sizeof valid_values[0])); + emit_param_index(&p[0],FALSE,valid_values,(sizeof valid_values / sizeof valid_values[0])); + outinstr(name,p,(sizeof p / sizeof p[0])); +} + +static void SC_FASTCALL emit_do_align(char *name) +{ + static const cell valid_values[] = { 0,sizeof(cell)-1 }; + emit_outval p[1]; + + emit_param_index(&p[0],TRUE,valid_values,(sizeof valid_values / sizeof valid_values[0])); outinstr(name,p,(sizeof p / sizeof p[0])); } static void SC_FASTCALL emit_do_lctrl(char *name) { - static const cell valid_values[] = { 0,1,2,3,4,5,6,7,8,9 }; - ucell p[1]; + static const cell valid_values[] = { 0,9 }; + emit_outval p[1]; - emit_param_index(&p[0],valid_values,(sizeof valid_values / sizeof valid_values[0])); + emit_param_index(&p[0],TRUE,valid_values,(sizeof valid_values / sizeof valid_values[0])); outinstr(name,p,(sizeof p / sizeof p[0])); } static void SC_FASTCALL emit_do_sctrl(char *name) { static const cell valid_values[] = { 2,4,5,6,8,9 }; - ucell p[1]; + emit_outval p[1]; - emit_param_index(&p[0],valid_values,(sizeof valid_values / sizeof valid_values[0])); + emit_param_index(&p[0],FALSE,valid_values,(sizeof valid_values / sizeof valid_values[0])); outinstr(name,p,(sizeof p / sizeof p[0])); } static void SC_FASTCALL emit_do_call(char *name) { - char *funcname=NULL; + emit_outval p[1]; - emit_param_function((ucell *)&funcname,FALSE); - stgwrite("\t"); - stgwrite(name); - if (funcname!=NULL) { - stgwrite(" ."); - stgwrite(funcname); - } /* if */ - stgwrite("\n"); - code_idx+=opcodes(1)+opargs(1); + emit_param_function(&p[0],FALSE); + outinstr(name,p,(sizeof p / sizeof p[0])); } static void SC_FASTCALL emit_do_sysreq_c(char *name) { - ucell p[1]; + emit_outval p[1]; emit_param_function(&p[0],TRUE); @@ -6359,7 +6509,7 @@ static void SC_FASTCALL emit_do_sysreq_c(char *name) static void SC_FASTCALL emit_do_sysreq_n(char *name) { - ucell p[2]; + emit_outval p[2]; emit_param_function(&p[0],TRUE); emit_param_any(&p[1]); @@ -6375,14 +6525,14 @@ static void SC_FASTCALL emit_do_sysreq_n(char *name) } else { outinstr("push.c",&p[1],1); outinstr("sysreq.c",&p[0],1); - p[1]+=sizeof(cell); + p[1].value.ucell+=sizeof(cell); outinstr("stack",&p[1],1); } /* if */ } static void SC_FASTCALL emit_do_const(char *name) { - ucell p[2]; + emit_outval p[2]; emit_param_data(&p[0]); emit_param_any(&p[1]); @@ -6406,7 +6556,7 @@ static void SC_FASTCALL emit_do_const(char *name) static void SC_FASTCALL emit_do_const_s(char *name) { - ucell p[2]; + emit_outval p[2]; emit_param_local(&p[0]); emit_param_any(&p[1]); @@ -6430,7 +6580,7 @@ static void SC_FASTCALL emit_do_const_s(char *name) static void SC_FASTCALL emit_do_load_both(char *name) { - ucell p[2]; + emit_outval p[2]; emit_param_data(&p[0]); emit_param_data(&p[1]); @@ -6450,7 +6600,7 @@ static void SC_FASTCALL emit_do_load_both(char *name) static void SC_FASTCALL emit_do_load_s_both(char *name) { - ucell p[2]; + emit_outval p[2]; emit_param_local(&p[0]); emit_param_local(&p[1]); @@ -6470,7 +6620,7 @@ static void SC_FASTCALL emit_do_load_s_both(char *name) static void SC_FASTCALL emit_do_pushn_c(char *name) { - ucell p[5]; + emit_outval p[5]; int i,numargs; assert(name[0]=='p' && name[1]=='u' && name[2]=='s' @@ -6492,7 +6642,7 @@ static void SC_FASTCALL emit_do_pushn_c(char *name) static void SC_FASTCALL emit_do_pushn(char *name) { - ucell p[5]; + emit_outval p[5]; int i,numargs; assert(name[0]=='p' && name[1]=='u' && name[2]=='s' @@ -6514,7 +6664,7 @@ static void SC_FASTCALL emit_do_pushn(char *name) static void SC_FASTCALL emit_do_pushn_s_adr(char *name) { - ucell p[5]; + emit_outval p[5]; int i,numargs; assert(name[0]=='p' && name[1]=='u' && name[2]=='s' @@ -6541,16 +6691,16 @@ static EMIT_OPCODE emit_opcodelist[] = { { 87, "add.c", emit_parm1_any }, { 14, "addr.alt", emit_parm1_local }, { 13, "addr.pri", emit_parm1_local }, - { 30, "align.alt", emit_parm1_any }, - { 29, "align.pri", emit_parm1_any }, + { 30, "align.alt", emit_do_align }, + { 29, "align.pri", emit_do_align }, { 81, "and", emit_parm0 }, - {121, "bounds", emit_parm1_any }, + {121, "bounds", emit_parm1_integer }, {137, "break", emit_parm0 }, { 49, "call", emit_do_call }, { 50, "call.pri", emit_parm0 }, { 0, "case", emit_do_case }, {130, "casetbl", emit_do_casetbl }, - {118, "cmps", emit_parm1_any }, + {118, "cmps", emit_parm1_nonneg }, {156, "const", emit_do_const }, { 12, "const.alt", emit_parm1_any }, { 11, "const.pri", emit_parm1_any }, @@ -6563,13 +6713,13 @@ static EMIT_OPCODE emit_opcodelist[] = { { 95, "eq", emit_parm0 }, {106, "eq.c.alt", emit_parm1_any }, {105, "eq.c.pri", emit_parm1_any }, - {119, "fill", emit_parm1_any }, + {119, "fill", emit_parm1_nonneg }, {100, "geq", emit_parm0 }, { 99, "grtr", emit_parm0 }, - {120, "halt", emit_parm1_any }, - { 45, "heap", emit_parm1_any }, + {120, "halt", emit_parm1_nonneg }, + { 45, "heap", emit_parm1_integer }, { 27, "idxaddr", emit_parm0 }, - { 28, "idxaddr.b", emit_parm1_any }, + { 28, "idxaddr.b", emit_parm1_shift }, {109, "inc", emit_parm1_data }, {108, "inc.alt", emit_parm0 }, {111, "inc.i", emit_parm0 }, @@ -6583,7 +6733,7 @@ static EMIT_OPCODE emit_opcodelist[] = { { 57, "jless", emit_parm1_label }, { 56, "jneq", emit_parm1_label }, { 54, "jnz", emit_parm1_label }, - { 52, "jrel", emit_parm1_any }, + { 52, "jrel", emit_parm1_integer }, { 64, "jsgeq", emit_parm1_label }, { 63, "jsgrtr", emit_parm1_label }, { 62, "jsleq", emit_parm1_label }, @@ -6595,7 +6745,7 @@ static EMIT_OPCODE emit_opcodelist[] = { { 98, "leq", emit_parm0 }, { 97, "less", emit_parm0 }, { 25, "lidx", emit_parm0 }, - { 26, "lidx.b", emit_parm1_any }, + { 26, "lidx.b", emit_parm1_shift }, { 2, "load.alt", emit_parm1_data }, {154, "load.both", emit_do_load_both }, { 9, "load.i", emit_parm0 }, @@ -6610,7 +6760,7 @@ static EMIT_OPCODE emit_opcodelist[] = { { 7, "lref.s.pri", emit_parm1_local }, { 34, "move.alt", emit_parm0 }, { 33, "move.pri", emit_parm0 }, - {117, "movs", emit_parm1_any }, + {117, "movs", emit_parm1_nonneg }, { 85, "neg", emit_parm0 }, { 96, "neq", emit_parm0 }, {134, "nop", emit_parm0 }, @@ -6624,7 +6774,7 @@ static EMIT_OPCODE emit_opcodelist[] = { { 37, "push.alt", emit_parm0 }, { 39, "push.c", emit_parm1_any }, { 36, "push.pri", emit_parm0 }, - { 38, "push.r", emit_parm1_any }, + { 38, "push.r", emit_parm1_integer }, { 41, "push.s", emit_parm1_local }, {139, "push2", emit_do_pushn }, {141, "push2.adr", emit_do_pushn_s_adr }, @@ -6650,23 +6800,23 @@ static EMIT_OPCODE emit_opcodelist[] = { {104, "sgeq", emit_parm0 }, {103, "sgrtr", emit_parm0 }, { 65, "shl", emit_parm0 }, - { 69, "shl.c.alt", emit_parm1_any }, - { 68, "shl.c.pri", emit_parm1_any }, + { 69, "shl.c.alt", emit_parm1_shift }, + { 68, "shl.c.pri", emit_parm1_shift }, { 66, "shr", emit_parm0 }, - { 71, "shr.c.alt", emit_parm1_any }, - { 70, "shr.c.pri", emit_parm1_any }, + { 71, "shr.c.alt", emit_parm1_shift }, + { 70, "shr.c.pri", emit_parm1_shift }, { 94, "sign.alt", emit_parm0 }, { 93, "sign.pri", emit_parm0 }, {102, "sleq", emit_parm0 }, {101, "sless", emit_parm0 }, { 72, "smul", emit_parm0 }, - { 88, "smul.c", emit_parm1_any }, + { 88, "smul.c", emit_parm1_integer }, { 20, "sref.alt", emit_parm1_data }, { 19, "sref.pri", emit_parm1_data }, { 22, "sref.s.alt", emit_parm1_local }, { 21, "sref.s.pri", emit_parm1_local }, { 67, "sshr", emit_parm0 }, - { 44, "stack", emit_parm1_any }, + { 44, "stack", emit_parm1_integer }, { 16, "stor.alt", emit_parm1_data }, { 23, "stor.i", emit_parm0 }, { 15, "stor.pri", emit_parm1_data }, @@ -6696,10 +6846,7 @@ static int emit_findopcode(const char *instr,int maxlen) { int low,high,mid,cmp; - /* look up the instruction with a binary search - * the assembler is case insensitive to instructions (but case sensitive - * to symbols) - */ + /* look up the instruction with a binary search */ low=1; /* entry 0 is reserved (for "not found") */ high=(sizeof emit_opcodelist / sizeof emit_opcodelist[0])-1; while (lowtMIDDLE && tok<=tLAST)) { @@ -6739,7 +6886,7 @@ SC_FUNC void emit_parse_line(void) * and copy the instruction name */ lptr-=len; - for(i=0; ivalue & TAGMASK); ptr=ptr->next) + for (ptr=tagname_tab.first; ptr!=NULL && tag!=(int)(ptr->value & TAGMASK); ptr=ptr->next) /* nothing */; if (ptr!=NULL) ptr->value |= PUBLICTAG; @@ -7045,7 +7192,7 @@ static void dostate(void) /* find the optional entry() function for the state */ sym=findglb(uENTRYFUNC,sGLOBAL); if (sc_status==statWRITE && sym!=NULL && sym->ident==iFUNCTN && sym->states!=NULL) { - for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + for (stlist=sym->states->first; stlist!=NULL; stlist=stlist->next) { assert(strlen(stlist->name)!=0); if (state_getfsa(stlist->index)==automaton->index && state_inlist(stlist->index,(int)state->value)) break; /* found! */ @@ -7067,7 +7214,7 @@ static void dostate(void) /* get the last list id attached to the function, this contains the source states */ assert(curfunc!=NULL); if (curfunc->states!=NULL) { - stlist=curfunc->states->next; + stlist=curfunc->states->first; assert(stlist!=NULL); while (stlist->next!=NULL) stlist=stlist->next; diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 5180ae5..821f2a8 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -20,6 +20,7 @@ * * Version: $Id: sc2.c 3655 2006-10-23 20:17:52Z thiadmer $ */ + #include #include #include @@ -396,8 +397,11 @@ static void readline(unsigned char *line) *line='\0'; /* delete line */ cont=FALSE; } else { - /* check whether to erase leading whitespace after '\\' on next line */ + /* check whether to erase leading spaces */ if (cont) { + unsigned char *ptr=line; + while (*ptr<=' ' && *ptr!='\0') + ptr++; if (ptr!=line) memmove(line,ptr,strlen((char*)ptr)+1); } /* if */ @@ -414,6 +418,10 @@ static void readline(unsigned char *line) ptr--; /* skip trailing whitespace */ if (*ptr=='\\') { cont=TRUE; + /* set '\a' at the position of '\\' to make it possible to check + * for a line continuation in a single line comment (error 49) + */ + *ptr++='\a'; *ptr='\0'; /* erase '\n' (and any trailing whitespace) */ } /* if */ } /* if */ @@ -441,6 +449,7 @@ static void readline(unsigned char *line) static void stripcomment(unsigned char *line) { char c; + char* continuation; #if !defined SC_LIGHT #define COMMENT_LIMIT 100 #define COMMENT_MARGIN 40 /* length of the longest word */ @@ -512,6 +521,24 @@ static void stripcomment(unsigned char *line) if (icomment==2) *line++=' '; } else if (*line=='/' && *(line+1)=='/'){ /* comment to end of line */ + continuation=(char*)line; + while ((continuation=strchr(continuation,'\a'))!=NULL){ + /* don't give the error if the next line is also commented out. + it is quite annoying to get an error for commenting out a define using: + + // + // #define LONG_MACRO\ + // did span \ + // multiple lines + // + */ + while (*continuation<=' ' && *continuation!='\0') + continuation++; /* skip whitespace */ + if (*continuation!='/' || *(continuation+1)!='/') { + error(49); /* invalid line continuation */ + break; + } + } #if !defined SC_LIGHT if (*(line+2)=='/' && *(line+3)<=' ') { /* documentation comment */ @@ -903,6 +930,23 @@ static const unsigned char *getstring(unsigned char *dest,int max,const unsigned return line; } +/* strdupwithouta + * + * Duplicate a string, stripping out `\a`s. + */ +static char* strdupwithouta(const char* sourcestring) +{ + char* result=strdup(sourcestring); + char* a=result; + if (result==NULL) { + return NULL; + } + while ((a=strchr(a,'\a'))!=NULL) { + *a=' '; + } + return result; +} + enum { CMD_NONE, CMD_TERM, @@ -1115,7 +1159,7 @@ static int command(void) /* remove leading whitespace */ while (*lptr<=' ' && *lptr!='\0') lptr++; - pc_deprecate=strdup((const char *)lptr); + pc_deprecate=strdupwithouta((const char *)lptr); if (pc_deprecate!=NULL) { char *ptr=pc_deprecate+strlen(pc_deprecate)-1; /* remove trailing whitespace */ @@ -1499,7 +1543,7 @@ static int command(void) while (*lptr<=' ' && *lptr!='\0') lptr++; if (!SKIPPING) { - char *usermsg=strdup((const char *)lptr); + char *usermsg=strdupwithouta((const char *)lptr); if (usermsg!=NULL) { char *ptr=usermsg+strlen(usermsg)-1; /* remove trailing whitespace and newlines */ @@ -1893,6 +1937,10 @@ static const unsigned char *unpackedstring(const unsigned char *lptr,int *flags) while (*lptr==' ' || *lptr=='\t') /* this is as defines with parameters may add them */ lptr++; /* when you use a space after , in a match pattern */ while (*lptr!='\0') { + if (*lptr=='\a') { + lptr++; + continue; + } /* if */ if (!instring) { if (*lptr=='\"') { instring=1; @@ -1963,6 +2011,10 @@ static const unsigned char *packedstring(const unsigned char *lptr,int *flags) i=sizeof(ucell)-(sCHARBITS/8); /* start at most significant byte */ val=0; while (*lptr!='\0') { + if (*lptr=='\a') { /* ignore '\a' (which was inserted at a line concatenation) */ + lptr++; + continue; + } /* if */ if (!instring) { if (*lptr=='\"') { instring=1; @@ -2028,7 +2080,7 @@ static const unsigned char *packedstring(const unsigned char *lptr,int *flags) if (*lptr==',' || *lptr==')' || *lptr=='}' || *lptr==';' || *lptr==':' || *lptr=='\n' || *lptr=='\r') - lptr=stringize; /* backtrack to end of last string for closing " */ + lptr=stringize; /* backtrack to end of last string for closing " */ return lptr; } @@ -2097,8 +2149,8 @@ char *sc_tokens[] = { "#tryinclude", "#undef", "#warning", ";", ";", "-integer value-", "-rational value-", "-identifier-", "-label-", "-string-", - "-numeric value-", "-data offset-", "-local variable-", "-function-", - "-native function-" + "-any value-", "-numeric value-", "-data offset-", "-local variable-", + "-function-", "-native function-", "-nonnegative integer-" }; SC_FUNC int lex(cell *lexvalue,char **lexsym) @@ -2949,7 +3001,7 @@ SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_ sym->usage &= ~uDEFINE; /* clear "defined" flag */ /* set all states as "undefined" too */ if (sym->states!=NULL) - for (stateptr=sym->states->next; stateptr!=NULL; stateptr=stateptr->next) + for (stateptr=sym->states->first; stateptr!=NULL; stateptr=stateptr->next) stateptr->value=0; /* for user defined operators, also remove the "prototyped" flag, as * user-defined operators *must* be declared before use @@ -2989,10 +3041,10 @@ static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int a && (sym->parent==NULL || sym->ident==iCONSTEXPR) /* sub-types (hierarchical types) are skipped, except for enum fields */ && (sym->fnumber<0 || sym->fnumber==fnumber)) /* check file number for scope */ { - assert(sym->states==NULL || sym->states->next!=NULL); /* first element of the state list is the "root" */ + assert(sym->states==NULL || sym->states->first!=NULL); /* first element of the state list is the "root" */ if (sym->ident==iFUNCTN || (automaton<0 && sym->states==NULL) - || (automaton>=0 && sym->states!=NULL && state_getfsa(sym->states->next->index)==automaton)) + || (automaton>=0 && sym->states!=NULL && state_getfsa(sym->states->first->index)==automaton)) { if (cmptag==NULL && sym->fnumber==fnumber) return sym; /* return first match */ @@ -3110,8 +3162,8 @@ SC_FUNC symbol *findglb(const char *name,int filter) * also verify whether there is an intersection between the symbol's * state list and the current state list */ - assert(sym->states!=NULL && sym->states->next!=NULL); - if (!state_conflict_id(sc_curstates,sym->states->next->index)) + assert(sym->states!=NULL && sym->states->first!=NULL); + if (!state_conflict_id(sc_curstates,sym->states->first->index)) sym=NULL; } /* if */ } /* if */ diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index 235680e..f4bef71 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -20,6 +20,7 @@ * * Version: $Id: sc3.c 3660 2006-11-05 13:05:09Z thiadmer $ */ + #include #include #include /* for _MAX_PATH */ @@ -512,6 +513,7 @@ static int plnge_rel(int *opstr,int opoff,int (*hier)(value *lval),value *lval) error(212); if (count>0) { relop_prefix(); + lval2.boolresult=lval->boolresult; *lval=lval2; /* copy right hand expression of the previous iteration */ } /* if */ opidx+=opoff; @@ -747,7 +749,7 @@ SC_FUNC int sc_getstateid(constvalue **automaton,constvalue **state) return 0; tokeninfo(&val,&str); /* do not copy the name yet, must check automaton first */ if (*automaton==NULL) { - error(86,name); /* unknown automaton */ + error_suggest(86,name,NULL,estAUTOMATON,0); /* unknown automaton */ return 0; } /* if */ assert((*automaton)->index>0); @@ -767,7 +769,7 @@ SC_FUNC int sc_getstateid(constvalue **automaton,constvalue **state) char *fsaname=(*automaton)->name; if (*fsaname=='\0') fsaname="
"; - error(87,name,fsaname); /* unknown state for automaton */ + error_suggest(87,name,fsaname,estSTATE,fsa); /* unknown state for automaton */ return 0; } /* if */ @@ -1014,8 +1016,8 @@ static int hier14(value *lval1) return error(48); /* array dimensions must match */ else if (ltlengthval) || val==0) return error(47); /* array sizes must match */ - else if (lval3.ident!=iARRAYCELL && !matchtag(lval3.sym->x.tags.index,idxtag,TRUE)) - error(229,(lval2.sym!=NULL) ? lval2.sym->name : lval3.sym->name); /* index tag mismatch */ + else if (lval3.ident!=iARRAYCELL) + check_index_tagmismatch((lval2.sym!=NULL) ? lval2.sym->name : lval3.sym->name,lval3.sym->x.tags.index,idxtag,TRUE,0); if (level>0) { /* check the sizes of all sublevels too */ symbol *sym1 = lval3.sym; @@ -1036,8 +1038,8 @@ static int hier14(value *lval1) */ if (sym1->dim.array.length!=sym2->dim.array.length) error(47); /* array sizes must match */ - else if (!matchtag(sym1->x.tags.index,sym2->x.tags.index,TRUE)) - error(229,sym2->name); /* index tag mismatch */ + else + check_index_tagmismatch(sym2->name,sym1->x.tags.index,sym2->x.tags.index,TRUE,0); } /* for */ /* get the total size in cells of the multi-dimensional array */ val=array_totalsize(lval3.sym); @@ -1319,7 +1321,7 @@ static int hier2(value *lval) paranthese++; tok=lex(&val,&st); if (tok!=tSYMBOL) - return error(20,st); /* illegal symbol name */ + return error_suggest(20,st,NULL,estNONSYMBOL,tok); /* illegal symbol name */ sym=findloc(st); if (sym==NULL) sym=findglb(st,sSTATEVAR); @@ -1342,18 +1344,18 @@ static int hier2(value *lval) paranthese++; tok=lex(&val,&st); if (tok!=tSYMBOL) - return error(20,st); /* illegal symbol name */ + return error_suggest(20,st,NULL,estNONSYMBOL,tok); /* illegal symbol name */ sym=findloc(st); if (sym==NULL) sym=findglb(st,sSTATEVAR); if (sym==NULL) - return error(17,st); /* undefined symbol */ + return error_suggest(17,st,NULL,estSYMBOL,essVARCONST); /* undefined symbol */ if (sym->ident==iCONSTEXPR) error(39); /* constant symbol has no size */ else if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) error(72); /* "function" symbol has no size */ else if ((sym->usage & uDEFINE)==0) - return error(17,st); /* undefined symbol (symbol is in the table, but it is "used" only) */ + return error_suggest(17,st,NULL,estSYMBOL,essVARCONST); /* undefined symbol (symbol is in the table, but it is "used" only) */ clear_value(lval); lval->ident=iCONSTEXPR; lval->constval=1; /* preset */ @@ -1368,7 +1370,7 @@ static int hier2(value *lval) int cmptag=subsym->x.tags.index; tokeninfo(&val,&idxname); if ((idxsym=findconst(idxname,&cmptag))==NULL) - error(80,idxname); /* unknown symbol, or non-constant */ + error_suggest(80,idxname,NULL,estSYMBOL,essCONST); /* unknown symbol, or non-constant */ else if (cmptag>1) error(91,idxname); /* ambiguous constant */ } /* if */ @@ -1395,7 +1397,7 @@ static int hier2(value *lval) paranthese++; tok=lex(&val,&st); if (tok!=tSYMBOL && tok!=tLABEL) - return error(20,st); /* illegal symbol name */ + return error_suggest(20,st,NULL,estNONSYMBOL,tok); /* illegal symbol name */ if (tok==tLABEL) { constvalue *tagsym=find_constval(&tagname_tab,st,0); tag=(int)((tagsym!=NULL) ? tagsym->value : 0); @@ -1404,9 +1406,9 @@ static int hier2(value *lval) if (sym==NULL) sym=findglb(st,sSTATEVAR); if (sym==NULL) - return error(17,st); /* undefined symbol */ + return error_suggest(17,st,NULL,estSYMBOL,essNONLABEL); /* undefined symbol */ if ((sym->usage & uDEFINE)==0) - return error(17,st); /* undefined symbol (symbol is in the table, but it is "used" only) */ + return error_suggest(17,st,NULL,estSYMBOL,essNONLABEL); /* undefined symbol (symbol is in the table, but it is "used" only) */ tag=sym->tag; } /* if */ if (sym!=NULL && (sym->ident==iARRAY || sym->ident==iREFARRAY)) { @@ -1420,7 +1422,7 @@ static int hier2(value *lval) int cmptag=subsym->x.tags.index; tokeninfo(&val,&idxname); if ((idxsym=findconst(idxname,&cmptag))==NULL) - error(80,idxname); /* unknown symbol, or non-constant */ + error_suggest(80,idxname,NULL,estSYMBOL,essCONST); /* unknown symbol, or non-constant */ else if (cmptag>1) error(91,idxname); /* ambiguous constant */ } /* if */ @@ -1433,10 +1435,13 @@ static int hier2(value *lval) else if (level==sym->dim.array.level+1 && idxsym!=NULL) tag=idxsym->x.tags.index; } /* if */ - exporttag(tag); + if (tag!=0) { + exporttag(tag); + tag |= PUBLICTAG; + } /* if */ clear_value(lval); lval->ident=iCONSTEXPR; - lval->constval=tag | PUBLICTAG; + lval->constval=tag; ldconst(lval->constval,sPRI); while (paranthese--) needtoken(')'); @@ -1596,7 +1601,7 @@ restart: needtoken(close); return FALSE; } else if (sym->ident!=iARRAY && sym->ident!=iREFARRAY){ - error(28,sym->name); /* cannot subscript, variable is not an array */ + error_suggest(28,sym->name,NULL,estSYMBOL,essARRAY);/* cannot subscript, variable is not an array */ needtoken(close); return FALSE; } else if (sym->dim.array.level>0 && close!=']') { @@ -1849,10 +1854,10 @@ static int primary(value *lval) * implemented, issue an error */ if ((sym->usage & uPROTOTYPED)==0) - error(17,st); + error_suggest(17,st,NULL,estSYMBOL,essFUNCTN); /* undefined symbol */ } else { if ((sym->usage & uDEFINE)==0) - error(17,st); + error_suggest(17,st,NULL,estSYMBOL,essVARCONST); /* undefined symbol */ lval->sym=sym; lval->ident=sym->ident; lval->tag=sym->tag; @@ -1865,7 +1870,7 @@ static int primary(value *lval) } /* if */ } else { if (!sc_allowproccall) - return error(17,st); /* undefined symbol */ + return error_suggest(17,st,NULL,estSYMBOL,essVARCONST); /* undefined symbol */ /* an unknown symbol, but used in a way compatible with the "procedure * call" syntax. So assume that the symbol refers to a function. */ @@ -1981,8 +1986,8 @@ static int nesting=0; value lval = {0}; arginfo *arg; char arglist[sMAXARGS]; - constvalue arrayszlst = { NULL, "", 0, 0};/* array size list starts empty */ - constvalue taglst = { NULL, "", 0, 0}; /* tag list starts empty */ + constvalue_root arrayszlst = { NULL, NULL};/* array size list starts empty */ + constvalue_root taglst = { NULL, NULL}; /* tag list starts empty */ symbol *symret; cell lexval; char *lexstr; @@ -2078,8 +2083,10 @@ static int nesting=0; * of the function; check it again for functions with a variable * argument list */ - if (argpos>=sMAXARGS) + if (argpos>=sMAXARGS) { error(45); /* too many function arguments */ + break; + } /* if */ stgmark((char)(sEXPRSTART+argpos));/* mark beginning of new expression in stage */ if (arglist[argpos]!=ARG_UNHANDLED) error(58); /* argument already set */ @@ -2209,19 +2216,23 @@ static int nesting=0; if (lval.sym==NULL || lval.ident==iARRAYCELL) { if (arg[argidx].numdim!=1) { error(48); /* array dimensions must match */ - } else if (arg[argidx].dim[0]!=0) { - assert(arg[argidx].dim[0]>0); - if (lval.ident==iARRAYCELL) { - error(47); /* array sizes must match */ - } else { - assert(lval.constval!=0); /* literal array must have a size */ - /* A literal array must have exactly the same size as the - * function argument; a literal string may be smaller than - * the function argument. - */ - if ((lval.constval>0 && arg[argidx].dim[0]!=lval.constval) - || (lval.constval<0 && arg[argidx].dim[0] < -lval.constval)) - error(47); /* array sizes must match */ + } else { + if (lval.sym==NULL && (arg[argidx].usage & uCONST)==0 && (sym->usage & uNATIVE)==0) + error(239); + if (arg[argidx].dim[0]!=0) { + assert(arg[argidx].dim[0]>0); + if (lval.ident==iARRAYCELL) { + error(7); /* array sizes must match */ + } else { + assert(lval.constval!=0); /* literal array must have a size */ + /* A literal array must have exactly the same size as the + * function argument; a literal string may be smaller than + * the function argument. + */ + if ((lval.constval>0 && arg[argidx].dim[0]!=lval.constval) + || (lval.constval<0 && arg[argidx].dim[0]<-lval.constval)) + error(47); /* array sizes must match */ + } /* if */ } /* if */ } /* if */ if (lval.ident!=iARRAYCELL || lval.constval > 0) { @@ -2245,8 +2256,8 @@ static int nesting=0; assert(leveldim.array.length!=arg[argidx].dim[level]) error(47); /* array sizes must match */ - else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE)) - error(229,sym->name); /* index tag mismatch */ + else + check_index_tagmismatch(sym->name,arg[argidx].idxtag[level],sym->x.tags.index,TRUE,0); append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level); sym=finddepend(sym); assert(sym!=NULL); @@ -2257,15 +2268,16 @@ static int nesting=0; assert(sym!=NULL); if (arg[argidx].dim[level]!=0 && sym->dim.array.length!=arg[argidx].dim[level]) error(47); /* array sizes must match */ - else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE)) - error(229,sym->name); /* index tag mismatch */ + else + check_index_tagmismatch(sym->name,arg[argidx].idxtag[level],sym->x.tags.index,TRUE,0); append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level); } /* if */ /* address already in PRI */ check_tagmismatch_multiple(arg[argidx].tags,arg[argidx].numtags,lval.tag,-1); if (lval.tag!=0) append_constval(&taglst,arg[argidx].name,lval.tag,0); - // ??? set uWRITTEN? + if (lval.sym!=NULL && (arg[argidx].usage & uCONST)==0) + markusage(lval.sym,uWRITTEN); argidx++; /* argument done */ break; } /* switch */ @@ -2335,6 +2347,8 @@ static int nesting=0; check_userop(NULL,arg[argidx].defvalue_tag,arg[argidx].tags[0],2,NULL,&dummytag); assert(dummytag==arg[argidx].tags[0]); } /* if */ + if (arg[argidx].defvalue_tag!=0) + append_constval(&taglst,arg[argidx].name,arg[argidx].defvalue_tag,0); pushreg(sPRI); /* store the function argument on the stack */ markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ nest_stkusage++; diff --git a/source/compiler/sc4.c b/source/compiler/sc4.c index 2d9e2b8..0f5d1af 100644 --- a/source/compiler/sc4.c +++ b/source/compiler/sc4.c @@ -20,6 +20,7 @@ * * Version: $Id: sc4.c 3633 2006-08-11 16:20:18Z thiadmer $ */ + #include #include #include @@ -75,7 +76,7 @@ SC_FUNC void writeleader(symbol *root) */ assert(glb_declared==0); begdseg(); - for (fsa=sc_automaton_tab.next; fsa!=NULL; fsa=fsa->next) { + for (fsa=sc_automaton_tab.first; fsa!=NULL; fsa=fsa->next) { defstorage(); stgwrite("0\t; automaton "); if (strlen(fsa->name)==0) @@ -91,7 +92,7 @@ SC_FUNC void writeleader(symbol *root) begcseg(); for (sym=root->next; sym!=NULL; sym=sym->next) { if (sym->ident==iFUNCTN && (sym->usage & (uPUBLIC | uREAD))!=0 && sym->states!=NULL) { - stlist=sym->states->next; + stlist=sym->states->first; assert(stlist!=NULL); /* there should be at least one state item */ listid=stlist->index; assert(listid==-1 || listid>0); @@ -109,7 +110,7 @@ SC_FUNC void writeleader(symbol *root) continue; } /* if */ /* generate label numbers for all statelist ids */ - for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + for (stlist=sym->states->first; stlist!=NULL; stlist=stlist->next) { assert(strlen(stlist->name)==0); strcpy(stlist->name,itoh(getlabel())); } /* for */ @@ -126,7 +127,7 @@ SC_FUNC void writeleader(symbol *root) */ statecount=0; strcpy(lbl_default,itoh(lbl_nostate)); - for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + for (stlist=sym->states->first; stlist!=NULL; stlist=stlist->next) { if (stlist->index==-1) { assert(strlen(stlist->name)name); @@ -146,10 +147,10 @@ SC_FUNC void writeleader(symbol *root) /* generate the jump table */ setlabel(lbl_table); ffcase(statecount,lbl_default,TRUE); - for (state=sc_state_tab.next; state!=NULL; state=state->next) { + for (state=sc_state_tab.first; state!=NULL; state=state->next) { if (state->index==fsa_id) { /* find the label for this list id */ - for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + for (stlist=sym->states->first; stlist!=NULL; stlist=stlist->next) { if (stlist->index!=-1 && state_inlist(stlist->index,(int)state->value)) { ffcase(state->value,stlist->name,FALSE); break; @@ -343,9 +344,12 @@ SC_FUNC void markexpr(optmark type,const char *name,cell offset) * * Global references: funcstatus (referred to only) */ -SC_FUNC void startfunc(char *fname) +SC_FUNC void startfunc(char *fname,int generateproc) { - stgwrite("\tproc"); + if (generateproc) { + stgwrite("\tproc"); + code_idx+=opcodes(1); + } /* if */ if (sc_asmfile) { char symname[2*sNAMEMAX+16]; funcdisplayname(symname,fname); @@ -353,7 +357,6 @@ SC_FUNC void startfunc(char *fname) stgwrite(symname); } /* if */ stgwrite("\n"); - code_idx+=opcodes(1); } /* endfunc @@ -1379,19 +1382,31 @@ SC_FUNC void outval(cell val,int newline) } /* write an instruction with arguments */ -SC_FUNC void outinstr(const char *name,ucell args[],int numargs) +SC_FUNC void outinstr(const char *name,emit_outval params[],int numparams) { int i; stgwrite("\t"); stgwrite(name); - for (i=0; i #if defined __WIN32__ || defined _WIN32 || defined __MSDOS__ #include @@ -28,6 +29,7 @@ #if defined LINUX || defined __GNUC__ #include #endif +#include #include #include #include /* ANSI standardized variable argument list functions */ @@ -183,7 +185,7 @@ static char *warnmsg[] = { /*226*/ "a variable is assigned to itself (symbol \"%s\")\n", /*227*/ "more initiallers than enum fields\n", /*228*/ "length of initialler exceeds size of the enum field\n", -/*229*/ "index tag mismatch (symbol \"%s\")\n", +/*229*/ "index tag mismatch (symbol \"%s\"): expected tag %s but found %s\n", /*230*/ "no implementation for state \"%s\" in function \"%s\", no fall-back\n", /*231*/ "state specification on forward declaration is ignored\n", /*232*/ "output file is written, but with compact encoding disabled\n", @@ -191,7 +193,13 @@ static char *warnmsg[] = { /*234*/ "function is deprecated (symbol \"%s\") %s\n", /*235*/ "public function lacks forward declaration (symbol \"%s\")\n", /*236*/ "unknown parameter in substitution (incorrect #define pattern)\n", -/*237*/ "user warning: %s\n" +/*237*/ "user warning: %s\n", +/*238*/ "meaningless combination of class specifiers (%s)\n", +/*239*/ "literal array/string passed to a non-const parameter\n" +}; + +static char *noticemsg[] = { +/*001*/ "; did you mean \"%s\"?\n" }; #define NUM_WARNINGS (sizeof warnmsg / sizeof warnmsg[0]) @@ -217,13 +225,22 @@ static int errwarn; * fcurrent (reffered to only) * errflag (altered) */ -SC_FUNC int error(int number,...) +SC_FUNC int error(long number,...) { static char *prefix[3]={ "error", "fatal error", "warning" }; static int lastline,errorcount; static short lastfile; char *msg,*pre; va_list argptr; + char string[128]; + int notice; + + /* split the error field between the real error/warning number and an optional + * "notice" number + */ + notice=(unsigned long)number >> (sizeof(long)*4); + number&=(~(unsigned long)0) >> (sizeof(long)*4); + assert(number>0 && number<300); /* errflag is reset on each semicolon. * In a two-pass compiler, an error should not be reported twice. Therefore @@ -241,26 +258,37 @@ static short lastfile; return 0; } /* if */ - if (number<100){ + if (number<100) { + assert(number>0 && number<(1+arraysize(errmsg))); msg=errmsg[number-1]; pre=prefix[0]; errflag=TRUE; /* set errflag (skip rest of erroneous expression) */ errnum++; - } else if (number<200){ + } else if (number<200) { + assert(number>=100 && number<(100+arraysize(fatalmsg))); msg=fatalmsg[number-100]; pre=prefix[1]; errnum++; /* a fatal error also counts as an error */ } else if (errwarn) { + assert(number>=200 && number<(200+arraysize(warnmsg))); msg=warnmsg[number-200]; pre=prefix[0]; errflag=TRUE; errnum++; } else { + assert(number>=200 && number<(200+arraysize(warnmsg))); msg=warnmsg[number-200]; pre=prefix[2]; warnnum++; } /* if */ + if (notice!=0) { + assert(notice>0 && notice<(1+arraysize(noticemsg)) && noticemsg[notice-1][0]!='\0'); + strcpy(string,msg); + strcpy(&string[strlen(string)-1],noticemsg[notice-1]); + msg=string; + } /* if */ + assert(errstart<=fline); if (errline>0) errstart=errline; @@ -268,9 +296,9 @@ static short lastfile; errline=fline; assert(errstart<=errline); va_start(argptr,number); - if (strlen(errfname)==0) { + if (errfname[0]=='\0') { int start=(errstart==errline) ? -1 : errstart; - if (pc_error(number,msg,inpfname,start,errline,argptr)) { + if (pc_error((int)number,msg,inpfname,start,errline,argptr)) { if (outf!=NULL) { pc_closeasm(outf,TRUE); outf=NULL; @@ -281,9 +309,9 @@ static short lastfile; FILE *fp=fopen(errfname,"a"); if (fp!=NULL) { if (errstart>=0 && errstart!=errline) - fprintf(fp,"%s(%d -- %d) : %s %03d: ",inpfname,errstart,errline,pre,number); + fprintf(fp,"%s(%d -- %d) : %s %03d: ",inpfname,errstart,errline,pre,(int)number); else - fprintf(fp,"%s(%d) : %s %03d: ",inpfname,errline,pre,number); + fprintf(fp,"%s(%d) : %s %03d: ",inpfname,errline,pre,(int)number); vfprintf(fp,msg,argptr); fclose(fp); } /* if */ @@ -291,7 +319,7 @@ static short lastfile; va_end(argptr); if ((number>=100 && number<200) || errnum>25){ - if (strlen(errfname)==0) { + if (errfname[0]=='\0') { va_start(argptr,number); pc_error(0,"\nCompilation aborted.\n\n",NULL,0,0,argptr); va_end(argptr); @@ -420,3 +448,240 @@ int pc_geterrorwarnings() return errwarn; } +/* Implementation of Levenshtein distance, by Lorenzo Seidenari + */ +static int minimum(int a,int b,int c) +{ + int min=a; + if(b0 && m>0); + d=(int*)malloc((sizeof(int))*(m+1)*(n+1)); + m++; + n++; + //Step 2 + for (k=0;kMAX_EDIT_DIST) + max_dist=MAX_EDIT_DIST; + return max_dist; +} + +static int find_closest_symbol_table(const char *name,const symbol *root,int symboltype,symbol **closest_sym) +{ + int dist,max_dist,closest_dist=INT_MAX; + char symname[2*sNAMEMAX+16]; + symbol *sym; + int ident; + assert(closest_sym!=NULL); + *closest_sym =NULL; + assert(name!=NULL); + max_dist=get_max_dist(name); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->fnumber!=-1 && sym->fnumber!=fcurrent) + continue; + ident=sym->ident; + if (symboltype==essNONLABEL) { + if (ident==iLABEL) + continue; + } else if (symboltype==essVARCONST) { + if (ident!=iCONSTEXPR && ident!=iVARIABLE && ident!=iREFERENCE && ident!=iARRAY && ident!=iREFARRAY) + continue; + } else if (symboltype==essARRAY) { + if (ident!=iARRAY && ident!=iREFARRAY) + continue; + } else if (symboltype==essCONST) { + if (ident!=iCONSTEXPR) + continue; + } else if (symboltype==essFUNCTN) { + if ((ident!=iFUNCTN && ident!=iREFFUNC) || (sym->usage & uDEFINE)==0) + continue; + } else if (symboltype==essLABEL) { + if (ident!=iLABEL || (sym->usage & uDEFINE)==0) + continue; + } /* if */ + funcdisplayname(symname,sym->name); + dist=levenshtein_distance(name,symname); + if (dist>max_dist || dist>=closest_dist) + continue; + *closest_sym =sym; + closest_dist=dist; + if (closest_dist<=1) + break; + } /* for */ + return closest_dist; +} + +static symbol *find_closest_symbol(const char *name,int symboltype) +{ + symbol *symloc,*symglb; + int distloc,distglb; + + if (sc_status==statFIRST) + return NULL; + assert(name!=NULL); + if (name[0]=='\0') + return NULL; + distloc=find_closest_symbol_table(name,&loctab,symboltype,&symloc); + if (distloc<=1) + distglb=INT_MAX; /* don't bother searching in the global table */ + else + distglb=find_closest_symbol_table(name,&glbtab,symboltype,&symglb); + return (distglbname[0]!='\0') { + dist=levenshtein_distance(name,ptr->name); + if (distnext; + } /* while */ + return closest_match; +} + +static constvalue *find_closest_state(const char *name,int fsa) +{ + constvalue *ptr=sc_state_tab.first; + constvalue *closest_match=NULL; + int dist,max_dist,closest_dist=INT_MAX; + + assert(name!=NULL); + max_dist=get_max_dist(name); + while (ptr!=NULL) { + if (ptr->index==fsa && ptr->name[0]!='\0') { + dist=levenshtein_distance(name,ptr->name); + if (distnext; + } /* while */ + return closest_match; +} + +static constvalue *find_closest_automaton_for_state(const char *statename,int fsa) +{ + constvalue *ptr=sc_state_tab.first; + constvalue *closest_match=NULL; + constvalue *automaton; + const char *fsaname; + int dist,max_dist,closest_dist=INT_MAX; + + assert(statename!=NULL); + max_dist=get_max_dist(statename); + automaton=automaton_findid(ptr->index); + assert(automaton!=NULL); + fsaname=automaton->name; + while (ptr!=NULL) { + if (fsa!=ptr->index && ptr->name[0]!='\0' && strcmp(statename,ptr->name)==0) { + automaton=automaton_findid(ptr->index); + assert(automaton!=NULL); + dist=levenshtein_distance(fsaname,automaton->name); + if (distnext; + } /* while */ + return closest_match; +} + +SC_FUNC int error_suggest(int number,const char *name,const char *name2,int type,int subtype) +{ + char string[sNAMEMAX*2+2]; /* for ":" */ + const char *closest_name=NULL; + + /* don't bother finding the closest names on errors + * that aren't going to be shown on the 1'st pass + */ + if ((errflag || sc_status!=statWRITE) && (number<100 || number>=200)) + return 0; + + if (type==estSYMBOL || (type==estNONSYMBOL && tMIDDLEname; + } else if (type==estAUTOMATON) { + constvalue *closest_automaton=find_closest_automaton(name); + if (closest_automaton!=NULL) + closest_name=closest_automaton->name; + } else if (type==estSTATE) { + constvalue *closest_state=find_closest_state(name,subtype); + if (closest_state !=NULL) { + closest_name=closest_state->name; + } else { + constvalue *closest_automaton=find_closest_automaton_for_state(name,subtype); + if (closest_automaton !=NULL) { + sprintf(string,"%s:%s", closest_automaton->name,name); + closest_name=string; + } /* if */ + } /* if */ + } else { + assert(0); + } /* if */ + + if (closest_name==NULL) { + error(number,name,name2); + } else if (name2!=NULL) { + error(makelong(number,1),name,name2,closest_name); + } else { + error(makelong(number,1),name,closest_name); + } /* if */ + return 0; +} diff --git a/source/compiler/sc6.c b/source/compiler/sc6.c index 0216f41..5e95a04 100644 --- a/source/compiler/sc6.c +++ b/source/compiler/sc6.c @@ -20,6 +20,7 @@ * * Version: $Id: sc6.c 3648 2006-10-12 11:24:50Z thiadmer $ */ + #include #include #include /* for macro max() */ @@ -102,7 +103,7 @@ static ucell getparam(const char *s,char **n) char name[sNAMEMAX+1]; symbol *sym; - if (*s=='.') { + if (s[0]=='.') { /* this is a function, find it in the global symbol table */ for (i=0; !isspace(*(++s)); i++) { assert(*s!='\0'); @@ -115,6 +116,12 @@ static ucell getparam(const char *s,char **n) assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC); assert(sym->vclass==sGLOBAL); result=sym->addr; + } else if (s[0]=='l' && s[1]=='.') { + /* this is a label */ + i=(int)hex2long(s+2,NULL); + assert(i>=0 && inext) { + for (constptr=libname_tab.first; constptr!=NULL; constptr=constptr->next) { if (constptr->value>0) { assert(strlen(constptr->name)>0); numlibraries++; @@ -824,7 +831,7 @@ SC_FUNC int assemble(FILE *fout,FILE *fin) /* count number of public tags */ numtags=0; - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + for (constptr=tagname_tab.first; constptr!=NULL; constptr=constptr->next) { if ((constptr->value & PUBLICTAG)!=0) { assert(strlen(constptr->name)>0); numtags++; @@ -950,7 +957,7 @@ SC_FUNC int assemble(FILE *fout,FILE *fin) /* write the libraries table */ if (pc_addlibtable) { count=0; - for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { + for (constptr=libname_tab.first; constptr!=NULL; constptr=constptr->next) { if (constptr->value>0) { assert(strlen(constptr->name)>0); func.address=0; @@ -994,7 +1001,7 @@ SC_FUNC int assemble(FILE *fout,FILE *fin) /* write the public tagnames table */ count=0; - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + for (constptr=tagname_tab.first; constptr!=NULL; constptr=constptr->next) { if ((constptr->value & PUBLICTAG)!=0) { assert(strlen(constptr->name)>0); func.address=constptr->value & TAGMASK; @@ -1214,21 +1221,21 @@ static void append_dbginfo(FILE *fout) } /* for */ /* tag table */ - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + for (constptr=tagname_tab.first; constptr!=NULL; constptr=constptr->next) { assert(strlen(constptr->name)>0); dbghdr.tags++; dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name); } /* for */ /* automaton table */ - for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { + for (constptr=sc_automaton_tab.first; constptr!=NULL; constptr=constptr->next) { assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); dbghdr.automatons++; dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name); } /* for */ /* state table */ - for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { + for (constptr=sc_state_tab.first; constptr!=NULL; constptr=constptr->next) { assert(strlen(constptr->name)>0); dbghdr.states++; dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name); @@ -1347,7 +1354,7 @@ static void append_dbginfo(FILE *fout) } /* for */ /* tag table */ - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + for (constptr=tagname_tab.first; constptr!=NULL; constptr=constptr->next) { assert(strlen(constptr->name)>0); id1=(int16_t)(constptr->value & TAGMASK); #if BYTE_ORDER==BIG_ENDIAN @@ -1358,7 +1365,7 @@ static void append_dbginfo(FILE *fout) } /* for */ /* automaton table */ - for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { + for (constptr=sc_automaton_tab.first; constptr!=NULL; constptr=constptr->next) { assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); id1=(int16_t)constptr->index; address=(ucell)constptr->value; @@ -1372,7 +1379,7 @@ static void append_dbginfo(FILE *fout) } /* for */ /* state table */ - for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { + for (constptr=sc_state_tab.first; constptr!=NULL; constptr=constptr->next) { assert(strlen(constptr->name)>0); id1=(int16_t)constptr->value; id2=(int16_t)constptr->index; diff --git a/source/compiler/sc7.c b/source/compiler/sc7.c index 07a25b5..5a0ed28 100644 --- a/source/compiler/sc7.c +++ b/source/compiler/sc7.c @@ -47,6 +47,7 @@ * * Version: $Id: sc7.c 3579 2006-06-06 13:35:29Z thiadmer $ */ + #include #include #include /* for atoi() */ @@ -1332,14 +1333,12 @@ SC_FUNC void stgmark(char mark) static int rebuffer(char *str) { if (sc_status==statWRITE) { + int st_len=strlen(str); if (pipeidx>=2 && stgpipe[pipeidx-1]=='\0' && stgpipe[pipeidx-2]!='\n') - pipeidx-=1; /* overwrite last '\0' */ - while (*str!='\0') { /* copy to staging buffer */ - CHECK_STGPIPE(pipeidx); - stgpipe[pipeidx++]=*str++; - } /* while */ - CHECK_STGPIPE(pipeidx); - stgpipe[pipeidx++]='\0'; + pipeidx-=1; /* overwrite last '\0' */ + CHECK_STGPIPE(pipeidx+st_len+1); + memcpy(stgpipe+pipeidx,str,st_len+1); /* copy to staging buffer */ + pipeidx+=st_len+1; } /* if */ return TRUE; } @@ -1372,22 +1371,18 @@ static int filewrite(char *str) SC_FUNC void stgwrite(const char *st) { int len; - int st_len; + int st_len=strlen(st); if (staging) { assert(stgidx==0 || stgbuf!=NULL); /* staging buffer must be valid if there is (apparently) something in it */ if (stgidx>=2 && stgbuf[stgidx-1]=='\0' && stgbuf[stgidx-2]!='\n') - stgidx-=1; /* overwrite last '\0' */ - while (*st!='\0') { /* copy to staging buffer */ - CHECK_STGBUFFER(stgidx); - stgbuf[stgidx++]=*st++; - stglen++; - } /* while */ - CHECK_STGBUFFER(stgidx); - stgbuf[stgidx++]='\0'; + stgidx-=1; /* overwrite last '\0' */ + CHECK_STGBUFFER(stgidx+st_len+1); + memcpy(stgbuf+stgidx,st,st_len+1); /* copy to staging buffer */ + stgidx+=st_len+1; + stglen+=st_len; } else { len=(stgbuf!=NULL) ? stglen : 0; - st_len=strlen(st); CHECK_STGBUFFER(len+st_len+1); memcpy(stgbuf+len,st,st_len+1); len=len+st_len; diff --git a/source/compiler/sci18n.c b/source/compiler/sci18n.c index fa94025..78c1a36 100644 --- a/source/compiler/sci18n.c +++ b/source/compiler/sci18n.c @@ -32,6 +32,7 @@ * * Version: $Id: sci18n.c 3612 2006-07-22 09:59:46Z thiadmer $ */ + #include #include #include diff --git a/source/compiler/sclist.c b/source/compiler/sclist.c index a9f0279..60fd248 100644 --- a/source/compiler/sclist.c +++ b/source/compiler/sclist.c @@ -26,6 +26,7 @@ * * Version: $Id: sclist.c 3660 2006-11-05 13:05:09Z thiadmer $ */ + #include #include #include diff --git a/source/compiler/scstate.c b/source/compiler/scstate.c index a1fdfb3..6e87202 100644 --- a/source/compiler/scstate.c +++ b/source/compiler/scstate.c @@ -45,6 +45,7 @@ * * Version: $Id: scstate.c 3579 2006-06-06 13:35:29Z thiadmer $ */ + #include #include #include @@ -76,7 +77,7 @@ static constvalue *find_automaton(const char *name,int *last) assert(last!=NULL); *last=0; - ptr=sc_automaton_tab.next; + ptr=sc_automaton_tab.first; while (ptr!=NULL) { if (strcmp(name,ptr->name)==0) return ptr; @@ -110,7 +111,7 @@ SC_FUNC constvalue *automaton_find(const char *name) SC_FUNC constvalue *automaton_findid(int id) { constvalue *ptr; - for (ptr=sc_automaton_tab.next; ptr!=NULL && ptr->index!=id; ptr=ptr->next) + for (ptr=sc_automaton_tab.first; ptr!=NULL && ptr->index!=id; ptr=ptr->next) /* nothing */; return ptr; } @@ -122,7 +123,7 @@ static constvalue *find_state(const char *name,int fsa,int *last) assert(last!=NULL); *last=0; - ptr=sc_state_tab.next; + ptr=sc_state_tab.first; while (ptr!=NULL) { if (ptr->index==fsa) { if (strcmp(name,ptr->name)==0) @@ -158,7 +159,7 @@ SC_FUNC constvalue *state_find(const char *name,int fsa_id) SC_FUNC constvalue *state_findid(int id) { constvalue *ptr; - for (ptr=sc_state_tab.next; ptr!=NULL && ptr->value!=id; ptr=ptr->next) + for (ptr=sc_state_tab.first; ptr!=NULL && ptr->value!=id; ptr=ptr->next) /* nothing */; return ptr; } @@ -341,7 +342,7 @@ SC_FUNC void state_conflict(symbol *root) continue; /* hierarchical data type or no function */ if (sym->states==NULL) continue; /* this function has no states */ - for (srcptr=sym->states->next; srcptr!=NULL; srcptr=srcptr->next) { + for (srcptr=sym->states->first; srcptr!=NULL; srcptr=srcptr->next) { if (srcptr->index==-1) continue; /* state list id -1 is a special case */ psrc=state_getlist_ptr(srcptr->index); diff --git a/source/compiler/scvars.c b/source/compiler/scvars.c index 43de38a..05fefa1 100644 --- a/source/compiler/scvars.c +++ b/source/compiler/scvars.c @@ -22,6 +22,7 @@ * * Version: $Id: scvars.c 3655 2006-10-23 20:17:52Z thiadmer $ */ + #include #include /* for _MAX_PATH */ #include "sc.h" @@ -37,8 +38,8 @@ SC_VDEFINE struct hashtable_t symbol_cache_ht; SC_VDEFINE cell *litq; /* the literal queue */ SC_VDEFINE unsigned char pline[sLINEMAX+1]; /* the line read from the input file */ SC_VDEFINE const unsigned char *lptr; /* points to the current position in "pline" */ -SC_VDEFINE constvalue tagname_tab={ NULL, "", 0, 0}; /* tagname table */ -SC_VDEFINE constvalue libname_tab={ NULL, "", 0, 0}; /* library table (#pragma library "..." syntax) */ +SC_VDEFINE constvalue_root tagname_tab={ NULL, NULL}; /* tagname table */ +SC_VDEFINE constvalue_root libname_tab={ NULL, NULL}; /* library table (#pragma library "..." syntax) */ SC_VDEFINE constvalue *curlibrary=NULL; /* current library */ SC_VDEFINE int pc_addlibtable=TRUE; /* is the library table added to the AMX file? */ SC_VDEFINE symbol *curfunc; /* pointer to current function */ @@ -96,8 +97,8 @@ SC_VDEFINE int pc_naked=FALSE; /* if true mark following function a SC_VDEFINE int pc_compat=FALSE; /* running in compatibility mode? */ SC_VDEFINE int pc_recursion=FALSE; /* enable detailed recursion report? */ -SC_VDEFINE constvalue sc_automaton_tab = { NULL, "", 0, 0}; /* automaton table */ -SC_VDEFINE constvalue sc_state_tab = { NULL, "", 0, 0}; /* state table */ +SC_VDEFINE constvalue_root sc_automaton_tab = { NULL, NULL}; /* automaton table */ +SC_VDEFINE constvalue_root sc_state_tab = { NULL, NULL}; /* state table */ SC_VDEFINE FILE *inpf = NULL; /* file read from (source or include) */ SC_VDEFINE FILE *inpf_org= NULL; /* main source file */ diff --git a/source/compiler/tests/.gitignore b/source/compiler/tests/.gitignore new file mode 100644 index 0000000..e96bec9 --- /dev/null +++ b/source/compiler/tests/.gitignore @@ -0,0 +1,3 @@ +*.amx +*.asm +*.lst diff --git a/source/compiler/tests/CMakeLists.txt b/source/compiler/tests/CMakeLists.txt index a341039..282f668 100644 --- a/source/compiler/tests/CMakeLists.txt +++ b/source/compiler/tests/CMakeLists.txt @@ -1,55 +1,29 @@ -set(DEFAULT_COMPILER_OPTIONS - -i${CMAKE_SOURCE_DIR}/include - "-\;+" - "-(+") +find_package(PythonInterp 2.7) -function(add_compiler_test test_name options) +if(PYTHONINTERP_FOUND) + add_custom_target(pawncc_tests + COMMAND ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/run_tests.py + -c $ + -d $ + -r $ + -i ../../../include + DEPENDS pawncc + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + + file(GLOB_RECURSE meta_files "*.meta") + foreach(meta_file IN LISTS meta_files) + get_filename_component(test_name ${meta_file} NAME_WE) add_test(NAME ${test_name} - COMMAND $ ${DEFAULT_COMPILER_OPTIONS} ${options}) - set_tests_properties(${test_name} PROPERTIES - ENVIRONMENT PATH=$) -endfunction() - -# Compile tests -# -# These tests compare compile output against a regular expression and fail if the output -# doesn't match the expected pattern. - -add_compiler_test(gh_217 ${CMAKE_CURRENT_SOURCE_DIR}/gh_217.pwn) -set_tests_properties(gh_217 PROPERTIES PASS_REGULAR_EXPRESSION "\ -.*\\.pwn\\(11\\) : warning 237: user warning: this is warning 1\ -.*\\.pwn\\(13\\) : warning 237: user warning: this iswarning 2\ -.*\\.pwn\\(15\\) : warning 237: user warning: this is warning 3\ -.*\\.pwn\\(17\\) : warning 237: user warning: this is warning 4\ -.*\\.pwn\\(27\\) : warning 234: function is deprecated \\(symbol \"f\"\\) don't use this functionplease\ -.*\\.pwn\\(32\\) : warning 234: function is deprecated \\(symbol \"f\"\\) don't use this functionplease\ -") - -add_compiler_test(reset_errline_gh_230 ${CMAKE_CURRENT_SOURCE_DIR}/reset_errline_gh_230.pwn) -set_tests_properties(reset_errline_gh_230 PROPERTIES PASS_REGULAR_EXPRESSION "\ -.*\\.pwn\\(2\\) : error 017: undefined symbol \\\"undefined\\\"\ -.*\\.pwn\\(2\\) : warning 215: expression has no effect\ -.*\\.pwn\\(7\\) : warning 204: symbol is assigned a value that is never used: \\\"y\\\"\ -.*\\.pwn\\(4\\) : warning 204: symbol is assigned a value that is never used: \\\"x\\\"\ -") - -add_compiler_test(unused_symbol_line_gh_252 ${CMAKE_CURRENT_SOURCE_DIR}/unused_symbol_line_gh_252.pwn) -set_tests_properties(unused_symbol_line_gh_252 PROPERTIES PASS_REGULAR_EXPRESSION "\ -.*\\.pwn\\(4\\) : warning 203: symbol is never used: \\\"y\\\" -.*\\.pwn\\(8\\) : warning 203: symbol is never used: \\\"z\\\" -.*\\.pwn\\(1\\) : warning 203: symbol is never used: \\\"x\\\" -") - -add_compiler_test(gh_283 ${CMAKE_CURRENT_SOURCE_DIR}/gh_283.pwn) -set_tests_properties(gh_283 PROPERTIES PASS_REGULAR_EXPRESSION "\ -.*\\.pwn\\(5\\) : warning 234: function is deprecated \\(symbol \"print\"\\)\ -") - -# Crashers -# -# These tests simply check that the compiler doesn't crash. -# -# TODO: Probably need to support tests that exist with a non-zero code but don't crash? -# Right now this will cause a failure. - -add_compiler_test(md_array_crash_gh_220 ${CMAKE_CURRENT_SOURCE_DIR}/md_array_crash_gh_220.pwn) + COMMAND ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/run_tests.py + -c $ + -d $ + -r $ + -i ../../../include + ${test_name} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + endforeach() +else() + message("Python was not found, you will not be able to run the tests") +endif() diff --git a/source/compiler/tests/const_array_args_and_literals_gh_276.meta b/source/compiler/tests/const_array_args_and_literals_gh_276.meta new file mode 100644 index 0000000..861e659 --- /dev/null +++ b/source/compiler/tests/const_array_args_and_literals_gh_276.meta @@ -0,0 +1,11 @@ +{ + 'test_type': 'output_check', + 'errors': """ +const_array_args_and_literals_gh_276.pwn(13) : warning 214: possibly a "const" array argument was intended: "arr" +const_array_args_and_literals_gh_276.pwn(18) : warning 214: possibly a "const" array argument was intended: "arr" +const_array_args_and_literals_gh_276.pwn(30) : warning 214: possibly a "const" array argument was intended: "arr" +const_array_args_and_literals_gh_276.pwn(39) : warning 239: literal array/string passed to a non-const parameter +const_array_args_and_literals_gh_276.pwn(40) : warning 239: literal array/string passed to a non-const parameter +const_array_args_and_literals_gh_276.pwn(41) : warning 239: literal array/string passed to a non-const parameter +""" +} diff --git a/source/compiler/tests/const_array_args_and_literals_gh_276.pwn b/source/compiler/tests/const_array_args_and_literals_gh_276.pwn new file mode 100644 index 0000000..fdc990d --- /dev/null +++ b/source/compiler/tests/const_array_args_and_literals_gh_276.pwn @@ -0,0 +1,61 @@ +forward OnDialogResponse(playerid, dialogid, response, listitem, inputtext[]); + +native SetTimer(funcname[], interval, repeating); +public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[]) +{ + +} + +f0(arr[]) { + #pragma unused arr +} + +f1(arr[]) { // line 13 + new a = arr[0]; + #pragma unused a +} + +f2(arr[5]) {// line 18 + new a = arr[0]; + #pragma unused a +} +f3(const arr[]) { + new a = arr[0]; + #pragma unused a +} +f4(const arr[5]) { + new a = arr[0]; + #pragma unused a +} +f5(arr[][]) { // line 30 + new a = arr[0][0]; + #pragma unused a +} +f6(arr[][]) { + arr[0][0] = 0; +} + +main () { + f0("test"); // line 39 + f1("test"); // line 40 + f2("test"); // line 41 + f3("test"); + f4("test"); + + new arr[5]; + f1(arr); + f2(arr); + f3(arr); + f4(arr); + + f1(arr[0]); + //f2(arr[0]); - array size must match + f3(arr[0]); + //f4(arr[0]); - array size must match + + new arr2[1][1]; + f5(arr2); + f6(arr2); + + SetTimer("test", 0, 0); +} diff --git a/source/compiler/tests/constexpr_result_prop_gh_308.meta b/source/compiler/tests/constexpr_result_prop_gh_308.meta new file mode 100644 index 0000000..197d682 --- /dev/null +++ b/source/compiler/tests/constexpr_result_prop_gh_308.meta @@ -0,0 +1,9 @@ +{ + 'test_type': 'output_check', + 'errors': """ +constexpr_result_prop_gh_308.pwn(2) : warning 237: user warning: "Test passed." +constexpr_result_prop_gh_308.pwn(6) : warning 237: user warning: "Test passed." +constexpr_result_prop_gh_308.pwn(10) : warning 237: user warning: "Test passed." +constexpr_result_prop_gh_308.pwn(14) : warning 237: user warning: "Test passed." +""" +} diff --git a/source/compiler/tests/constexpr_result_prop_gh_308.pwn b/source/compiler/tests/constexpr_result_prop_gh_308.pwn new file mode 100644 index 0000000..0592bad --- /dev/null +++ b/source/compiler/tests/constexpr_result_prop_gh_308.pwn @@ -0,0 +1,19 @@ +#if (30 < 40 < 50) + #warning "Test passed." +#endif + +#if !(30 < 40 < 35) + #warning "Test passed." +#endif + +#if (30 < 40) + #warning "Test passed." +#endif + +#if !(40 < 35) + #warning "Test passed." +#endif + +main () { + +} diff --git a/source/compiler/tests/destructor_not_impl_gh_310.meta b/source/compiler/tests/destructor_not_impl_gh_310.meta new file mode 100644 index 0000000..3e1e1be --- /dev/null +++ b/source/compiler/tests/destructor_not_impl_gh_310.meta @@ -0,0 +1,6 @@ +{ + 'test_type': 'output_check', + 'errors': """ +destructor_not_impl_gh_310.pwn(4) : error 004: function "operator~(Error:)" is not implemented +""" +} diff --git a/source/compiler/tests/destructor_not_impl_gh_310.pwn b/source/compiler/tests/destructor_not_impl_gh_310.pwn new file mode 100644 index 0000000..7855e78 --- /dev/null +++ b/source/compiler/tests/destructor_not_impl_gh_310.pwn @@ -0,0 +1,4 @@ +forward operator~(Error:right[], size); +main() { + new Error:e; +} diff --git a/source/compiler/tests/gh_217.meta b/source/compiler/tests/gh_217.meta new file mode 100644 index 0000000..f40a838 --- /dev/null +++ b/source/compiler/tests/gh_217.meta @@ -0,0 +1,11 @@ +{ + 'test_type': 'output_check', + 'errors': """ +gh_217.pwn(11) : warning 237: user warning: this is warning 1 +gh_217.pwn(13) : warning 237: user warning: this is warning 2 +gh_217.pwn(15) : warning 237: user warning: this is warning 3 +gh_217.pwn(17) : warning 237: user warning: this is warning 4 +gh_217.pwn(28) : warning 234: function is deprecated (symbol "f") don't use this function please +gh_217.pwn(33) : warning 234: function is deprecated (symbol "f") don't use this function please +""" +} diff --git a/source/compiler/tests/gh_217.pwn b/source/compiler/tests/gh_217.pwn index 2d2d24c..9b6fe6e 100644 --- a/source/compiler/tests/gh_217.pwn +++ b/source/compiler/tests/gh_217.pwn @@ -1,6 +1,6 @@ // TODO: Check that string literals are concatenated correctly -native print(const s[]); +#include #define d1\ print("ok") @@ -17,7 +17,8 @@ warning 3 warning 4 // single-line comments can span \ -multiple lines if you really want it +//multiple lines if you really want it +// no they can't, only with more //s. #pragma deprecated don't\ use \ diff --git a/source/compiler/tests/gh_283.meta b/source/compiler/tests/gh_283.meta new file mode 100644 index 0000000..3fcfc5f --- /dev/null +++ b/source/compiler/tests/gh_283.meta @@ -0,0 +1,6 @@ +{ + 'test_type': 'output_check', + 'errors': """ + gh_283.pwn(5) : warning 234: function is deprecated (symbol "f") + """ +} diff --git a/source/compiler/tests/gh_283.pwn b/source/compiler/tests/gh_283.pwn index 87645ac..0cc57dd 100644 --- a/source/compiler/tests/gh_283.pwn +++ b/source/compiler/tests/gh_283.pwn @@ -1,6 +1,6 @@ #pragma deprecated -native print(const string[]); +native f(); main() { - print("Hello World"); + f(); } diff --git a/source/compiler/tests/gh_353_symbol_suggestions.inc b/source/compiler/tests/gh_353_symbol_suggestions.inc new file mode 100644 index 0000000..8f07b7f --- /dev/null +++ b/source/compiler/tests/gh_353_symbol_suggestions.inc @@ -0,0 +1,2 @@ +static staticvar; +#pragma unused staticvar \ No newline at end of file diff --git a/source/compiler/tests/gh_353_symbol_suggestions.meta b/source/compiler/tests/gh_353_symbol_suggestions.meta new file mode 100644 index 0000000..0344afe --- /dev/null +++ b/source/compiler/tests/gh_353_symbol_suggestions.meta @@ -0,0 +1,24 @@ +{ + 'test_type': 'output_check', + 'errors': """ +gh_353_symbol_suggestions.pwn(12) : error 017: undefined symbol "abcxyz" +gh_353_symbol_suggestions.pwn(20) : error 017: undefined symbol "length" +gh_353_symbol_suggestions.pwn(30) : error 017: undefined symbol "float" +gh_353_symbol_suggestions.pwn(40) : error 017: undefined symbol "ab" +gh_353_symbol_suggestions.pwn(41) : error 017: undefined symbol "ab" +gh_353_symbol_suggestions.pwn(50) : error 017: undefined symbol "staticval" +gh_353_symbol_suggestions.pwn(58) : error 017: undefined symbol "val"; did you mean "var"? +gh_353_symbol_suggestions.pwn(62) : error 017: undefined symbol "celmax"; did you mean "cellmax"? +gh_353_symbol_suggestions.pwn(66) : error 017: undefined symbol "strcaf"; did you mean "strcat"? +gh_353_symbol_suggestions.pwn(69) : error 017: undefined symbol "test_e17"; did you mean "test_e017"? +gh_353_symbol_suggestions.pwn(78) : error 019: not a label: "lb"; did you mean "lbl"? +gh_353_symbol_suggestions.pwn(85) : error 020: invalid symbol name "assert"; did you mean "asset"? +gh_353_symbol_suggestions.pwn(96) : error 080: unknown symbol, or not a constant symbol (symbol "idx"); did you mean "id"? +gh_353_symbol_suggestions.pwn(107) : error 086: unknown automaton "automaton1"; did you mean "automaton_1"? +gh_353_symbol_suggestions.pwn(107) : error 036: empty statement +gh_353_symbol_suggestions.pwn(114) : error 087: unknown state "BEING1" for automaton "automaton_2"; did you mean "BEING_1"? +gh_353_symbol_suggestions.pwn(114) : error 036: empty statement +gh_353_symbol_suggestions.pwn(117) : error 087: unknown state "STATE_1" for automaton "automaton_2"; did you mean "automaton_1:STATE_1"? +gh_353_symbol_suggestions.pwn(117) : error 036: empty statement + """ +} diff --git a/source/compiler/tests/gh_353_symbol_suggestions.pwn b/source/compiler/tests/gh_353_symbol_suggestions.pwn new file mode 100644 index 0000000..3a9e792 --- /dev/null +++ b/source/compiler/tests/gh_353_symbol_suggestions.pwn @@ -0,0 +1,118 @@ +#include +#include +#include +#include "gh_353_symbol_suggestions.inc" + +forward test_nosuggest1(); +public test_nosuggest1() +{ + // The compiler shouldn't suggest any name for this error + // since "abcxyz" and "abcd" differ by more than 2 symbols. + const abcd = 1; + printf("%d\n", abcxyz); + #pragma unused abcd +} + +forward test_nosuggest2(); +public test_nosuggest2() +{ + // There are no "()" after "length", so the compiler shouldn't suggest "flength". + printf("%d\n", length); +} + +forward test_nosuggest3(); +public test_nosuggest3() +{ + // float.inc is not #included, so float() is not defined. + // After the 1'st pass the compiler thinks float() is an unimplemented function, + // so it shouldn't suggest variable "flt" in this case. + new Float:flt; + return float(0); + #pragma unused flt +} + +forward test_nosuggest4(); +public test_nosuggest4() +{ + // "abc" is a label so the compiler shouldn't suggest its name + // where a variable or named constant is expected. +abc: + printf("%d\n", ab); + printf("%d\n", tagof ab); + #pragma unused abc +} + +forward test_nosuggest5(); +public test_nosuggest5() +{ + // As the name suggests, variable "staticvar" is defined as static + // within another file, so the compiler shouldn't suggest its name here. + return staticval; +} + +forward test_e017(); +public test_e017() +{ + // error 017: undefined symbol "val"; did you mean "var"? + new var = 1; + printf("%d\n", val); + #pragma unused var + + // error 017: undefined symbol "celmax"; did you mean "cellmax"? + printf("%d\n", celmax); + + // error 017: undefined symbol "strcaf"; did you mean "strcat"? + new str[4] = "a"; + strcaf(str, "b"); + + // error 017: undefined symbol "test_e17"; did you mean "test_e017"? + printf("%d\n", tagof test_e17); +} + +forward test_e019(); +public test_e019() +{ + // error 019: not a label: "lb"; did you mean "lbl"? +lbl: + goto lb; +} + +forward test_e020(); +public test_e020() +{ + // error 020: invalid symbol name "assert"; did you mean "asset"? + new asset = 0; + printf("%d\n", defined assert); + #pragma unused asset +} + +forward test_e080(); +public test_e080() +{ + // error 080: unknown symbol, or not a constant symbol (symbol "idx"); did you mean "id"? + new values[1]; + new idx = 0; + const id = 0; + printf("%d\n", sizeof values[idx]); + #pragma unused values, idx, id +} + +stock func1(){} +stock func2(){} + +forward test_e086(); +public test_e086() +{ + // error 086: unknown automaton "automaton1"; did you mean "automaton_1"? + state automaton1:STATE_1; +} + +forward test_e087(); +public test_e087() +{ + // error 087: unknown state BEING1" for automaton "automaton_2"; did you mean "BEING_1"? + state automaton_2:BEING1; + + // error 087: unknown state "STATE_1" for automaton "automaton_2"; did you mean "automaton_1:STATE_1"? + state automaton_2:STATE_1; +} diff --git a/source/compiler/tests/md_array_crash_gh_220.meta b/source/compiler/tests/md_array_crash_gh_220.meta new file mode 100644 index 0000000..065c642 --- /dev/null +++ b/source/compiler/tests/md_array_crash_gh_220.meta @@ -0,0 +1,8 @@ +{ + 'test_type': 'output_check', + 'errors': """ +md_array_crash_gh_220.pwn(6) : fatal error 111: user error: OK + +Compilation aborted. +""" +} diff --git a/source/compiler/tests/md_array_crash_gh_220.pwn b/source/compiler/tests/md_array_crash_gh_220.pwn index 74f27c7..20f2a75 100644 --- a/source/compiler/tests/md_array_crash_gh_220.pwn +++ b/source/compiler/tests/md_array_crash_gh_220.pwn @@ -3,4 +3,5 @@ new b[2000][500] = { { 0, -1, ... }, ... }; main() { a[0][0] = b[0][0]; -} \ No newline at end of file + #error OK +} diff --git a/source/compiler/tests/md_array_size_chk_gh_314.meta b/source/compiler/tests/md_array_size_chk_gh_314.meta new file mode 100644 index 0000000..44b8595 --- /dev/null +++ b/source/compiler/tests/md_array_size_chk_gh_314.meta @@ -0,0 +1,10 @@ +{ + 'test_type': 'output_check', + 'errors': """ +md_array_size_chk_gh_314.pwn(1) : error 009: invalid array size (negative, zero or out of bounds) +md_array_size_chk_gh_314.pwn(2) : error 009: invalid array size (negative, zero or out of bounds) +md_array_size_chk_gh_314.pwn(3) : error 009: invalid array size (negative, zero or out of bounds) +md_array_size_chk_gh_314.pwn(5) : error 009: invalid array size (negative, zero or out of bounds) +md_array_size_chk_gh_314.pwn(30) : warning 224: indeterminate array size in "sizeof" expression (symbol "") +""" +} diff --git a/source/compiler/tests/md_array_size_chk_gh_314.pwn b/source/compiler/tests/md_array_size_chk_gh_314.pwn new file mode 100644 index 0000000..f926648 --- /dev/null +++ b/source/compiler/tests/md_array_size_chk_gh_314.pwn @@ -0,0 +1,40 @@ +new arr1[]; +new arr2[5][]; +new arr3[5][][5]; +new arr4[5][5]; +new arr5[][]; + +f1(arr[]) { + #pragma unused arr +} +f2(arr[5][]) { + #pragma unused arr +} +f3(arr[5][][5]) { + #pragma unused arr +} +f4(arr[5][5]) { + #pragma unused arr +} +f5(arr[][]) { + #pragma unused arr +} + +main () { + arr1[0] = 0; + arr2[0][0] = 0; + arr3[0][0][0] = 0; + arr4[0][0] = 0; + arr5[0][0] = 0; + + new a = sizeof(arr1); + a = sizeof(arr1[]); + a = sizeof(arr5[][]); + #pragma unused a + + f1(arr1); + f2(arr2); + f3(arr3); + f4(arr4); + f5(arr5); +} diff --git a/source/compiler/tests/meaningless_class_specifiers_gh_172.meta b/source/compiler/tests/meaningless_class_specifiers_gh_172.meta new file mode 100644 index 0000000..d6ccf1a --- /dev/null +++ b/source/compiler/tests/meaningless_class_specifiers_gh_172.meta @@ -0,0 +1,7 @@ +{ + 'test_type': 'output_check', + 'errors': """ +meaningless_class_specifiers_gh_172.pwn(1) : warning 238: meaningless combination of class specifiers (const reference) +meaningless_class_specifiers_gh_172.pwn(4) : warning 238: meaningless combination of class specifiers (const variable arguments) +""" +} diff --git a/source/compiler/tests/meaningless_class_specifiers_gh_172.pwn b/source/compiler/tests/meaningless_class_specifiers_gh_172.pwn new file mode 100755 index 0000000..1fd4d91 --- /dev/null +++ b/source/compiler/tests/meaningless_class_specifiers_gh_172.pwn @@ -0,0 +1,24 @@ +f1(const &v) { + #pragma unused v +} +f2(const ...) { } +f3(const v) { + #pragma unused v +} +f4(...) { } +f5(v) { + #pragma unused v +} +f6(&v) { + #pragma unused v +} + +main() { + new a; + f1(a); + f2(a); + f3(a); + f4(a); + f5(a); + f6(a); +} diff --git a/source/compiler/tests/pcode_test_example.meta b/source/compiler/tests/pcode_test_example.meta new file mode 100644 index 0000000..efcbc92 --- /dev/null +++ b/source/compiler/tests/pcode_test_example.meta @@ -0,0 +1,12 @@ +{ + 'test_type': 'pcode_check', + 'code_pattern': r""" +[0-9a-f]+ proc +[0-9a-f]+ push.c 00000000 +[0-9a-f]+ push.c 00000004 +[0-9a-f]+ sysreq.c 00000000 +[0-9a-f]+ stack 00000008 +[0-9a-f]+ zero.pri +[0-9a-f]+ retn +""" +} diff --git a/source/compiler/tests/pcode_test_example.pwn b/source/compiler/tests/pcode_test_example.pwn new file mode 100644 index 0000000..bf6507d --- /dev/null +++ b/source/compiler/tests/pcode_test_example.pwn @@ -0,0 +1,5 @@ +#include + +main() { + printf("Hello World!"); +} diff --git a/source/compiler/tests/reset_errline_gh_230.meta b/source/compiler/tests/reset_errline_gh_230.meta new file mode 100644 index 0000000..8b754d7 --- /dev/null +++ b/source/compiler/tests/reset_errline_gh_230.meta @@ -0,0 +1,9 @@ +{ + 'test_type': 'output_check', + 'errors': """ +reset_errline_gh_230.pwn(2) : error 017: undefined symbol \"undefined\" +reset_errline_gh_230.pwn(2) : warning 215: expression has no effect +reset_errline_gh_230.pwn(7) : warning 204: symbol is assigned a value that is never used: "y" +reset_errline_gh_230.pwn(4) : warning 204: symbol is assigned a value that is never used: "x" +""" +} diff --git a/source/compiler/tests/run_tests.py b/source/compiler/tests/run_tests.py new file mode 100644 index 0000000..820f3c9 --- /dev/null +++ b/source/compiler/tests/run_tests.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python + +import argparse +import glob +import os.path +import re +import subprocess +import sys + +parser = argparse.ArgumentParser() +parser.add_argument('-c', '--compiler', + required=True, + help='path to compiler executable (pawncc)') +parser.add_argument('-d', '--disassembler', + help='path to disassembler executable (pawndisasm)') +parser.add_argument('-i', '--include', + dest='include_dirs', + action='append', + help='add custom include directories for compile tests') +parser.add_argument('-r', '--runner', + help='path to runner executable (pawnruns)') +parser.add_argument('tests', metavar='test_name', nargs='*') +options = parser.parse_args(sys.argv[1:]) + +def run_command(args, executable=None, merge_stderr=False): + process = subprocess.Popen(args, + executable=executable, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + stdout, stderr = process.communicate() + stdout = stdout.decode('utf-8') + stderr = stderr.decode('utf-8') + if merge_stderr: + output = '' + if stdout: + output += stdout + if stderr: + output += stderr + return (process, output) + else: + return (process, stdout, stderr) + +def run_compiler(args): + final_args = [';+', '-(+'] + if options.include_dirs is not None: + for dir in options.include_dirs: + final_args.append('-i' + dir) + if args is not None: + final_args += args + return run_command(executable=options.compiler, args=final_args) + +def normalize_newlines(s): + return s.replace('\r', '') + +def remove_asm_comments(s): + return re.sub(r'\s*;.*\n', '\n', s) + +def strip(s): + return s.strip(' \t\r\n') + +class OutputCheckTest: + def __init__(self, name, errors=None, extra_args=None): + self.name = name + self.errors = errors + self.extra_args = extra_args + + def run(self): + args = [self.name + '.pwn'] + if self.extra_args is not None: + args += extra_args + process, stdout, stderr = run_compiler(args=args) + if self.errors is None: + if process.returncode != 0: + result = False + self.fail_reason = """ + No errors specified and process exited with non-zero status + """ + return False + + errors = strip(stderr) + expected_errors = strip(self.errors) + if errors != expected_errors: + self.fail_reason = ( + 'Error output didn\'t match\n\nExpected errors:\n\n{}\n\n' + 'Actual errors:\n\n{}' + ).format(expected_errors, errors) + return False + return True + +class PCodeCheckTest: + def __init__(self, + name, + code_pattern=None, + extra_args=None): + self.name = name + self.code_pattern = code_pattern + self.extra_args = extra_args + + def run(self): + args = ['-d0', self.name + '.pwn'] + if self.extra_args is not None: + args += extra_args + process, stdout, stderr = run_compiler(args=args) + if process.returncode != 0: + self.fail_reason = \ + 'Compiler exited with status {}'.format(process.returncode) + errors = stderr + if errors: + self.fail_reason += '\n\nErrors:\n\n{}'.format(errors) + return False + + if options.disassembler is None: + self.fail_reason = 'Disassembler path is not set, can\'t run this test' + return False + process, output = run_command([ + options.disassembler, + self.name + '.amx' + ], merge_stderr=True) + if process.returncode != 0: + self.fail_reason = \ + 'Disassembler exited with status {}'.format(process.returncode) + if output: + self.fail_reason += '\n\nOutput:\n\n{}'.format(output) + return False + with open(self.name + '.lst', 'r') as dump_file: + dump = dump_file.read() + if self.code_pattern: + dump = remove_asm_comments(dump) + code_pattern = strip(normalize_newlines(self.code_pattern)) + if re.search(code_pattern, dump, re.MULTILINE) is None: + self.fail_reason = ( + 'Code didn\'t match\n\nExpected code:\n\n{}\n\n' + 'Actual code:\n\n{}' + ).format(code_pattern, dump) + return False + else: + self.fail_reason = 'Code pattern is required' + return False + return True + +class RuntimeTest: + def __init__(self, name, output, should_fail): + self.name = name + self.output = output + self.should_fail = should_fail + + def run(self): + process, stdout, stderr = run_compiler([self.name + '.pwn']) + if process.returncode != 0: + self.fail_reason = \ + 'Compiler exited with status {}'.format(process.returncode) + errors = stderr + if errors: + self.fail_reason += '\n\nErrors:\n\n{}'.format(errors) + return False + + if options.runner is None: + self.fail_reason = 'Runner path is not set, can\'t run this test' + return False + process, output = run_command([ + options.runner, self.name + '.amx' + ], merge_stderr=True) + if not self.should_fail and process.returncode != 0: + self.fail_reason = ( + 'Runner exited with status {}\n\nOutput: {}' + ).format(process.returncode, output) + return False + + output = strip(output) + expected_output = strip(self.output) + if output != expected_output: + self.fail_reason = ( + 'Output didn\'t match\n\nExpected output:\n\n{}\n\n' + 'Actual output:\n\n{}' + ).format(expected_output, output) + return False + return True + +tests = [] +num_tests_disabled = 0 + +for meta_file in glob.glob('*.meta'): + name = os.path.splitext(meta_file)[0] + if options.tests and name not in options.tests: + continue + metadata = eval(open(meta_file).read(), None, None) + if metadata.get('disabled'): + num_tests_disabled += 1 + continue + + test_type = metadata['test_type'] + if test_type == 'output_check': + tests.append(OutputCheckTest( + name=name, + errors=metadata.get('errors'), + extra_args=metadata.get('extra_args'))) + elif test_type == 'pcode_check': + tests.append(PCodeCheckTest( + name=name, + code_pattern=metadata.get('code_pattern'), + extra_args=metadata.get('extra_args'))) + elif test_type == 'runtime': + tests.append(RuntimeTest( + name=name, + output=metadata.get('output'), + should_fail=metadata.get('should_fail'))) + else: + raise KeyError('Unknown test type: ' + test_type) + +num_tests = len(tests) +sys.stdout.write( + 'DISCOVERED {} TEST{}'.format(num_tests, '' if num_tests == 1 else 'S')) +if num_tests_disabled > 0: + sys.stdout.write(' ({} DISABLED)'.format(num_tests_disabled)) + +if num_tests > 0: + sys.stdout.write('\n\n') + + num_tests_failed = 0 + for test in tests: + sys.stdout.write('Running ' + test.name + '... ') + if not test.run(): + sys.stdout.write('FAILED\n') + print('Test {} failed for the following reason: {}'.format( + test.name, test.fail_reason)) + print('') + num_tests_failed += 1 + else: + sys.stdout.write('PASSED\n') + num_tests_passed = len(tests) - num_tests_failed + if num_tests_failed > 0: + print('\n{} TEST{} PASSED, {} FAILED'.format( + num_tests_passed, + '' if num_tests_passed == 1 else 'S', + num_tests_failed)) + sys.exit(1) + else: + print('\nALL TESTS PASSED') diff --git a/source/compiler/tests/runtime_test_example.meta b/source/compiler/tests/runtime_test_example.meta new file mode 100644 index 0000000..49f67ed --- /dev/null +++ b/source/compiler/tests/runtime_test_example.meta @@ -0,0 +1,7 @@ +{ + 'test_type': 'runtime', + 'output': """ + Run time error 4: "Array index out of bounds" + """, + 'should_fail': True +} diff --git a/source/compiler/tests/runtime_test_example.pwn b/source/compiler/tests/runtime_test_example.pwn new file mode 100644 index 0000000..9907096 --- /dev/null +++ b/source/compiler/tests/runtime_test_example.pwn @@ -0,0 +1,10 @@ +#include + +main() { + new a[1] = {1}; + new i = 1; + + // When compiled with at least debug level 1 (default) the line below should produce: + // Run time error 4: "Array index out of bounds" + printf("%d", a[i]); +} diff --git a/source/compiler/tests/too_many_args_crash_gh_298.meta b/source/compiler/tests/too_many_args_crash_gh_298.meta new file mode 100644 index 0000000..9017075 --- /dev/null +++ b/source/compiler/tests/too_many_args_crash_gh_298.meta @@ -0,0 +1,12 @@ +{ + 'test_type': 'output_check', + 'errors': """ +too_many_args_crash_gh_298.pwn(2) : error 045: too many function arguments +too_many_args_crash_gh_298.pwn(2) : warning 215: expression has no effect +too_many_args_crash_gh_298.pwn(2) : error 001: expected token: ";", but found ")" +too_many_args_crash_gh_298.pwn(2) : error 029: invalid expression, assumed zero +too_many_args_crash_gh_298.pwn(2) : fatal error 107: too many error messages on one line + +Compilation aborted. +""" +} diff --git a/source/compiler/tests/too_many_args_crash_gh_298.pwn b/source/compiler/tests/too_many_args_crash_gh_298.pwn new file mode 100644 index 0000000..5c07367 --- /dev/null +++ b/source/compiler/tests/too_many_args_crash_gh_298.pwn @@ -0,0 +1,3 @@ +main() { + printf("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +} diff --git a/source/compiler/tests/unused_symbol_line_gh_252.meta b/source/compiler/tests/unused_symbol_line_gh_252.meta new file mode 100644 index 0000000..4cfffc8 --- /dev/null +++ b/source/compiler/tests/unused_symbol_line_gh_252.meta @@ -0,0 +1,8 @@ +{ + 'test_type': 'output_check', + 'errors': """ +unused_symbol_line_gh_252.pwn(4) : warning 203: symbol is never used: "y" +unused_symbol_line_gh_252.pwn(8) : warning 203: symbol is never used: "z" +unused_symbol_line_gh_252.pwn(1) : warning 203: symbol is never used: "x" +""" +}