c2nim tool added

This commit is contained in:
Andreas Rumpf
2010-07-21 09:44:47 +02:00
parent c441cdb64c
commit d10973adb0
28 changed files with 3498 additions and 66 deletions

View File

@@ -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:

68
lib/impure/osinfo_posix.nim Executable file
View File

@@ -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)

399
lib/impure/osinfo_win.nim Executable file
View File

@@ -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]))

View File

@@ -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))

View File

@@ -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)

View File

@@ -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 =

74
rod/c2nim/c2nim.nim Executable file
View File

@@ -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)

751
rod/c2nim/clex.nim Executable file
View File

@@ -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)

1469
rod/c2nim/cparse.nim Executable file

File diff suppressed because it is too large Load Diff

231
rod/c2nim/cpp.nim Executable file
View File

@@ -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)

235
rod/c2nim/manual.txt Normal file
View File

@@ -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...

View File

@@ -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, "||")

View File

@@ -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))])

View File

@@ -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)

View File

@@ -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)

View File

@@ -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")

View File

@@ -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)

View File

@@ -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)

View File

@@ -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))

View File

@@ -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)

View File

@@ -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])

View File

@@ -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"

View File

@@ -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
1 tack.nim 125
3 tambsys.nim
4 tarray.nim 10012
5 tarray2.nim [16, 25, 36]
6 tarray3.nim 3
7 tassert.nim assertion failure!this shall be always written
8 tbind1.nim 3
9 tbind3.nim 1

View File

@@ -0,0 +1,7 @@
# simple check for two dimensional arrays
const
myData = [[1,2,3], [4, 5, 6]]
echo myData[0][2] #OUT 3

155
todo.txt Executable file
View File

@@ -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

View File

@@ -55,7 +55,7 @@ case $ucpu in
mycpu="amd64" ;;
*sparc*|*sun* )
mycpu="sparc" ;;
*power* )
*power*|*Power* )
mycpu="powerpc" ;;
*mips* )
mycpu="mips" ;;

View File

@@ -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

View File

@@ -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)
--------------------------------