diff options
author | Andreas Rumpf <andreas@andreas-laptop> | 2010-07-21 09:44:47 +0200 |
---|---|---|
committer | Andreas Rumpf <andreas@andreas-laptop> | 2010-07-21 09:44:47 +0200 |
commit | d10973adb00840631e5314ec902d502f15934801 (patch) | |
tree | a207854b0cf984815beb26bf2d71933ce566f6d7 | |
parent | c441cdb64ca5394f74faadf76563bcfafeda18f4 (diff) | |
download | Nim-d10973adb00840631e5314ec902d502f15934801.tar.gz |
c2nim tool added
-rwxr-xr-x | doc/manual.txt | 2 | ||||
-rwxr-xr-x | lib/impure/osinfo_posix.nim | 68 | ||||
-rwxr-xr-x | lib/impure/osinfo_win.nim | 399 | ||||
-rwxr-xr-x | lib/nimbase.h | 2 | ||||
-rwxr-xr-x | lib/pure/osproc.nim | 2 | ||||
-rwxr-xr-x | lib/pure/strutils.nim | 7 | ||||
-rwxr-xr-x | rod/c2nim/c2nim.nim | 74 | ||||
-rwxr-xr-x | rod/c2nim/clex.nim | 751 | ||||
-rwxr-xr-x | rod/c2nim/cparse.nim | 1469 | ||||
-rwxr-xr-x | rod/c2nim/cpp.nim | 231 | ||||
-rw-r--r-- | rod/c2nim/manual.txt | 235 | ||||
-rwxr-xr-x | rod/ccgstmts.nim | 15 | ||||
-rwxr-xr-x | rod/extccomp.nim | 7 | ||||
-rwxr-xr-x | rod/msgs.nim | 2 | ||||
-rwxr-xr-x | rod/pragmas.nim | 11 | ||||
-rwxr-xr-x | rod/rnimsyn.nim | 10 | ||||
-rwxr-xr-x | rod/scanner.nim | 2 | ||||
-rwxr-xr-x | rod/semgnrc.nim | 12 | ||||
-rwxr-xr-x | rod/sigmatch.nim | 9 | ||||
-rwxr-xr-x | rod/syntaxes.nim | 49 | ||||
-rwxr-xr-x | rod/types.nim | 2 | ||||
-rw-r--r-- | tests/accept/compile/tenum3.nim | 16 | ||||
-rwxr-xr-x | tests/accept/run/spec.csv | 1 | ||||
-rw-r--r-- | tests/accept/run/tarray3.nim | 7 | ||||
-rwxr-xr-x | todo.txt | 155 | ||||
-rwxr-xr-x | tools/build.tmpl | 2 | ||||
-rwxr-xr-x | web/news.txt | 4 | ||||
-rwxr-xr-x | web/question.txt | 20 |
28 files changed, 3498 insertions, 66 deletions
diff --git a/doc/manual.txt b/doc/manual.txt index 490901b64..7fce8ac85 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -155,7 +155,7 @@ underscores ``__`` are not allowed:: letter ::= 'A'..'Z' | 'a'..'z' | '\x80'..'\xff' digit ::= '0'..'9' - IDENTIFIER ::= letter ( ['_'] letter | digit )* + IDENTIFIER ::= letter ( ['_'] (letter | digit) )* The following `keywords`:idx: are reserved and cannot be used as identifiers: diff --git a/lib/impure/osinfo_posix.nim b/lib/impure/osinfo_posix.nim new file mode 100755 index 000000000..4fde82012 --- /dev/null +++ b/lib/impure/osinfo_posix.nim @@ -0,0 +1,68 @@ +import posix, strutils, os + +type + Tstatfs {.importc: "struct statfs64", + header: "<sys/statfs.h>", final, pure.} = object + f_type: int + f_bsize: int + f_blocks: int + f_bfree: int + f_bavail: int + f_files: int + f_ffree: int + f_fsid: int + f_namelen: int + +proc statfs(path: string, buf: var Tstatfs): int {. + importc, header: "<sys/vfs.h>".} + + +proc getSystemVersion*(): string = + result = "" + + var unix_info: TUtsname + + if uname(unix_info) != 0: + os.OSError() + + if $unix_info.sysname == "Linux": + # Linux + result.add("Linux ") + + result.add($unix_info.release & " ") + result.add($unix_info.machine) + elif $unix_info.sysname == "Darwin": + # Darwin + result.add("Mac OS X ") + if "10" in $unix_info.release: + result.add("v10.6 Snow Leopard") + elif "9" in $unix_info.release: + result.add("v10.5 Leopard") + elif "8" in $unix_info.release: + result.add("v10.4 Tiger") + elif "7" in $unix_info.release: + result.add("v10.3 Panther") + elif "6" in $unix_info.release: + result.add("v10.2 Jaguar") + elif "1.4" in $unix_info.release: + result.add("v10.1 Puma") + elif "1.3" in $unix_info.release: + result.add("v10.0 Cheetah") + elif "0" in $unix_info.release: + result.add("Server 1.0 Hera") + else: + result.add($unix_info.sysname & " " & $unix_info.release) + + +when false: + var unix_info: TUtsname + echo(uname(unix_info)) + echo(unix_info.sysname) + echo("8" in $unix_info.release) + + echo(getSystemVersion()) + + var stfs: TStatfs + echo(statfs("sysinfo_posix.nim", stfs)) + echo(stfs.f_files) + diff --git a/lib/impure/osinfo_win.nim b/lib/impure/osinfo_win.nim new file mode 100755 index 000000000..86e437c92 --- /dev/null +++ b/lib/impure/osinfo_win.nim @@ -0,0 +1,399 @@ +# XXX clean up this mess! + +import winlean + +const + INVALID_HANDLE_VALUE = int(- 1) # GetStockObject + +type + TMEMORYSTATUSEX {.final, pure.} = object + dwLength: int32 + dwMemoryLoad: int32 + ullTotalPhys: int64 + ullAvailPhys: int64 + ullTotalPageFile: int64 + ullAvailPageFile: int64 + ullTotalVirtual: int64 + ullAvailVirtual: int64 + ullAvailExtendedVirtual: int64 + + SYSTEM_INFO* {.final, pure.} = object + wProcessorArchitecture*: int16 + wReserved*: int16 + dwPageSize*: int32 + lpMinimumApplicationAddress*: pointer + lpMaximumApplicationAddress*: pointer + dwActiveProcessorMask*: int32 + dwNumberOfProcessors*: int32 + dwProcessorType*: int32 + dwAllocationGranularity*: int32 + wProcessorLevel*: int16 + wProcessorRevision*: int16 + + LPSYSTEM_INFO* = ptr SYSTEM_INFO + TSYSTEMINFO* = SYSTEM_INFO + + TMemoryInfo* = object + MemoryLoad*: int ## occupied memory, in percent + TotalPhysMem*: int64 ## Total Physical memory, in bytes + AvailablePhysMem*: int64 ## Available physical memory, in bytes + TotalPageFile*: int64 ## The current committed memory limit + ## for the system or the current process, whichever is smaller, in bytes. + AvailablePageFile*: int64 ## The maximum amount of memory the current process can commit, in bytes. + TotalVirtualMem*: int64 ## Total virtual memory, in bytes + AvailableVirtualMem*: int64 ## Available virtual memory, in bytes + + TOSVERSIONINFOEX {.final, pure.} = object + dwOSVersionInfoSize: int32 + dwMajorVersion: int32 + dwMinorVersion: int32 + dwBuildNumber: int32 + dwPlatformId: int32 + szCSDVersion: array[0..127, char] + wServicePackMajor: int16 + wServicePackMinor: int16 + wSuiteMask: int16 + wProductType: int8 + wReserved: char + + TVersionInfo* = object + majorVersion*: int + minorVersion*: int + buildNumber*: int + platformID*: int + SPVersion*: string ## Full Service pack version string + SPMajor*: int ## Major service pack version + SPMinor*: int ## Minor service pack version + SuiteMask*: int + ProductType*: int + + TPartitionInfo* = tuple[FreeSpace, TotalSpace: filetime] + +const + # SuiteMask - VersionInfo.SuiteMask + VER_SUITE_BACKOFFICE* = 0x00000004 + VER_SUITE_BLADE* = 0x00000400 + VER_SUITE_COMPUTE_SERVER* = 0x00004000 + VER_SUITE_DATACENTER* = 0x00000080 + VER_SUITE_ENTERPRISE* = 0x00000002 + VER_SUITE_EMBEDDEDNT* = 0x00000040 + VER_SUITE_PERSONAL* = 0x00000200 + VER_SUITE_SINGLEUSERTS* = 0x00000100 + VER_SUITE_SMALLBUSINESS* = 0x00000001 + VER_SUITE_SMALLBUSINESS_RESTRICTED* = 0x00000020 + VER_SUITE_STORAGE_SERVER* = 0x00002000 + VER_SUITE_TERMINAL* = 0x00000010 + VER_SUITE_WH_SERVER* = 0x00008000 + + # ProductType - VersionInfo.ProductType + VER_NT_DOMAIN_CONTROLLER* = 0x0000002 + VER_NT_SERVER* = 0x0000003 + VER_NT_WORKSTATION* = 0x0000001 + + VER_PLATFORM_WIN32_NT* = 2 + + # Product Info - getProductInfo() - (Remove unused ones ?) + PRODUCT_BUSINESS* = 0x00000006 + PRODUCT_BUSINESS_N* = 0x00000010 + PRODUCT_CLUSTER_SERVER* = 0x00000012 + PRODUCT_DATACENTER_SERVER* = 0x00000008 + PRODUCT_DATACENTER_SERVER_CORE* = 0x0000000C + PRODUCT_DATACENTER_SERVER_CORE_V* = 0x00000027 + PRODUCT_DATACENTER_SERVER_V* = 0x00000025 + PRODUCT_ENTERPRISE* = 0x00000004 + PRODUCT_ENTERPRISE_E* = 0x00000046 + PRODUCT_ENTERPRISE_N* = 0x0000001B + PRODUCT_ENTERPRISE_SERVER* = 0x0000000A + PRODUCT_ENTERPRISE_SERVER_CORE* = 0x0000000E + PRODUCT_ENTERPRISE_SERVER_CORE_V* = 0x00000029 + PRODUCT_ENTERPRISE_SERVER_IA64* = 0x0000000F + PRODUCT_ENTERPRISE_SERVER_V* = 0x00000026 + PRODUCT_HOME_BASIC* = 0x00000002 + PRODUCT_HOME_BASIC_E* = 0x00000043 + PRODUCT_HOME_BASIC_N* = 0x00000005 + PRODUCT_HOME_PREMIUM* = 0x00000003 + PRODUCT_HOME_PREMIUM_E* = 0x00000044 + PRODUCT_HOME_PREMIUM_N* = 0x0000001A + PRODUCT_HYPERV* = 0x0000002A + PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT* = 0x0000001E + PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING* = 0x00000020 + PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY* = 0x0000001F + PRODUCT_PROFESSIONAL* = 0x00000030 + PRODUCT_PROFESSIONAL_E* = 0x00000045 + PRODUCT_PROFESSIONAL_N* = 0x00000031 + PRODUCT_SERVER_FOR_SMALLBUSINESS* = 0x00000018 + PRODUCT_SERVER_FOR_SMALLBUSINESS_V* = 0x00000023 + PRODUCT_SERVER_FOUNDATION* = 0x00000021 + PRODUCT_SMALLBUSINESS_SERVER* = 0x00000009 + PRODUCT_STANDARD_SERVER* = 0x00000007 + PRODUCT_STANDARD_SERVER_CORE * = 0x0000000D + PRODUCT_STANDARD_SERVER_CORE_V* = 0x00000028 + PRODUCT_STANDARD_SERVER_V* = 0x00000024 + PRODUCT_STARTER* = 0x0000000B + PRODUCT_STARTER_E* = 0x00000042 + PRODUCT_STARTER_N* = 0x0000002F + PRODUCT_STORAGE_ENTERPRISE_SERVER* = 0x00000017 + PRODUCT_STORAGE_EXPRESS_SERVER* = 0x00000014 + PRODUCT_STORAGE_STANDARD_SERVER* = 0x00000015 + PRODUCT_STORAGE_WORKGROUP_SERVER* = 0x00000016 + PRODUCT_UNDEFINED* = 0x00000000 + PRODUCT_ULTIMATE* = 0x00000001 + PRODUCT_ULTIMATE_E* = 0x00000047 + PRODUCT_ULTIMATE_N* = 0x0000001C + PRODUCT_WEB_SERVER* = 0x00000011 + PRODUCT_WEB_SERVER_CORE* = 0x0000001D + + PROCESSOR_ARCHITECTURE_AMD64* = 9 ## x64 (AMD or Intel) + PROCESSOR_ARCHITECTURE_IA64* = 6 ## Intel Itanium Processor Family (IPF) + PROCESSOR_ARCHITECTURE_INTEL* = 0 ## x86 + PROCESSOR_ARCHITECTURE_UNKNOWN* = 0xffff ## Unknown architecture. + + # GetSystemMetrics + SM_SERVERR2 = 89 + +proc GlobalMemoryStatusEx*(lpBuffer: var TMEMORYSTATUSEX){.stdcall, dynlib: "kernel32", + importc: "GlobalMemoryStatusEx".} + +proc getMemoryInfo*(): TMemoryInfo = + ## Retrieves memory info + var statex: TMEMORYSTATUSEX + statex.dwLength = sizeof(statex) + + GlobalMemoryStatusEx(statex) + result.MemoryLoad = statex.dwMemoryLoad + result.TotalPhysMem = statex.ullTotalPhys + result.AvailablePhysMem = statex.ullAvailPhys + result.TotalPageFile = statex.ullTotalPageFile + result.AvailablePageFile = statex.ullAvailPageFile + result.TotalVirtualMem = statex.ullTotalVirtual + result.AvailableVirtualMem = statex.ullAvailExtendedVirtual + +proc GetVersionEx*(lpVersionInformation: var TOSVERSIONINFOEX): WINBOOL{.stdcall, + dynlib: "kernel32", importc: "GetVersionExA".} + +proc GetProcAddress*(hModule: int, lpProcName: cstring): pointer{.stdcall, + dynlib: "kernel32", importc: "GetProcAddress".} + +proc GetModuleHandleA*(lpModuleName: cstring): int{.stdcall, + dynlib: "kernel32", importc.} + +proc getVersionInfo*(): TVersionInfo = + ## Retrieves operating system info + var osvi: TOSVERSIONINFOEX + osvi.dwOSVersionInfoSize = sizeof(osvi) + discard GetVersionEx(osvi) + result.majorVersion = osvi.dwMajorVersion + result.minorVersion = osvi.dwMinorVersion + result.buildNumber = osvi.dwBuildNumber + result.platformID = osvi.dwPlatformId + result.SPVersion = $osvi.szCSDVersion + result.SPMajor = osvi.wServicePackMajor + result.SPMinor = osvi.wServicePackMinor + result.SuiteMask = osvi.wSuiteMask + result.ProductType = osvi.wProductType + +proc getProductInfo*(majorVersion, minorVersion, SPMajorVersion, + SPMinorVersion: int): int = + ## Retrieves Windows' ProductInfo, this function only works in Vista and 7 + + var pGPI = cast[proc (dwOSMajorVersion, dwOSMinorVersion, + dwSpMajorVersion, dwSpMinorVersion: int32, outValue: Pint32)](GetProcAddress( + GetModuleHandleA("kernel32.dll"), "GetProductInfo")) + + if pGPI != nil: + var dwType: int32 + pGPI(int32(majorVersion), int32(minorVersion), int32(SPMajorVersion), int32(SPMinorVersion), addr(dwType)) + result = int(dwType) + else: + return PRODUCT_UNDEFINED + +proc GetSystemInfo*(lpSystemInfo: LPSYSTEM_INFO){.stdcall, dynlib: "kernel32", + importc: "GetSystemInfo".} + +proc getSystemInfo*(): TSYSTEM_INFO = + ## Returns the SystemInfo + + # Use GetNativeSystemInfo if it's available + var pGNSI = cast[proc (lpSystemInfo: LPSYSTEM_INFO)](GetProcAddress( + GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo")) + + var systemi: TSYSTEM_INFO + if pGNSI != nil: + pGNSI(addr(systemi)) + else: + GetSystemInfo(addr(systemi)) + + return systemi + +proc GetSystemMetrics*(nIndex: int32): int32{.stdcall, dynlib: "user32", + importc: "GetSystemMetrics".} + +proc `$`*(osvi: TVersionInfo): string = + ## Turns a VersionInfo object, into a string + + if osvi.platformID == VER_PLATFORM_WIN32_NT and osvi.majorVersion > 4: + result = "Microsoft " + + var si = getSystemInfo() + # Test for the specific product + if osvi.majorVersion == 6: + if osvi.minorVersion == 0: + if osvi.ProductType == VER_NT_WORKSTATION: + result.add("Windows Vista ") + else: result.add("Windows Server 2008 ") + elif osvi.minorVersion == 1: + if osvi.ProductType == VER_NT_WORKSTATION: + result.add("Windows 7 ") + else: result.add("Windows Server 2008 R2 ") + + var dwType = getProductInfo(osvi.majorVersion, osvi.minorVersion, 0, 0) + case dwType + of PRODUCT_ULTIMATE: + result.add("Ultimate Edition") + of PRODUCT_PROFESSIONAL: + result.add("Professional") + of PRODUCT_HOME_PREMIUM: + result.add("Home Premium Edition") + of PRODUCT_HOME_BASIC: + result.add("Home Basic Edition") + of PRODUCT_ENTERPRISE: + result.add("Enterprise Edition") + of PRODUCT_BUSINESS: + result.add("Business Edition") + of PRODUCT_STARTER: + result.add("Starter Edition") + of PRODUCT_CLUSTER_SERVER: + result.add("Cluster Server Edition") + of PRODUCT_DATACENTER_SERVER: + result.add("Datacenter Edition") + of PRODUCT_DATACENTER_SERVER_CORE: + result.add("Datacenter Edition (core installation)") + of PRODUCT_ENTERPRISE_SERVER: + result.add("Enterprise Edition") + of PRODUCT_ENTERPRISE_SERVER_CORE: + result.add("Enterprise Edition (core installation)") + of PRODUCT_ENTERPRISE_SERVER_IA64: + result.add("Enterprise Edition for Itanium-based Systems") + of PRODUCT_SMALLBUSINESS_SERVER: + result.add("Small Business Server") + of PRODUCT_STANDARD_SERVER: + result.add("Standard Edition") + of PRODUCT_STANDARD_SERVER_CORE: + result.add("Standard Edition (core installation)") + of PRODUCT_WEB_SERVER: + result.add("Web Server Edition") + else: + nil + # End of Windows 6.* + + if osvi.majorVersion == 5 and osvi.minorVersion == 2: + if GetSystemMetrics(SM_SERVERR2) != 0: + result.add("Windows Server 2003 R2, ") + elif (osvi.SuiteMask and VER_SUITE_PERSONAL) != 0: # Not sure if this will work + result.add("Windows Storage Server 2003") + elif (osvi.SuiteMask and VER_SUITE_WH_SERVER) != 0: + result.add("Windows Home Server") + elif osvi.ProductType == VER_NT_WORKSTATION and + si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64: + result.add("Windows XP Professional x64 Edition") + else: + result.add("Windows Server 2003, ") + + # Test for the specific product + if osvi.ProductType != VER_NT_WORKSTATION: + if ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_IA64: + if (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0: + result.add("Datacenter Edition for Itanium-based Systems") + elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0: + result.add("Enterprise Edition for Itanium-based Systems") + elif ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_AMD64: + if (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0: + result.add("Datacenter x64 Edition") + elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0: + result.add("Enterprise x64 Edition") + else: + result.add("Standard x64 Edition") + else: + if (osvi.SuiteMask and VER_SUITE_COMPUTE_SERVER) != 0: + result.add("Compute Cluster Edition") + elif (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0: + result.add("Datacenter Edition") + elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0: + result.add("Enterprise Edition") + elif (osvi.SuiteMask and VER_SUITE_BLADE) != 0: + result.add("Web Edition") + else: + result.add("Standard Edition") + # End of 5.2 + + if osvi.majorVersion == 5 and osvi.minorVersion == 1: + result.add("Windows XP ") + if (osvi.SuiteMask and VER_SUITE_PERSONAL) != 0: + result.add("Home Edition") + else: + result.add("Professional") + # End of 5.1 + + if osvi.majorVersion == 5 and osvi.minorVersion == 0: + result.add("Windows 2000 ") + if osvi.ProductType == VER_NT_WORKSTATION: + result.add("Professional") + else: + if (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0: + result.add("Datacenter Server") + elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0: + result.add("Advanced Server") + else: + result.add("Server") + # End of 5.0 + + # Include service pack (if any) and build number. + if len(osvi.SPVersion) > 0: + result.add(" ") + result.add(osvi.SPVersion) + + result.add(" (build " & $osvi.buildNumber & ")") + + if osvi.majorVersion >= 6: + if ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_AMD64: + result.add(", 64-bit") + elif ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_INTEL: + result.add(", 32-bit") + + else: + # Windows 98 etc... + result = "Unknown version of windows[Kernel version <= 4]" + + +proc getFileSize*(file: string): biggestInt = + var fileData: TWIN32_FIND_DATA + var hFile = FindFirstFileA(file, fileData) + + if hFile == INVALID_HANDLE_VALUE: + raise newException(EIO, $GetLastError()) + + return fileData.nFileSizeLow + +proc GetDiskFreeSpaceEx*(lpDirectoryName: cstring, lpFreeBytesAvailableToCaller, + lpTotalNumberOfBytes, + lpTotalNumberOfFreeBytes: var filetime): WINBOOL{. + stdcall, dynlib: "kernel32", importc: "GetDiskFreeSpaceExA".} + +proc getPartitionInfo*(partition: string): TPartitionInfo = + ## Retrieves partition info, for example ``partition`` may be ``"C:\"`` + var FreeBytes, TotalBytes, TotalFreeBytes: filetime + var res = GetDiskFreeSpaceEx(r"C:\", FreeBytes, TotalBytes, + TotalFreeBytes) + return (FreeBytes, TotalBytes) + +when isMainModule: + var r = getMemoryInfo() + echo("Memory load: ", r.MemoryLoad, "%") + + var osvi = getVersionInfo() + + echo($osvi) + + echo(getFileSize(r"osinfo_win.nim") div 1024 div 1024) + + echo(rdFileTime(getPartitionInfo(r"C:\")[0])) diff --git a/lib/nimbase.h b/lib/nimbase.h index 4c549120c..8e80b8261 100755 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -80,7 +80,7 @@ __TINYC__ /* ---------------- casting without correct aliasing rules ----------- */ -#if defined(__GNUCC__) +#if defined(__GNUC__) # define NIM_CAST(type, ptr) (((union{type __x__;}*)(ptr))->__x__) #else # define NIM_CAST(type, ptr) ((type)(ptr)) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index c9bd729d9..764a1f896 100755 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -117,7 +117,7 @@ when defined(macosx) or defined(bsd): proc countProcessors*(): int = ## returns the numer of the processors/cores the machine has. - ## Returns 0 if it cannot be determined. + ## Returns 0 if it cannot be detected. when defined(windows): var x = getenv("NUMBER_OF_PROCESSORS") if x.len > 0: result = parseInt(x) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 961c875ac..38f6ffa9a 100755 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -391,10 +391,13 @@ proc endsWith(s, suffix: string): bool = var i = 0 j = len(s) - len(suffix) - while true: - if suffix[i] == '\0': return true + while i+j <% s.len: if s[i+j] != suffix[i]: return false inc(i) + if suffix[i] == '\0': return true + +# 012345 +# 345 when false: proc abbrev(s: string, possibilities: openarray[string]): int = diff --git a/rod/c2nim/c2nim.nim b/rod/c2nim/c2nim.nim new file mode 100755 index 000000000..52d16ce05 --- /dev/null +++ b/rod/c2nim/c2nim.nim @@ -0,0 +1,74 @@ +# +# +# c2nim - C to Nimrod source converter +# (c) Copyright 2010 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import + strutils, os, times, parseopt, llstream, ast, rnimsyn, options, msgs, + clex, cparse + +const + Version = "0.8.10" + Usage = """ +c2nim - C to Nimrod source converter + (c) 2010 Andreas Rumpf +Usage: c2nim [options] inputfile [options] +Options: + -o, --out:FILE set output filename + --dynlib:SYMBOL import from dynlib: SYMBOL will be used for the import + --header:HEADER_FILE import from a HEADER_FILE (discouraged!) + --cdecl annotate procs with ``{.cdecl.}`` + --stdcall annotate procs with ``{.stdcall.}`` + --ref convert typ* to ref typ (default: ptr typ) + --prefix:PREFIX strip prefix for the generated Nimrod identifiers + (multiple --prefix options are supported) + --suffix:SUFFIX strip suffix for the generated Nimrod identifiers + (multiple --suffix options are supported) + --skip:IDENT skip IDENT in the input file + -v, --version write c2nim's version + -h, --help show this help +""" + +proc main(infile, outfile: string, options: PParserOptions) = + var start = getTime() + var stream = LLStreamOpen(infile, fmRead) + if stream == nil: rawMessage(errCannotOpenFile, infile) + var p: TParser + openParser(p, infile, stream, options) + var module = parseUnit(p) + closeParser(p) + renderModule(module, outfile) + rawMessage(hintSuccessX, [$gLinesCompiled, $(getTime() - start)]) + +var + infile = "" + outfile = "" + parserOptions = newParserOptions() +for kind, key, val in getopt(): + case kind + of cmdArgument: infile = key + of cmdLongOption, cmdShortOption: + case key.toLower + of "help", "h": + stdout.write(Usage) + quit(0) + of "version", "v": + stdout.write(Version & "\n") + quit(0) + of "o", "out": outfile = key + else: + if not parserOptions.setOption(key, val): + stdout.write("[Error] unknown option: " & key) + of cmdEnd: assert(false) +if infile.len == 0: + # no filename has been given, so we show the help: + stdout.write(Usage) +else: + if outfile.len == 0: + outfile = changeFileExt(infile, "nim") + infile = addFileExt(infile, "h") + main(infile, outfile, parserOptions) diff --git a/rod/c2nim/clex.nim b/rod/c2nim/clex.nim new file mode 100755 index 000000000..ecf337dd9 --- /dev/null +++ b/rod/c2nim/clex.nim @@ -0,0 +1,751 @@ +# +# +# c2nim - C to Nimrod source converter +# (c) Copyright 2010 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# This module implements an Ansi C scanner. This is an adaption from +# the scanner module. Keywords are not handled here, but in the parser to make +# it more flexible. + + +import + options, msgs, strutils, platform, lexbase, llstream + +const + MaxLineLength* = 80 # lines longer than this lead to a warning + numChars*: TCharSet = {'0'..'9', 'a'..'z', 'A'..'Z'} + SymChars*: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'} + SymStartChars*: TCharSet = {'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF'} + +type + TTokKind* = enum + pxInvalid, pxEof, + pxStarComment, # /* */ comment + pxLineComment, # // comment + pxDirective, # #define, etc. + pxDirectiveParLe, # #define m( with parle (yes, C is that ugly!) + pxDirConc, # ## + pxNewLine, # newline: end of directive + pxAmp, # & + pxAmpAmp, # && + pxAmpAsgn, # &= + pxAmpAmpAsgn, # &&= + pxBar, # | + pxBarBar, # || + pxBarAsgn, # |= + pxBarBarAsgn, # ||= + pxNot, # ! + pxPlusPlus, # ++ + pxMinusMinus, # -- + pxPlus, # + + pxPlusAsgn, # += + pxMinus, # - + pxMinusAsgn, # -= + pxMod, # % + pxModAsgn, # %= + pxSlash, # / + pxSlashAsgn, # /= + pxStar, # * + pxStarAsgn, # *= + pxHat, # ^ + pxHatAsgn, # ^= + pxAsgn, # = + pxEquals, # == + pxDot, # . + pxDotDotDot, # ... + pxLe, # <= + pxLt, # < + pxGe, # >= + pxGt, # > + pxNeq, # != + pxConditional, # ? + pxShl, # << + pxShlAsgn, # <<= + pxShr, # >> + pxShrAsgn, # >>= + pxTilde, # ~ + pxTildeAsgn, # ~= + pxArrow, # -> + pxScope, # :: + + pxStrLit, + pxCharLit, + pxSymbol, # a symbol + pxIntLit, + pxInt64Lit, # long constant like 0x70fffffff or out of int range + pxFloatLit, + pxParLe, pxParRi, + pxBracketLe, pxBracketRi, + pxComma, pxSemiColon, pxColon, + pxCurlyLe, pxCurlyRi + TTokKinds* = set[TTokKind] + +type + TNumericalBase* = enum base10, base2, base8, base16 + TToken* = object + xkind*: TTokKind # the type of the token + s*: string # parsed symbol, char or string literal + iNumber*: BiggestInt # the parsed integer literal + fNumber*: BiggestFloat # the parsed floating point literal + base*: TNumericalBase # the numerical base; only valid for int + # or float literals + next*: ref TToken # for C we need arbitrary look-ahead :-( + + TLexer* = object of TBaseLexer + filename*: string + inDirective: bool + + +proc getTok*(L: var TLexer, tok: var TToken) +proc PrintTok*(tok: TToken) +proc `$`*(tok: TToken): string +# implementation + +var + gLinesCompiled*: int + +proc fillToken(L: var TToken) = + L.xkind = pxInvalid + L.iNumber = 0 + L.s = "" + L.fNumber = 0.0 + L.base = base10 + +proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) = + openBaseLexer(lex, inputstream) + lex.filename = filename + +proc closeLexer*(lex: var TLexer) = + inc(gLinesCompiled, lex.LineNumber) + closeBaseLexer(lex) + +proc getColumn*(L: TLexer): int = + result = getColNumber(L, L.bufPos) + +proc getLineInfo*(L: TLexer): TLineInfo = + result = newLineInfo(L.filename, L.linenumber, getColNumber(L, L.bufpos)) + +proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") = + msgs.liMessage(getLineInfo(L), msg, arg) + +proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = + var info = newLineInfo(L.filename, L.linenumber, pos - L.lineStart) + msgs.liMessage(info, msg, arg) + +proc TokKindToStr*(k: TTokKind): string = + case k + of pxEof: result = "[EOF]" + of pxInvalid: result = "[invalid]" + of pxStarComment, pxLineComment: result = "[comment]" + of pxStrLit: result = "[string literal]" + of pxCharLit: result = "[char literal]" + + of pxDirective, pxDirectiveParLe: result = "#" # #define, etc. + of pxDirConc: result = "##" + of pxNewLine: result = "[NewLine]" + of pxAmp: result = "&" # & + of pxAmpAmp: result = "&&" # && + of pxAmpAsgn: result = "&=" # &= + of pxAmpAmpAsgn: result = "&&=" # &&= + of pxBar: result = "|" # | + of pxBarBar: result = "||" # || + of pxBarAsgn: result = "|=" # |= + of pxBarBarAsgn: result = "||=" # ||= + of pxNot: result = "!" # ! + of pxPlusPlus: result = "++" # ++ + of pxMinusMinus: result = "--" # -- + of pxPlus: result = "+" # + + of pxPlusAsgn: result = "+=" # += + of pxMinus: result = "-" # - + of pxMinusAsgn: result = "-=" # -= + of pxMod: result = "%" # % + of pxModAsgn: result = "%=" # %= + of pxSlash: result = "/" # / + of pxSlashAsgn: result = "/=" # /= + of pxStar: result = "*" # * + of pxStarAsgn: result = "*=" # *= + of pxHat: result = "^" # ^ + of pxHatAsgn: result = "^=" # ^= + of pxAsgn: result = "=" # = + of pxEquals: result = "==" # == + of pxDot: result = "." # . + of pxDotDotDot: result = "..." # ... + of pxLe: result = "<=" # <= + of pxLt: result = "<" # < + of pxGe: result = ">=" # >= + of pxGt: result = ">" # > + of pxNeq: result = "!=" # != + of pxConditional: result = "?" + of pxShl: result = "<<" + of pxShlAsgn: result = "<<=" + of pxShr: result = ">>" + of pxShrAsgn: result = ">>=" + of pxTilde: result = "~" + of pxTildeAsgn: result = "~=" + of pxArrow: result = "->" + of pxScope: result = "::" + + of pxSymbol: result = "[identifier]" + of pxIntLit, pxInt64Lit: result = "[integer literal]" + of pxFloatLit: result = "[floating point literal]" + of pxParLe: result = "(" + of pxParRi: result = ")" + of pxBracketLe: result = "[" + of pxBracketRi: result = "]" + of pxComma: result = "," + of pxSemiColon: result = ";" + of pxColon: result = ":" + of pxCurlyLe: result = "{" + of pxCurlyRi: result = "}" + +proc `$`(tok: TToken): string = + case tok.xkind + of pxSymbol, pxInvalid, pxStarComment, pxLineComment, pxStrLit: result = tok.s + of pxIntLit, pxInt64Lit: result = $tok.iNumber + of pxFloatLit: result = $tok.fNumber + else: result = TokKindToStr(tok.xkind) + +proc PrintTok(tok: TToken) = + writeln(stdout, $tok) + +proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) = + # matches ([chars]_)* + var pos = L.bufpos # use registers for pos, buf + var buf = L.buf + while true: + if buf[pos] in chars: + add(tok.s, buf[pos]) + Inc(pos) + else: + break + if buf[pos] == '_': + add(tok.s, '_') + Inc(pos) + L.bufPos = pos + +proc isFloatLiteral(s: string): bool = + for i in countup(0, len(s)-1): + if s[i] in {'.', 'e', 'E'}: + return true + +proc getNumber2(L: var TLexer, tok: var TToken) = + var pos = L.bufpos + 2 # skip 0b + tok.base = base2 + var xi: biggestInt = 0 + var bits = 0 + while true: + case L.buf[pos] + of 'A'..'Z', 'a'..'z': + # ignore type suffix: + inc(pos) + of '2'..'9', '.': + lexMessage(L, errInvalidNumber) + inc(pos) + of '_': + inc(pos) + of '0', '1': + xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) + inc(pos) + inc(bits) + else: break + tok.iNumber = xi + if (bits > 32): tok.xkind = pxInt64Lit + else: tok.xkind = pxIntLit + L.bufpos = pos + +proc getNumber8(L: var TLexer, tok: var TToken) = + var pos = L.bufpos + 2 # skip 0b + tok.base = base8 + var xi: biggestInt = 0 + var bits = 0 + while true: + case L.buf[pos] + of 'A'..'Z', 'a'..'z': + # ignore type suffix: + inc(pos) + of '8'..'9', '.': + lexMessage(L, errInvalidNumber) + inc(pos) + of '_': + inc(pos) + of '0'..'7': + xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0')) + inc(pos) + inc(bits) + else: break + tok.iNumber = xi + if (bits > 12): tok.xkind = pxInt64Lit + else: tok.xkind = pxIntLit + L.bufpos = pos + +proc getNumber16(L: var TLexer, tok: var TToken) = + var pos = L.bufpos + 2 # skip 0x + tok.base = base16 + var xi: biggestInt = 0 + var bits = 0 + while true: + case L.buf[pos] + of 'G'..'Z', 'g'..'z': + # ignore type suffix: + inc(pos) + of '_': inc(pos) + of '0'..'9': + xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) + inc(pos) + inc(bits, 4) + of 'a'..'f': + xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) + inc(pos) + inc(bits, 4) + of 'A'..'F': + xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) + inc(pos) + inc(bits, 4) + else: break + tok.iNumber = xi + if bits > 32: tok.xkind = pxInt64Lit + else: tok.xkind = pxIntLit + L.bufpos = pos + +proc getNumber(L: var TLexer, tok: var TToken) = + tok.base = base10 + matchUnderscoreChars(L, tok, {'0'..'9'}) + if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): + add(tok.s, '.') + inc(L.bufpos) + matchUnderscoreChars(L, tok, {'e', 'E', '+', '-', '0'..'9'}) + try: + if isFloatLiteral(tok.s): + tok.fnumber = parseFloat(tok.s) + tok.xkind = pxFloatLit + else: + tok.iNumber = ParseInt(tok.s) + if (tok.iNumber < low(int32)) or (tok.iNumber > high(int32)): + tok.xkind = pxInt64Lit + else: + tok.xkind = pxIntLit + except EInvalidValue: + lexMessage(L, errInvalidNumber, tok.s) + except EOverflow: + lexMessage(L, errNumberOutOfRange, tok.s) + # ignore type suffix: + while L.buf[L.bufpos] in {'A'..'Z', 'a'..'z'}: inc(L.bufpos) + +proc HandleCRLF(L: var TLexer, pos: int): int = + case L.buf[pos] + of CR: result = lexbase.HandleCR(L, pos) + of LF: result = lexbase.HandleLF(L, pos) + else: result = pos + +proc escape(L: var TLexer, tok: var TToken, allowEmpty=false) = + inc(L.bufpos) # skip \ + case L.buf[L.bufpos] + of 'b', 'B': + add(tok.s, '\b') + inc(L.bufpos) + of 't', 'T': + add(tok.s, '\t') + inc(L.bufpos) + of 'n', 'N': + add(tok.s, '\L') + inc(L.bufpos) + of 'f', 'F': + add(tok.s, '\f') + inc(L.bufpos) + of 'r', 'R': + add(tok.s, '\r') + inc(L.bufpos) + of '\'': + add(tok.s, '\'') + inc(L.bufpos) + of '"': + add(tok.s, '"') + inc(L.bufpos) + of '\\': + add(tok.s, '\b') + inc(L.bufpos) + of '0'..'7': + var xi = ord(L.buf[L.bufpos]) - ord('0') + inc(L.bufpos) + if L.buf[L.bufpos] in {'0'..'7'}: + xi = (xi shl 3) or (ord(L.buf[L.bufpos]) - ord('0')) + inc(L.bufpos) + if L.buf[L.bufpos] in {'0'..'7'}: + xi = (xi shl 3) or (ord(L.buf[L.bufpos]) - ord('0')) + inc(L.bufpos) + add(tok.s, chr(xi)) + elif not allowEmpty: + lexMessage(L, errInvalidCharacterConstant) + +proc getCharLit(L: var TLexer, tok: var TToken) = + inc(L.bufpos) # skip ' + if L.buf[L.bufpos] == '\\': + escape(L, tok) + else: + add(tok.s, L.buf[L.bufpos]) + inc(L.bufpos) + if L.buf[L.bufpos] == '\'': + inc(L.bufpos) + else: + lexMessage(L, errMissingFinalQuote) + tok.xkind = pxCharLit + +proc getString(L: var TLexer, tok: var TToken) = + var pos = L.bufPos + 1 # skip " + var buf = L.buf # put `buf` in a register + var line = L.linenumber # save linenumber for better error message + while true: + case buf[pos] + of '\"': + Inc(pos) + break + of CR: + pos = lexbase.HandleCR(L, pos) + buf = L.buf + of LF: + pos = lexbase.HandleLF(L, pos) + buf = L.buf + of lexbase.EndOfFile: + var line2 = L.linenumber + L.LineNumber = line + lexMessagePos(L, errClosingQuoteExpected, L.lineStart) + L.LineNumber = line2 + break + of '\\': + # we allow an empty \ for line concatenation, but we don't require it + # for line concatenation + L.bufpos = pos + escape(L, tok, allowEmpty=true) + pos = L.bufpos + else: + add(tok.s, buf[pos]) + Inc(pos) + L.bufpos = pos + tok.xkind = pxStrLit + +proc getSymbol(L: var TLexer, tok: var TToken) = + var pos = L.bufpos + var buf = L.buf + while true: + var c = buf[pos] + if c notin SymChars: break + add(tok.s, c) + Inc(pos) + L.bufpos = pos + tok.xkind = pxSymbol + +proc scanLineComment(L: var TLexer, tok: var TToken) = + var pos = L.bufpos + var buf = L.buf + # a comment ends if the next line does not start with the // on the same + # column after only whitespace + tok.xkind = pxLineComment + var col = getColNumber(L, pos) + while true: + inc(pos, 2) # skip // + add(tok.s, '#') + while not (buf[pos] in {CR, LF, lexbase.EndOfFile}): + add(tok.s, buf[pos]) + inc(pos) + pos = handleCRLF(L, pos) + buf = L.buf + var indent = 0 + while buf[pos] == ' ': + inc(pos) + inc(indent) + if (col == indent) and (buf[pos] == '/') and (buf[pos + 1] == '/'): + add(tok.s, "\n") + else: + break + L.bufpos = pos + +proc scanStarComment(L: var TLexer, tok: var TToken) = + var pos = L.bufpos + var buf = L.buf + tok.s = "#" + tok.xkind = pxStarComment + while true: + case buf[pos] + of CR, LF: + pos = HandleCRLF(L, pos) + buf = L.buf + add(tok.s, "\n#") + # skip annoying stars as line prefix: (eg. + # /* + # * ugly comment <-- this star + # */ + while buf[pos] in {' ', '\t'}: + add(tok.s, ' ') + inc(pos) + if buf[pos] == '*' and buf[pos+1] != '/': inc(pos) + of '*': + inc(pos) + if buf[pos] == '/': + inc(pos) + break + else: + add(tok.s, '*') + of lexbase.EndOfFile: + lexMessage(L, errTokenExpected, "*/") + else: + add(tok.s, buf[pos]) + inc(pos) + L.bufpos = pos + +proc skip(L: var TLexer, tok: var TToken) = + var pos = L.bufpos + var buf = L.buf + while true: + case buf[pos] + of '\\': + # Ignore \ line continuation characters when not inDirective + inc(pos) + if L.inDirective: + while buf[pos] in {' ', '\t'}: inc(pos) + if buf[pos] in {CR, LF}: + pos = HandleCRLF(L, pos) + buf = L.buf + of ' ', Tabulator: + Inc(pos) # newline is special: + of CR, LF: + pos = HandleCRLF(L, pos) + buf = L.buf + if L.inDirective: + tok.xkind = pxNewLine + L.inDirective = false + else: + break # EndOfFile also leaves the loop + L.bufpos = pos + +proc getDirective(L: var TLexer, tok: var TToken) = + var pos = L.bufpos + 1 + var buf = L.buf + while buf[pos] in {' ', '\t'}: inc(pos) + while buf[pos] in SymChars: + add(tok.s, buf[pos]) + inc(pos) + # a HACK: we need to distinguish + # #define x (...) + # from: + # #define x(...) + # + L.bufpos = pos + # look ahead: + while buf[pos] in {' ', '\t'}: inc(pos) + while buf[pos] in SymChars: inc(pos) + if buf[pos] == '(': tok.xkind = pxDirectiveParLe + else: tok.xkind = pxDirective + L.inDirective = true + +proc getTok(L: var TLexer, tok: var TToken) = + tok.xkind = pxInvalid + fillToken(tok) + skip(L, tok) + if tok.xkind == pxNewLine: return + var c = L.buf[L.bufpos] + if c in SymStartChars: + getSymbol(L, tok) + elif c == '0': + case L.buf[L.bufpos+1] + of 'x', 'X': getNumber16(L, tok) + of 'b', 'B': getNumber2(L, tok) + of '1'..'7': getNumber8(L, tok) + else: getNumber(L, tok) + elif c in {'1'..'9'}: + getNumber(L, tok) + else: + case c + of ';': + tok.xkind = pxSemicolon + Inc(L.bufpos) + of '/': + if L.buf[L.bufpos + 1] == '/': + scanLineComment(L, tok) + elif L.buf[L.bufpos+1] == '*': + inc(L.bufpos, 2) + scanStarComment(L, tok) + elif L.buf[L.bufpos+1] == '=': + inc(L.bufpos, 2) + tok.xkind = pxSlashAsgn + else: + tok.xkind = pxSlash + inc(L.bufpos) + of ',': + tok.xkind = pxComma + Inc(L.bufpos) + of '(': + Inc(L.bufpos) + tok.xkind = pxParLe + of '*': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + inc(L.bufpos) + tok.xkind = pxStarAsgn + else: + tok.xkind = pxStar + of ')': + Inc(L.bufpos) + tok.xkind = pxParRi + of '[': + Inc(L.bufpos) + tok.xkind = pxBracketLe + of ']': + Inc(L.bufpos) + tok.xkind = pxBracketRi + of '.': + inc(L.bufpos) + if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] == '.': + tok.xkind = pxDotDotDot + inc(L.bufpos, 2) + else: + tok.xkind = pxDot + of '{': + Inc(L.bufpos) + tok.xkind = pxCurlyLe + of '}': + Inc(L.bufpos) + tok.xkind = pxCurlyRi + of '+': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + tok.xkind = pxPlusAsgn + inc(L.bufpos) + elif L.buf[L.bufpos] == '+': + tok.xkind = pxPlusPlus + inc(L.bufpos) + else: + tok.xkind = pxPlus + of '-': + inc(L.bufpos) + case L.buf[L.bufpos] + of '>': + tok.xkind = pxArrow + inc(L.bufpos) + of '=': + tok.xkind = pxMinusAsgn + inc(L.bufpos) + of '-': + tok.xkind = pxMinusMinus + inc(L.bufpos) + else: + tok.xkind = pxMinus + of '?': + inc(L.bufpos) + tok.xkind = pxConditional + of ':': + inc(L.bufpos) + if L.buf[L.bufpos] == ':': + tok.xkind = pxScope + inc(L.bufpos) + else: + tok.xkind = pxColon + of '!': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + tok.xkind = pxNeq + inc(L.bufpos) + else: + tok.xkind = pxNot + of '<': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + inc(L.bufpos) + tok.xkind = pxLe + elif L.buf[L.bufpos] == '<': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + inc(L.bufpos) + tok.xkind = pxShlAsgn + else: + tok.xkind = pxShl + else: + tok.xkind = pxLt + of '>': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + inc(L.bufpos) + tok.xkind = pxGe + elif L.buf[L.bufpos] == '>': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + inc(L.bufpos) + tok.xkind = pxShrAsgn + else: + tok.xkind = pxShr + else: + tok.xkind = pxGt + of '=': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + tok.xkind = pxEquals + inc(L.bufpos) + else: + tok.xkind = pxAsgn + of '&': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + tok.xkind = pxAmpAsgn + inc(L.bufpos) + elif L.buf[L.bufpos] == '&': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + inc(L.bufpos) + tok.xkind = pxAmpAmpAsgn + else: + tok.xkind = pxAmpAmp + else: + tok.xkind = pxAmp + of '|': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + tok.xkind = pxBarAsgn + inc(L.bufpos) + elif L.buf[L.bufpos] == '|': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + inc(L.bufpos) + tok.xkind = pxBarBarAsgn + else: + tok.xkind = pxBarBar + else: + tok.xkind = pxBar + of '^': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + tok.xkind = pxHatAsgn + inc(L.bufpos) + else: + tok.xkind = pxHat + of '%': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + tok.xkind = pxModAsgn + inc(L.bufpos) + else: + tok.xkind = pxMod + of '~': + inc(L.bufpos) + if L.buf[L.bufpos] == '=': + tok.xkind = pxTildeAsgn + inc(L.bufpos) + else: + tok.xkind = pxTilde + of '#': + if L.buf[L.bufpos+1] == '#': + inc(L.bufpos, 2) + tok.xkind = pxDirConc + else: + getDirective(L, tok) + of '"': getString(L, tok) + of '\'': getCharLit(L, tok) + of lexbase.EndOfFile: + tok.xkind = pxEof + else: + tok.s = $c + tok.xkind = pxInvalid + lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') + Inc(L.bufpos) diff --git a/rod/c2nim/cparse.nim b/rod/c2nim/cparse.nim new file mode 100755 index 000000000..b637bfa61 --- /dev/null +++ b/rod/c2nim/cparse.nim @@ -0,0 +1,1469 @@ +# +# +# c2nim - C to Nimrod source converter +# (c) Copyright 2010 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# This module implements an Ansi C parser. +# It transfers a C source file into a Nimrod AST. Then the renderer can be +# used to convert the AST to its text representation. + +# XXX standalone structs and unions! +# XXX header pragma for struct and union fields! +# XXX rewrite symbol export handling! + +import + os, llstream, rnimsyn, clex, idents, strutils, pegs, ast, astalgo, msgs, + options, strtabs + +type + TParserFlag* = enum + pfRefs, ## use "ref" instead of "ptr" for C's typ* + pfCDecl, ## annotate procs with cdecl + pfStdCall ## annotate procs with stdcall + + TParserOptions {.final.} = object + flags: set[TParserFlag] + prefixes, suffixes, skipWords: seq[string] + mangleRules: seq[tuple[pattern: TPeg, frmt: string]] + dynlibSym, header: string + PParserOptions* = ref TParserOptions + + TParser* {.final.} = object + lex: TLexer + tok: ref TToken # current token + options: PParserOptions + backtrack: seq[ref TToken] + inTypeDef: int + scopeCounter: int + + TReplaceTuple* = array[0..1, string] + +proc newParserOptions*(): PParserOptions = + new(result) + result.prefixes = @[] + result.suffixes = @[] + result.skipWords = @[] + result.mangleRules = @[] + result.flags = {} + result.dynlibSym = "" + result.header = "" + +proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool = + result = true + case key + of "ref": incl(parserOptions.flags, pfRefs) + of "dynlib": parserOptions.dynlibSym = val + of "header": parserOptions.header = val + of "cdecl": incl(parserOptions.flags, pfCdecl) + of "stdcall": incl(parserOptions.flags, pfStdCall) + of "prefix": parserOptions.prefixes.add(val) + of "suffix": parserOptions.suffixes.add(val) + of "skip": parserOptions.skipWords.add(val) + else: result = false + +proc ParseUnit*(p: var TParser): PNode +proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, + options = newParserOptions()) +proc closeParser*(p: var TParser) +proc exSymbol*(n: var PNode) +proc fixRecordDef*(n: var PNode) + # XXX: move these two to an auxiliary module + +# implementation + +proc OpenParser(p: var TParser, filename: string, + inputStream: PLLStream, options = newParserOptions()) = + OpenLexer(p.lex, filename, inputStream) + p.options = options + p.backtrack = @[] + new(p.tok) + +proc CloseParser(p: var TParser) = CloseLexer(p.lex) +proc safeContext(p: var TParser) = p.backtrack.add(p.tok) +proc closeContext(p: var TParser) = discard p.backtrack.pop() +proc backtrackContext(p: var TParser) = p.tok = p.backtrack.pop() + +proc rawGetTok(p: var TParser) = + if p.tok.next != nil: + p.tok = p.tok.next + elif p.backtrack.len == 0: + p.tok.next = nil + getTok(p.lex, p.tok^) + else: + # We need the next token and must be able to backtrack. So we need to + # allocate a new token. + var t: ref TToken + new(t) + getTok(p.lex, t^) + p.tok.next = t + p.tok = t + +proc isSkipWord(p: TParser): bool = + for s in items(p.options.skipWords): + if p.tok.s == s: return true + +proc getTok(p: var TParser) = + while true: + rawGetTok(p) + if p.tok.xkind != pxSymbol or not isSkipWord(p): break + +proc parMessage(p: TParser, msg: TMsgKind, arg = "") = + #assert false + lexMessage(p.lex, msg, arg) + +proc parLineInfo(p: TParser): TLineInfo = + result = getLineInfo(p.lex) + +proc skipCom(p: var TParser, n: PNode) = + while p.tok.xkind in {pxLineComment, pxStarComment}: + if (n != nil): + if n.comment == nil: n.comment = p.tok.s + else: add(n.comment, "\n" & p.tok.s) + else: + parMessage(p, warnCommentXIgnored, p.tok.s) + getTok(p) + +proc skipStarCom(p: var TParser, n: PNode) = + while p.tok.xkind == pxStarComment: + if (n != nil): + if n.comment == nil: n.comment = p.tok.s + else: add(n.comment, "\n" & p.tok.s) + else: + parMessage(p, warnCommentXIgnored, p.tok.s) + getTok(p) + +proc getTok(p: var TParser, n: PNode) = + getTok(p) + skipCom(p, n) + +proc ExpectIdent(p: TParser) = + if p.tok.xkind != pxSymbol: + parMessage(p, errIdentifierExpected, $(p.tok^)) + +proc Eat(p: var TParser, xkind: TTokKind, n: PNode) = + if p.tok.xkind == xkind: getTok(p, n) + else: parMessage(p, errTokenExpected, TokKindToStr(xkind)) + +proc Eat(p: var TParser, xkind: TTokKind) = + if p.tok.xkind == xkind: getTok(p) + else: parMessage(p, errTokenExpected, TokKindToStr(xkind)) + +proc Eat(p: var TParser, tok: string, n: PNode) = + if p.tok.s == tok: getTok(p, n) + else: parMessage(p, errTokenExpected, tok) + +proc Opt(p: var TParser, xkind: TTokKind, n: PNode) = + if p.tok.xkind == xkind: getTok(p, n) + +proc addSon(father, a, b: PNode) = + addSon(father, a) + addSon(father, b) + +proc addSon(father, a, b, c: PNode) = + addSon(father, a) + addSon(father, b) + addSon(father, c) + +proc newNodeP(kind: TNodeKind, p: TParser): PNode = + result = newNodeI(kind, getLineInfo(p.lex)) + +proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode = + result = newNodeP(kind, p) + result.intVal = intVal + +proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat, + p: TParser): PNode = + result = newNodeP(kind, p) + result.floatVal = floatVal + +proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode = + result = newNodeP(kind, p) + result.strVal = strVal + +proc newIdentNodeP(ident: PIdent, p: TParser): PNode = + result = newNodeP(nkIdent, p) + result.ident = ident + +proc newIdentNodeP(ident: string, p: TParser): PNode = + result = newIdentNodeP(getIdent(ident), p) + +proc mangleName(s: string, p: TParser): string = + for pattern, frmt in items(p.options.mangleRules): + if s.match(pattern): + return s.replace(pattern, frmt) + block prefixes: + for prefix in items(p.options.prefixes): + if s.startsWith(prefix): + result = s.copy(prefix.len) + break prefixes + result = s + for suffix in items(p.options.suffixes): + if result.endsWith(suffix): + setLen(result, result.len - suffix.len) + break + +proc mangledIdent(ident: string, p: TParser): PNode = + result = newNodeP(nkIdent, p) + result.ident = getIdent(mangleName(ident, p)) + +proc newIdentPair(a, b: string, p: TParser): PNode = + result = newNodeP(nkExprColonExpr, p) + addSon(result, newIdentNodeP(a, p)) + addSon(result, newIdentNodeP(b, p)) + +proc newIdentStrLitPair(a, b: string, p: TParser): PNode = + result = newNodeP(nkExprColonExpr, p) + addSon(result, newIdentNodeP(a, p)) + addSon(result, newStrNodeP(nkStrLit, b, p)) + +proc addImportToPragma(pragmas: PNode, ident: string, p: TParser) = + addSon(pragmas, newIdentStrLitPair("importc", ident, p)) + if p.options.dynlibSym.len > 0: + addSon(pragmas, newIdentPair("dynlib", p.options.dynlibSym, p)) + else: + addSon(pragmas, newIdentStrLitPair("header", p.options.header, p)) + +proc mangledIdentAndImport(ident: string, p: TParser): PNode = + result = mangledIdent(ident, p) + if p.scopeCounter > 0: return + if p.options.dynlibSym.len > 0 or p.options.header.len > 0: + var a = result + result = newNodeP(nkPragmaExpr, p) + var pragmas = newNodeP(nkPragma, p) + addSon(result, a) + addSon(result, pragmas) + addImportToPragma(pragmas, ident, p) + +proc DoImport(ident: string, pragmas: PNode, p: TParser) = + if p.options.dynlibSym.len > 0 or p.options.header.len > 0: + addImportToPragma(pragmas, ident, p) + +proc newBinary(opr: string, a, b: PNode, p: TParser): PNode = + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP(getIdent(opr), p)) + addSon(result, a) + addSon(result, b) + +# --------------- symbol exporter -------------------------------------------- + +proc identVis(p: var TParser): PNode = + # identifier with visability + var a = mangledIdent(p.tok.s, p) + result = newNodeP(nkPostfix, p) + addSon(result, newIdentNodeP("*", p)) + addSon(result, a) + getTok(p) + +proc exSymbol(n: var PNode) = + case n.kind + of nkPostfix: + nil + of nkPragmaExpr: + exSymbol(n.sons[0]) + of nkIdent, nkAccQuoted: + var a = newNodeI(nkPostFix, n.info) + addSon(a, newIdentNode(getIdent("*"), n.info)) + addSon(a, n) + n = a + else: internalError(n.info, "exSymbol(): " & $n.kind) + +proc fixRecordDef(n: var PNode) = + if n == nil: return + case n.kind + of nkRecCase: + fixRecordDef(n.sons[0]) + for i in countup(1, sonsLen(n) - 1): + var length = sonsLen(n.sons[i]) + fixRecordDef(n.sons[i].sons[length - 1]) + of nkRecList, nkRecWhen, nkElse, nkOfBranch, nkElifBranch, nkObjectTy: + for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) + of nkIdentDefs: + for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i]) + of nkNilLit: nil + else: internalError(n.info, "fixRecordDef(): " & $n.kind) + +proc addPragmaToIdent(ident: var PNode, pragma: PNode) = + var pragmasNode: PNode + if ident.kind != nkPragmaExpr: + pragmasNode = newNodeI(nkPragma, ident.info) + var e = newNodeI(nkPragmaExpr, ident.info) + addSon(e, ident) + addSon(e, pragmasNode) + ident = e + else: + pragmasNode = ident.sons[1] + if pragmasNode.kind != nkPragma: + InternalError(ident.info, "addPragmaToIdent") + addSon(pragmasNode, pragma) + +proc exSymbols(n: PNode) = + if n == nil: return + case n.kind + of nkEmpty..nkNilLit: nil + of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos]) + of nkWhenStmt: + for i in countup(0, sonsLen(n) - 1): exSymbols(lastSon(n.sons[i])) + of nkStmtList: + for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i]) + of nkVarSection, nkConstSection: + for i in countup(0, sonsLen(n) - 1): exSymbol(n.sons[i].sons[0]) + of nkTypeSection: + for i in countup(0, sonsLen(n) - 1): + exSymbol(n.sons[i].sons[0]) + if (n.sons[i].sons[2] != nil) and + (n.sons[i].sons[2].kind == nkObjectTy): + fixRecordDef(n.sons[i].sons[2]) + else: nil + +# --------------- parser ----------------------------------------------------- +# We use this parsing rule: If it looks like a declaration, it is one. This +# avoids to build a symbol table, which can't be done reliably anyway for our +# purposes. + +proc expression(p: var TParser): PNode +proc constantExpression(p: var TParser): PNode +proc assignmentExpression(p: var TParser): PNode +proc compoundStatement(p: var TParser): PNode +proc statement(p: var TParser): PNode + +proc declKeyword(s: string): bool = + # returns true if it is a keyword that introduces a declaration + case s + of "extern", "static", "auto", "register", "const", "volatile", "restrict", + "inline", "__inline", "__cdecl", "__stdcall", "__syscall", "__fastcall", + "__safecall", "void", "struct", "union", "enum", "typedef", + "short", "int", "long", "float", "double", "signed", "unsigned", "char": + result = true + +proc stmtKeyword(s: string): bool = + case s + of "if", "for", "while", "do", "switch", "break", "continue", "return", + "goto": + result = true + +# ------------------- type desc ----------------------------------------------- + +proc skipIdent(p: var TParser): PNode = + expectIdent(p) + result = mangledIdent(p.tok.s, p) + getTok(p, result) + +proc isIntType(s: string): bool = + case s + of "short", "int", "long", "float", "double", "signed", "unsigned": + result = true + +proc skipConst(p: var TParser) = + while p.tok.xkind == pxSymbol and + (p.tok.s == "const" or p.tok.s == "volatile" or p.tok.s == "restrict"): + getTok(p, nil) + +proc typeAtom(p: var TParser): PNode = + if p.tok.xkind != pxSymbol: return nil + skipConst(p) + ExpectIdent(p) + case p.tok.s + of "void": + result = newNodeP(nkNilLit, p) # little hack + getTok(p, nil) + of "struct", "union", "enum": + getTok(p, nil) + result = skipIdent(p) + elif isIntType(p.tok.s): + var x = "c" & p.tok.s + getTok(p, nil) + while p.tok.xkind == pxSymbol and isIntType(p.tok.s): + add(x, p.tok.s) + getTok(p, nil) + result = newIdentNodeP(x, p) + else: + result = newIdentNodeP(p.tok.s, p) + getTok(p, result) + +proc newPointerTy(p: TParser, typ: PNode): PNode = + if pfRefs in p.options.flags: + result = newNodeP(nkRefTy, p) + else: + result = newNodeP(nkPtrTy, p) + result.addSon(typ) + +proc pointer(p: var TParser, a: PNode): PNode = + result = a + var i = 0 + skipConst(p) + while p.tok.xkind == pxStar: + inc(i) + getTok(p, result) + skipConst(p) + result = newPointerTy(p, result) + if a.kind == nkIdent and a.ident.s == "char": + if i >= 2: + result = newIdentNodeP("cstringArray", p) + for j in 1..i-2: result = newPointerTy(p, result) + elif i == 1: result = newIdentNodeP("cstring", p) + elif a.kind == nkNilLit and i > 0: + result = newIdentNodeP("pointer", p) + for j in 1..i-1: result = newPointerTy(p, result) + +proc parseTypeSuffix(p: var TParser, typ: PNode): PNode = + result = typ + while p.tok.xkind == pxBracketLe: + getTok(p, result) + skipConst(p) # POSIX contains: ``int [restrict]`` + if p.tok.xkind != pxBracketRi: + var tmp = result + var index = expression(p) + # array type: + result = newNodeP(nkBracketExpr, p) + addSon(result, newIdentNodeP("array", p)) + var r = newNodeP(nkRange, p) + addSon(r, newIntNodeP(nkIntLit, 0, p)) + addSon(r, newBinary("-", index, newIntNodeP(nkIntLit, 1, p), p)) + addSon(result, r) + addSon(result, tmp) + else: + # pointer type: + var tmp = result + if pfRefs in p.options.flags: + result = newNodeP(nkRefTy, p) + else: + result = newNodeP(nkPtrTy, p) + result.addSon(tmp) + eat(p, pxBracketRi, result) + +proc typeDesc(p: var TParser): PNode = + result = typeAtom(p) + if result != nil: + result = pointer(p, result) + +proc parseStructBody(p: var TParser): PNode = + result = newNodeP(nkRecList, p) + eat(p, pxCurlyLe, result) + while p.tok.xkind notin {pxEof, pxCurlyRi}: + var baseTyp = typeAtom(p) + while true: + var def = newNodeP(nkIdentDefs, p) + var t = pointer(p, baseTyp) + var i = skipIdent(p) + t = parseTypeSuffix(p, t) + addSon(def, i, t, nil) + addSon(result, def) + if p.tok.xkind != pxComma: break + getTok(p, def) + eat(p, pxSemicolon, lastSon(result)) + eat(p, pxCurlyRi, result) + +proc structPragmas(p: TParser, name: PNode): PNode = + result = newNodeP(nkPragmaExpr, p) + addson(result, name) + var pragmas = newNodep(nkPragma, p) + addSon(pragmas, newIdentNodeP("pure", p)) + addSon(pragmas, newIdentNodeP("final", p)) + addSon(result, pragmas) + +proc enumPragmas(p: TParser, name: PNode): PNode = + result = newNodeP(nkPragmaExpr, p) + addson(result, name) + var pragmas = newNodep(nkPragma, p) + var e = newNodeP(nkExprColonExpr, p) + addSon(e, newIdentNodeP("size", p)) + addSon(e, newIntNodeP(nkIntLit, 4, p)) + addSon(pragmas, e) + addSon(result, pragmas) + +proc parseStruct(p: var TParser): PNode = + result = newNodeP(nkObjectTy, p) + addSon(result, nil) # no pragmas + addSon(result, nil) # no inheritance + if p.tok.xkind == pxCurlyLe: + addSon(result, parseStructBody(p)) + else: + addSon(result, newNodeP(nkRecList, p)) + +proc parseParam(p: var TParser, params: PNode) = + var typ = typeDesc(p) + # support for ``(void)`` parameter list: + if typ.kind == nkNilLit and p.tok.xkind == pxParRi: return + var name: PNode + if p.tok.xkind == pxSymbol: + name = skipIdent(p) + else: + # generate a name for the formal parameter: + var idx = sonsLen(params)+1 + name = newIdentNodeP("a" & $idx, p) + typ = parseTypeSuffix(p, typ) + var x = newNodeP(nkIdentDefs, p) + addSon(x, name) + addSon(x, typ) + if p.tok.xkind == pxAsgn: + # we support default parameters for C++: + getTok(p, x) + addSon(x, assignmentExpression(p)) + else: + addSon(x, nil) + addSon(params, x) + +proc parseFormalParams(p: var TParser, params, pragmas: PNode) = + eat(p, pxParLe, params) + while p.tok.xkind notin {pxEof, pxParRi}: + if p.tok.xkind == pxDotDotDot: + addSon(pragmas, newIdentNodeP("varargs", p)) + getTok(p, pragmas) + break + parseParam(p, params) + if p.tok.xkind != pxComma: break + getTok(p, params) + eat(p, pxParRi, params) + +proc parseCallConv(p: var TParser, pragmas: PNode) = + while p.tok.xkind == pxSymbol: + case p.tok.s + of "inline", "__inline": addSon(pragmas, newIdentNodeP("inline", p)) + of "__cdecl": addSon(pragmas, newIdentNodeP("cdecl", p)) + of "__stdcall": addSon(pragmas, newIdentNodeP("stdcall", p)) + of "__syscall": addSon(pragmas, newIdentNodeP("syscall", p)) + of "__fastcall": addSon(pragmas, newIdentNodeP("fastcall", p)) + of "__safecall": addSon(pragmas, newIdentNodeP("safecall", p)) + else: break + getTok(p, nil) + +proc parseFunctionPointerDecl(p: var TParser, rettyp: PNode): PNode = + var procType = newNodeP(nkProcTy, p) + var pragmas = newNodeP(nkPragma, p) + if pfCDecl in p.options.flags: + addSon(pragmas, newIdentNodeP("cdecl", p)) + elif pfStdCall in p.options.flags: + addSon(pragmas, newIdentNodeP("stdcall", p)) + var params = newNodeP(nkFormalParams, p) + eat(p, pxParLe, params) + addSon(params, rettyp) + parseCallConv(p, pragmas) + if p.tok.xkind == pxStar: getTok(p, params) + else: parMessage(p, errTokenExpected, "*") + var name = skipIdent(p) + eat(p, pxParRi, name) + parseFormalParams(p, params, pragmas) + addSon(procType, params) + addSon(procType, pragmas) + + if p.inTypeDef == 0: + result = newNodeP(nkVarSection, p) + var def = newNodeP(nkIdentDefs, p) + addSon(def, name) + addSon(def, procType) + addSon(def, nil) + addSon(result, def) + else: + result = newNodeP(nkTypeDef, p) + addSon(result, name) + addSon(result, nil) # no generics + addSon(result, procType) + +proc addTypeDef(section, name, t: PNode) = + var def = newNodeI(nkTypeDef, name.info) + addSon(def, name, nil, t) + addSon(section, def) + +proc otherTypeDef(p: var TParser, section, typ: PNode) = + var name, t: PNode + case p.tok.xkind + of pxParLe: + # function pointer: typedef typ (*name)(); + getTok(p, nil) + var x = parseFunctionPointerDecl(p, typ) + name = x[0] + t = x[2] + of pxStar: + # typedef typ *b; + t = pointer(p, typ) + name = skipIdent(p) + else: + # typedef typ name; + name = skipIdent(p) + t = parseTypeSuffix(p, t) + addTypeDef(section, name, t) + +proc parseTrailingDefinedTypes(p: var TParser, section, typ: PNode) = + while p.tok.xkind == pxComma: + getTok(p, nil) + var newTyp = pointer(p, typ) + var newName = skipIdent(p) + newTyp = parseTypeSuffix(p, newTyp) + addTypeDef(section, newName, newTyp) + +proc enumFields(p: var TParser): PNode = + result = newNodeP(nkEnumTy, p) + addSon(result, nil) # enum does not inherit from anything + while true: + var e = skipIdent(p) + if p.tok.xkind == pxAsgn: + getTok(p, e) + var c = constantExpression(p) + var a = e + e = newNodeP(nkEnumFieldDef, p) + addSon(e, a) + addSon(e, c) + skipCom(p, e) + + addSon(result, e) + if p.tok.xkind != pxComma: break + getTok(p, e) + +proc parseTypeDef(p: var TParser): PNode = + result = newNodeP(nkTypeSection, p) + while p.tok.xkind == pxSymbol and p.tok.s == "typedef": + getTok(p, result) + inc(p.inTypeDef) + expectIdent(p) + case p.tok.s + of "struct", "union": + getTok(p, result) + if p.tok.xkind == pxCurlyLe: + var t = parseStruct(p) + var name = skipIdent(p) + addTypeDef(result, structPragmas(p, name), t) + parseTrailingDefinedTypes(p, result, name) + elif p.tok.xkind == pxSymbol: + # name to be defined or type "struct a", we don't know yet: + var nameOrType = skipIdent(p) + case p.tok.xkind + of pxCurlyLe: + var t = parseStruct(p) + if p.tok.xkind == pxSymbol: + # typedef struct tagABC {} abc, *pabc; + # --> abc is a better type name than tagABC! + var name = skipIdent(p) + addTypeDef(result, structPragmas(p, name), t) + parseTrailingDefinedTypes(p, result, name) + else: + addTypeDef(result, structPragmas(p, nameOrType), t) + of pxSymbol: + # typedef struct a a? + if mangleName(p.tok.s, p) == nameOrType.ident.s: + # ignore the declaration: + getTok(p, nil) + else: + # typedef struct a b; or typedef struct a b[45]; + otherTypeDef(p, result, nameOrType) + else: + otherTypeDef(p, result, nameOrType) + else: + expectIdent(p) + of "enum": + getTok(p, result) + if p.tok.xkind == pxCurlyLe: + getTok(p, result) + var t = enumFields(p) + eat(p, pxCurlyRi, t) + var name = skipIdent(p) + addTypeDef(result, enumPragmas(p, name), t) + parseTrailingDefinedTypes(p, result, name) + elif p.tok.xkind == pxSymbol: + # name to be defined or type "enum a", we don't know yet: + var nameOrType = skipIdent(p) + case p.tok.xkind + of pxCurlyLe: + getTok(p, result) + var t = enumFields(p) + eat(p, pxCurlyRi, t) + if p.tok.xkind == pxSymbol: + # typedef enum tagABC {} abc, *pabc; + # --> abc is a better type name than tagABC! + var name = skipIdent(p) + addTypeDef(result, enumPragmas(p, name), t) + parseTrailingDefinedTypes(p, result, name) + else: + addTypeDef(result, enumPragmas(p, nameOrType), t) + of pxSymbol: + # typedef enum a a? + if mangleName(p.tok.s, p) == nameOrType.ident.s: + # ignore the declaration: + getTok(p, nil) + else: + # typedef enum a b; or typedef enum a b[45]; + otherTypeDef(p, result, nameOrType) + else: + otherTypeDef(p, result, nameOrType) + else: + expectIdent(p) + else: + var t = typeAtom(p) + otherTypeDef(p, result, t) + + eat(p, pxSemicolon) + dec(p.inTypeDef) + +proc skipDeclarationSpecifiers(p: var TParser) = + while p.tok.xkind == pxSymbol: + case p.tok.s + of "extern", "static", "auto", "register", "const", "volatile": + getTok(p, nil) + else: break + +proc parseInitializer(p: var TParser): PNode = + if p.tok.xkind == pxCurlyLe: + result = newNodeP(nkBracket, p) + getTok(p, result) + while p.tok.xkind notin {pxEof, pxCurlyRi}: + addSon(result, parseInitializer(p)) + opt(p, pxComma, nil) + eat(p, pxCurlyRi, result) + else: + result = assignmentExpression(p) + +proc addInitializer(p: var TParser, def: PNode) = + if p.tok.xkind == pxAsgn: + getTok(p, def) + addSon(def, parseInitializer(p)) + else: + addSon(def, nil) + +proc parseVarDecl(p: var TParser, baseTyp, typ: PNode, + origName: string): PNode = + result = newNodeP(nkVarSection, p) + var def = newNodeP(nkIdentDefs, p) + addSon(def, mangledIdentAndImport(origName, p)) + addSon(def, parseTypeSuffix(p, typ)) + addInitializer(p, def) + addSon(result, def) + + while p.tok.xkind == pxComma: + getTok(p, def) + var t = pointer(p, baseTyp) + expectIdent(p) + def = newNodeP(nkIdentDefs, p) + addSon(def, mangledIdentAndImport(p.tok.s, p)) + getTok(p, def) + addSon(def, parseTypeSuffix(p, t)) + addInitializer(p, def) + addSon(result, def) + eat(p, pxSemicolon, result) + +proc declaration(p: var TParser): PNode = + result = newNodeP(nkProcDef, p) + var pragmas = newNodeP(nkPragma, p) + + skipDeclarationSpecifiers(p) + parseCallConv(p, pragmas) + skipDeclarationSpecifiers(p) + expectIdent(p) + var baseTyp = typeAtom(p) + var rettyp = pointer(p, baseTyp) + if rettyp != nil and rettyp.kind == nkNilLit: rettyp = nil + skipDeclarationSpecifiers(p) + parseCallConv(p, pragmas) + skipDeclarationSpecifiers(p) + + if p.tok.xkind == pxParLe: + # Function pointer declaration: This is of course only a heuristic, but the + # best we can do here. + result = parseFunctionPointerDecl(p, rettyp) + eat(p, pxSemicolon, result) + return + ExpectIdent(p) + var origName = p.tok.s + getTok(p) # skip identifier + case p.tok.xkind + of pxParLe: + # really a function! + var name = mangledIdent(origName, p) + var params = newNodeP(nkFormalParams, p) + addSon(params, rettyp) + parseFormalParams(p, params, pragmas) + + if pfCDecl in p.options.flags: + addSon(pragmas, newIdentNodeP("cdecl", p)) + elif pfStdcall in p.options.flags: + addSon(pragmas, newIdentNodeP("stdcall", p)) + addSon(result, name) + addSon(result, nil) # no generics + addSon(result, params) + addSon(result, pragmas) + case p.tok.xkind + of pxSemicolon: + getTok(p) + addSon(result, nil) # nobody + if p.scopeCounter == 0: DoImport(origName, pragmas, p) + of pxCurlyLe: + addSon(result, compoundStatement(p)) + else: + parMessage(p, errTokenExpected, ";") + if sonsLen(result.sons[pragmasPos]) == 0: result.sons[pragmasPos] = nil + of pxAsgn, pxSemicolon, pxComma: + result = parseVarDecl(p, baseTyp, rettyp, origName) + else: + parMessage(p, errTokenExpected, ";") + +proc createConst(name, typ, val: PNode, p: TParser): PNode = + result = newNodeP(nkConstDef, p) + addSon(result, name, typ, val) + +proc enumSpecifier(p: var TParser): PNode = + getTok(p, nil) # skip "enum" + case p.tok.xkind + of pxCurlyLe: + # make a const section out of it: + result = newNodeP(nkConstSection, p) + getTok(p, result) + var i = 0 + while true: + var name = skipIdent(p) + var val: PNode + if p.tok.xkind == pxAsgn: + getTok(p, name) + val = constantExpression(p) + if val.kind == nkIntLit: i = int(val.intVal)+1 + else: parMessage(p, errXExpected, "int literal") + else: + val = newIntNodeP(nkIntLit, i, p) + inc(i) + var c = createConst(name, nil, val, p) + addSon(result, c) + if p.tok.xkind != pxComma: break + getTok(p, c) + eat(p, pxCurlyRi, result) + eat(p, pxSemicolon) + of pxSymbol: + result = skipIdent(p) + if p.tok.xkind == pxCurlyLe: + var name = result + # create a type section containing the enum + result = newNodeP(nkTypeSection, p) + var t = newNodeP(nkTypeDef, p) + getTok(p, t) + var e = enumFields(p) + addSon(t, name, nil, e) # nil for generic params + addSon(result, t) + else: + parMessage(p, errTokenExpected, "{") + +# Expressions + +proc setBaseFlags(n: PNode, base: TNumericalBase) = + case base + of base10: nil + of base2: incl(n.flags, nfBase2) + of base8: incl(n.flags, nfBase8) + of base16: incl(n.flags, nfBase16) + +proc primaryExpression(p: var TParser): PNode = + case p.tok.xkind + of pxSymbol: + if p.tok.s == "NULL": + result = newNodeP(nkNilLit, p) + else: + result = mangledIdent(p.tok.s, p) + getTok(p, result) + of pxIntLit: + result = newIntNodeP(nkIntLit, p.tok.iNumber, p) + setBaseFlags(result, p.tok.base) + getTok(p, result) + of pxInt64Lit: + result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p) + setBaseFlags(result, p.tok.base) + getTok(p, result) + of pxFloatLit: + result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p) + setBaseFlags(result, p.tok.base) + getTok(p, result) + of pxStrLit: + # Ansi C allows implicit string literal concatenations: + result = newStrNodeP(nkStrLit, p.tok.s, p) + getTok(p, result) + while p.tok.xkind == pxStrLit: + add(result.strVal, p.tok.s) + getTok(p, result) + of pxCharLit: + result = newIntNodeP(nkCharLit, ord(p.tok.s[0]), p) + getTok(p, result) + of pxParLe: + result = newNodeP(nkPar, p) + getTok(p, result) + addSon(result, expression(p)) + eat(p, pxParRi, result) + else: + result = nil + +proc unaryExpression(p: var TParser): PNode +proc castExpression(p: var TParser): PNode = + if p.tok.xkind == pxParLe: + SafeContext(p) + result = newNodeP(nkCast, p) + getTok(p, result) + var a = typeDesc(p) + if a != nil and p.tok.xkind == pxParRi: + closeContext(p) + eat(p, pxParRi, result) + addSon(result, a) + addSon(result, castExpression(p)) + else: + backtrackContext(p) + result = unaryExpression(p) + else: + result = unaryExpression(p) + +proc multiplicativeExpression(p: var TParser): PNode = + result = castExpression(p) + while true: + case p.tok.xkind + of pxStar: + var a = result + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("*", p), a) + getTok(p, result) + var b = castExpression(p) + addSon(result, b) + of pxSlash: + var a = result + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("div", p), a) + getTok(p, result) + var b = castExpression(p) + addSon(result, b) + of pxMod: + var a = result + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("mod", p), a) + getTok(p, result) + var b = castExpression(p) + addSon(result, b) + else: break + +proc additiveExpression(p: var TParser): PNode = + result = multiplicativeExpression(p) + while true: + case p.tok.xkind + of pxPlus: + var a = result + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("+", p), a) + getTok(p, result) + var b = multiplicativeExpression(p) + addSon(result, b) + of pxMinus: + var a = result + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("-", p), a) + getTok(p, result) + var b = multiplicativeExpression(p) + addSon(result, b) + else: break + +proc incdec(p: var TParser, opr: string): PNode = + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP(opr, p)) + gettok(p, result) + addSon(result, unaryExpression(p)) + +proc unaryOp(p: var TParser, kind: TNodeKind): PNode = + result = newNodeP(kind, p) + getTok(p, result) + addSon(result, castExpression(p)) + +proc prefixCall(p: var TParser, opr: string): PNode = + result = newNodeP(nkPrefix, p) + addSon(result, newIdentNodeP(opr, p)) + gettok(p, result) + addSon(result, castExpression(p)) + +proc postfixExpression(p: var TParser): PNode = + result = primaryExpression(p) + while true: + case p.tok.xkind + of pxBracketLe: + var a = result + result = newNodeP(nkBracketExpr, p) + addSon(result, a) + getTok(p, result) + var b = expression(p) + addSon(result, b) + eat(p, pxBracketRi, result) + of pxParLe: + var a = result + result = newNodeP(nkCall, p) + addSon(result, a) + getTok(p, result) + if p.tok.xkind != pxParRi: + a = assignmentExpression(p) + addSon(result, a) + while p.tok.xkind == pxComma: + getTok(p, a) + a = assignmentExpression(p) + addSon(result, a) + eat(p, pxParRi, result) + of pxDot, pxArrow: + var a = result + result = newNodeP(nkDotExpr, p) + addSon(result, a) + getTok(p, result) + addSon(result, skipIdent(p)) + of pxPlusPlus: + var a = result + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP("inc", p)) + gettok(p, result) + addSon(result, a) + of pxMinusMinus: + var a = result + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP("dec", p)) + gettok(p, result) + addSon(result, a) + else: break + +proc unaryExpression(p: var TParser): PNode = + case p.tok.xkind + of pxPlusPlus: result = incdec(p, "inc") + of pxMinusMinus: result = incdec(p, "dec") + of pxAmp: result = unaryOp(p, nkAddr) + of pxStar: result = unaryOp(p, nkDerefExpr) + of pxPlus: result = prefixCall(p, "+") + of pxMinus: result = prefixCall(p, "-") + of pxTilde: result = prefixCall(p, "not") + of pxNot: result = prefixCall(p, "not") + of pxSymbol: + if p.tok.s == "sizeof": + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP("sizeof", p)) + getTok(p, result) + if p.tok.xkind == pxParLe: + getTok(p, result) + addson(result, typeDesc(p)) + eat(p, pxParRi, result) + else: + addSon(result, unaryExpression(p)) + else: + result = postfixExpression(p) + else: result = postfixExpression(p) + +proc expression(p: var TParser): PNode = + # we cannot support C's ``,`` operator + result = assignmentExpression(p) + if p.tok.xkind == pxComma: + getTok(p, result) + parMessage(p, errOperatorExpected, ",") + +proc conditionalExpression(p: var TParser): PNode + +proc constantExpression(p: var TParser): PNode = + result = conditionalExpression(p) + +proc lvalue(p: var TParser): PNode = + result = unaryExpression(p) + +proc asgnExpr(p: var TParser, opr: string, a: PNode): PNode = + closeContext(p) + getTok(p, a) + var b = assignmentExpression(p) + result = newNodeP(nkAsgn, p) + addSon(result, a) + addSon(result, newBinary(opr, copyTree(a), b, p)) + +proc incdec(p: var TParser, opr: string, a: PNode): PNode = + closeContext(p) + getTok(p, a) + var b = assignmentExpression(p) + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP(getIdent(opr), p)) + addSon(result, a) + addSon(result, b) + +proc assignmentExpression(p: var TParser): PNode = + safeContext(p) + var a = lvalue(p) + case p.tok.xkind + of pxAsgn: + closeContext(p) + getTok(p, a) + var b = assignmentExpression(p) + result = newNodeP(nkAsgn, p) + addSon(result, a) + addSon(result, b) + of pxPlusAsgn: result = incDec(p, "inc", a) + of pxMinusAsgn: result = incDec(p, "dec", a) + of pxStarAsgn: result = asgnExpr(p, "*", a) + of pxSlashAsgn: result = asgnExpr(p, "/", a) + of pxModAsgn: result = asgnExpr(p, "mod", a) + of pxShlAsgn: result = asgnExpr(p, "shl", a) + of pxShrAsgn: result = asgnExpr(p, "shr", a) + of pxAmpAsgn: result = asgnExpr(p, "and", a) + of pxHatAsgn: result = asgnExpr(p, "xor", a) + of pxBarAsgn: result = asgnExpr(p, "or", a) + else: + backtrackContext(p) + result = conditionalExpression(p) + +proc shiftExpression(p: var TParser): PNode = + result = additiveExpression(p) + while p.tok.xkind in {pxShl, pxShr}: + var op = if p.tok.xkind == pxShl: "shl" else: "shr" + getTok(p, result) + var a = result + var b = additiveExpression(p) + result = newBinary(op, a, b, p) + +proc relationalExpression(p: var TParser): PNode = + result = shiftExpression(p) + # Nimrod uses ``<`` and ``<=``, etc. too: + while p.tok.xkind in {pxLt, pxLe, pxGt, pxGe}: + var op = TokKindToStr(p.tok.xkind) + getTok(p, result) + var a = result + var b = shiftExpression(p) + result = newBinary(op, a, b, p) + +proc equalityExpression(p: var TParser): PNode = + result = relationalExpression(p) + # Nimrod uses ``==`` and ``!=`` too: + while p.tok.xkind in {pxEquals, pxNeq}: + var op = TokKindToStr(p.tok.xkind) + getTok(p, result) + var a = result + var b = relationalExpression(p) + result = newBinary(op, a, b, p) + +proc andExpression(p: var TParser): PNode = + result = equalityExpression(p) + while p.tok.xkind == pxAmp: + getTok(p, result) + var a = result + var b = equalityExpression(p) + result = newBinary("&", a, b, p) + +proc exclusiveOrExpression(p: var TParser): PNode = + result = andExpression(p) + while p.tok.xkind == pxHat: + getTok(p, result) + var a = result + var b = andExpression(p) + result = newBinary("^", a, b, p) + +proc inclusiveOrExpression(p: var TParser): PNode = + result = exclusiveOrExpression(p) + while p.tok.xkind == pxBar: + getTok(p, result) + var a = result + var b = exclusiveOrExpression(p) + result = newBinary("or", a, b, p) + +proc logicalAndExpression(p: var TParser): PNode = + result = inclusiveOrExpression(p) + while p.tok.xkind == pxAmpAmp: + getTok(p, result) + var a = result + var b = inclusiveOrExpression(p) + result = newBinary("and", a, b, p) + +proc logicalOrExpression(p: var TParser): PNode = + result = logicalAndExpression(p) + while p.tok.xkind == pxBarBar: + getTok(p, result) + var a = result + var b = logicalAndExpression(p) + result = newBinary("or", a, b, p) + +proc conditionalExpression(p: var TParser): PNode = + result = logicalOrExpression(p) + if p.tok.xkind == pxConditional: + getTok(p, result) # skip '?' + var a = result + var b = expression(p) + eat(p, pxColon, b) + var c = conditionalExpression(p) + result = newNodeP(nkIfExpr, p) + var branch = newNodeP(nkElifExpr, p) + addSon(branch, a) + addSon(branch, b) + addSon(result, branch) + branch = newNodeP(nkElseExpr, p) + addSon(branch, c) + addSon(result, branch) + +# Statements + +proc buildStmtList(a: PNode): PNode = + if a.kind == nkStmtList: result = a + else: + result = newNodeI(nkStmtList, a.info) + addSon(result, a) + +proc nestedStatement(p: var TParser): PNode = + # careful: We need to translate: + # if (x) if (y) stmt; + # into: + # if x: + # if x: + # stmt + # + # Nimrod requires complex statements to be nested in whitespace! + const + complexStmt = {nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, + nkTemplateDef, nkIteratorDef, nkMacroStmt, nkIfStmt, + nkWhenStmt, nkForStmt, nkWhileStmt, nkCaseStmt, nkVarSection, + nkConstSection, nkTypeSection, nkTryStmt, nkBlockStmt, nkStmtList, + nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkStmtListType, nkBlockType} + result = statement(p) + if result.kind in complexStmt: + result = buildStmtList(result) + +proc expressionStatement(p: var TParser): PNode = + # do not skip the comment after a semicolon to make a new nkCommentStmt + if p.tok.xkind == pxSemicolon: + getTok(p) + else: + result = expression(p) + if p.tok.xkind == pxSemicolon: getTok(p) + else: parMessage(p, errTokenExpected, ";") + +proc parseIf(p: var TParser): PNode = + # we parse additional "else if"s too here for better Nimrod code + result = newNodeP(nkIfStmt, p) + while true: + getTok(p) # skip ``if`` + var branch = newNodeP(nkElifBranch, p) + skipCom(p, branch) + eat(p, pxParLe, branch) + addSon(branch, expression(p)) + eat(p, pxParRi, branch) + addSon(branch, nestedStatement(p)) + addSon(result, branch) + if p.tok.s == "else": + getTok(p, result) + if p.tok.s != "if": + # ordinary else part: + branch = newNodeP(nkElse, p) + addSon(branch, nestedStatement(p)) + addSon(result, branch) + break + else: + break + +proc parseWhile(p: var TParser): PNode = + result = newNodeP(nkWhileStmt, p) + getTok(p, result) + eat(p, pxParLe, result) + addSon(result, expression(p)) + eat(p, pxParRi, result) + addSon(result, nestedStatement(p)) + +proc parseDoWhile(p: var TParser): PNode = + # we only support ``do stmt while (0)`` as an idiom for + # ``block: stmt`` + result = newNodeP(nkBlockStmt, p) + getTok(p, result) # skip "do" + addSon(result, nil, nestedStatement(p)) + eat(p, "while", result) + eat(p, pxParLe, result) + if p.tok.xkind == pxIntLit and p.tok.iNumber == 0: getTok(p, result) + else: parMessage(p, errTokenExpected, "0") + eat(p, pxParRi, result) + if p.tok.xkind == pxSemicolon: getTok(p) + +proc declarationOrStatement(p: var TParser): PNode = + if p.tok.xkind != pxSymbol: + result = expressionStatement(p) + elif declKeyword(p.tok.s): + result = declaration(p) + else: + # ordinary identifier: + safeContext(p) + getTok(p) # skip identifier to look ahead + case p.tok.xkind + of pxSymbol, pxStar: + # we parse + # a b + # a * b + # always as declarations! This is of course not correct, but good + # enough for most real world C code out there. + backtrackContext(p) + result = declaration(p) + of pxColon: + # it is only a label: + closeContext(p) + getTok(p) + result = statement(p) + else: + backtrackContext(p) + result = expressionStatement(p) + +proc parseFor(p: var TParser, result: PNode) = + # 'for' '(' expression_statement expression_statement expression? ')' + # statement + getTok(p, result) + eat(p, pxParLe, result) + var initStmt = declarationOrStatement(p) + addSonIfNotNil(result, initStmt) + var w = newNodeP(nkWhileStmt, p) + var condition = expressionStatement(p) + if condition == nil: condition = newIdentNodeP("true", p) + addSon(w, condition) + var step = if p.tok.xkind != pxParRi: expression(p) else: nil + eat(p, pxParRi, step) + var loopBody = nestedStatement(p) + if step != nil: + loopBody = buildStmtList(loopBody) + addSon(loopBody, step) + addSon(w, loopBody) + addSon(result, w) + +proc switchStatement(p: var TParser): PNode = + result = newNodeP(nkStmtList, p) + while true: + if p.tok.xkind in {pxEof, pxCurlyRi}: break + case p.tok.s + of "break": + getTok(p, result) + eat(p, pxSemicolon, result) + break + of "return", "continue", "goto": + addSon(result, statement(p)) + break + of "case", "default": + break + else: nil + addSon(result, statement(p)) + if sonsLen(result) == 0: + # translate empty statement list to Nimrod's ``nil`` statement + result = newNodeP(nkNilLit, p) + +proc rangeExpression(p: var TParser): PNode = + # We support GCC's extension: ``case expr...expr:`` + result = constantExpression(p) + if p.tok.xkind == pxDotDotDot: + getTok(p, result) + var a = result + var b = constantExpression(p) + result = newNodeP(nkRange, p) + addSon(result, a) + addSon(result, b) + +proc parseSwitch(p: var TParser): PNode = + # We cannot support Duff's device or C's crazy switch syntax. We just support + # sane usages of switch. ;-) + result = newNodeP(nkCaseStmt, p) + getTok(p, result) + eat(p, pxParLe, result) + addSon(result, expression(p)) + eat(p, pxParRi, result) + eat(p, pxCurlyLe, result) + var b: PNode + while (p.tok.xkind != pxCurlyRi) and (p.tok.xkind != pxEof): + case p.tok.s + of "default": + b = newNodeP(nkElse, p) + getTok(p, b) + eat(p, pxColon, b) + of "case": + b = newNodeP(nkOfBranch, p) + while p.tok.xkind == pxSymbol and p.tok.s == "case": + getTok(p, b) + addSon(b, rangeExpression(p)) + eat(p, pxColon, b) + else: + parMessage(p, errXExpected, "case") + addSon(b, switchStatement(p)) + addSon(result, b) + if b.kind == nkElse: break + eat(p, pxCurlyRi) + +proc embedStmts(sl, a: PNode) = + if a.kind != nkStmtList: + addSon(sl, a) + else: + for i in 0..sonsLen(a)-1: addSon(sl, a[i]) + +proc compoundStatement(p: var TParser): PNode = + result = newNodeP(nkStmtList, p) + eat(p, pxCurlyLe) + inc(p.scopeCounter) + while p.tok.xkind notin {pxEof, pxCurlyRi}: + var a = statement(p) + if a == nil: break + embedStmts(result, a) + if sonsLen(result) == 0: + # translate ``{}`` to Nimrod's ``nil`` statement + result = newNodeP(nkNilLit, p) + dec(p.scopeCounter) + eat(p, pxCurlyRi) + +include cpp + +proc statement(p: var TParser): PNode = + case p.tok.xkind + of pxSymbol: + case p.tok.s + of "if": result = parseIf(p) + of "switch": result = parseSwitch(p) + of "while": result = parseWhile(p) + of "do": result = parseDoWhile(p) + of "for": + result = newNodeP(nkStmtList, p) + parseFor(p, result) + of "goto": + # we cannot support "goto"; in hand-written C, "goto" is most often used + # to break a block, so we convert it to a break statement with label. + result = newNodeP(nkBreakStmt, p) + getTok(p) + addSon(result, skipIdent(p)) + eat(p, pxSemicolon) + of "continue": + result = newNodeP(nkContinueStmt, p) + getTok(p) + eat(p, pxSemicolon) + addSon(result, nil) + of "break": + result = newNodeP(nkBreakStmt, p) + getTok(p) + eat(p, pxSemicolon) + addSon(result, nil) + of "return": + result = newNodeP(nkReturnStmt, p) + getTok(p) + # special case for ``return (expr)`` because I hate the redundant + # parenthesis ;-) + if p.tok.xkind == pxParLe: + getTok(p, result) + addSon(result, expression(p)) + eat(p, pxParRi, result) + elif p.tok.xkind != pxSemicolon: + addSon(result, expression(p)) + else: + addSon(result, nil) + eat(p, pxSemicolon) + of "enum": + result = enumSpecifier(p) + of "typedef": + result = parseTypeDef(p) + else: + result = declarationOrStatement(p) + of pxCurlyLe: + result = compoundStatement(p) + of pxDirective, pxDirectiveParLe: + result = parseDir(p) + of pxLineComment, pxStarComment: + result = newNodeP(nkCommentStmt, p) + skipCom(p, result) + of pxSemicolon: + # empty statement: + getTok(p) + if p.tok.xkind in {pxLineComment, pxStarComment}: + result = newNodeP(nkCommentStmt, p) + skipCom(p, result) + else: + result = newNodeP(nkNilLit, p) + else: + result = expressionStatement(p) + #parMessage(p, errStmtExpected) + +proc parseUnit(p: var TParser): PNode = + result = newNodeP(nkStmtList, p) + getTok(p) # read first token + while p.tok.xkind != pxEof: + var s = statement(p) + if s != nil: embedStmts(result, s) + exSymbols(result) + diff --git a/rod/c2nim/cpp.nim b/rod/c2nim/cpp.nim new file mode 100755 index 000000000..61873628e --- /dev/null +++ b/rod/c2nim/cpp.nim @@ -0,0 +1,231 @@ +# Preprocessor support + +const + c2nimSymbol = "C2NIM" + +proc eatNewLine(p: var TParser, n: PNode) = + if p.tok.xkind == pxLineComment: + skipCom(p, n) + if p.tok.xkind == pxNewLine: getTok(p) + else: + eat(p, pxNewLine) + +proc parseDefineBody(p: var TParser, tmplDef: PNode): string = + if p.tok.xkind == pxCurlyLe or + (p.tok.xkind == pxSymbol and (declKeyword(p.tok.s) or stmtKeyword(p.tok.s))): + addSon(tmplDef, statement(p)) + result = "stmt" + elif p.tok.xkind in {pxLineComment, pxNewLine}: + addSon(tmplDef, buildStmtList(newNodeP(nkNilLit, p))) + result = "stmt" + else: + addSon(tmplDef, buildStmtList(expression(p))) + result = "expr" + +proc parseDefine(p: var TParser): PNode = + if p.tok.xkind == pxDirectiveParLe: + # a macro with parameters: + result = newNodeP(nkTemplateDef, p) + getTok(p) + addSon(result, skipIdent(p)) + eat(p, pxParLe) + var params = newNodeP(nkFormalParams, p) + # return type; not known yet: + addSon(params, nil) + var identDefs = newNodeP(nkIdentDefs, p) + while p.tok.xkind != pxParRi: + addSon(identDefs, skipIdent(p)) + skipStarCom(p, nil) + if p.tok.xkind != pxComma: break + getTok(p) + addSon(identDefs, newIdentNodeP("expr", p)) + addSon(identDefs, nil) + addSon(params, identDefs) + eat(p, pxParRi) + + addSon(result, nil) # no generic parameters + addSon(result, params) + addSon(result, nil) # no pragmas + var kind = parseDefineBody(p, result) + params.sons[0] = newIdentNodeP(kind, p) + eatNewLine(p, result) + else: + # a macro without parameters: + result = newNodeP(nkConstSection, p) + while p.tok.xkind == pxDirective and p.tok.s == "define": + getTok(p) # skip #define + var c = newNodeP(nkConstDef, p) + addSon(c, skipIdent(p)) + addSon(c, nil) + skipStarCom(p, c) + if p.tok.xkind in {pxLineComment, pxNewLine, pxEof}: + addSon(c, newIdentNodeP("true", p)) + else: + addSon(c, expression(p)) + addSon(result, c) + eatNewLine(p, c) + +proc isDir(p: TParser, dir: string): bool = + result = p.tok.xkind in {pxDirectiveParLe, pxDirective} and p.tok.s == dir + +proc parseInclude(p: var TParser): PNode = + result = newNodeP(nkImportStmt, p) + while isDir(p, "include"): + getTok(p) # skip "include" + if p.tok.xkind == pxStrLit: + var file = newStrNodeP(nkStrLit, p.tok.s, p) + addSon(result, file) + getTok(p) + skipStarCom(p, file) + elif p.tok.xkind == pxLt: + while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) + else: + parMessage(p, errXExpected, "string literal") + eatNewLine(p, nil) + if sonsLen(result) == 0: + # we only parsed includes that we chose to ignore: + result = nil + +proc definedExprAux(p: var TParser): PNode = + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP("defined", p)) + addSon(result, skipIdent(p)) + +proc parseStmtList(p: var TParser): PNode = + result = newNodeP(nkStmtList, p) + while true: + case p.tok.xkind + of pxEof: break + of pxDirectiveParLe, pxDirective: + case p.tok.s + of "else", "endif", "elif": break + else: nil + addSon(result, statement(p)) + +proc parseIfDirAux(p: var TParser, result: PNode) = + addSon(result.sons[0], parseStmtList(p)) + while isDir(p, "elif"): + var b = newNodeP(nkElifBranch, p) + getTok(p) + addSon(b, expression(p)) + eatNewLine(p, nil) + addSon(b, parseStmtList(p)) + addSon(result, b) + if isDir(p, "else"): + var s = newNodeP(nkElse, p) + while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) + eatNewLine(p, nil) + addSon(s, parseStmtList(p)) + addSon(result, s) + if isDir(p, "endif"): + while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) + eatNewLine(p, nil) + else: + parMessage(p, errXExpected, "#endif") + +proc specialIf(p: TParser): bool = + ExpectIdent(p) + result = p.tok.s == c2nimSymbol + +proc chooseBranch(whenStmt: PNode, branch: int): PNode = + var L = sonsLen(whenStmt) + if branch < L: + if L == 2 and whenStmt[1].kind == nkElse or branch == 0: + result = lastSon(whenStmt[branch]) + else: + var b = whenStmt[branch] + assert(b.kind == nkElifBranch) + result = newNodeI(nkWhenStmt, whenStmt.info) + for i in branch .. L-1: + addSon(result, whenStmt[i]) + +proc skipIfdefCPlusPlus(p: var TParser): PNode = + while p.tok.xkind != pxEof: + if isDir(p, "endif"): + while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) + eatNewLine(p, nil) + return + getTok(p) + parMessage(p, errXExpected, "#endif") + +proc parseIfdefDir(p: var TParser): PNode = + result = newNodeP(nkWhenStmt, p) + addSon(result, newNodeP(nkElifBranch, p)) + getTok(p) + var special = specialIf(p) + if p.tok.s == "__cplusplus": + return skipIfdefCPlusPlus(p) + addSon(result.sons[0], definedExprAux(p)) + eatNewLine(p, nil) + parseIfDirAux(p, result) + if special: + result = chooseBranch(result, 0) + +proc parseIfndefDir(p: var TParser): PNode = + result = newNodeP(nkWhenStmt, p) + addSon(result, newNodeP(nkElifBranch, p)) + getTok(p) + var special = specialIf(p) + var e = newNodeP(nkCall, p) + addSon(e, newIdentNodeP("not", p)) + addSon(e, definedExprAux(p)) + eatNewLine(p, nil) + addSon(result.sons[0], e) + parseIfDirAux(p, result) + if special: + result = chooseBranch(result, 1) + +proc parseIfDir(p: var TParser): PNode = + result = newNodeP(nkWhenStmt, p) + addSon(result, newNodeP(nkElifBranch, p)) + getTok(p) + addSon(result.sons[0], expression(p)) + eatNewLine(p, nil) + parseIfDirAux(p, result) + +proc parseMangleDir(p: var TParser) = + var col = getColumn(p.lex) + 2 + getTok(p) + if p.tok.xkind != pxStrLit: ExpectIdent(p) + try: + var pattern = parsePeg( + input = p.tok.s, + filename = p.lex.filename, + line = p.lex.linenumber, + col = col) + getTok(p) + if p.tok.xkind != pxStrLit: ExpectIdent(p) + p.options.mangleRules.add((pattern, p.tok.s)) + getTok(p) + except EInvalidPeg: + parMessage(p, errUser, getCurrentExceptionMsg()) + eatNewLine(p, nil) + +proc parseDir(p: var TParser): PNode = + assert(p.tok.xkind in {pxDirective, pxDirectiveParLe}) + case p.tok.s + of "define": result = parseDefine(p) + of "include": result = parseInclude(p) + of "ifdef": result = parseIfdefDir(p) + of "ifndef": result = parseIfndefDir(p) + of "if": result = parseIfDir(p) + of "cdecl", "stdcall", "ref": + discard setOption(p.options, p.tok.s) + getTok(p) + eatNewLine(p, nil) + of "dynlib", "header", "prefix", "suffix", "skip": + var key = p.tok.s + getTok(p) + if p.tok.xkind != pxStrLit: ExpectIdent(p) + discard setOption(p.options, key, p.tok.s) + getTok(p) + eatNewLine(p, nil) + of "mangle": + parseMangleDir(p) + else: + # ignore unimportant/unknown directive ("undef", "pragma", "error") + while true: + getTok(p) + if p.tok.xkind in {pxEof, pxNewLine, pxLineComment}: break + eatNewLine(p, nil) + diff --git a/rod/c2nim/manual.txt b/rod/c2nim/manual.txt new file mode 100644 index 000000000..d0c45272b --- /dev/null +++ b/rod/c2nim/manual.txt @@ -0,0 +1,235 @@ +================================= + c2nim User's manual +================================= + +:Author: Andreas Rumpf +:Version: 0.8.10 + +Introduction +============ + +c2nim is a tool to translate Ansi C code to Nimrod. The output is +human-readable Nimrod code that is meant to be tweaked by hand after the +translation process. c2nim is no real compiler! + +c2nim is preliminary meant to translate C header files. Because of this, the +preprocessor is part of the parser. For example: + +.. code-block:: C + #define abc 123 + #define xyz 789 + +Is translated into: + +.. code-block:: Nimrod + const + abc* = 123 + xyz* = 789 + + +c2nim is meant to translate fragments of C code and thus does not follow +include files. c2nim cannot parse all of Ansi C and many constructs cannot +be represented in Nimrod: for example `duff's device`:idx: cannot be translated +to Nimrod. + + +Preprocessor support +==================== + +Even though the translation process is not perfect, it is often the case that +the translated Nimrod code does not need any tweaking by hand. In other cases +it may be preferable to modify the input file instead of the generated Nimrod +code so that c2nim can parse it properly. c2nim's preprocessor defines the +symbol ``C2NIM`` that can be used to mark code sections: + +.. code-block:: C + #ifndef C2NIM + // C2NIM should ignore this prototype: + int fprintf(FILE* f, const char* frmt, ...); + #endif + +The ``C2NIM`` symbol is only recognized in ``#ifdef`` and ``#ifndef`` +constructs! ``#if defined(C2NIM)`` does **not** work. + +c2nim *processes* ``#ifdef C2NIM`` and ``#ifndef C2NIM`` directives, but other +``#if[def]`` directives are *translated* into Nimrod's ``when`` construct: + +.. code-block:: C + #ifdef DEBUG + # define OUT(x) printf("%s\n", x) + #else + # define OUT(x) + #endif + +Is translated into: + +.. code-block:: Nimrod + when defined(debug): + template OUT(x: expr): expr = + printf("%s\x0A", x) + else: + template OUT(x: expr): stmt = + nil + +As can been seen from the example, C's macros with parameters are mapped +to Nimrod's templates. This mapping is the best one can do, but it is of course +not accurate: Nimrod's templates operate on syntax trees whereas C's +macros work on the token level. c2nim cannot translate any macro that contains +the ``##`` token concatenation operator. + +c2nim's preprocessor supports special directives that affect how the output +is generated. They should be put into a ``#ifdef C2NIM`` section so that +ordinary C compilers ignore them. + + +``#stdcall`` and ``#cdecl`` directives +-------------------------------------- +**Note**: There are also ``--stdcall`` and ``--cdecl`` command line options +that can be used for the same purpose. + +These directives tell c2nim that it should annotate every proc (or proc type) +with the ``stdcall`` / ``cdecl`` calling convention. + + +``#dynlib`` directive +--------------------- +**Note**: There is also a ``--dynlib`` command line option that can be used for +the same purpose. + +This directive tells c2nim that it should annotate every proc that resulted +from a C function prototype with the ``dynlib`` pragma: + +.. code-block:: C + + #ifdef C2NIM + # dynlib iupdll + # cdecl + # if defined(windows) + # define iupdll "iup.dll" + # elif defined(macosx) + # define iupdll "libiup.dynlib" + # else + # define iupdll "libiup.so" + # endif + #endif + + int IupConvertXYToPos(PIhandle ih, int x, int y); + +Is translated to: + +.. code-block:: Nimrod + when defined(windows): + const iupdll* = "iup.dll" + elif defined(macosx): + const iupdll* = "libiup.dynlib" + else: + const iupdll* = "libiup.so" + + proc IupConvertXYToPos*(ih: PIhandle, x: cint, y: cint): cint {. + importc: "IupConvertXYToPos", cdecl, dynlib: iupdll.} + +Note how the example contains extra C code to declare the ``iupdll`` symbol +in the generated Nimrod code. + + +``#header`` directive +--------------------- +**Note**: There is also a ``--header`` command line option that can be used for +the same purpose. + +The ``#header`` directive tells c2nim that it should annotate every proc that +resulted from a C function prototype and every exported variable and type with +the ``header`` pragma: + +.. code-block:: C + + #ifdef C2NIM + # header "iup.h" + #endif + + int IupConvertXYToPos(PIhandle ih, int x, int y); + +Is translated to: + +.. code-block:: Nimrod + proc IupConvertXYToPos*(ih: PIhandle, x: cint, y: cint): cint {. + importc: "IupConvertXYToPos", header: "iup.h".} + +The ``#header`` and the ``#dynlib`` directives are mutually exclusive. +A binding that uses ``dynlib`` is much more preferable over one that uses +``header``! The Nimrod compiler might drop support for the ``header`` pragma +in the future as it cannot work for backends that do not generate C code. + + +``#prefix`` and ``#suffix`` directives +-------------------------------------- + +**Note**: There are also ``--prefix`` and ``--suffix`` command line options +that can be used for the same purpose. + +c2nim does not do any name mangling by default. However the + ``#prefix`` and ``#suffix`` directives can be used to strip prefixes and +suffixes from the identifiers in the C code: + +.. code-block:: C + + #ifdef C2NIM + # prefix Iup + # dynlib dllname + # cdecl + #endif + + int IupConvertXYToPos(PIhandle ih, int x, int y); + +Is translated to: + +.. code-block:: Nimrod + + proc ConvertXYToPos*(ih: PIhandle, x: cint, y: cint): cint {. + importc: "IupConvertXYToPos", cdecl, dynlib: dllname.} + + +``#mangle`` directive +--------------------- + +Even more sophisticated name mangling can be achieved by the ``#mangle`` +directive: It takes a PEG pattern and format string that specify how the +identifier should be converted: + +.. code-block:: C + #mangle "'GTK_'{.*}" "TGtk$1" + + +``#skip`` directive +------------------- +**Note**: There is also ``--skip`` command line option that can be used for the +same purpose. + +Often C code contains special macros that affect the declaration of a function +prototype but confuse c2nim's parser: + +.. code-block:: C + // does not parse! + EXPORT int f(void); + EXPORT int g(void); + +Instead of to remove ``EXPORT`` from the input source file, one can tell c2nim +to skip special identifiers: + +.. code-block:: C + #skip EXPORT + // does parse now! + EXPORT int f(void); + EXPORT int g(void); + + +Limitations +=========== + +* C's ``,`` operator (comma operator) is not supported. +* C's ``union`` has no equivalent in Nimrod. +* Standalone ``struct x {}`` declarations are not implemented. Put them into + a ``typedef``. +* The condition in a ``do while(condition)`` statement must be ``0``. +* Lots of other small issues... + diff --git a/rod/ccgstmts.nim b/rod/ccgstmts.nim index f011f32a0..78ef4f24f 100755 --- a/rod/ccgstmts.nim +++ b/rod/ccgstmts.nim @@ -581,16 +581,13 @@ proc genTryStmt(p: BProc, t: PNode) = # sp.status = RangeError; /* if raise; else 0 */ # } # } + # excHandler = excHandler->prev; /* deactivate this safe point */ # /* finally: */ # printf('fin!\n'); # if (sp.status != 0) # longjmp(excHandler->context, sp.status); - # excHandler = excHandler->prev; /* deactivate this safe point */ - var - i, length, blen: int - safePoint, orExpr: PRope genLineDir(p, t) - safePoint = getTempName() + var safePoint = getTempName() useMagic(p.module, "TSafePoint") useMagic(p.module, "E_Base") useMagic(p.module, "excHandler") @@ -600,13 +597,13 @@ proc genTryStmt(p: BProc, t: PNode) = if optStackTrace in p.Options: app(p.s[cpsStmts], "framePtr = (TFrame*)&F;" & tnl) appf(p.s[cpsStmts], "if ($1.status == 0) {$n", [safePoint]) - length = sonsLen(t) + var length = sonsLen(t) inc(p.nestedTryStmts) genStmts(p, t.sons[0]) app(p.s[cpsStmts], "} else {" & tnl) - i = 1 + var i = 1 while (i < length) and (t.sons[i].kind == nkExceptBranch): - blen = sonsLen(t.sons[i]) + var blen = sonsLen(t.sons[i]) if blen == 1: # general except section: if i > 1: app(p.s[cpsStmts], "else {" & tnl) @@ -614,7 +611,7 @@ proc genTryStmt(p: BProc, t: PNode) = appf(p.s[cpsStmts], "$1.status = 0;$n", [safePoint]) if i > 1: app(p.s[cpsStmts], '}' & tnl) else: - orExpr = nil + var orExpr: PRope = nil for j in countup(0, blen - 2): assert(t.sons[i].sons[j].kind == nkType) if orExpr != nil: app(orExpr, "||") diff --git a/rod/extccomp.nim b/rod/extccomp.nim index 6ca816eb1..d7b429339 100755 --- a/rod/extccomp.nim +++ b/rod/extccomp.nim @@ -50,7 +50,7 @@ const compilerExe: "gcc", compileTmpl: "-c $options $include -o $objfile $file", buildGui: " -mwindows", - buildDll: " -mdll", + buildDll: " -shared", linkerExe: "gcc", linkTmpl: "$options $buildgui $builddll -o $exefile $objfiles", includeCmd: " -I", @@ -65,7 +65,7 @@ const compilerExe: "llvm-gcc", compileTmpl: "-c $options $include -o $objfile $file", buildGui: " -mwindows", - buildDll: " -mdll", + buildDll: " -shared", linkerExe: "llvm-gcc", linkTmpl: "$options $buildgui $builddll -o $exefile $objfiles", includeCmd: " -I", @@ -225,7 +225,7 @@ const var ccompiler*: TSystemCC = ccGcc -const # the used compiler +const # the used compiler hExt* = "h" var cExt*: string = "c" # extension of generated C/C++ files @@ -490,7 +490,6 @@ proc CallCCompiler(projectfile: string) = generateScript(projectFile, script) proc genMappingFiles(list: TLinkedList): PRope = - result = nil var it = PStrEntry(list.head) while it != nil: appf(result, "--file:r\"$1\"$n", [toRope(AddFileExt(it.data, cExt))]) diff --git a/rod/msgs.nim b/rod/msgs.nim index ff42bc690..bae60df97 100755 --- a/rod/msgs.nim +++ b/rod/msgs.nim @@ -408,7 +408,7 @@ proc toColumn(info: TLineInfo): int = proc MessageOut(s: string) = # change only this proc to put it elsewhere Writeln(stdout, s) - + proc coordToStr(coord: int): string = if coord == - 1: result = "???" else: result = $(coord) diff --git a/rod/pragmas.nim b/rod/pragmas.nim index 28e4b2a7b..c52e4a10b 100755 --- a/rod/pragmas.nim +++ b/rod/pragmas.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2009 Andreas Rumpf +# (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -37,7 +37,7 @@ const lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wPure, wDeprecated} typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, - wPure, wHeader, wCompilerProc, wFinal} + wPure, wHeader, wCompilerProc, wFinal, wSize} fieldPragmas* = {wImportc, wExportc, wDeprecated} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib} @@ -344,6 +344,13 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = sym.typ.align = expectIntLit(c, it) if not IsPowerOfTwo(sym.typ.align) and (sym.typ.align != 0): liMessage(it.info, errPowerOfTwoExpected) + of wSize: + if sym.typ == nil: invalidPragma(it) + var size = expectIntLit(c, it) + if not IsPowerOfTwo(size) or size <= 0 or size > 8: + liMessage(it.info, errPowerOfTwoExpected) + else: + sym.typ.size = size of wNodecl: noVal(it) incl(sym.loc.Flags, lfNoDecl) diff --git a/rod/rnimsyn.nim b/rod/rnimsyn.nim index 60cfd38b1..d0c7dc9e3 100755 --- a/rod/rnimsyn.nim +++ b/rod/rnimsyn.nim @@ -615,13 +615,19 @@ proc gproc(g: var TSrcGen, n: PNode) = proc gblock(g: var TSrcGen, n: PNode) = var c: TContext initContext(c) - putWithSpace(g, tkBlock, "block") - gsub(g, n.sons[0]) + if n.sons[0] != nil: + putWithSpace(g, tkBlock, "block") + gsub(g, n.sons[0]) + else: + put(g, tkBlock, "block") putWithSpace(g, tkColon, ":") if longMode(n) or (lsub(n.sons[1]) + g.lineLen > maxLineLen): incl(c.flags, rfLongMode) gcoms(g) + # XXX I don't get why this is needed here! gstmts should already handle this! + indentNL(g) gstmts(g, n.sons[1], c) + dedent(g) proc gasm(g: var TSrcGen, n: PNode) = putWithSpace(g, tkAsm, "asm") diff --git a/rod/scanner.nim b/rod/scanner.nim index fb9a9fa2c..348c5c73d 100755 --- a/rod/scanner.nim +++ b/rod/scanner.nim @@ -427,7 +427,7 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = case L.buf[L.bufpos] of 'n', 'N': if tok.toktype == tkCharLit: lexMessage(L, errNnotAllowedInCharacter) - tok.literal = tok.literal & tnl + add(tok.literal, tnl) Inc(L.bufpos) of 'r', 'R', 'c', 'C': add(tok.literal, CR) diff --git a/rod/semgnrc.nim b/rod/semgnrc.nim index fbc9f18bd..0a291cd42 100755 --- a/rod/semgnrc.nim +++ b/rod/semgnrc.nim @@ -1,11 +1,12 @@ # # # The Nimrod Compiler -# (c) Copyright 2009 Andreas Rumpf +# (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # + # This implements the first pass over the generic body; it resolves some # symbols. Thus for generics there is a two-phase symbol lookup just like # in C++. @@ -155,8 +156,8 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode if (a.kind != nkIdentDefs): IllFormedAst(a) checkMinSonsLen(a, 3) L = sonsLen(a) - a.sons[L - 2] = semGenericStmt(c, a.sons[L - 2], {withinTypeDesc}) # do not perform symbol lookup for default - # expressions + a.sons[L - 2] = semGenericStmt(c, a.sons[L - 2], {withinTypeDesc}) + # do not perform symbol lookup for default expressions for j in countup(0, L - 3): addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) of nkConstSection: @@ -196,8 +197,7 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode of nkEnumFieldDef: a = n.sons[i].sons[0] of nkIdent: a = n.sons[i] else: illFormedAst(n) - addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos - - 1) + addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1) of nkObjectTy, nkTupleTy: nil of nkFormalParams: @@ -229,4 +229,4 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode else: for i in countup(0, sonsLen(n) - 1): result.sons[i] = semGenericStmt(c, n.sons[i], flags) - \ No newline at end of file + diff --git a/rod/sigmatch.nim b/rod/sigmatch.nim index a1b322d86..a33265f40 100755 --- a/rod/sigmatch.nim +++ b/rod/sigmatch.nim @@ -92,18 +92,15 @@ proc getNotFoundError(c: PContext, n: PNode): string = # Gives a detailed error message; this is seperated from semDirectCall, # as semDirectCall is already pretty slow (and we need this information only # in case of an error). - var - sym: PSym - o: TOverloadIter - candidates: string result = msgKindToString(errTypeMismatch) for i in countup(1, sonsLen(n) - 1): #debug(n.sons[i].typ); add(result, typeToString(n.sons[i].typ)) if i != sonsLen(n) - 1: add(result, ", ") add(result, ')') - candidates = "" - sym = initOverloadIter(o, c, n.sons[0]) + var candidates = "" + var o: TOverloadIter + var sym = initOverloadIter(o, c, n.sons[0]) while sym != nil: if sym.kind in {skProc, skMethod, skIterator, skConverter}: add(candidates, getProcHeader(sym)) diff --git a/rod/syntaxes.nim b/rod/syntaxes.nim index d757ad9f6..9361ad2ff 100755 --- a/rod/syntaxes.nim +++ b/rod/syntaxes.nim @@ -37,6 +37,7 @@ proc parseAll*(p: var TParsers): PNode proc parseTopLevelStmt*(p: var TParsers): PNode # implements an iterator. Returns the next top-level statement or nil if end # of stream. + # implementation proc ParseFile(filename: string): PNode = @@ -57,7 +58,8 @@ proc parseAll(p: var TParsers): PNode = of skinBraces: result = pbraces.parseAll(p.parser) of skinEndX: - InternalError("parser to implement") # skinEndX: result := pendx.parseAll(p.parser); + InternalError("parser to implement") + # skinEndX: result := pendx.parseAll(p.parser); proc parseTopLevelStmt(p: var TParsers): PNode = case p.skin @@ -66,39 +68,33 @@ proc parseTopLevelStmt(p: var TParsers): PNode = of skinBraces: result = pbraces.parseTopLevelStmt(p.parser) of skinEndX: - InternalError("parser to implement") #skinEndX: result := pendx.parseTopLevelStmt(p.parser); + InternalError("parser to implement") + #skinEndX: result := pendx.parseTopLevelStmt(p.parser); proc UTF8_BOM(s: string): int = - if (s[0] == '\xEF') and (s[0 + 1] == '\xBB') and (s[0 + 2] == '\xBF'): + if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'): result = 3 else: result = 0 proc containsShebang(s: string, i: int): bool = - var j: int - result = false if (s[i] == '#') and (s[i + 1] == '!'): - j = i + 2 + var j = i + 2 while s[j] in WhiteSpace: inc(j) result = s[j] == '/' proc parsePipe(filename: string, inputStream: PLLStream): PNode = - var - line: string - s: PLLStream - i: int - q: TParser - result = nil - s = LLStreamOpen(filename, fmRead) + var s = LLStreamOpen(filename, fmRead) if s != nil: - line = LLStreamReadLine(s) - i = UTF8_Bom(line) + 0 + var line = LLStreamReadLine(s) + var i = UTF8_Bom(line) if containsShebang(line, i): line = LLStreamReadLine(s) i = 0 if (line[i] == '#') and (line[i + 1] == '!'): inc(i, 2) while line[i] in WhiteSpace: inc(i) + var q: TParser OpenParser(q, filename, LLStreamOpen(copy(line, i))) result = pnimsyn.parseAll(q) CloseParser(q) @@ -124,12 +120,10 @@ proc getCallee(n: PNode): PIdent = else: rawMessage(errXNotAllowedHere, renderTree(n)) -proc applyFilter(p: var TParsers, n: PNode, filename: string, stdin: PLLStream): PLLStream = - var - ident: PIdent - f: TFilterKind - ident = getCallee(n) - f = getFilter(ident) +proc applyFilter(p: var TParsers, n: PNode, filename: string, + stdin: PLLStream): PLLStream = + var ident = getCallee(n) + var f = getFilter(ident) case f of filtNone: p.skin = getParser(ident) @@ -146,7 +140,8 @@ proc applyFilter(p: var TParsers, n: PNode, filename: string, stdin: PLLStream): messageOut(result.s) rawMessage(hintCodeEnd, []) -proc evalPipe(p: var TParsers, n: PNode, filename: string, start: PLLStream): PLLStream = +proc evalPipe(p: var TParsers, n: PNode, filename: string, + start: PLLStream): PLLStream = result = start if n == nil: return if (n.kind == nkInfix) and (n.sons[0].kind == nkIdent) and @@ -162,16 +157,14 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string, start: PLLStream): PL result = applyFilter(p, n, filename, result) proc openParsers(p: var TParsers, filename: string, inputstream: PLLStream) = - var - pipe: PNode - s: PLLStream + var s: PLLStream p.skin = skinStandard - pipe = parsePipe(filename, inputStream) + var pipe = parsePipe(filename, inputStream) if pipe != nil: s = evalPipe(p, pipe, filename, inputStream) else: s = inputStream case p.skin - of skinStandard, skinBraces, skinEndX: pnimsyn.openParser(p.parser, filename, - s) + of skinStandard, skinBraces, skinEndX: + pnimsyn.openParser(p.parser, filename, s) proc closeParsers(p: var TParsers) = pnimsyn.closeParser(p.parser) diff --git a/rod/types.nim b/rod/types.nim index b1c07178b..ed9d5271a 100755 --- a/rod/types.nim +++ b/rod/types.nim @@ -502,7 +502,7 @@ proc base(t: PType): PType = proc firstOrd(t: PType): biggestInt = case t.kind - of tyBool, tyChar, tySequence, tyOpenArray: + of tyBool, tyChar, tySequence, tyOpenArray, tyString: result = 0 of tySet, tyVar: result = firstOrd(t.sons[0]) diff --git a/tests/accept/compile/tenum3.nim b/tests/accept/compile/tenum3.nim new file mode 100644 index 000000000..09a516932 --- /dev/null +++ b/tests/accept/compile/tenum3.nim @@ -0,0 +1,16 @@ +# Test enum with explicit size + +type + TEnumHole {.size: sizeof(int).} = enum + eA = 0, + eB = 4, + eC = 5 + +var + e: TEnumHole = eB + +case e +of eA: echo "A" +of eB: echo "B" +of eC: echo "C" + diff --git a/tests/accept/run/spec.csv b/tests/accept/run/spec.csv index f176b5645..ec98fa70e 100755 --- a/tests/accept/run/spec.csv +++ b/tests/accept/run/spec.csv @@ -3,6 +3,7 @@ tambsym2.nim;7 tambsys.nim; tarray.nim;10012 tarray2.nim;[16, 25, 36] +tarray3.nim;3 tassert.nim;assertion failure!this shall be always written tbind1.nim;3 tbind3.nim;1 diff --git a/tests/accept/run/tarray3.nim b/tests/accept/run/tarray3.nim new file mode 100644 index 000000000..881bb7ba4 --- /dev/null +++ b/tests/accept/run/tarray3.nim @@ -0,0 +1,7 @@ +# simple check for two dimensional arrays + +const + myData = [[1,2,3], [4, 5, 6]] + +echo myData[0][2] #OUT 3 + diff --git a/todo.txt b/todo.txt new file mode 100755 index 000000000..553efb884 --- /dev/null +++ b/todo.txt @@ -0,0 +1,155 @@ +For version 0.8.10 +================== + +- fix exception handling +- fix implicit generic routines +- fix the streams implementation so that they use methods + + +TO IMPLEMENT +============ + +* fix overloading resolution +* wrong co-/contravariance +* startsWith `=^` +* endsWith `=$` +* ignore case `=?` --> `=$?` + +Bugs +---- +- proc (x: int) is passable to proc (x: var int) !? +- detected by pegs module 64bit: p(result, result) should use a temporary! +- seq[TLoc] in C code generator always failed --> probably some reference + counting is wrong; try to reproduce this in the GC test +- the parser allows empty object case branches +- SDL event handling +- accurate file/line information + + +To implement +------------ + +* icon installation for the Windows installer +* sort routine +* hash tables and sets +* the two other parsers +* distinct types for array/seq indexes +* constant sequences +* IMPLEMENT GENERIC TYPES! + - implement expr parameters + - document generic types better + +* implement closures for the C code generator + + + +Low priority +------------ +- resizing of strings/sequences could take into account the memory that + is allocated +- implicit conversions from ``ptr/ref T`` to ``var T`` (from + ``ptr/ref T`` to ``T``)? +- documentation: type converters +- typeAllowed() for parameters... +- find a way to reintroduce the cleanup() pass for C code generation: this + is hard because of partial evaluation --> symbol files will fix this as + a side effect +- implement better error handling: errornous top level statements are ignored + and not part of the symbal table --> for REPL + --> this needs deletion operation for symbol table! +- floating point checks for EcmaScript +- enhance `` notation for identifier concatenation: `concat` a `these` +- prefer proc in current module over other procs with same overloading result? +- real types for template results +- generalized case statement (requires better transf) +- normalize for the DOM +- script layer! +- tlastmod returns wrong results on BSD (Linux, MacOS X: works) +- nested tuple unpacking + + +Library +------- + +- float formatting +- locale support +- conversion between character sets +- bignums +- ftp, smtp (and other internet protocols) + +- finish json module: use coroutines for this! + +- pdcurses bindings +- automate module: expect-like module for Nimrod + +- for system: + proc `@` [T](a: openArray[T]): seq[T] = + newSeq(result, a.len) + for i in 0..a.len-1: result[i] = a[i] + + --> ensure @[] calls the array version! + + +For the next versions +===================== + +- support for generation of dynamic libraries + + +Further ideas/nice to have +========================== + +- queues additional to streams: have two positions (read/write) instead of one + + +Version 2 +--------- + +- language change: inheritance should only work with reference types, so that + the ``type`` field is not needed for objects! --> zero overhead aggregation + BETTER: ``is`` and safe object conversions only work with ref objects. Same + for multi methods. + +- explicit nil types? + * nil seq[int] + * nil string + * nil ref int + * nil ptr THallo + * nil proc + +.. code-block:: nimrod + var + x: string = nil # initialized with invalid value! + if not isNil(x): + # now x + +- better for backwards compatibility: default nilable, but ``not nil`` + notation:: + + type + PWindow = ref TWindow not nil + + +Low priority +------------ + +- ``when T is int`` for generic code +- ``when validCode( proc () )`` for generic code + + + when compiles: + + elif compiles: + + +- macros: ``typecheck`` pragma; this allows transformations based on types! +- find a way for easy constructors and destructors; (destructors are much more + important than constructors) +- code generated for type information is wasteful + + +RST +--- +- footnotes; prefix :i: whitespace before :i:, _reference, `reference`__ + __ anonymous: www.nimrod.org + diff --git a/tools/build.tmpl b/tools/build.tmpl index a18831f95..5ba3ca755 100755 --- a/tools/build.tmpl +++ b/tools/build.tmpl @@ -55,7 +55,7 @@ case $ucpu in mycpu="amd64" ;; *sparc*|*sun* ) mycpu="sparc" ;; - *power* ) + *power*|*Power* ) mycpu="powerpc" ;; *mips* ) mycpu="mips" ;; diff --git a/web/news.txt b/web/news.txt index 301cd9ead..27f37b374 100755 --- a/web/news.txt +++ b/web/news.txt @@ -16,6 +16,8 @@ Bugfixes - Bugfix: ``dialogs.ChooseFileToSave`` uses ``STOCK_SAVE`` instead of ``STOCK_OPEN`` for the GTK backend. - Bugfix: ``raise`` within an exception handler did not work. +- Bugfix: ``low(somestring)`` crashed the compiler. +- Bugfix: ``strutils.endsWith`` lacked range checking. Changes affecting backwards compatibility @@ -37,6 +39,8 @@ Additions - Added ``times.epochTime`` and ``times.cpuTime``. - Implemented explicit type arguments for generics. - Implemented implicit type arguments for generics. +- Implemented ``{.size: sizeof(cint).}`` pragma for enum types. This is useful + for interfacing with C. 2010-03-14 Version 0.8.8 released diff --git a/web/question.txt b/web/question.txt index bce9c0c74..6d029eae5 100755 --- a/web/question.txt +++ b/web/question.txt @@ -58,9 +58,29 @@ to write as low-level Nimrod code as C requires you to do. That said the only overhead Nimrod has over C is the GC which has been tuned for years. +What about JVM/CLR backends? +---------------------------- + +A JVM backend is almost impossible. The JVM is not expressive enough. It has +never been designed as a general purpose VM anyway. A CLR backend is possible +but would require much work. + + Compilation =========== +Which option to use for the fastest executable? +----------------------------------------------- + +For the standard configuration file, ``-d:release`` does the trick. + + +Which option to use for the smallest executable? +------------------------------------------------ + +For the standard configuration file, ``-d:quick --opt:size`` does the trick. + + Execution of GCC fails (Windows) -------------------------------- |