deleted web and dist
This commit is contained in:
parent
07d5a8085b
commit
972c510861
50 changed files with 4986 additions and 2289 deletions
73
lib/base/lex.nim
Normal file
73
lib/base/lex.nim
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# Lexer generator for Nimrod
|
||||
# (c) 2008 Andreas Rumpf
|
||||
|
||||
# Stress testing for the macro feature
|
||||
|
||||
# the syntax that should be supported is:
|
||||
|
||||
# template numpostfix =
|
||||
# '\'' & 'F'|'f'|'i'|'I' & "32"|"64"|"8"|"16"
|
||||
# template edigits =
|
||||
# 'e'|'E' & +digits
|
||||
# tokens(
|
||||
# tkIdent: +UniIdentStart & *UniIdentRest,
|
||||
# tkHexNumber: '0' & ('x'|'X') & +hexDigits & ?( numpostfix ),
|
||||
# tkOctNumber: '0' & ('c'|'C') & +octDigits & ?( numpostfix ),
|
||||
# tkBinNumber: '0' & ('b'|'B') & +binDigits & ?( numpostfix ),
|
||||
# tkIntNumber: +digits & ?( numpostfix ),
|
||||
# tkFloatNumber: +digits & ('.' & +digits & ?(edigits) | edigits) & ?(numpostfix),
|
||||
#
|
||||
# )
|
||||
# actions(
|
||||
# tkIdent: lookup
|
||||
# )
|
||||
#
|
||||
|
||||
#
|
||||
# match inputstream
|
||||
# of +('A'..'Z' | '_' | 'a'..'z') *('A'..'Z' | '_' | 'a'..'z' | '0'..'9') :
|
||||
#
|
||||
# x = inputstream[pos..length]
|
||||
# of '0' 'x' +('0'..'9' | 'a'..'f' | '_' | 'A'..'F') :
|
||||
# y = ...
|
||||
|
||||
const
|
||||
AsciiLetter = {'A'..'Z', 'a'..'z'}
|
||||
uniLetter = AsciiLetter + {'\128'..'\255'}
|
||||
digits = {'0'..'9'}
|
||||
hexDigits = {'0'..'9', 'a'..'f', 'A'..'F'}
|
||||
octDigits = {'0'..'7'}
|
||||
binDigits = {'0'..'1'}
|
||||
AsciiIdentStart = AsciiLetter + {'_'}
|
||||
AsciiIdentRest = AsciiIdentStart + Digits
|
||||
UniIdentStart = UniLetter + {'_'}
|
||||
UniIdentRest = UniIdentStart + Digits
|
||||
|
||||
# --> if match(s, +AsciiIdentStart & *AsciiIdentRest):
|
||||
|
||||
# Regular expressions in Nimrod itself!
|
||||
# -------------------------------------
|
||||
#
|
||||
# 'a' -- matches the character a
|
||||
# 'a'..'z' -- range operator '-'
|
||||
# 'A' | 'B' -- alternative operator |
|
||||
# * 'a' -- prefix * is needed
|
||||
# + 'a' -- prefix + is needed
|
||||
# ? 'a' -- prefix ? is needed
|
||||
# *? prefix is needed
|
||||
# +? prefix is needed
|
||||
# letter -- character classes with real names!
|
||||
# letters
|
||||
# white
|
||||
# whites
|
||||
# any -- any character
|
||||
# () -- are Nimrod syntax
|
||||
# ! 'a'-'z'
|
||||
#
|
||||
# -- concatentation via proc call:
|
||||
#
|
||||
# re('A' 'Z' *word )
|
||||
|
||||
macro re(n: expr): expr =
|
||||
|
||||
result = newCall("magic_re", x)
|
||||
12
lib/contnrs.nim
Normal file
12
lib/contnrs.nim
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Container library for Nimrod
|
||||
# Implemented with macros, because generics sucks in many ways
|
||||
|
||||
# Data structures for now:
|
||||
# TTable, TSet, TList
|
||||
# Algorithms: Trees, hashing,
|
||||
|
||||
TTable[key, val, [Algorithm]]
|
||||
|
||||
macro TTable(n: typeexpr): typeexpr =
|
||||
|
||||
|
||||
450
lib/ecmas/dom.nim
Normal file
450
lib/ecmas/dom.nim
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2006 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Declaration of the Document Object Model for the ECMAScript backend.
|
||||
## (c) 2008 Andreas Rumpf
|
||||
|
||||
when not defined(ecmascript):
|
||||
{.error: "This module only works on the ECMAScript platform".}
|
||||
|
||||
type
|
||||
TEventHandlers* {.importc.} = object of TObject
|
||||
onabort*: proc (event: ref TEvent)
|
||||
onblur*: proc (event: ref TEvent)
|
||||
onchange*: proc (event: ref TEvent)
|
||||
onclick*: proc (event: ref TEvent)
|
||||
ondblclick*: proc (event: ref TEvent)
|
||||
onerror*: proc (event: ref TEvent)
|
||||
onfocus*: proc (event: ref TEvent)
|
||||
onkeydown*: proc (event: ref TEvent)
|
||||
onkeypress*: proc (event: ref TEvent)
|
||||
onkeyup*: proc (event: ref TEvent)
|
||||
onload*: proc (event: ref TEvent)
|
||||
onmousedown*: proc (event: ref TEvent)
|
||||
onmousemove*: proc (event: ref TEvent)
|
||||
onmouseout*: proc (event: ref TEvent)
|
||||
onmouseover*: proc (event: ref TEvent)
|
||||
onmouseup*: proc (event: ref TEvent)
|
||||
onreset*: proc (event: ref TEvent)
|
||||
onselect*: proc (event: ref TEvent)
|
||||
onsubmit*: proc (event: ref TEvent)
|
||||
onunload*: proc (event: ref TEvent)
|
||||
|
||||
TWindow* {.importc.} = object of TEventHandlers
|
||||
document*: ref TDocument
|
||||
event*: ref TEvent
|
||||
history*: ref THistory
|
||||
location*: ref TLocation
|
||||
closed*: bool
|
||||
defaultStatus*: cstring
|
||||
innerHeight*, innerWidth*: int
|
||||
locationbar*: ref TLocationBar
|
||||
menubar*: ref TMenuBar
|
||||
name*: cstring
|
||||
outerHeight*, outerWidth*: int
|
||||
pageXOffset*, pageYOffset*: int
|
||||
personalbar*: ref TPersonalBar
|
||||
scrollbars*: ref TScrollBars
|
||||
statusbar*: ref TStatusBar
|
||||
status*: cstring
|
||||
toolbar*: ref TToolBar
|
||||
|
||||
alert*: proc (msg: cstring)
|
||||
back*: proc ()
|
||||
blur*: proc ()
|
||||
captureEvents*: proc (eventMask: int)
|
||||
clearInterval*: proc (interval: ref TInterval)
|
||||
clearTimeout*: proc (timeout: ref TTimeOut)
|
||||
close*: proc ()
|
||||
confirm*: proc (msg: cstring): bool
|
||||
disableExternalCapture*: proc ()
|
||||
enableExternalCapture*: proc ()
|
||||
find*: proc (text: cstring, caseSensitive = false, backwards = false)
|
||||
focus*: proc ()
|
||||
forward*: proc ()
|
||||
handleEvent*: proc (e: ref TEvent)
|
||||
home*: proc ()
|
||||
moveBy*: proc (x, y: int)
|
||||
moveTo*: proc (x, y: int)
|
||||
open*: proc (uri, windowname: cstring,
|
||||
properties: cstring = nil): ref TWindow
|
||||
print*: proc ()
|
||||
prompt*: proc (text, default: cstring): cstring
|
||||
releaseEvents*: proc (eventMask: int)
|
||||
resizeBy*: proc (x, y: int)
|
||||
resizeTo*: proc (x, y: int)
|
||||
routeEvent*: proc (event: ref TEvent)
|
||||
scrollBy*: proc (x, y: int)
|
||||
scrollTo*: proc (x, y: int)
|
||||
setInterval*: proc (code: cstring, pause: int): ref TInterval
|
||||
setTimeout*: proc (code: cstring, pause: int): ref TTimeOut
|
||||
stop*: proc ()
|
||||
frames*: seq[TFrame]
|
||||
|
||||
TFrame* {.importc.} = object of TWindow
|
||||
|
||||
TDocument* {.importc.} = object of TEventHandlers
|
||||
alinkColor*: cstring
|
||||
bgColor*: cstring
|
||||
charset*: cstring
|
||||
cookie*: cstring
|
||||
defaultCharset*: cstring
|
||||
fgColor*: cstring
|
||||
lastModified*: cstring
|
||||
linkColor*: cstring
|
||||
referrer*: cstring
|
||||
title*: cstring
|
||||
URL*: cstring
|
||||
vlinkColor*: cstring
|
||||
captureEvents*: proc (eventMask: int)
|
||||
createAttribute*: proc (identifier: cstring): ref TNode
|
||||
createElement*: proc (identifier: cstring): ref TNode
|
||||
createTextNode*: proc (identifier: cstring): ref TNode
|
||||
getElementById*: proc (id: cstring): ref TNode
|
||||
getElementsByName*: proc (name: cstring): seq[ref TNode]
|
||||
getElementsByTagName*: proc (name: cstring): seq[ref TNode]
|
||||
getSelection*: proc (): cstring
|
||||
handleEvent*: proc (event: ref TEvent)
|
||||
open*: proc ()
|
||||
releaseEvents*: proc (eventMask: int)
|
||||
routeEvent*: proc (event: ref TEvent)
|
||||
write*: proc (text: cstring)
|
||||
writeln*: proc (text: cstring)
|
||||
anchors*: seq[ref TAnchor]
|
||||
forms*: seq[ref TForm]
|
||||
images*: seq[ref TImage]
|
||||
applets*: seq[ref TApplet]
|
||||
embeds*: seq[ref TEmbed]
|
||||
links*: seq[ref TLink]
|
||||
|
||||
TLink* {.importc.} = object of TObject
|
||||
name*: cstring
|
||||
target*: cstring
|
||||
text*: cstring
|
||||
x*: int
|
||||
y*: int
|
||||
|
||||
TEmbed* {.importc.} = object of TObject
|
||||
height*: int
|
||||
hspace*: int
|
||||
name*: cstring
|
||||
src*: cstring
|
||||
width*: int
|
||||
`type`*: cstring
|
||||
vspace*: int
|
||||
play*: proc ()
|
||||
stop*: proc ()
|
||||
|
||||
TAnchor* {.importc.} = object of TObject
|
||||
name*: cstring
|
||||
text*: cstring
|
||||
x*, y*: int
|
||||
|
||||
TApplet* {.importc.} = object of TObject
|
||||
|
||||
TElement* {.importc.} = object of TEventHandlers
|
||||
checked*: bool
|
||||
defaultChecked*: bool
|
||||
defaultValue*: cstring
|
||||
disabled*: bool
|
||||
form*: ref TForm
|
||||
name*: cstring
|
||||
readOnly*: bool
|
||||
`type`*: cstring
|
||||
value*: cstring
|
||||
blur*: proc ()
|
||||
click*: proc ()
|
||||
focus*: proc ()
|
||||
handleEvent*: proc (event: ref TEvent)
|
||||
select*: proc ()
|
||||
options*: seq[ref TOption]
|
||||
|
||||
TOption* {.importc.} = object of TObject
|
||||
defaultSelected*: bool
|
||||
selected*: bool
|
||||
selectedIndex*: int
|
||||
text*: cstring
|
||||
value*: cstring
|
||||
|
||||
TForm* {.importc.} = object of TEventHandlers
|
||||
action*: cstring
|
||||
encoding*: cstring
|
||||
`method`*: cstring
|
||||
name*: cstring
|
||||
target*: cstring
|
||||
handleEvent*: proc (event: ref TEvent)
|
||||
reset*: proc ()
|
||||
submit*: proc ()
|
||||
elements*: seq[ref TElement]
|
||||
|
||||
TImage* {.importc.} = object of TEventHandlers
|
||||
border*: int
|
||||
complete*: bool
|
||||
height*: int
|
||||
hspace*: int
|
||||
lowsrc*: cstring
|
||||
name*: cstring
|
||||
src*: cstring
|
||||
vspace*: int
|
||||
width*: int
|
||||
handleEvent*: proc (event: ref TEvent)
|
||||
|
||||
TNodeType* = enum
|
||||
ElementNode = 1,
|
||||
AttributeNode,
|
||||
TextNode,
|
||||
CDATANode,
|
||||
EntityRefNode,
|
||||
EntityNode,
|
||||
ProcessingInstructionNode,
|
||||
CommentNode,
|
||||
DocumentNode,
|
||||
DocumentTypeNode,
|
||||
DocumentFragmentNode,
|
||||
NotationNode
|
||||
TNode* {.importc.} = object of TObject
|
||||
attributes*: seq[ref TNode]
|
||||
childNodes*: seq[ref TNode]
|
||||
data*: cstring
|
||||
firstChild*: ref TNode
|
||||
lastChild*: ref TNode
|
||||
nextSibling*: ref TNode
|
||||
nodeName*: cstring
|
||||
nodeType*: TNodeType
|
||||
nodeValue*: cstring
|
||||
parentNode*: ref TNode
|
||||
previousSibling*: ref TNode
|
||||
appendChild*: proc (child: ref TNode)
|
||||
appendData*: proc (data: cstring)
|
||||
cloneNode*: proc (copyContent: bool)
|
||||
deleteData*: proc (start, len: int)
|
||||
getAttribute*: proc (attr: cstring): cstring
|
||||
getAttributeNode*: proc (attr: cstring): ref TNode
|
||||
getElementsByTagName*: proc (): seq[ref TNode]
|
||||
hasChildNodes*: proc (): bool
|
||||
insertBefore*: proc (newNode, before: ref TNode)
|
||||
insertData*: proc (position: int, data: cstring)
|
||||
removeAttribute*: proc (attr: cstring)
|
||||
removeAttributeNode*: proc (attr: ref TNode)
|
||||
removeChild*: proc (child: ref TNode)
|
||||
replaceChild*: proc (newNode, oldNode: ref TNode)
|
||||
replaceData*: proc (start, len: int, text: cstring)
|
||||
setAttribute*: proc (name, value: cstring)
|
||||
setAttributeNode*: proc (attr: ref TNode)
|
||||
style*: ref TStyle
|
||||
|
||||
TStyle* {.importc.} = object of TObject
|
||||
background*: cstring
|
||||
backgroundAttachment*: cstring
|
||||
backgroundColor*: cstring
|
||||
backgroundImage*: cstring
|
||||
backgroundPosition*: cstring
|
||||
backgroundRepeat*: cstring
|
||||
border*: cstring
|
||||
borderBottom*: cstring
|
||||
borderBottomColor*: cstring
|
||||
borderBottomStyle*: cstring
|
||||
borderBottomWidth*: cstring
|
||||
borderColor*: cstring
|
||||
borderLeft*: cstring
|
||||
borderLeftColor*: cstring
|
||||
borderLeftStyle*: cstring
|
||||
borderLeftWidth*: cstring
|
||||
borderRight*: cstring
|
||||
borderRightColor*: cstring
|
||||
borderRightStyle*: cstring
|
||||
borderRightWidth*: cstring
|
||||
borderStyle*: cstring
|
||||
borderTop*: cstring
|
||||
borderTopColor*: cstring
|
||||
borderTopStyle*: cstring
|
||||
borderTopWidth*: cstring
|
||||
borderWidth*: cstring
|
||||
bottom*: cstring
|
||||
captionSide*: cstring
|
||||
clear*: cstring
|
||||
clip*: cstring
|
||||
color*: cstring
|
||||
cursor*: cstring
|
||||
direction*: cstring
|
||||
display*: cstring
|
||||
emptyCells*: cstring
|
||||
cssFloat*: cstring
|
||||
font*: cstring
|
||||
fontFamily*: cstring
|
||||
fontSize*: cstring
|
||||
fontStretch*: cstring
|
||||
fontStyle*: cstring
|
||||
fontVariant*: cstring
|
||||
fontWeight*: cstring
|
||||
height*: cstring
|
||||
left*: cstring
|
||||
letterSpacing*: cstring
|
||||
lineHeight*: cstring
|
||||
listStyle*: cstring
|
||||
listStyleImage*: cstring
|
||||
listStylePosition*: cstring
|
||||
listStyleType*: cstring
|
||||
margin*: cstring
|
||||
marginBottom*: cstring
|
||||
marginLeft*: cstring
|
||||
marginRight*: cstring
|
||||
marginTop*: cstring
|
||||
maxHeight*: cstring
|
||||
maxWidth*: cstring
|
||||
minHeight*: cstring
|
||||
minWidth*: cstring
|
||||
overflow*: cstring
|
||||
padding*: cstring
|
||||
paddingBottom*: cstring
|
||||
paddingLeft*: cstring
|
||||
paddingRight*: cstring
|
||||
paddingTop*: cstring
|
||||
pageBreakAfter*: cstring
|
||||
pageBreakBefore*: cstring
|
||||
position*: cstring
|
||||
right*: cstring
|
||||
scrollbar3dLightColor*: cstring
|
||||
scrollbarArrowColor*: cstring
|
||||
scrollbarBaseColor*: cstring
|
||||
scrollbarDarkshadowColor*: cstring
|
||||
scrollbarFaceColor*: cstring
|
||||
scrollbarHighlightColor*: cstring
|
||||
scrollbarShadowColor*: cstring
|
||||
scrollbarTrackColor*: cstring
|
||||
tableLayout*: cstring
|
||||
textAlign*: cstring
|
||||
textDecoration*: cstring
|
||||
textIndent*: cstring
|
||||
textTransform*: cstring
|
||||
top*: cstring
|
||||
verticalAlign*: cstring
|
||||
visibility*: cstring
|
||||
width*: cstring
|
||||
wordSpacing*: cstring
|
||||
zIndex*: int
|
||||
getAttribute*: proc (attr: cstring, caseSensitive=false): cstring
|
||||
removeAttribute*: proc (attr: cstring, caseSensitive=false)
|
||||
setAttribute*: proc (attr, value: cstring, caseSensitive=false)
|
||||
|
||||
TEvent* {.importc.} = object of TObject
|
||||
altKey*, ctrlKey*, shiftKey*: bool
|
||||
button*: int
|
||||
clientX*, clientY*: int
|
||||
keyCode*: int
|
||||
layerX*, layerY*: int
|
||||
modifiers*: int
|
||||
ALT_MASK*, CONTROL_MASK*, SHIFT_MASK*, META_MASK*: int
|
||||
offsetX*, offsetY*: int
|
||||
pageX*, pageY*: int
|
||||
screenX*, screenY*: int
|
||||
which*: int
|
||||
`type`*: cstring
|
||||
x*, y*: int
|
||||
ABORT*: int
|
||||
BLUR*: int
|
||||
CHANGE*: int
|
||||
CLICK*: int
|
||||
DBLCLICK*: int
|
||||
DRAGDROP*: int
|
||||
ERROR*: int
|
||||
FOCUS*: int
|
||||
KEYDOWN*: int
|
||||
KEYPRESS*: int
|
||||
KEYUP*: int
|
||||
LOAD*: int
|
||||
MOUSEDOWN*: int
|
||||
MOUSEMOVE*: int
|
||||
MOUSEOUT*: int
|
||||
MOUSEOVER*: int
|
||||
MOUSEUP*: int
|
||||
MOVE*: int
|
||||
RESET*: int
|
||||
RESIZE*: int
|
||||
SELECT*: int
|
||||
SUBMIT*: int
|
||||
UNLOAD*: int
|
||||
|
||||
TLocation* {.importc.} = object of TObject
|
||||
hash*: cstring
|
||||
host*: cstring
|
||||
hostname*: cstring
|
||||
href*: cstring
|
||||
pathname*: cstring
|
||||
port*: cstring
|
||||
protocol*: cstring
|
||||
search*: cstring
|
||||
reload*: proc ()
|
||||
replace*: proc (s: cstring)
|
||||
|
||||
THistory* {.importc.} = object of TObject
|
||||
length*: int
|
||||
back*: proc ()
|
||||
forward*: proc ()
|
||||
go*: proc (pagesToJump: int)
|
||||
|
||||
TNavigator* {.importc.} = object of TObject
|
||||
appCodeName*: cstring
|
||||
appName*: cstring
|
||||
appVersion*: cstring
|
||||
cookieEnabled*: bool
|
||||
language*: cstring
|
||||
platform*: cstring
|
||||
userAgent*: cstring
|
||||
javaEnabled*: proc (): bool
|
||||
mimeTypes*: seq[ref TMimeType]
|
||||
|
||||
TPlugin* {.importc.} = object of TObject
|
||||
description*: cstring
|
||||
filename*: cstring
|
||||
name*: cstring
|
||||
|
||||
TMimeType* {.importc.} = object of TObject
|
||||
description*: cstring
|
||||
enabledPlugin*: ref TPlugin
|
||||
suffixes*: seq[cstring]
|
||||
`type`*: cstring
|
||||
|
||||
TLocationBar* {.importc.} = object of TObject
|
||||
visible*: bool
|
||||
TMenuBar* = TLocationBar
|
||||
TPersonalBar* = TLocationBar
|
||||
TScrollBars* = TLocationBar
|
||||
TToolBar* = TLocationBar
|
||||
TStatusBar* = TLocationBar
|
||||
|
||||
TScreen* {.importc.} = object of TObject
|
||||
availHeight*: int
|
||||
availWidth*: int
|
||||
colorDepth*: int
|
||||
height*: int
|
||||
pixelDepth*: int
|
||||
width*: int
|
||||
|
||||
TTimeOut* {.importc.} = object of TObject
|
||||
TInterval* {.importc.} = object of TObject
|
||||
|
||||
var
|
||||
window* {.importc, nodecl.}: ref TWindow
|
||||
document* {.importc, nodecl.}: ref TDocument
|
||||
navigator* {.importc, nodecl.}: ref TNavigator
|
||||
screen* {.importc, nodecl.}: ref TScreen
|
||||
|
||||
proc decodeURI*(uri: cstring): cstring {.importc, nodecl.}
|
||||
proc encodeURI*(uri: cstring): cstring {.importc, nodecl.}
|
||||
|
||||
proc escape*(uri: cstring): cstring {.importc, nodecl.}
|
||||
proc unescape*(uri: cstring): cstring {.importc, nodecl.}
|
||||
|
||||
proc decodeURIComponent*(uri: cstring): cstring {.importc, nodecl.}
|
||||
proc encodeURIComponent*(uri: cstring): cstring {.importc, nodecl.}
|
||||
proc isFinite(x: biggestFloat): bool {.importc, nodecl.}
|
||||
proc isNaN(x: biggestFloat): bool {.importc, nodecl.}
|
||||
proc parseFloat(s: cstring): biggestFloat {.importc, nodecl.}
|
||||
proc parseInt(s: cstring): int {.importc, nodecl.}
|
||||
527
lib/ecmasys.nim
Normal file
527
lib/ecmasys.nim
Normal file
|
|
@ -0,0 +1,527 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Stubs for the GC interface:
|
||||
|
||||
proc GC_disable() = nil
|
||||
proc GC_enable() = nil
|
||||
proc GC_fullCollect() = nil
|
||||
proc GC_setStrategy(strategy: TGC_Strategy) = nil
|
||||
proc GC_enableMarkAndSweep() = nil
|
||||
proc GC_disableMarkAndSweep() = nil
|
||||
|
||||
proc getOccupiedMem(): int = return -1
|
||||
proc getFreeMem(): int = return -1
|
||||
proc getTotalMem(): int = return -1
|
||||
|
||||
proc alert(s: cstring) {.importc, nodecl.}
|
||||
|
||||
type
|
||||
PSafePoint = ptr TSafePoint
|
||||
TSafePoint {.compilerproc, final.} = object
|
||||
prev: PSafePoint # points to next safe point
|
||||
exc: ref E_Base
|
||||
|
||||
PCallFrame = ptr TCallFrame
|
||||
TCallFrame {.importc, nodecl, final.} = object
|
||||
prev: PCallFrame
|
||||
procname: CString
|
||||
line: int # current line number
|
||||
filename: CString
|
||||
|
||||
var
|
||||
framePtr {.importc, nodecl, volatile.}: PCallFrame
|
||||
excHandler {.importc, nodecl, volatile.}: PSafePoint = nil
|
||||
# list of exception handlers
|
||||
# a global variable for the root of all try blocks
|
||||
|
||||
{.push stacktrace: off.}
|
||||
proc nimBoolToStr(x: bool): string {.compilerproc.} =
|
||||
if x: result = "true"
|
||||
else: result = "false"
|
||||
|
||||
proc nimCharToStr(x: char): string {.compilerproc.} =
|
||||
result = newString(1)
|
||||
result[0] = x
|
||||
|
||||
proc getCurrentExceptionMsg(): string =
|
||||
if excHandler != nil: return $excHandler.exc.msg
|
||||
return ""
|
||||
|
||||
proc auxWriteStackTrace(f: PCallFrame): string =
|
||||
type
|
||||
TTempFrame = tuple[procname: CString, line: int]
|
||||
var
|
||||
it = f
|
||||
i = 0
|
||||
total = 0
|
||||
tempFrames: array [0..63, TTempFrame]
|
||||
while it != nil and i <= high(tempFrames):
|
||||
tempFrames[i].procname = it.procname
|
||||
tempFrames[i].line = it.line
|
||||
inc(i)
|
||||
inc(total)
|
||||
it = it.prev
|
||||
while it != nil:
|
||||
inc(total)
|
||||
it = it.prev
|
||||
result = ""
|
||||
# if the buffer overflowed print '...':
|
||||
if total != i:
|
||||
add(result, "(")
|
||||
add(result, $(total-i))
|
||||
add(result, " calls omitted) ...\n")
|
||||
for j in countdown(i-1, 0):
|
||||
add(result, tempFrames[j].procname)
|
||||
if tempFrames[j].line > 0:
|
||||
add(result, ", line: ")
|
||||
add(result, $tempFrames[j].line)
|
||||
add(result, "\n")
|
||||
|
||||
proc rawWriteStackTrace(): string =
|
||||
if framePtr == nil:
|
||||
result = "No stack traceback available\n"
|
||||
else:
|
||||
result = "Traceback (most recent call last)\n"& auxWriteStackTrace(framePtr)
|
||||
framePtr = nil
|
||||
|
||||
proc raiseException(e: ref E_Base, ename: cstring) {.compilerproc, pure.} =
|
||||
e.name = ename
|
||||
if excHandler != nil:
|
||||
excHandler.exc = e
|
||||
else:
|
||||
var buf = rawWriteStackTrace()
|
||||
if e.msg != nil and e.msg[0] != '\0':
|
||||
add(buf, "Error: unhandled exception: ")
|
||||
add(buf, e.msg)
|
||||
else:
|
||||
add(buf, "Error: unhandled exception")
|
||||
add(buf, " [")
|
||||
add(buf, ename)
|
||||
add(buf, "]\n")
|
||||
alert(buf)
|
||||
asm """throw `e`;"""
|
||||
|
||||
proc reraiseException() =
|
||||
if excHandler == nil:
|
||||
raise newException(ENoExceptionToReraise, "no exception to reraise")
|
||||
else:
|
||||
asm """throw excHandler.exc;"""
|
||||
|
||||
proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} =
|
||||
raise newException(EOverflow, "over- or underflow")
|
||||
|
||||
proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn.} =
|
||||
raise newException(EDivByZero, "divison by zero")
|
||||
|
||||
proc raiseRangeError() {.compilerproc, noreturn.} =
|
||||
raise newException(EOutOfRange, "value out of range")
|
||||
|
||||
proc raiseIndexError() {.compilerproc, noreturn.} =
|
||||
raise newException(EInvalidIndex, "index out of bounds")
|
||||
|
||||
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
|
||||
raise newException(EInvalidField, f & " is not accessible")
|
||||
|
||||
|
||||
|
||||
proc SetConstr() {.varargs, pure, compilerproc.} =
|
||||
asm """
|
||||
var result = {};
|
||||
for (var i = 0; i < arguments.length; ++i) {
|
||||
var x = arguments[i];
|
||||
if (typeof(x) == "object") {
|
||||
for (var j = x[0]; j <= x[1]; ++j) {
|
||||
result[j] = true;
|
||||
}
|
||||
} else {
|
||||
result[x] = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc cstrToNimstr(c: cstring): string {.pure, compilerproc.} =
|
||||
asm """
|
||||
var result = [];
|
||||
for (var i = 0; i < `c`.length; ++i) {
|
||||
result[i] = `c`.charCodeAt(i);
|
||||
}
|
||||
result[result.length] = 0; // terminating zero
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc toEcmaStr(s: string): cstring {.pure, compilerproc.} =
|
||||
asm """
|
||||
var len = `s`.length-1;
|
||||
var result = new Array(len);
|
||||
var fcc = String.fromCharCode;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
result[i] = fcc(`s`[i]);
|
||||
}
|
||||
return result.join("");
|
||||
"""
|
||||
|
||||
proc mnewString(len: int): string {.pure, compilerproc.} =
|
||||
asm """
|
||||
var result = new Array(`len`+1);
|
||||
result[0] = 0;
|
||||
result[`len`] = 0;
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc SetCard(a: int): int {.compilerproc, pure.} =
|
||||
# argument type is a fake
|
||||
asm """
|
||||
var result = 0;
|
||||
for (var elem in `a`) { ++result; }
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc SetEq(a, b: int): bool {.compilerproc, pure.} =
|
||||
asm """
|
||||
for (var elem in `a`) { if (!`b`[elem]) return false; }
|
||||
for (var elem in `b`) { if (!`a`[elem]) return false; }
|
||||
return true;
|
||||
"""
|
||||
|
||||
proc SetLe(a, b: int): bool {.compilerproc, pure.} =
|
||||
asm """
|
||||
for (var elem in `a`) { if (!`b`[elem]) return false; }
|
||||
return true;
|
||||
"""
|
||||
|
||||
proc SetLt(a, b: int): bool {.compilerproc.} =
|
||||
result = SetLe(a, b) and not SetEq(a, b)
|
||||
|
||||
proc SetMul(a, b: int): int {.compilerproc, pure.} =
|
||||
asm """
|
||||
var result = {};
|
||||
for (var elem in `a`) {
|
||||
if (`b`[elem]) { result[elem] = true; }
|
||||
}
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc SetPlus(a, b: int): int {.compilerproc, pure.} =
|
||||
asm """
|
||||
var result = {};
|
||||
for (var elem in `a`) { result[elem] = true; }
|
||||
for (var elem in `b`) { result[elem] = true; }
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc SetMinus(a, b: int): int {.compilerproc, pure.} =
|
||||
asm """
|
||||
var result = {};
|
||||
for (var elem in `a`) {
|
||||
if (!`b`[elem]) { result[elem] = true; }
|
||||
}
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc cmpStrings(a, b: string): int {.pure, compilerProc.} =
|
||||
asm """
|
||||
if (`a` == `b`) return 0;
|
||||
if (!`a`) return -1;
|
||||
if (!`b`) return 1;
|
||||
for (var i = 0; i < `a`.length-1; ++i) {
|
||||
var result = `a`[i] - `b`[i];
|
||||
if (result != 0) return result;
|
||||
}
|
||||
return 0;
|
||||
"""
|
||||
|
||||
proc cmp(x, y: string): int = return cmpStrings(x, y)
|
||||
|
||||
proc eqStrings(a, b: string): bool {.pure, compilerProc.} =
|
||||
asm """
|
||||
if (`a == `b`) return true;
|
||||
if ((!`a`) || (!`b`)) return false;
|
||||
var alen = `a`.length;
|
||||
if (alen != `b`.length) return false;
|
||||
for (var i = 0; i < alen; ++i)
|
||||
if (`a`[i] != `b`[i]) return false;
|
||||
return true;
|
||||
"""
|
||||
|
||||
type
|
||||
TDocument {.importc.} = object of TObject
|
||||
write: proc (text: cstring)
|
||||
writeln: proc (text: cstring)
|
||||
createAttribute: proc (identifier: cstring): ref TNode
|
||||
createElement: proc (identifier: cstring): ref TNode
|
||||
createTextNode: proc (identifier: cstring): ref TNode
|
||||
getElementById: proc (id: cstring): ref TNode
|
||||
getElementsByName: proc (name: cstring): seq[ref TNode]
|
||||
getElementsByTagName: proc (name: cstring): seq[ref TNode]
|
||||
|
||||
TNodeType* = enum
|
||||
ElementNode = 1,
|
||||
AttributeNode,
|
||||
TextNode,
|
||||
CDATANode,
|
||||
EntityRefNode,
|
||||
EntityNode,
|
||||
ProcessingInstructionNode,
|
||||
CommentNode,
|
||||
DocumentNode,
|
||||
DocumentTypeNode,
|
||||
DocumentFragmentNode,
|
||||
NotationNode
|
||||
TNode* {.importc.} = object of TObject
|
||||
attributes*: seq[ref TNode]
|
||||
childNodes*: seq[ref TNode]
|
||||
data*: cstring
|
||||
firstChild*: ref TNode
|
||||
lastChild*: ref TNode
|
||||
nextSibling*: ref TNode
|
||||
nodeName*: cstring
|
||||
nodeType*: TNodeType
|
||||
nodeValue*: cstring
|
||||
parentNode*: ref TNode
|
||||
previousSibling*: ref TNode
|
||||
appendChild*: proc (child: ref TNode)
|
||||
appendData*: proc (data: cstring)
|
||||
cloneNode*: proc (copyContent: bool)
|
||||
deleteData*: proc (start, len: int)
|
||||
getAttribute*: proc (attr: cstring): cstring
|
||||
getAttributeNode*: proc (attr: cstring): ref TNode
|
||||
getElementsByTagName*: proc (): seq[ref TNode]
|
||||
hasChildNodes*: proc (): bool
|
||||
insertBefore*: proc (newNode, before: ref TNode)
|
||||
insertData*: proc (position: int, data: cstring)
|
||||
removeAttribute*: proc (attr: cstring)
|
||||
removeAttributeNode*: proc (attr: ref TNode)
|
||||
removeChild*: proc (child: ref TNode)
|
||||
replaceChild*: proc (newNode, oldNode: ref TNode)
|
||||
replaceData*: proc (start, len: int, text: cstring)
|
||||
setAttribute*: proc (name, value: cstring)
|
||||
setAttributeNode*: proc (attr: ref TNode)
|
||||
|
||||
var
|
||||
document {.importc, nodecl.}: ref TDocument
|
||||
|
||||
proc ewriteln(x: cstring) =
|
||||
var node = document.getElementsByTagName("body")[0]
|
||||
if node != nil:
|
||||
node.appendChild(document.createTextNode(x))
|
||||
node.appendChild(document.createElement("br"))
|
||||
else:
|
||||
raise newException(EInvalidValue, "<body> element does not exist yet!")
|
||||
|
||||
proc echo*(x: int) = ewriteln($x)
|
||||
proc echo*(x: float) = ewriteln($x)
|
||||
proc echo*(x: bool) = ewriteln(if x: cstring("true") else: cstring("false"))
|
||||
proc echo*(x: string) = ewriteln(x)
|
||||
proc echo*(x: cstring) = ewriteln(x)
|
||||
|
||||
proc echo[Ty](x: Ty) =
|
||||
echo(x)
|
||||
|
||||
# Arithmetic:
|
||||
proc addInt(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
var result = `a` + `b`;
|
||||
if (result > 2147483647 || result < -2147483648) raiseOverflow();
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc subInt(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
var result = `a` - `b`;
|
||||
if (result > 2147483647 || result < -2147483648) raiseOverflow();
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc mulInt(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
var result = `a` * `b`;
|
||||
if (result > 2147483647 || result < -2147483648) raiseOverflow();
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc divInt(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
if (`b` == 0) raiseDivByZero();
|
||||
if (`b` == -1 && `a` == 2147483647) raiseOverflow();
|
||||
return Math.floor(`a` / `b`);
|
||||
"""
|
||||
|
||||
proc modInt(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
if (`b` == 0) raiseDivByZero();
|
||||
if (`b` == -1 && `a` == 2147483647) raiseOverflow();
|
||||
return Math.floor(`a` % `b`);
|
||||
"""
|
||||
|
||||
|
||||
|
||||
proc addInt64(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
var result = `a` + `b`;
|
||||
if (result > 9223372036854775807
|
||||
|| result < -9223372036854775808) raiseOverflow();
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc subInt64(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
var result = `a` - `b`;
|
||||
if (result > 9223372036854775807
|
||||
|| result < -9223372036854775808) raiseOverflow();
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc mulInt64(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
var result = `a` * `b`;
|
||||
if (result > 9223372036854775807
|
||||
|| result < -9223372036854775808) raiseOverflow();
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc divInt64(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
if (`b` == 0) raiseDivByZero();
|
||||
if (`b` == -1 && `a` == 9223372036854775807) raiseOverflow();
|
||||
return Math.floor(`a` / `b`);
|
||||
"""
|
||||
|
||||
proc modInt64(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
if (`b` == 0) raiseDivByZero();
|
||||
if (`b` == -1 && `a` == 9223372036854775807) raiseOverflow();
|
||||
return Math.floor(`a` % `b`);
|
||||
"""
|
||||
|
||||
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
|
||||
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
|
||||
|
||||
proc internalAssert(file: cstring, line: int) {.pure, compilerproc.} =
|
||||
var
|
||||
e: ref EAssertionFailed
|
||||
new(e)
|
||||
asm """`e`.message = "[Assertion failure] file: "+`file`+", line: "+`line`"""
|
||||
raise e
|
||||
|
||||
include hti
|
||||
|
||||
proc isFatPointer(ti: PNimType): bool =
|
||||
# This has to be consistens with the code generator!
|
||||
return ti.base.kind notin {tyRecord, tyRecordConstr, tyObject,
|
||||
tyArray, tyArrayConstr, tyPureObject, tyTuple,
|
||||
tyEmptySet, tyOpenArray, tySet, tyVar, tyRef, tyPtr}
|
||||
|
||||
proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
|
||||
|
||||
proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.exportc.} =
|
||||
case n.kind
|
||||
of nkNone: assert(false)
|
||||
of nkSlot:
|
||||
asm "`dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ);"
|
||||
of nkList:
|
||||
for i in 0..n.len-1:
|
||||
NimCopyAux(dest, src, n.sons[i])
|
||||
of nkCase:
|
||||
asm """
|
||||
`dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ);
|
||||
for (var i = 0; i < `n`.sons.length; ++i) {
|
||||
NimCopyAux(`dest`, `src`, `n`.sons[i][1]);
|
||||
}
|
||||
"""
|
||||
|
||||
proc NimCopy(x: pointer, ti: PNimType): pointer =
|
||||
case ti.kind
|
||||
of tyPtr, tyRef, tyVar, tyNil:
|
||||
if not isFatPointer(ti):
|
||||
result = x
|
||||
else:
|
||||
asm """
|
||||
`result` = [null, 0];
|
||||
`result`[0] = `x`[0];
|
||||
`result`[1] = `x`[1];
|
||||
"""
|
||||
of tyEmptySet, tySet:
|
||||
asm """
|
||||
`result` = {};
|
||||
for (var key in `x`) { `result`[key] = `x`[key]; }
|
||||
"""
|
||||
of tyPureObject, tyTuple, tyObject:
|
||||
if ti.base != nil: result = NimCopy(x, ti.base)
|
||||
elif ti.kind == tyObject:
|
||||
asm "`result` = {m_type: `ti`};"
|
||||
else:
|
||||
asm "`result` = {};"
|
||||
NimCopyAux(result, x, ti.node)
|
||||
of tySequence, tyArrayConstr, tyOpenArray, tyArray:
|
||||
asm """
|
||||
`result` = new Array(`x`.length);
|
||||
for (var i = 0; i < `x`.length; ++i) {
|
||||
`result`[i] = NimCopy(`x`[i], `ti`.base);
|
||||
}
|
||||
"""
|
||||
of tyString:
|
||||
asm "`result` = `x`.slice(0);"
|
||||
else:
|
||||
result = x
|
||||
|
||||
|
||||
proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {.
|
||||
pure, compilerproc.} =
|
||||
# types are fake
|
||||
asm """
|
||||
var result = new Array(`len`);
|
||||
for (var i = 0; i < `len`; ++i) result[i] = NimCopy(`value`, `typ`);
|
||||
return result;
|
||||
"""
|
||||
|
||||
proc chckIndx(i, a, b: int): int {.compilerproc.} =
|
||||
if i >= a and i <= b: return i
|
||||
else: raiseIndexError()
|
||||
|
||||
proc chckRange(i, a, b: int): int {.compilerproc.} =
|
||||
if i >= a and i <= b: return i
|
||||
else: raiseRangeError()
|
||||
|
||||
proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
|
||||
# checks if obj is of type subclass:
|
||||
var x = obj
|
||||
if x == subclass: return # optimized fast path
|
||||
while x != subclass:
|
||||
if x == nil:
|
||||
raise newException(EInvalidObjectConversion, "invalid object conversion")
|
||||
x = x.base
|
||||
|
||||
{.pop.}
|
||||
|
||||
#proc AddU($1, $2)
|
||||
#SubU($1, $2)
|
||||
#MulU($1, $2)
|
||||
#DivU($1, $2)
|
||||
#ModU($1, $2)
|
||||
#AddU64($1, $2)
|
||||
#SubU64($1, $2)
|
||||
#MulU64($1, $2)
|
||||
#DivU64($1, $2)
|
||||
#ModU64($1, $2)
|
||||
#LeU($1, $2)
|
||||
#LtU($1, $2)
|
||||
#LeU64($1, $2)
|
||||
#LtU64($1, $2)
|
||||
#Ze($1)
|
||||
#Ze64($1)
|
||||
#ToU8($1)
|
||||
#ToU16($1)
|
||||
#ToU32($1)
|
||||
|
||||
#NegInt($1)
|
||||
#NegInt64($1)
|
||||
#AbsInt($1)
|
||||
#AbsInt64($1)
|
||||
170
lib/generics.nim
Normal file
170
lib/generics.nim
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains basic abstract data types.
|
||||
|
||||
type
|
||||
TListItem[T] = object
|
||||
next, prev: ref TListItem[T]
|
||||
data: T
|
||||
|
||||
TList*[T] = ref TListItem[T]
|
||||
# TIndex*[T] = object
|
||||
|
||||
#proc succ*[T](i: TIndex[T]): TIndex[T] =
|
||||
# result = i.next
|
||||
|
||||
#proc pred*[T](i: TIndex[T]): TIndex[T] =
|
||||
# result = i.prev
|
||||
|
||||
#proc getIndex*[T](c: TList[T]): TIndex[TList[T]] =
|
||||
# return c
|
||||
|
||||
proc init*[T](c: var TList[T]) {.inline.} =
|
||||
c = nil
|
||||
|
||||
iterator items*[T](c: TList[T]): var T {.inline.} =
|
||||
var it = c
|
||||
while it != nil:
|
||||
yield it.data
|
||||
it = it.next
|
||||
|
||||
proc add*[T](c: var TList[T], item: T) {.inline.} =
|
||||
var it: ref TListItem[T]
|
||||
new(it)
|
||||
it.data = item
|
||||
it.prev = c.prev
|
||||
it.next = c.next
|
||||
c = it
|
||||
|
||||
proc incl*[T](c: var TList[T], item: T) =
|
||||
for i in items(c):
|
||||
if i == item: return
|
||||
add(c, item)
|
||||
|
||||
proc excl*[T](c: var TList[T], item: T) =
|
||||
var it: TList[T] = c
|
||||
while it != nil:
|
||||
if it.data == item:
|
||||
# remove from list
|
||||
it = it.next
|
||||
|
||||
proc del*[T](c: var TList[T], item: T) {.inline.} = excl(c, item)
|
||||
|
||||
proc hash*(p: pointer): int {.inline.} =
|
||||
# Returns the hash value of a pointer. This is very fast.
|
||||
return cast[TAddress](p) shr 3
|
||||
|
||||
proc hash*(x: int): int {.inline.} = return x
|
||||
proc hash*(x: char): int {.inline.} = return ord(x)
|
||||
proc hash*(x: bool): int {.inline.} = return ord(x)
|
||||
|
||||
proc hash*(s: string): int =
|
||||
# The generic hash table implementation work on a type `T` that has a hash
|
||||
# proc. Predefined for string, pointers, int, char, bool.
|
||||
var h = 0
|
||||
for i in 0..s.len-1:
|
||||
h = h +% Ord(s[i])
|
||||
h = h +% h shl 10
|
||||
h = h xor (h shr 6)
|
||||
h = h +% h shl 3
|
||||
h = h xor (h shr 11)
|
||||
h = h +% h shl 15
|
||||
result = h
|
||||
|
||||
proc isNil*(x: int): bool {.inline.} = return x == low(int)
|
||||
proc nilValue*(x: int): int {.inline.} = return low(int)
|
||||
proc nilValue*(x: pointer): pointer {.inline.} = return nil
|
||||
proc nilValue*(x: string): string {.inline.} = return nil
|
||||
proc nilValue*[T](x: seq[T]): seq[T] {.inline.} = return nil
|
||||
proc nilValue*(x: float): float {.inline.} = return NaN
|
||||
|
||||
proc mustRehash(len, counter: int): bool =
|
||||
assert(len > counter)
|
||||
result = (len * 2 < counter * 3) or (len-counter < 4)
|
||||
|
||||
proc nextTry(h, maxHash: int): int {.inline.} =
|
||||
return ((5*%h) +% 1) and maxHash
|
||||
|
||||
type
|
||||
TPair*[TKey, TValue] = tuple[key: TKey, val: TValue]
|
||||
TTable*[TKey, TValue] =
|
||||
object of TObject ## A table which stores (key, value)
|
||||
## pairs. The used algorithm is hashing.
|
||||
d: seq[TPair[TKey, TValue]]
|
||||
counter: natural
|
||||
|
||||
const
|
||||
growthFactor = 2 # must be power of two
|
||||
|
||||
proc init*[TKey, TValue](t: var TTable[TKey, TValue], capacity: natural = 32) =
|
||||
t.d = [] # XXX
|
||||
setLen(t.d, capacity)
|
||||
|
||||
proc len*[TKey, TValue](t: TTable[TKey, TValue]): natural = return t.counter
|
||||
|
||||
iterator pairs*[TKey,TValue](t: TTable[TKey,TValue]): TPair[TKey, TValue] =
|
||||
for i in 0..t.d.len-1:
|
||||
if not isNil(t.d[i].key):
|
||||
yield (t.d[i].key, t.d[i].val)
|
||||
|
||||
proc TableRawGet[TKey, TValue](t: TTable[TKey, TValue], key: TKey): int =
|
||||
var h = hash(key) and high(t.d)
|
||||
while not isNil(t.d[h].key):
|
||||
if t.d[h].key == key: return h
|
||||
h = nextTry(h, high(t.d))
|
||||
return -1
|
||||
|
||||
proc `[]`*[TKey, TValue](t: TTable[TKey, TValue], key: TKey): TValue =
|
||||
var index = TableRawGet(t, key)
|
||||
return if index >= 0: t.d[index].val else: nilValue(t.d[0].val)
|
||||
|
||||
proc TableRawInsert[TKey, TValue](data: var seq[TPair[TKey, TValue]],
|
||||
key: TKey, val: TValue) =
|
||||
var h = hash(key) and high(data)
|
||||
while not isNil(data[h].key):
|
||||
assert(data[h].key != key)
|
||||
h = nextTry(h, high(data))
|
||||
assert(isNil(data[h].key))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc TableEnlarge[TKey, TValue](t: var TTable[TKey, TValue]) =
|
||||
var n: seq[TPair[TKey,TValue]] = []
|
||||
setLen(n, len(t.d) * growthFactor) # XXX
|
||||
for i in 0..high(t.d):
|
||||
if not isNil(t.d[i].key):
|
||||
TableRawInsert(n, t.d[i].key, t.d[i].val)
|
||||
swap(t.d, n)
|
||||
|
||||
proc `[]=`*[TKey, TValue](t: var TTable[TKey, TValue], key: TKey, val: TValue) =
|
||||
var index = TableRawGet(t, key)
|
||||
if index >= 0:
|
||||
t.d[index].val = val
|
||||
else:
|
||||
if mustRehash(len(t.d), t.counter): TableEnlarge(t)
|
||||
TableRawInsert(t.d, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
proc add*[TKey, TValue](t: var TTable[TKey, TValue], key: TKey, val: TValue) =
|
||||
if mustRehash(len(t.d), t.counter): TableEnlarge(t)
|
||||
TableRawInsert(t.d, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
proc test =
|
||||
var
|
||||
t: TTable[string, int]
|
||||
init(t)
|
||||
t["key1"] = 1
|
||||
t["key2"] = 2
|
||||
t["key3"] = 3
|
||||
for key, val in pairs(t):
|
||||
echo(key & " = " & $val)
|
||||
|
||||
test()
|
||||
97
lib/hashes.nim
Normal file
97
lib/hashes.nim
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements efficient computations of hash values for diverse
|
||||
## Nimrod types.
|
||||
|
||||
import
|
||||
strutils
|
||||
|
||||
type
|
||||
THash* = int ## a hash value; hash tables using these values should
|
||||
## always have a size of a power of two and can use the ``and``
|
||||
## operator instead of ``mod`` for truncation of the hash value.
|
||||
|
||||
proc concHash(h: THash, val: int): THash {.inline.} =
|
||||
result = h +% val
|
||||
result = result +% result shl 10
|
||||
result = result xor (result shr 6)
|
||||
|
||||
proc finishHash(h: THash): THash {.inline.} =
|
||||
result = h +% h shl 3
|
||||
result = result xor (result shr 11)
|
||||
result = result +% result shl 15
|
||||
|
||||
proc hashData*(Data: Pointer, Size: int): THash =
|
||||
## hashes an array of bytes of size `size`
|
||||
var
|
||||
h: THash
|
||||
p: cstring
|
||||
i, s: int
|
||||
h = 0
|
||||
p = cast[cstring](Data)
|
||||
i = 0
|
||||
s = size
|
||||
while s > 0:
|
||||
h = concHash(h, ord(p[i]))
|
||||
Inc(i)
|
||||
Dec(s)
|
||||
result = finishHash(h)
|
||||
|
||||
proc hash*(x: Pointer): THash {.inline.} =
|
||||
## efficient hashing of pointers
|
||||
result = (cast[THash](x)) shr 3 # skip the alignment
|
||||
|
||||
proc hash*(x: int): THash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = x
|
||||
|
||||
proc hash*(x: int64): THash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = toU32(x)
|
||||
|
||||
proc hash*(x: char): THash {.inline.} =
|
||||
## efficient hashing of characters
|
||||
result = ord(x)
|
||||
|
||||
proc hash*(x: string): THash =
|
||||
## efficient hashing of strings
|
||||
var h: THash
|
||||
h = 0
|
||||
for i in 0..x.len-1:
|
||||
h = concHash(h, ord(x[i]))
|
||||
result = finishHash(h)
|
||||
|
||||
proc hashIgnoreStyle*(x: string): THash =
|
||||
## efficient hashing of strings; style is ignored
|
||||
var
|
||||
h: THash
|
||||
c: Char
|
||||
h = 0
|
||||
for i in 0..x.len-1:
|
||||
c = x[i]
|
||||
if c == '_':
|
||||
continue # skip _
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = concHash(h, ord(c))
|
||||
result = finishHash(h)
|
||||
|
||||
proc hashIgnoreCase*(x: string): THash =
|
||||
## efficient hashing of strings; case is ignored
|
||||
var
|
||||
h: THash
|
||||
c: Char
|
||||
h = 0
|
||||
for i in 0..x.len-1:
|
||||
c = x[i]
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = concHash(h, ord(c))
|
||||
result = finishHash(h)
|
||||
186
lib/lexbase.nim
Normal file
186
lib/lexbase.nim
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements a base object of a lexer with efficient buffer
|
||||
## handling. In fact I believe that this is the most efficient method of
|
||||
## buffer handling that exists! Only at line endings checks are necessary
|
||||
## if the buffer needs refilling.
|
||||
|
||||
import
|
||||
strutils
|
||||
|
||||
const
|
||||
EndOfFile* = '\0' ## end of file marker
|
||||
# A little picture makes everything clear :-)
|
||||
# buf:
|
||||
# "Example Text\n ha!" bufLen = 17
|
||||
# ^pos = 0 ^ sentinel = 12
|
||||
#
|
||||
NewLines* = {'\c', '\L'}
|
||||
|
||||
type
|
||||
TBaseLexer* = object of TObject ## the base lexer. Inherit your lexer from
|
||||
## this object.
|
||||
bufpos*: int ## the current position within the buffer
|
||||
buf*: cstring ## the buffer itself
|
||||
bufLen*: int ## length of buffer in characters
|
||||
f*: tfile ## the file that is read
|
||||
LineNumber*: int ## the current line number
|
||||
sentinel: int
|
||||
lineStart: int # index of last line start in buffer
|
||||
fileOpened: bool
|
||||
|
||||
proc initBaseLexer*(L: var TBaseLexer, filename: string, bufLen: int = 8192): bool
|
||||
## inits the TBaseLexer object with a file to scan
|
||||
|
||||
proc initBaseLexerFromBuffer*(L: var TBaseLexer, buffer: string)
|
||||
## inits the TBaseLexer with a buffer to scan
|
||||
|
||||
proc deinitBaseLexer*(L: var TBaseLexer)
|
||||
## deinitializes the base lexer. This needs to be called to close the file.
|
||||
|
||||
proc getCurrentLine*(L: TBaseLexer, marker: bool = true): string
|
||||
## retrieves the current line.
|
||||
|
||||
proc getColNumber*(L: TBaseLexer, pos: int): int
|
||||
## retrieves the current column.
|
||||
|
||||
proc HandleCR*(L: var TBaseLexer, pos: int): int
|
||||
## Call this if you scanned over '\c' in the buffer; it returns the the
|
||||
## position to continue the scanning from. `pos` must be the position
|
||||
## of the '\c'.
|
||||
proc HandleLF*(L: var TBaseLexer, pos: int): int
|
||||
## Call this if you scanned over '\L' in the buffer; it returns the the
|
||||
## position to continue the scanning from. `pos` must be the position
|
||||
## of the '\L'.
|
||||
|
||||
# implementation
|
||||
|
||||
const
|
||||
chrSize = sizeof(char)
|
||||
|
||||
proc deinitBaseLexer(L: var TBaseLexer) =
|
||||
dealloc(L.buf)
|
||||
if L.fileOpened: closeFile(L.f)
|
||||
|
||||
proc FillBuffer(L: var TBaseLexer) =
|
||||
var
|
||||
charsRead, toCopy, s: int # all are in characters,
|
||||
# not bytes (in case this
|
||||
# is not the same)
|
||||
oldBufLen: int
|
||||
# we know here that pos == L.sentinel, but not if this proc
|
||||
# is called the first time by initBaseLexer()
|
||||
assert(L.sentinel < L.bufLen)
|
||||
toCopy = L.BufLen - L.sentinel - 1
|
||||
assert(toCopy >= 0)
|
||||
if toCopy > 0:
|
||||
MoveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize) # "moveMem" handles overlapping regions
|
||||
charsRead = ReadBuffer(L.f, addr(L.buf[toCopy]), (L.sentinel + 1) * chrSize) div
|
||||
chrSize
|
||||
s = toCopy + charsRead
|
||||
if charsRead < L.sentinel + 1:
|
||||
L.buf[s] = EndOfFile # set end marker
|
||||
L.sentinel = s
|
||||
else:
|
||||
# compute sentinel:
|
||||
dec(s) # BUGFIX (valgrind)
|
||||
while true:
|
||||
assert(s < L.bufLen)
|
||||
while (s >= 0) and not (L.buf[s] in NewLines): Dec(s)
|
||||
if s >= 0:
|
||||
# we found an appropriate character for a sentinel:
|
||||
L.sentinel = s
|
||||
break
|
||||
else:
|
||||
# rather than to give up here because the line is too long,
|
||||
# double the buffer's size and try again:
|
||||
oldBufLen = L.BufLen
|
||||
L.bufLen = L.BufLen * 2
|
||||
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
|
||||
assert(L.bufLen - oldBuflen == oldBufLen)
|
||||
charsRead = ReadBuffer(L.f, addr(L.buf[oldBufLen]), oldBufLen * chrSize) div
|
||||
chrSize
|
||||
if charsRead < oldBufLen:
|
||||
L.buf[oldBufLen + charsRead] = EndOfFile
|
||||
L.sentinel = oldBufLen + charsRead
|
||||
break
|
||||
s = L.bufLen - 1
|
||||
|
||||
proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
|
||||
assert(pos <= L.sentinel)
|
||||
if pos < L.sentinel:
|
||||
result = pos + 1 # nothing to do
|
||||
else:
|
||||
fillBuffer(L)
|
||||
L.bufpos = 0 # XXX: is this really correct?
|
||||
result = 0
|
||||
L.lineStart = result
|
||||
|
||||
proc HandleCR(L: var TBaseLexer, pos: int): int =
|
||||
assert(L.buf[pos] == '\c')
|
||||
inc(L.linenumber)
|
||||
result = fillBaseLexer(L, pos)
|
||||
if L.buf[result] == '\L':
|
||||
result = fillBaseLexer(L, result)
|
||||
|
||||
proc HandleLF(L: var TBaseLexer, pos: int): int =
|
||||
assert(L.buf[pos] == '\L')
|
||||
inc(L.linenumber)
|
||||
result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
|
||||
|
||||
proc skip_UTF_8_BOM(L: var TBaseLexer) =
|
||||
if (L.buf[0] == '\xEF') and (L.buf[1] == '\xBB') and (L.buf[2] == '\xBF'):
|
||||
inc(L.bufpos, 3)
|
||||
inc(L.lineStart, 3)
|
||||
|
||||
proc initBaseLexer(L: var TBaseLexer, filename: string, bufLen: int = 8192): bool =
|
||||
assert(bufLen > 0)
|
||||
L.bufpos = 0
|
||||
L.bufLen = bufLen
|
||||
L.buf = cast[cstring](alloc(bufLen * chrSize))
|
||||
L.sentinel = bufLen - 1
|
||||
L.lineStart = 0
|
||||
L.linenumber = 1 # lines start at 1
|
||||
L.fileOpened = openFile(L.f, filename)
|
||||
result = L.fileOpened
|
||||
if result:
|
||||
fillBuffer(L)
|
||||
skip_UTF_8_BOM(L)
|
||||
|
||||
proc initBaseLexerFromBuffer(L: var TBaseLexer, buffer: string) =
|
||||
L.bufpos = 0
|
||||
L.bufLen = len(buffer) + 1
|
||||
L.buf = cast[cstring](alloc(L.bufLen * chrSize))
|
||||
L.sentinel = L.bufLen - 1
|
||||
L.lineStart = 0
|
||||
L.linenumber = 1 # lines start at 1
|
||||
L.fileOpened = false
|
||||
if L.bufLen > 0:
|
||||
copyMem(L.buf, cast[pointer](buffer), L.bufLen)
|
||||
L.buf[L.bufLen - 1] = EndOfFile
|
||||
else:
|
||||
L.buf[0] = EndOfFile
|
||||
skip_UTF_8_BOM(L)
|
||||
|
||||
proc getColNumber(L: TBaseLexer, pos: int): int =
|
||||
result = pos - L.lineStart
|
||||
assert(result >= 0)
|
||||
|
||||
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
|
||||
var i: int
|
||||
result = ""
|
||||
i = L.lineStart
|
||||
while not (L.buf[i] in {'\c', '\L', EndOfFile}):
|
||||
add(result, L.buf[i])
|
||||
inc(i)
|
||||
add(result, "\n")
|
||||
if marker:
|
||||
add(result, RepeatChar(getColNumber(L, L.bufpos)) & "^\n")
|
||||
|
||||
176
lib/macros.nim
Normal file
176
lib/macros.nim
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
|
||||
## This module contains the interface to the compiler's abstract syntax tree.
|
||||
## Abstract syntax trees should be modified in macros.
|
||||
|
||||
#[[[cog
|
||||
#def toEnum(name, elems, prefix):
|
||||
# body = ""
|
||||
# counter = 0
|
||||
# for e in elems:
|
||||
# if counter % 4 == 0: p = "\n "
|
||||
# else: p = ""
|
||||
# body += p + prefix + e[2:] + ', '
|
||||
# counter += 1
|
||||
#
|
||||
# return " TNimrod%s* = enum%s\n TNim%ss* = set[TNimrod%s]\n" \
|
||||
# % (name, body.rstrip(", "), name, name)
|
||||
#
|
||||
#enums = eval(file("data/ast.yml").read())
|
||||
#cog.out("type\n")
|
||||
#i = 0
|
||||
#for key, val in enums.iteritems():
|
||||
# if key.endswith("Flag"): continue
|
||||
# cog.out(toEnum(key, val, ["nnk", "nty", "nsk"][i]))
|
||||
# i += 1
|
||||
#]]]
|
||||
type
|
||||
TNimrodNodeKind* = enum
|
||||
nnkNone, nnkEmpty, nnkIdent, nnkSym,
|
||||
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
|
||||
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit,
|
||||
nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit,
|
||||
nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall,
|
||||
nnkCommand, nnkCall, nnkGenericCall, nnkExplicitTypeListCall,
|
||||
nnkExprEqExpr, nnkExprColonExpr, nnkIdentDefs, nnkInfix,
|
||||
nnkPrefix, nnkPostfix, nnkPar, nnkCurly,
|
||||
nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
|
||||
nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
|
||||
nnkElifExpr, nnkElseExpr, nnkLambda, nnkAccQuoted,
|
||||
nnkHeaderQuoted, nnkTableConstr, nnkQualified, nnkHiddenStdConv,
|
||||
nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast,
|
||||
nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
|
||||
nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
|
||||
nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, nnkAsgn,
|
||||
nnkDefaultTypeParam, nnkGenericParams, nnkFormalParams, nnkOfInherit,
|
||||
nnkModule, nnkProcDef, nnkConverterDef, nnkMacroDef,
|
||||
nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkElifBranch,
|
||||
nnkExceptBranch, nnkElse, nnkMacroStmt, nnkAsmStmt,
|
||||
nnkPragma, nnkIfStmt, nnkWhenStmt, nnkForStmt,
|
||||
nnkWhileStmt, nnkCaseStmt, nnkVarSection, nnkConstSection,
|
||||
nnkConstDef, nnkTypeSection, nnkTypeDef, nnkYieldStmt,
|
||||
nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkReturnStmt,
|
||||
nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkDiscardStmt,
|
||||
nnkStmtList, nnkImportStmt, nnkFromStmt, nnkImportAs,
|
||||
nnkIncludeStmt, nnkAccessStmt, nnkCommentStmt, nnkStmtListExpr,
|
||||
nnkBlockExpr, nnkVm, nnkTypeOfExpr, nnkObjectTy,
|
||||
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
||||
nnkRefTy, nnkPtrTy, nnkVarTy, nnkProcTy,
|
||||
nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
|
||||
TNimNodeKinds* = set[TNimrodNodeKind]
|
||||
TNimrodTypeKind* = enum
|
||||
ntyNone, ntyBool, ntyChar, ntyEmptySet,
|
||||
ntyArrayConstr, ntyNil, ntyGeneric, ntyGenericInst,
|
||||
ntyGenericParam, ntyEnum, ntyAnyEnum, ntyArray,
|
||||
ntyObject, ntyTuple, ntySet, ntyRange,
|
||||
ntyPtr, ntyRef, ntyVar, ntySequence,
|
||||
ntyProc, ntyPointer, ntyOpenArray, ntyString,
|
||||
ntyCString, ntyForward, ntyInt, ntyInt8,
|
||||
ntyInt16, ntyInt32, ntyInt64, ntyFloat,
|
||||
ntyFloat32, ntyFloat64, ntyFloat128
|
||||
TNimTypeKinds* = set[TNimrodTypeKind]
|
||||
TNimrodSymKind* = enum
|
||||
nskUnknownSym, nskConditional, nskDynLib, nskParam,
|
||||
nskTypeParam, nskTemp, nskType, nskConst,
|
||||
nskVar, nskProc, nskIterator, nskConverter,
|
||||
nskMacro, nskTemplate, nskField, nskEnumField,
|
||||
nskForVar, nskModule, nskLabel
|
||||
TNimSymKinds* = set[TNimrodSymKind]
|
||||
#[[[end]]]
|
||||
|
||||
type
|
||||
TNimrodNode {.final.} = object # hidden
|
||||
TNimrodSymbol {.final.} = object # hidden
|
||||
TNimrodType {.final.} = object # hidden
|
||||
PNimrodType* {.compilerproc.} = ref TNimrodType
|
||||
PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
|
||||
PNimrodNode* {.compilerproc.} = ref TNimrodNode
|
||||
expr* = PNimrodNode
|
||||
stmt* = PNimrodNode
|
||||
|
||||
# Nodes should be reference counted to make the `copy` operation very fast!
|
||||
# However, this is difficult to achieve: modify(n[0][1]) should propagate to
|
||||
# its father. How to do this without back references?
|
||||
|
||||
proc `[]`* (n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
|
||||
proc `[]=`* (n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
|
||||
## provide access to `n`'s children
|
||||
|
||||
type
|
||||
TNimrodIdent = object of TObject
|
||||
|
||||
converter StrToIdent*(s: string): TNimrodIdent {.magic: "StrToIdent".}
|
||||
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
|
||||
proc `==`* (a, b: TNimrodIdent): bool {.magic: "EqIdent".}
|
||||
|
||||
proc len*(n: PNimrodNode): int {.magic: "NLen".}
|
||||
|
||||
## returns the number of children that a node has
|
||||
proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
|
||||
proc add*(father: PNimrodNode, child: openArray[PNimrodNode]) {.magic: "NAddMultiple".}
|
||||
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
|
||||
proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
|
||||
|
||||
proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
|
||||
proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}
|
||||
proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".}
|
||||
proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}
|
||||
proc typ*(n: PNimrodNode): PNimrodType {.magic: "NGetType".}
|
||||
proc strVal*(n: PNimrodNode): string {.magic: "NStrVal".}
|
||||
|
||||
proc `intVal=`*(n: PNimrodNode, val: biggestInt) {.magic: "NSetIntVal".}
|
||||
proc `floatVal=`*(n: PNimrodNode, val: biggestFloat) {.magic: "NSetFloatVal".}
|
||||
proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}
|
||||
proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
|
||||
proc `typ=`*(n: PNimrodNode, typ: PNimrodType) {.magic: "NSetType".}
|
||||
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
|
||||
|
||||
proc newNimNode*(kind: TNimrodNodeKind,
|
||||
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
|
||||
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
|
||||
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
|
||||
|
||||
proc error*(msg: string) {.magic: "NError".}
|
||||
proc warning*(msg: string) {.magic: "NWarning".}
|
||||
proc hint*(msg: string) {.magic: "NHint".}
|
||||
|
||||
proc newStrLitNode*(s: string): PNimrodNode {.nodecl.} =
|
||||
result = newNimNode(nnkStrLit)
|
||||
result.strVal = s
|
||||
|
||||
proc newIntLitNode*(i: biggestInt): PNimrodNode {.nodecl.} =
|
||||
result = newNimNode(nnkIntLit)
|
||||
result.intVal = i
|
||||
|
||||
proc newIntLitNode*(f: biggestFloat): PNimrodNode {.nodecl.} =
|
||||
result = newNimNode(nnkFloatLit)
|
||||
result.floatVal = f
|
||||
|
||||
proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.nodecl.} =
|
||||
result = newNimNode(nnkIdent)
|
||||
result.ident = i
|
||||
|
||||
proc toStrLit*(n: PNimrodNode): PNimrodNode {.nodecl.} =
|
||||
return newStrLitNode(repr(n))
|
||||
|
||||
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.nodecl.} =
|
||||
if n.kind != k: error("macro expects a node of kind: " & repr(k))
|
||||
|
||||
proc expectMinLen*(n: PNimrodNode, min: int) {.nodecl.} =
|
||||
if n.len < min: error("macro expects a node with " & $min & " children")
|
||||
|
||||
proc newCall*(theProc: TNimrodIdent,
|
||||
args: openArray[PNimrodNode]): PNimrodNode {.nodecl.} =
|
||||
## produces a new call node. `theProc` is the proc that is called with
|
||||
## the arguments ``args[0..]``.
|
||||
result = newNimNode(nnkCall)
|
||||
result.add(newIdentNode(theProc))
|
||||
result.add(args)
|
||||
366
lib/parsecfg.nim
Normal file
366
lib/parsecfg.nim
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## The ``parsecfg`` module implements a high performance configuration file
|
||||
## parser. The configuration file's syntax is similar to the Windows ``.ini``
|
||||
## format, but much more powerful, as it is not a line based parser. String
|
||||
## literals, raw string literals and triple quote string literals are supported
|
||||
## as in the Nimrod programming language.
|
||||
|
||||
## This is an example of how a configuration file may look like:
|
||||
##
|
||||
## .. include:: doc/mytest.cfg
|
||||
## :literal:
|
||||
## The file ``tests/tparscfg.nim`` demonstrates how to use the
|
||||
## configuration file parser:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## :file: tests/tparscfg.nim
|
||||
|
||||
|
||||
import
|
||||
hashes, strutils, lexbase
|
||||
|
||||
type
|
||||
TCfgEventKind* = enum ## enumation of all events that may occur when parsing
|
||||
cfgEof, ## end of file reached
|
||||
cfgSectionStart, ## a ``[section]`` has been parsed
|
||||
cfgKeyValuePair, ## a ``key=value`` pair has been detected
|
||||
cfgOption, ## a ``--key=value`` command line option
|
||||
cfgError ## an error ocurred during parsing
|
||||
|
||||
TCfgEvent* = object of TObject ## describes a parsing event
|
||||
case kind*: TCfgEventKind ## the kind of the event
|
||||
of cfgEof: dummy: string
|
||||
of cfgSectionStart:
|
||||
section*: string ## `section` contains the name of the
|
||||
## parsed section start (syntax: ``[section]``)
|
||||
of cfgKeyValuePair, cfgOption:
|
||||
key*, value*: string ## contains the (key, value) pair if an option
|
||||
## of the form ``--key: value`` or an ordinary
|
||||
## ``key= value`` pair has been parsed.
|
||||
## ``value==""`` if it was not specified in the
|
||||
## configuration file.
|
||||
of cfgError: ## the parser encountered an error: `msg`
|
||||
msg*: string ## contains the error message. No exceptions
|
||||
## are thrown if a parse error occurs.
|
||||
|
||||
TTokKind = enum
|
||||
tkInvalid, tkEof,
|
||||
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
|
||||
TToken{.final.} = object # a token
|
||||
kind: TTokKind # the type of the token
|
||||
literal: string # the parsed (string) literal
|
||||
|
||||
TParserState = enum
|
||||
startState, commaState
|
||||
TCfgParser* = object of TBaseLexer ## the parser object.
|
||||
tok: TToken
|
||||
state: TParserState
|
||||
filename: string
|
||||
|
||||
proc Open*(c: var TCfgParser, filename: string): bool
|
||||
## initializes the parser, open the file for reading and returns true if
|
||||
## successful (the file has been found).
|
||||
|
||||
proc OpenFromBuffer*(c: var TCfgParser, buf: string)
|
||||
## initializes the parser with a buffer. This cannot fail.
|
||||
|
||||
proc close*(c: var TCfgParser)
|
||||
## closes the parser `c`.
|
||||
|
||||
proc next*(c: var TCfgParser): TCfgEvent
|
||||
## retrieves the first/next event. This controls the parser.
|
||||
|
||||
proc getColumn*(c: TCfgParser): int
|
||||
## get the current column the parser has arrived at.
|
||||
|
||||
proc getLine*(c: TCfgParser): int
|
||||
## get the current line the parser has arrived at.
|
||||
|
||||
proc getFilename*(c: TCfgParser): string
|
||||
## get the filename of the file that the parser processes.
|
||||
|
||||
|
||||
# implementation
|
||||
|
||||
const
|
||||
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
||||
|
||||
proc rawGetTok(c: var TCfgParser, tok: var TToken)
|
||||
proc open(c: var TCfgParser, filename: string): bool =
|
||||
result = initBaseLexer(c, filename)
|
||||
c.filename = filename
|
||||
c.state = startState
|
||||
c.tok.kind = tkInvalid
|
||||
c.tok.literal = ""
|
||||
if result: rawGetTok(c, c.tok)
|
||||
|
||||
proc openFromBuffer(c: var TCfgParser, buf: string) =
|
||||
initBaseLexerFromBuffer(c, buf)
|
||||
c.filename = "buffer"
|
||||
c.state = startState
|
||||
c.tok.kind = tkInvalid
|
||||
c.tok.literal = ""
|
||||
rawGetTok(c, c.tok)
|
||||
|
||||
proc close(c: var TCfgParser) =
|
||||
deinitBaseLexer(c)
|
||||
|
||||
proc getColumn(c: TCfgParser): int =
|
||||
result = getColNumber(c, c.bufPos)
|
||||
|
||||
proc getLine(c: TCfgParser): int =
|
||||
result = c.linenumber
|
||||
|
||||
proc getFilename(c: TCfgParser): string =
|
||||
result = c.filename
|
||||
|
||||
proc handleHexChar(c: var TCfgParser, xi: var int) =
|
||||
case c.buf[c.bufpos]
|
||||
of '0'..'9':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
|
||||
inc(c.bufpos)
|
||||
of 'a'..'f':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
|
||||
inc(c.bufpos)
|
||||
of 'A'..'F':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
|
||||
inc(c.bufpos)
|
||||
else:
|
||||
nil
|
||||
|
||||
proc handleDecChars(c: var TCfgParser, xi: var int) =
|
||||
while c.buf[c.bufpos] in {'0'..'9'}:
|
||||
xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
|
||||
inc(c.bufpos)
|
||||
|
||||
proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
|
||||
var xi: int
|
||||
inc(c.bufpos) # skip '\'
|
||||
case c.buf[c.bufpos]
|
||||
of 'n', 'N':
|
||||
tok.literal = tok.literal & nl
|
||||
Inc(c.bufpos)
|
||||
of 'r', 'R', 'c', 'C':
|
||||
add(tok.literal, '\c')
|
||||
Inc(c.bufpos)
|
||||
of 'l', 'L':
|
||||
add(tok.literal, '\L')
|
||||
Inc(c.bufpos)
|
||||
of 'f', 'F':
|
||||
add(tok.literal, '\f')
|
||||
inc(c.bufpos)
|
||||
of 'e', 'E':
|
||||
add(tok.literal, '\e')
|
||||
Inc(c.bufpos)
|
||||
of 'a', 'A':
|
||||
add(tok.literal, '\a')
|
||||
Inc(c.bufpos)
|
||||
of 'b', 'B':
|
||||
add(tok.literal, '\b')
|
||||
Inc(c.bufpos)
|
||||
of 'v', 'V':
|
||||
add(tok.literal, '\v')
|
||||
Inc(c.bufpos)
|
||||
of 't', 'T':
|
||||
add(tok.literal, '\t')
|
||||
Inc(c.bufpos)
|
||||
of '\'', '\"':
|
||||
add(tok.literal, c.buf[c.bufpos])
|
||||
Inc(c.bufpos)
|
||||
of '\\':
|
||||
add(tok.literal, '\\')
|
||||
Inc(c.bufpos)
|
||||
of 'x', 'X':
|
||||
inc(c.bufpos)
|
||||
xi = 0
|
||||
handleHexChar(c, xi)
|
||||
handleHexChar(c, xi)
|
||||
add(tok.literal, Chr(xi))
|
||||
of '0'..'9':
|
||||
xi = 0
|
||||
handleDecChars(c, xi)
|
||||
if (xi <= 255): add(tok.literal, Chr(xi))
|
||||
else: tok.kind = tkInvalid
|
||||
else: tok.kind = tkInvalid
|
||||
|
||||
proc HandleCRLF(c: var TCfgParser, pos: int): int =
|
||||
case c.buf[pos]
|
||||
of '\c': result = lexbase.HandleCR(c, pos)
|
||||
of '\L': result = lexbase.HandleLF(c, pos)
|
||||
else: result = pos
|
||||
|
||||
proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
||||
var
|
||||
pos: int
|
||||
ch: Char
|
||||
buf: cstring
|
||||
pos = c.bufPos + 1 # skip "
|
||||
buf = c.buf # put `buf` in a register
|
||||
tok.kind = tkSymbol
|
||||
if (buf[pos] == '\"') and (buf[pos + 1] == '\"'):
|
||||
# long string literal:
|
||||
inc(pos, 2) # skip ""
|
||||
# skip leading newline:
|
||||
pos = HandleCRLF(c, pos)
|
||||
while true:
|
||||
case buf[pos]
|
||||
of '\"':
|
||||
if (buf[pos + 1] == '\"') and (buf[pos + 2] == '\"'): break
|
||||
add(tok.literal, '\"')
|
||||
Inc(pos)
|
||||
of '\c', '\L':
|
||||
pos = HandleCRLF(c, pos)
|
||||
tok.literal = tok.literal & nl
|
||||
of lexbase.EndOfFile:
|
||||
tok.kind = tkInvalid
|
||||
break
|
||||
else:
|
||||
add(tok.literal, buf[pos])
|
||||
Inc(pos)
|
||||
c.bufpos = pos +
|
||||
3 # skip the three """
|
||||
else:
|
||||
# ordinary string literal
|
||||
while true:
|
||||
ch = buf[pos]
|
||||
if ch == '\"':
|
||||
inc(pos) # skip '"'
|
||||
break
|
||||
if ch in {'\c', '\L', lexbase.EndOfFile}:
|
||||
tok.kind = tkInvalid
|
||||
break
|
||||
if (ch == '\\') and not rawMode:
|
||||
c.bufPos = pos
|
||||
getEscapedChar(c, tok)
|
||||
pos = c.bufPos
|
||||
else:
|
||||
add(tok.literal, ch)
|
||||
Inc(pos)
|
||||
c.bufpos = pos
|
||||
|
||||
proc getSymbol(c: var TCfgParser, tok: var TToken) =
|
||||
var
|
||||
pos: int
|
||||
buf: cstring
|
||||
pos = c.bufpos
|
||||
buf = c.buf
|
||||
while true:
|
||||
add(tok.literal, buf[pos])
|
||||
Inc(pos)
|
||||
if not (buf[pos] in SymChars): break
|
||||
c.bufpos = pos
|
||||
tok.kind = tkSymbol
|
||||
|
||||
proc skip(c: var TCfgParser) =
|
||||
var
|
||||
buf: cstring
|
||||
pos: int
|
||||
pos = c.bufpos
|
||||
buf = c.buf
|
||||
while true:
|
||||
case buf[pos]
|
||||
of ' ', '\t':
|
||||
Inc(pos)
|
||||
of '#', ';':
|
||||
while not (buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos)
|
||||
of '\c', '\L':
|
||||
pos = HandleCRLF(c, pos)
|
||||
else:
|
||||
break # EndOfFile also leaves the loop
|
||||
c.bufpos = pos
|
||||
|
||||
proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
||||
tok.kind = tkInvalid
|
||||
setlen(tok.literal, 0)
|
||||
skip(c)
|
||||
case c.buf[c.bufpos]
|
||||
of '=':
|
||||
tok.kind = tkEquals
|
||||
inc(c.bufpos)
|
||||
of '-':
|
||||
inc(c.bufPos)
|
||||
if c.buf[c.bufPos] == '-': inc(c.bufPos)
|
||||
tok.kind = tkDashDash
|
||||
tok.literal = "--"
|
||||
of ':':
|
||||
tok.kind = tkColon
|
||||
inc(c.bufpos)
|
||||
tok.literal = ":"
|
||||
of 'r', 'R':
|
||||
if c.buf[c.bufPos + 1] == '\"':
|
||||
Inc(c.bufPos)
|
||||
getString(c, tok, true)
|
||||
else:
|
||||
getSymbol(c, tok)
|
||||
of '[':
|
||||
tok.kind = tkBracketLe
|
||||
inc(c.bufpos)
|
||||
tok.literal = "]"
|
||||
of ']':
|
||||
tok.kind = tkBracketRi
|
||||
Inc(c.bufpos)
|
||||
tok.literal = "]"
|
||||
of '\"':
|
||||
getString(c, tok, false)
|
||||
of lexbase.EndOfFile:
|
||||
tok.kind = tkEof
|
||||
else: getSymbol(c, tok)
|
||||
|
||||
proc errorStr(c: TCfgParser, msg: string): string =
|
||||
result = `%`("$1($2, $3) Error: $4",
|
||||
[c.filename, toString(getLine(c)), toString(getColumn(c)), msg])
|
||||
|
||||
proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
|
||||
if c.tok.kind == tkSymbol:
|
||||
result.kind = kind
|
||||
result.key = c.tok.literal
|
||||
result.value = ""
|
||||
rawGetTok(c, c.tok)
|
||||
if c.tok.kind in {tkEquals, tkColon}:
|
||||
rawGetTok(c, c.tok)
|
||||
if c.tok.kind == tkSymbol:
|
||||
result.value = c.tok.literal
|
||||
else:
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
||||
rawGetTok(c, c.tok)
|
||||
else:
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
||||
rawGetTok(c, c.tok)
|
||||
|
||||
proc next(c: var TCfgParser): TCfgEvent =
|
||||
case c.tok.kind
|
||||
of tkEof:
|
||||
result.kind = cfgEof
|
||||
of tkDashDash:
|
||||
rawGetTok(c, c.tok)
|
||||
result = getKeyValPair(c, cfgOption)
|
||||
of tkSymbol:
|
||||
result = getKeyValPair(c, cfgKeyValuePair)
|
||||
of tkBracketLe:
|
||||
rawGetTok(c, c.tok)
|
||||
if c.tok.kind == tkSymbol:
|
||||
result.kind = cfgSectionStart
|
||||
result.section = c.tok.literal
|
||||
else:
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
||||
rawGetTok(c, c.tok)
|
||||
if c.tok.kind == tkBracketRi:
|
||||
rawGetTok(c, c.tok)
|
||||
else:
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "\']\' expected, but found: " & c.tok.literal)
|
||||
of tkInvalid, tkEquals, tkColon, tkBracketRi:
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "invalid token: " & c.tok.literal)
|
||||
rawGetTok(c, c.tok)
|
||||
154
lib/parseopt.nim
Normal file
154
lib/parseopt.nim
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module provides the standard Nimrod command line parser.
|
||||
## It supports one convenience iterator over all command line options and some
|
||||
## lower-level features.
|
||||
|
||||
{.push debugger: off.}
|
||||
|
||||
import
|
||||
os, strutils
|
||||
|
||||
type
|
||||
TCmdLineKind* = enum ## the detected command line token
|
||||
cmdEnd, ## end of command line reached
|
||||
cmdArgument, ## argument detected
|
||||
cmdLongoption, ## a long option ``--option`` detected
|
||||
cmdShortOption ## a short option ``-c`` detected
|
||||
TOptParser* =
|
||||
object of TObject ## this object implements the command line parser
|
||||
cmd: string
|
||||
pos: int
|
||||
inShortState: bool
|
||||
kind*: TCmdLineKind ## the dected command line token
|
||||
key*, val*: string ## key and value pair; ``key`` is the option
|
||||
## or the argument, ``value`` is not "" if
|
||||
## the option was given a value
|
||||
|
||||
proc init*(cmdline: string = ""): TOptParser
|
||||
## inits the option parser. If ``cmdline == ""``, the real command line
|
||||
## (as provided by the ``OS`` module) is taken.
|
||||
|
||||
proc next*(p: var TOptParser)
|
||||
## parses the first or next option; ``p.kind`` describes what token has been
|
||||
## parsed. ``p.key`` and ``p.val`` are set accordingly.
|
||||
|
||||
proc getRestOfCommandLine*(p: TOptParser): string
|
||||
## retrieves the rest of the command line that has not been parsed yet.
|
||||
|
||||
# implementation
|
||||
|
||||
proc init(cmdline: string = ""): TOptParser =
|
||||
result.pos = strStart
|
||||
result.inShortState = false
|
||||
if cmdline != "":
|
||||
result.cmd = cmdline
|
||||
else:
|
||||
result.cmd = ""
|
||||
for i in countup(1, ParamCount()):
|
||||
result.cmd = result.cmd & quoteIfSpaceExists(paramStr(i)) & ' '
|
||||
result.kind = cmdEnd
|
||||
result.key = ""
|
||||
result.val = ""
|
||||
|
||||
proc parseWord(s: string, i: int, w: var string,
|
||||
delim: TCharSet = {'\x09', ' ', '\0'}): int =
|
||||
result = i
|
||||
if s[result] == '\"':
|
||||
inc(result)
|
||||
while not (s[result] in {'\0', '\"'}):
|
||||
add(w, s[result])
|
||||
inc(result)
|
||||
if s[result] == '\"': inc(result)
|
||||
else:
|
||||
while not (s[result] in delim):
|
||||
add(w, s[result])
|
||||
inc(result)
|
||||
|
||||
proc handleShortOption(p: var TOptParser) =
|
||||
var i: int
|
||||
i = p.pos
|
||||
p.kind = cmdShortOption
|
||||
add(p.key, p.cmd[i])
|
||||
inc(i)
|
||||
p.inShortState = true
|
||||
while p.cmd[i] in {'\x09', ' '}:
|
||||
inc(i)
|
||||
p.inShortState = false
|
||||
if p.cmd[i] in {':', '='}:
|
||||
inc(i)
|
||||
p.inShortState = false
|
||||
while p.cmd[i] in {'\x09', ' '}: inc(i)
|
||||
i = parseWord(p.cmd, i, p.val)
|
||||
if p.cmd[i] == '\0': p.inShortState = false
|
||||
p.pos = i
|
||||
|
||||
proc next(p: var TOptParser) =
|
||||
var i: int
|
||||
i = p.pos
|
||||
while p.cmd[i] in {'\x09', ' '}: inc(i)
|
||||
p.pos = i
|
||||
setlen(p.key, 0)
|
||||
setlen(p.val, 0)
|
||||
if p.inShortState:
|
||||
handleShortOption(p)
|
||||
return
|
||||
case p.cmd[i]
|
||||
of '\0':
|
||||
p.kind = cmdEnd
|
||||
of '-':
|
||||
inc(i)
|
||||
if p.cmd[i] == '-':
|
||||
p.kind = cmdLongOption
|
||||
inc(i)
|
||||
i = parseWord(p.cmd, i, p.key, {'\0', ' ', '\x09', ':', '='})
|
||||
while p.cmd[i] in {'\x09', ' '}: inc(i)
|
||||
if p.cmd[i] in {':', '='}:
|
||||
inc(i)
|
||||
while p.cmd[i] in {'\x09', ' '}: inc(i)
|
||||
p.pos = parseWord(p.cmd, i, p.val)
|
||||
else:
|
||||
p.pos = i
|
||||
else:
|
||||
p.pos = i
|
||||
handleShortOption(p)
|
||||
else:
|
||||
p.kind = cmdArgument
|
||||
p.pos = parseWord(p.cmd, i, p.key)
|
||||
|
||||
proc getRestOfCommandLine(p: TOptParser): string =
|
||||
result = strip(copy(p.cmd, p.pos + strStart, len(p.cmd) - 1))
|
||||
|
||||
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
|
||||
##this is an convenience iterator for iterating over the command line.
|
||||
##This uses the TOptParser object. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var
|
||||
## filename = ""
|
||||
## for kind, key, val in getopt():
|
||||
## case kind
|
||||
## of cmdArgument:
|
||||
## filename = key
|
||||
## of cmdLongOption, cmdShortOption:
|
||||
## case key
|
||||
## of "help", "h": writeHelp()
|
||||
## of "version", "v": writeVersion()
|
||||
## of cmdEnd: assert(false) # cannot happen
|
||||
## if filename == "":
|
||||
## # no filename has been given, so we show the help:
|
||||
## writeHelp()
|
||||
var p = init()
|
||||
while true:
|
||||
next(p)
|
||||
if p.kind == cmdEnd: break
|
||||
yield (p.kind, p.key, p.val)
|
||||
|
||||
{.pop.}
|
||||
19
lib/smallalc.nim
Normal file
19
lib/smallalc.nim
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Handle small allocations efficiently
|
||||
# We allocate and manage memory by pages. All objects within a page belong to
|
||||
# the same type. Thus, we safe the type field. Minimum requested block is
|
||||
# 8 bytes. Alignment is 8 bytes.
|
||||
|
||||
|
||||
type
|
||||
TChunk {.pure.} = object
|
||||
kind: TChunkKind
|
||||
prev, next: ptr TChunk
|
||||
|
||||
TSmallChunk = object of TChunk # all objects of the same size
|
||||
typ: PNimType
|
||||
free: int
|
||||
data: array [0.., int]
|
||||
|
||||
proc allocSmall(typ: PNimType): pointer =
|
||||
|
||||
|
||||
210
lib/strtabs.nim
Normal file
210
lib/strtabs.nim
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## The ``strtabs`` module implements an efficient hash table that is a mapping
|
||||
## from strings to strings. Supports a case-sensitive, case-insensitive and
|
||||
## style-insensitive mode. An efficient string substitution operator ``%``
|
||||
## for the string table is also provided.
|
||||
|
||||
import
|
||||
os, hashes, strutils
|
||||
|
||||
type
|
||||
TStringTableMode* = enum # describes the tables operation mode
|
||||
modeCaseSensitive, # the table is case sensitive
|
||||
modeCaseInsensitive, # the table is case insensitive
|
||||
modeStyleInsensitive # the table is style insensitive
|
||||
TKeyValuePair = tuple[key, val: string]
|
||||
TKeyValuePairSeq = seq[TKeyValuePair]
|
||||
TStringTable* = object of TObject
|
||||
counter: int
|
||||
data: TKeyValuePairSeq
|
||||
mode: TStringTableMode
|
||||
|
||||
PStringTable* = ref TStringTable ## use this type to declare string tables
|
||||
|
||||
proc newStringTable*(keyValuePairs: openarray[string],
|
||||
mode: TStringTableMode = modeCaseSensitive): PStringTable
|
||||
## creates a new string table with given key value pairs.
|
||||
## Example::
|
||||
## var mytab = newStringTable("key1", "val1", "key2", "val2",
|
||||
## modeCaseInsensitive)
|
||||
|
||||
proc newStringTable*(mode: TStringTableMode = modeCaseSensitive): PStringTable
|
||||
## creates a new string table that is empty.
|
||||
|
||||
proc `[]=`*(t: PStringTable, key, val: string)
|
||||
## puts a (key, value)-pair into `t`.
|
||||
|
||||
proc `[]`*(t: PStringTable, key: string): string
|
||||
## retrieves the value at ``t[key]``. If `key` is not in `t`, "" is returned
|
||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||
## exists.
|
||||
|
||||
proc hasKey*(t: PStringTable, key: string): bool
|
||||
## returns true iff `key` is in the table `t`.
|
||||
|
||||
proc len*(t: PStringTable): int =
|
||||
## returns the number of keys in `t`.
|
||||
result = t.counter
|
||||
|
||||
iterator pairs*(t: PStringTable): tuple[key, value: string] =
|
||||
## iterates over any (key, value) pair in the table `t`.
|
||||
for h in 0..high(t.data):
|
||||
if not isNil(t.data[h].key):
|
||||
yield (t.data[h].key, t.data[h].val)
|
||||
|
||||
type
|
||||
TFormatFlag* = enum # flags for the `%` operator
|
||||
useEnvironment, # use environment variable if the ``$key``
|
||||
# is not found in the table
|
||||
useEmpty, # use the empty string as a default, thus it
|
||||
# won't throw an exception if ``$key`` is not
|
||||
# in the table
|
||||
useKey # do not replace ``$key`` if it is not found
|
||||
# in the table (or in the environment)
|
||||
|
||||
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string
|
||||
## The `%` operator for string tables.
|
||||
|
||||
# implementation
|
||||
|
||||
const
|
||||
growthFactor = 2
|
||||
startSize = 64
|
||||
|
||||
proc newStringTable(mode: TStringTableMode = modeCaseSensitive): PStringTable =
|
||||
new(result)
|
||||
result.mode = mode
|
||||
result.counter = 0
|
||||
result.data = []
|
||||
setlen(result.data, startSize) # XXX
|
||||
|
||||
proc newStringTable(keyValuePairs: openarray[string],
|
||||
mode: TStringTableMode = modeCaseSensitive): PStringTable =
|
||||
result = newStringTable(mode)
|
||||
var i = 0
|
||||
while i < high(keyValuePairs):
|
||||
result[keyValuePairs[i]] = keyValuePairs[i + 1]
|
||||
inc(i, 2)
|
||||
|
||||
proc myhash(t: PStringTable, key: string): THash =
|
||||
case t.mode
|
||||
of modeCaseSensitive: result = hashes.hash(key)
|
||||
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
|
||||
of modeStyleInsensitive: result = hashes.hashIgnoreStyle(key)
|
||||
|
||||
proc myCmp(t: PStringTable, a, b: string): bool =
|
||||
case t.mode
|
||||
of modeCaseSensitive: result = cmp(a, b) == 0
|
||||
of modeCaseInsensitive: result = cmpIgnoreCase(a, b) == 0
|
||||
of modeStyleInsensitive: result = cmpIgnoreStyle(a, b) == 0
|
||||
|
||||
proc mustRehash(length, counter: int): bool =
|
||||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
const
|
||||
EmptySeq = []
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash =
|
||||
result = ((5 * h) + 1) and maxHash # For any initial h in range(maxHash), repeating that maxHash times
|
||||
# generates each int in range(maxHash) exactly once (see any text on
|
||||
# random-number generation for proof).
|
||||
|
||||
proc RawGet(t: PStringTable, key: string): int =
|
||||
var h: THash
|
||||
h = myhash(t, key) and high(t.data) # start with real hash value
|
||||
while not isNil(t.data[h].key):
|
||||
if mycmp(t, t.data[h].key, key):
|
||||
return h
|
||||
h = nextTry(h, high(t.data))
|
||||
result = - 1
|
||||
|
||||
proc `[]`(t: PStringTable, key: string): string =
|
||||
var index: int
|
||||
index = RawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = ""
|
||||
|
||||
proc hasKey(t: PStringTable, key: string): bool =
|
||||
result = rawGet(t, key) >= 0
|
||||
|
||||
proc RawInsert(t: PStringTable, data: var TKeyValuePairSeq, key, val: string) =
|
||||
var h: THash
|
||||
h = myhash(t, key) and high(data)
|
||||
while not isNil(data[h].key):
|
||||
h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc Enlarge(t: PStringTable) =
|
||||
var n: TKeyValuePairSeq
|
||||
n = emptySeq
|
||||
setlen(n, len(t.data) * growthFactor)
|
||||
for i in countup(0, high(t.data)):
|
||||
if not isNil(t.data[i].key): RawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||
swap(t.data, n)
|
||||
|
||||
proc `[]=`(t: PStringTable, key, val: string) =
|
||||
var index = RawGet(t, key)
|
||||
if index >= 0:
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter): Enlarge(t)
|
||||
RawInsert(t, t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
proc RaiseFormatException(s: string) =
|
||||
var e: ref EInvalidValue
|
||||
new(e)
|
||||
e.msg = "format string: key not found: " & s
|
||||
raise e
|
||||
|
||||
proc getValue(t: PStringTable, flags: set[TFormatFlag], key: string): string =
|
||||
if hasKey(t, key): return t[key]
|
||||
if useEnvironment in flags: result = os.getEnv(key)
|
||||
else: result = ""
|
||||
if (result == ""):
|
||||
if useKey in flags: result = '$' & key
|
||||
elif not (useEmpty in flags): raiseFormatException(key)
|
||||
|
||||
proc `%`(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string =
|
||||
const
|
||||
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
||||
var
|
||||
i, j: int
|
||||
key: string
|
||||
result = ""
|
||||
i = strStart
|
||||
while i <= len(f) + strStart - 1:
|
||||
if f[i] == '$':
|
||||
case f[i + 1]
|
||||
of '$':
|
||||
add(result, '$')
|
||||
inc(i, 2)
|
||||
of '{':
|
||||
j = i + 1
|
||||
while (j <= len(f) + strStart - 1) and (f[j] != '}'): inc(j)
|
||||
key = copy(f, i + 2, j - 1)
|
||||
result = result & getValue(t, flags, key)
|
||||
i = j + 1
|
||||
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_':
|
||||
j = i + 1
|
||||
while (j <= len(f) + strStart - 1) and (f[j] in PatternChars): inc(j)
|
||||
key = copy(f, i+1, j - 1)
|
||||
result = result & getValue(t, flags, key)
|
||||
i = j
|
||||
else:
|
||||
add(result, f[i])
|
||||
inc(i)
|
||||
else:
|
||||
add(result, f[i])
|
||||
inc(i)
|
||||
|
||||
72
lib/tinalloc.nim
Normal file
72
lib/tinalloc.nim
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Memory handling for small objects
|
||||
|
||||
const
|
||||
minRequestSize = 2 * sizeof(pointer) # minimal block is 16 bytes
|
||||
pageSize = 1024 * sizeof(int)
|
||||
pageBits = pageSize div minRequestSize
|
||||
pageMask = pageSize-1
|
||||
|
||||
bitarraySize = pageBits div (sizeof(int)*8)
|
||||
dataSize = pageSize - (bitarraySize+6) * sizeof(pointer)
|
||||
|
||||
type
|
||||
TMinRequest {.final.} = object
|
||||
next, prev: ptr TMinRequest # stores next free bit
|
||||
|
||||
TChunk {.pure.} = object # a chunk manages at least a page
|
||||
size: int # lowest bit signals if it is a small chunk (0) or
|
||||
# a big chunk (1)
|
||||
typ: PNimType
|
||||
next, prev: ptr TChunk
|
||||
nextOfSameType: ptr TChunk
|
||||
|
||||
TSmallChunk = object of TChunk ## manages pageSize bytes for a type and a
|
||||
## fixed size
|
||||
free: int ## index of first free bit
|
||||
bits: array[0..bitarraySize-1, int]
|
||||
data: array[0..dataSize div minRequestSize - 1, TMinRequest]
|
||||
|
||||
PSmallChunk = ptr TSmallChunk
|
||||
|
||||
assert(sizeof(TSmallChunk) == pageSize)
|
||||
|
||||
proc getNewChunk(size: int, typ: PNimType): PSmallChunk =
|
||||
result = cast[PSmallChunk](getPages(1))
|
||||
result.size = PageSize
|
||||
result.typ = typ
|
||||
result.next = chunkHead
|
||||
result.prev = nil
|
||||
chunkHead.prev = result
|
||||
chunkHead = result.next
|
||||
result.nextOfSameType = cast[PSmallChunk](typ.chunk)
|
||||
typ.chunk = result
|
||||
result.free = addr(result.data[0])
|
||||
result.data[0].next = addr(result.data[1])
|
||||
result.data[0].prev = nil
|
||||
result.data[high(result.data)].next = nil
|
||||
result.data[high(result.data)].prev = addr(result.data[high(result.data)-1])
|
||||
for i in 1..high(result.data)-1:
|
||||
result.data[i].next = addr(result.data[i+1])
|
||||
result.data[i].prev = addr(result.data[i-1])
|
||||
|
||||
proc newSmallObj(size: int, typ: PNimType): pointer =
|
||||
var chunk = cast[PSmallChunk](typ.chunk)
|
||||
if chunk == nil or chunk.free <= 0:
|
||||
if chunk.free < 0: GC_collect()
|
||||
chunk = getNewChunk(size, typ)
|
||||
chunk.nextOfSameType = typ.chunk
|
||||
typ.chunk = chunk
|
||||
var idx = chunk.free
|
||||
setBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)
|
||||
result = cast[pointer](cast[TAddress](addr(chunk.data)) +
|
||||
minRequestSize * idx)
|
||||
var res = cast[PMinRequest](result)
|
||||
chunk.free = res.next
|
||||
res.next
|
||||
|
||||
proc freeObj(obj: pointer) =
|
||||
var chunk = cast[PChunk](cast[TAddress(obj) and not pageMask)
|
||||
if size and 1 == 0: # small chunk
|
||||
var idx = (cast[TAddress](obj) shr pageShift) div minRequestSize
|
||||
resetBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)
|
||||
|
||||
1107
lib/wz_jsgraphics.js
Normal file
1107
lib/wz_jsgraphics.js
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue