deleted web and dist

This commit is contained in:
Andreas Rumpf 2008-08-23 11:32:48 +02:00
commit 972c510861
50 changed files with 4986 additions and 2289 deletions

73
lib/base/lex.nim Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff