Creating bindings for C libraries/ru: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Creating bindings for C libraries/ru
| Old | New | Line | |
|---|---|---|---|
| 1 | + | # Введение | |
| 2 | + | ||
| 3 | + | Эта статья описывает, как подключить и использовать С-библиотеки в языке Паскаль. Для использования библиотек, написанных на языке С вы должны преобразовать все типы данных, функции и переменные в библиотеках в соответствии стандартам языка Паскаль. Выполнить данную операцию можно используя утилиту h2pas (или аналогичное ПО), либо вручную, переписав код. | |
| 4 | + | ||
| 5 | + | # H2PAS | |
| 6 | + | ||
| 7 | + | ## Обзор | |
| 8 | + | ||
| 9 | + | [[H2Pas/ru|H2Pas]] это инструмент, который автоматически переводит C библиотеки. GUI для Lazarus, использующее h2pas и другие инструменты автоматизируют процесс. Он помогает создать набор правил, которые можно использовать чтобы обновить порт, отчего после обновления C библиотеки перевести ее будет намного проще. Приятной особенностью является то, что Мастер h2pas использует временные файлы, поэтому файлы заголовков остаются нетронутыми. | |
| 10 | + | ||
| 11 | + | ## Процесс работы | |
| 12 | + | ||
| 13 | + | - Найдите C заголовки, которые вы хотите перевести. | |
| 14 | + | - Создайте папку для работы и придумайте имя для портированной библиотеки. | |
| 15 | + | - Создайте проект h2pas используя Мастер h2pas. | |
| 16 | + | - Добавьте заголовочные файлы в проект. | |
| 17 | + | - Настройте проект h2pas. | |
| 18 | + | - Запустите мастер. | |
| 19 | + | - Исправьте ошибки и запустите мастер еще раз. | |
| 20 | + | - Когда h2pas запустится без ошибок, протестируйте сборку и отформатируйте код. | |
| 21 | + | - Напишите пару программ для тестов. | |
| 22 | + | - Опубликуйте порт на lazarus-ccr или на сайте FreePascal. | |
| 23 | + | ||
| 24 | + | ## Установка инструментов | |
| 25 | + | ||
| 26 | + | Инструмент h2pas поставляется с каждой обычной установкой fpc | |
| 27 | + | ||
| 28 | + | Установите пакет h2paswizard в IDE Lazarus. Откройте "Package-\>Install/Uninstall Packages", выберите в правом списке пакет H2PasWizard и щёлкните 'Install selection', потом - 'Save and rebuild IDE'. Перезапустите IDE и у вас появится новый пункт меню: Tools -\> h2pas | |
| 29 | + | ||
| 30 | + | ## Получение заголовочных файлов C | |
| 31 | + | ||
| 32 | + | Заголовочный файл C имеет расширение .h и описывает интерфейс C библиотеки. Обычно их не предоставляют вместе с библиотекой. Для этого нужно скачать исходный код или пакет разработчика библиотеки. Например заголовочные файлы библиотеки gtk находятся в gtk+-devel. | |
| 33 | + | ||
| 34 | + | ### Пример: MPICH2 | |
| 35 | + | ||
| 36 | + | Скачайте mpich2-1.0.3.tar.gz здесь: <http://www-unix.mcs.anl.gov/mpi/mpich2/> и распакуйте его. Файлы .h находятся в mpich2-1.0.3/src/include. | |
| 37 | + | ||
| 38 | + | Больше информации о MPICH для FPC вы можете найти [[MPICH|здесь]]. | |
| 39 | + | ||
| 40 | + | ## Создание папки для работы, выбор имени | |
| 41 | + | ||
| 42 | + | Создайте папку с подходящим названием. Имя не должно содержать специальных символов: пробелов, запятых и т.д. Скопируйте файлы .h в эту папку. | |
| 43 | + | ||
| 44 | + | ### Пример: MPICH2 | |
| 45 | + | ||
| 46 | + | Мы будем использовать папку h2p для файлов Pascal. h2p/c_sources будет использоваться для файлов .h. | |
| 47 | + | ||
| 48 | + | ``` bash | |
| 49 | + | mkdir -p h2p/c_sources | |
| 50 | + | cp mpich2-1.0.3/src/include/*.h h2p/c_sources/ | |
| 51 | + | ``` | |
| 52 | + | ||
| 53 | + | ## Создание проекта h2pas с помощью мастера | |
| 54 | + | ||
| 55 | + | Откройте мастер h2pas: "Tools -\> h2pas". Вы сможете переключаться между окном мастера и другими окнами IDE. Ваш последний проект h2pas откроется автоматически. Чтобы создать новый проект, нажмите "Settings -\> New/Clear settings". Потом нажмите на кнопку "Save settings" и выберете имя файла. | |
| 56 | + | ||
| 57 | + | Заметка: Если вы открываете мастер в первый раз, то вам нужно будет в меню настроек задать путь к h2pas.exe. Конвертер h2pas находится там-же где и компилятор FreePascal (fpc.exe), если вы скачали FPC вместе с Lazarus, то смотрите в ...\Lazarus\fpc\*version*\bin. Сохраните настройки чтоб не повторять те же действия снова. | |
| 58 | + | ||
| 59 | + | ### Пример: MPICH2 | |
| 60 | + | ||
| 61 | + | Нажмите "Settings -\> New/Clear settings".Потом нажмите на кнопку "Save settings" и сохраните как h2p/mpi2.h2p. | |
| 62 | + | ||
| 63 | + | ## Add the .h files to the h2pas-project | |
| 64 | + | ||
| 65 | + | In the "C header files" page you can add/delete the .h files. You can enable/disable .h files to convert only part of the files. | |
| 66 | + | ||
| 67 | + | ### Example: MPICH2 | |
| 68 | + | ||
| 69 | + | Click on "C header files -\> Add .h files ..." and select "mpi.h" and "mpio.h". They will be enabled automatically. Select the mpi.h file and click "Merge all but this", so that the wizard combines all header files into one unit (mpi.pas). | |
| 70 | + | ||
| 71 | + | ## Setup the h2pas options | |
| 72 | + | ||
| 73 | + | Under "h2pas Options" you can set the parameters for the h2pas program. | |
| 74 | + | ||
| 75 | + | ### Example: MPICH2 | |
| 76 | + | ||
| 77 | + | - Enable -e, -D, -p, -w and disable all others. | |
| 78 | + | - The -l library path is "mpich". | |
| 79 | + | - Output extension is ".pas" | |
| 80 | + | - Output directory is h2pas/, which is the default, so leave it empty | |
| 81 | + | ||
| 82 | + | ## Run the wizard | |
| 83 | + | ||
| 84 | + | Click on the bottom button "Run h2pas". This will copy the \<example\>.h file to a temporary \<example\>.tmp.h file and run the tools listed under "Before h2pas". Then it runs h2pas to convert the \<example\>.tmp.h into \<example\>.inc or \<example\>.pas or whatever output extension you setup on the h2pas page. Then it runs the tools listed under "After h2pas" on the output files. | |
| 85 | + | ||
| 86 | + | If h2pas finds a syntax error, the IDE will open the example.tmp.h file and jumps to the error line. h2pas often only reports 'syntax error', which is very unspecific. See [[Common problems when converting C header files]]. | |
| 87 | + | ||
| 88 | + | ### Example: MPICH2 | |
| 89 | + | ||
| 90 | + | The h2pas wizard already contains all tools to convert all the specials of this header file, so h2pas runs without errors. But the created unit is not yet ready. Read further. | |
| 91 | + | ||
| 92 | + | Add the following to the *Undefines* property: | |
| 93 | + | ||
| 94 | + | ` MPI_INCLUDED`\ | |
| 95 | + | ` MPIO_INCLUDE`\ | |
| 96 | + | ` NEEDS_MPI_FINT`\ | |
| 97 | + | ` MPI_BUILD_PROFILING`\ | |
| 98 | + | ` MPICH_SUPPRESS_PROTOTYPES`\ | |
| 99 | + | ` MPI_Wtime`\ | |
| 100 | + | ` HAVE_MPICH2G2`\ | |
| 101 | + | ` FOO`\ | |
| 102 | + | ` HAVE_PRAGMA_HP_SEC_DEF` | |
| 103 | + | ||
| 104 | + | Add the following to the *Defines* property: | |
| 105 | + | ||
| 106 | + | ` MPICH2`\ | |
| 107 | + | ` MPICH_NEW_MPIIO` | |
| 108 | + | ||
| 109 | + | ## Writing your own converter tools | |
| 110 | + | ||
| 111 | + | ### Using the tool "Search and replace" | |
| 112 | + | ||
| 113 | + | Many things like renaming a variable can be done by the **Search and replace** tool. Add the tool via the **Add new tool** button on either the 'Before h2pas' or the 'After h2pas' page. Then set the **SearchFor**, **ReplaceWith**, **Options** and **Caption** property. | |
| 114 | + | ||
| 115 | + | ### Example: rename an identifier Tguint to guint | |
| 116 | + | ||
| 117 | + | | Property | Value | | |
| 118 | + | |-------------|-------------------------------| | |
| 119 | + | | Caption | Rename Tguint to guint | | |
| 120 | + | | SearchFor | Tguint | | |
| 121 | + | | ReplaceWith | guint | | |
| 122 | + | | Options | \[trtMatchCase,trtWholeWord\] | | |
| 123 | + | ||
| 124 | + | ### Example: rename several identifiers | |
| 125 | + | ||
| 126 | + | Rename Tguint to guint, Tgulong to gulong, Tgint to gint: | |
| 127 | + | ||
| 128 | + | <table> | |
| 129 | + | <thead> | |
| 130 | + | <tr> | |
| 131 | + | <th><p>Property</p></th> | |
| 132 | + | <th><p>Value</p></th> | |
| 133 | + | </tr> | |
| 134 | + | </thead> | |
| 135 | + | <tbody> | |
| 136 | + | <tr> | |
| 137 | + | <td><p>Caption</p></td> | |
| 138 | + | <td><p>Rename Tguint to guint</p></td> | |
| 139 | + | </tr> | |
| 140 | + | <tr> | |
| 141 | + | <td><p>SearchFor</p></td> | |
| 142 | + | <td><pre><code>T(guint|gint|gulong)</code></pre></td> | |
| 143 | + | </tr> | |
| 144 | + | <tr> | |
| 145 | + | <td><p>ReplaceWith</p></td> | |
| 146 | + | <td><p>$1</p></td> | |
| 147 | + | </tr> | |
| 148 | + | <tr> | |
| 149 | + | <td><p>Options</p></td> | |
| 150 | + | <td><p>[trtMatchCase,trtWholeWord,trtRegExpr]</p></td> | |
| 151 | + | </tr> | |
| 152 | + | </tbody> | |
| 153 | + | </table> | |
| 154 | + | ||
| 155 | + | ### Example: MPICH2 | |
| 156 | + | ||
| 157 | + | #### Removing Makro MPI_DUP_FN | |
| 158 | + | ||
| 159 | + | The *MPI_DUP_FN* is defined two times in the mpi C headers. Once as a macro to something that does not exist, and later to a real function. So, the first should be deleted. | |
| 160 | + | ||
| 161 | + | Add a 'Search and replace' tool to the before H2Pas tools (at the end) with the following settings: | |
| 162 | + | ||
| 163 | + | <table> | |
| 164 | + | <thead> | |
| 165 | + | <tr> | |
| 166 | + | <th><p>Property</p></th> | |
| 167 | + | <th><p>Value</p></th> | |
| 168 | + | </tr> | |
| 169 | + | </thead> | |
| 170 | + | <tbody> | |
| 171 | + | <tr> | |
| 172 | + | <td><p>Caption</p></td> | |
| 173 | + | <td><p>Remove dummy define MPI_DUP_FN</p></td> | |
| 174 | + | </tr> | |
| 175 | + | <tr> | |
| 176 | + | <td><p>Name</p></td> | |
| 177 | + | <td><p>RemoveDummyDefineMPI_DUP_FN</p></td> | |
| 178 | + | </tr> | |
| 179 | + | <tr> | |
| 180 | + | <td><p>SearchFor</p></td> | |
| 181 | + | <td><pre><code>^#define\s+MPI_DUP_FN\s+MPIR_Dup_fn</code></pre></td> | |
| 182 | + | </tr> | |
| 183 | + | <tr> | |
| 184 | + | <td><p>ReplaceWith</p></td> | |
| 185 | + | <td></td> | |
| 186 | + | </tr> | |
| 187 | + | <tr> | |
| 188 | + | <td><p>Options</p></td> | |
| 189 | + | <td><p>[trtMatchCase,trtRegExpr]</p></td> | |
| 190 | + | </tr> | |
| 191 | + | </tbody> | |
| 192 | + | </table> | |
| 193 | + | ||
| 194 | + | #### Removing typecasts to MPI_DUP_FN | |
| 195 | + | ||
| 196 | + | Add a 'Search and replace' tool to the before H2Pas tools (at the end) with the following settings: | |
| 197 | + | ||
| 198 | + | <table> | |
| 199 | + | <thead> | |
| 200 | + | <tr> | |
| 201 | + | <th><p>Property</p></th> | |
| 202 | + | <th><p>Value</p></th> | |
| 203 | + | </tr> | |
| 204 | + | </thead> | |
| 205 | + | <tbody> | |
| 206 | + | <tr> | |
| 207 | + | <td><p>Caption</p></td> | |
| 208 | + | <td><p>Remove MPI_DUP_FN Makros</p></td> | |
| 209 | + | </tr> | |
| 210 | + | <tr> | |
| 211 | + | <td><p>Name</p></td> | |
| 212 | + | <td><p>Remove_MPI_DUP_Func</p></td> | |
| 213 | + | </tr> | |
| 214 | + | <tr> | |
| 215 | + | <td><p>SearchFor</p></td> | |
| 216 | + | <td><pre><code>^#define .*MPI_DUP_FN\).*$</code></pre></td> | |
| 217 | + | </tr> | |
| 218 | + | <tr> | |
| 219 | + | <td><p>ReplaceWith</p></td> | |
| 220 | + | <td></td> | |
| 221 | + | </tr> | |
| 222 | + | <tr> | |
| 223 | + | <td><p>Options</p></td> | |
| 224 | + | <td><p>[trtMatchCase,trtRegExpr]</p></td> | |
| 225 | + | </tr> | |
| 226 | + | </tbody> | |
| 227 | + | </table> | |
| 228 | + | ||
| 229 | + | #### Removing functions MPIO_Test, PMPIO_Test, MPIO_Wait and PMPIO_Wait | |
| 230 | + | ||
| 231 | + | The MPIO_Test, PMPIO_Test, MPIO_Wait and PMPIO_Wait functions are defined twice and redefined via macro. So, the functions can be removed: | |
| 232 | + | ||
| 233 | + | Add a 'Search and replace' tool to the before H2Pas tools (at the end) with the following settings: | |
| 234 | + | ||
| 235 | + | <table> | |
| 236 | + | <thead> | |
| 237 | + | <tr> | |
| 238 | + | <th><p>Property</p></th> | |
| 239 | + | <th><p>Value</p></th> | |
| 240 | + | </tr> | |
| 241 | + | </thead> | |
| 242 | + | <tbody> | |
| 243 | + | <tr> | |
| 244 | + | <td><p>Caption</p></td> | |
| 245 | + | <td><pre><code>Remove Func P?MPIO_(Test|Wait)</code></pre></td> | |
| 246 | + | </tr> | |
| 247 | + | <tr> | |
| 248 | + | <td><p>Name</p></td> | |
| 249 | + | <td><p>RemoveFuncP_MPIO_Test_Wait</p></td> | |
| 250 | + | </tr> | |
| 251 | + | <tr> | |
| 252 | + | <td><p>SearchFor</p></td> | |
| 253 | + | <td><pre><code>^int P?MPIO_(Test|Wait).*$</code></pre></td> | |
| 254 | + | </tr> | |
| 255 | + | <tr> | |
| 256 | + | <td><p>ReplaceWith</p></td> | |
| 257 | + | <td></td> | |
| 258 | + | </tr> | |
| 259 | + | <tr> | |
| 260 | + | <td><p>Options</p></td> | |
| 261 | + | <td><p>[trtMatchCase,trtRegExpr]</p></td> | |
| 262 | + | </tr> | |
| 263 | + | </tbody> | |
| 264 | + | </table> | |
| 265 | + | ||
| 266 | + | ## Improving an existing tool | |
| 267 | + | ||
| 268 | + | You found a bug and want to fix it or you want to extend one of the above tools. Great! | |
| 269 | + | ||
| 270 | + | Most of the above tools are defined in the h2pasconvert unit, which is part of the h2paswizard package. Basically a tool needs a classname, a description and an **Execute** method. To test/debug a tool outside the IDE and save a lot of compilation time, see the project components/simpleideintf/examples/testh2pastool.lpi. Compile it and start it on a console/terminal with the filename of a .h file as first command parameter. For example: | |
| 271 | + | ||
| 272 | + | ``` bash | |
| 273 | + | ./testh2pastool files/h2pastest.pas | |
| 274 | + | ``` | |
| 275 | + | ||
| 276 | + | ## Writing a custom tool | |
| 277 | + | ||
| 278 | + | You can write your own conversion tools and register them in the IDE easily. Start a package and a add a new unit (say unit1) for your new class. See the existing tools as example and read the prior section. When you wrote a new tool and tested it with the above simpleideintf project, then register it in the IDE: Add a **register** procedure to your unit (unit1), like the following (pseudo code): | |
| 279 | + | ||
| 280 | + | ``` pascal | |
| 281 | + | uses | |
| 282 | + | Classes, ..., IDETextConverter; | |
| 283 | + | ||
| 284 | + | type | |
| 285 | + | TYourToolClass = class(TCustomTextConverterTool) | |
| 286 | + | public | |
| 287 | + | class function ClassDescription: string; override; | |
| 288 | + | function Execute(aText: TIDETextConverter): TModalResult; override; | |
| 289 | + | end; | |
| 290 | + | ||
| 291 | + | procedure Register; | |
| 292 | + | ||
| 293 | + | implementation | |
| 294 | + | ||
| 295 | + | procedure Register; | |
| 296 | + | begin | |
| 297 | + | TextConverterToolClasses.RegisterClass(TYourToolClass); | |
| 298 | + | end; | |
| 299 | + | ``` | |
| 300 | + | ||
| 301 | + | Do not forget to enable the **register** checkbox of the unit in the package editor, otherwise the Register procedure will not be called by the IDE. Then install your package in the IDE. | |
| 302 | + | ||
| 303 | + | ## Future work / Missing stuff | |
| 304 | + | ||
| 305 | + | - A tool to convert static linked procedures to dynamic linked procedure variables | |
| 306 | + | - A tool to fix functions without parameter names by searching the .c files. This could also add a comment, in which c file it found the function. | |
| 307 | + | - Find missing identifiers and let the user check which one to comment or replace with base types. | |
| 308 | + | - Create a list of macro functions, which were half translated. | |
| 309 | + | ||
| 310 | + | C: | |
| 311 | + | ||
| 312 | + | ``` c | |
| 313 | + | typedef struct page | |
| 314 | + | { | |
| 315 | + | u32int present : 1; // Page present in memory | |
| 316 | + | u32int rw : 1; // Read-only if clear, readwrite if set | |
| 317 | + | u32int user : 1; // Supervisor level only if clear | |
| 318 | + | u32int accessed : 1; // Has the page been accessed since last refresh? | |
| 319 | + | u32int dirty : 1; // Has the page been written to since last refresh? | |
| 320 | + | u32int unused : 7; // Amalgamation of unused and reserved bits | |
| 321 | + | u32int frame : 20; // Frame address (shifted right 12 bits) | |
| 322 | + | } page_t; | |
| 323 | + | ``` | |
| 324 | + | ||
| 325 | + | Pascal: | |
| 326 | + | ||
| 327 | + | ``` pascal | |
| 328 | + | type | |
| 329 | + | Unsigned_7 = 0 .. (1 shl 7) - 1; | |
| 330 | + | Unsigned_20 = 0 .. (1 shl 20) - 1; | |
| 331 | + | ||
| 332 | + | type | |
| 333 | + | page_t = bitpacked record | |
| 334 | + | present : boolean; | |
| 335 | + | rw : boolean; | |
| 336 | + | user : boolean; | |
| 337 | + | accessed : boolean; | |
| 338 | + | dirty : boolean; | |
| 339 | + | unused : Unsigned_7; | |
| 340 | + | frame : Unsigned_20; | |
| 341 | + | end; | |
| 342 | + | ``` | |
| 343 | + | ||
| 344 | + | # Convert C to Pascal with lua script | |
| 345 | + | ||
| 346 | + | I'm impressed by the power of the lua scripting language and how easy it is to do funny things with it. | |
| 347 | + | ||
| 348 | + | ``` lua | |
| 349 | + | -- | |
| 350 | + | -- Please send any improvement to mingodadATgmailDOTcom. | |
| 351 | + | -- | |
| 352 | + | ||
| 353 | + | inFileName = arg[1] or 'g:\\tmp\\plua\\clua\\lvm.c' | |
| 354 | + | ||
| 355 | + | fh = assert(io.open(inFileName)) | |
| 356 | + | cbody = fh:read('*a') | |
| 357 | + | fh:close() | |
| 358 | + | ||
| 359 | + | pasbody = cbody | |
| 360 | + | ||
| 361 | + | function split(str, pat) | |
| 362 | + | local t = {} -- NOTE: use {n = 0} in Lua-5.0 | |
| 363 | + | local fpat = '(.-)' .. pat | |
| 364 | + | local last_end = 1 | |
| 365 | + | local s, e, cap = str:find(fpat, 1) | |
| 366 | + | while s do | |
| 367 | + | if s ~= 1 or cap ~= '' then | |
| 368 | + | table.insert(t, cap) | |
| 369 | + | end | |
| 370 | + | last_end = e+1 | |
| 371 | + | s, e, cap = str:find(fpat, last_end) | |
| 372 | + | end | |
| 373 | + | if last_end <= #str then | |
| 374 | + | cap = str:sub(last_end) | |
| 375 | + | table.insert(t, cap) | |
| 376 | + | end | |
| 377 | + | return t | |
| 378 | + | end | |
| 379 | + | ||
| 380 | + | function trim(s) | |
| 381 | + | -- from PiL2 20.4 | |
| 382 | + | return (s:gsub('^%s*(.-)%s*$', '%1')) | |
| 383 | + | end | |
| 384 | + | ||
| 385 | + | function trimChars(str, c1, c2) | |
| 386 | + | return str:gsub('^%' .. c1 .. '*(.-)%' .. c2 .. '*$', '%1') | |
| 387 | + | end | |
| 388 | + | ||
| 389 | + | function getPascalType(cType) | |
| 390 | + | if cType == 'void' then | |
| 391 | + | return 'pointer' | |
| 392 | + | elseif cType == 'int' then | |
| 393 | + | return 'integer' | |
| 394 | + | elseif cType == 'short' then | |
| 395 | + | return 'Shortint' | |
| 396 | + | elseif cType == 'define' then | |
| 397 | + | return 'is_define' | |
| 398 | + | else | |
| 399 | + | return cType | |
| 400 | + | end | |
| 401 | + | end | |
| 402 | + | ||
| 403 | + | --check var name and type | |
| 404 | + | --if var starts with a capital letter | |
| 405 | + | -- add 'T' informt of it | |
| 406 | + | --if var is preceded by * then add a 'P' for type | |
| 407 | + | ||
| 408 | + | function checkVarType(aType, aVar) | |
| 409 | + | aType = getPascalType(aType) | |
| 410 | + | aType = aType:gsub('^(%u)', 'T%1') | |
| 411 | + | aVar = aVar:gsub('^(%*)', function(m) | |
| 412 | + | aType = 'P' .. aType | |
| 413 | + | return '' | |
| 414 | + | end) | |
| 415 | + | aVar = aVar:gsub('(%[[^]]+%])', function(m) | |
| 416 | + | aType = 'array' .. m .. ' of ' .. aType | |
| 417 | + | return '' | |
| 418 | + | end) | |
| 419 | + | return aType, aVar | |
| 420 | + | end | |
| 421 | + | ||
| 422 | + | function varDeclaration(aType, aVar) | |
| 423 | + | aType, aVar = checkVarType(aType, aVar) | |
| 424 | + | return aVar .. ' : ' .. aType | |
| 425 | + | end | |
| 426 | + | ||
| 427 | + | function checkDeclarationOrGoto(m1, m2, m3) | |
| 428 | + | if m2 ~= 'goto' then | |
| 429 | + | return '\n' .. m1 .. varDeclaration(m2, m3) .. ';' | |
| 430 | + | else | |
| 431 | + | return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';' | |
| 432 | + | end | |
| 433 | + | end | |
| 434 | + | ||
| 435 | + | function addProcFunc(mtype, mname, mparams, mbody) | |
| 436 | + | local str, isFunc, rType, k, v | |
| 437 | + | if mtype == 'void' then | |
| 438 | + | str = '\nprocedure ' | |
| 439 | + | isFunc = false | |
| 440 | + | else | |
| 441 | + | str = '\nfunction ' | |
| 442 | + | isFunc = true | |
| 443 | + | end | |
| 444 | + | mtype, mname = checkVarType(mtype, mname) | |
| 445 | + | local vparams = split(trimChars(mparams, '(',')'), ',') | |
| 446 | + | str = str .. mname .. '(' | |
| 447 | + | ||
| 448 | + | local tparams = {} | |
| 449 | + | for k, v in pairs(vparams) do | |
| 450 | + | local vparam = split(trim(v), ' ') | |
| 451 | + | if #vparam > 2 then | |
| 452 | + | table.insert(tparams, vparam[1] .. ' ' .. varDeclaration(vparam[2], vparam[3])) | |
| 453 | + | else | |
| 454 | + | --print(vparam[1], vparam[2]) | |
| 455 | + | table.insert(tparams, varDeclaration(vparam[1], vparam[2] or 'is_define')) | |
| 456 | + | end | |
| 457 | + | end | |
| 458 | + | ||
| 459 | + | str = str .. table.concat(tparams, '; ') .. ')' | |
| 460 | + | if isFunc then | |
| 461 | + | if mtype == 'int' then | |
| 462 | + | rType = 'integer' | |
| 463 | + | else | |
| 464 | + | rType = mtype | |
| 465 | + | end | |
| 466 | + | str = str .. ':' .. rType | |
| 467 | + | end | |
| 468 | + | ||
| 469 | + | if mbody then | |
| 470 | + | local tblLabels = {} | |
| 471 | + | for v in mbody:gmatch('\n[\t ]*([%w_]+)[\t ]*:[^=]') do | |
| 472 | + | if v ~= 'default' then | |
| 473 | + | table.insert(tblLabels, v) | |
| 474 | + | end | |
| 475 | + | end | |
| 476 | + | ||
| 477 | + | if #tblLabels > 0 then | |
| 478 | + | str = str .. ';\nlabel\n '.. table.concat(tblLabels, ', ') | |
| 479 | + | end | |
| 480 | + | ||
| 481 | + | local declarations = {} | |
| 482 | + | ||
| 483 | + | mbody = mbody:gsub('(\n[\t ]*)const[\t ]+([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)','%1 %2 %3 := %4') | |
| 484 | + | ||
| 485 | + | mbody = mbody:gsub('(\n[\t ]*)([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)', function(m1, m2, m3, m4) | |
| 486 | + | table.insert(declarations, varDeclaration(m2, m3) .. ';' ) | |
| 487 | + | return m1 .. m3 .. ' := ' .. m4 | |
| 488 | + | end) | |
| 489 | + | ||
| 490 | + | mbody = mbody:gsub('\n[\t ]*const[\t ]*([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*;', function(m1,m2) | |
| 491 | + | table.insert(declarations, varDeclaration(m1, m2) .. ';') | |
| 492 | + | return '' | |
| 493 | + | end) | |
| 494 | + | ||
| 495 | + | mbody = mbody:gsub('\n([\t ]*)([%w_]+)[\t ]+(%*?[%w_ ,]+);', function(m1,m2,m3) | |
| 496 | + | if m2 ~= 'goto' then | |
| 497 | + | table.insert(declarations, varDeclaration(m2, m3) .. ';') | |
| 498 | + | return '' | |
| 499 | + | else | |
| 500 | + | return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';' | |
| 501 | + | end | |
| 502 | + | end) | |
| 503 | + | ||
| 504 | + | if #declarations > 0 then | |
| 505 | + | local unique_decl = {} | |
| 506 | + | table.sort(declarations) | |
| 507 | + | local lastV = '' | |
| 508 | + | local k2, v2 | |
| 509 | + | for k2,v2 in pairs(declarations) do | |
| 510 | + | if v2 ~= lastV then | |
| 511 | + | lastV = v2 | |
| 512 | + | table.insert(unique_decl, v2) | |
| 513 | + | end | |
| 514 | + | end | |
| 515 | + | str = str .. ';\nvar\n '.. table.concat(unique_decl, '\n ') .. '\n' | |
| 516 | + | else | |
| 517 | + | str = str .. ';\n' | |
| 518 | + | end | |
| 519 | + | ||
| 520 | + | str = str .. 'begin' .. trimChars(mbody, '{','}') .. 'end' | |
| 521 | + | end | |
| 522 | + | return str .. ';' | |
| 523 | + | end | |
| 524 | + | ||
| 525 | + | --************************************* | |
| 526 | + | -- | |
| 527 | + | -- Order of substitutions are important | |
| 528 | + | -- | |
| 529 | + | --************************************* | |
| 530 | + | ||
| 531 | + | --=assignements statements | |
| 532 | + | pasbody = pasbody:gsub("([\t ]*)([%w_]+)[\t ]*=[\t ]*([^=;\n]+);", '%1%2 := %3;') | |
| 533 | + | ||
| 534 | + | --return | |
| 535 | + | pasbody = pasbody:gsub('([\t ]*)return[\t ]+([^;]+);', '%1exit(%2);') | |
| 536 | + | ||
| 537 | + | --NULL | |
| 538 | + | pasbody = pasbody:gsub('([^%w]+)NULL([^%w]+)', '%1nil%2') | |
| 539 | + | ||
| 540 | + | --void functions to procedure declaratios | |
| 541 | + | pasbody = pasbody:gsub('([%w_*]+)[\t ]+([%w_*]+)[\t ]*(%b())[%s]*(%b{})', addProcFunc) | |
| 542 | + | --pasbody = pasbody:gsub('\n([%w_]+)%s+([^%s]+)%s*(%b());', addProcFunc) | |
| 543 | + | ||
| 544 | + | --const/static | |
| 545 | + | tblLeaveCommented = { | |
| 546 | + | 'const', 'static' | |
| 547 | + | } | |
| 548 | + | for k,v in pairs(tblLeaveCommented) do | |
| 549 | + | pasbody = pasbody:gsub('([\t ]*)(' .. v .. ')([\t ]*)', '%1{%2}%3') | |
| 550 | + | end | |
| 551 | + | ||
| 552 | + | --defines to const | |
| 553 | + | isConstEmmited = false | |
| 554 | + | function addConst(m1,m2) | |
| 555 | + | local sm | |
| 556 | + | sm = '' | |
| 557 | + | if not isConstEmmited then | |
| 558 | + | isConstEmmited = true | |
| 559 | + | sm = '\nconst' | |
| 560 | + | end | |
| 561 | + | return sm .. '\n\t' .. m1 .. ' = ' .. m2 .. ';' | |
| 562 | + | end | |
| 563 | + | ||
| 564 | + | isConstEmmited = false | |
| 565 | + | pasbody = pasbody:gsub('\n#define%s+([^%s]+)%s+(%b())', '\nconst %1 = %2;') | |
| 566 | + | isConstEmmited = false | |
| 567 | + | pasbody = pasbody:gsub('\n#define%s+([^%s]+)[ \t]+([^%s]+)', '\nconst %1 = %2;') | |
| 568 | + | ||
| 569 | + | --includes | |
| 570 | + | tblUses = {} | |
| 571 | + | pasbody = pasbody:gsub('\n#[ \t]*(include)%s+([^\n]+)', function(m1,m2) | |
| 572 | + | for w in m2:gmatch('"([^.]+)%..+"') do | |
| 573 | + | table.insert(tblUses, w) | |
| 574 | + | end | |
| 575 | + | return '\n//#' .. m1 .. ' ' .. m2 | |
| 576 | + | end) | |
| 577 | + | pasbody = '//uses ' .. table.concat(tblUses, ', ') .. ';\n' .. pasbody | |
| 578 | + | ||
| 579 | + | --defines to compiler directives | |
| 580 | + | pasbody = pasbody:gsub('\n#([^\n]+)', '\n{$%1}') | |
| 581 | + | ||
| 582 | + | --comments | |
| 583 | + | pasbody = pasbody:gsub('\n#', '\n//#') | |
| 584 | + | pasbody = pasbody:gsub('/%*', '(*') | |
| 585 | + | pasbody = pasbody:gsub('%*/', '*)') | |
| 586 | + | ||
| 587 | + | --declarations | |
| 588 | + | pasbody = pasbody:gsub('\n([\t ]*)([%w_]+)[\t ]+([%w_ ,]+);', checkDeclarationOrGoto) | |
| 589 | + | ||
| 590 | + | --structs | |
| 591 | + | function parseStruct(m1, m2) | |
| 592 | + | return '\n' .. m1 .. ' = record\n' .. trimChars(m2, '{', '}') .. '\nend' | |
| 593 | + | end | |
| 594 | + | pasbody = pasbody:gsub('\n[\t ]*struct[\t ]+([%w_]+)[\t ]*(%b{})', parseStruct) | |
| 595 | + | ||
| 596 | + | --if statements | |
| 597 | + | parseStatementCalled = false | |
| 598 | + | ||
| 599 | + | function parseStatement(mSpace, mStatement, mCond, mBody) | |
| 600 | + | parseStatementCalled = true | |
| 601 | + | local vStatement | |
| 602 | + | if mStatement == 'if' then | |
| 603 | + | vStatement = mStatement .. ' ' .. mCond .. ' then' | |
| 604 | + | elseif mStatement == 'else' then | |
| 605 | + | return mSpace .. mStatement .. ' begin' .. trimChars(mCond, '{', '}') .. ' end;' | |
| 606 | + | elseif (mStatement == 'while') then | |
| 607 | + | vStatement = mStatement .. ' ' .. mCond .. ' do' | |
| 608 | + | elseif (mStatement == 'for') then | |
| 609 | + | vStatement = mStatement .. ' ' .. mCond .. ' do' | |
| 610 | + | --local forArgs = split(trimChars(mCond, '(', ')'), ';') | |
| 611 | + | --return mSpace .. (forArgs[1] or '') .. ';' .. mSpace ..'while (' .. | |
| 612 | + | -- (forArgs[2] or 'true') .. ') do' .. mSpace .. 'begin' .. mSpace .. trimChars(mBody, '{', '}') .. '\n' .. mSpace .. | |
| 613 | + | -- (forArgs[3] or '') .. ';\n' .. mSpace .. 'end;' | |
| 614 | + | else | |
| 615 | + | vStatement = mStatement .. ' ' .. mCond .. '\n' | |
| 616 | + | end | |
| 617 | + | return mSpace .. vStatement .. ' begin' .. trimChars(mBody, '{', '}') .. 'end;' | |
| 618 | + | end | |
| 619 | + | ||
| 620 | + | -- for nested blocks | |
| 621 | + | parseStatementCalled = true | |
| 622 | + | while(parseStatementCalled) do | |
| 623 | + | parseStatementCalled = false | |
| 624 | + | pasbody = pasbody:gsub('([^%w_])(if)[\t ]*(%b())[\t ]*(%b{})', parseStatement) | |
| 625 | + | end | |
| 626 | + | ||
| 627 | + | -- else | |
| 628 | + | pasbody = pasbody:gsub('([\t ]+)(else)[\t ]*(%b{})', parseStatement) | |
| 629 | + | ||
| 630 | + | pasbody = pasbody:gsub("([^%w_])if[\t ]*(%b())[\t ]*([^;\n]+);", '%1if %2 then %3;') | |
| 631 | + | ||
| 632 | + | --?: short if | |
| 633 | + | pasbody = pasbody:gsub("([^?])?([^:;\n]):[\t ]*([^%s;]+);", ' iff(%1, %2, %3) ') | |
| 634 | + | ||
| 635 | + | --while loop | |
| 636 | + | -- for nested blocks | |
| 637 | + | parseStatementCalled = true | |
| 638 | + | while(parseStatementCalled) do | |
| 639 | + | parseStatementCalled = false | |
| 640 | + | pasbody = pasbody:gsub('([^%w_])(while)[\t ]*(%b())[\t ]*(%b{})', parseStatement) | |
| 641 | + | end | |
| 642 | + | ||
| 643 | + | --for loop | |
| 644 | + | -- for nested blocks | |
| 645 | + | parseStatementCalled = true | |
| 646 | + | while(parseStatementCalled) do | |
| 647 | + | parseStatementCalled = false | |
| 648 | + | pasbody = pasbody:gsub('([^%w_])(for)[\t ]*(%b())%s*(%b{})', parseStatement) | |
| 649 | + | end | |
| 650 | + | ||
| 651 | + | --do/while | |
| 652 | + | -- for nested blocks | |
| 653 | + | parseStatementCalled = true | |
| 654 | + | while(parseStatementCalled) do | |
| 655 | + | parseStatementCalled = false | |
| 656 | + | pasbody = pasbody:gsub("([^%w_])do[\t ]*(%b{})[\t ]*while([^;\n]+);", | |
| 657 | + | function(m1, m2, m3) | |
| 658 | + | parseStatementCalled = true | |
| 659 | + | return m1 .. 'repeat ' .. trimChars(m2, '{', '}') .. 'until not' .. m3 | |
| 660 | + | end) | |
| 661 | + | end | |
| 662 | + | ||
| 663 | + | --switch/case statements | |
| 664 | + | ||
| 665 | + | --begin/end enclose blocks around | |
| 666 | + | -- for nested blocks | |
| 667 | + | parseStatementCalled = true | |
| 668 | + | while(parseStatementCalled) do | |
| 669 | + | parseStatementCalled = false | |
| 670 | + | pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(%b{})", | |
| 671 | + | function(m1, m2, m3) | |
| 672 | + | parseStatementCalled = true | |
| 673 | + | return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;' | |
| 674 | + | end) | |
| 675 | + | pasbody = pasbody:gsub("([^%w_]default[\t ]*):[%s]*(%b{})", | |
| 676 | + | function(m1, m2, m3) | |
| 677 | + | parseStatementCalled = true | |
| 678 | + | return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;' | |
| 679 | + | end) | |
| 680 | + | end | |
| 681 | + | ||
| 682 | + | --breaks remove | |
| 683 | + | pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(.-)break;", '%1: %2') | |
| 684 | + | ||
| 685 | + | --case | |
| 686 | + | pasbody = pasbody:gsub("([^%w_])case[\t ]+([^:]+):", '%1%2:') | |
| 687 | + | ||
| 688 | + | -- for nested blocks | |
| 689 | + | parseStatementCalled = true | |
| 690 | + | while(parseStatementCalled) do | |
| 691 | + | parseStatementCalled = false | |
| 692 | + | pasbody = pasbody:gsub("([^%w_])switch[\t ]*(%b())[\t ]*(%b{})", | |
| 693 | + | function(m1, m2, m3) | |
| 694 | + | parseStatementCalled = true | |
| 695 | + | return m1 .. 'case ' .. m2 .. ' of ' .. trimChars(m3, '{', '}') .. 'end;' | |
| 696 | + | end) | |
| 697 | + | end | |
| 698 | + | ||
| 699 | + | --pasbody = pasbody:gsub("([\t ]*)case[\t ]+('.')[\t ]*:\n", '%1%2:\n') | |
| 700 | + | --pasbody = pasbody:gsub("([\t ]*)case[\t ]+(%d+)[\t ]*:\n", '%1%2:\n') | |
| 701 | + | --pasbody = pasbody:gsub("([\t ]*)(case)[\t ]+('.')[\t ]*:[\t ]*(%b{})", '%1%2 : %3') | |
| 702 | + | ||
| 703 | + | --pre/pos increments | |
| 704 | + | -- (n)++ -> PosInc(n) | |
| 705 | + | pasbody = pasbody:gsub("(%([^%s+]+%))%+%+", 'PosInc%1') | |
| 706 | + | -- n*++ -> PosInc(n*) | |
| 707 | + | pasbody = pasbody:gsub("([^%s*+]+)%+%+", 'PosInc(%1)') | |
| 708 | + | -- ++(n) -> PreInc(n) | |
| 709 | + | pasbody = pasbody:gsub("%+%+(%([^%s;+]+%))", 'PreInc%1') | |
| 710 | + | -- ++*n -> PreInc(n) | |
| 711 | + | pasbody = pasbody:gsub("%+%+([^%s*;+]+)", 'PreInc(%1)') | |
| 712 | + | ||
| 713 | + | -- (n)-- | |
| 714 | + | pasbody = pasbody:gsub("(%([^%s-]+%))%-%-", 'PosDec%1') | |
| 715 | + | -- n*-- | |
| 716 | + | pasbody = pasbody:gsub("([^%s*-]+)%-%-", 'PosDec(%1)') | |
| 717 | + | -- --(n) | |
| 718 | + | pasbody = pasbody:gsub("%-%-(%([^%s;-]+%))", 'PreDec%1') | |
| 719 | + | -- --n | |
| 720 | + | pasbody = pasbody:gsub("%-%-([^%s;-]+)", 'PreDec(%1)') | |
| 721 | + | ||
| 722 | + | --boolean operators | |
| 723 | + | tblBooleanOperators = { | |
| 724 | + | {'||', 'or'}, | |
| 725 | + | {'&&', 'and'}, | |
| 726 | + | } | |
| 727 | + | for k,v in pairs(tblBooleanOperators) do | |
| 728 | + | pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1) ' .. v[2] .. ' (%2') | |
| 729 | + | end | |
| 730 | + | ||
| 731 | + | --pointers | |
| 732 | + | pasbody = pasbody:gsub("%->", '.') | |
| 733 | + | pasbody = pasbody:gsub("%(%*([%w_]+)", '(%1^') | |
| 734 | + | pasbody = pasbody:gsub("%*%*([%w_]+)", '%1^^') | |
| 735 | + | pasbody = pasbody:gsub("&([%w_]+)", '@%1') | |
| 736 | + | pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:[\t ]*([%w_]+);", '%1 : ^%2;') | |
| 737 | + | pasbody = pasbody:gsub("%^char", 'pchar') | |
| 738 | + | --pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:=[\t ]*([^;\n]+);", '%1^ := %2;') | |
| 739 | + | ||
| 740 | + | --bit operators | |
| 741 | + | tblBitOperators = { | |
| 742 | + | {'<<', 'shl'}, | |
| 743 | + | {'>>', 'shr'}, | |
| 744 | + | {'|', 'or'}, | |
| 745 | + | {'&', 'and'}, | |
| 746 | + | {'~', 'not'}, | |
| 747 | + | } | |
| 748 | + | ||
| 749 | + | for k,v in pairs(tblBitOperators) do | |
| 750 | + | pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1 ' .. v[2] .. ' %2') | |
| 751 | + | end | |
| 752 | + | ||
| 753 | + | --==eq operator | |
| 754 | + | pasbody = pasbody:gsub("([\t ]*)%=%=([\t ]*)", '%1 = %2') | |
| 755 | + | ||
| 756 | + | --!= not eq operator | |
| 757 | + | pasbody = pasbody:gsub("([\t ]*)!%=([\t ]*)", '%1 <> %2') | |
| 758 | + | ||
| 759 | + | --! unary | |
| 760 | + | pasbody = pasbody:gsub('([\t ]*)!([\t ]*)', '%1 not %2') | |
| 761 | + | ||
| 762 | + | --blocks | |
| 763 | + | --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' .. trimChars(m1, '{', '}') .. 'end;' end) | |
| 764 | + | --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' .. trimChars(m1, '{', '}') .. 'end;' end) | |
| 765 | + | ||
| 766 | + | --strings and escaped characters | |
| 767 | + | tblEscapedCahrs = { | |
| 768 | + | {'\\"', '"'}, | |
| 769 | + | {'\\\\', '\\'}, | |
| 770 | + | {'\\t', "'#9"}, | |
| 771 | + | {'\\n', "'#10'"}, | |
| 772 | + | {'\\f', "'#12'"}, | |
| 773 | + | } | |
| 774 | + | pasbody = pasbody:gsub('%b""', function(m1) | |
| 775 | + | return "'" .. trimChars(m1:gsub("'", "''") , '"', '"') .. "'" | |
| 776 | + | end) | |
| 777 | + | for k,v in pairs(tblEscapedCahrs) do | |
| 778 | + | pasbody = pasbody:gsub(v[1], v[2]) | |
| 779 | + | end | |
| 780 | + | ||
| 781 | + | --common functions/procedure | |
| 782 | + | tblOrigFuncNewFunc = { | |
| 783 | + | {'printf', 'format'}, | |
| 784 | + | {'malloc', 'GetMem'}, | |
| 785 | + | {'free', 'Dispose'}, | |
| 786 | + | {'memcpy', 'Move'}, | |
| 787 | + | } | |
| 788 | + | ||
| 789 | + | for k,v in pairs(tblOrigFuncNewFunc) do | |
| 790 | + | pasbody = pasbody:gsub('([\t ]+)' .. v[1] .. '%(', '%1' .. v[2] .. '(') | |
| 791 | + | end | |
| 792 | + | ||
| 793 | + | --typedefs | |
| 794 | + | isTypeEmmited = false | |
| 795 | + | function addType(m1,m2) | |
| 796 | + | local sm | |
| 797 | + | sm = '' | |
| 798 | + | if not isTypeEmmited then | |
| 799 | + | isTypeEmmited = true | |
| 800 | + | sm = '\ntype' | |
| 801 | + | end | |
| 802 | + | return sm .. '\n\t' .. m2 .. ' = ' .. m1 .. ';' | |
| 803 | + | end | |
| 804 | + | --pasbody = pasbody:gsub('\ntypedef%s+struct%s+([^%s]+)%s+([^;]+)', addType) | |
| 805 | + | --pasbody = pasbody:gsub('\ntypedef%s+([^%s]+)%s+([^;]-)', addType) | |
| 806 | + | pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct[\t ]+([%w_]+)[\t ]+([^;]+)', '\n%2 = %1') | |
| 807 | + | pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct%s+(%b{})%s+([%w_]+);', | |
| 808 | + | function(m1, m2) return m2 .. ' = record' .. trimChars(m1, '{', '}') .. 'end;' end) | |
| 809 | + | ||
| 810 | + | -- print the results | |
| 811 | + | print(pasbody) | |
| 812 | + | ``` | |
| 813 | + | ||
| 814 | + | Here is an example of a c file converted. | |
| 815 | + | ||
| 816 | + | ``` pascal | |
| 817 | + | //uses lua, llimits, lmem, lstate, lzio; | |
| 818 | + | (* | |
| 819 | + | ** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ | |
| 820 | + | ** a generic input stream interface | |
| 821 | + | ** See Copyright Notice in lua.h | |
| 822 | + | *) | |
| 823 | + | ||
| 824 | + | //#include <string.h> | |
| 825 | + | ||
| 826 | + | {$define lzio_c} | |
| 827 | + | {$define LUA_CORE} | |
| 828 | + | ||
| 829 | + | //#include 'lua.h' | |
| 830 | + | ||
| 831 | + | //#include 'llimits.h' | |
| 832 | + | //#include 'lmem.h' | |
| 833 | + | //#include 'lstate.h' | |
| 834 | + | //#include 'lzio.h' | |
| 835 | + | ||
| 836 | + | function luaZ_fill(z : PTZIO):integer; | |
| 837 | + | var | |
| 838 | + | L : Plua_State; | |
| 839 | + | buff : Pchar; | |
| 840 | + | size : size_t; | |
| 841 | + | begin | |
| 842 | + | *L := z.L; | |
| 843 | + | lua_unlock(L); | |
| 844 | + | buff := z.reader(L, z.data, @size); | |
| 845 | + | lua_lock(L); | |
| 846 | + | if (buff = nil ) or ( size = 0) then exit(EOZ); | |
| 847 | + | z.n := size - 1; | |
| 848 | + | z.p := buff; | |
| 849 | + | exit(char2int(PosInc^((z.p)))); | |
| 850 | + | end; | |
| 851 | + | ||
| 852 | + | function luaZ_lookahead(z : PTZIO):integer; | |
| 853 | + | begin | |
| 854 | + | if (z.n = 0) then begin | |
| 855 | + | if (luaZ_fill(z) = EOZ) | |
| 856 | + | exit(EOZ); | |
| 857 | + | else begin | |
| 858 | + | PosInc(z.n); (* luaZ_fill removed first byte; put back it *) | |
| 859 | + | z-PosDec(>p); | |
| 860 | + | end; | |
| 861 | + | end; | |
| 862 | + | exit(char2int(z^.p)); | |
| 863 | + | end; | |
| 864 | + | ||
| 865 | + | procedure luaZ_init(L : Plua_State; z : PTZIO; reader : lua_Reader; data : Ppointer); | |
| 866 | + | begin | |
| 867 | + | z.L := L; | |
| 868 | + | z.reader := reader; | |
| 869 | + | z.data := data; | |
| 870 | + | z.n := 0; | |
| 871 | + | z.p := nil; | |
| 872 | + | end; | |
| 873 | + | ||
| 874 | + | (* --------------------------------------------------------------- read --- *) | |
| 875 | + | ||
| 876 | + | function luaZ_read(z : PTZIO; b : Ppointer; n : size_t):size_t; | |
| 877 | + | var | |
| 878 | + | m : size_t; | |
| 879 | + | begin | |
| 880 | + | while (n) do begin | |
| 881 | + | if (luaZ_lookahead(z) = EOZ) | |
| 882 | + | exit(n); (* exit(number of missing bytes *) | |
| 883 | + | m = (n <= z.n) ? n : z.n); (* min. between n and z.n *) | |
| 884 | + | Move(b, z.p, m); | |
| 885 | + | z.n -= m; | |
| 886 | + | z.p += m; | |
| 887 | + | b := (char *)b + m; | |
| 888 | + | n -= m; | |
| 889 | + | end; | |
| 890 | + | exit(0); | |
| 891 | + | end; | |
| 892 | + | ||
| 893 | + | (* ------------------------------------------------------------------------ *) | |
| 894 | + | ||
| 895 | + | function luaZ_openspace(L : Plua_State; buff : PTMbuffer; n : size_t):Pchar; | |
| 896 | + | begin | |
| 897 | + | if (n > buff.buffsize) then begin | |
| 898 | + | if (n < LUA_MINBUFFER) then n := LUA_MINBUFFER; | |
| 899 | + | luaZ_resizebuffer(L, buff, n); | |
| 900 | + | end; | |
| 901 | + | exit(buff.buffer); | |
| 902 | + | end; | |
| 903 | + | ``` | |
| 904 | + | ||
| 905 | + | # Manual conversion of C headers to Pascal | |
| 906 | + | ||
| 907 | + | In this case one should utilize ones knowledge of C and Pascal together to make the headers. One can learn how to do the conversion by looking at examples. | |
| 908 | + | ||
| 909 | + | ## Converting constants | |
| 910 | + | ||
| 911 | + | C: | |
| 912 | + | ||
| 913 | + | ``` c | |
| 914 | + | #define SOME_CONSTANT 5 | |
| 915 | + | #define ANOTHER_CONSTANT 6 | |
| 916 | + | ``` | |
| 917 | + | ||
| 918 | + | Pascal: | |
| 919 | + | ||
| 920 | + | ``` pascal | |
| 921 | + | const | |
| 922 | + | SOME_CONSTANT = 5; | |
| 923 | + | ANOTHER_CONSTANT = 6; | |
| 924 | + | ``` | |
| 925 | + | ||
| 926 | + | ## Converting exported routines | |
| 927 | + | ||
| 928 | + | The most basic routines can be translated like this: | |
| 929 | + | ||
| 930 | + | C: | |
| 931 | + | ||
| 932 | + | ``` c | |
| 933 | + | int somekind_of_method(int* param_first, int* param_second) | |
| 934 | + | void another_method(char*) | |
| 935 | + | ``` | |
| 936 | + | ||
| 937 | + | Pascal: | |
| 938 | + | ||
| 939 | + | ``` pascal | |
| 940 | + | uses ctypes; | |
| 941 | + | ||
| 942 | + | const | |
| 943 | + | MyLib = 'mylib'; | |
| 944 | + | ||
| 945 | + | function somekind_of_method(param_first, param_second: pcint): cint; external MyLib; | |
| 946 | + | procedure another_method_renamed(param1: PChar); external MyLib name 'another_method'; | |
| 947 | + | ``` | |
| 948 | + | ||
| 949 | + | Note that one should use the ctypes unit together with the types that it declares to convert C headers instead of trying to use the basic Pascal types directly. This ensures that the bindings are correct in all platforms, as the C types might have different sizes on different architectures and operating systems. | |
| 950 | + | ||
| 951 | + | Also, note that pointers to basic C types can be converted to types declared in ctypes, for example, int* is a pointer to a C int, so we can use the type pcint (pointer to a c integer). The C type int can be converted to cint and in Pascal we can shorten the declaration of parameters which are declared one after as folows: "param_first, param_second: pcint". | |
| 952 | + | ||
| 953 | + | Finally, note that if the return value is **void**, then we should use a procedure instead of function. | |
| 954 | + | ||
| 955 | + | See also the [[http://www.freepascal.org/docs-html/ref/refse85.html External Functions]] section of the reference guide. | |
| 956 | + | ||
| 957 | + | # Publish your bindings on Lazarus-CCR or Free Pascal | |
| 958 | + | ||
| 959 | + | You can submit your binding code to the Lazarus CCR repository (see e.g. [[Using the Lazarus-ccr SVN repository]]) so others can use (and maintain) it, too. If the bindings are useful for general, cross-platform work, they could be included in the FreePascal distribution as well. | |
| 960 | + | ||
| 961 | + | # See also | |
| 962 | + | ||
| 963 | + | - [1](http://gd.tuwien.ac.at/languages/pascal/fpc/docs-pdf/CinFreePascal.pdf) Short tutorial that describes the process of using C and C++ code in FreePascal, including writing of wrapper code | |
| 964 | + | - [[Common problems when converting C header files]] | |
| 965 | + | - [[Pascal for C users]] | |
| 966 | + | - [[Lazarus/FPC Libraries]] | |
| 967 | + | - [[SWIG]] | |
| 968 | + | ||
| 969 | + | --- | |
| 970 | + | *From the Free Pascal wiki, page "Creating bindings for C libraries/ru", as it stood on 2020-02-12 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
