version 0.8.5: added Nimrod version of the compiler

This commit is contained in:
Andreas Rumpf
2009-12-07 01:23:19 +01:00
parent 90119066ad
commit e254741541
78 changed files with 35801 additions and 0 deletions

1096
rod/ast.nim Executable file

File diff suppressed because it is too large Load Diff

830
rod/astalgo.nim Executable file
View File

@@ -0,0 +1,830 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Algorithms for the abstract syntax tree: hash tables, lists
# and sets of nodes are supported. Efficiency is important as
# the data structures here are used in various places of the compiler.
import
ast, nhashes, strutils, options, msgs, ropes, idents
proc hashNode*(p: PObject): THash
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope
# Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes.
# If maxRecDepht <> -1 then it won't print the whole graph.
proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): PRope
proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope
proc lineInfoToStr*(info: TLineInfo): PRope
# ----------------------- node sets: ---------------------------------------
proc ObjectSetContains*(t: TObjectSet, obj: PObject): bool
# returns true whether n is in t
proc ObjectSetIncl*(t: var TObjectSet, obj: PObject)
# include an element n in the table t
proc ObjectSetContainsOrIncl*(t: var TObjectSet, obj: PObject): bool
# more are not needed ...
# ----------------------- (key, val)-Hashtables ----------------------------
proc TablePut*(t: var TTable, key, val: PObject)
proc TableGet*(t: TTable, key: PObject): PObject
type
TCmpProc* = proc (key, closure: PObject): bool # should return true if found
proc TableSearch*(t: TTable, key, closure: PObject, comparator: TCmpProc): PObject
# return val as soon as comparator returns true; if this never happens,
# nil is returned
# ----------------------- str table -----------------------------------------
proc StrTableContains*(t: TStrTable, n: PSym): bool
proc StrTableAdd*(t: var TStrTable, n: PSym)
proc StrTableGet*(t: TStrTable, name: PIdent): PSym
proc StrTableIncl*(t: var TStrTable, n: PSym): bool
# returns true if n is already in the string table
# the iterator scheme:
type
TTabIter*{.final.} = object # consider all fields here private
h*: THash # current hash
proc InitTabIter*(ti: var TTabIter, tab: TStrTable): PSym
proc NextIter*(ti: var TTabIter, tab: TStrTable): PSym
# usage:
# var i: TTabIter; s: PSym;
# s := InitTabIter(i, table);
# while s <> nil do begin
# ...
# s := NextIter(i, table);
# end;
type
TIdentIter*{.final.} = object # iterator over all syms with the same identifier
h*: THash # current hash
name*: PIdent
proc InitIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym
proc NextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym
# -------------- symbol table ----------------------------------------------
# Each TParser object (which represents a module being compiled) has its own
# symbol table. A symbol table is organized as a stack of str tables. The
# stack represents the different scopes.
# Stack pointer:
# 0 imported symbols from other modules
# 1 module level
# 2 proc level
# 3 nested statements
# ...
#
type
TSymTab*{.final.} = object
tos*: Natural # top of stack
stack*: seq[TStrTable]
proc InitSymTab*(tab: var TSymTab)
proc DeinitSymTab*(tab: var TSymTab)
proc SymTabGet*(tab: TSymTab, s: PIdent): PSym
proc SymTabLocalGet*(tab: TSymTab, s: PIdent): PSym
proc SymTabAdd*(tab: var TSymTab, e: PSym)
proc SymTabAddAt*(tab: var TSymTab, e: PSym, at: Natural)
proc SymTabAddUnique*(tab: var TSymTab, e: PSym): TResult
proc SymTabAddUniqueAt*(tab: var TSymTab, e: PSym, at: Natural): TResult
proc OpenScope*(tab: var TSymTab)
proc RawCloseScope*(tab: var TSymTab)
# the real "closeScope" adds some
# checks in parsobj
# these are for debugging only:
proc debug*(n: PSym)
proc debug*(n: PType)
proc debug*(n: PNode)
# --------------------------- ident tables ----------------------------------
proc IdTableGet*(t: TIdTable, key: PIdObj): PObject
proc IdTableGet*(t: TIdTable, key: int): PObject
proc IdTablePut*(t: var TIdTable, key: PIdObj, val: PObject)
proc IdTableHasObjectAsKey*(t: TIdTable, key: PIdObj): bool
# checks if `t` contains the `key` (compared by the pointer value, not only
# `key`'s id)
proc IdNodeTableGet*(t: TIdNodeTable, key: PIdObj): PNode
proc IdNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode)
proc writeIdNodeTable*(t: TIdNodeTable)
# ---------------------------------------------------------------------------
proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
proc lookupInRecord*(n: PNode, field: PIdent): PSym
proc getModule*(s: PSym): PSym
proc mustRehash*(length, counter: int): bool
proc nextTry*(h, maxHash: THash): THash
# ------------- table[int, int] ---------------------------------------------
const
InvalidKey* = low(int)
type
TIIPair*{.final.} = object
key*, val*: int
TIIPairSeq* = seq[TIIPair]
TIITable*{.final.} = object # table[int, int]
counter*: int
data*: TIIPairSeq
proc initIITable*(x: var TIITable)
proc IITableGet*(t: TIITable, key: int): int
proc IITablePut*(t: var TIITable, key, val: int)
# implementation
proc lookupInRecord(n: PNode, field: PIdent): PSym =
result = nil
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
result = lookupInRecord(n.sons[i], field)
if result != nil: return
of nkRecCase:
if (n.sons[0].kind != nkSym): InternalError(n.info, "lookupInRecord")
result = lookupInRecord(n.sons[0], field)
if result != nil: return
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
result = lookupInRecord(lastSon(n.sons[i]), field)
if result != nil: return
else: internalError(n.info, "lookupInRecord(record case branch)")
of nkSym:
if n.sym.name.id == field.id: result = n.sym
else: internalError(n.info, "lookupInRecord()")
proc getModule(s: PSym): PSym =
result = s
assert((result.kind == skModule) or (result.owner != result))
while (result != nil) and (result.kind != skModule): result = result.owner
proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
for i in countup(start, sonsLen(list) - 1):
if list.sons[i].kind != nkSym: InternalError(list.info, "getSymFromList")
result = list.sons[i].sym
if result.name.id == ident.id: return
result = nil
proc hashNode(p: PObject): THash =
result = hashPtr(cast[pointer](p))
proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc spaces(x: int): PRope =
# returns x spaces
result = toRope(repeatChar(x))
proc toYamlChar(c: Char): string =
case c
of '\0'..'\x1F', '\x80'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4)
of '\'', '\"', '\\': result = '\\' & c
else: result = c & ""
proc makeYamlString(s: string): PRope =
# We have to split long strings into many ropes. Otherwise
# this could trigger InternalError(111). See the ropes module for
# further information.
const
MaxLineLength = 64
var res: string
result = nil
res = "\""
for i in countup(0, len(s) + 0 - 1):
if (i - 0 + 1) mod MaxLineLength == 0:
add(res, '\"')
add(res, "\n")
app(result, toRope(res))
res = "\"" # reset
add(res, toYamlChar(s[i]))
add(res, '\"')
app(result, toRope(res))
proc flagsToStr[T](flags: set[T]): PRope =
if flags == {}:
result = toRope("[]")
else:
result = nil
for x in items(flags):
if result != nil: app(result, ", ")
app(result, makeYamlString($x))
result = con("[", con(result, "]"))
proc lineInfoToStr(info: TLineInfo): PRope =
result = ropef("[$1, $2, $3]", [makeYamlString(toFilename(info)),
toRope(toLinenumber(info)),
toRope(toColumn(info))])
proc treeToYamlAux(n: PNode, marker: var TIntSet, indent, maxRecDepth: int): PRope
proc symToYamlAux(n: PSym, marker: var TIntSet, indent, maxRecDepth: int): PRope
proc typeToYamlAux(n: PType, marker: var TIntSet, indent, maxRecDepth: int): PRope
proc strTableToYaml(n: TStrTable, marker: var TIntSet, indent: int,
maxRecDepth: int): PRope =
var
istr: PRope
mycount: int
istr = spaces(indent + 2)
result = toRope("[")
mycount = 0
for i in countup(0, high(n.data)):
if n.data[i] != nil:
if mycount > 0: app(result, ",")
appf(result, "$n$1$2",
[istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)])
inc(mycount)
if mycount > 0: appf(result, "$n$1", [spaces(indent)])
app(result, "]")
assert(mycount == n.counter)
proc ropeConstr(indent: int, c: openarray[PRope]): PRope =
# array of (name, value) pairs
var
istr: PRope
i: int
istr = spaces(indent + 2)
result = toRope("{")
i = 0
while i <= high(c):
if i > 0: app(result, ",")
appf(result, "$n$1\"$2\": $3", [istr, c[i], c[i + 1]])
inc(i, 2)
appf(result, "$n$1}", [spaces(indent)])
proc symToYamlAux(n: PSym, marker: var TIntSet, indent: int, maxRecDepth: int): PRope =
var ast: PRope
if n == nil:
result = toRope("null")
elif IntSetContainsOrIncl(marker, n.id):
result = ropef("\"$1 @$2\"", [toRope(n.name.s), toRope(
strutils.toHex(cast[TAddress](n), sizeof(n) * 2))])
else:
ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1)
result = ropeConstr(indent, [toRope("kind"),
makeYamlString($n.kind),
toRope("name"), makeYamlString(n.name.s),
toRope("typ"), typeToYamlAux(n.typ, marker,
indent + 2, maxRecDepth - 1), toRope("info"), lineInfoToStr(n.info),
toRope("flags"), flagsToStr(n.flags),
toRope("magic"),
makeYamlString(MagicToStr[n.magic]),
toRope("ast"), ast, toRope("options"),
flagsToStr(n.options), toRope("position"),
toRope(n.position)])
proc typeToYamlAux(n: PType, marker: var TIntSet, indent: int, maxRecDepth: int): PRope =
if n == nil:
result = toRope("null")
elif intSetContainsOrIncl(marker, n.id):
result = ropef("\"$1 @$2\"", [toRope($n.kind), toRope(
strutils.toHex(cast[TAddress](n), sizeof(n) * 2))])
else:
if sonsLen(n) > 0:
result = toRope("[")
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",")
appf(result, "$n$1$2", [spaces(indent + 4), typeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)])
appf(result, "$n$1]", [spaces(indent + 2)])
else:
result = toRope("null")
result = ropeConstr(indent, [toRope("kind"),
makeYamlString($n.kind),
toRope("sym"), symToYamlAux(n.sym, marker,
indent + 2, maxRecDepth - 1), toRope("n"), treeToYamlAux(n.n, marker,
indent + 2, maxRecDepth - 1), toRope("flags"), FlagsToStr(n.flags),
toRope("callconv"),
makeYamlString(CallingConvToStr[n.callConv]),
toRope("size"), toRope(n.size),
toRope("align"), toRope(n.align),
toRope("sons"), result])
proc treeToYamlAux(n: PNode, marker: var TIntSet, indent: int, maxRecDepth: int): PRope =
var istr: PRope
if n == nil:
result = toRope("null")
else:
istr = spaces(indent + 2)
result = ropef("{$n$1\"kind\": $2",
[istr, makeYamlString($n.kind)])
if maxRecDepth != 0:
appf(result, ",$n$1\"info\": $2", [istr, lineInfoToStr(n.info)])
case n.kind
of nkCharLit..nkInt64Lit:
appf(result, ",$n$1\"intVal\": $2", [istr, toRope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, ",$n$1\"floatVal\": $2", [istr, toRopeF(n.floatVal)])
of nkStrLit..nkTripleStrLit:
appf(result, ",$n$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
appf(result, ",$n$1\"sym\": $2",
[istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
of nkIdent:
if n.ident != nil:
appf(result, ",$n$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
else:
appf(result, ",$n$1\"ident\": null", [istr])
else:
if sonsLen(n) > 0:
appf(result, ",$n$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",")
appf(result, "$n$1$2", [spaces(indent + 4), treeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)])
appf(result, "$n$1]", [istr])
appf(result, ",$n$1\"typ\": $2",
[istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
appf(result, "$n$1}", [spaces(indent)])
proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope =
var marker: TIntSet
IntSetInit(marker)
result = treeToYamlAux(n, marker, indent, maxRecDepth)
proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): PRope =
var marker: TIntSet
IntSetInit(marker)
result = typeToYamlAux(n, marker, indent, maxRecDepth)
proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope =
var marker: TIntSet
IntSetInit(marker)
result = symToYamlAux(n, marker, indent, maxRecDepth)
proc debugType(n: PType): PRope =
if n == nil:
result = toRope("null")
else:
result = toRope($n.kind)
if n.sym != nil:
app(result, " ")
app(result, n.sym.name.s)
if (n.kind != tyString) and (sonsLen(n) > 0):
app(result, "(")
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ", ")
if n.sons[i] == nil:
app(result, "null")
else:
app(result, debugType(n.sons[i]))
app(result, ")")
proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope =
var istr: PRope
if n == nil:
result = toRope("null")
else:
istr = spaces(indent + 2)
result = ropef("{$n$1\"kind\": $2",
[istr, makeYamlString($n.kind)])
if maxRecDepth != 0:
case n.kind
of nkCharLit..nkInt64Lit:
appf(result, ",$n$1\"intVal\": $2", [istr, toRope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, ",$n$1\"floatVal\": $2", [istr, toRopeF(n.floatVal)])
of nkStrLit..nkTripleStrLit:
appf(result, ",$n$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
appf(result, ",$n$1\"sym\": $2_$3",
[istr, toRope(n.sym.name.s), toRope(n.sym.id)])
of nkIdent:
if n.ident != nil:
appf(result, ",$n$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
else:
appf(result, ",$n$1\"ident\": null", [istr])
else:
if sonsLen(n) > 0:
appf(result, ",$n$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",")
appf(result, "$n$1$2", [spaces(indent + 4), debugTree(n.sons[i],
indent + 4, maxRecDepth - 1)])
appf(result, "$n$1]", [istr])
appf(result, "$n$1}", [spaces(indent)])
proc debug(n: PSym) =
writeln(stdout, ropeToStr(ropef("$1_$2", [toRope(n.name.s), toRope(n.id)])))
proc debug(n: PType) =
writeln(stdout, ropeToStr(debugType(n)))
proc debug(n: PNode) =
writeln(stdout, ropeToStr(debugTree(n, 0, 100)))
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 objectSetContains(t: TObjectSet, obj: PObject): bool =
# returns true whether n is in t
var h: THash
h = hashNode(obj) and high(t.data) # start with real hash value
while t.data[h] != nil:
if (t.data[h] == obj):
return true
h = nextTry(h, high(t.data))
result = false
proc objectSetRawInsert(data: var TObjectSeq, obj: PObject) =
var h: THash
h = HashNode(obj) and high(data)
while data[h] != nil:
assert(data[h] != obj)
h = nextTry(h, high(data))
assert(data[h] == nil)
data[h] = obj
proc objectSetEnlarge(t: var TObjectSet) =
var n: TObjectSeq
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)):
if t.data[i] != nil: objectSetRawInsert(n, t.data[i])
swap(t.data, n)
proc objectSetIncl(t: var TObjectSet, obj: PObject) =
if mustRehash(len(t.data), t.counter): objectSetEnlarge(t)
objectSetRawInsert(t.data, obj)
inc(t.counter)
proc objectSetContainsOrIncl(t: var TObjectSet, obj: PObject): bool =
# returns true if obj is already in the string table:
var
h: THash
it: PObject
h = HashNode(obj) and high(t.data)
while true:
it = t.data[h]
if it == nil: break
if it == obj:
return true # found it
h = nextTry(h, high(t.data))
if mustRehash(len(t.data), t.counter):
objectSetEnlarge(t)
objectSetRawInsert(t.data, obj)
else:
assert(t.data[h] == nil)
t.data[h] = obj
inc(t.counter)
result = false
proc TableRawGet(t: TTable, key: PObject): int =
var h: THash
h = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil:
if (t.data[h].key == key):
return h
h = nextTry(h, high(t.data))
result = - 1
proc TableSearch(t: TTable, key, closure: PObject, comparator: TCmpProc): PObject =
var h: THash
h = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil:
if (t.data[h].key == key):
if comparator(t.data[h].val, closure):
# BUGFIX 1
return t.data[h].val
h = nextTry(h, high(t.data))
result = nil
proc TableGet(t: TTable, key: PObject): PObject =
var index: int
index = TableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc TableRawInsert(data: var TPairSeq, key, val: PObject) =
var h: THash
h = HashNode(key) and high(data)
while data[h].key != nil:
assert(data[h].key != key)
h = nextTry(h, high(data))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc TableEnlarge(t: var TTable) =
var n: TPairSeq
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)):
if t.data[i].key != nil: TableRawInsert(n, t.data[i].key, t.data[i].val)
swap(t.data, n)
proc TablePut(t: var TTable, key, val: PObject) =
var index: int
index = TableRawGet(t, key)
if index >= 0:
t.data[index].val = val
else:
if mustRehash(len(t.data), t.counter): TableEnlarge(t)
TableRawInsert(t.data, key, val)
inc(t.counter)
proc StrTableContains(t: TStrTable, n: PSym): bool =
var h: THash
h = n.name.h and high(t.data) # start with real hash value
while t.data[h] != nil:
if (t.data[h] == n):
return true
h = nextTry(h, high(t.data))
result = false
proc StrTableRawInsert(data: var TSymSeq, n: PSym) =
var h: THash
h = n.name.h and high(data)
while data[h] != nil:
if data[h] == n: InternalError(n.info, "StrTableRawInsert: " & n.name.s)
h = nextTry(h, high(data))
assert(data[h] == nil)
data[h] = n
proc StrTableEnlarge(t: var TStrTable) =
var n: TSymSeq
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)):
if t.data[i] != nil: StrTableRawInsert(n, t.data[i])
swap(t.data, n)
proc StrTableAdd(t: var TStrTable, n: PSym) =
if mustRehash(len(t.data), t.counter): StrTableEnlarge(t)
StrTableRawInsert(t.data, n)
inc(t.counter)
proc StrTableIncl(t: var TStrTable, n: PSym): bool =
# returns true if n is already in the string table:
var
h: THash
it: PSym
h = n.name.h and high(t.data)
while true:
it = t.data[h]
if it == nil: break
if it.name.id == n.name.id:
return true # found it
h = nextTry(h, high(t.data))
if mustRehash(len(t.data), t.counter):
StrTableEnlarge(t)
StrTableRawInsert(t.data, n)
else:
assert(t.data[h] == nil)
t.data[h] = n
inc(t.counter)
result = false
proc StrTableGet(t: TStrTable, name: PIdent): PSym =
var h: THash
h = name.h and high(t.data)
while true:
result = t.data[h]
if result == nil: break
if result.name.id == name.id: break
h = nextTry(h, high(t.data))
proc InitIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
ti.h = s.h
ti.name = s
if tab.Counter == 0: result = nil
else: result = NextIdentIter(ti, tab)
proc NextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
var h, start: THash
h = ti.h and high(tab.data)
start = h
result = tab.data[h]
while (result != nil):
if result.Name.id == ti.name.id: break
h = nextTry(h, high(tab.data))
if h == start:
result = nil
break
result = tab.data[h]
ti.h = nextTry(h, high(tab.data))
proc InitTabIter(ti: var TTabIter, tab: TStrTable): PSym =
ti.h = 0 # we start by zero ...
if tab.counter == 0:
result = nil # FIX 1: removed endless loop
else:
result = NextIter(ti, tab)
proc NextIter(ti: var TTabIter, tab: TStrTable): PSym =
result = nil
while (ti.h <= high(tab.data)):
result = tab.data[ti.h]
Inc(ti.h) # ... and increment by one always
if result != nil: break
proc InitSymTab(tab: var TSymTab) =
tab.tos = 0
tab.stack = EmptySeq
proc DeinitSymTab(tab: var TSymTab) =
tab.stack = nil
proc SymTabLocalGet(tab: TSymTab, s: PIdent): PSym =
result = StrTableGet(tab.stack[tab.tos - 1], s)
proc SymTabGet(tab: TSymTab, s: PIdent): PSym =
for i in countdown(tab.tos - 1, 0):
result = StrTableGet(tab.stack[i], s)
if result != nil: return
result = nil
proc SymTabAddAt(tab: var TSymTab, e: PSym, at: Natural) =
StrTableAdd(tab.stack[at], e)
proc SymTabAdd(tab: var TSymTab, e: PSym) =
StrTableAdd(tab.stack[tab.tos - 1], e)
proc SymTabAddUniqueAt(tab: var TSymTab, e: PSym, at: Natural): TResult =
if StrTableGet(tab.stack[at], e.name) != nil:
result = Failure
else:
StrTableAdd(tab.stack[at], e)
result = Success
proc SymTabAddUnique(tab: var TSymTab, e: PSym): TResult =
result = SymTabAddUniqueAt(tab, e, tab.tos - 1)
proc OpenScope(tab: var TSymTab) =
if tab.tos >= len(tab.stack): setlen(tab.stack, tab.tos + 1)
initStrTable(tab.stack[tab.tos])
Inc(tab.tos)
proc RawCloseScope(tab: var TSymTab) =
Dec(tab.tos) #tab.stack[tab.tos] := nil;
proc hasEmptySlot(data: TIdPairSeq): bool =
for h in countup(0, high(data)):
if data[h].key == nil:
return true
result = false
proc IdTableRawGet(t: TIdTable, key: int): int =
var h: THash
h = key and high(t.data) # start with real hash value
while t.data[h].key != nil:
if (t.data[h].key.id == key):
return h
h = nextTry(h, high(t.data))
result = - 1
proc IdTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool =
var index: int
index = IdTableRawGet(t, key.id)
if index >= 0: result = t.data[index].key == key
else: result = false
proc IdTableGet(t: TIdTable, key: PIdObj): PObject =
var index: int
index = IdTableRawGet(t, key.id)
if index >= 0: result = t.data[index].val
else: result = nil
proc IdTableGet(t: TIdTable, key: int): PObject =
var index: int
index = IdTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc IdTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: PObject) =
var h: THash
h = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
h = nextTry(h, high(data))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc IdTablePut(t: var TIdTable, key: PIdObj, val: PObject) =
var
index: int
n: TIdPairSeq
index = IdTableRawGet(t, key.id)
if index >= 0:
assert(t.data[index].key != nil)
t.data[index].val = val
else:
if mustRehash(len(t.data), t.counter):
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)):
if t.data[i].key != nil:
IdTableRawInsert(n, t.data[i].key, t.data[i].val)
assert(hasEmptySlot(n))
swap(t.data, n)
IdTableRawInsert(t.data, key, val)
inc(t.counter)
proc writeIdNodeTable(t: TIdNodeTable) =
var h: THash
nil
proc IdNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
var h: THash
h = key.id and high(t.data) # start with real hash value
while t.data[h].key != nil:
if (t.data[h].key.id == key.id):
return h
h = nextTry(h, high(t.data))
result = - 1
proc IdNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
var index: int
index = IdNodeTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc IdNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: THash
h = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
h = nextTry(h, high(data))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc IdNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
var
index: int
n: TIdNodePairSeq
index = IdNodeTableRawGet(t, key)
if index >= 0:
assert(t.data[index].key != nil)
t.data[index].val = val
else:
if mustRehash(len(t.data), t.counter):
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)):
if t.data[i].key != nil:
IdNodeTableRawInsert(n, t.data[i].key, t.data[i].val)
swap(t.data, n)
IdNodeTableRawInsert(t.data, key, val)
inc(t.counter)
proc initIITable(x: var TIITable) =
x.counter = 0
newSeq(x.data, startSize)
for i in countup(0, startSize - 1): x.data[i].key = InvalidKey
proc IITableRawGet(t: TIITable, key: int): int =
var h: THash
h = key and high(t.data) # start with real hash value
while t.data[h].key != InvalidKey:
if (t.data[h].key == key):
return h
h = nextTry(h, high(t.data))
result = - 1
proc IITableGet(t: TIITable, key: int): int =
var index: int
index = IITableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = InvalidKey
proc IITableRawInsert(data: var TIIPairSeq, key, val: int) =
var h: THash
h = key and high(data)
while data[h].key != InvalidKey:
assert(data[h].key != key)
h = nextTry(h, high(data))
assert(data[h].key == InvalidKey)
data[h].key = key
data[h].val = val
proc IITablePut(t: var TIITable, key, val: int) =
var
index: int
n: TIIPairSeq
index = IITableRawGet(t, key)
if index >= 0:
assert(t.data[index].key != InvalidKey)
t.data[index].val = val
else:
if mustRehash(len(t.data), t.counter):
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(n)): n[i].key = InvalidKey
for i in countup(0, high(t.data)):
if t.data[i].key != InvalidKey:
IITableRawInsert(n, t.data[i].key, t.data[i].val)
swap(t.data, n)
IITableRawInsert(t.data, key, val)
inc(t.counter)

71
rod/bitsets.nim Executable file
View File

@@ -0,0 +1,71 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this unit handles Nimrod sets; it implements bit sets
# the code here should be reused in the Nimrod standard library
type
TBitSet* = seq[int8] # we use byte here to avoid issues with
# cross-compiling; uint would be more efficient
# however
const
ElemSize* = sizeof(int8) * 8
proc BitSetInit*(b: var TBitSet, length: int)
proc BitSetUnion*(x: var TBitSet, y: TBitSet)
proc BitSetDiff*(x: var TBitSet, y: TBitSet)
proc BitSetSymDiff*(x: var TBitSet, y: TBitSet)
proc BitSetIntersect*(x: var TBitSet, y: TBitSet)
proc BitSetIncl*(x: var TBitSet, elem: BiggestInt)
proc BitSetExcl*(x: var TBitSet, elem: BiggestInt)
proc BitSetIn*(x: TBitSet, e: BiggestInt): bool
proc BitSetEquals*(x, y: TBitSet): bool
proc BitSetContains*(x, y: TBitSet): bool
# implementation
proc BitSetIn(x: TBitSet, e: BiggestInt): bool =
result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
toU8(0)
proc BitSetIncl(x: var TBitSet, elem: BiggestInt) =
assert(elem >= 0)
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
toU8(int(1 shl (elem mod ElemSize)))
proc BitSetExcl(x: var TBitSet, elem: BiggestInt) =
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
not toU8(int(1 shl (elem mod ElemSize)))
proc BitSetInit(b: var TBitSet, length: int) =
newSeq(b, length)
proc BitSetUnion(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] or y[i]
proc BitSetDiff(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] and not y[i]
proc BitSetSymDiff(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] xor y[i]
proc BitSetIntersect(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] and y[i]
proc BitSetEquals(x, y: TBitSet): bool =
for i in countup(0, high(x)):
if x[i] != y[i]:
return false
result = true
proc BitSetContains(x, y: TBitSet): bool =
for i in countup(0, high(x)):
if (x[i] and not y[i]) != int8(0):
return false
result = true

1816
rod/ccgexprs.nim Executable file

File diff suppressed because it is too large Load Diff

743
rod/ccgstmts.nim Executable file
View File

@@ -0,0 +1,743 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
const
RangeExpandLimit = 256 # do not generate ranges
# over 'RangeExpandLimit' elements
proc genLineDir(p: BProc, t: PNode) =
var line = toLinenumber(t.info) # BUGFIX
if line < 0:
line = 0 # negative numbers are not allowed in #line
if optLineDir in p.Options:
appff(p.s[cpsStmts], "#line $2 \"$1\"$n", "; line $2 \"$1\"$n",
[toRope(toFilename(t.info)), toRope(line)])
if ({optStackTrace, optEndb} * p.Options == {optStackTrace, optEndb}) and
((p.prc == nil) or not (sfPure in p.prc.flags)):
useMagic(p.module, "endb") # new: endb support
appff(p.s[cpsStmts], "endb($1);$n", "call void @endb(%NI $1)$n",
[toRope(line)])
elif ({optLineTrace, optStackTrace} * p.Options ==
{optLineTrace, optStackTrace}) and
((p.prc == nil) or not (sfPure in p.prc.flags)):
inc(p.labels)
appff(p.s[cpsStmts], "F.line = $1;$n",
"%LOC$2 = getelementptr %TF %F, %NI 2$n" &
"store %NI $1, %NI* %LOC$2$n", [toRope(line), toRope(p.labels)])
proc finishTryStmt(p: BProc, howMany: int) =
for i in countup(1, howMany):
inc(p.labels, 3)
appff(p.s[cpsStmts], "excHandler = excHandler->prev;$n",
"%LOC$1 = load %TSafePoint** @excHandler$n" &
"%LOC$2 = getelementptr %TSafePoint* %LOC$1, %NI 0$n" &
"%LOC$3 = load %TSafePoint** %LOC$2$n" &
"store %TSafePoint* %LOC$3, %TSafePoint** @excHandler$n",
[toRope(p.labels), toRope(p.labels - 1), toRope(p.labels - 2)])
proc genReturnStmt(p: BProc, t: PNode) =
p.beforeRetNeeded = true
genLineDir(p, t)
if (t.sons[0] != nil): genStmts(p, t.sons[0])
finishTryStmt(p, p.nestedTryStmts)
appff(p.s[cpsStmts], "goto BeforeRet;$n", "br label %BeforeRet$n", [])
proc initVariable(p: BProc, v: PSym) =
if containsGarbageCollectedRef(v.typ) or (v.ast == nil):
if not (skipTypes(v.typ, abstractVarRange).Kind in
{tyArray, tyArrayConstr, tySet, tyTuple, tyObject}):
if gCmd == cmdCompileToLLVM:
appf(p.s[cpsStmts], "store $2 0, $2* $1$n",
[addrLoc(v.loc), getTypeDesc(p.module, v.loc.t)])
else:
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(v.loc)])
else:
if gCmd == cmdCompileToLLVM:
app(p.module.s[cfsProcHeaders],
"declare void @llvm.memset.i32(i8*, i8, i32, i32)" & tnl)
inc(p.labels, 2)
appf(p.s[cpsStmts], "%LOC$3 = getelementptr $2* null, %NI 1$n" &
"%LOC$4 = cast $2* %LOC$3 to i32$n" &
"call void @llvm.memset.i32(i8* $1, i8 0, i32 %LOC$4, i32 0)$n", [
addrLoc(v.loc), getTypeDesc(p.module, v.loc.t), toRope(p.labels),
toRope(p.labels - 1)])
else:
appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(v.loc), rdLoc(v.loc)])
proc genVarTuple(p: BProc, n: PNode) =
var
L: int
v: PSym
tup, field: TLoc
t: PType
if n.kind != nkVarTuple: InternalError(n.info, "genVarTuple")
L = sonsLen(n)
genLineDir(p, n)
initLocExpr(p, n.sons[L - 1], tup)
t = tup.t
for i in countup(0, L - 3):
v = n.sons[i].sym
if sfGlobal in v.flags:
assignGlobalVar(p, v)
else:
assignLocalVar(p, v)
initVariable(p, v)
initLoc(field, locExpr, t.sons[i], tup.s)
if t.n == nil:
field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)])
else:
if (t.n.sons[i].kind != nkSym): InternalError(n.info, "genVarTuple")
field.r = ropef("$1.$2",
[rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)])
putLocIntoDest(p, v.loc, field)
genObjectInit(p, v.typ, v.loc, true)
proc genVarStmt(p: BProc, n: PNode) =
var
v: PSym
a: PNode
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.kind == nkIdentDefs:
assert(a.sons[0].kind == nkSym)
v = a.sons[0].sym
if sfGlobal in v.flags:
assignGlobalVar(p, v)
else:
assignLocalVar(p, v)
initVariable(p, v) # XXX: this is not required if a.sons[2] != nil,
# unless it is a GC'ed pointer
if a.sons[2] != nil:
genLineDir(p, a)
expr(p, a.sons[2], v.loc)
genObjectInit(p, v.typ, v.loc, true) # correct position
else:
genVarTuple(p, a)
proc genConstStmt(p: BProc, t: PNode) =
var c: PSym
for i in countup(0, sonsLen(t) - 1):
if t.sons[i].kind == nkCommentStmt: continue
if t.sons[i].kind != nkConstDef: InternalError(t.info, "genConstStmt")
c = t.sons[i].sons[0].sym # This can happen for forward consts:
if (c.ast != nil) and (c.typ.kind in ConstantDataTypes) and
not (lfNoDecl in c.loc.flags):
# generate the data:
fillLoc(c.loc, locData, c.typ, mangleName(c), OnUnknown)
if sfImportc in c.flags:
appf(p.module.s[cfsData], "extern NIM_CONST $1 $2;$n",
[getTypeDesc(p.module, c.typ), c.loc.r])
else:
appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(p.module, c.typ), c.loc.r, genConstExpr(p, c.ast)])
proc genIfStmt(p: BProc, n: PNode) =
#
# if (!expr1) goto L1;
# thenPart
# goto LEnd
# L1:
# if (!expr2) goto L2;
# thenPart2
# goto LEnd
# L2:
# elsePart
# Lend:
#
var
a: TLoc
Lelse: TLabel
genLineDir(p, n)
var Lend = getLabel(p)
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
case it.kind
of nkElifBranch:
initLocExpr(p, it.sons[0], a)
Lelse = getLabel(p)
inc(p.labels)
appff(p.s[cpsStmts], "if (!$1) goto $2;$n",
"br i1 $1, label %LOC$3, label %$2$n" & "LOC$3: $n",
[rdLoc(a), Lelse, toRope(p.labels)])
genStmts(p, it.sons[1])
if sonsLen(n) > 1:
appff(p.s[cpsStmts], "goto $1;$n", "br label %$1$n", [Lend])
fixLabel(p, Lelse)
of nkElse:
genStmts(p, it.sons[0])
else: internalError(n.info, "genIfStmt()")
if sonsLen(n) > 1: fixLabel(p, Lend)
proc genWhileStmt(p: BProc, t: PNode) =
# we don't generate labels here as for example GCC would produce
# significantly worse code
var
a: TLoc
Labl: TLabel
length: int
genLineDir(p, t)
assert(sonsLen(t) == 2)
inc(p.labels)
Labl = con("LA", toRope(p.labels))
length = len(p.blocks)
setlen(p.blocks, length + 1)
p.blocks[length].id = - p.labels # negative because it isn't used yet
p.blocks[length].nestedTryStmts = p.nestedTryStmts
app(p.s[cpsStmts], "while (1) {" & tnl)
initLocExpr(p, t.sons[0], a)
if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0):
p.blocks[length].id = abs(p.blocks[length].id)
appf(p.s[cpsStmts], "if (!$1) goto $2;$n", [rdLoc(a), Labl])
genStmts(p, t.sons[1])
if p.blocks[length].id > 0: appf(p.s[cpsStmts], "} $1: ;$n", [Labl])
else: app(p.s[cpsStmts], '}' & tnl)
setlen(p.blocks, len(p.blocks) - 1)
proc genBlock(p: BProc, t: PNode, d: var TLoc) =
inc(p.labels)
var idx = len(p.blocks)
if t.sons[0] != nil:
# named block?
assert(t.sons[0].kind == nkSym)
var sym = t.sons[0].sym
sym.loc.k = locOther
sym.loc.a = idx
setlen(p.blocks, idx + 1)
p.blocks[idx].id = - p.labels # negative because it isn't used yet
p.blocks[idx].nestedTryStmts = p.nestedTryStmts
if t.kind == nkBlockExpr: genStmtListExpr(p, t.sons[1], d)
else: genStmts(p, t.sons[1])
if p.blocks[idx].id > 0:
appf(p.s[cpsStmts], "LA$1: ;$n", [toRope(p.blocks[idx].id)])
setlen(p.blocks, idx)
proc genBreakStmt(p: BProc, t: PNode) =
genLineDir(p, t)
var idx = len(p.blocks) - 1
if t.sons[0] != nil:
# named break?
assert(t.sons[0].kind == nkSym)
var sym = t.sons[0].sym
assert(sym.loc.k == locOther)
idx = sym.loc.a
p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
finishTryStmt(p, p.nestedTryStmts - p.blocks[idx].nestedTryStmts)
appf(p.s[cpsStmts], "goto LA$1;$n", [toRope(p.blocks[idx].id)])
proc genAsmStmt(p: BProc, t: PNode) =
var
sym: PSym
r, s: PRope
a: TLoc
genLineDir(p, t)
assert(t.kind == nkAsmStmt)
s = nil
for i in countup(0, sonsLen(t) - 1):
case t.sons[i].Kind
of nkStrLit..nkTripleStrLit:
app(s, t.sons[i].strVal)
of nkSym:
sym = t.sons[i].sym
if sym.kind in {skProc, skMethod}:
initLocExpr(p, t.sons[i], a)
app(s, rdLoc(a))
else:
r = sym.loc.r
if r == nil:
# if no name has already been given,
# it doesn't matter much:
r = mangleName(sym)
sym.loc.r = r # but be consequent!
app(s, r)
else: InternalError(t.sons[i].info, "genAsmStmt()")
appf(p.s[cpsStmts], CC[ccompiler].asmStmtFrmt, [s])
proc getRaiseFrmt(p: BProc): string =
if gCmd == cmdCompileToCpp:
result = "throw nimException($1, $2);$n"
else:
useMagic(p.module, "E_Base")
result = "raiseException((E_Base*)$1, $2);$n"
proc genRaiseStmt(p: BProc, t: PNode) =
var
e: PRope
a: TLoc
typ: PType
genLineDir(p, t)
if t.sons[0] != nil:
if gCmd != cmdCompileToCpp: useMagic(p.module, "raiseException")
InitLocExpr(p, t.sons[0], a)
e = rdLoc(a)
typ = t.sons[0].typ
while typ.kind in {tyVar, tyRef, tyPtr}: typ = typ.sons[0]
appf(p.s[cpsStmts], getRaiseFrmt(p), [e, makeCString(typ.sym.name.s)])
else:
# reraise the last exception:
if gCmd == cmdCompileToCpp:
app(p.s[cpsStmts], "throw;" & tnl)
else:
useMagic(p.module, "reraiseException")
app(p.s[cpsStmts], "reraiseException();" & tnl)
const
stringCaseThreshold = 100000
# above X strings a hash-switch for strings is generated
# this version sets it too high to avoid hashing, because this has not
# been tested for a long time
# XXX test and enable this optimization!
proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
rangeFormat, eqFormat: TFormatStr, labl: TLabel) =
var
x, y: TLoc
var length = sonsLen(b)
for i in countup(0, length - 2):
if b.sons[i].kind == nkRange:
initLocExpr(p, b.sons[i].sons[0], x)
initLocExpr(p, b.sons[i].sons[1], y)
appf(p.s[cpsStmts], rangeFormat,
[rdCharLoc(e), rdCharLoc(x), rdCharLoc(y), labl])
else:
initLocExpr(p, b.sons[i], x)
appf(p.s[cpsStmts], eqFormat, [rdCharLoc(e), rdCharLoc(x), labl])
proc genCaseSecondPass(p: BProc, t: PNode, labId: int) =
var Lend = getLabel(p)
for i in countup(1, sonsLen(t) - 1):
appf(p.s[cpsStmts], "LA$1: ;$n", [toRope(labId + i)])
if t.sons[i].kind == nkOfBranch: # else statement
var length = sonsLen(t.sons[i])
genStmts(p, t.sons[i].sons[length - 1])
appf(p.s[cpsStmts], "goto $1;$n", [Lend])
else:
genStmts(p, t.sons[i].sons[0])
fixLabel(p, Lend)
proc genCaseGeneric(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr) =
# generate a C-if statement for a Nimrod case statement
var a: TLoc
initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto:
var labId = p.labels
for i in countup(1, sonsLen(t) - 1):
inc(p.labels)
if t.sons[i].kind == nkOfBranch: # else statement
genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat,
con("LA", toRope(p.labels)))
else:
appf(p.s[cpsStmts], "goto LA$1;$n", [toRope(p.labels)])
genCaseSecondPass(p, t, labId)
proc hashString(s: string): biggestInt =
var
a: int32
b: int64
if CPU[targetCPU].bit == 64:
# we have to use the same bitwidth
# as the target CPU
b = 0
for i in countup(0, len(s) - 1):
b = b +% Ord(s[i])
b = b +% `shl`(b, 10)
b = b xor `shr`(b, 6)
b = b +% `shl`(b, 3)
b = b xor `shr`(b, 11)
b = b +% `shl`(b, 15)
result = b
else:
a = 0
for i in countup(0, len(s) - 1):
a = a +% int32(Ord(s[i]))
a = a +% `shl`(a, int32(10))
a = a xor `shr`(a, int32(6))
a = a +% `shl`(a, int32(3))
a = a xor `shr`(a, int32(11))
a = a +% `shl`(a, int32(15))
result = a
type
TRopeSeq = seq[PRope]
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
branches: var TRopeSeq) =
var
length, j: int
x: TLoc
length = sonsLen(b)
for i in countup(0, length - 2):
assert(b.sons[i].kind != nkRange)
initLocExpr(p, b.sons[i], x)
assert(b.sons[i].kind in {nkStrLit..nkTripleStrLit})
j = int(hashString(b.sons[i].strVal) and high(branches))
appf(branches[j], "if (eqStrings($1, $2)) goto $3;$n",
[rdLoc(e), rdLoc(x), labl])
proc genStringCase(p: BProc, t: PNode) =
var
strings, bitMask, labId: int
a: TLoc
branches: TRopeSeq
useMagic(p.module, "eqStrings") # count how many constant strings there are in the case:
strings = 0
for i in countup(1, sonsLen(t) - 1):
if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1)
if strings > stringCaseThreshold:
useMagic(p.module, "hashString")
bitMask = math.nextPowerOfTwo(strings) - 1
newSeq(branches, bitMask + 1)
initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto:
labId = p.labels
for i in countup(1, sonsLen(t) - 1):
inc(p.labels)
if t.sons[i].kind == nkOfBranch:
genCaseStringBranch(p, t.sons[i], a, con("LA", toRope(p.labels)),
branches)
else:
# else statement: nothing to do yet
# but we reserved a label, which we use later
appf(p.s[cpsStmts], "switch (hashString($1) & $2) {$n",
[rdLoc(a), toRope(bitMask)])
for j in countup(0, high(branches)):
if branches[j] != nil:
appf(p.s[cpsStmts], "case $1: $n$2break;$n",
[intLiteral(j), branches[j]])
app(p.s[cpsStmts], '}' & tnl) # else statement:
if t.sons[sonsLen(t) - 1].kind != nkOfBranch:
appf(p.s[cpsStmts], "goto LA$1;$n", [toRope(p.labels)]) # third pass: generate statements
genCaseSecondPass(p, t, labId)
else:
genCaseGeneric(p, t, "", "if (eqStrings($1, $2)) goto $3;$n")
proc branchHasTooBigRange(b: PNode): bool =
for i in countup(0, sonsLen(b) - 2):
# last son is block
if (b.sons[i].Kind == nkRange) and
(b.sons[i].sons[1].intVal - b.sons[i].sons[0].intVal > RangeExpandLimit):
return true
result = false
proc genOrdinalCase(p: BProc, t: PNode) =
# We analyse if we have a too big switch range. If this is the case,
# we generate an ordinary if statement and rely on the C compiler
# to produce good code.
var
canGenerateSwitch, hasDefault: bool
length: int
a: TLoc
v: PNode
canGenerateSwitch = true
if not (hasSwitchRange in CC[ccompiler].props):
for i in countup(1, sonsLen(t) - 1):
if (t.sons[i].kind == nkOfBranch) and branchHasTooBigRange(t.sons[i]):
canGenerateSwitch = false
break
if canGenerateSwitch:
initLocExpr(p, t.sons[0], a)
appf(p.s[cpsStmts], "switch ($1) {$n", [rdCharLoc(a)])
hasDefault = false
for i in countup(1, sonsLen(t) - 1):
if t.sons[i].kind == nkOfBranch:
length = sonsLen(t.sons[i])
for j in countup(0, length - 2):
if t.sons[i].sons[j].kind == nkRange:
# a range
if hasSwitchRange in CC[ccompiler].props:
appf(p.s[cpsStmts], "case $1 ... $2:$n", [
genLiteral(p, t.sons[i].sons[j].sons[0]),
genLiteral(p, t.sons[i].sons[j].sons[1])])
else:
v = copyNode(t.sons[i].sons[j].sons[0])
while (v.intVal <= t.sons[i].sons[j].sons[1].intVal):
appf(p.s[cpsStmts], "case $1:$n", [genLiteral(p, v)])
Inc(v.intVal)
else:
appf(p.s[cpsStmts], "case $1:$n", [genLiteral(p, t.sons[i].sons[j])])
genStmts(p, t.sons[i].sons[length - 1])
else:
# else part of case statement:
app(p.s[cpsStmts], "default:" & tnl)
genStmts(p, t.sons[i].sons[0])
hasDefault = true
app(p.s[cpsStmts], "break;" & tnl)
if (hasAssume in CC[ccompiler].props) and not hasDefault:
app(p.s[cpsStmts], "default: __assume(0);" & tnl)
app(p.s[cpsStmts], '}' & tnl)
else:
genCaseGeneric(p, t, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
"if ($1 == $2) goto $3;$n")
proc genCaseStmt(p: BProc, t: PNode) =
genLineDir(p, t)
case skipTypes(t.sons[0].typ, abstractVarRange).kind
of tyString:
genStringCase(p, t)
of tyFloat..tyFloat128:
genCaseGeneric(p, t, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
"if ($1 == $2) goto $3;$n") # ordinal type: generate a switch statement
else: genOrdinalCase(p, t)
proc hasGeneralExceptSection(t: PNode): bool =
var length, i, blen: int
length = sonsLen(t)
i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch):
blen = sonsLen(t.sons[i])
if blen == 1:
return true
inc(i)
result = false
proc genTryStmtCpp(p: BProc, t: PNode) =
# code to generate:
#
# bool tmpRethrow = false;
# try
# {
# myDiv(4, 9);
# } catch (NimException& tmp) {
# tmpRethrow = true;
# switch (tmp.exc)
# {
# case DIVIDE_BY_ZERO:
# tmpRethrow = false;
# printf('Division by Zero\n');
# break;
# default: // used for general except!
# generalExceptPart();
# tmpRethrow = false;
# }
# }
# excHandler = excHandler->prev; // we handled the exception
# finallyPart();
# if (tmpRethrow) throw;
var
rethrowFlag: PRope
exc: PRope
i, length, blen: int
genLineDir(p, t)
rethrowFlag = nil
exc = getTempName()
if not hasGeneralExceptSection(t):
rethrowFlag = getTempName()
appf(p.s[cpsLocals], "volatile NIM_BOOL $1 = NIM_FALSE;$n", [rethrowFlag])
if optStackTrace in p.Options:
app(p.s[cpsStmts], "framePtr = (TFrame*)&F;" & tnl)
app(p.s[cpsStmts], "try {" & tnl)
inc(p.nestedTryStmts)
genStmts(p, t.sons[0])
length = sonsLen(t)
if t.sons[1].kind == nkExceptBranch:
appf(p.s[cpsStmts], "} catch (NimException& $1) {$n", [exc])
if rethrowFlag != nil:
appf(p.s[cpsStmts], "$1 = NIM_TRUE;$n", [rethrowFlag])
appf(p.s[cpsStmts], "if ($1.sp.exc) {$n", [exc])
i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch):
blen = sonsLen(t.sons[i])
if blen == 1:
# general except section:
app(p.s[cpsStmts], "default: " & tnl)
genStmts(p, t.sons[i].sons[0])
else:
for j in countup(0, blen - 2):
assert(t.sons[i].sons[j].kind == nkType)
appf(p.s[cpsStmts], "case $1:$n", [toRope(t.sons[i].sons[j].typ.id)])
genStmts(p, t.sons[i].sons[blen - 1])
if rethrowFlag != nil:
appf(p.s[cpsStmts], "$1 = NIM_FALSE; ", [rethrowFlag])
app(p.s[cpsStmts], "break;" & tnl)
inc(i)
if t.sons[1].kind == nkExceptBranch:
app(p.s[cpsStmts], "}}" & tnl) # end of catch-switch statement
dec(p.nestedTryStmts)
app(p.s[cpsStmts], "excHandler = excHandler->prev;" & tnl)
if (i < length) and (t.sons[i].kind == nkFinally):
genStmts(p, t.sons[i].sons[0])
if rethrowFlag != nil:
appf(p.s[cpsStmts], "if ($1) { throw; }$n", [rethrowFlag])
proc genTryStmt(p: BProc, t: PNode) =
# code to generate:
#
# sp.prev = excHandler;
# excHandler = &sp;
# sp.status = setjmp(sp.context);
# if (sp.status == 0) {
# myDiv(4, 9);
# } else {
# /* except DivisionByZero: */
# if (sp.status == DivisionByZero) {
# printf('Division by Zero\n');
#
# /* longjmp(excHandler->context, RangeError); /* raise rangeError */
# sp.status = RangeError; /* if raise; else 0 */
# }
# }
# /* finally: */
# printf('fin!\n');
# if (sp.status != 0)
# longjmp(excHandler->context, sp.status);
# excHandler = excHandler->prev; /* deactivate this safe point */
var
i, length, blen: int
safePoint, orExpr: PRope
genLineDir(p, t)
safePoint = getTempName()
useMagic(p.module, "TSafePoint")
useMagic(p.module, "E_Base")
useMagic(p.module, "excHandler")
appf(p.s[cpsLocals], "TSafePoint $1;$n", [safePoint])
appf(p.s[cpsStmts], "$1.prev = excHandler;$n" & "excHandler = &$1;$n" &
"$1.status = setjmp($1.context);$n", [safePoint])
if optStackTrace in p.Options:
app(p.s[cpsStmts], "framePtr = (TFrame*)&F;" & tnl)
appf(p.s[cpsStmts], "if ($1.status == 0) {$n", [safePoint])
length = sonsLen(t)
inc(p.nestedTryStmts)
genStmts(p, t.sons[0])
app(p.s[cpsStmts], "} else {" & tnl)
i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch):
blen = sonsLen(t.sons[i])
if blen == 1:
# general except section:
if i > 1: app(p.s[cpsStmts], "else {" & tnl)
genStmts(p, t.sons[i].sons[0])
appf(p.s[cpsStmts], "$1.status = 0;$n", [safePoint])
if i > 1: app(p.s[cpsStmts], '}' & tnl)
else:
orExpr = nil
for j in countup(0, blen - 2):
assert(t.sons[i].sons[j].kind == nkType)
if orExpr != nil: app(orExpr, "||")
appf(orExpr, "($1.exc->Sup.m_type == $2)",
[safePoint, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
if i > 1: app(p.s[cpsStmts], "else ")
appf(p.s[cpsStmts], "if ($1) {$n", [orExpr])
genStmts(p, t.sons[i].sons[blen - 1]) # code to clear the exception:
appf(p.s[cpsStmts], "$1.status = 0;}$n", [safePoint])
inc(i)
app(p.s[cpsStmts], '}' & tnl) # end of if statement
finishTryStmt(p, p.nestedTryStmts)
dec(p.nestedTryStmts)
if (i < length) and (t.sons[i].kind == nkFinally):
genStmts(p, t.sons[i].sons[0])
useMagic(p.module, "raiseException")
appf(p.s[cpsStmts], "if ($1.status != 0) { " &
"raiseException($1.exc, $1.exc->name); }$n", [safePoint])
var
breakPointId: int = 0
gBreakpoints: PRope # later the breakpoints are inserted into the main proc
proc genBreakPoint(p: BProc, t: PNode) =
var name: string
if optEndb in p.Options:
if t.kind == nkExprColonExpr:
assert(t.sons[1].kind in {nkStrLit..nkTripleStrLit})
name = normalize(t.sons[1].strVal)
else:
inc(breakPointId)
name = "bp" & $(breakPointId)
genLineDir(p, t) # BUGFIX
appf(gBreakpoints,
"dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [
toRope(toLinenumber(t.info)), makeCString(toFilename(t.info)),
makeCString(name)])
proc genPragma(p: BProc, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
var key: PNode
if it.kind == nkExprColonExpr:
key = it.sons[0]
else:
key = it
if key.kind == nkIdent:
case whichKeyword(key.ident)
of wBreakpoint:
genBreakPoint(p, it)
of wDeadCodeElim:
if not (optDeadCodeElim in gGlobalOptions):
# we need to keep track of ``deadCodeElim`` pragma
if (sfDeadCodeElim in p.module.module.flags):
addPendingModule(p.module)
else:
nil
proc genAsgn(p: BProc, e: PNode) =
var a: TLoc
genLineDir(p, e) # BUGFIX
InitLocExpr(p, e.sons[0], a)
assert(a.t != nil)
expr(p, e.sons[1], a)
proc genFastAsgn(p: BProc, e: PNode) =
var a: TLoc
genLineDir(p, e) # BUGFIX
InitLocExpr(p, e.sons[0], a)
incl(a.flags, lfNoDeepCopy)
assert(a.t != nil)
expr(p, e.sons[1], a)
proc genStmts(p: BProc, t: PNode) =
var
a: TLoc
prc: PSym
#assert(t <> nil);
if inCheckpoint(t.info): MessageOut(renderTree(t))
case t.kind
of nkEmpty:
nil
of nkStmtList:
for i in countup(0, sonsLen(t) - 1): genStmts(p, t.sons[i])
of nkBlockStmt: genBlock(p, t, a)
of nkIfStmt: genIfStmt(p, t)
of nkWhileStmt: genWhileStmt(p, t)
of nkVarSection: genVarStmt(p, t)
of nkConstSection: genConstStmt(p, t)
of nkForStmt: internalError(t.info, "for statement not eliminated")
of nkCaseStmt: genCaseStmt(p, t)
of nkReturnStmt: genReturnStmt(p, t)
of nkBreakStmt: genBreakStmt(p, t)
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
nkCallStrLit:
genLineDir(p, t)
initLocExpr(p, t, a)
of nkAsgn: genAsgn(p, t)
of nkFastAsgn: genFastAsgn(p, t)
of nkDiscardStmt:
genLineDir(p, t)
initLocExpr(p, t.sons[0], a)
of nkAsmStmt: genAsmStmt(p, t)
of nkTryStmt:
if gCmd == cmdCompileToCpp: genTryStmtCpp(p, t)
else: genTryStmt(p, t)
of nkRaiseStmt: genRaiseStmt(p, t)
of nkTypeSection:
# we have to emit the type information for object types here to support
# separate compilation:
genTypeSection(p.module, t)
of nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, nkImportStmt,
nkFromStmt, nkTemplateDef, nkMacroDef:
nil
of nkPragma: genPragma(p, t)
of nkProcDef, nkMethodDef, nkConverterDef:
if (t.sons[genericParamsPos] == nil):
prc = t.sons[namePos].sym
if not (optDeadCodeElim in gGlobalOptions) and
not (sfDeadCodeElim in getModule(prc).flags) or
({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
(prc.kind == skMethod):
if (t.sons[codePos] != nil) or (lfDynamicLib in prc.loc.flags):
genProc(p.module, prc)
else: internalError(t.info, "genStmts(" & $t.kind & ')')

788
rod/ccgtypes.nim Executable file
View File

@@ -0,0 +1,788 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
#var
# newDummyVar: int; // just to check the symbol file mechanism
# ------------------------- Name Mangling --------------------------------
proc mangle(name: string): string =
case name[0]
of 'a'..'z':
result = ""
add(result, chr(ord(name[0]) - ord('a') + ord('A')))
of '0'..'9', 'A'..'Z':
result = ""
add(result, name[0])
else: result = "HEX" & toHex(ord(name[0]), 2)
for i in countup(0 + 1, len(name) + 0 - 1):
case name[i]
of 'A'..'Z':
add(result, chr(ord(name[i]) - ord('A') + ord('a')))
of '_':
nil
of 'a'..'z', '0'..'9':
add(result, name[i])
else:
add(result, "HEX")
add(result, toHex(ord(name[i]), 2))
proc mangleName(s: PSym): PRope =
result = s.loc.r
if result == nil:
if gCmd == cmdCompileToLLVM:
case s.kind
of skProc, skMethod, skConverter, skConst:
result = toRope("@")
of skVar:
if (sfGlobal in s.flags): result = toRope("@")
else: result = toRope("%")
of skForVar, skTemp, skParam, skType, skEnumField, skModule:
result = toRope("%")
else: InternalError(s.info, "mangleName")
app(result, toRope(mangle(s.name.s)))
app(result, "_")
app(result, toRope(s.id))
if optGenMapping in gGlobalOptions:
if s.owner != nil:
appf(gMapping, "r\"$1.$2\": $3$n",
[toRope(s.owner.Name.s), toRope(s.name.s), result])
s.loc.r = result
proc getTypeName(typ: PType): PRope =
if (typ.sym != nil) and ({sfImportc, sfExportc} * typ.sym.flags != {}) and
(gCmd != cmdCompileToLLVM):
result = typ.sym.loc.r
else:
if typ.loc.r == nil: typ.loc.r = ropeff("TY$1", "%TY$1", [toRope(typ.id)])
result = typ.loc.r
if result == nil: InternalError("getTypeName: " & $typ.kind)
proc mapType(typ: PType): TCTypeKind =
case typ.kind
of tyNone:
result = ctVoid
of tyBool:
result = ctBool
of tyChar:
result = ctChar
of tySet:
case int(getSize(typ))
of 1: result = ctInt8
of 2: result = ctInt16
of 4: result = ctInt32
of 8: result = ctInt64
else: result = ctArray
of tyOpenArray, tyArrayConstr, tyArray:
result = ctArray
of tyObject, tyTuple:
result = ctStruct
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal:
result = mapType(lastSon(typ))
of tyEnum:
if firstOrd(typ) < 0:
result = ctInt32
else:
case int(getSize(typ))
of 1: result = ctUInt8
of 2: result = ctUInt16
of 4: result = ctInt32
of 8: result = ctInt64
else: internalError("mapType")
of tyRange:
result = mapType(typ.sons[0])
of tyPtr, tyVar, tyRef:
case typ.sons[0].kind
of tyOpenArray, tyArrayConstr, tyArray: result = ctArray
else: result = ctPtr
of tyPointer:
result = ctPtr
of tySequence:
result = ctNimSeq
of tyProc:
result = ctProc
of tyString:
result = ctNimStr
of tyCString:
result = ctCString
of tyInt..tyFloat128:
result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
else: InternalError("mapType")
proc mapReturnType(typ: PType): TCTypeKind =
if skipTypes(typ, abstractInst).kind == tyArray: result = ctPtr
else: result = mapType(typ)
proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope
proc needsComplexAssignment(typ: PType): bool =
result = containsGarbageCollectedRef(typ)
proc isInvalidReturnType(rettype: PType): bool =
# Arrays and sets cannot be returned by a C procedure, because C is
# such a poor programming language.
# We exclude records with refs too. This enhances efficiency and
# is necessary for proper code generation of assignments.
if rettype == nil:
result = true
else:
case mapType(rettype)
of ctArray:
result = not (skipTypes(rettype, abstractInst).kind in
{tyVar, tyRef, tyPtr})
of ctStruct:
result = needsComplexAssignment(skipTypes(rettype, abstractInst))
else: result = false
const
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
"N_STDCALL", "N_CDECL", "N_SAFECALL", "N_SYSCALL", # this is probably not correct for all platforms,
# but one can #define it to what one wants so it will be no problem
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_CLOSURE", "N_NOCONV"]
CallingConvToStrLLVM: array[TCallingConvention, string] = ["fastcc $1",
"stdcall $1", "ccc $1", "safecall $1", "syscall $1", "$1 alwaysinline",
"$1 noinline", "fastcc $1", "ccc $1", "$1"]
proc CacheGetType(tab: TIdTable, key: PType): PRope =
# returns nil if we need to declare this type
# since types are now unique via the ``GetUniqueType`` mechanism, this slow
# linear search is not necessary anymore:
result = PRope(IdTableGet(tab, key))
proc getTempName(): PRope =
result = ropeff("TMP$1", "%TMP$1", [toRope(gId)])
inc(gId)
proc getGlobalTempName(): PRope =
result = ropeff("TMP$1", "@TMP$1", [toRope(gId)])
inc(gId)
proc ccgIntroducedPtr(s: PSym): bool =
var pt: PType
pt = s.typ
assert(not (sfResult in s.flags))
case pt.Kind
of tyObject:
# XXX quick hack floatSize*2 for the pegs module under 64bit
if (optByRef in s.options) or (getSize(pt) > platform.floatSize * 2):
result = true # requested anyway
elif (tfFinal in pt.flags) and (pt.sons[0] == nil):
result = false # no need, because no subtyping possible
else:
result = true # ordinary objects are always passed by reference,
# otherwise casting doesn't work
of tyTuple:
result = (getSize(pt) > platform.floatSize) or (optByRef in s.options)
else: result = false
proc fillResult(param: PSym) =
fillLoc(param.loc, locParam, param.typ, ropeff("Result", "%Result", []),
OnStack)
if (mapReturnType(param.typ) != ctArray) and IsInvalidReturnType(param.typ):
incl(param.loc.flags, lfIndirect)
param.loc.s = OnUnknown
proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
check: var TIntSet) =
var
j: int
param: PSym
arr: PType
params = nil
if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]):
rettype = toRope("void")
else:
rettype = getTypeDescAux(m, t.sons[0], check)
for i in countup(1, sonsLen(t.n) - 1):
if t.n.sons[i].kind != nkSym: InternalError(t.n.info, "genProcParams")
param = t.n.sons[i].sym
fillLoc(param.loc, locParam, param.typ, mangleName(param), OnStack)
app(params, getTypeDescAux(m, param.typ, check))
if ccgIntroducedPtr(param):
app(params, "*")
incl(param.loc.flags, lfIndirect)
param.loc.s = OnUnknown
app(params, " ")
app(params, param.loc.r) # declare the len field for open arrays:
arr = param.typ
if arr.kind == tyVar: arr = arr.sons[0]
j = 0
while arr.Kind == tyOpenArray:
# need to pass hidden parameter:
appff(params, ", NI $1Len$2", ", @NI $1Len$2", [param.loc.r, toRope(j)])
inc(j)
arr = arr.sons[0]
if i < sonsLen(t.n) - 1: app(params, ", ")
if (t.sons[0] != nil) and isInvalidReturnType(t.sons[0]):
if params != nil: app(params, ", ")
arr = t.sons[0]
app(params, getTypeDescAux(m, arr, check))
if (mapReturnType(t.sons[0]) != ctArray) or (gCmd == cmdCompileToLLVM):
app(params, "*")
appff(params, " Result", " @Result", [])
if t.callConv == ccClosure:
if params != nil: app(params, ", ")
app(params, "void* ClPart")
if tfVarargs in t.flags:
if params != nil: app(params, ", ")
app(params, "...")
if (params == nil) and (gCmd != cmdCompileToLLVM): app(params, "void)")
else: app(params, ")")
params = con("(", params)
proc isImportedType(t: PType): bool =
result = (t.sym != nil) and (sfImportc in t.sym.flags)
proc typeNameOrLiteral(t: PType, literal: string): PRope =
if (t.sym != nil) and (sfImportc in t.sym.flags) and (t.sym.magic == mNone):
result = getTypeName(t)
else:
result = toRope(literal)
proc getSimpleTypeDesc(m: BModule, typ: PType): PRope =
const
NumericalTypeToStr: array[tyInt..tyFloat128, string] = ["NI", "NI8", "NI16",
"NI32", "NI64", "NF", "NF32", "NF64", "NF128"]
case typ.Kind
of tyPointer:
result = typeNameOrLiteral(typ, "void*")
of tyEnum:
if firstOrd(typ) < 0:
result = typeNameOrLiteral(typ, "NI32")
else:
case int(getSize(typ))
of 1: result = typeNameOrLiteral(typ, "NU8")
of 2: result = typeNameOrLiteral(typ, "NU16")
of 4: result = typeNameOrLiteral(typ, "NI32")
of 8: result = typeNameOrLiteral(typ, "NI64")
else:
internalError(typ.sym.info, "getSimpleTypeDesc: " & $(getSize(typ)))
result = nil
of tyString:
useMagic(m, "NimStringDesc")
result = typeNameOrLiteral(typ, "NimStringDesc*")
of tyCstring:
result = typeNameOrLiteral(typ, "NCSTRING")
of tyBool:
result = typeNameOrLiteral(typ, "NIM_BOOL")
of tyChar:
result = typeNameOrLiteral(typ, "NIM_CHAR")
of tyNil:
result = typeNameOrLiteral(typ, "0")
of tyInt..tyFloat128:
result = typeNameOrLiteral(typ, NumericalTypeToStr[typ.Kind])
of tyRange:
result = getSimpleTypeDesc(m, typ.sons[0])
else: result = nil
proc getTypePre(m: BModule, typ: PType): PRope =
if typ == nil:
result = toRope("void")
else:
result = getSimpleTypeDesc(m, typ)
if result == nil: result = CacheGetType(m.typeCache, typ)
proc getForwardStructFormat(): string =
if gCmd == cmdCompileToCpp: result = "struct $1;$n"
else: result = "typedef struct $1 $1;$n"
proc getTypeForward(m: BModule, typ: PType): PRope =
result = CacheGetType(m.forwTypeCache, typ)
if result != nil: return
result = getTypePre(m, typ)
if result != nil: return
case typ.kind
of tySequence, tyTuple, tyObject:
result = getTypeName(typ)
if not isImportedType(typ):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
IdTablePut(m.forwTypeCache, typ, result)
else: InternalError("getTypeForward(" & $typ.kind & ')')
proc mangleRecFieldName(field: PSym, rectype: PType): PRope =
if (rectype.sym != nil) and
({sfImportc, sfExportc} * rectype.sym.flags != {}):
result = field.loc.r
else:
result = toRope(mangle(field.name.s))
if result == nil: InternalError(field.info, "mangleRecFieldName")
proc genRecordFieldsAux(m: BModule, n: PNode, accessExpr: PRope, rectype: PType,
check: var TIntSet): PRope =
var
ae, uname, sname, a: PRope
k: PNode
field: PSym
result = nil
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
app(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check))
of nkRecCase:
if (n.sons[0].kind != nkSym): InternalError(n.info, "genRecordFieldsAux")
app(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check))
uname = toRope(mangle(n.sons[0].sym.name.s) & 'U')
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, uname])
else: ae = uname
app(result, "union {" & tnl)
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
k = lastSon(n.sons[i])
if k.kind != nkSym:
sname = con("S", toRope(i))
a = genRecordFieldsAux(m, k, ropef("$1.$2", [ae, sname]), rectype,
check)
if a != nil:
app(result, "struct {")
app(result, a)
appf(result, "} $1;$n", [sname])
else:
app(result, genRecordFieldsAux(m, k, ae, rectype, check))
else: internalError("genRecordFieldsAux(record case branch)")
appf(result, "} $1;$n", [uname])
of nkSym:
field = n.sym
assert(field.ast == nil)
sname = mangleRecFieldName(field, rectype)
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
else: ae = sname
fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
else: internalError(n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
result = genRecordFieldsAux(m, typ.n, nil, typ, check)
proc getRecordDesc(m: BModule, typ: PType, name: PRope,
check: var TIntSet): PRope =
# declare the record:
var hasField = false
if typ.kind == tyObject:
useMagic(m, "TNimType")
if typ.sons[0] == nil:
if (typ.sym != nil) and (sfPure in typ.sym.flags) or
(tfFinal in typ.flags):
result = ropef("struct $1 {$n", [name])
else:
result = ropef("struct $1 {$nTNimType* m_type;$n", [name])
hasField = true
elif gCmd == cmdCompileToCpp:
result = ropef("struct $1 : public $2 {$n",
[name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true
else:
result = ropef("struct $1 {$n $2 Sup;$n",
[name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true
else:
result = ropef("struct $1 {$n", [name])
var desc = getRecordFields(m, typ, check)
if (desc == nil) and not hasField:
appf(result, "char dummy;$n", [])
else:
app(result, desc)
app(result, "};" & tnl)
proc getTupleDesc(m: BModule, typ: PType, name: PRope,
check: var TIntSet): PRope =
result = ropef("struct $1 {$n", [name])
var desc: PRope = nil
for i in countup(0, sonsLen(typ) - 1):
appf(desc, "$1 Field$2;$n",
[getTypeDescAux(m, typ.sons[i], check), toRope(i)])
if (desc == nil): app(result, "char dummy;" & tnl)
else: app(result, desc)
app(result, "};" & tnl)
proc pushType(m: BModule, typ: PType) =
add(m.typeStack, typ)
proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
# returns only the type's name
var
name, rettype, desc, recdesc: PRope
n: biggestInt
t, et: PType
t = getUniqueType(typ)
if t == nil: InternalError("getTypeDescAux: t == nil")
if t.sym != nil: useHeader(m, t.sym)
result = getTypePre(m, t)
if result != nil: return
if IntSetContainsOrIncl(check, t.id):
InternalError("cannot generate C type for: " & typeToString(typ))
# XXX: this BUG is hard to fix -> we need to introduce helper structs,
# but determining when this needs to be done is hard. We should split
# C type generation into an analysis and a code generation phase somehow.
case t.Kind
of tyRef, tyPtr, tyVar:
et = getUniqueType(t.sons[0])
if et.kind in {tyArrayConstr, tyArray, tyOpenArray}:
et = getUniqueType(elemType(et))
case et.Kind
of tyObject, tyTuple:
# no restriction! We have a forward declaration for structs
name = getTypeForward(m, et)
result = con(name, "*")
IdTablePut(m.typeCache, t, result)
pushType(m, et)
of tySequence:
# no restriction! We have a forward declaration for structs
name = getTypeForward(m, et)
result = con(name, "**")
IdTablePut(m.typeCache, t, result)
pushType(m, et)
else:
# else we have a strong dependency :-(
result = con(getTypeDescAux(m, et, check), "*")
IdTablePut(m.typeCache, t, result)
of tyOpenArray:
et = getUniqueType(t.sons[0])
result = con(getTypeDescAux(m, et, check), "*")
IdTablePut(m.typeCache, t, result)
of tyProc:
result = getTypeName(t)
IdTablePut(m.typeCache, t, result)
genProcParams(m, t, rettype, desc, check)
if not isImportedType(t):
if t.callConv != ccClosure: # procedure vars may need a closure!
appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
[toRope(CallingConvToStr[t.callConv]), rettype, result, desc])
else:
appf(m.s[cfsTypes], "typedef struct $1 {$n" &
"N_CDECL_PTR($2, PrcPart) $3;$n" & "void* ClPart;$n};$n",
[result, rettype, desc])
of tySequence:
# we cannot use getTypeForward here because then t would be associated
# with the name of the struct, not with the pointer to the struct:
result = CacheGetType(m.forwTypeCache, t)
if result == nil:
result = getTypeName(t)
if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
IdTablePut(m.forwTypeCache, t, result)
assert(CacheGetType(m.typeCache, t) == nil)
IdTablePut(m.typeCache, t, con(result, "*"))
if not isImportedType(t):
useMagic(m, "TGenericSeq")
if skipTypes(t.sons[0], abstractInst).kind != tyEmpty:
appf(m.s[cfsSeqTypes], "struct $2 {$n" & " TGenericSeq Sup;$n" &
" $1 data[SEQ_DECL_SIZE];$n" & "};$n",
[getTypeDescAux(m, t.sons[0], check), result])
else:
result = toRope("TGenericSeq")
app(result, "*")
of tyArrayConstr, tyArray:
n = lengthOrd(t)
if n <= 0:
n = 1 # make an array of at least one element
result = getTypeName(t)
IdTablePut(m.typeCache, t, result)
if not isImportedType(t):
appf(m.s[cfsTypes], "typedef $1 $2[$3];$n",
[getTypeDescAux(m, t.sons[1], check), result, ToRope(n)])
of tyObject, tyTuple:
result = CacheGetType(m.forwTypeCache, t)
if result == nil:
result = getTypeName(t)
if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
IdTablePut(m.forwTypeCache, t, result)
IdTablePut(m.typeCache, t, result) # always call for sideeffects:
if t.n != nil: recdesc = getRecordDesc(m, t, result, check)
else: recdesc = getTupleDesc(m, t, result, check)
if not isImportedType(t): app(m.s[cfsTypes], recdesc)
of tySet:
case int(getSize(t))
of 1: result = toRope("NU8")
of 2: result = toRope("NU16")
of 4: result = toRope("NU32")
of 8: result = toRope("NU64")
else:
result = getTypeName(t)
IdTablePut(m.typeCache, t, result)
if not isImportedType(t):
appf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
[result, toRope(getSize(t))])
of tyGenericInst, tyDistinct, tyOrdinal:
result = getTypeDescAux(m, lastSon(t), check)
else:
InternalError("getTypeDescAux(" & $t.kind & ')')
result = nil
proc getTypeDesc(m: BModule, typ: PType): PRope =
var check: TIntSet
IntSetInit(check)
result = getTypeDescAux(m, typ, check)
proc getTypeDesc(m: BModule, magic: string): PRope =
var sym = magicsys.getCompilerProc(magic)
if sym != nil:
result = getTypeDesc(m, sym.typ)
else:
rawMessage(errSystemNeeds, magic)
result = nil
proc finishTypeDescriptions(m: BModule) =
var i = 0
while i < len(m.typeStack):
discard getTypeDesc(m, m.typeStack[i])
inc(i)
proc genProcHeader(m: BModule, prc: PSym): PRope =
var
rettype, params: PRope
check: TIntSet
# using static is needed for inline procs
if (prc.typ.callConv == ccInline): result = toRope("static ")
else: result = nil
IntSetInit(check)
fillLoc(prc.loc, locProc, prc.typ, mangleName(prc), OnUnknown)
genProcParams(m, prc.typ, rettype, params, check)
appf(result, "$1($2, $3)$4",
[toRope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r, params])
proc genTypeInfo(m: BModule, typ: PType): PRope
proc getNimNode(m: BModule): PRope =
result = ropef("$1[$2]", [m.typeNodesName, toRope(m.typeNodes)])
inc(m.typeNodes)
proc getNimType(m: BModule): PRope =
result = ropef("$1[$2]", [m.nimTypesName, toRope(m.nimTypes)])
inc(m.nimTypes)
proc allocMemTI(m: BModule, typ: PType, name: PRope) =
var tmp = getNimType(m)
appf(m.s[cfsTypeInit2], "$2 = &$1;$n", [tmp, name])
proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) =
var nimtypeKind: int
allocMemTI(m, typ, name)
if (typ.kind == tyObject) and (tfFinal in typ.flags) and
(typ.sons[0] == nil):
nimtypeKind = ord(high(TTypeKind)) + 1 # tyPureObject
else:
nimtypeKind = ord(typ.kind)
appf(m.s[cfsTypeInit3],
"$1->size = sizeof($2);$n" & "$1->kind = $3;$n" & "$1->base = $4;$n",
[name, getTypeDesc(m, typ), toRope(nimtypeKind), base])
# compute type flags for GC optimization
var flags = 0
if not containsGarbageCollectedRef(typ): flags = flags or 1
if not canFormAcycle(typ): flags = flags or 2
#else MessageOut("can contain a cycle: " & typeToString(typ))
if flags != 0:
appf(m.s[cfsTypeInit3], "$1->flags = $2;$n", [name, toRope(flags)])
appf(m.s[cfsVars], "TNimType* $1; /* $2 */$n",
[name, toRope(typeToString(typ))])
proc genTypeInfoAux(m: BModule, typ: PType, name: PRope) =
var base: PRope
if (sonsLen(typ) > 0) and (typ.sons[0] != nil):
base = genTypeInfo(m, typ.sons[0])
else:
base = toRope("0")
genTypeInfoAuxBase(m, typ, name, base)
proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: PRope) =
var
tmp, tmp2: PRope
length, x, y: int
field: PSym
b: PNode
case n.kind
of nkRecList:
length = sonsLen(n)
if length == 1:
genObjectFields(m, typ, n.sons[0], expr)
elif length > 0:
tmp = getTempName()
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n",
[tmp, toRope(length)])
for i in countup(0, length - 1):
tmp2 = getNimNode(m)
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2])
genObjectFields(m, typ, n.sons[i], tmp2)
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
[expr, toRope(length), tmp])
else:
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n",
[expr, toRope(length)])
of nkRecCase:
length = sonsLen(n)
assert(n.sons[0].kind == nkSym)
field = n.sons[0].sym
tmp = getTempName()
useMagic(m, "chckNil")
appf(m.s[cfsTypeInit3], "$1.kind = 3;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"chckNil($1.typ);$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
"$1.len = $7;$n", [expr, getTypeDesc(m, typ), field.loc.r,
genTypeInfo(m, field.typ), makeCString(field.name.s),
tmp, toRope(lengthOrd(field.typ))])
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n",
[tmp, toRope(lengthOrd(field.typ) + 1)])
for i in countup(1, length - 1):
b = n.sons[i] # branch
tmp2 = getNimNode(m)
genObjectFields(m, typ, lastSon(b), tmp2)
case b.kind
of nkOfBranch:
if sonsLen(b) < 2:
internalError(b.info, "genObjectFields; nkOfBranch broken")
for j in countup(0, sonsLen(b) - 2):
if b.sons[j].kind == nkRange:
x = int(getOrdValue(b.sons[j].sons[0]))
y = int(getOrdValue(b.sons[j].sons[1]))
while x <= y:
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(x), tmp2])
inc(x)
else:
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
[tmp, toRope(getOrdValue(b.sons[j])), tmp2])
of nkElse:
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
[tmp, toRope(lengthOrd(field.typ)), tmp2])
else: internalError(n.info, "genObjectFields(nkRecCase)")
of nkSym:
field = n.sym
useMagic(m, "chckNil")
appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"chckNil($1.typ);$n" & "$1.name = $5;$n", [expr, getTypeDesc(m, typ),
field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)])
else: internalError(n.info, "genObjectFields")
proc genObjectInfo(m: BModule, typ: PType, name: PRope) =
var tmp: PRope
if typ.kind == tyObject: genTypeInfoAux(m, typ, name)
else: genTypeInfoAuxBase(m, typ, name, toRope("0"))
tmp = getNimNode(m)
genObjectFields(m, typ, typ.n, tmp)
appf(m.s[cfsTypeInit3], "$1->node = &$2;$n", [name, tmp])
proc genTupleInfo(m: BModule, typ: PType, name: PRope) =
var
tmp, expr, tmp2: PRope
length: int
a: PType
genTypeInfoAuxBase(m, typ, name, toRope("0"))
expr = getNimNode(m)
length = sonsLen(typ)
if length > 0:
tmp = getTempName()
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, toRope(length)])
for i in countup(0, length - 1):
a = typ.sons[i]
tmp2 = getNimNode(m)
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2])
useMagic(m, "chckNil")
appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
"$1.offset = offsetof($2, Field$3);$n" & "$1.typ = $4;$n" &
"chckNil($1.typ);$n" & "$1.name = \"Field$3\";$n",
[tmp2, getTypeDesc(m, typ), toRope(i), genTypeInfo(m, a)])
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
[expr, toRope(length), tmp])
else:
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n",
[expr, toRope(length)])
appf(m.s[cfsTypeInit3], "$1->node = &$2;$n", [name, tmp])
proc genEnumInfo(m: BModule, typ: PType, name: PRope) =
var
nodePtrs, elemNode, enumNames, enumArray, counter, specialCases: PRope
length, firstNimNode: int
field: PSym
# Type information for enumerations is quite heavy, so we do some
# optimizations here: The ``typ`` field is never set, as it is redundant
# anyway. We generate a cstring array and a loop over it. Exceptional
# positions will be reset after the loop.
genTypeInfoAux(m, typ, name)
nodePtrs = getTempName()
length = sonsLen(typ.n)
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n",
[nodePtrs, toRope(length)])
enumNames = nil
specialCases = nil
firstNimNode = m.typeNodes
for i in countup(0, length - 1):
assert(typ.n.sons[i].kind == nkSym)
field = typ.n.sons[i].sym
elemNode = getNimNode(m)
app(enumNames, makeCString(field.name.s))
if i < length - 1: app(enumNames, ", " & tnl)
if field.position != i:
appf(specialCases, "$1.offset = $2;$n", [elemNode, toRope(field.position)])
enumArray = getTempName()
counter = getTempName()
appf(m.s[cfsTypeInit1], "NI $1;$n", [counter])
appf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n",
[enumArray, toRope(length), enumNames])
appf(m.s[cfsTypeInit3], "for ($1 = 0; $1 < $2; $1++) {$n" &
"$3[$1+$4].kind = 1;$n" & "$3[$1+$4].offset = $1;$n" &
"$3[$1+$4].name = $5[$1];$n" & "$6[$1] = &$3[$1+$4];$n" & "}$n", [counter,
toRope(length), m.typeNodesName, toRope(firstNimNode), enumArray, nodePtrs])
app(m.s[cfsTypeInit3], specialCases)
appf(m.s[cfsTypeInit3],
"$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n$4->node = &$1;$n",
[getNimNode(m), toRope(length), nodePtrs, name])
proc genSetInfo(m: BModule, typ: PType, name: PRope) =
assert(typ.sons[0] != nil)
genTypeInfoAux(m, typ, name)
var tmp = getNimNode(m)
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3->node = &$1;$n",
[tmp, toRope(firstOrd(typ)), name])
proc genArrayInfo(m: BModule, typ: PType, name: PRope) =
genTypeInfoAuxBase(m, typ, name, genTypeInfo(m, typ.sons[1]))
var
gToTypeInfoId: TIiTable
proc genTypeInfo(m: BModule, typ: PType): PRope =
var dataGenerated: bool
var t = getUniqueType(typ)
var id = IiTableGet(gToTypeInfoId, t.id)
if id == invalidKey:
dataGenerated = false
id = t.id # getID();
IiTablePut(gToTypeInfoId, t.id, id)
else:
dataGenerated = true
result = ropef("NTI$1", [toRope(id)])
if not IntSetContainsOrIncl(m.typeInfoMarker, id):
# declare type information structures:
useMagic(m, "TNimType")
useMagic(m, "TNimNode")
appf(m.s[cfsVars], "extern TNimType* $1; /* $2 */$n",
[result, toRope(typeToString(t))])
if dataGenerated: return
case t.kind
of tyEmpty:
result = toRope("0")
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128,
tyVar:
genTypeInfoAuxBase(gNimDat, t, result, toRope("0"))
of tyRef, tyPtr, tySequence, tyRange:
genTypeInfoAux(gNimDat, t, result)
of tyArrayConstr, tyArray:
genArrayInfo(gNimDat, t, result)
of tySet:
genSetInfo(gNimDat, t, result)
of tyEnum:
genEnumInfo(gNimDat, t, result)
of tyObject:
genObjectInfo(gNimDat, t, result)
of tyTuple:
if t.n != nil: genObjectInfo(gNimDat, t, result)
else: genTupleInfo(gNimDat, t, result)
else: InternalError("genTypeInfo(" & $t.kind & ')')
proc genTypeSection(m: BModule, n: PNode) =
nil

138
rod/ccgutils.nim Executable file
View File

@@ -0,0 +1,138 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module declares some helpers for the C code generator.
import
ast, astalgo, ropes, lists, nhashes, strutils, types, msgs
proc toCChar*(c: Char): string
proc makeCString*(s: string): PRope
proc makeLLVMString*(s: string): PRope
proc TableGetType*(tab: TIdTable, key: PType): PObject
proc GetUniqueType*(key: PType): PType
# implementation
var gTypeTable: array[TTypeKind, TIdTable]
proc initTypeTables() =
for i in countup(low(TTypeKind), high(TTypeKind)): InitIdTable(gTypeTable[i])
proc GetUniqueType(key: PType): PType =
var
t: PType
k: TTypeKind
# this is a hotspot in the compiler!
result = key
if key == nil: return
k = key.kind
case k #
# case key.Kind of
# tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
# tyInt..tyFloat128, tyProc, tyAnyEnum: begin end;
# tyNone, tyForward:
# InternalError('GetUniqueType: ' + typeToString(key));
# tyGenericParam, tyGeneric, tyAbstract, tySequence,
# tyOpenArray, tySet, tyVar, tyRef, tyPtr, tyArrayConstr,
# tyArray, tyTuple, tyRange: begin
# // we have to do a slow linear search because types may need
# // to be compared by their structure:
# if IdTableHasObjectAsKey(gTypeTable, key) then exit;
# for h := 0 to high(gTypeTable.data) do begin
# t := PType(gTypeTable.data[h].key);
# if (t <> nil) and sameType(t, key) then begin result := t; exit end
# end;
# IdTablePut(gTypeTable, key, key);
# end;
# tyObject, tyEnum: begin
# result := PType(IdTableGet(gTypeTable, key));
# if result = nil then begin
# IdTablePut(gTypeTable, key, key);
# result := key;
# end
# end;
# tyGenericInst, tyAbstract: result := GetUniqueType(lastSon(key));
# end;
of tyObject, tyEnum:
result = PType(IdTableGet(gTypeTable[k], key))
if result == nil:
IdTablePut(gTypeTable[k], key, key)
result = key
of tyGenericInst, tyDistinct, tyOrdinal:
result = GetUniqueType(lastSon(key))
of tyProc:
nil
else:
# we have to do a slow linear search because types may need
# to be compared by their structure:
if IdTableHasObjectAsKey(gTypeTable[k], key): return
for h in countup(0, high(gTypeTable[k].data)):
t = PType(gTypeTable[k].data[h].key)
if (t != nil) and sameType(t, key):
return t
IdTablePut(gTypeTable[k], key, key)
proc TableGetType(tab: TIdTable, key: PType): PObject =
var t: PType
# returns nil if we need to declare this type
result = IdTableGet(tab, key)
if (result == nil) and (tab.counter > 0):
# we have to do a slow linear search because types may need
# to be compared by their structure:
for h in countup(0, high(tab.data)):
t = PType(tab.data[h].key)
if t != nil:
if sameType(t, key):
return tab.data[h].val
proc toCChar(c: Char): string =
case c
of '\0'..'\x1F', '\x80'..'\xFF': result = '\\' & toOctal(c)
of '\'', '\"', '\\': result = '\\' & c
else: result = $(c)
proc makeCString(s: string): PRope =
# BUGFIX: We have to split long strings into many ropes. Otherwise
# this could trigger an InternalError(). See the ropes module for
# further information.
const
MaxLineLength = 64
var res: string
result = nil
res = "\""
for i in countup(0, len(s) + 0 - 1):
if (i - 0 + 1) mod MaxLineLength == 0:
add(res, '\"')
add(res, "\n")
app(result, toRope(res)) # reset:
setlen(res, 1)
res[0] = '\"'
add(res, toCChar(s[i]))
add(res, '\"')
app(result, toRope(res))
proc makeLLVMString(s: string): PRope =
const
MaxLineLength = 64
var res: string
result = nil
res = "c\""
for i in countup(0, len(s) + 0 - 1):
if (i - 0 + 1) mod MaxLineLength == 0:
app(result, toRope(res))
setlen(res, 0)
case s[i]
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
add(res, '\\')
add(res, toHex(ord(s[i]), 2))
else: add(res, s[i])
add(res, "\\00\"")
app(result, toRope(res))
InitTypeTables()

905
rod/cgen.nim Executable file
View File

@@ -0,0 +1,905 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This is the new C code generator; much cleaner and faster
# than the old one. It also generates better code.
import
ast, astalgo, strutils, nhashes, trees, platform, magicsys, extccomp, options,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, rnimsyn, treetab, cgmeth
proc cgenPass*(): TPass
# implementation
type
TLabel = PRope # for the C generator a label is just a rope
TCFileSection = enum # the sections a generated C file consists of
cfsHeaders, # section for C include file headers
cfsForwardTypes, # section for C forward typedefs
cfsTypes, # section for C typedefs
cfsSeqTypes, # section for sequence types only
# this is needed for strange type generation
# reasons
cfsFieldInfo, # section for field information
cfsTypeInfo, # section for type information
cfsProcHeaders, # section for C procs prototypes
cfsData, # section for C constant data
cfsVars, # section for C variable declarations
cfsProcs, # section for C procs that are not inline
cfsTypeInit1, # section 1 for declarations of type information
cfsTypeInit2, # section 2 for initialization of type information
cfsTypeInit3, # section 3 for initialization of type information
cfsDebugInit, # section for initialization of debug information
cfsDynLibInit, # section for initialization of dynamic library binding
cfsDynLibDeinit # section for deinitialization of dynamic libraries
TCTypeKind = enum # describes the type kind of a C type
ctVoid, ctChar, ctBool, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctFloat, ctFloat32, ctFloat64,
ctFloat128, ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString
TCFileSections = array[TCFileSection, PRope] # TCFileSections represents a generated C file
TCProcSection = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc
cpsInit, # section for initialization of variables for C proc
cpsStmts # section of local statements for C proc
TCProcSections = array[TCProcSection, PRope] # TCProcSections represents a generated C proc
BModule = ref TCGen
BProc = ref TCProc
TBlock{.final.} = object
id*: int # the ID of the label; positive means that it
# has been used (i.e. the label should be emitted)
nestedTryStmts*: int # how many try statements is it nested into
TCProc{.final.} = object # represents C proc that is currently generated
s*: TCProcSections # the procs sections; short name for readability
prc*: PSym # the Nimrod proc that this C proc belongs to
BeforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
nestedTryStmts*: Natural # in how many nested try statements we are
# (the vars must be volatile then)
labels*: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks
options*: TOptions # options that should be used for code
# generation; this is the same as prc.options
# unless prc == nil
frameLen*: int # current length of frame descriptor
sendClosure*: PType # closure record type that we pass
receiveClosure*: PType # closure record type that we get
module*: BModule # used to prevent excessive parameter passing
TTypeSeq = seq[PType]
TCGen = object of TPassContext # represents a C source file
module*: PSym
filename*: string
s*: TCFileSections # sections of the C file
cfilename*: string # filename of the module (including path,
# without extension)
typeCache*: TIdTable # cache the generated types
forwTypeCache*: TIdTable # cache for forward declarations of types
declaredThings*: TIntSet # things we have declared in this .c file
declaredProtos*: TIntSet # prototypes we have declared in this .c file
headerFiles*: TLinkedList # needed headers to include
typeInfoMarker*: TIntSet # needed for generating type information
initProc*: BProc # code for init procedure
typeStack*: TTypeSeq # used for type generation
dataCache*: TNodeTable
forwardedProcs*: TSymSeq # keep forwarded procs here
typeNodes*, nimTypes*: int # used for type info generation
typeNodesName*, nimTypesName*: PRope # used for type info generation
labels*: natural # for generating unique module-scope names
var
mainModProcs, mainModInit: PRope # parts of the main module
gMapping: PRope # the generated mapping file (if requested)
gProcProfile: Natural # proc profile counter
gGeneratedSyms: TIntSet # set of ID's of generated symbols
gPendingModules: seq[BModule] = @ [] # list of modules that are not finished with code generation
gForwardedProcsCounter: int = 0
gNimDat: BModule # generated global data
proc ropeff(cformat, llvmformat: string, args: openarray[PRope]): PRope =
if gCmd == cmdCompileToLLVM: result = ropef(llvmformat, args)
else: result = ropef(cformat, args)
proc appff(dest: var PRope, cformat, llvmformat: string, args: openarray[PRope]) =
if gCmd == cmdCompileToLLVM: appf(dest, llvmformat, args)
else: appf(dest, cformat, args)
proc addForwardedProc(m: BModule, prc: PSym) =
var L: int
L = len(m.forwardedProcs)
setlen(m.forwardedProcs, L + 1)
m.forwardedProcs[L] = prc
inc(gForwardedProcsCounter)
proc addPendingModule(m: BModule) =
var L: int
for i in countup(0, high(gPendingModules)):
if gPendingModules[i] == m:
InternalError("module already pending: " & m.module.name.s)
L = len(gPendingModules)
setlen(gPendingModules, L + 1)
gPendingModules[L] = m
proc findPendingModule(m: BModule, s: PSym): BModule =
var ms: PSym
ms = getModule(s)
if ms.id == m.module.id:
return m
for i in countup(0, high(gPendingModules)):
result = gPendingModules[i]
if result.module.id == ms.id: return
InternalError(s.info, "no pending module found for: " & s.name.s)
proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) =
result.k = k
result.s = s
result.t = GetUniqueType(typ)
result.r = nil
result.a = - 1
result.flags = {}
proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) =
# fills the loc if it is not already initialized
if a.k == locNone:
a.k = k
a.t = getUniqueType(typ)
a.a = - 1
a.s = s
if a.r == nil: a.r = r
proc newProc(prc: PSym, module: BModule): BProc =
new(result)
result.prc = prc
result.module = module
if prc != nil: result.options = prc.options
else: result.options = gOptions
result.blocks = @ []
proc isSimpleConst(typ: PType): bool =
result = not (skipTypes(typ, abstractVar).kind in
{tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence})
proc useHeader(m: BModule, sym: PSym) =
if lfHeader in sym.loc.Flags:
assert(sym.annex != nil)
discard lists.IncludeStr(m.headerFiles, sym.annex.path)
proc UseMagic(m: BModule, name: string)
include
"ccgtypes.nim"
# ------------------------------ Manager of temporaries ------------------
proc getTemp(p: BProc, t: PType, result: var TLoc) =
inc(p.labels)
if gCmd == cmdCompileToLLVM:
result.r = con("%LOC", toRope(p.labels))
else:
result.r = con("LOC", toRope(p.labels))
appf(p.s[cpsLocals], "$1 $2;$n", [getTypeDesc(p.module, t), result.r])
result.k = locTemp
result.a = - 1
result.t = getUniqueType(t)
result.s = OnStack
result.flags = {}
proc cstringLit(p: BProc, r: var PRope, s: string): PRope =
if gCmd == cmdCompileToLLVM:
inc(p.module.labels)
inc(p.labels)
result = ropef("%LOC$1", [toRope(p.labels)])
appf(p.module.s[cfsData], "@C$1 = private constant [$2 x i8] $3$n",
[toRope(p.module.labels), toRope(len(s)), makeLLVMString(s)])
appf(r, "$1 = getelementptr [$2 x i8]* @C$3, %NI 0, %NI 0$n",
[result, toRope(len(s)), toRope(p.module.labels)])
else:
result = makeCString(s)
proc cstringLit(m: BModule, r: var PRope, s: string): PRope =
if gCmd == cmdCompileToLLVM:
inc(m.labels, 2)
result = ropef("%MOC$1", [toRope(m.labels - 1)])
appf(m.s[cfsData], "@MOC$1 = private constant [$2 x i8] $3$n",
[toRope(m.labels), toRope(len(s)), makeLLVMString(s)])
appf(r, "$1 = getelementptr [$2 x i8]* @MOC$3, %NI 0, %NI 0$n",
[result, toRope(len(s)), toRope(m.labels)])
else:
result = makeCString(s)
proc allocParam(p: BProc, s: PSym) =
var tmp: PRope
assert(s.kind == skParam)
if not (lfParamCopy in s.loc.flags):
inc(p.labels)
tmp = con("%LOC", toRope(p.labels))
incl(s.loc.flags, lfParamCopy)
incl(s.loc.flags, lfIndirect)
appf(p.s[cpsInit], "$1 = alloca $3$n" & "store $3 $2, $3* $1$n",
[tmp, s.loc.r, getTypeDesc(p.module, s.loc.t)])
s.loc.r = tmp
proc localDebugInfo(p: BProc, s: PSym) =
var name, a: PRope
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
if gCmd == cmdCompileToLLVM:
# "address" is the 0th field
# "typ" is the 1rst field
# "name" is the 2nd field
name = cstringLit(p, p.s[cpsInit], normalize(s.name.s))
if (s.kind == skParam) and not ccgIntroducedPtr(s): allocParam(p, s)
inc(p.labels, 3)
appf(p.s[cpsInit], "%LOC$6 = getelementptr %TF* %F, %NI 0, $1, %NI 0$n" &
"%LOC$7 = getelementptr %TF* %F, %NI 0, $1, %NI 1$n" &
"%LOC$8 = getelementptr %TF* %F, %NI 0, $1, %NI 2$n" &
"store i8* $2, i8** %LOC$6$n" & "store $3* $4, $3** %LOC$7$n" &
"store i8* $5, i8** %LOC$8$n", [toRope(p.frameLen), s.loc.r,
getTypeDesc(p.module, "TNimType"),
genTypeInfo(p.module, s.loc.t), name,
toRope(p.labels), toRope(p.labels - 1),
toRope(p.labels - 2)])
else:
a = con("&", s.loc.r)
if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r
appf(p.s[cpsInit],
"F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n", [
toRope(p.frameLen), makeCString(normalize(s.name.s)), a,
genTypeInfo(p.module, s.loc.t)])
inc(p.frameLen)
proc assignLocalVar(p: BProc, s: PSym) =
#assert(s.loc.k == locNone) // not yet assigned
# this need not be fullfilled for inline procs; they are regenerated
# for each module that uses them!
if s.loc.k == locNone:
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack)
if gCmd == cmdCompileToLLVM:
appf(p.s[cpsLocals], "$1 = alloca $2$n",
[s.loc.r, getTypeDesc(p.module, s.loc.t)])
incl(s.loc.flags, lfIndirect)
else:
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.s[cpsLocals], " register")
if (sfVolatile in s.flags) or (p.nestedTryStmts > 0):
app(p.s[cpsLocals], " volatile")
appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
localDebugInfo(p, s)
proc assignGlobalVar(p: BProc, s: PSym) =
if s.loc.k == locNone:
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
if gCmd == cmdCompileToLLVM:
appf(p.module.s[cfsVars], "$1 = linkonce global $2 zeroinitializer$n",
[s.loc.r, getTypeDesc(p.module, s.loc.t)])
incl(s.loc.flags, lfIndirect)
else:
useHeader(p.module, s)
if lfNoDecl in s.loc.flags: return
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
if sfThreadVar in s.flags: app(p.module.s[cfsVars], " NIM_THREADVAR")
appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
if {optStackTrace, optEndb} * p.module.module.options ==
{optStackTrace, optEndb}:
useMagic(p.module, "dbgRegisterGlobal")
appff(p.module.s[cfsDebugInit], "dbgRegisterGlobal($1, &$2, $3);$n",
"call void @dbgRegisterGlobal(i8* $1, i8* $2, $4* $3)$n", [cstringLit(
p, p.module.s[cfsDebugInit], normalize(s.owner.name.s & '.' & s.name.s)),
s.loc.r, genTypeInfo(p.module, s.typ), getTypeDesc(p.module, "TNimType")])
proc iff(cond: bool, the, els: PRope): PRope =
if cond: result = the
else: result = els
proc assignParam(p: BProc, s: PSym) =
assert(s.loc.r != nil)
if (sfAddrTaken in s.flags) and (gCmd == cmdCompileToLLVM): allocParam(p, s)
localDebugInfo(p, s)
proc fillProcLoc(sym: PSym) =
if sym.loc.k == locNone:
fillLoc(sym.loc, locProc, sym.typ, mangleName(sym), OnStack)
proc getLabel(p: BProc): TLabel =
inc(p.labels)
result = con("LA", toRope(p.labels))
proc fixLabel(p: BProc, labl: TLabel) =
appf(p.s[cpsStmts], "$1: ;$n", [labl])
proc genVarPrototype(m: BModule, sym: PSym)
proc genConstPrototype(m: BModule, sym: PSym)
proc genProc(m: BModule, prc: PSym)
proc genStmts(p: BProc, t: PNode)
proc genProcPrototype(m: BModule, sym: PSym)
include
"ccgexprs.nim"
include
"ccgstmts.nim"
# ----------------------------- dynamic library handling -----------------
# We don't finalize dynamic libs as this does the OS for us.
proc libCandidates(s: string, dest: var TStringSeq) =
var
prefix, suffix: string
le, ri, L: int
temp: TStringSeq
le = strutils.find(s, '(')
ri = strutils.find(s, ')')
if (le >= 0) and (ri > le):
prefix = copy(s, 0, le - 1)
suffix = copy(s, ri + 1)
temp = split(copy(s, le + 1, ri - 1), {'|'})
for i in countup(0, high(temp)):
libCandidates(prefix & temp[i] & suffix, dest)
else:
add(dest, s)
proc loadDynamicLib(m: BModule, lib: PLib) =
var
tmp, loadlib: PRope
s: TStringSeq
assert(lib != nil)
if not lib.generated:
lib.generated = true
tmp = getGlobalTempName()
assert(lib.name == nil)
lib.name = tmp # BUGFIX: useMagic has awful side-effects
appff(m.s[cfsVars], "static void* $1;$n",
"$1 = linkonce global i8* zeroinitializer$n", [tmp])
s = @ []
libCandidates(lib.path, s)
loadlib = nil
for i in countup(0, high(s)):
inc(m.labels)
if i > 0: app(loadlib, "||")
appff(loadlib, "($1 = nimLoadLibrary((NimStringDesc*) &$2))$n", "%MOC$4 = call i8* @nimLoadLibrary($3 $2)$n" &
"store i8* %MOC$4, i8** $1$n", [tmp, getStrLit(m, s[i]),
getTypeDesc(m, getSysType(tyString)), toRope(m.labels)])
appff(m.s[cfsDynLibInit],
"if (!($1)) nimLoadLibraryError((NimStringDesc*) &$2);$n",
"XXX too implement", [loadlib, getStrLit(m, lib.path)]) #appf(m.s[cfsDynLibDeinit],
# 'if ($1 != NIM_NIL) nimUnloadLibrary($1);$n', [tmp]);
useMagic(m, "nimLoadLibrary")
useMagic(m, "nimUnloadLibrary")
useMagic(m, "NimStringDesc")
useMagic(m, "nimLoadLibraryError")
if lib.name == nil: InternalError("loadDynamicLib")
proc SymInDynamicLib(m: BModule, sym: PSym) =
var
lib: PLib
extname, tmp: PRope
lib = sym.annex
extname = sym.loc.r
loadDynamicLib(m, lib)
useMagic(m, "nimGetProcAddr")
if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect)
tmp = ropeff("Dl_$1", "@Dl_$1", [toRope(sym.id)])
sym.loc.r = tmp # from now on we only need the internal name
sym.typ.sym = nil # generate a new name
inc(m.labels, 2)
appff(m.s[cfsDynLibInit], "$1 = ($2) nimGetProcAddr($3, $4);$n", "%MOC$5 = load i8* $3$n" &
"%MOC$6 = call $2 @nimGetProcAddr(i8* %MOC$5, i8* $4)$n" &
"store $2 %MOC$6, $2* $1$n", [tmp, getTypeDesc(m, sym.typ), lib.name, cstringLit(
m, m.s[cfsDynLibInit], ropeToStr(extname)), toRope(m.labels),
toRope(m.labels - 1)])
appff(m.s[cfsVars], "$2 $1;$n", "$1 = linkonce global $2 zeroinitializer$n",
[sym.loc.r, getTypeDesc(m, sym.loc.t)])
proc UseMagic(m: BModule, name: string) =
var sym: PSym
sym = magicsys.getCompilerProc(name)
if sym != nil:
case sym.kind
of skProc, skMethod, skConverter: genProc(m, sym)
of skVar: genVarPrototype(m, sym)
of skType: discard getTypeDesc(m, sym.typ)
else: InternalError("useMagic: " & name)
elif not (sfSystemModule in m.module.flags):
rawMessage(errSystemNeeds, name) # don't be too picky here
proc generateHeaders(m: BModule) =
var it: PStrEntry
app(m.s[cfsHeaders], "#include \"nimbase.h\"" & tnl & tnl)
it = PStrEntry(m.headerFiles.head)
while it != nil:
if not (it.data[0] in {'\"', '<'}):
appf(m.s[cfsHeaders], "#include \"$1\"$n", [toRope(it.data)])
else:
appf(m.s[cfsHeaders], "#include $1$n", [toRope(it.data)])
it = PStrEntry(it.Next)
proc getFrameDecl(p: BProc) =
var slots: PRope
if p.frameLen > 0:
useMagic(p.module, "TVarSlot")
slots = ropeff(" TVarSlot s[$1];$n", ", [$1 x %TVarSlot]",
[toRope(p.frameLen)])
else:
slots = nil
appff(p.s[cpsLocals], "volatile struct {TFrame* prev;" &
"NCSTRING procname;NI line;NCSTRING filename;" & "NI len;$n$1} F;$n",
"%TF = type {%TFrame*, i8*, %NI, %NI$1}$n" & "%F = alloca %TF$n",
[slots])
inc(p.labels)
prepend(p.s[cpsInit], ropeff("F.len = $1;$n", "%LOC$2 = getelementptr %TF %F, %NI 4$n" &
"store %NI $1, %NI* %LOC$2$n", [toRope(p.frameLen), toRope(p.labels)]))
proc retIsNotVoid(s: PSym): bool =
result = (s.typ.sons[0] != nil) and not isInvalidReturnType(s.typ.sons[0])
proc initFrame(p: BProc, procname, filename: PRope): PRope =
inc(p.labels, 5)
result = ropeff("F.procname = $1;$n" & "F.prev = framePtr;$n" &
"F.filename = $2;$n" & "F.line = 0;$n" & "framePtr = (TFrame*)&F;$n", "%LOC$3 = getelementptr %TF %F, %NI 1$n" &
"%LOC$4 = getelementptr %TF %F, %NI 0$n" &
"%LOC$5 = getelementptr %TF %F, %NI 3$n" &
"%LOC$6 = getelementptr %TF %F, %NI 2$n" & "store i8* $1, i8** %LOC$3$n" &
"store %TFrame* @framePtr, %TFrame** %LOC$4$n" &
"store i8* $2, i8** %LOC$5$n" & "store %NI 0, %NI* %LOC$6$n" &
"%LOC$7 = bitcast %TF* %F to %TFrame*$n" &
"store %TFrame* %LOC$7, %TFrame** @framePtr$n", [procname, filename,
toRope(p.labels), toRope(p.labels - 1), toRope(p.labels - 2),
toRope(p.labels - 3), toRope(p.labels - 4)])
proc deinitFrame(p: BProc): PRope =
inc(p.labels, 3)
result = ropeff("framePtr = framePtr->prev;$n", "%LOC$1 = load %TFrame* @framePtr$n" &
"%LOC$2 = getelementptr %TFrame* %LOC$1, %NI 0$n" &
"%LOC$3 = load %TFrame** %LOC$2$n" &
"store %TFrame* $LOC$3, %TFrame** @framePtr", [toRope(p.labels),
toRope(p.labels - 1), toRope(p.labels - 2)])
proc genProcAux(m: BModule, prc: PSym) =
var
p: BProc
generatedProc, header, returnStmt, procname, filename: PRope
res, param: PSym
p = newProc(prc, m)
header = genProcHeader(m, prc)
if (gCmd != cmdCompileToLLVM) and (lfExportLib in prc.loc.flags):
header = con("N_LIB_EXPORT ", header)
returnStmt = nil
assert(prc.ast != nil)
if not (sfPure in prc.flags) and (prc.typ.sons[0] != nil):
res = prc.ast.sons[resultPos].sym # get result symbol
if not isInvalidReturnType(prc.typ.sons[0]):
# declare the result symbol:
assignLocalVar(p, res)
assert(res.loc.r != nil)
returnStmt = ropeff("return $1;$n", "ret $1$n", [rdLoc(res.loc)])
else:
fillResult(res)
assignParam(p, res)
if skipTypes(res.typ, abstractInst).kind == tyArray:
incl(res.loc.flags, lfIndirect)
res.loc.s = OnUnknown
initVariable(p, res)
genObjectInit(p, res.typ, res.loc, true)
for i in countup(1, sonsLen(prc.typ.n) - 1):
param = prc.typ.n.sons[i].sym
assignParam(p, param)
genStmts(p, prc.ast.sons[codePos]) # modifies p.locals, p.init, etc.
if sfPure in prc.flags:
generatedProc = ropeff("$1 {$n$2$3$4}$n", "define $1 {$n$2$3$4}$n", [header,
p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]])
else:
generatedProc = ropeff("$1 {$n", "define $1 {$n", [header])
if optStackTrace in prc.options:
getFrameDecl(p)
app(generatedProc, p.s[cpsLocals])
procname = CStringLit(p, generatedProc,
prc.owner.name.s & '.' & prc.name.s)
filename = CStringLit(p, generatedProc, toFilename(prc.info))
app(generatedProc, initFrame(p, procname, filename))
else:
app(generatedProc, p.s[cpsLocals])
if (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM):
if gProcProfile >= 64 * 1024:
InternalError(prc.info, "too many procedures for profiling")
useMagic(m, "profileData")
app(p.s[cpsLocals], "ticks NIM_profilingStart;" & tnl)
if prc.loc.a < 0:
appf(m.s[cfsDebugInit], "profileData[$1].procname = $2;$n", [
toRope(gProcProfile),
makeCString(prc.owner.name.s & '.' & prc.name.s)])
prc.loc.a = gProcProfile
inc(gProcProfile)
prepend(p.s[cpsInit], toRope("NIM_profilingStart = getticks();" & tnl))
app(generatedProc, p.s[cpsInit])
app(generatedProc, p.s[cpsStmts])
if p.beforeRetNeeded: app(generatedProc, "BeforeRet: ;" & tnl)
if optStackTrace in prc.options: app(generatedProc, deinitFrame(p))
if (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM):
appf(generatedProc, "profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n",
[toRope(prc.loc.a)])
app(generatedProc, returnStmt)
app(generatedProc, '}' & tnl)
app(m.s[cfsProcs], generatedProc) #if prc.kind = skMethod then addMethodToCompile(gNimDat, prc);
proc genProcPrototype(m: BModule, sym: PSym) =
useHeader(m, sym)
if (lfNoDecl in sym.loc.Flags): return
if lfDynamicLib in sym.loc.Flags:
if (sym.owner.id != m.module.id) and
not intSetContainsOrIncl(m.declaredThings, sym.id):
appff(m.s[cfsVars], "extern $1 Dl_$2;$n",
"@Dl_$2 = linkonce global $1 zeroinitializer$n",
[getTypeDesc(m, sym.loc.t), toRope(sym.id)])
if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect)
else:
if not IntSetContainsOrIncl(m.declaredProtos, sym.id):
appf(m.s[cfsProcHeaders], "$1;$n", [genProcHeader(m, sym)])
proc genProcNoForward(m: BModule, prc: PSym) =
fillProcLoc(prc)
useHeader(m, prc)
genProcPrototype(m, prc)
if (lfNoDecl in prc.loc.Flags): return
if prc.typ.callConv == ccInline:
# We add inline procs to the calling module to enable C based inlining.
# This also means that a check with ``gGeneratedSyms`` is wrong, we need
# a check for ``m.declaredThings``.
if not intSetContainsOrIncl(m.declaredThings, prc.id): genProcAux(m, prc)
elif lfDynamicLib in prc.loc.flags:
if not IntSetContainsOrIncl(gGeneratedSyms, prc.id):
SymInDynamicLib(findPendingModule(m, prc), prc)
elif not (sfImportc in prc.flags):
if not IntSetContainsOrIncl(gGeneratedSyms, prc.id):
genProcAux(findPendingModule(m, prc), prc)
proc genProc(m: BModule, prc: PSym) =
if sfBorrow in prc.flags: return
fillProcLoc(prc)
if {sfForward, sfFromGeneric} * prc.flags != {}: addForwardedProc(m, prc)
else: genProcNoForward(m, prc)
proc genVarPrototype(m: BModule, sym: PSym) =
assert(sfGlobal in sym.flags)
useHeader(m, sym)
fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap)
if (lfNoDecl in sym.loc.Flags) or
intSetContainsOrIncl(m.declaredThings, sym.id):
return
if sym.owner.id != m.module.id:
# else we already have the symbol generated!
assert(sym.loc.r != nil)
if gCmd == cmdCompileToLLVM:
incl(sym.loc.flags, lfIndirect)
appf(m.s[cfsVars], "$1 = linkonce global $2 zeroinitializer$n",
[sym.loc.r, getTypeDesc(m, sym.loc.t)])
else:
app(m.s[cfsVars], "extern ")
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
if sfRegister in sym.flags: app(m.s[cfsVars], " register")
if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile")
if sfThreadVar in sym.flags: app(m.s[cfsVars], " NIM_THREADVAR")
appf(m.s[cfsVars], " $1;$n", [sym.loc.r])
proc genConstPrototype(m: BModule, sym: PSym) =
useHeader(m, sym)
if sym.loc.k == locNone:
fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown)
if (lfNoDecl in sym.loc.Flags) or
intSetContainsOrIncl(m.declaredThings, sym.id):
return
if sym.owner.id != m.module.id:
# else we already have the symbol generated!
assert(sym.loc.r != nil)
appff(m.s[cfsData], "extern NIM_CONST $1 $2;$n",
"$1 = linkonce constant $2 zeroinitializer",
[getTypeDesc(m, sym.loc.t), sym.loc.r])
proc getFileHeader(cfilenoext: string): PRope =
if optCompileOnly in gGlobalOptions:
result = ropeff("/* Generated by Nimrod Compiler v$1 */$n" &
"/* (c) 2009 Andreas Rumpf */$n", "; Generated by Nimrod Compiler v$1$n" &
"; (c) 2009 Andreas Rumpf$n", [toRope(versionAsString)])
else:
result = ropeff("/* Generated by Nimrod Compiler v$1 */$n" &
"/* (c) 2009 Andreas Rumpf */$n" & "/* Compiled for: $2, $3, $4 */$n" &
"/* Command for C compiler:$n $5 */$n", "; Generated by Nimrod Compiler v$1$n" &
"; (c) 2009 Andreas Rumpf$n" & "; Compiled for: $2, $3, $4$n" &
"; Command for LLVM compiler:$n $5$n", [toRope(versionAsString),
toRope(platform.OS[targetOS].name),
toRope(platform.CPU[targetCPU].name),
toRope(extccomp.CC[extccomp.ccompiler].name),
toRope(getCompileCFileCmd(cfilenoext))])
case platform.CPU[targetCPU].intSize
of 16:
appff(result,
"$ntypedef short int NI;$n" & "typedef unsigned short int NU;$n",
"$n%NI = type i16$n", [])
of 32:
appff(result,
"$ntypedef long int NI;$n" & "typedef unsigned long int NU;$n",
"$n%NI = type i32$n", [])
of 64:
appff(result, "$ntypedef long long int NI;$n" &
"typedef unsigned long long int NU;$n", "$n%NI = type i64$n", [])
else:
nil
proc genMainProc(m: BModule) =
const
CommonMainBody = " setStackBottom(dummy);$n" & " nim__datInit();$n" &
" systemInit();$n" & "$1" & "$2"
CommonMainBodyLLVM = " %MOC$3 = bitcast [8 x %NI]* %dummy to i8*$n" &
" call void @setStackBottom(i8* %MOC$3)$n" &
" call void @nim__datInit()$n" & " call void systemInit()$n" & "$1" &
"$2"
PosixNimMain = "int cmdCount;$n" & "char** cmdLine;$n" & "char** gEnv;$n" &
"N_CDECL(void, NimMain)(void) {$n" & " int dummy[8];$n" &
CommonMainBody & "}$n"
PosixCMain = "int main(int argc, char** args, char** env) {$n" &
" cmdLine = args;$n" & " cmdCount = argc;$n" & " gEnv = env;$n" &
" NimMain();$n" & " return 0;$n" & "}$n"
PosixNimMainLLVM = "@cmdCount = linkonce i32$n" &
"@cmdLine = linkonce i8**$n" & "@gEnv = linkonce i8**$n" &
"define void @NimMain(void) {$n" & " %dummy = alloca [8 x %NI]$n" &
CommonMainBodyLLVM & "}$n"
PosixCMainLLVM = "define i32 @main(i32 %argc, i8** %args, i8** %env) {$n" &
" store i8** %args, i8*** @cmdLine$n" &
" store i32 %argc, i32* @cmdCount$n" &
" store i8** %env, i8*** @gEnv$n" & " call void @NimMain()$n" &
" ret i32 0$n" & "}$n"
WinNimMain = "N_CDECL(void, NimMain)(void) {$n" & " int dummy[8];$n" &
CommonMainBody & "}$n"
WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $n" &
" HINSTANCE hPrevInstance, $n" &
" LPSTR lpCmdLine, int nCmdShow) {$n" &
" NimMain();$n" & " return 0;$n" & "}$n"
WinNimMainLLVM = "define void @NimMain(void) {$n" &
" %dummy = alloca [8 x %NI]$n" & CommonMainBodyLLVM & "}$n"
WinCMainLLVM = "define stdcall i32 @WinMain(i32 %hCurInstance, $n" &
" i32 %hPrevInstance, $n" &
" i8* %lpCmdLine, i32 %nCmdShow) {$n" &
" call void @NimMain()$n" & " ret i32 0$n" & "}$n"
WinNimDllMain = "N_LIB_EXPORT N_CDECL(void, NimMain)(void) {$n" &
" int dummy[8];$n" & CommonMainBody & "}$n"
WinCDllMain = "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n" &
" LPVOID lpvReserved) {$n" & " NimMain();$n" &
" return 1;$n" & "}$n"
WinNimDllMainLLVM = WinNimMainLLVM
WinCDllMainLLVM = "define stdcall i32 @DllMain(i32 %hinstDLL, i32 %fwdreason, $n" &
" i8* %lpvReserved) {$n" &
" call void @NimMain()$n" & " ret i32 1$n" & "}$n"
var nimMain, otherMain: TFormatStr
useMagic(m, "setStackBottom")
if (platform.targetOS == osWindows) and
(gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}):
if optGenGuiApp in gGlobalOptions:
if gCmd == cmdCompileToLLVM:
nimMain = WinNimMainLLVM
otherMain = WinCMainLLVM
else:
nimMain = WinNimMain
otherMain = WinCMain
else:
if gCmd == cmdCompileToLLVM:
nimMain = WinNimDllMainLLVM
otherMain = WinCDllMainLLVM
else:
nimMain = WinNimDllMain
otherMain = WinCDllMain
discard lists.IncludeStr(m.headerFiles, "<windows.h>")
else:
if gCmd == cmdCompileToLLVM:
nimMain = PosixNimMainLLVM
otherMain = PosixCMainLLVM
else:
nimMain = PosixNimMain
otherMain = PosixCMain
if gBreakpoints != nil: useMagic(m, "dbgRegisterBreakpoint")
inc(m.labels)
appf(m.s[cfsProcs], nimMain, [gBreakpoints, mainModInit, toRope(m.labels)])
if not (optNoMain in gGlobalOptions): appf(m.s[cfsProcs], otherMain, [])
proc getInitName(m: PSym): PRope =
result = ropeff("$1Init", "@$1Init", [toRope(m.name.s)])
proc registerModuleToMain(m: PSym) =
var initname: PRope
initname = getInitName(m)
appff(mainModProcs, "N_NOINLINE(void, $1)(void);$n",
"declare void $1() noinline$n", [initname])
if not (sfSystemModule in m.flags):
appff(mainModInit, "$1();$n", "call void ()* $1$n", [initname])
proc genInitCode(m: BModule) =
var initname, prc, procname, filename: PRope
if optProfiler in m.initProc.options:
# This does not really belong here, but there is no good place for this
# code. I don't want to put this to the proc generation as the
# ``IncludeStr`` call is quite slow.
discard lists.IncludeStr(m.headerFiles, "<cycle.h>")
initname = getInitName(m.module)
prc = ropeff("N_NOINLINE(void, $1)(void) {$n",
"define void $1() noinline {$n", [initname])
if m.typeNodes > 0:
useMagic(m, "TNimNode")
appff(m.s[cfsTypeInit1], "static TNimNode $1[$2];$n",
"$1 = private alloca [$2 x @TNimNode]$n",
[m.typeNodesName, toRope(m.typeNodes)])
if m.nimTypes > 0:
useMagic(m, "TNimType")
appff(m.s[cfsTypeInit1], "static TNimType $1[$2];$n",
"$1 = private alloca [$2 x @TNimType]$n",
[m.nimTypesName, toRope(m.nimTypes)])
if optStackTrace in m.initProc.options:
getFrameDecl(m.initProc)
app(prc, m.initProc.s[cpsLocals])
app(prc, m.s[cfsTypeInit1])
procname = CStringLit(m.initProc, prc, "module " & m.module.name.s)
filename = CStringLit(m.initProc, prc, toFilename(m.module.info))
app(prc, initFrame(m.initProc, procname, filename))
else:
app(prc, m.initProc.s[cpsLocals])
app(prc, m.s[cfsTypeInit1])
app(prc, m.s[cfsTypeInit2])
app(prc, m.s[cfsTypeInit3])
app(prc, m.s[cfsDebugInit])
app(prc, m.s[cfsDynLibInit])
app(prc, m.initProc.s[cpsInit])
app(prc, m.initProc.s[cpsStmts])
if optStackTrace in m.initProc.options: app(prc, deinitFrame(m.initProc))
app(prc, '}' & tnl & tnl)
app(m.s[cfsProcs], prc)
proc genModule(m: BModule, cfilenoext: string): PRope =
result = getFileHeader(cfilenoext)
generateHeaders(m)
for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i])
proc rawNewModule(module: PSym, filename: string): BModule =
new(result)
InitLinkedList(result.headerFiles)
intSetInit(result.declaredThings)
intSetInit(result.declaredProtos)
result.cfilename = filename
result.filename = filename
initIdTable(result.typeCache)
initIdTable(result.forwTypeCache)
result.module = module
intSetInit(result.typeInfoMarker)
result.initProc = newProc(nil, result)
result.initProc.options = gOptions
initNodeTable(result.dataCache)
result.typeStack = @ []
result.forwardedProcs = @ []
result.typeNodesName = getTempName()
result.nimTypesName = getTempName()
proc newModule(module: PSym, filename: string): BModule =
result = rawNewModule(module, filename)
if (optDeadCodeElim in gGlobalOptions):
if (sfDeadCodeElim in module.flags):
InternalError("added pending module twice: " & filename)
addPendingModule(result)
proc registerTypeInfoModule() =
const
moduleName = "nim__dat"
var s: PSym
s = NewSym(skModule, getIdent(moduleName), nil)
gNimDat = rawNewModule(s, joinPath(options.projectPath, moduleName) & ".nim")
addPendingModule(gNimDat)
appff(mainModProcs, "N_NOINLINE(void, $1)(void);$n",
"declare void $1() noinline$n", [getInitName(s)])
proc myOpen(module: PSym, filename: string): PPassContext =
if gNimDat == nil: registerTypeInfoModule()
result = newModule(module, filename)
proc myOpenCached(module: PSym, filename: string, rd: PRodReader): PPassContext =
var cfile, cfilenoext, objFile: string
if gNimDat == nil:
registerTypeInfoModule() #MessageOut('cgen.myOpenCached has been called ' + filename);
cfile = changeFileExt(completeCFilePath(filename), cExt)
cfilenoext = changeFileExt(cfile, "")
addFileToLink(cfilenoext)
registerModuleToMain(module) # XXX: this cannot be right here, initalization has to be appended during
# the ``myClose`` call
result = nil
proc shouldRecompile(code: PRope, cfile, cfilenoext: string): bool =
var objFile: string
result = true
if not (optForceFullMake in gGlobalOptions):
objFile = toObjFile(cfilenoext)
if writeRopeIfNotEqual(code, cfile): return
if ExistsFile(objFile) and os.FileNewer(objFile, cfile): result = false
else:
writeRope(code, cfile)
proc myProcess(b: PPassContext, n: PNode): PNode =
var m: BModule
result = n
if b == nil: return
m = BModule(b)
m.initProc.options = gOptions
genStmts(m.initProc, n)
proc finishModule(m: BModule) =
var
i: int
prc: PSym
i = 0
while i <= high(m.forwardedProcs):
# Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use
# a ``for`` loop here
prc = m.forwardedProcs[i]
if sfForward in prc.flags: InternalError(prc.info, "still forwarded")
genProcNoForward(m, prc)
inc(i)
assert(gForwardedProcsCounter >= i)
dec(gForwardedProcsCounter, i)
setlen(m.forwardedProcs, 0)
proc writeModule(m: BModule) =
var
cfile, cfilenoext: string
code: PRope
# generate code for the init statements of the module:
genInitCode(m)
finishTypeDescriptions(m)
cfile = completeCFilePath(m.cfilename)
cfilenoext = changeFileExt(cfile, "")
if sfMainModule in m.module.flags:
# generate main file:
app(m.s[cfsProcHeaders], mainModProcs)
code = genModule(m, cfilenoext)
if shouldRecompile(code, changeFileExt(cfile, cExt), cfilenoext):
addFileToCompile(cfilenoext)
addFileToLink(cfilenoext)
proc myClose(b: PPassContext, n: PNode): PNode =
var
m: BModule
disp: PNode
result = n
if b == nil: return
m = BModule(b)
if n != nil:
m.initProc.options = gOptions
genStmts(m.initProc, n)
registerModuleToMain(m.module)
if not (optDeadCodeElim in gGlobalOptions) and
not (sfDeadCodeElim in m.module.flags):
finishModule(m)
if sfMainModule in m.module.flags:
disp = generateMethodDispatchers()
for i in countup(0, sonsLen(disp) - 1): genProcAux(gNimDat, disp.sons[i].sym)
genMainProc(m) # we need to process the transitive closure because recursive module
# deps are allowed (and the system module is processed in the wrong
# order anyway)
while gForwardedProcsCounter > 0:
for i in countup(0, high(gPendingModules)):
finishModule(gPendingModules[i])
for i in countup(0, high(gPendingModules)): writeModule(gPendingModules[i])
setlen(gPendingModules, 0)
if not (optDeadCodeElim in gGlobalOptions) and
not (sfDeadCodeElim in m.module.flags):
writeModule(m)
if sfMainModule in m.module.flags: writeMapping(gMapping)
proc cgenPass(): TPass =
initPass(result)
result.open = myOpen
result.openCached = myOpenCached
result.process = myProcess
result.close = myClose
InitIiTable(gToTypeInfoId)
IntSetInit(gGeneratedSyms)

204
rod/cgmeth.nim Executable file
View File

@@ -0,0 +1,204 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements code generation for multi methods.
import
options, ast, astalgo, msgs, idents, rnimsyn, types, magicsys
proc methodDef*(s: PSym)
proc methodCall*(n: PNode): PNode
proc generateMethodDispatchers*(): PNode
# implementation
const
skipPtrs = {tyVar, tyPtr, tyRef, tyGenericInst}
proc genConv(n: PNode, d: PType, downcast: bool): PNode =
var
dest, source: PType
diff: int
dest = skipTypes(d, abstractPtrs)
source = skipTypes(n.typ, abstractPtrs)
if (source.kind == tyObject) and (dest.kind == tyObject):
diff = inheritanceDiff(dest, source)
if diff == high(int): InternalError(n.info, "cgmeth.genConv")
if diff < 0:
result = newNodeIT(nkObjUpConv, n.info, d)
addSon(result, n)
if downCast: InternalError(n.info, "cgmeth.genConv: no upcast allowed")
elif diff > 0:
result = newNodeIT(nkObjDownConv, n.info, d)
addSon(result, n)
if not downCast:
InternalError(n.info, "cgmeth.genConv: no downcast allowed")
else:
result = n
else:
result = n
proc methodCall(n: PNode): PNode =
var disp: PSym
result = n
disp = lastSon(result.sons[0].sym.ast).sym
result.sons[0].sym = disp
for i in countup(1, sonsLen(result) - 1):
result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
var gMethods: seq[TSymSeq]
proc sameMethodBucket(a, b: PSym): bool =
var aa, bb: PType
result = false
if a.name.id != b.name.id: return
if sonsLen(a.typ) != sonsLen(b.typ):
return # check for return type:
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
for i in countup(1, sonsLen(a.typ) - 1):
aa = a.typ.sons[i]
bb = b.typ.sons[i]
while true:
aa = skipTypes(aa, {tyGenericInst})
bb = skipTypes(bb, {tyGenericInst})
if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}):
aa = aa.sons[0]
bb = bb.sons[0]
else:
break
if sameType(aa, bb) or
(aa.kind == tyObject) and (bb.kind == tyObject) and
(inheritanceDiff(bb, aa) < 0):
nil
else:
return
result = true
proc methodDef(s: PSym) =
var
L, q: int
disp: PSym
L = len(gMethods)
for i in countup(0, L - 1):
if sameMethodBucket(gMethods[i][0], s):
add(gMethods[i], s) # store a symbol to the dispatcher:
addSon(s.ast, lastSon(gMethods[i][0].ast))
return
add(gMethods, @ [s]) # create a new dispatcher:
disp = copySym(s)
disp.typ = copyType(disp.typ, disp.typ.owner, false)
if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
disp.ast = copyTree(s.ast)
disp.ast.sons[codePos] = nil
if s.typ.sons[0] != nil:
disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
addSon(s.ast, newSymNode(disp))
proc relevantCol(methods: TSymSeq, col: int): bool =
var t: PType
# returns true iff the position is relevant
t = methods[0].typ.sons[col]
result = false
if skipTypes(t, skipPtrs).kind == tyObject:
for i in countup(1, high(methods)):
if not SameType(methods[i].typ.sons[col], t):
return true
proc cmpSignatures(a, b: PSym, relevantCols: TIntSet): int =
var
d: int
aa, bb: PType
result = 0
for col in countup(1, sonsLen(a.typ) - 1):
if intSetContains(relevantCols, col):
aa = skipTypes(a.typ.sons[col], skipPtrs)
bb = skipTypes(b.typ.sons[col], skipPtrs)
d = inheritanceDiff(aa, bb)
if (d != high(int)):
return d
proc sortBucket(a: var TSymSeq, relevantCols: TIntSet) =
# we use shellsort here; fast and simple
var
N, j, h: int
v: PSym
N = len(a)
h = 1
while true:
h = 3 * h + 1
if h > N: break
while true:
h = h div 3
for i in countup(h, N - 1):
v = a[i]
j = i
while cmpSignatures(a[j - h], v, relevantCols) >= 0:
a[j] = a[j - h]
j = j - h
if j < h: break
a[j] = v
if h == 1: break
proc genDispatcher(methods: TSymSeq, relevantCols: TIntSet): PSym =
var
disp, cond, call, ret, a, isn: PNode
base, curr, ands, iss: PSym
paramLen: int
base = lastSon(methods[0].ast).sym
result = base
paramLen = sonsLen(base.typ)
disp = newNodeI(nkIfStmt, base.info)
ands = getSysSym("and")
iss = getSysSym("is")
for meth in countup(0, high(methods)):
curr = methods[meth] # generate condition:
cond = nil
for col in countup(1, paramLen - 1):
if IntSetContains(relevantCols, col):
isn = newNodeIT(nkCall, base.info, getSysType(tyBool))
addSon(isn, newSymNode(iss))
addSon(isn, newSymNode(base.typ.n.sons[col].sym))
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
if cond != nil:
a = newNodeIT(nkCall, base.info, getSysType(tyBool))
addSon(a, newSymNode(ands))
addSon(a, cond)
addSon(a, isn)
cond = a
else:
cond = isn
call = newNodeI(nkCall, base.info)
addSon(call, newSymNode(curr))
for col in countup(1, paramLen - 1):
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
curr.typ.sons[col], false))
if base.typ.sons[0] != nil:
a = newNodeI(nkAsgn, base.info)
addSon(a, newSymNode(base.ast.sons[resultPos].sym))
addSon(a, call)
ret = newNodeI(nkReturnStmt, base.info)
addSon(ret, a)
else:
ret = call
a = newNodeI(nkElifBranch, base.info)
addSon(a, cond)
addSon(a, ret)
addSon(disp, a)
result.ast.sons[codePos] = disp
proc generateMethodDispatchers(): PNode =
var relevantCols: TIntSet
result = newNode(nkStmtList)
for bucket in countup(0, len(gMethods) - 1):
IntSetInit(relevantCols)
for col in countup(1, sonsLen(gMethods[bucket][0].typ) - 1):
if relevantCol(gMethods[bucket], col): IntSetIncl(relevantCols, col)
sortBucket(gMethods[bucket], relevantCols)
addSon(result, newSymNode(genDispatcher(gMethods[bucket], relevantCols)))
gMethods = @ []

49
rod/charsets.nim Executable file
View File

@@ -0,0 +1,49 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
const
CharSize* = SizeOf(Char)
Lrz* = ' '
Apo* = '\''
Tabulator* = '\x09'
ESC* = '\x1B'
CR* = '\x0D'
FF* = '\x0C'
LF* = '\x0A'
BEL* = '\x07'
BACKSPACE* = '\x08'
VT* = '\x0B'
when defined(macos):
DirSep == ':'
"\n" == CR & ""
FirstNLchar == CR
PathSep == ';' # XXX: is this correct?
else:
when defined(unix):
DirSep == '/'
"\n" == LF & ""
FirstNLchar == LF
PathSep == ':'
else:
# windows, dos
DirSep == '\\'
"\n" == CR + LF
FirstNLchar == CR
DriveSeparator == ':'
PathSep == ';'
UpLetters == {'A'..'Z', '\xC0'..'\xDE'}
DownLetters == {'a'..'z', '\xDF'..'\xFF'}
Numbers == {'0'..'9'}
Letters == UpLetters + DownLetters
type
TCharSet* = set[Char]
PCharSet* = ref TCharSet
# implementation

422
rod/commands.nim Executable file
View File

@@ -0,0 +1,422 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module handles the parsing of command line arguments.
import
os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists,
wordrecg
proc writeCommandLineUsage*()
type
TCmdLinePass* = enum
passCmd1, # first pass over the command line
passCmd2, # second pass over the command line
passPP # preprocessor called ProcessCommand()
proc ProcessCommand*(switch: string, pass: TCmdLinePass)
proc processSwitch*(switch, arg: string, pass: TCmdlinePass, info: TLineInfo)
# implementation
const
HelpMessage = "Nimrod Compiler Version $1 (" & compileDate & ") [$2: $3]" &
"\n" & "Copyright (c) 2004-2009 by Andreas Rumpf" & "\n"
const
Usage = """
Usage::
nimrod command [options] inputfile [arguments]
Command::
compile, c compile project with default code generator (C)
compileToC, cc compile project with C code generator
doc generate the documentation for inputfile
rst2html converts a reStructuredText file to HTML
rst2tex converts a reStructuredText file to TeX
Arguments:
arguments are passed to the program being run (if --run option is selected)
Options:
-p, --path:PATH add path to search paths
-o, --out:FILE set the output filename
-d, --define:SYMBOL define a conditional symbol
-u, --undef:SYMBOL undefine a conditional symbol
-f, --forceBuild force rebuilding of all modules
--symbolFiles:on|off use symbol files to speed up compilation (buggy!)
--stackTrace:on|off code generation for stack trace ON|OFF
--lineTrace:on|off code generation for line trace ON|OFF
--debugger:on|off turn Embedded Nimrod Debugger ON|OFF
-x, --checks:on|off code generation for all runtime checks ON|OFF
--objChecks:on|off code generation for obj conversion checks ON|OFF
--fieldChecks:on|off code generation for case variant fields ON|OFF
--rangeChecks:on|off code generation for range checks ON|OFF
--boundChecks:on|off code generation for bound checks ON|OFF
--overflowChecks:on|off code generation for over-/underflow checks ON|OFF
-a, --assertions:on|off code generation for assertions ON|OFF
--deadCodeElim:on|off whole program dead code elimination ON|OFF
--opt:none|speed|size optimize not at all or for speed|size
--app:console|gui|lib generate a console|GUI application|dynamic library
-r, --run run the compiled program with given arguments
--advanced show advanced command line switches
-h, --help show this help
"""
AdvancedUsage = """
Advanced commands::
pas convert a Pascal file to Nimrod syntax
pretty pretty print the inputfile
genDepend generate a DOT file containing the
module dependency graph
listDef list all defined conditionals and exit
check checks the project for syntax and semantic
parse parses a single file (for debugging Nimrod)
Advanced options:
-w, --warnings:on|off warnings ON|OFF
--warning[X]:on|off specific warning X ON|OFF
--hints:on|off hints ON|OFF
--hint[X]:on|off specific hint X ON|OFF
--lib:PATH set the system library path
-c, --compileOnly compile only; do not assemble or link
--noLinking compile but do not link
--noMain do not generate a main procedure
--genScript generate a compile script (in the 'nimcache'
subdirectory named 'compile_$project$scriptext')
--os:SYMBOL set the target operating system (cross-compilation)
--cpu:SYMBOL set the target processor (cross-compilation)
--debuginfo enables debug information
-t, --passc:OPTION pass an option to the C compiler
-l, --passl:OPTION pass an option to the linker
--genMapping generate a mapping file containing
(Nimrod, mangled) identifier pairs
--lineDir:on|off generation of #line directive ON|OFF
--checkpoints:on|off turn on|off checkpoints; for debugging Nimrod
--skipCfg do not read the general configuration file
--skipProjCfg do not read the project's configuration file
--gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC
--index:FILE use FILE to generate a documenation index file
--putenv:key=value set an environment variable
--listCmd list the commands used to execute external programs
--parallelBuild=0|1|... perform a parallel build
value = number of processors (0 for auto-detect)
--verbosity:0|1|2|3 set Nimrod's verbosity level (0 is default)
-v, --version show detailed version information
"""
proc getCommandLineDesc(): string =
result = `%`(HelpMessage, [VersionAsString, platform.os[platform.hostOS].name,
cpu[platform.hostCPU].name]) & Usage
var
helpWritten: bool # BUGFIX 19
versionWritten: bool
advHelpWritten: bool
proc HelpOnError(pass: TCmdLinePass) =
if (pass == passCmd1) and not helpWritten:
# BUGFIX 19
MessageOut(getCommandLineDesc())
helpWritten = true
quit(0)
proc writeAdvancedUsage(pass: TCmdLinePass) =
if (pass == passCmd1) and not advHelpWritten:
# BUGFIX 19
MessageOut(`%`(HelpMessage, [VersionAsString,
platform.os[platform.hostOS].name,
cpu[platform.hostCPU].name]) & AdvancedUsage)
advHelpWritten = true
helpWritten = true
quit(0)
proc writeVersionInfo(pass: TCmdLinePass) =
if (pass == passCmd1) and not versionWritten:
versionWritten = true
helpWritten = true
messageOut(`%`(HelpMessage, [VersionAsString,
platform.os[platform.hostOS].name,
cpu[platform.hostCPU].name]))
quit(0)
proc writeCommandLineUsage() =
if not helpWritten:
messageOut(getCommandLineDesc())
helpWritten = true
proc InvalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) =
liMessage(info, errInvalidCmdLineOption, switch)
proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass,
info: TLineInfo) =
var i: int
cmd = ""
i = 0
if (i < len(switch) + 0) and (switch[i] == '-'): inc(i)
if (i < len(switch) + 0) and (switch[i] == '-'): inc(i)
while i < len(switch) + 0:
case switch[i]
of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': add(cmd, switch[i])
else: break
inc(i)
if i >= len(switch) + 0: arg = ""
elif switch[i] in {':', '=', '['}: arg = copy(switch, i + 1)
else: InvalidCmdLineOption(pass, switch, info)
proc ProcessOnOffSwitch(op: TOptions, arg: string, pass: TCmdlinePass,
info: TLineInfo) =
case whichKeyword(arg)
of wOn: gOptions = gOptions + op
of wOff: gOptions = gOptions - op
else: liMessage(info, errOnOrOffExpectedButXFound, arg)
proc ProcessOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdlinePass,
info: TLineInfo) =
case whichKeyword(arg)
of wOn: gGlobalOptions = gGlobalOptions + op
of wOff: gGlobalOptions = gGlobalOptions - op
else: liMessage(info, errOnOrOffExpectedButXFound, arg)
proc ExpectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if (arg == ""): liMessage(info, errCmdLineArgExpected, switch)
proc ExpectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if (arg != ""): liMessage(info, errCmdLineNoArgExpected, switch)
proc ProcessSpecificNote(arg: string, state: TSpecialWord, pass: TCmdlinePass,
info: TLineInfo) =
var
i, x: int
n: TNoteKind
id: string
id = "" # arg = "X]:on|off"
i = 0
n = hintMin
while (i < len(arg) + 0) and (arg[i] != ']'):
add(id, arg[i])
inc(i)
if (i < len(arg) + 0) and (arg[i] == ']'): inc(i)
else: InvalidCmdLineOption(pass, arg, info)
if (i < len(arg) + 0) and (arg[i] in {':', '='}): inc(i)
else: InvalidCmdLineOption(pass, arg, info)
if state == wHint:
x = findStr(msgs.HintsToStr, id)
if x >= 0: n = TNoteKind(x + ord(hintMin))
else: InvalidCmdLineOption(pass, arg, info)
else:
x = findStr(msgs.WarningsToStr, id)
if x >= 0: n = TNoteKind(x + ord(warnMin))
else: InvalidCmdLineOption(pass, arg, info)
case whichKeyword(copy(arg, i))
of wOn: incl(gNotes, n)
of wOff: excl(gNotes, n)
else: liMessage(info, errOnOrOffExpectedButXFound, arg)
proc processPath(path: string): string =
result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath])
proc processCompile(filename: string) =
var found, trunc: string
found = findFile(filename)
if found == "": found = filename
trunc = changeFileExt(found, "")
extccomp.addExternalFileToCompile(trunc)
extccomp.addFileToLink(completeCFilePath(trunc, false))
proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
var
theOS: TSystemOS
cpu: TSystemCPU
key, val, path: string
case whichKeyword(switch)
of wPath, wP:
expectArg(switch, arg, pass, info)
path = processPath(arg)
discard lists.IncludeStr(options.searchPaths, path)
of wOut, wO:
expectArg(switch, arg, pass, info)
options.outFile = arg
of wDefine, wD:
expectArg(switch, arg, pass, info)
DefineSymbol(arg)
of wUndef, wU:
expectArg(switch, arg, pass, info)
UndefSymbol(arg)
of wCompile:
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: processCompile(arg)
of wLink:
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: addFileToLink(arg)
of wDebuginfo:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optCDebug)
of wCompileOnly, wC:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optCompileOnly)
of wNoLinking:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optNoLinking)
of wNoMain:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optNoMain)
of wForceBuild, wF:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optForceFullMake)
of wGC:
expectArg(switch, arg, pass, info)
case whichKeyword(arg)
of wBoehm:
incl(gGlobalOptions, optBoehmGC)
excl(gGlobalOptions, optRefcGC)
DefineSymbol("boehmgc")
of wRefc:
excl(gGlobalOptions, optBoehmGC)
incl(gGlobalOptions, optRefcGC)
of wNone:
excl(gGlobalOptions, optRefcGC)
excl(gGlobalOptions, optBoehmGC)
defineSymbol("nogc")
else: liMessage(info, errNoneBoehmRefcExpectedButXFound, arg)
of wWarnings, wW: ProcessOnOffSwitch({optWarns}, arg, pass, info)
of wWarning: ProcessSpecificNote(arg, wWarning, pass, info)
of wHint: ProcessSpecificNote(arg, wHint, pass, info)
of wHints: ProcessOnOffSwitch({optHints}, arg, pass, info)
of wCheckpoints: ProcessOnOffSwitch({optCheckpoints}, arg, pass, info)
of wStackTrace: ProcessOnOffSwitch({optStackTrace}, arg, pass, info)
of wLineTrace: ProcessOnOffSwitch({optLineTrace}, arg, pass, info)
of wDebugger:
ProcessOnOffSwitch({optEndb}, arg, pass, info)
if optEndb in gOptions: DefineSymbol("endb")
else: UndefSymbol("endb")
of wProfiler:
ProcessOnOffSwitch({optProfiler}, arg, pass, info)
if optProfiler in gOptions: DefineSymbol("profiler")
else: UndefSymbol("profiler")
of wChecks, wX: ProcessOnOffSwitch(checksOptions, arg, pass, info)
of wObjChecks: ProcessOnOffSwitch({optObjCheck}, arg, pass, info)
of wFieldChecks: ProcessOnOffSwitch({optFieldCheck}, arg, pass, info)
of wRangeChecks: ProcessOnOffSwitch({optRangeCheck}, arg, pass, info)
of wBoundChecks: ProcessOnOffSwitch({optBoundsCheck}, arg, pass, info)
of wOverflowChecks: ProcessOnOffSwitch({optOverflowCheck}, arg, pass, info)
of wLineDir: ProcessOnOffSwitch({optLineDir}, arg, pass, info)
of wAssertions, wA: ProcessOnOffSwitch({optAssert}, arg, pass, info)
of wDeadCodeElim: ProcessOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
of wOpt:
expectArg(switch, arg, pass, info)
case whichKeyword(arg)
of wSpeed:
incl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
of wSize:
excl(gOptions, optOptimizeSpeed)
incl(gOptions, optOptimizeSize)
of wNone:
excl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
else: liMessage(info, errNoneSpeedOrSizeExpectedButXFound, arg)
of wApp:
expectArg(switch, arg, pass, info)
case whichKeyword(arg)
of wGui:
incl(gGlobalOptions, optGenGuiApp)
defineSymbol("guiapp")
of wConsole:
excl(gGlobalOptions, optGenGuiApp)
of wLib:
incl(gGlobalOptions, optGenDynLib)
excl(gGlobalOptions, optGenGuiApp)
defineSymbol("library")
else: liMessage(info, errGuiConsoleOrLibExpectedButXFound, arg)
of wListDef:
expectNoArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: condsyms.listSymbols()
of wPassC, wT:
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: extccomp.addCompileOption(arg)
of wPassL, wL:
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg)
of wIndex:
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: gIndexFile = arg
of wImport:
expectArg(switch, arg, pass, info)
options.addImplicitMod(arg)
of wListCmd:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optListCmd)
of wGenMapping:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optGenMapping)
of wOS:
expectArg(switch, arg, pass, info)
if (pass == passCmd1):
theOS = platform.NameToOS(arg)
if theOS == osNone: liMessage(info, errUnknownOS, arg)
if theOS != platform.hostOS:
setTarget(theOS, targetCPU)
incl(gGlobalOptions, optCompileOnly)
condsyms.InitDefines()
of wCPU:
expectArg(switch, arg, pass, info)
if (pass == passCmd1):
cpu = platform.NameToCPU(arg)
if cpu == cpuNone: liMessage(info, errUnknownCPU, arg)
if cpu != platform.hostCPU:
setTarget(targetOS, cpu)
incl(gGlobalOptions, optCompileOnly)
condsyms.InitDefines()
of wRun, wR:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optRun)
of wVerbosity:
expectArg(switch, arg, pass, info)
gVerbosity = parseInt(arg)
of wParallelBuild:
expectArg(switch, arg, pass, info)
gNumberOfProcessors = parseInt(arg)
of wVersion, wV:
expectNoArg(switch, arg, pass, info)
writeVersionInfo(pass)
of wAdvanced:
expectNoArg(switch, arg, pass, info)
writeAdvancedUsage(pass)
of wHelp, wH:
expectNoArg(switch, arg, pass, info)
helpOnError(pass)
of wSymbolFiles:
ProcessOnOffSwitchG({optSymbolFiles}, arg, pass, info)
of wSkipCfg:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSkipConfigFile)
of wSkipProjCfg:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSkipProjConfigFile)
of wGenScript:
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optGenScript)
of wLib:
expectArg(switch, arg, pass, info)
libpath = processPath(arg)
of wPutEnv:
expectArg(switch, arg, pass, info)
splitSwitch(arg, key, val, pass, info)
os.putEnv(key, val)
of wCC:
expectArg(switch, arg, pass, info)
setCC(arg)
else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: InvalidCmdLineOption(pass, switch, info)
proc ProcessCommand(switch: string, pass: TCmdLinePass) =
var
cmd, arg: string
info: TLineInfo
info = newLineInfo("command line", 1, 1)
splitSwitch(switch, cmd, arg, pass, info)
ProcessSwitch(cmd, arg, pass, info)

112
rod/condsyms.nim Executable file
View File

@@ -0,0 +1,112 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module handles the conditional symbols.
import
ast, astalgo, msgs, nhashes, platform, strutils, idents
var gSymbols*: TStrTable
proc InitDefines*()
proc DeinitDefines*()
proc DefineSymbol*(symbol: string)
proc UndefSymbol*(symbol: string)
proc isDefined*(symbol: PIdent): bool
proc ListSymbols*()
proc countDefinedSymbols*(): int
# implementation
proc DefineSymbol(symbol: string) =
var
sym: PSym
i: PIdent
i = getIdent(symbol)
sym = StrTableGet(gSymbols, i)
if sym == nil:
new(sym) # circumvent the ID mechanism
sym.kind = skConditional
sym.name = i
StrTableAdd(gSymbols, sym)
sym.position = 1
proc UndefSymbol(symbol: string) =
var sym: PSym
sym = StrTableGet(gSymbols, getIdent(symbol))
if sym != nil: sym.position = 0
proc isDefined(symbol: PIdent): bool =
var sym: PSym
sym = StrTableGet(gSymbols, symbol)
result = (sym != nil) and (sym.position == 1)
proc ListSymbols() =
var
it: TTabIter
s: PSym
s = InitTabIter(it, gSymbols)
MessageOut("-- List of currently defined symbols --")
while s != nil:
if s.position == 1: MessageOut(s.name.s)
s = nextIter(it, gSymbols)
MessageOut("-- End of list --")
proc countDefinedSymbols(): int =
var
it: TTabIter
s: PSym
s = InitTabIter(it, gSymbols)
result = 0
while s != nil:
if s.position == 1: inc(result)
s = nextIter(it, gSymbols)
proc InitDefines() =
initStrTable(gSymbols)
DefineSymbol("nimrod") # 'nimrod' is always defined
# add platform specific symbols:
case targetCPU
of cpuI386: DefineSymbol("x86")
of cpuIa64: DefineSymbol("itanium")
of cpuAmd64: DefineSymbol("x8664")
else:
nil
case targetOS
of osDOS:
DefineSymbol("msdos")
of osWindows:
DefineSymbol("mswindows")
DefineSymbol("win32")
of osLinux, osMorphOS, osSkyOS, osIrix, osPalmOS, osQNX, osAtari, osAix:
# these are all 'unix-like'
DefineSymbol("unix")
DefineSymbol("posix")
of osSolaris:
DefineSymbol("sunos")
DefineSymbol("unix")
DefineSymbol("posix")
of osNetBSD, osFreeBSD, osOpenBSD:
DefineSymbol("unix")
DefineSymbol("bsd")
DefineSymbol("posix")
of osMacOS:
DefineSymbol("macintosh")
of osMacOSX:
DefineSymbol("macintosh")
DefineSymbol("unix")
DefineSymbol("posix")
else:
nil
DefineSymbol("cpu" & $(cpu[targetCPU].bit))
DefineSymbol(normalize(endianToStr[cpu[targetCPU].endian]))
DefineSymbol(cpu[targetCPU].name)
DefineSymbol(platform.os[targetOS].name)
proc DeinitDefines() =
nil

145
rod/crc.nim Executable file
View File

@@ -0,0 +1,145 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import
strutils
type
TCrc32* = int32
const
InitCrc32* = TCrc32(- 1)
InitAdler32* = int32(1)
proc updateCrc32*(val: int8, crc: TCrc32): TCrc32
proc updateCrc32*(val: Char, crc: TCrc32): TCrc32
proc crcFromBuf*(buf: Pointer, length: int): TCrc32
proc strCrc32*(s: string): TCrc32
proc crcFromFile*(filename: string): TCrc32
proc updateAdler32*(adler: int32, buf: pointer, length: int): int32
# implementation
type
TCRC_TabEntry = int
const
crc32table: array[0..255, TCRC_TabEntry] = [0, 1996959894, - 301047508,
- 1727442502, 124634137, 1886057615, - 379345611, - 1637575261, 249268274,
2044508324, - 522852066, - 1747789432, 162941995, 2125561021, - 407360249,
- 1866523247, 498536548, 1789927666, - 205950648, - 2067906082, 450548861,
1843258603, - 187386543, - 2083289657, 325883990, 1684777152, - 43845254,
- 1973040660, 335633487, 1661365465, - 99664541, - 1928851979, 997073096,
1281953886, - 715111964, - 1570279054, 1006888145, 1258607687, - 770865667,
- 1526024853, 901097722, 1119000684, - 608450090, - 1396901568, 853044451,
1172266101, - 589951537, - 1412350631, 651767980, 1373503546, - 925412992,
- 1076862698, 565507253, 1454621731, - 809855591, - 1195530993, 671266974,
1594198024, - 972236366, - 1324619484, 795835527, 1483230225, - 1050600021,
- 1234817731, 1994146192, 31158534, - 1731059524, - 271249366, 1907459465,
112637215, - 1614814043, - 390540237, 2013776290, 251722036, - 1777751922,
- 519137256, 2137656763, 141376813, - 1855689577, - 429695999, 1802195444,
476864866, - 2056965928, - 228458418, 1812370925, 453092731, - 2113342271,
- 183516073, 1706088902, 314042704, - 1950435094, - 54949764, 1658658271,
366619977, - 1932296973, - 69972891, 1303535960, 984961486, - 1547960204,
- 725929758, 1256170817, 1037604311, - 1529756563, - 740887301, 1131014506,
879679996, - 1385723834, - 631195440, 1141124467, 855842277, - 1442165665,
- 586318647, 1342533948, 654459306, - 1106571248, - 921952122, 1466479909,
544179635, - 1184443383, - 832445281, 1591671054, 702138776, - 1328506846,
- 942167884, 1504918807, 783551873, - 1212326853, - 1061524307, - 306674912,
- 1698712650, 62317068, 1957810842, - 355121351, - 1647151185, 81470997,
1943803523, - 480048366, - 1805370492, 225274430, 2053790376, - 468791541,
- 1828061283, 167816743, 2097651377, - 267414716, - 2029476910, 503444072,
1762050814, - 144550051, - 2140837941, 426522225, 1852507879, - 19653770,
- 1982649376, 282753626, 1742555852, - 105259153, - 1900089351, 397917763,
1622183637, - 690576408, - 1580100738, 953729732, 1340076626, - 776247311,
- 1497606297, 1068828381, 1219638859, - 670225446, - 1358292148, 906185462,
1090812512, - 547295293, - 1469587627, 829329135, 1181335161, - 882789492,
- 1134132454, 628085408, 1382605366, - 871598187, - 1156888829, 570562233,
1426400815, - 977650754, - 1296233688, 733239954, 1555261956, - 1026031705,
- 1244606671, 752459403, 1541320221, - 1687895376, - 328994266, 1969922972,
40735498, - 1677130071, - 351390145, 1913087877, 83908371, - 1782625662,
- 491226604, 2075208622, 213261112, - 1831694693, - 438977011, 2094854071,
198958881, - 2032938284, - 237706686, 1759359992, 534414190, - 2118248755,
- 155638181, 1873836001, 414664567, - 2012718362, - 15766928, 1711684554,
285281116, - 1889165569, - 127750551, 1634467795, 376229701, - 1609899400,
- 686959890, 1308918612, 956543938, - 1486412191, - 799009033, 1231636301,
1047427035, - 1362007478, - 640263460, 1088359270, 936918000, - 1447252397,
- 558129467, 1202900863, 817233897, - 1111625188, - 893730166, 1404277552,
615818150, - 1160759803, - 841546093, 1423857449, 601450431, - 1285129682,
- 1000256840, 1567103746, 711928724, - 1274298825, - 1022587231, 1510334235,
755167117]
proc updateCrc32(val: int8, crc: TCrc32): TCrc32 =
result = TCrc32(crc32Table[(int(crc) xor (int(val) and 0x000000FF)) and
0x000000FF]) xor (crc shr TCrc32(8))
proc updateCrc32(val: Char, crc: TCrc32): TCrc32 =
result = updateCrc32(int8(ord(val)), crc)
proc strCrc32(s: string): TCrc32 =
result = InitCrc32
for i in countup(0, len(s) + 0 - 1): result = updateCrc32(s[i], result)
type
TByteArray = array[0..10000000, int8]
PByteArray = ref TByteArray
proc crcFromBuf(buf: Pointer, length: int): TCrc32 =
var p: PByteArray
p = cast[PByteArray](buf)
result = InitCrc32
for i in countup(0, length - 1): result = updateCrc32(p[i], result)
proc crcFromFile(filename: string): TCrc32 =
const
bufSize = 8 * 1024
var
bin: tfile
buf: Pointer
readBytes: int
p: PByteArray
result = InitCrc32
if not open(bin, filename):
return # not equal if file does not exist
buf = alloc(BufSize)
p = cast[PByteArray](buf)
while true:
readBytes = readBuffer(bin, buf, bufSize)
for i in countup(0, readBytes - 1): result = updateCrc32(p[i], result)
if readBytes != bufSize: break
dealloc(buf)
close(bin)
const
base = int32(65521) # largest prime smaller than 65536
#NMAX = 5552; original code with unsigned 32 bit integer
# NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
nmax = 3854 # code with signed 32 bit integer
# NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^31-1
# The penalty is the time loss in the extra MOD-calls.
proc updateAdler32(adler: int32, buf: pointer, length: int): int32 =
var
s1, s2: int32
L, k, b: int
s1 = adler and int32(0x0000FFFF)
s2 = (adler shr int32(16)) and int32(0x0000FFFF)
L = length
b = 0
while (L > 0):
if L < nmax: k = L
else: k = nmax
dec(L, k)
while (k > 0):
s1 = s1 +% int32((cast[cstring](buf))[b])
s2 = s2 +% s1
inc(b)
dec(k)
s1 = `%%`(s1, base)
s2 = `%%`(s2, base)
result = (s2 shl int32(16)) or s1

68
rod/depends.nim Executable file
View File

@@ -0,0 +1,68 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements a dependency file generator.
import
os, options, ast, astalgo, msgs, ropes, idents, passes, importer
proc genDependPass*(): TPass
proc generateDot*(project: string)
# implementation
type
TGen = object of TPassContext
module*: PSym
filename*: string
PGen = ref TGen
var gDotGraph: PRope
proc addDependencyAux(importing, imported: string) =
# the generated DOT file; we need a global variable
appf(gDotGraph, "$1 -> $2;$n", [toRope(importing), toRope(imported)]) # s1 -> s2_4
# [label="[0-9]"];
proc addDotDependency(c: PPassContext, n: PNode): PNode =
var
g: PGen
imported: string
result = n
if n == nil: return
g = PGen(c)
case n.kind
of nkImportStmt:
for i in countup(0, sonsLen(n) - 1):
imported = splitFile(getModuleFile(n.sons[i])).name
addDependencyAux(g.module.name.s, imported)
of nkFromStmt:
imported = splitFile(getModuleFile(n.sons[0])).name
addDependencyAux(g.module.name.s, imported)
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i])
else:
nil
proc generateDot(project: string) =
writeRope(ropef("digraph $1 {$n$2}$n", [
toRope(changeFileExt(extractFileName(project), "")), gDotGraph]),
changeFileExt(project, "dot"))
proc myOpen(module: PSym, filename: string): PPassContext =
var g: PGen
new(g)
g.module = module
g.filename = filename
result = g
proc gendependPass(): TPass =
initPass(result)
result.open = myOpen
result.process = addDotDependency

915
rod/docgen.nim Executable file
View File

@@ -0,0 +1,915 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This is the documentation generator. It is currently pretty simple: No
# semantic checking is done for the code. Cross-references are generated
# by knowing how the anchors are going to be named.
import
ast, astalgo, strutils, nhashes, options, nversion, msgs, os, ropes, idents,
wordrecg, math, syntaxes, rnimsyn, scanner, rst, times, highlite
proc CommandDoc*(filename: string)
proc CommandRst2Html*(filename: string)
proc CommandRst2TeX*(filename: string)
# implementation
type
TTocEntry{.final.} = object
n*: PRstNode
refname*, header*: PRope
TSections = array[TSymKind, PRope]
TMetaEnum = enum
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
TDocumentor{.final.} = object # contains a module's documentation
filename*: string # filename of the source file; without extension
basedir*: string # base directory (where to put the documentation)
modDesc*: PRope # module description
dependsOn*: PRope # dependencies
id*: int # for generating IDs
splitAfter*: int # split too long entries in the TOC
tocPart*: seq[TTocEntry]
hasToc*: bool
toc*, section*: TSections
indexFile*, theIndex*: PRstNode
indexValFilename*: string
indent*, verbatim*: int # for code generation
meta*: array[TMetaEnum, PRope]
PDoc = ref TDocumentor
var splitter: string = "<wbr />"
proc findIndexNode(n: PRstNode): PRstNode =
if n == nil:
result = nil
elif n.kind == rnIndex:
result = n.sons[2]
if result == nil:
result = newRstNode(rnDefList)
n.sons[2] = result
elif result.kind == rnInner:
result = result.sons[0]
else:
result = nil
for i in countup(0, rsonsLen(n) - 1):
result = findIndexNode(n.sons[i])
if result != nil: return
proc initIndexFile(d: PDoc) =
var
h: PRstNode
dummyHasToc: bool
if gIndexFile == "": return
gIndexFile = addFileExt(gIndexFile, "txt")
d.indexValFilename = changeFileExt(extractFilename(d.filename), HtmlExt)
if ExistsFile(gIndexFile):
d.indexFile = rstParse(readFile(gIndexFile), false, gIndexFile, 0, 1,
dummyHasToc)
d.theIndex = findIndexNode(d.indexFile)
if (d.theIndex == nil) or (d.theIndex.kind != rnDefList):
rawMessage(errXisNoValidIndexFile, gIndexFile)
clearIndex(d.theIndex, d.indexValFilename)
else:
d.indexFile = newRstNode(rnInner)
h = newRstNode(rnOverline)
h.level = 1
addSon(h, newRstNode(rnLeaf, "Index"))
addSon(d.indexFile, h)
h = newRstNode(rnIndex)
addSon(h, nil) # no argument
addSon(h, nil) # no options
d.theIndex = newRstNode(rnDefList)
addSon(h, d.theIndex)
addSon(d.indexFile, h)
proc newDocumentor(filename: string): PDoc =
var s: string
new(result)
result.tocPart = @ []
result.filename = filename
result.id = 100
result.splitAfter = 20
s = getConfigVar("split.item.toc")
if s != "": result.splitAfter = parseInt(s)
proc getVarIdx(varnames: openarray[string], id: string): int =
for i in countup(0, high(varnames)):
if cmpIgnoreStyle(varnames[i], id) == 0:
return i
result = - 1
proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string],
varvalues: openarray[PRope]): PRope =
var
i, j, L, start, idx, num: int
id: string
i = 0
L = len(frmt)
result = nil
num = 0
while i <= L + 0 - 1:
if frmt[i] == '$':
inc(i) # skip '$'
case frmt[i]
of '#':
app(result, varvalues[num])
inc(num)
inc(i)
of '$':
app(result, "$")
inc(i)
of '0'..'9':
j = 0
while true:
j = (j * 10) + Ord(frmt[i]) - ord('0')
inc(i)
if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
num = j
app(result, varvalues[j - 1])
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
id = ""
while true:
add(id, frmt[i])
inc(i)
if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx])
else: rawMessage(errUnkownSubstitionVar, id)
of '{':
id = ""
inc(i)
while frmt[i] != '}':
if frmt[i] == '\0': rawMessage(errTokenExpected, "}")
add(id, frmt[i])
inc(i)
inc(i) # skip }
# search for the variable:
idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx])
else: rawMessage(errUnkownSubstitionVar, id)
else: InternalError("ropeFormatNamedVars")
start = i
while (i <= L + 0 - 1):
if (frmt[i] != '$'): inc(i)
else: break
if i - 1 >= start: app(result, copy(frmt, start, i - 1))
proc addXmlChar(dest: var string, c: Char) =
case c
of '&': add(dest, "&amp;")
of '<': add(dest, "&lt;")
of '>': add(dest, "&gt;")
of '\"': add(dest, "&quot;")
else: add(dest, c)
proc addRtfChar(dest: var string, c: Char) =
case c
of '{': add(dest, "\\{")
of '}': add(dest, "\\}")
of '\\': add(dest, "\\\\")
else: add(dest, c)
proc addTexChar(dest: var string, c: Char) =
case c
of '_': add(dest, "\\_")
of '{': add(dest, "\\symbol{123}")
of '}': add(dest, "\\symbol{125}")
of '[': add(dest, "\\symbol{91}")
of ']': add(dest, "\\symbol{93}")
of '\\': add(dest, "\\symbol{92}")
of '$': add(dest, "\\$")
of '&': add(dest, "\\&")
of '#': add(dest, "\\#")
of '%': add(dest, "\\%")
of '~': add(dest, "\\symbol{126}")
of '@': add(dest, "\\symbol{64}")
of '^': add(dest, "\\symbol{94}")
of '`': add(dest, "\\symbol{96}")
else: add(dest, c)
proc escChar(dest: var string, c: Char) =
if gCmd != cmdRst2Tex: addXmlChar(dest, c)
else: addTexChar(dest, c)
proc nextSplitPoint(s: string, start: int): int =
result = start
while result < len(s) + 0:
case s[result]
of '_':
return
of 'a'..'z':
if result + 1 < len(s) + 0:
if s[result + 1] in {'A'..'Z'}: return
else:
nil
inc(result)
dec(result) # last valid index
proc esc(s: string, splitAfter: int = - 1): string =
var j, k, partLen: int
result = ""
if splitAfter >= 0:
partLen = 0
j = 0
while j < len(s) + 0:
k = nextSplitPoint(s, j)
if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
partLen = 0
add(result, splitter)
for i in countup(j, k): escChar(result, s[i])
inc(partLen, k - j + 1)
j = k + 1
else:
for i in countup(0, len(s) + 0 - 1): escChar(result, s[i])
proc disp(xml, tex: string): string =
if gCmd != cmdRst2Tex: result = xml
else: result = tex
proc dispF(xml, tex: string, args: openarray[PRope]): PRope =
if gCmd != cmdRst2Tex: result = ropef(xml, args)
else: result = ropef(tex, args)
proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) =
if gCmd != cmdRst2Tex: appf(dest, xml, args)
else: appf(dest, tex, args)
proc renderRstToOut(d: PDoc, n: PRstNode): PRope
proc renderAux(d: PDoc, n: PRstNode, outer: string = "$1"): PRope =
result = nil
for i in countup(0, rsonsLen(n) - 1): app(result, renderRstToOut(d, n.sons[i]))
result = ropef(outer, [result])
proc setIndexForSourceTerm(d: PDoc, name: PRstNode, id: int) =
var a, h: PRstNode
if d.theIndex == nil: return
h = newRstNode(rnHyperlink)
a = newRstNode(rnLeaf, d.indexValFilename & disp("#", "") & $(id))
addSon(h, a)
addSon(h, a)
a = newRstNode(rnIdx)
addSon(a, name)
setIndexPair(d.theIndex, a, h)
proc renderIndexTerm(d: PDoc, n: PRstNode): PRope =
var a, h: PRstNode
inc(d.id)
result = dispF("<em id=\"$1\">$2</em>", "$2\\label{$1}",
[toRope(d.id), renderAux(d, n)])
h = newRstNode(rnHyperlink)
a = newRstNode(rnLeaf, d.indexValFilename & disp("#", "") & $(d.id))
addSon(h, a)
addSon(h, a)
setIndexPair(d.theIndex, n, h)
proc genComment(d: PDoc, n: PNode): PRope =
var dummyHasToc: bool
if (n.comment != nil) and startsWith(n.comment, "##"):
result = renderRstToOut(d, rstParse(n.comment, true, toFilename(n.info),
toLineNumber(n.info), toColumn(n.info),
dummyHasToc))
else:
result = nil
proc genRecComment(d: PDoc, n: PNode): PRope =
if n == nil:
return nil
result = genComment(d, n)
if result == nil:
if not (n.kind in {nkEmpty..nkNilLit}):
for i in countup(0, sonsLen(n) - 1):
result = genRecComment(d, n.sons[i])
if result != nil: return
else:
n.comment = nil
proc isVisible(n: PNode): bool =
var v: PIdent
result = false
if n.kind == nkPostfix:
if (sonsLen(n) == 2) and (n.sons[0].kind == nkIdent):
v = n.sons[0].ident
result = (v.id == ord(wStar)) or (v.id == ord(wMinus))
elif n.kind == nkSym:
result = sfInInterface in n.sym.flags
elif n.kind == nkPragmaExpr:
result = isVisible(n.sons[0])
proc getName(n: PNode, splitAfter: int = - 1): string =
case n.kind
of nkPostfix: result = getName(n.sons[1], splitAfter)
of nkPragmaExpr: result = getName(n.sons[0], splitAfter)
of nkSym: result = esc(n.sym.name.s, splitAfter)
of nkIdent: result = esc(n.ident.s, splitAfter)
of nkAccQuoted: result = esc("`") & getName(n.sons[0], splitAfter) & esc("`")
else:
internalError(n.info, "getName()")
result = ""
proc getRstName(n: PNode): PRstNode =
case n.kind
of nkPostfix: result = getRstName(n.sons[1])
of nkPragmaExpr: result = getRstName(n.sons[0])
of nkSym: result = newRstNode(rnLeaf, n.sym.name.s)
of nkIdent: result = newRstNode(rnLeaf, n.ident.s)
of nkAccQuoted: result = getRstName(n.sons[0])
else:
internalError(n.info, "getRstName()")
result = nil
proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
var
r: TSrcGen
kind: TTokType
literal: string
name, result, comm: PRope
if not isVisible(nameNode): return
name = toRope(getName(nameNode))
result = nil
literal = ""
kind = tkEof
comm = genRecComment(d, n) # call this here for the side-effect!
initTokRender(r, n, {renderNoPragmas, renderNoBody, renderNoComments,
renderDocComments})
while true:
getNextTok(r, kind, literal)
case kind
of tkEof:
break
of tkComment:
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
[toRope(esc(literal))])
of tokKeywordLow..tokKeywordHigh:
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
[toRope(literal)])
of tkOpr, tkHat:
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
[toRope(esc(literal))])
of tkStrLit..tkTripleStrLit:
dispA(result, "<span class=\"StringLit\">$1</span>",
"\\spanStringLit{$1}", [toRope(esc(literal))])
of tkCharLit:
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
[toRope(esc(literal))])
of tkIntLit..tkInt64Lit:
dispA(result, "<span class=\"DecNumber\">$1</span>",
"\\spanDecNumber{$1}", [toRope(esc(literal))])
of tkFloatLit..tkFloat64Lit:
dispA(result, "<span class=\"FloatNumber\">$1</span>",
"\\spanFloatNumber{$1}", [toRope(esc(literal))])
of tkSymbol:
dispA(result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [toRope(esc(literal))])
of tkInd, tkSad, tkDed, tkSpaces:
app(result, literal)
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
tkAccent:
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
[toRope(esc(literal))])
else: InternalError(n.info, "docgen.genThing(" & toktypeToStr[kind] & ')')
inc(d.id)
app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
["name", "header", "desc", "itemID"],
[name, result, comm, toRope(d.id)]))
app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
["name", "header", "desc", "itemID"], [
toRope(getName(nameNode, d.splitAfter)), result, comm, toRope(d.id)]))
setIndexForSourceTerm(d, getRstName(nameNode), d.id)
proc renderHeadline(d: PDoc, n: PRstNode): PRope =
var
length: int
refname: PRope
result = nil
for i in countup(0, rsonsLen(n) - 1): app(result, renderRstToOut(d, n.sons[i]))
refname = toRope(rstnodeToRefname(n))
if d.hasToc:
length = len(d.tocPart)
setlen(d.tocPart, length + 1)
d.tocPart[length].refname = refname
d.tocPart[length].n = n
d.tocPart[length].header = result
result = dispF("<h$1><a class=\"toc-backref\" id=\"$2\" href=\"#$2_toc\">$3</a></h$1>",
"\\rsth$4{$3}\\label{$2}$n", [toRope(n.level),
d.tocPart[length].refname, result,
toRope(chr(n.level - 1 + ord('A')) & "")])
else:
result = dispF("<h$1 id=\"$2\">$3</h$1>", "\\rsth$4{$3}\\label{$2}$n", [
toRope(n.level), refname, result,
toRope(chr(n.level - 1 + ord('A')) & "")])
proc renderOverline(d: PDoc, n: PRstNode): PRope =
var t: PRope
t = nil
for i in countup(0, rsonsLen(n) - 1): app(t, renderRstToOut(d, n.sons[i]))
result = nil
if d.meta[metaTitle] == nil:
d.meta[metaTitle] = t
elif d.meta[metaSubtitle] == nil:
d.meta[metaSubtitle] = t
else:
result = dispF("<h$1 id=\"$2\"><center>$3</center></h$1>",
"\\rstov$4{$3}\\label{$2}$n", [toRope(n.level),
toRope(rstnodeToRefname(n)), t, toRope(chr(n.level - 1 + ord('A')) & "")])
proc renderRstToRst(d: PDoc, n: PRstNode): PRope
proc renderRstSons(d: PDoc, n: PRstNode): PRope =
result = nil
for i in countup(0, rsonsLen(n) - 1): app(result, renderRstToRst(d, n.sons[i]))
proc renderRstToRst(d: PDoc, n: PRstNode): PRope =
# this is needed for the index generation; it may also be useful for
# debugging, but most code is already debugged...
const
lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+']
var
L: int
ind: PRope
result = nil
if n == nil: return
ind = toRope(repeatChar(d.indent))
case n.kind
of rnInner:
result = renderRstSons(d, n)
of rnHeadline:
result = renderRstSons(d, n)
L = ropeLen(result)
result = ropef("$n$1$2$n$1$3",
[ind, result, toRope(repeatChar(L, lvlToChar[n.level]))])
of rnOverline:
result = renderRstSons(d, n)
L = ropeLen(result)
result = ropef("$n$1$3$n$1$2$n$1$3",
[ind, result, toRope(repeatChar(L, lvlToChar[n.level]))])
of rnTransition:
result = ropef("$n$n$1$2$n$n", [ind, toRope(repeatChar(78 - d.indent, '-'))])
of rnParagraph:
result = renderRstSons(d, n)
result = ropef("$n$n$1$2", [ind, result])
of rnBulletItem:
inc(d.indent, 2)
result = renderRstSons(d, n)
if result != nil: result = ropef("$n$1* $2", [ind, result])
dec(d.indent, 2)
of rnEnumItem:
inc(d.indent, 4)
result = renderRstSons(d, n)
if result != nil: result = ropef("$n$1(#) $2", [ind, result])
dec(d.indent, 4)
of rnOptionList, rnFieldList, rnDefList, rnDefItem, rnLineBlock, rnFieldName,
rnFieldBody, rnStandaloneHyperlink, rnBulletList, rnEnumList:
result = renderRstSons(d, n)
of rnDefName:
result = renderRstSons(d, n)
result = ropef("$n$n$1$2", [ind, result])
of rnDefBody:
inc(d.indent, 2)
result = renderRstSons(d, n)
if n.sons[0].kind != rnBulletList: result = ropef("$n$1 $2", [ind, result])
dec(d.indent, 2)
of rnField:
result = renderRstToRst(d, n.sons[0])
L = max(ropeLen(result) + 3, 30)
inc(d.indent, L)
result = ropef("$n$1:$2:$3$4", [ind, result, toRope(
repeatChar(L - ropeLen(result) - 2)), renderRstToRst(d, n.sons[1])])
dec(d.indent, L)
of rnLineBlockItem:
result = renderRstSons(d, n)
result = ropef("$n$1| $2", [ind, result])
of rnBlockQuote:
inc(d.indent, 2)
result = renderRstSons(d, n)
dec(d.indent, 2)
of rnRef:
result = renderRstSons(d, n)
result = ropef("`$1`_", [result])
of rnHyperlink:
result = ropef("`$1 <$2>`_",
[renderRstToRst(d, n.sons[0]), renderRstToRst(d, n.sons[1])])
of rnGeneralRole:
result = renderRstToRst(d, n.sons[0])
result = ropef("`$1`:$2:", [result, renderRstToRst(d, n.sons[1])])
of rnSub:
result = renderRstSons(d, n)
result = ropef("`$1`:sub:", [result])
of rnSup:
result = renderRstSons(d, n)
result = ropef("`$1`:sup:", [result])
of rnIdx:
result = renderRstSons(d, n)
result = ropef("`$1`:idx:", [result])
of rnEmphasis:
result = renderRstSons(d, n)
result = ropef("*$1*", [result])
of rnStrongEmphasis:
result = renderRstSons(d, n)
result = ropef("**$1**", [result])
of rnInterpretedText:
result = renderRstSons(d, n)
result = ropef("`$1`", [result])
of rnInlineLiteral:
inc(d.verbatim)
result = renderRstSons(d, n)
result = ropef("``$1``", [result])
dec(d.verbatim)
of rnLeaf:
if (d.verbatim == 0) and (n.text == "\\"):
result = toRope("\\\\") # XXX: escape more special characters!
else:
result = toRope(n.text)
of rnIndex:
inc(d.indent, 3)
if n.sons[2] != nil: result = renderRstSons(d, n.sons[2])
dec(d.indent, 3)
result = ropef("$n$n$1.. index::$n$2", [ind, result])
of rnContents:
result = ropef("$n$n$1.. contents::", [ind])
else: rawMessage(errCannotRenderX, rstnodeKindToStr[n.kind])
proc renderTocEntry(d: PDoc, e: TTocEntry): PRope =
result = dispF("<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>$n",
"\\item\\label{$1_toc} $2\\ref{$1}$n", [e.refname, e.header])
proc renderTocEntries(d: PDoc, j: var int, lvl: int): PRope =
var a: int
result = nil
while (j <= high(d.tocPart)):
a = abs(d.tocPart[j].n.level)
if (a == lvl):
app(result, renderTocEntry(d, d.tocPart[j]))
inc(j)
elif (a > lvl):
app(result, renderTocEntries(d, j, a))
else:
break
if lvl > 1:
result = dispF("<ul class=\"simple\">$1</ul>",
"\\begin{enumerate}$1\\end{enumerate}", [result])
proc fieldAux(s: string): PRope =
result = toRope(strip(s))
proc renderImage(d: PDoc, n: PRstNode): PRope =
var
s, scale: string
options: PRope
options = nil
s = getFieldValue(n, "scale")
if s != "": dispA(options, " scale=\"$1\"", " scale=$1", [fieldAux(scale)])
s = getFieldValue(n, "height")
if s != "": dispA(options, " height=\"$1\"", " height=$1", [fieldAux(s)])
s = getFieldValue(n, "width")
if s != "": dispA(options, " width=\"$1\"", " width=$1", [fieldAux(s)])
s = getFieldValue(n, "alt")
if s != "": dispA(options, " alt=\"$1\"", "", [fieldAux(s)])
s = getFieldValue(n, "align")
if s != "": dispA(options, " align=\"$1\"", "", [fieldAux(s)])
if options != nil: options = dispF("$1", "[$1]", [options])
result = dispF("<img src=\"$1\"$2 />", "\\includegraphics$2{$1}",
[toRope(getArgument(n)), options])
if rsonsLen(n) >= 3: app(result, renderRstToOut(d, n.sons[2]))
proc renderCodeBlock(d: PDoc, n: PRstNode): PRope =
var
m: PRstNode
g: TGeneralTokenizer
langstr: string
lang: TSourceLanguage
result = nil
if n.sons[2] == nil: return
m = n.sons[2].sons[0]
if (m.kind != rnLeaf): InternalError("renderCodeBlock")
langstr = strip(getArgument(n))
if langstr == "":
lang = langNimrod # default language
else:
lang = getSourceLanguage(langstr)
if lang == langNone:
rawMessage(warnLanguageXNotSupported, langstr)
result = toRope(m.text)
else:
initGeneralTokenizer(g, m.text)
while true:
getNextToken(g, lang)
case g.kind
of gtEof:
break
of gtNone, gtWhitespace:
app(result, copy(m.text, g.start + 0, g.length + g.start - 1 + 0))
else:
dispA(result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", [
toRope(esc(copy(m.text, g.start + 0, g.length + g.start - 1 + 0))),
toRope(tokenClassToStr[g.kind])])
deinitGeneralTokenizer(g)
if result != nil:
result = dispF("<pre>$1</pre>", "\\begin{rstpre}$n$1$n\\end{rstpre}$n",
[result])
proc renderContainer(d: PDoc, n: PRstNode): PRope =
var arg: PRope
result = renderRstToOut(d, n.sons[2])
arg = toRope(strip(getArgument(n)))
if arg == nil: result = dispF("<div>$1</div>", "$1", [result])
else: result = dispF("<div class=\"$1\">$2</div>", "$2", [arg, result])
proc texColumns(n: PRstNode): string =
result = ""
for i in countup(1, rsonsLen(n)): add(result, "|X")
proc renderField(d: PDoc, n: PRstNode): PRope =
var
fieldname: string
fieldval: PRope
b: bool
b = false
if gCmd == cmdRst2Tex:
fieldname = addNodes(n.sons[0])
fieldval = toRope(esc(strip(addNodes(n.sons[1]))))
if cmpIgnoreStyle(fieldname, "author") == 0:
if d.meta[metaAuthor] == nil:
d.meta[metaAuthor] = fieldval
b = true
elif cmpIgnoreStyle(fieldName, "version") == 0:
if d.meta[metaVersion] == nil:
d.meta[metaVersion] = fieldval
b = true
if b: result = nil
else: result = renderAux(d, n, disp("<tr>$1</tr>$n", "$1"))
proc renderRstToOut(d: PDoc, n: PRstNode): PRope =
if n == nil:
return nil
case n.kind
of rnInner:
result = renderAux(d, n)
of rnHeadline:
result = renderHeadline(d, n)
of rnOverline:
result = renderOverline(d, n)
of rnTransition:
result = renderAux(d, n, disp("<hr />" & "\n", "\\hrule" & "\n"))
of rnParagraph:
result = renderAux(d, n, disp("<p>$1</p>" & "\n", "$1$n$n"))
of rnBulletList:
result = renderAux(d, n, disp("<ul class=\"simple\">$1</ul>" & "\n",
"\\begin{itemize}$1\\end{itemize}" & "\n"))
of rnBulletItem, rnEnumItem:
result = renderAux(d, n, disp("<li>$1</li>" & "\n", "\\item $1" & "\n"))
of rnEnumList:
result = renderAux(d, n, disp("<ol class=\"simple\">$1</ol>" & "\n",
"\\begin{enumerate}$1\\end{enumerate}" & "\n"))
of rnDefList:
result = renderAux(d, n, disp("<dl class=\"docutils\">$1</dl>" & "\n", "\\begin{description}$1\\end{description}" &
"\n"))
of rnDefItem:
result = renderAux(d, n)
of rnDefName:
result = renderAux(d, n, disp("<dt>$1</dt>" & "\n", "\\item[$1] "))
of rnDefBody:
result = renderAux(d, n, disp("<dd>$1</dd>" & "\n", "$1" & "\n"))
of rnFieldList:
result = nil
for i in countup(0, rsonsLen(n) - 1):
app(result, renderRstToOut(d, n.sons[i]))
if result != nil:
result = dispf("<table class=\"docinfo\" frame=\"void\" rules=\"none\">" &
"<col class=\"docinfo-name\" />" &
"<col class=\"docinfo-content\" />" & "<tbody valign=\"top\">$1" &
"</tbody></table>", "\\begin{description}$1\\end{description}" & "\n",
[result])
of rnField:
result = renderField(d, n)
of rnFieldName:
result = renderAux(d, n, disp("<th class=\"docinfo-name\">$1:</th>",
"\\item[$1:]"))
of rnFieldBody:
result = renderAux(d, n, disp("<td>$1</td>", " $1$n"))
of rnIndex:
result = renderRstToOut(d, n.sons[2])
of rnOptionList:
result = renderAux(d, n, disp("<table frame=\"void\">$1</table>", "\\begin{description}$n$1\\end{description}" &
"\n"))
of rnOptionListItem:
result = renderAux(d, n, disp("<tr>$1</tr>$n", "$1"))
of rnOptionGroup:
result = renderAux(d, n, disp("<th align=\"left\">$1</th>", "\\item[$1]"))
of rnDescription:
result = renderAux(d, n, disp("<td align=\"left\">$1</td>$n", " $1$n"))
of rnOption, rnOptionString, rnOptionArgument:
InternalError("renderRstToOut")
of rnLiteralBlock:
result = renderAux(d, n, disp("<pre>$1</pre>$n",
"\\begin{rstpre}$n$1$n\\end{rstpre}$n"))
of rnQuotedLiteralBlock:
InternalError("renderRstToOut")
of rnLineBlock:
result = renderAux(d, n, disp("<p>$1</p>", "$1$n$n"))
of rnLineBlockItem:
result = renderAux(d, n, disp("$1<br />", "$1\\\\$n"))
of rnBlockQuote:
result = renderAux(d, n, disp("<blockquote><p>$1</p></blockquote>$n",
"\\begin{quote}$1\\end{quote}$n"))
of rnTable, rnGridTable:
result = renderAux(d, n, disp("<table border=\"1\" class=\"docutils\">$1</table>", "\\begin{table}\\begin{rsttab}{" &
texColumns(n) & "|}$n\\hline$n$1\\end{rsttab}\\end{table}"))
of rnTableRow:
if rsonsLen(n) >= 1:
result = renderRstToOut(d, n.sons[0])
for i in countup(1, rsonsLen(n) - 1):
dispa(result, "$1", " & $1", [renderRstToOut(d, n.sons[i])])
result = dispf("<tr>$1</tr>$n", "$1\\\\$n\\hline$n", [result])
else:
result = nil
of rnTableDataCell:
result = renderAux(d, n, disp("<td>$1</td>", "$1"))
of rnTableHeaderCell:
result = renderAux(d, n, disp("<th>$1</th>", "\\textbf{$1}"))
of rnLabel:
InternalError("renderRstToOut") # used for footnotes and other
of rnFootnote:
InternalError("renderRstToOut") # a footnote
of rnCitation:
InternalError("renderRstToOut") # similar to footnote
of rnRef:
result = dispF("<a class=\"reference external\" href=\"#$2\">$1</a>",
"$1\\ref{$2}", [renderAux(d, n), toRope(rstnodeToRefname(n))])
of rnStandaloneHyperlink:
result = renderAux(d, n, disp("<a class=\"reference external\" href=\"$1\">$1</a>",
"\\href{$1}{$1}"))
of rnHyperlink:
result = dispF("<a class=\"reference external\" href=\"$2\">$1</a>",
"\\href{$2}{$1}",
[renderRstToOut(d, n.sons[0]), renderRstToOut(d, n.sons[1])])
of rnDirArg, rnRaw:
result = renderAux(d, n)
of rnImage, rnFigure:
result = renderImage(d, n)
of rnCodeBlock:
result = renderCodeBlock(d, n)
of rnContainer:
result = renderContainer(d, n)
of rnSubstitutionReferences, rnSubstitutionDef:
result = renderAux(d, n, disp("|$1|", "|$1|"))
of rnDirective:
result = renderAux(d, n, "") # Inline markup:
of rnGeneralRole:
result = dispF("<span class=\"$2\">$1</span>", "\\span$2{$1}",
[renderRstToOut(d, n.sons[0]), renderRstToOut(d, n.sons[1])])
of rnSub:
result = renderAux(d, n, disp("<sub>$1</sub>", "\\rstsub{$1}"))
of rnSup:
result = renderAux(d, n, disp("<sup>$1</sup>", "\\rstsup{$1}"))
of rnEmphasis:
result = renderAux(d, n, disp("<em>$1</em>", "\\emph{$1}"))
of rnStrongEmphasis:
result = renderAux(d, n, disp("<strong>$1</strong>", "\\textbf{$1}"))
of rnInterpretedText:
result = renderAux(d, n, disp("<cite>$1</cite>", "\\emph{$1}"))
of rnIdx:
if d.theIndex == nil:
result = renderAux(d, n, disp("<em>$1</em>", "\\emph{$1}"))
else:
result = renderIndexTerm(d, n)
of rnInlineLiteral:
result = renderAux(d, n, disp("<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
"\\texttt{$1}"))
of rnLeaf:
result = toRope(esc(n.text))
of rnContents:
d.hasToc = true
of rnTitle:
d.meta[metaTitle] = renderRstToOut(d, n.sons[0])
else: InternalError("renderRstToOut")
proc generateDoc(d: PDoc, n: PNode) =
if n == nil: return
case n.kind
of nkCommentStmt:
app(d.modDesc, genComment(d, n))
of nkProcDef:
genItem(d, n, n.sons[namePos], skProc)
of nkMethodDef:
genItem(d, n, n.sons[namePos], skMethod)
of nkIteratorDef:
genItem(d, n, n.sons[namePos], skIterator)
of nkMacroDef:
genItem(d, n, n.sons[namePos], skMacro)
of nkTemplateDef:
genItem(d, n, n.sons[namePos], skTemplate)
of nkConverterDef:
genItem(d, n, n.sons[namePos], skConverter)
of nkVarSection:
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt:
genItem(d, n.sons[i], n.sons[i].sons[0], skVar)
of nkConstSection:
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt:
genItem(d, n.sons[i], n.sons[i].sons[0], skConst)
of nkTypeSection:
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt:
genItem(d, n.sons[i], n.sons[i].sons[0], skType)
of nkStmtList:
for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i])
of nkWhenStmt:
# generate documentation for the first branch only:
generateDoc(d, lastSon(n.sons[0]))
else:
nil
proc genSection(d: PDoc, kind: TSymKind) =
if d.section[kind] == nil: return
var title = toRope(copy($kind, 0 + 2) & 's')
d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [
toRope(ord(kind)), title, toRope(ord(kind) + 50), d.section[kind]])
d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [
toRope(ord(kind)), title, toRope(ord(kind) + 50), d.toc[kind]])
proc genOutFile(d: PDoc): PRope =
var
code, toc, title, content: PRope
bodyname: string
j: int
j = 0
toc = renderTocEntries(d, j, 1)
code = nil
content = nil
title = nil
for i in countup(low(TSymKind), high(TSymKind)):
genSection(d, i)
app(toc, d.toc[i])
if toc != nil:
toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc])
for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i])
if d.meta[metaTitle] != nil: title = d.meta[metaTitle]
else: title = toRope("Module " &
extractFilename(changeFileExt(d.filename, "")))
if d.hasToc: bodyname = "doc.body_toc"
else: bodyname = "doc.body_no_toc"
content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
"tableofcontents", "moduledesc", "date", "time", "content"], [title, toc,
d.modDesc, toRope(getDateStr()), toRope(getClockStr()), code])
if not (optCompileOnly in gGlobalOptions):
code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "content", "author",
"version"], [title, toc, d.modDesc, toRope(getDateStr()),
toRope(getClockStr()), content, d.meta[metaAuthor],
d.meta[metaVersion]])
else:
code = content
result = code
proc generateIndex(d: PDoc) =
if d.theIndex != nil:
sortIndex(d.theIndex)
writeRope(renderRstToRst(d, d.indexFile), gIndexFile)
proc CommandDoc(filename: string) =
var
ast: PNode
d: PDoc
ast = parseFile(addFileExt(filename, nimExt))
if ast == nil: return
d = newDocumentor(filename)
initIndexFile(d)
d.hasToc = true
generateDoc(d, ast)
writeRope(genOutFile(d), getOutFile(filename, HtmlExt))
generateIndex(d)
proc CommandRstAux(filename, outExt: string) =
var
filen: string
d: PDoc
rst: PRstNode
code: PRope
filen = addFileExt(filename, "txt")
d = newDocumentor(filen)
initIndexFile(d)
rst = rstParse(readFile(filen), false, filen, 0, 1, d.hasToc)
d.modDesc = renderRstToOut(d, rst)
code = genOutFile(d)
writeRope(code, getOutFile(filename, outExt))
generateIndex(d)
proc CommandRst2Html(filename: string) =
CommandRstAux(filename, HtmlExt)
proc CommandRst2TeX(filename: string) =
splitter = "\\-"
CommandRstAux(filename, TexExt)

1448
rod/ecmasgen.nim Executable file

File diff suppressed because it is too large Load Diff

1135
rod/evals.nim Executable file

File diff suppressed because it is too large Load Diff

489
rod/extccomp.nim Executable file
View File

@@ -0,0 +1,489 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# module for calling the different external C compilers
# some things are read in from the configuration file
import
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs
type
TSystemCC* = enum
ccNone, ccGcc, ccLLVM_Gcc, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, ccTcc, ccPcc,
ccUcc, ccIcc, ccGpp
TInfoCCProp* = enum # properties of the C compiler:
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
hasComputedGoto, # CC has computed goto (GNU C extension)
hasCpp, # CC is/contains a C++ compiler
hasAssume # CC has __assume (Visual C extension)
TInfoCCProps* = set[TInfoCCProp]
TInfoCC* = tuple[
name: string, # the short name of the compiler
objExt: string, # the compiler's object file extenstion
optSpeed: string, # the options for optimization for speed
optSize: string, # the options for optimization for size
compilerExe: string, # the compiler's executable
compileTmpl: string, # the compile command template
buildGui: string, # command to build a GUI application
buildDll: string, # command to build a shared library
linkerExe: string, # the linker's executable
linkTmpl: string, # command to link files to produce an exe
includeCmd: string, # command to add an include dir
debug: string, # flags for debug build
pic: string, # command for position independent code
# used on some platforms
asmStmtFrmt: string, # format of ASM statement
props: TInfoCCProps] # properties of the C compiler
const
CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
(name: "gcc",
objExt: "o",
optSpeed: " -O3 -ffast-math ",
optSize: " -Os -ffast-math ",
compilerExe: "gcc",
compileTmpl: "-c $options $include -o $objfile $file",
buildGui: " -mwindows",
buildDll: " -mdll",
linkerExe: "gcc",
linkTmpl: "$options $buildgui $builddll -o $exefile $objfiles",
includeCmd: " -I",
debug: "",
pic: "-fPIC",
asmStmtFrmt: "asm($1);$n",
props: {hasSwitchRange, hasComputedGoto, hasCpp}),
(name: "llvm_gcc",
objExt: "o",
optSpeed: " -O3 -ffast-math ",
optSize: " -Os -ffast-math ",
compilerExe: "llvm-gcc",
compileTmpl: "-c $options $include -o $objfile $file",
buildGui: " -mwindows",
buildDll: " -mdll",
linkerExe: "llvm-gcc",
linkTmpl: "$options $buildgui $builddll -o $exefile $objfiles",
includeCmd: " -I",
debug: "", pic: "-fPIC",
asmStmtFrmt: "asm($1);$n",
props: {hasSwitchRange, hasComputedGoto, hasCpp}),
(name: "lcc",
objExt: "obj",
optSpeed: " -O -p6 ",
optSize: " -O -p6 ",
compilerExe: "lcc",
compileTmpl: "$options $include -Fo$objfile $file",
buildGui: " -subsystem windows",
buildDll: " -dll",
linkerExe: "lcclnk",
linkTmpl: "$options $buildgui $builddll -O $exefile $objfiles",
includeCmd: " -I",
debug: " -g5 ",
pic: "",
asmStmtFrmt: "_asm{$n$1$n}$n",
props: {}),
(name: "bcc",
objExt: "obj",
optSpeed: " -O2 -6 ",
optSize: " -O1 -6 ",
compilerExe: "bcc32",
compileTmpl: "-c $options $include -o$objfile $file",
buildGui: " -tW",
buildDll: " -tWD",
linkerExe: "bcc32",
linkTmpl: "$options $buildgui $builddll -e$exefile $objfiles",
includeCmd: " -I",
debug: "",
pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n",
props: {hasCpp}),
(name: "dmc",
objExt: "obj",
optSpeed: " -ff -o -6 ",
optSize: " -ff -o -6 ",
compilerExe: "dmc",
compileTmpl: "-c $options $include -o$objfile $file",
buildGui: " -L/exet:nt/su:windows",
buildDll: " -WD",
linkerExe: "dmc",
linkTmpl: "$options $buildgui $builddll -o$exefile $objfiles",
includeCmd: " -I",
debug: " -g ",
pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n",
props: {hasCpp}),
(name: "wcc",
objExt: "obj",
optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
optSize: "",
compilerExe: "wcl386",
compileTmpl: "-c $options $include -fo=$objfile $file",
buildGui: " -bw",
buildDll: " -bd",
linkerExe: "wcl386",
linkTmpl: "$options $buildgui $builddll -fe=$exefile $objfiles ",
includeCmd: " -i=",
debug: " -d2 ",
pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n",
props: {hasCpp}),
(name: "vcc",
objExt: "obj",
optSpeed: " /Ogityb2 /G7 /arch:SSE2 ",
optSize: " /O1 /G7 ",
compilerExe: "cl",
compileTmpl: "/c $options $include /Fo$objfile $file",
buildGui: " /link /SUBSYSTEM:WINDOWS ",
buildDll: " /LD",
linkerExe: "cl",
linkTmpl: "$options $builddll /Fe$exefile $objfiles $buildgui",
includeCmd: " /I",
debug: " /GZ /Zi ",
pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n",
props: {hasCpp, hasAssume}),
(name: "tcc",
objExt: "o",
optSpeed: "",
optSize: "",
compilerExe: "tcc",
compileTmpl: "-c $options $include -o $objfile $file",
buildGui: "UNAVAILABLE!",
buildDll: " -shared",
linkerExe: "tcc",
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
includeCmd: " -I",
debug: " -g ",
pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n",
props: {hasSwitchRange, hasComputedGoto}),
(name: "pcc", # Pelles C
objExt: "obj",
optSpeed: " -Ox ",
optSize: " -Os ",
compilerExe: "cc",
compileTmpl: "-c $options $include -Fo$objfile $file",
buildGui: " -SUBSYSTEM:WINDOWS",
buildDll: " -DLL",
linkerExe: "cc",
linkTmpl: "$options $buildgui $builddll -OUT:$exefile $objfiles",
includeCmd: " -I",
debug: " -Zi ",
pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n",
props: {}),
(name: "ucc",
objExt: "o",
optSpeed: " -O3 ",
optSize: " -O1 ",
compilerExe: "cc",
compileTmpl: "-c $options $include -o $objfile $file",
buildGui: "",
buildDll: " -shared ",
linkerExe: "cc",
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
includeCmd: " -I",
debug: "",
pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n",
props: {}),
(name: "icc",
objExt: "o",
optSpeed: " -O3 ",
optSize: " -Os ",
compilerExe: "icc",
compileTmpl: "-c $options $include -o $objfile $file",
buildGui: " -mwindows",
buildDll: " -mdll",
linkerExe: "icc",
linkTmpl: "$options $buildgui $builddll -o $exefile $objfiles",
includeCmd: " -I",
debug: "",
pic: "-fPIC",
asmStmtFrmt: "asm($1);$n",
props: {hasSwitchRange, hasComputedGoto, hasCpp}),
(name: "gpp",
objExt: "o",
optSpeed: " -O3 -ffast-math ",
optSize: " -Os -ffast-math ",
compilerExe: "g++",
compileTmpl: "-c $options $include -o $objfile $file",
buildGui: " -mwindows",
buildDll: " -mdll",
linkerExe: "g++",
linkTmpl: "$options $buildgui $builddll -o $exefile $objfiles",
includeCmd: " -I",
debug: " -g ",
pic: "-fPIC",
asmStmtFrmt: "asm($1);$n",
props: {hasSwitchRange, hasComputedGoto, hasCpp})]
var ccompiler*: TSystemCC = ccGcc
const # the used compiler
hExt* = "h"
var cExt*: string = "c" # extension of generated C/C++ files
# (can be changed to .cpp later)
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string
proc getCompileCFileCmd*(cfilename: string, isExternal: bool = false): string
proc addFileToCompile*(filename: string)
proc addExternalFileToCompile*(filename: string)
proc addFileToLink*(filename: string)
proc addCompileOption*(option: string)
proc addLinkOption*(option: string)
proc toObjFile*(filenameWithoutExt: string): string
proc CallCCompiler*(projectFile: string)
proc execExternalProgram*(cmd: string)
proc NameToCC*(name: string): TSystemCC
proc initVars*()
proc setCC*(ccname: string)
proc writeMapping*(gSymbolMapping: PRope)
# implementation
var
toLink, toCompile, externalToCompile: TLinkedList
linkOptions: string = ""
compileOptions: string = ""
ccompilerpath: string = ""
proc setCC(ccname: string) =
ccompiler = nameToCC(ccname)
if ccompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
compileOptions = getConfigVar(CC[ccompiler].name & ".options.always")
linkOptions = getConfigVar(CC[ccompiler].name & ".options.linker")
ccompilerpath = getConfigVar(CC[ccompiler].name & ".path")
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[ccompiler].name)
proc initVars() =
# we need to define the symbol here, because ``CC`` may have never been set!
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[ccompiler].name)
if gCmd == cmdCompileToCpp: cExt = ".cpp"
addCompileOption(getConfigVar(CC[ccompiler].name & ".options.always"))
addLinkOption(getConfigVar(CC[ccompiler].name & ".options.linker"))
if len(ccompilerPath) == 0:
ccompilerpath = getConfigVar(CC[ccompiler].name & ".path")
proc completeCFilePath(cfile: string, createSubDir: bool = true): string =
result = completeGeneratedFilePath(cfile, createSubDir)
proc NameToCC(name: string): TSystemCC =
for i in countup(succ(ccNone), high(TSystemCC)):
if cmpIgnoreStyle(name, CC[i].name) == 0:
return i
result = ccNone
proc addOpt(dest: var string, src: string) =
if (len(dest) == 0) or (dest[len(dest) - 1 + 0] != ' '): add(dest, " ")
add(dest, src)
proc addCompileOption(option: string) =
if strutils.find(compileOptions, option, 0) < 0:
addOpt(compileOptions, option)
proc addLinkOption(option: string) =
if find(linkOptions, option, 0) < 0: addOpt(linkOptions, option)
proc toObjFile(filenameWithoutExt: string): string =
result = changeFileExt(filenameWithoutExt, cc[ccompiler].objExt)
proc addFileToCompile(filename: string) =
appendStr(toCompile, filename)
proc addExternalFileToCompile(filename: string) =
appendStr(externalToCompile, filename)
proc addFileToLink(filename: string) =
prependStr(toLink, filename) # BUGFIX
#appendStr(toLink, filename);
proc execExternalProgram(cmd: string) =
if (optListCmd in gGlobalOptions) or (gVerbosity > 0): MessageOut(cmd)
if execCmd(cmd) != 0: rawMessage(errExecutionOfProgramFailed)
proc generateScript(projectFile: string, script: PRope) =
var (dir, name, ext) = splitFile(projectFile)
WriteRope(script, dir / addFileExt("compile_" & name,
platform.os[targetOS].scriptExt))
proc getOptSpeed(c: TSystemCC): string =
result = getConfigVar(cc[c].name & ".options.speed")
if result == "":
result = cc[c].optSpeed # use default settings from this file
proc getDebug(c: TSystemCC): string =
result = getConfigVar(cc[c].name & ".options.debug")
if result == "":
result = cc[c].debug # use default settings from this file
proc getOptSize(c: TSystemCC): string =
result = getConfigVar(cc[c].name & ".options.size")
if result == "":
result = cc[c].optSize # use default settings from this file
const
specialFileA = 42
specialFileB = 42
var fileCounter: int
proc getCompileCFileCmd(cfilename: string, isExternal: bool = false): string =
var
cfile, objfile, options, includeCmd, compilePattern, key, trunk, exe: string
var c = ccompiler
options = compileOptions
trunk = splitFile(cfilename).name
if optCDebug in gGlobalOptions:
key = trunk & ".debug"
if existsConfigVar(key): addOpt(options, getConfigVar(key))
else: addOpt(options, getDebug(c))
if (optOptimizeSpeed in gOptions):
#if ((fileCounter >= specialFileA) and (fileCounter <= specialFileB)) then
key = trunk & ".speed"
if existsConfigVar(key): addOpt(options, getConfigVar(key))
else: addOpt(options, getOptSpeed(c))
elif optOptimizeSize in gOptions:
key = trunk & ".size"
if existsConfigVar(key): addOpt(options, getConfigVar(key))
else: addOpt(options, getOptSize(c))
key = trunk & ".always"
if existsConfigVar(key): addOpt(options, getConfigVar(key))
exe = cc[c].compilerExe
key = cc[c].name & ".exe"
if existsConfigVar(key): exe = getConfigVar(key)
if targetOS == osWindows: exe = addFileExt(exe, "exe")
if (optGenDynLib in gGlobalOptions) and
(ospNeedsPIC in platform.OS[targetOS].props):
add(options, ' ' & cc[c].pic)
if targetOS == platform.hostOS:
# compute include paths:
includeCmd = cc[c].includeCmd # this is more complex than needed, but
# a workaround of a FPC bug...
add(includeCmd, quoteIfContainsWhite(libpath))
compilePattern = JoinPath(ccompilerpath, exe)
else:
includeCmd = ""
compilePattern = cc[c].compilerExe
if targetOS == platform.hostOS: cfile = cfilename
else: cfile = extractFileName(cfilename)
if not isExternal or (targetOS != platform.hostOS): objfile = toObjFile(cfile)
else: objfile = completeCFilePath(toObjFile(cfile))
cfile = quoteIfContainsWhite(AddFileExt(cfile, cExt))
objfile = quoteIfContainsWhite(objfile)
result = quoteIfContainsWhite(`%`(compilePattern, ["file", cfile, "objfile",
objfile, "options", options, "include", includeCmd, "nimrod",
getPrefixDir(), "lib", libpath]))
add(result, ' ')
add(result, `%`(cc[c].compileTmpl, ["file", cfile, "objfile", objfile,
"options", options, "include", includeCmd,
"nimrod",
quoteIfContainsWhite(getPrefixDir()),
"lib", quoteIfContainsWhite(libpath)]))
proc CompileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
isExternal: bool) =
var it = PStrEntry(list.head)
while it != nil:
inc(fileCounter) # call the C compiler for the .c file:
var compileCmd = getCompileCFileCmd(it.data, isExternal)
if not (optCompileOnly in gGlobalOptions):
add(cmds, compileCmd) #execExternalProgram(compileCmd);
if (optGenScript in gGlobalOptions):
app(script, compileCmd)
app(script, tnl)
it = PStrEntry(it.next)
proc CallCCompiler(projectfile: string) =
var
linkCmd, buildgui, builddll: string
if (gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}):
return # speed up that call if only compiling and no script shall be
# generated
if (toCompile.head == nil) and (externalToCompile.head == nil): return
fileCounter = 0
var c = ccompiler
var script: PRope = nil
var cmds: TStringSeq = @[]
CompileCFile(toCompile, script, cmds, false)
CompileCFile(externalToCompile, script, cmds, true)
if not (optCompileOnly in gGlobalOptions):
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
var res = 0
if gNumberOfProcessors <= 1:
for i in countup(0, high(cmds)): res = max(execCmd(cmds[i]), res)
elif (optListCmd in gGlobalOptions) or (gVerbosity > 0):
res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams},
gNumberOfProcessors)
else:
res = execProcesses(cmds, {poUseShell, poParentStreams},
gNumberOfProcessors)
if res != 0: rawMessage(errExecutionOfProgramFailed)
if not (optNoLinking in gGlobalOptions):
# call the linker:
var linkerExe = getConfigVar(cc[c].name & ".linkerexe")
if len(linkerExe) == 0: linkerExe = cc[c].linkerExe
if targetOS == osWindows: linkerExe = addFileExt(linkerExe, "exe")
if (platform.hostOS != targetOS): linkCmd = quoteIfContainsWhite(linkerExe)
else: linkCmd = quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe))
if optGenGuiApp in gGlobalOptions: buildGui = cc[c].buildGui
else: buildGui = ""
var exefile: string
if optGenDynLib in gGlobalOptions:
exefile = `%`(platform.os[targetOS].dllFrmt, [splitFile(projectFile).name])
buildDll = cc[c].buildDll
else:
exefile = splitFile(projectFile).name & platform.os[targetOS].exeExt
buildDll = ""
if targetOS == platform.hostOS:
exefile = joinPath(splitFile(projectFile).dir, exefile)
exefile = quoteIfContainsWhite(exefile)
var it = PStrEntry(toLink.head)
var objfiles = ""
while it != nil:
add(objfiles, " ")
if targetOS == platform.hostOS:
add(objfiles, quoteIfContainsWhite(toObjfile(it.data)))
else:
add(objfiles, quoteIfContainsWhite(toObjfile(extractFileName(it.data))))
it = PStrEntry(it.next)
linkCmd = quoteIfContainsWhite(`%`(linkCmd, ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath]))
add(linkCmd, ' ')
add(linkCmd, `%`(cc[c].linkTmpl, ["builddll", builddll, "buildgui",
buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile,
"nimrod",
quoteIfContainsWhite(getPrefixDir()),
"lib", quoteIfContainsWhite(libpath)]))
if not (optCompileOnly in gGlobalOptions): execExternalProgram(linkCmd)
else:
linkCmd = ""
if optGenScript in gGlobalOptions:
app(script, linkCmd)
app(script, tnl)
generateScript(projectFile, script)
proc genMappingFiles(list: TLinkedList): PRope =
result = nil
var it = PStrEntry(list.head)
while it != nil:
appf(result, "--file:r\"$1\"$n", [toRope(AddFileExt(it.data, cExt))])
it = PStrEntry(it.next)
proc writeMapping(gSymbolMapping: PRope) =
if not (optGenMapping in gGlobalOptions): return
var code = toRope("[C_Files]" & "\n")
app(code, genMappingFiles(toCompile))
app(code, genMappingFiles(externalToCompile))
appf(code, "[Symbols]$n$1", [gSymbolMapping])
WriteRope(code, joinPath(projectPath, "mapping.txt"))

85
rod/filters.nim Executable file
View File

@@ -0,0 +1,85 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements Nimrod's simple filters and helpers for filters.
import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, rnimsyn
proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream
# helpers to retrieve arguments:
proc charArg*(n: PNode, name: string, pos: int, default: Char): Char
proc strArg*(n: PNode, name: string, pos: int, default: string): string
proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
# implementation
proc invalidPragma(n: PNode) =
liMessage(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
proc getArg(n: PNode, name: string, pos: int): PNode =
result = nil
if n.kind in {nkEmpty..nkNilLit}: return
for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkExprEqExpr:
if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n)
if IdentEq(n.sons[i].sons[0].ident, name):
return n.sons[i].sons[1]
elif i == pos:
return n.sons[i]
proc charArg(n: PNode, name: string, pos: int, default: Char): Char =
var x: PNode
x = getArg(n, name, pos)
if x == nil: result = default
elif x.kind == nkCharLit: result = chr(int(x.intVal))
else: invalidPragma(n)
proc strArg(n: PNode, name: string, pos: int, default: string): string =
var x: PNode
x = getArg(n, name, pos)
if x == nil: result = default
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
else: invalidPragma(n)
proc boolArg(n: PNode, name: string, pos: int, default: bool): bool =
var x: PNode
x = getArg(n, name, pos)
if x == nil: result = default
elif (x.kind == nkIdent) and IdentEq(x.ident, "true"): result = true
elif (x.kind == nkIdent) and IdentEq(x.ident, "false"): result = false
else: invalidPragma(n)
proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var
line, pattern, stripped: string
leading, trailing: bool
pattern = strArg(call, "startswith", 1, "")
leading = boolArg(call, "leading", 2, true)
trailing = boolArg(call, "trailing", 3, true)
result = LLStreamOpen("")
while not LLStreamAtEnd(stdin):
line = LLStreamReadLine(stdin)
stripped = strip(line, leading, trailing)
if (len(pattern) == 0) or startsWith(stripped, pattern):
LLStreamWriteln(result, stripped)
else:
LLStreamWriteln(result, line)
LLStreamClose(stdin)
proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var line, sub, by: string
sub = strArg(call, "sub", 1, "")
if len(sub) == 0: invalidPragma(call)
by = strArg(call, "by", 2, "")
result = LLStreamOpen("")
while not LLStreamAtEnd(stdin):
line = LLStreamReadLine(stdin)
LLStreamWriteln(result, replace(line, sub, by))
LLStreamClose(stdin)

5
rod/hashtest.nim Executable file
View File

@@ -0,0 +1,5 @@
import
nhashes
writeln(stdout, getNormalizedHash(ParamStr(1)))

545
rod/highlite.nim Executable file
View File

@@ -0,0 +1,545 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Source highlighter for programming or markup languages.
# Currently only few languages are supported, other languages may be added.
# The interface supports one language nested in another.
import
nhashes, options, msgs, strutils, platform, idents, lexbase, wordrecg, scanner
type
TTokenClass* = enum
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
gtOperator, gtPunctation, gtComment, gtLongComment, gtRegularExpression,
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
gtReference, gtOther
TGeneralTokenizer* = object of TObject
kind*: TTokenClass
start*, length*: int # private:
buf*: cstring
pos*: int
state*: TTokenClass
TSourceLanguage* = enum
langNone, langNimrod, langCpp, langCsharp, langC, langJava
const
sourceLanguageToStr*: array[TSourceLanguage, string] = ["none", "Nimrod",
"C++", "C#", "C", "Java"]
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace",
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
"EscapeSequence", "Operator", "Punctation", "Comment", "LongComment",
"RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData",
"Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink",
"Label", "Reference", "Other"]
proc getSourceLanguage*(name: string): TSourceLanguage
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string)
proc deinitGeneralTokenizer*(g: var TGeneralTokenizer)
proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage)
# implementation
proc getSourceLanguage(name: string): TSourceLanguage =
for i in countup(succ(low(TSourceLanguage)), high(TSourceLanguage)):
if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
return i
result = langNone
proc initGeneralTokenizer(g: var TGeneralTokenizer, buf: string) =
var pos: int
g.buf = cstring(buf)
g.kind = low(TTokenClass)
g.start = 0
g.length = 0
g.state = low(TTokenClass)
pos = 0 # skip initial whitespace:
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
g.pos = pos
proc deinitGeneralTokenizer(g: var TGeneralTokenizer) =
nil
proc nimGetKeyword(id: string): TTokenClass =
var i: PIdent
i = getIdent(id)
if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and
(i.id <= ord(tokKeywordHigh) - ord(tkSymbol)):
result = gtKeyword
else:
result = gtIdentifier
proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int =
var pos: int
pos = position
if g.buf[pos] == '\'':
inc(pos)
case g.buf[pos]
of 'f', 'F':
g.kind = gtFloatNumber
inc(pos)
if g.buf[pos] in {'0'..'9'}: inc(pos)
if g.buf[pos] in {'0'..'9'}: inc(pos)
of 'i', 'I':
inc(pos)
if g.buf[pos] in {'0'..'9'}: inc(pos)
if g.buf[pos] in {'0'..'9'}: inc(pos)
else:
nil
result = pos
proc nimNumber(g: var TGeneralTokenizer, position: int): int =
const
decChars = {'0'..'9', '_'}
var pos: int
pos = position
g.kind = gtDecNumber
while g.buf[pos] in decChars: inc(pos)
if g.buf[pos] == '.':
g.kind = gtFloatNumber
inc(pos)
while g.buf[pos] in decChars: inc(pos)
if g.buf[pos] in {'e', 'E'}:
g.kind = gtFloatNumber
inc(pos)
if g.buf[pos] in {'+', '-'}: inc(pos)
while g.buf[pos] in decChars: inc(pos)
result = nimNumberPostfix(g, pos)
proc nimNextToken(g: var TGeneralTokenizer) =
const
hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'}
octChars = {'0'..'7', '_'}
binChars = {'0'..'1', '_'}
var
pos: int
id: string
pos = g.pos
g.start = g.pos
if g.state == gtStringLit:
g.kind = gtStringLit
while true:
case g.buf[pos]
of '\\':
g.kind = gtEscapeSequence
inc(pos)
case g.buf[pos]
of 'x', 'X':
inc(pos)
if g.buf[pos] in hexChars: inc(pos)
if g.buf[pos] in hexChars: inc(pos)
of '0'..'9':
while g.buf[pos] in {'0'..'9'}: inc(pos)
of '\0':
g.state = gtNone
else: inc(pos)
break
of '\0', '\x0D', '\x0A':
g.state = gtNone
break
of '\"':
inc(pos)
g.state = gtNone
break
else: inc(pos)
else:
case g.buf[pos]
of ' ', '\x09'..'\x0D':
g.kind = gtWhitespace
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
of '#':
g.kind = gtComment
while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}): inc(pos)
of 'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF':
id = ""
while g.buf[pos] in scanner.SymChars + {'_'}:
add(id, g.buf[pos])
inc(pos)
if (g.buf[pos] == '\"'):
if (g.buf[pos + 1] == '\"') and (g.buf[pos + 2] == '\"'):
inc(pos, 3)
g.kind = gtLongStringLit
while true:
case g.buf[pos]
of '\0':
break
of '\"':
inc(pos)
if (g.buf[pos] == '\"') and (g.buf[pos + 1] == '\"'):
inc(pos, 2)
break
else: inc(pos)
else:
g.kind = gtRawData
inc(pos)
while not (g.buf[pos] in {'\0', '\"', '\x0A', '\x0D'}): inc(pos)
if g.buf[pos] == '\"': inc(pos)
else:
g.kind = nimGetKeyword(id)
of '0':
inc(pos)
case g.buf[pos]
of 'b', 'B':
inc(pos)
while g.buf[pos] in binChars: inc(pos)
pos = nimNumberPostfix(g, pos)
of 'x', 'X':
inc(pos)
while g.buf[pos] in hexChars: inc(pos)
pos = nimNumberPostfix(g, pos)
of 'o', 'O':
inc(pos)
while g.buf[pos] in octChars: inc(pos)
pos = nimNumberPostfix(g, pos)
else: pos = nimNumber(g, pos)
of '1'..'9':
pos = nimNumber(g, pos)
of '\'':
inc(pos)
g.kind = gtCharLit
while true:
case g.buf[pos]
of '\0', '\x0D', '\x0A':
break
of '\'':
inc(pos)
break
of '\\':
inc(pos, 2)
else: inc(pos)
of '\"':
inc(pos)
if (g.buf[pos] == '\"') and (g.buf[pos + 1] == '\"'):
inc(pos, 2)
g.kind = gtLongStringLit
while true:
case g.buf[pos]
of '\0':
break
of '\"':
inc(pos)
if (g.buf[pos] == '\"') and (g.buf[pos + 1] == '\"'):
inc(pos, 2)
break
else: inc(pos)
else:
g.kind = gtStringLit
while true:
case g.buf[pos]
of '\0', '\x0D', '\x0A':
break
of '\"':
inc(pos)
break
of '\\':
g.state = g.kind
break
else: inc(pos)
of '(', ')', '[', ']', '{', '}', '`', ':', ',', ';':
inc(pos)
g.kind = gtPunctation
of '\0':
g.kind = gtEof
else:
if g.buf[pos] in scanner.OpChars:
g.kind = gtOperator
while g.buf[pos] in scanner.OpChars: inc(pos)
else:
inc(pos)
g.kind = gtNone
g.length = pos - g.pos
if (g.kind != gtEof) and (g.length <= 0):
InternalError("nimNextToken: " & $(g.buf))
g.pos = pos
proc generalNumber(g: var TGeneralTokenizer, position: int): int =
const
decChars = {'0'..'9'}
var pos: int
pos = position
g.kind = gtDecNumber
while g.buf[pos] in decChars: inc(pos)
if g.buf[pos] == '.':
g.kind = gtFloatNumber
inc(pos)
while g.buf[pos] in decChars: inc(pos)
if g.buf[pos] in {'e', 'E'}:
g.kind = gtFloatNumber
inc(pos)
if g.buf[pos] in {'+', '-'}: inc(pos)
while g.buf[pos] in decChars: inc(pos)
result = pos
proc generalStrLit(g: var TGeneralTokenizer, position: int): int =
const
decChars = {'0'..'9'}
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
var
pos: int
c: Char
pos = position
g.kind = gtStringLit
c = g.buf[pos]
inc(pos) # skip " or '
while true:
case g.buf[pos]
of '\0':
break
of '\\':
inc(pos)
case g.buf[pos]
of '\0':
break
of '0'..'9':
while g.buf[pos] in decChars: inc(pos)
of 'x', 'X':
inc(pos)
if g.buf[pos] in hexChars: inc(pos)
if g.buf[pos] in hexChars: inc(pos)
else: inc(pos, 2)
else:
if g.buf[pos] == c:
inc(pos)
break
else:
inc(pos)
result = pos
proc isKeyword(x: openarray[string], y: string): int =
var a, b, mid, c: int
a = 0
b = len(x) - 1
while a <= b:
mid = (a + b) div 2
c = cmp(x[mid], y)
if c < 0:
a = mid + 1
elif c > 0:
b = mid - 1
else:
return mid
result = - 1
proc isKeywordIgnoreCase(x: openarray[string], y: string): int =
var a, b, mid, c: int
a = 0
b = len(x) - 1
while a <= b:
mid = (a + b) div 2
c = cmpIgnoreCase(x[mid], y)
if c < 0:
a = mid + 1
elif c > 0:
b = mid - 1
else:
return mid
result = - 1
type
TTokenizerFlag = enum
hasPreprocessor, hasNestedComments
TTokenizerFlags = set[TTokenizerFlag]
proc clikeNextToken(g: var TGeneralTokenizer, keywords: openarray[string],
flags: TTokenizerFlags) =
const
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
octChars = {'0'..'7'}
binChars = {'0'..'1'}
symChars = {'A'..'Z', 'a'..'z', '0'..'9', '_', '\x80'..'\xFF'}
var
pos, nested: int
id: string
pos = g.pos
g.start = g.pos
if g.state == gtStringLit:
g.kind = gtStringLit
while true:
case g.buf[pos]
of '\\':
g.kind = gtEscapeSequence
inc(pos)
case g.buf[pos]
of 'x', 'X':
inc(pos)
if g.buf[pos] in hexChars: inc(pos)
if g.buf[pos] in hexChars: inc(pos)
of '0'..'9':
while g.buf[pos] in {'0'..'9'}: inc(pos)
of '\0':
g.state = gtNone
else: inc(pos)
break
of '\0', '\x0D', '\x0A':
g.state = gtNone
break
of '\"':
inc(pos)
g.state = gtNone
break
else: inc(pos)
else:
case g.buf[pos]
of ' ', '\x09'..'\x0D':
g.kind = gtWhitespace
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
of '/':
inc(pos)
if g.buf[pos] == '/':
g.kind = gtComment
while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}): inc(pos)
elif g.buf[pos] == '*':
g.kind = gtLongComment
nested = 0
inc(pos)
while true:
case g.buf[pos]
of '*':
inc(pos)
if g.buf[pos] == '/':
inc(pos)
if nested == 0: break
of '/':
inc(pos)
if g.buf[pos] == '*':
inc(pos)
if hasNestedComments in flags: inc(nested)
of '\0':
break
else: inc(pos)
of '#':
inc(pos)
if hasPreprocessor in flags:
g.kind = gtPreprocessor
while g.buf[pos] in {' ', Tabulator}: inc(pos)
while g.buf[pos] in symChars: inc(pos)
else:
g.kind = gtOperator
of 'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF':
id = ""
while g.buf[pos] in SymChars:
add(id, g.buf[pos])
inc(pos)
if isKeyword(keywords, id) >= 0: g.kind = gtKeyword
else: g.kind = gtIdentifier
of '0':
inc(pos)
case g.buf[pos]
of 'b', 'B':
inc(pos)
while g.buf[pos] in binChars: inc(pos)
if g.buf[pos] in {'A'..'Z', 'a'..'z'}: inc(pos)
of 'x', 'X':
inc(pos)
while g.buf[pos] in hexChars: inc(pos)
if g.buf[pos] in {'A'..'Z', 'a'..'z'}: inc(pos)
of '0'..'7':
inc(pos)
while g.buf[pos] in octChars: inc(pos)
if g.buf[pos] in {'A'..'Z', 'a'..'z'}: inc(pos)
else:
pos = generalNumber(g, pos)
if g.buf[pos] in {'A'..'Z', 'a'..'z'}: inc(pos)
of '1'..'9':
pos = generalNumber(g, pos)
if g.buf[pos] in {'A'..'Z', 'a'..'z'}: inc(pos)
of '\'':
pos = generalStrLit(g, pos)
g.kind = gtCharLit
of '\"':
inc(pos)
g.kind = gtStringLit
while true:
case g.buf[pos]
of '\0':
break
of '\"':
inc(pos)
break
of '\\':
g.state = g.kind
break
else: inc(pos)
of '(', ')', '[', ']', '{', '}', ':', ',', ';', '.':
inc(pos)
g.kind = gtPunctation
of '\0':
g.kind = gtEof
else:
if g.buf[pos] in scanner.OpChars:
g.kind = gtOperator
while g.buf[pos] in scanner.OpChars: inc(pos)
else:
inc(pos)
g.kind = gtNone
g.length = pos - g.pos
if (g.kind != gtEof) and (g.length <= 0): InternalError("clikeNextToken")
g.pos = pos
proc cNextToken(g: var TGeneralTokenizer) =
const
keywords: array[0..36, string] = ["_Bool", "_Complex", "_Imaginary", "auto",
"break", "case", "char", "const", "continue", "default", "do", "double",
"else", "enum", "extern", "float", "for", "goto", "if", "inline", "int",
"long", "register", "restrict", "return", "short", "signed", "sizeof",
"static", "struct", "switch", "typedef", "union", "unsigned", "void",
"volatile", "while"]
clikeNextToken(g, keywords, {hasPreprocessor})
proc cppNextToken(g: var TGeneralTokenizer) =
const
keywords: array[0..47, string] = ["asm", "auto", "break", "case", "catch",
"char", "class", "const", "continue", "default", "delete", "do", "double",
"else", "enum", "extern", "float", "for", "friend", "goto", "if",
"inline", "int", "long", "new", "operator", "private", "protected",
"public", "register", "return", "short", "signed", "sizeof", "static",
"struct", "switch", "template", "this", "throw", "try", "typedef",
"union", "unsigned", "virtual", "void", "volatile", "while"]
clikeNextToken(g, keywords, {hasPreprocessor})
proc csharpNextToken(g: var TGeneralTokenizer) =
const
keywords: array[0..76, string] = ["abstract", "as", "base", "bool", "break",
"byte", "case", "catch", "char", "checked", "class", "const", "continue",
"decimal", "default", "delegate", "do", "double", "else", "enum", "event",
"explicit", "extern", "false", "finally", "fixed", "float", "for",
"foreach", "goto", "if", "implicit", "in", "int", "interface", "internal",
"is", "lock", "long", "namespace", "new", "null", "object", "operator",
"out", "override", "params", "private", "protected", "public", "readonly",
"ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc",
"static", "string", "struct", "switch", "this", "throw", "true", "try",
"typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using",
"virtual", "void", "volatile", "while"]
clikeNextToken(g, keywords, {hasPreprocessor})
proc javaNextToken(g: var TGeneralTokenizer) =
const
keywords: array[0..52, string] = ["abstract", "assert", "boolean", "break",
"byte", "case", "catch", "char", "class", "const", "continue", "default",
"do", "double", "else", "enum", "extends", "false", "final", "finally",
"float", "for", "goto", "if", "implements", "import", "instanceof", "int",
"interface", "long", "native", "new", "null", "package", "private",
"protected", "public", "return", "short", "static", "strictfp", "super",
"switch", "synchronized", "this", "throw", "throws", "transient", "true",
"try", "void", "volatile", "while"]
clikeNextToken(g, keywords, {})
proc getNextToken(g: var TGeneralTokenizer, lang: TSourceLanguage) =
case lang
of langNimrod: nimNextToken(g)
of langCpp: cppNextToken(g)
of langCsharp: csharpNextToken(g)
of langC: cNextToken(g)
of langJava: javaNextToken(g)
else: InternalError("getNextToken")

132
rod/idents.nim Executable file
View File

@@ -0,0 +1,132 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Identifier handling
# An identifier is a shared non-modifiable string that can be compared by its
# id. This module is essential for the compiler's performance.
import
nhashes, strutils
type
TIdObj* = object of TObject
id*: int # unique id; use this for comparisons and not the pointers
PIdObj* = ref TIdObj
PIdent* = ref TIdent
TIdent*{.acyclic.} = object of TIdObj
s*: string
next*: PIdent # for hash-table chaining
h*: THash # hash value of s
proc getIdent*(identifier: string): PIdent
proc getIdent*(identifier: string, h: THash): PIdent
proc getIdent*(identifier: cstring, length: int, h: THash): PIdent
# special version for the scanner; the scanner's buffering scheme makes
# this horribly efficient. Most of the time no character copying is needed!
proc IdentEq*(id: PIdent, name: string): bool
# implementation
proc IdentEq(id: PIdent, name: string): bool =
result = id.id == getIdent(name).id
var buckets: array[0..4096 * 2 - 1, PIdent]
proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
var
aa, bb: char
i, j: int
i = 0
j = 0
result = 1
while j < blen:
while a[i] == '_': inc(i)
while b[j] == '_':
inc(j) # tolower inlined:
aa = a[i]
bb = b[j]
if (aa >= 'A') and (aa <= 'Z'): aa = chr(ord(aa) + (ord('a') - ord('A')))
if (bb >= 'A') and (bb <= 'Z'): bb = chr(ord(bb) + (ord('a') - ord('A')))
result = ord(aa) - ord(bb)
if (result != 0) or (aa == '\0'): break
inc(i)
inc(j)
if result == 0:
if a[i] != '\0': result = 1
proc cmpExact(a, b: cstring, blen: int): int =
var
aa, bb: char
i, j: int
i = 0
j = 0
result = 1
while j < blen:
aa = a[i]
bb = b[j]
result = ord(aa) - ord(bb)
if (result != 0) or (aa == '\0'): break
inc(i)
inc(j)
if result == 0:
if a[i] != '\0': result = 1
proc getIdent(identifier: string): PIdent =
result = getIdent(cstring(identifier), len(identifier),
getNormalizedHash(identifier))
proc getIdent(identifier: string, h: THash): PIdent =
result = getIdent(cstring(identifier), len(identifier), h)
var wordCounter: int = 1
proc getIdent(identifier: cstring, length: int, h: THash): PIdent =
var
idx, id: int
last: PIdent
idx = h and high(buckets)
result = buckets[idx]
last = nil
id = 0
while result != nil:
if cmpExact(cstring(result.s), identifier, length) == 0:
if last != nil:
# make access to last looked up identifier faster:
last.next = result.next
result.next = buckets[idx]
buckets[idx] = result
return
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
#if (id <> 0) and (id <> result.id) then begin
# result := buckets[idx];
# writeln('current id ', id);
# for i := 0 to len-1 do write(identifier[i]);
# writeln;
# while result <> nil do begin
# writeln(result.s, ' ', result.id);
# result := result.next
# end
# end;
assert((id == 0) or (id == result.id))
id = result.id
last = result
result = result.next
new(result)
result.h = h
result.s = newString(length)
for i in countup(0, length + 0 - 1): result.s[i] = identifier[i - 0]
result.next = buckets[idx]
buckets[idx] = result
if id == 0:
inc(wordCounter)
result.id = - wordCounter
else:
result.id = id # writeln('new word ', result.s);

127
rod/importer.nim Executable file
View File

@@ -0,0 +1,127 @@
#
#
# 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 the symbol importing mechanism.
import
strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, semdata,
passes
proc evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(c: PContext, n: PNode): PNode
proc importAllSymbols*(c: PContext, fromMod: PSym)
proc getModuleFile*(n: PNode): string
# implementation
proc findModule(info: TLineInfo, modulename: string): string =
# returns path to module
result = options.FindFile(AddFileExt(modulename, nimExt))
if result == "": liMessage(info, errCannotOpenFile, modulename)
proc getModuleFile(n: PNode): string =
case n.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
result = findModule(n.info, UnixToNativePath(n.strVal))
of nkIdent:
result = findModule(n.info, n.ident.s)
of nkSym:
result = findModule(n.info, n.sym.name.s)
else:
internalError(n.info, "getModuleFile()")
result = ""
proc rawImportSymbol(c: PContext, s: PSym) =
var
check, copy, e: PSym
etyp: PType # enumeration type
it: TIdentIter
# This does not handle stubs, because otherwise loading on demand would be
# pointless in practice. So importing stubs is fine here!
copy = s # do not copy symbols when importing!
# check if we have already a symbol of the same name:
check = StrTableGet(c.tab.stack[importTablePos], s.name)
if (check != nil) and (check.id != copy.id):
if not (s.kind in OverloadableSyms):
# s and check need to be qualified:
IntSetIncl(c.AmbiguousSymbols, copy.id)
IntSetIncl(c.AmbiguousSymbols, check.id)
StrTableAdd(c.tab.stack[importTablePos], copy)
if s.kind == skType:
etyp = s.typ
if etyp.kind in {tyBool, tyEnum}:
for j in countup(0, sonsLen(etyp.n) - 1):
e = etyp.n.sons[j].sym
if (e.Kind != skEnumField):
InternalError(s.info, "rawImportSymbol")
# BUGFIX: because of aliases for enums the symbol may already
# have been put into the symbol table
# BUGFIX: but only iff they are the same symbols!
check = InitIdentIter(it, c.tab.stack[importTablePos], e.name)
while check != nil:
if check.id == e.id:
e = nil
break
check = NextIdentIter(it, c.tab.stack[importTablePos])
if e != nil:
rawImportSymbol(c, e)
elif s.kind == skConverter:
addConverter(c, s) # rodgen assures that converters are no stubs
proc importSymbol(c: PContext, ident: PNode, fromMod: PSym) =
var
s, e: PSym
it: TIdentIter
if (ident.kind != nkIdent): InternalError(ident.info, "importSymbol")
s = StrTableGet(fromMod.tab, ident.ident)
if s == nil: liMessage(ident.info, errUndeclaredIdentifier, ident.ident.s)
if s.kind == skStub: loadStub(s)
if not (s.Kind in ExportableSymKinds):
InternalError(ident.info, "importSymbol: 2")
# for an enumeration we have to add all identifiers
case s.Kind
of skProc, skMethod, skIterator, skMacro, skTemplate, skConverter:
# for a overloadable syms add all overloaded routines
e = InitIdentIter(it, fromMod.tab, s.name)
while e != nil:
if (e.name.id != s.Name.id): InternalError(ident.info, "importSymbol: 3")
rawImportSymbol(c, e)
e = NextIdentIter(it, fromMod.tab)
else: rawImportSymbol(c, s)
proc importAllSymbols(c: PContext, fromMod: PSym) =
var i: TTabIter
var s = InitTabIter(i, fromMod.tab)
while s != nil:
if s.kind != skModule:
if s.kind != skEnumField:
if not (s.Kind in ExportableSymKinds):
InternalError(s.info, "importAllSymbols: " & $s.kind)
rawImportSymbol(c, s) # this is correct!
s = NextIter(i, fromMod.tab)
proc evalImport(c: PContext, n: PNode): PNode =
result = n
for i in countup(0, sonsLen(n) - 1):
var f = getModuleFile(n.sons[i])
var m = gImportModule(f)
if sfDeprecated in m.flags:
liMessage(n.sons[i].info, warnDeprecated, m.name.s)
# ``addDecl`` needs to be done before ``importAllSymbols``!
addDecl(c, m) # add symbol to symbol table of module
importAllSymbols(c, m)
proc evalFrom(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 2)
var f = getModuleFile(n.sons[0])
var m = gImportModule(f)
n.sons[0] = newSymNode(m)
addDecl(c, m) # add symbol to symbol table of module
for i in countup(1, sonsLen(n) - 1): importSymbol(c, n.sons[i], m)

15
rod/interact.nim Executable file
View File

@@ -0,0 +1,15 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This file implements interactive sessions.
import
llstream, strutils, ropes, nstrtabs, msgs
# implementation

170
rod/lexbase.nim Executable file
View File

@@ -0,0 +1,170 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# 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
llstream, strutils
const
Lrz* = ' '
Apo* = '\''
Tabulator* = '\x09'
ESC* = '\x1B'
CR* = '\x0D'
FF* = '\x0C'
LF* = '\x0A'
BEL* = '\x07'
BACKSPACE* = '\x08'
VT* = '\x0B'
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* = {CR, LF}
type
TBaseLexer* = object of TObject
bufpos*: int
buf*: cstring
bufLen*: int # length of buffer in characters
stream*: PLLStream # we read from this stream
LineNumber*: int # the current line number
# private data:
sentinel*: int
lineStart*: int # index of last line start in buffer
proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
bufLen: int = 8192)
# 8K is a reasonable buffer size
proc closeBaseLexer*(L: var TBaseLexer)
proc getCurrentLine*(L: TBaseLexer, marker: bool = true): string
proc getColNumber*(L: TBaseLexer, pos: int): int
proc HandleCR*(L: var TBaseLexer, pos: int): int
# Call this if you scanned over CR in the buffer; it returns the
# position to continue the scanning from. `pos` must be the position
# of the CR.
proc HandleLF*(L: var TBaseLexer, pos: int): int
# Call this if you scanned over LF in the buffer; it returns the the
# position to continue the scanning from. `pos` must be the position
# of the LF.
# implementation
const
chrSize = sizeof(char)
proc closeBaseLexer(L: var TBaseLexer) =
dealloc(L.buf)
LLStreamClose(L.stream)
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 = LLStreamRead(L.stream, 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 = LLStreamRead(L.stream, 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] == CR)
inc(L.linenumber)
result = fillBaseLexer(L, pos)
if L.buf[result] == LF:
result = fillBaseLexer(L, result)
proc HandleLF(L: var TBaseLexer, pos: int): int =
assert(L.buf[pos] == LF)
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 openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen: int = 8192) =
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.stream = inputstream
fillBuffer(L)
skip_UTF_8_BOM(L)
proc getColNumber(L: TBaseLexer, pos: int): int =
result = abs(pos - L.lineStart)
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
var i: int
result = ""
i = L.lineStart
while not (L.buf[i] in {CR, LF, EndOfFile}):
add(result, L.buf[i])
inc(i)
result = result & "\n"
if marker:
result = result & RepeatChar(getColNumber(L, L.bufpos)) & '^' & "\n"

107
rod/lists.nim Executable file
View File

@@ -0,0 +1,107 @@
#
#
# 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 generic doubled linked list.
type
PListEntry* = ref TListEntry
TListEntry* = object of TObject
prev*, next*: PListEntry
TStrEntry* = object of TListEntry
data*: string
PStrEntry* = ref TStrEntry
TLinkedList* = object # for the "find" operation:
head*, tail*: PListEntry
Counter*: int
TCompareProc* = proc (entry: PListEntry, closure: Pointer): bool
proc InitLinkedList*(list: var TLinkedList)
proc Append*(list: var TLinkedList, entry: PListEntry)
proc Prepend*(list: var TLinkedList, entry: PListEntry)
proc Remove*(list: var TLinkedList, entry: PListEntry)
proc InsertBefore*(list: var TLinkedList, pos, entry: PListEntry)
proc Find*(list: TLinkedList, fn: TCompareProc, closure: Pointer): PListEntry
proc AppendStr*(list: var TLinkedList, data: string)
proc IncludeStr*(list: var TLinkedList, data: string): bool
proc PrependStr*(list: var TLinkedList, data: string)
# implementation
proc InitLinkedList(list: var TLinkedList) =
list.Counter = 0
list.head = nil
list.tail = nil
proc Append(list: var TLinkedList, entry: PListEntry) =
Inc(list.counter)
entry.next = nil
entry.prev = list.tail
if list.tail != nil:
assert(list.tail.next == nil)
list.tail.next = entry
list.tail = entry
if list.head == nil: list.head = entry
proc newStrEntry(data: string): PStrEntry =
new(result)
result.data = data
proc AppendStr(list: var TLinkedList, data: string) =
append(list, newStrEntry(data))
proc PrependStr(list: var TLinkedList, data: string) =
prepend(list, newStrEntry(data))
proc IncludeStr(list: var TLinkedList, data: string): bool =
var it: PListEntry
it = list.head
while it != nil:
if PStrEntry(it).data == data:
return true # already in list
it = it.next
AppendStr(list, data) # else: add to list
result = false
proc InsertBefore(list: var TLinkedList, pos, entry: PListEntry) =
assert(pos != nil)
if pos == list.head:
prepend(list, entry)
else:
Inc(list.counter)
entry.next = pos
entry.prev = pos.prev
if pos.prev != nil: pos.prev.next = entry
pos.prev = entry
proc Prepend(list: var TLinkedList, entry: PListEntry) =
Inc(list.counter)
entry.prev = nil
entry.next = list.head
if list.head != nil:
assert(list.head.prev == nil)
list.head.prev = entry
list.head = entry
if list.tail == nil: list.tail = entry
proc Remove(list: var TLinkedList, entry: PListEntry) =
Dec(list.counter)
if entry == list.tail:
list.tail = entry.prev
if entry == list.head:
list.head = entry.next
if entry.next != nil: entry.next.prev = entry.prev
if entry.prev != nil: entry.prev.next = entry.next
proc Find(list: TLinkedList, fn: TCompareProc, closure: Pointer): PListEntry =
result = list.head
while result != nil:
if fn(result, closure): return
result = result.next

195
rod/llstream.nim Executable file
View File

@@ -0,0 +1,195 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Low-level streams for high performance.
import
strutils
type
TLLStreamKind* = enum # stream encapsulates stdin
llsNone, # null stream: reading and writing has no effect
llsString, # stream encapsulates a string
llsFile, # stream encapsulates a file
llsStdIn
TLLStream* = object of TObject
kind*: TLLStreamKind # accessible for low-level access (lexbase uses this)
f*: tfile
s*: string
rd*, wr*: int # for string streams
PLLStream* = ref TLLStream
proc LLStreamOpen*(data: string): PLLStream
proc LLStreamOpen*(f: var tfile): PLLStream
proc LLStreamOpen*(filename: string, mode: TFileMode): PLLStream
proc LLStreamOpen*(): PLLStream
proc LLStreamOpenStdIn*(): PLLStream
proc LLStreamClose*(s: PLLStream)
proc LLStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int
proc LLStreamReadLine*(s: PLLStream): string
proc LLStreamReadAll*(s: PLLStream): string
proc LLStreamWrite*(s: PLLStream, data: string)
proc LLStreamWrite*(s: PLLStream, data: Char)
proc LLStreamWrite*(s: PLLStream, buf: pointer, buflen: int)
proc LLStreamWriteln*(s: PLLStream, data: string)
proc LLStreamAtEnd*(s: PLLStream): bool
# implementation
proc LLStreamOpen(data: string): PLLStream =
new(result)
result.s = data
result.kind = llsString
proc LLStreamOpen(f: var tfile): PLLStream =
new(result)
result.f = f
result.kind = llsFile
proc LLStreamOpen(filename: string, mode: TFileMode): PLLStream =
new(result)
result.kind = llsFile
if not open(result.f, filename, mode): result = nil
proc LLStreamOpen(): PLLStream =
new(result)
result.kind = llsNone
proc LLStreamOpenStdIn(): PLLStream =
new(result)
result.kind = llsStdIn
result.s = ""
proc LLStreamClose(s: PLLStream) =
case s.kind
of llsNone, llsString, llsStdIn:
nil
of llsFile:
close(s.f)
proc LLreadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
var
line: string
L: int
s.s = ""
s.rd = 0
while true:
write(stdout, "Nimrod> ")
line = readLine(stdin)
L = len(line)
add(s.s, line)
add(s.s, "\n")
if (L > 0) and (line[L - 1 + 0] == '#'): break
result = min(bufLen, len(s.s) - s.rd)
if result > 0:
copyMem(buf, addr(s.s[0 + s.rd]), result)
inc(s.rd, result)
proc LLStreamRead(s: PLLStream, buf: pointer, bufLen: int): int =
case s.kind
of llsNone:
result = 0
of llsString:
result = min(bufLen, len(s.s) - s.rd)
if result > 0:
copyMem(buf, addr(s.s[0 + s.rd]), result)
inc(s.rd, result)
of llsFile:
result = readBuffer(s.f, buf, bufLen)
of llsStdIn:
result = LLreadFromStdin(s, buf, bufLen)
proc LLStreamReadLine(s: PLLStream): string =
case s.kind
of llsNone:
result = ""
of llsString:
result = ""
while s.rd < len(s.s):
case s.s[s.rd + 0]
of '\x0D':
inc(s.rd)
if s.s[s.rd + 0] == '\x0A': inc(s.rd)
break
of '\x0A':
inc(s.rd)
break
else:
add(result, s.s[s.rd + 0])
inc(s.rd)
of llsFile:
result = readLine(s.f)
of llsStdIn:
result = readLine(stdin)
proc LLStreamAtEnd(s: PLLStream): bool =
case s.kind
of llsNone: result = true
of llsString: result = s.rd >= len(s.s)
of llsFile: result = endOfFile(s.f)
of llsStdIn: result = false
proc LLStreamWrite(s: PLLStream, data: string) =
case s.kind
of llsNone, llsStdIn:
nil
of llsString:
add(s.s, data)
inc(s.wr, len(data))
of llsFile:
write(s.f, data)
proc LLStreamWriteln(s: PLLStream, data: string) =
LLStreamWrite(s, data)
LLStreamWrite(s, "\n")
proc LLStreamWrite(s: PLLStream, data: Char) =
var c: char
case s.kind
of llsNone, llsStdIn:
nil
of llsString:
add(s.s, data)
inc(s.wr)
of llsFile:
c = data
discard writeBuffer(s.f, addr(c), sizeof(c))
proc LLStreamWrite(s: PLLStream, buf: pointer, buflen: int) =
case s.kind
of llsNone, llsStdIn:
nil
of llsString:
if bufLen > 0:
setlen(s.s, len(s.s) + bufLen)
copyMem(addr(s.s[0 + s.wr]), buf, bufLen)
inc(s.wr, bufLen)
of llsFile:
discard writeBuffer(s.f, buf, bufLen)
proc LLStreamReadAll(s: PLLStream): string =
const
bufSize = 2048
var bytes, i: int
case s.kind
of llsNone, llsStdIn:
result = ""
of llsString:
if s.rd == 0: result = s.s
else: result = copy(s.s, s.rd + 0)
s.rd = len(s.s)
of llsFile:
result = newString(bufSize)
bytes = readBuffer(s.f, addr(result[0]), bufSize)
i = bytes
while bytes == bufSize:
setlen(result, i + bufSize)
bytes = readBuffer(s.f, addr(result[i + 0]), bufSize)
inc(i, bytes)
setlen(result, i)

103
rod/llvmdata.nim Executable file
View File

@@ -0,0 +1,103 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this module implements data structures for emitting LLVM.
import
ast, astalgo, idents, lists, passes
type
VTypeKind* = enum
VoidTyID, #/< 0: type with no size
FloatTyID, #/< 1: 32 bit floating point type
DoubleTyID, #/< 2: 64 bit floating point type
X86_FP80TyID, #/< 3: 80 bit floating point type (X87)
FP128TyID, #/< 4: 128 bit floating point type (112-bit mantissa)
PPC_FP128TyID, #/< 5: 128 bit floating point type (two 64-bits)
LabelTyID, #/< 6: Labels
MetadataTyID, #/< 7: Metadata
# Derived types... see DerivedTypes.h file...
# Make sure FirstDerivedTyID stays up to date!!!
IntegerTyID, #/< 8: Arbitrary bit width integers
FunctionTyID, #/< 9: Functions
StructTyID, #/< 10: Structures
ArrayTyID, #/< 11: Arrays
PointerTyID, #/< 12: Pointers
OpaqueTyID, #/< 13: Opaque: type with unknown structure
VectorTyID #/< 14: SIMD 'packed' format, or other vector type
VType* = ref VTypeDesc
VTypeSeq* = seq[VType]
VTypeDesc* = object of TIdObj
k*: VTypeKind
s*: VTypeSeq
arrayLen*: int
name*: string
VInstrKind* = enum
iNone, iAdd, iSub, iMul, iDiv, iMod
VLocalVar*{.final.} = object
VInstr*{.final.} = object #/ This represents a single basic block in LLVM. A basic block is simply a
#/ container of instructions that execute sequentially. Basic blocks are Values
#/ because they are referenced by instructions such as branches and switch
#/ tables. The type of a BasicBlock is "Type::LabelTy" because the basic block
#/ represents a label to which a branch can jump.
#/
k*: VInstrKind
VBlock* = ref VBlockDesc
VBlockDesc*{.final.} = object # LLVM basic block
# list of instructions
VLinkage* = enum
ExternalLinkage, # Externally visible function
LinkOnceLinkage, # Keep one copy of function when linking (inline)
WeakLinkage, # Keep one copy of function when linking (weak)
AppendingLinkage, # Special purpose, only applies to global arrays
InternalLinkage, # Rename collisions when linking (static functions)
DLLImportLinkage, # Function to be imported from DLL
DLLExportLinkage, # Function to be accessible from DLL
ExternalWeakLinkage, # ExternalWeak linkage description
GhostLinkage # Stand-in functions for streaming fns from bitcode
VVisibility* = enum
DefaultVisibility, # The GV is visible
HiddenVisibility, # The GV is hidden
ProtectedVisibility # The GV is protected
TLLVMCallConv* = enum
CCallConv = 0, FastCallConv = 8, ColdCallConv = 9, X86StdcallCallConv = 64,
X86FastcallCallConv = 65
VProc* = ref VProcDesc
VProcDesc*{.final.} = object
b*: VBlock
name*: string
sym*: PSym # proc that is generated
linkage*: VLinkage
vis*: VVisibility
callConv*: VCallConv
next*: VProc
VModule* = ref VModuleDesc
VModuleDesc* = object of TPassContext # represents a C source file
sym*: PSym
filename*: string
typeCache*: TIdTable # cache the generated types
forwTypeCache*: TIdTable # cache for forward declarations of types
declaredThings*: TIntSet # things we have declared in this file
declaredProtos*: TIntSet # prototypes we have declared in this file
headerFiles*: TLinkedList # needed headers to include
typeInfoMarker*: TIntSet # needed for generating type information
initProc*: VProc # code for init procedure
typeStack*: TTypeSeq # used for type generation
dataCache*: TNodeTable
forwardedProcs*: TSymSeq # keep forwarded procs here
typeNodes*, nimTypes*: int # used for type info generation
typeNodesName*, nimTypesName*: PRope # used for type info generation
labels*: natural # for generating unique module-scope names
next*: VModule # to stack modules
# implementation

658
rod/llvmdyn.nim Executable file
View File

@@ -0,0 +1,658 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this module implements the interface to LLVM.
const
llvmdll* = "llvm.dll" # Opaque types.
#
# The top-level container for all other LLVM Intermediate Representation (IR)
# objects. See the llvm::Module class.
#
type
cuint* = int32
PLLVMBasicBlockRef* = ref TLLVMBasicBlockRef
PLLVMMemoryBufferRef* = ref TLLVMMemoryBufferRef
PLLVMTypeRef* = ref TLLVMTypeRef
PLLVMValueRef* = ref TLLVMValueRef
TLLVMOpaqueModule*{.final.} = object
TLLVMModuleRef* = ref TLLVMOpaqueModule #
# Each value in the LLVM IR has a type, an instance of [lltype]. See the
# llvm: : Type class.
#
TLLVMOpaqueType*{.final.} = object
TLLVMTypeRef* = ref TLLVMOpaqueType #
# When building recursive types using [refine_type], [lltype] values may become
# invalid; use [lltypehandle] to resolve this problem. See the
# llvm: : AbstractTypeHolder] class.
#
TLLVMOpaqueTypeHandle*{.final.} = object
TLLVMTypeHandleRef* = ref TLLVMOpaqueTypeHandle
TLLVMOpaqueValue*{.final.} = object
TLLVMValueRef* = ref TLLVMOpaqueValue
TLLVMOpaqueBasicBlock*{.final.} = object
TLLVMBasicBlockRef* = ref TLLVMOpaqueBasicBlock
TLLVMOpaqueBuilder*{.final.} = object
TLLVMBuilderRef* = ref TLLVMOpaqueBuilder # Used to provide a module to JIT or interpreter.
# See the llvm: : ModuleProvider class.
#
TLLVMOpaqueModuleProvider*{.final.} = object
TLLVMModuleProviderRef* = ref TLLVMOpaqueModuleProvider # Used to provide a module to JIT or interpreter.
# See the llvm: : MemoryBuffer class.
#
TLLVMOpaqueMemoryBuffer*{.final.} = object
TLLVMMemoryBufferRef* = ref TLLVMOpaqueMemoryBuffer
TLLVMTypeKind* = enum
LLVMVoidTypeKind, # type with no size
LLVMFloatTypeKind, # 32 bit floating point type
LLVMDoubleTypeKind, # 64 bit floating point type
LLVMX86_FP80TypeKind, # 80 bit floating point type (X87)
LLVMFP128TypeKind, # 128 bit floating point type (112-bit mantissa)
LLVMPPC_FP128TypeKind, # 128 bit floating point type (two 64-bits)
LLVMLabelTypeKind, # Labels
LLVMIntegerTypeKind, # Arbitrary bit width integers
LLVMFunctionTypeKind, # Functions
LLVMStructTypeKind, # Structures
LLVMArrayTypeKind, # Arrays
LLVMPointerTypeKind, # Pointers
LLVMOpaqueTypeKind, # Opaque: type with unknown structure
LLVMVectorTypeKind # SIMD 'packed' format, or other vector type
TLLVMLinkage* = enum
LLVMExternalLinkage, # Externally visible function
LLVMLinkOnceLinkage, # Keep one copy of function when linking (inline)
LLVMWeakLinkage, # Keep one copy of function when linking (weak)
LLVMAppendingLinkage, # Special purpose, only applies to global arrays
LLVMInternalLinkage, # Rename collisions when linking (static functions)
LLVMDLLImportLinkage, # Function to be imported from DLL
LLVMDLLExportLinkage, # Function to be accessible from DLL
LLVMExternalWeakLinkage, # ExternalWeak linkage description
LLVMGhostLinkage # Stand-in functions for streaming fns from bitcode
TLLVMVisibility* = enum
LLVMDefaultVisibility, # The GV is visible
LLVMHiddenVisibility, # The GV is hidden
LLVMProtectedVisibility # The GV is protected
TLLVMCallConv* = enum
LLVMCCallConv = 0, LLVMFastCallConv = 8, LLVMColdCallConv = 9,
LLVMX86StdcallCallConv = 64, LLVMX86FastcallCallConv = 65
TLLVMIntPredicate* = enum
LLVMIntEQ = 32, # equal
LLVMIntNE, # not equal
LLVMIntUGT, # unsigned greater than
LLVMIntUGE, # unsigned greater or equal
LLVMIntULT, # unsigned less than
LLVMIntULE, # unsigned less or equal
LLVMIntSGT, # signed greater than
LLVMIntSGE, # signed greater or equal
LLVMIntSLT, # signed less than
LLVMIntSLE # signed less or equal
TLLVMRealPredicate* = enum #===-- Error handling ----------------------------------------------------===
LLVMRealPredicateFalse, # Always false (always folded)
LLVMRealOEQ, # True if ordered and equal
LLVMRealOGT, # True if ordered and greater than
LLVMRealOGE, # True if ordered and greater than or equal
LLVMRealOLT, # True if ordered and less than
LLVMRealOLE, # True if ordered and less than or equal
LLVMRealONE, # True if ordered and operands are unequal
LLVMRealORD, # True if ordered (no nans)
LLVMRealUNO, # True if unordered: isnan(X) | isnan(Y)
LLVMRealUEQ, # True if unordered or equal
LLVMRealUGT, # True if unordered or greater than
LLVMRealUGE, # True if unordered, greater than, or equal
LLVMRealULT, # True if unordered or less than
LLVMRealULE, # True if unordered, less than, or equal
LLVMRealUNE, # True if unordered or not equal
LLVMRealPredicateTrue # Always true (always folded)
proc LLVMDisposeMessage*(msg: cstring){.cdecl, dynlib: llvmdll, importc.}
#===-- Modules -----------------------------------------------------------===
# Create and destroy modules.
proc LLVMModuleCreateWithName*(ModuleID: cstring): TLLVMModuleRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMDisposeModule*(M: TLLVMModuleRef){.cdecl, dynlib: llvmdll, importc.}
# Data layout
proc LLVMGetDataLayout*(M: TLLVMModuleRef): cstring{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMSetDataLayout*(M: TLLVMModuleRef, Triple: cstring){.cdecl,
dynlib: llvmdll, importc.}
# Target triple
proc LLVMGetTarget*(M: TLLVMModuleRef): cstring{.cdecl, dynlib: llvmdll, importc.}
# Const before type ignored
proc LLVMSetTarget*(M: TLLVMModuleRef, Triple: cstring){.cdecl, dynlib: llvmdll,
importc.}
# Same as Module: : addTypeName.
proc LLVMAddTypeName*(M: TLLVMModuleRef, Name: cstring, Ty: TLLVMTypeRef): int32{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMDeleteTypeName*(M: TLLVMModuleRef, Name: cstring){.cdecl,
dynlib: llvmdll, importc.}
#===-- Types -------------------------------------------------------------===
# LLVM types conform to the following hierarchy:
# *
# * types:
# * integer type
# * real type
# * function type
# * sequence types:
# * array type
# * pointer type
# * vector type
# * void type
# * label type
# * opaque type
#
proc LLVMGetTypeKind*(Ty: TLLVMTypeRef): TLLVMTypeKind{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMRefineAbstractType*(AbstractType: TLLVMTypeRef,
ConcreteType: TLLVMTypeRef){.cdecl,
dynlib: llvmdll, importc.}
# Operations on integer types
proc LLVMInt1Type*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMInt8Type*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMInt16Type*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMInt32Type*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMInt64Type*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMIntType*(NumBits: cuint): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMGetIntTypeWidth*(IntegerTy: TLLVMTypeRef): cuint{.cdecl,
dynlib: llvmdll, importc.}
# Operations on real types
proc LLVMFloatType*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMDoubleType*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMX86FP80Type*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMFP128Type*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMPPCFP128Type*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
# Operations on function types
proc LLVMFunctionType*(ReturnType: TLLVMTypeRef, ParamTypes: PLLVMTypeRef,
ParamCount: cuint, IsVarArg: int32): TLLVMTypeRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMIsFunctionVarArg*(FunctionTy: TLLVMTypeRef): int32{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetReturnType*(FunctionTy: TLLVMTypeRef): TLLVMTypeRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMCountParamTypes*(FunctionTy: TLLVMTypeRef): cuint{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetParamTypes*(FunctionTy: TLLVMTypeRef, Dest: PLLVMTypeRef){.cdecl,
dynlib: llvmdll, importc.}
# Operations on struct types
proc LLVMStructType*(ElementTypes: PLLVMTypeRef, ElementCount: cuint,
isPacked: int32): TLLVMTypeRef{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMCountStructElementTypes*(StructTy: TLLVMTypeRef): cuint{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetStructElementTypes*(StructTy: TLLVMTypeRef, Dest: pLLVMTypeRef){.
cdecl, dynlib: llvmdll, importc.}
proc LLVMIsPackedStruct*(StructTy: TLLVMTypeRef): int32{.cdecl, dynlib: llvmdll,
importc.}
# Operations on array, pointer, and vector types (sequence types)
proc LLVMArrayType*(ElementType: TLLVMTypeRef, ElementCount: cuint): TLLVMTypeRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMPointerType*(ElementType: TLLVMTypeRef, AddressSpace: cuint): TLLVMTypeRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMVectorType*(ElementType: TLLVMTypeRef, ElementCount: cuint): TLLVMTypeRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMGetElementType*(Ty: TLLVMTypeRef): TLLVMTypeRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetArrayLength*(ArrayTy: TLLVMTypeRef): cuint{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMGetPointerAddressSpace*(PointerTy: TLLVMTypeRef): cuint{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetVectorSize*(VectorTy: TLLVMTypeRef): cuint{.cdecl, dynlib: llvmdll,
importc.}
# Operations on other types
proc LLVMVoidType*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMLabelType*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMOpaqueType*(): TLLVMTypeRef{.cdecl, dynlib: llvmdll, importc.}
# Operations on type handles
proc LLVMCreateTypeHandle*(PotentiallyAbstractTy: TLLVMTypeRef): TLLVMTypeHandleRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMRefineType*(AbstractTy: TLLVMTypeRef, ConcreteTy: TLLVMTypeRef){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMResolveTypeHandle*(TypeHandle: TLLVMTypeHandleRef): TLLVMTypeRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMDisposeTypeHandle*(TypeHandle: TLLVMTypeHandleRef){.cdecl,
dynlib: llvmdll, importc.}
#===-- Values ------------------------------------------------------------===
# The bulk of LLVM's object model consists of values, which comprise a very
# * rich type hierarchy.
# *
# * values:
# * constants:
# * scalar constants
# * composite contants
# * globals:
# * global variable
# * function
# * alias
# * basic blocks
#
# Operations on all values
proc LLVMTypeOf*(Val: TLLVMValueRef): TLLVMTypeRef{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMGetValueName*(Val: TLLVMValueRef): cstring{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMSetValueName*(Val: TLLVMValueRef, Name: cstring){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMDumpValue*(Val: TLLVMValueRef){.cdecl, dynlib: llvmdll, importc.}
# Operations on constants of any type
proc LLVMConstNull*(Ty: TLLVMTypeRef): TLLVMValueRef{.cdecl, dynlib: llvmdll,
importc.}
# all zeroes
proc LLVMConstAllOnes*(Ty: TLLVMTypeRef): TLLVMValueRef{.cdecl, dynlib: llvmdll,
importc.}
# only for int/vector
proc LLVMGetUndef*(Ty: TLLVMTypeRef): TLLVMValueRef{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMIsConstant*(Val: TLLVMValueRef): int32{.cdecl, dynlib: llvmdll, importc.}
proc LLVMIsNull*(Val: TLLVMValueRef): int32{.cdecl, dynlib: llvmdll, importc.}
proc LLVMIsUndef*(Val: TLLVMValueRef): int32{.cdecl, dynlib: llvmdll, importc.}
# Operations on scalar constants
proc LLVMConstInt*(IntTy: TLLVMTypeRef, N: qword, SignExtend: int32): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstReal*(RealTy: TLLVMTypeRef, N: float64): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
# Operations on composite constants
proc LLVMConstString*(Str: cstring, len: cuint, DontNullTerminate: int32): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstArray*(ArrayTy: TLLVMTypeRef, ConstantVals: pLLVMValueRef,
len: cuint): TLLVMValueRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMConstStruct*(ConstantVals: pLLVMValueRef, Count: cuint, ispacked: int32): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstVector*(ScalarConstantVals: pLLVMValueRef, Size: cuint): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
# Constant expressions
proc LLVMSizeOf*(Ty: TLLVMTypeRef): TLLVMValueRef{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMConstNeg*(ConstantVal: TLLVMValueRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMConstNot*(ConstantVal: TLLVMValueRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMConstAdd*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstSub*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstMul*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstUDiv*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstSDiv*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstFDiv*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstURem*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstSRem*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstFRem*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstAnd*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstOr*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstXor*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstICmp*(Predicate: TLLVMIntPredicate, LHSConstant: TLLVMValueRef,
RHSConstant: TLLVMValueRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMConstFCmp*(Predicate: TLLVMRealPredicate, LHSConstant: TLLVMValueRef,
RHSConstant: TLLVMValueRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMConstShl*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstLShr*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstAShr*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstGEP*(ConstantVal: TLLVMValueRef, ConstantIndices: PLLVMValueRef,
NumIndices: cuint): TLLVMValueRef{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMConstTrunc*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstSExt*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstZExt*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstFPTrunc*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstFPExt*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstUIToFP*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstSIToFP*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstFPToUI*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstFPToSI*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstPtrToInt*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstIntToPtr*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstBitCast*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstSelect*(ConstantCondition: TLLVMValueRef,
ConstantIfTrue: TLLVMValueRef,
ConstantIfFalse: TLLVMValueRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMConstExtractElement*(VectorConstant: TLLVMValueRef,
IndexConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstInsertElement*(VectorConstant: TLLVMValueRef,
ElementValueConstant: TLLVMValueRef,
IndexConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMConstShuffleVector*(VectorAConstant: TLLVMValueRef,
VectorBConstant: TLLVMValueRef,
MaskConstant: TLLVMValueRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
# Operations on global variables, functions, and aliases (globals)
proc LLVMIsDeclaration*(Global: TLLVMValueRef): int32{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMGetLinkage*(Global: TLLVMValueRef): TLLVMLinkage{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMSetLinkage*(Global: TLLVMValueRef, Linkage: TLLVMLinkage){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetSection*(Global: TLLVMValueRef): cstring{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMSetSection*(Global: TLLVMValueRef, Section: cstring){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetVisibility*(Global: TLLVMValueRef): TLLVMVisibility{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMSetVisibility*(Global: TLLVMValueRef, Viz: TLLVMVisibility){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetAlignment*(Global: TLLVMValueRef): cuint{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMSetAlignment*(Global: TLLVMValueRef, Bytes: cuint){.cdecl,
dynlib: llvmdll, importc.}
# Operations on global variables
# Const before type ignored
proc LLVMAddGlobal*(M: TLLVMModuleRef, Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
# Const before type ignored
proc LLVMGetNamedGlobal*(M: TLLVMModuleRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMDeleteGlobal*(GlobalVar: TLLVMValueRef){.cdecl, dynlib: llvmdll,
importc.}
proc LLVMHasInitializer*(GlobalVar: TLLVMValueRef): int32{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetInitializer*(GlobalVar: TLLVMValueRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMSetInitializer*(GlobalVar: TLLVMValueRef, ConstantVal: TLLVMValueRef){.
cdecl, dynlib: llvmdll, importc.}
proc LLVMIsThreadLocal*(GlobalVar: TLLVMValueRef): int32{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMSetThreadLocal*(GlobalVar: TLLVMValueRef, IsThreadLocal: int32){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMIsGlobalConstant*(GlobalVar: TLLVMValueRef): int32{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMSetGlobalConstant*(GlobalVar: TLLVMValueRef, IsConstant: int32){.cdecl,
dynlib: llvmdll, importc.}
# Operations on functions
# Const before type ignored
proc LLVMAddFunction*(M: TLLVMModuleRef, Name: cstring, FunctionTy: TLLVMTypeRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
# Const before type ignored
proc LLVMGetNamedFunction*(M: TLLVMModuleRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMDeleteFunction*(Fn: TLLVMValueRef){.cdecl, dynlib: llvmdll, importc.}
proc LLVMCountParams*(Fn: TLLVMValueRef): cuint{.cdecl, dynlib: llvmdll, importc.}
proc LLVMGetParams*(Fn: TLLVMValueRef, Params: PLLVMValueRef){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetParam*(Fn: TLLVMValueRef, Index: cuint): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetIntrinsicID*(Fn: TLLVMValueRef): cuint{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMGetFunctionCallConv*(Fn: TLLVMValueRef): cuint{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMSetFunctionCallConv*(Fn: TLLVMValueRef, CC: cuint){.cdecl,
dynlib: llvmdll, importc.}
# Const before type ignored
proc LLVMGetCollector*(Fn: TLLVMValueRef): cstring{.cdecl, dynlib: llvmdll,
importc.}
# Const before type ignored
proc LLVMSetCollector*(Fn: TLLVMValueRef, Coll: cstring){.cdecl,
dynlib: llvmdll, importc.}
# Operations on basic blocks
proc LLVMBasicBlockAsValue*(Bb: TLLVMBasicBlockRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMValueIsBasicBlock*(Val: TLLVMValueRef): int32{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMValueAsBasicBlock*(Val: TLLVMValueRef): TLLVMBasicBlockRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMCountBasicBlocks*(Fn: TLLVMValueRef): cuint{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMGetBasicBlocks*(Fn: TLLVMValueRef, BasicBlocks: PLLVMBasicBlockRef){.
cdecl, dynlib: llvmdll, importc.}
proc LLVMGetEntryBasicBlock*(Fn: TLLVMValueRef): TLLVMBasicBlockRef{.cdecl,
dynlib: llvmdll, importc.}
# Const before type ignored
proc LLVMAppendBasicBlock*(Fn: TLLVMValueRef, Name: cstring): TLLVMBasicBlockRef{.
cdecl, dynlib: llvmdll, importc.}
# Const before type ignored
proc LLVMInsertBasicBlock*(InsertBeforeBB: TLLVMBasicBlockRef, Name: cstring): TLLVMBasicBlockRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMDeleteBasicBlock*(BB: TLLVMBasicBlockRef){.cdecl, dynlib: llvmdll,
importc.}
# Operations on call sites
proc LLVMSetInstructionCallConv*(Instr: TLLVMValueRef, CC: cuint){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMGetInstructionCallConv*(Instr: TLLVMValueRef): cuint{.cdecl,
dynlib: llvmdll, importc.}
# Operations on phi nodes
proc LLVMAddIncoming*(PhiNode: TLLVMValueRef, IncomingValues: PLLVMValueRef,
IncomingBlocks: PLLVMBasicBlockRef, Count: cuint){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMCountIncoming*(PhiNode: TLLVMValueRef): cuint{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMGetIncomingValue*(PhiNode: TLLVMValueRef, Index: cuint): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMGetIncomingBlock*(PhiNode: TLLVMValueRef, Index: cuint): TLLVMBasicBlockRef{.
cdecl, dynlib: llvmdll, importc.}
#===-- Instruction builders ----------------------------------------------===
# An instruction builder represents a point within a basic block, and is the
# * exclusive means of building instructions using the C interface.
#
proc LLVMCreateBuilder*(): TLLVMBuilderRef{.cdecl, dynlib: llvmdll, importc.}
proc LLVMPositionBuilderBefore*(Builder: TLLVMBuilderRef, Instr: TLLVMValueRef){.
cdecl, dynlib: llvmdll, importc.}
proc LLVMPositionBuilderAtEnd*(Builder: TLLVMBuilderRef,
theBlock: TLLVMBasicBlockRef){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMDisposeBuilder*(Builder: TLLVMBuilderRef){.cdecl, dynlib: llvmdll,
importc.}
# Terminators
proc LLVMBuildRetVoid*(para1: TLLVMBuilderRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildRet*(para1: TLLVMBuilderRef, V: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildBr*(para1: TLLVMBuilderRef, Dest: TLLVMBasicBlockRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildCondBr*(para1: TLLVMBuilderRef, IfCond: TLLVMValueRef,
ThenBranch: TLLVMBasicBlockRef,
ElseBranch: TLLVMBasicBlockRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildSwitch*(para1: TLLVMBuilderRef, V: TLLVMValueRef,
ElseBranch: TLLVMBasicBlockRef, NumCases: cuint): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
# Const before type ignored
proc LLVMBuildInvoke*(para1: TLLVMBuilderRef, Fn: TLLVMValueRef,
Args: PLLVMValueRef, NumArgs: cuint,
ThenBranch: TLLVMBasicBlockRef, Catch: TLLVMBasicBlockRef,
Name: cstring): TLLVMValueRef{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMBuildUnwind*(para1: TLLVMBuilderRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildUnreachable*(para1: TLLVMBuilderRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
# Add a case to the switch instruction
proc LLVMAddCase*(Switch: TLLVMValueRef, OnVal: TLLVMValueRef,
Dest: TLLVMBasicBlockRef){.cdecl, dynlib: llvmdll, importc.}
# Arithmetic
proc LLVMBuildAdd*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildSub*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildMul*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildUDiv*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildSDiv*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildFDiv*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildURem*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildSRem*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildFRem*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildShl*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildLShr*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildAShr*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildAnd*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildOr*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildXor*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildNeg*(para1: TLLVMBuilderRef, V: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildNot*(para1: TLLVMBuilderRef, V: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
# Memory
proc LLVMBuildMalloc*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildArrayMalloc*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef,
Val: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildAlloca*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildArrayAlloca*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef,
Val: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildFree*(para1: TLLVMBuilderRef, PointerVal: TLLVMValueRef): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildLoad*(para1: TLLVMBuilderRef, PointerVal: TLLVMValueRef,
Name: cstring): TLLVMValueRef{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMBuildStore*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
thePtr: TLLVMValueRef): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildGEP*(B: TLLVMBuilderRef, Pointer: TLLVMValueRef,
Indices: PLLVMValueRef, NumIndices: cuint, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
# Casts
proc LLVMBuildTrunc*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildZExt*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildSExt*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildFPToUI*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildFPToSI*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildUIToFP*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildSIToFP*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildFPTrunc*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildFPExt*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildPtrToInt*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildIntToPtr*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildBitCast*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
# Comparisons
proc LLVMBuildICmp*(para1: TLLVMBuilderRef, Op: TLLVMIntPredicate,
LHS: TLLVMValueRef, RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildFCmp*(para1: TLLVMBuilderRef, Op: TLLVMRealPredicate,
LHS: TLLVMValueRef, RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
# Miscellaneous instructions
proc LLVMBuildPhi*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildCall*(para1: TLLVMBuilderRef, Fn: TLLVMValueRef,
Args: PLLVMValueRef, NumArgs: cuint, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildSelect*(para1: TLLVMBuilderRef, IfCond: TLLVMValueRef,
ThenBranch: TLLVMValueRef, ElseBranch: TLLVMValueRef,
Name: cstring): TLLVMValueRef{.cdecl, dynlib: llvmdll,
importc.}
proc LLVMBuildVAArg*(para1: TLLVMBuilderRef, List: TLLVMValueRef,
Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildExtractElement*(para1: TLLVMBuilderRef, VecVal: TLLVMValueRef,
Index: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl, dynlib: llvmdll, importc.}
proc LLVMBuildInsertElement*(para1: TLLVMBuilderRef, VecVal: TLLVMValueRef,
EltVal: TLLVMValueRef, Index: TLLVMValueRef,
Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMBuildShuffleVector*(para1: TLLVMBuilderRef, V1: TLLVMValueRef,
V2: TLLVMValueRef, Mask: TLLVMValueRef,
Name: cstring): TLLVMValueRef{.cdecl,
dynlib: llvmdll, importc.}
#===-- Module providers --------------------------------------------------===
# Encapsulates the module M in a module provider, taking ownership of the
# module.
# See the constructor llvm: : ExistingModuleProvider: : ExistingModuleProvider.
#
proc LLVMCreateModuleProviderForExistingModule*(M: TLLVMModuleRef): TLLVMModuleProviderRef{.
cdecl, dynlib: llvmdll, importc.}
# Destroys the module provider MP as well as the contained module.
# See the destructor llvm: : ModuleProvider: : ~ModuleProvider.
#
proc LLVMDisposeModuleProvider*(MP: TLLVMModuleProviderRef){.cdecl,
dynlib: llvmdll, importc.}
#===-- Memory buffers ----------------------------------------------------===
proc LLVMCreateMemoryBufferWithContentsOfFile*(Path: cstring,
OutMemBuf: pLLVMMemoryBufferRef, OutMessage: var cstring): int32{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMCreateMemoryBufferWithSTDIN*(OutMemBuf: pLLVMMemoryBufferRef,
OutMessage: var cstring): int32{.cdecl,
dynlib: llvmdll, importc.}
proc LLVMDisposeMemoryBuffer*(MemBuf: TLLVMMemoryBufferRef){.cdecl,
dynlib: llvmdll, importc.}
proc LLVMWriteBitcodeToFile*(M: TLLVMModuleRef, path: cstring): int{.cdecl,
dynlib: llvmdll, importc.}
# Writes a module to the specified path. Returns 0 on success.
# implementation

547
rod/llvmstat.nim Executable file
View File

@@ -0,0 +1,547 @@
#
#
# 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 the interface to LLVM.
import # Opaque types.
#
# The top-level container for all other LLVM Intermediate Representation (IR)
# objects. See the llvm::Module class.
#
ropes
type
cuint* = int32
TLLVMTypeKind* = enum
LLVMVoidTypeKind, # type with no size
LLVMFloatTypeKind, # 32 bit floating point type
LLVMDoubleTypeKind, # 64 bit floating point type
LLVMX86_FP80TypeKind, # 80 bit floating point type (X87)
LLVMFP128TypeKind, # 128 bit floating point type (112-bit mantissa)
LLVMPPC_FP128TypeKind, # 128 bit floating point type (two 64-bits)
LLVMLabelTypeKind, # Labels
LLVMIntegerTypeKind, # Arbitrary bit width integers
LLVMFunctionTypeKind, # Functions
LLVMStructTypeKind, # Structures
LLVMArrayTypeKind, # Arrays
LLVMPointerTypeKind, # Pointers
LLVMOpaqueTypeKind, # Opaque: type with unknown structure
LLVMVectorTypeKind # SIMD 'packed' format, or other vector type
TLLVMLinkage* = enum
LLVMExternalLinkage, # Externally visible function
LLVMLinkOnceLinkage, # Keep one copy of function when linking (inline)
LLVMWeakLinkage, # Keep one copy of function when linking (weak)
LLVMAppendingLinkage, # Special purpose, only applies to global arrays
LLVMInternalLinkage, # Rename collisions when linking (static functions)
LLVMDLLImportLinkage, # Function to be imported from DLL
LLVMDLLExportLinkage, # Function to be accessible from DLL
LLVMExternalWeakLinkage, # ExternalWeak linkage description
LLVMGhostLinkage # Stand-in functions for streaming fns from bitcode
TLLVMVisibility* = enum
LLVMDefaultVisibility, # The GV is visible
LLVMHiddenVisibility, # The GV is hidden
LLVMProtectedVisibility # The GV is protected
TLLVMCallConv* = enum
LLVMCCallConv = 0, LLVMFastCallConv = 8, LLVMColdCallConv = 9,
LLVMX86StdcallCallConv = 64, LLVMX86FastcallCallConv = 65
TLLVMIntPredicate* = enum
LLVMIntEQ = 32, # equal
LLVMIntNE, # not equal
LLVMIntUGT, # unsigned greater than
LLVMIntUGE, # unsigned greater or equal
LLVMIntULT, # unsigned less than
LLVMIntULE, # unsigned less or equal
LLVMIntSGT, # signed greater than
LLVMIntSGE, # signed greater or equal
LLVMIntSLT, # signed less than
LLVMIntSLE # signed less or equal
TLLVMRealPredicate* = enum
LLVMRealPredicateFalse, # Always false (always folded)
LLVMRealOEQ, # True if ordered and equal
LLVMRealOGT, # True if ordered and greater than
LLVMRealOGE, # True if ordered and greater than or equal
LLVMRealOLT, # True if ordered and less than
LLVMRealOLE, # True if ordered and less than or equal
LLVMRealONE, # True if ordered and operands are unequal
LLVMRealORD, # True if ordered (no nans)
LLVMRealUNO, # True if unordered: isnan(X) | isnan(Y)
LLVMRealUEQ, # True if unordered or equal
LLVMRealUGT, # True if unordered or greater than
LLVMRealUGE, # True if unordered, greater than, or equal
LLVMRealULT, # True if unordered or less than
LLVMRealULE, # True if unordered, less than, or equal
LLVMRealUNE, # True if unordered or not equal
LLVMRealPredicateTrue # Always true (always folded)
PLLVMBasicBlockRef* = ref TLLVMBasicBlockRef
PLLVMMemoryBufferRef* = ref TLLVMMemoryBufferRef
PLLVMTypeRef* = ref TLLVMTypeRef
PLLVMValueRef* = ref TLLVMValueRef
TLLVMOpaqueModule*{.final.} = object
code*: PRope
TLLVMModuleRef* = ref TLLVMOpaqueModule #
# Each value in the LLVM IR has a type, an instance of [lltype]. See the
# llvm::Type class.
#
TLLVMOpaqueType*{.final.} = object
kind*: TLLVMTypeKind
TLLVMTypeRef* = ref TLLVMOpaqueType #
# When building recursive types using [refine_type], [lltype] values may become
# invalid; use [lltypehandle] to resolve this problem. See the
# llvm::AbstractTypeHolder] class.
#
TLLVMOpaqueTypeHandle*{.final.} = object
TLLVMTypeHandleRef* = ref TLLVMOpaqueTypeHandle
TLLVMOpaqueValue*{.final.} = object
TLLVMValueRef* = ref TLLVMOpaqueValue
TLLVMOpaqueBasicBlock*{.final.} = object
TLLVMBasicBlockRef* = ref TLLVMOpaqueBasicBlock
TLLVMOpaqueBuilder*{.final.} = object
TLLVMBuilderRef* = ref TLLVMOpaqueBuilder # Used to provide a module to JIT or interpreter.
# See the llvm::ModuleProvider class.
#
TLLVMOpaqueModuleProvider*{.final.} = object
TLLVMModuleProviderRef* = ref TLLVMOpaqueModuleProvider # Used to provide a module to JIT or interpreter.
# See the llvm: : MemoryBuffer class.
#
TLLVMOpaqueMemoryBuffer*{.final.} = object
TLLVMMemoryBufferRef* = ref TLLVMOpaqueMemoryBuffer #===-- Error handling ----------------------------------------------------===
proc LLVMDisposeMessage*(msg: cstring){.cdecl.}
#===-- Modules -----------------------------------------------------------===
# Create and destroy modules.
proc LLVMModuleCreateWithName*(ModuleID: cstring): TLLVMModuleRef{.cdecl.}
proc LLVMDisposeModule*(M: TLLVMModuleRef){.cdecl.}
# Data layout
proc LLVMGetDataLayout*(M: TLLVMModuleRef): cstring{.cdecl.}
proc LLVMSetDataLayout*(M: TLLVMModuleRef, Triple: cstring){.cdecl.}
# Target triple
proc LLVMGetTarget*(M: TLLVMModuleRef): cstring{.cdecl.}
proc LLVMSetTarget*(M: TLLVMModuleRef, Triple: cstring){.cdecl.}
# Same as Module: : addTypeName.
proc LLVMAddTypeName*(M: TLLVMModuleRef, Name: cstring, Ty: TLLVMTypeRef): int32{.
cdecl.}
proc LLVMDeleteTypeName*(M: TLLVMModuleRef, Name: cstring){.cdecl.}
#===-- Types -------------------------------------------------------------===
# LLVM types conform to the following hierarchy:
# *
# * types:
# * integer type
# * real type
# * function type
# * sequence types:
# * array type
# * pointer type
# * vector type
# * void type
# * label type
# * opaque type
#
proc LLVMGetTypeKind*(Ty: TLLVMTypeRef): TLLVMTypeKind{.cdecl.}
proc LLVMRefineAbstractType*(AbstractType: TLLVMTypeRef,
ConcreteType: TLLVMTypeRef){.cdecl.}
# Operations on integer types
proc LLVMInt1Type*(): TLLVMTypeRef{.cdecl.}
proc LLVMInt8Type*(): TLLVMTypeRef{.cdecl.}
proc LLVMInt16Type*(): TLLVMTypeRef{.cdecl.}
proc LLVMInt32Type*(): TLLVMTypeRef{.cdecl.}
proc LLVMInt64Type*(): TLLVMTypeRef{.cdecl.}
proc LLVMIntType*(NumBits: cuint): TLLVMTypeRef{.cdecl.}
proc LLVMGetIntTypeWidth*(IntegerTy: TLLVMTypeRef): cuint{.cdecl.}
# Operations on real types
proc LLVMFloatType*(): TLLVMTypeRef{.cdecl.}
proc LLVMDoubleType*(): TLLVMTypeRef{.cdecl.}
proc LLVMX86FP80Type*(): TLLVMTypeRef{.cdecl.}
proc LLVMFP128Type*(): TLLVMTypeRef{.cdecl.}
proc LLVMPPCFP128Type*(): TLLVMTypeRef{.cdecl.}
# Operations on function types
proc LLVMFunctionType*(ReturnType: TLLVMTypeRef, ParamTypes: PLLVMTypeRef,
ParamCount: cuint, IsVarArg: int32): TLLVMTypeRef{.cdecl.}
proc LLVMIsFunctionVarArg*(FunctionTy: TLLVMTypeRef): int32{.cdecl.}
proc LLVMGetReturnType*(FunctionTy: TLLVMTypeRef): TLLVMTypeRef{.cdecl.}
proc LLVMCountParamTypes*(FunctionTy: TLLVMTypeRef): cuint{.cdecl.}
proc LLVMGetParamTypes*(FunctionTy: TLLVMTypeRef, Dest: PLLVMTypeRef){.cdecl.}
# Operations on struct types
proc LLVMStructType*(ElementTypes: PLLVMTypeRef, ElementCount: cuint,
isPacked: int32): TLLVMTypeRef{.cdecl.}
proc LLVMCountStructElementTypes*(StructTy: TLLVMTypeRef): cuint{.cdecl.}
proc LLVMGetStructElementTypes*(StructTy: TLLVMTypeRef, Dest: pLLVMTypeRef){.
cdecl.}
proc LLVMIsPackedStruct*(StructTy: TLLVMTypeRef): int32{.cdecl.}
# Operations on array, pointer, and vector types (sequence types)
proc LLVMArrayType*(ElementType: TLLVMTypeRef, ElementCount: cuint): TLLVMTypeRef{.
cdecl.}
proc LLVMPointerType*(ElementType: TLLVMTypeRef, AddressSpace: cuint): TLLVMTypeRef{.
cdecl.}
proc LLVMVectorType*(ElementType: TLLVMTypeRef, ElementCount: cuint): TLLVMTypeRef{.
cdecl.}
proc LLVMGetElementType*(Ty: TLLVMTypeRef): TLLVMTypeRef{.cdecl.}
proc LLVMGetArrayLength*(ArrayTy: TLLVMTypeRef): cuint{.cdecl.}
proc LLVMGetPointerAddressSpace*(PointerTy: TLLVMTypeRef): cuint{.cdecl.}
proc LLVMGetVectorSize*(VectorTy: TLLVMTypeRef): cuint{.cdecl.}
# Operations on other types
proc LLVMVoidType*(): TLLVMTypeRef{.cdecl.}
proc LLVMLabelType*(): TLLVMTypeRef{.cdecl.}
proc LLVMOpaqueType*(): TLLVMTypeRef{.cdecl.}
# Operations on type handles
proc LLVMCreateTypeHandle*(PotentiallyAbstractTy: TLLVMTypeRef): TLLVMTypeHandleRef{.
cdecl.}
proc LLVMRefineType*(AbstractTy: TLLVMTypeRef, ConcreteTy: TLLVMTypeRef){.cdecl.}
proc LLVMResolveTypeHandle*(TypeHandle: TLLVMTypeHandleRef): TLLVMTypeRef{.cdecl.}
proc LLVMDisposeTypeHandle*(TypeHandle: TLLVMTypeHandleRef){.cdecl.}
#===-- Values ------------------------------------------------------------===
# The bulk of LLVM's object model consists of values, which comprise a very
# * rich type hierarchy.
# *
# * values:
# * constants:
# * scalar constants
# * composite contants
# * globals:
# * global variable
# * function
# * alias
# * basic blocks
#
# Operations on all values
proc LLVMTypeOf*(Val: TLLVMValueRef): TLLVMTypeRef{.cdecl.}
proc LLVMGetValueName*(Val: TLLVMValueRef): cstring{.cdecl.}
proc LLVMSetValueName*(Val: TLLVMValueRef, Name: cstring){.cdecl.}
proc LLVMDumpValue*(Val: TLLVMValueRef){.cdecl.}
# Operations on constants of any type
proc LLVMConstNull*(Ty: TLLVMTypeRef): TLLVMValueRef{.cdecl.}
# all zeroes
proc LLVMConstAllOnes*(Ty: TLLVMTypeRef): TLLVMValueRef{.cdecl.}
# only for int/vector
proc LLVMGetUndef*(Ty: TLLVMTypeRef): TLLVMValueRef{.cdecl.}
proc LLVMIsConstant*(Val: TLLVMValueRef): int32{.cdecl.}
proc LLVMIsNull*(Val: TLLVMValueRef): int32{.cdecl.}
proc LLVMIsUndef*(Val: TLLVMValueRef): int32{.cdecl.}
# Operations on scalar constants
proc LLVMConstInt*(IntTy: TLLVMTypeRef, N: qword, SignExtend: int32): TLLVMValueRef{.
cdecl.}
proc LLVMConstReal*(RealTy: TLLVMTypeRef, N: float64): TLLVMValueRef{.cdecl.}
# Operations on composite constants
proc LLVMConstString*(Str: cstring, len: cuint, DontNullTerminate: int32): TLLVMValueRef{.
cdecl.}
proc LLVMConstArray*(ArrayTy: TLLVMTypeRef, ConstantVals: pLLVMValueRef,
len: cuint): TLLVMValueRef{.cdecl.}
proc LLVMConstStruct*(ConstantVals: pLLVMValueRef, Count: cuint, ispacked: int32): TLLVMValueRef{.
cdecl.}
proc LLVMConstVector*(ScalarConstantVals: pLLVMValueRef, Size: cuint): TLLVMValueRef{.
cdecl.}
# Constant expressions
proc LLVMSizeOf*(Ty: TLLVMTypeRef): TLLVMValueRef{.cdecl.}
proc LLVMConstNeg*(ConstantVal: TLLVMValueRef): TLLVMValueRef{.cdecl.}
proc LLVMConstNot*(ConstantVal: TLLVMValueRef): TLLVMValueRef{.cdecl.}
proc LLVMConstAdd*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstSub*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstMul*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstUDiv*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstSDiv*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstFDiv*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstURem*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstSRem*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstFRem*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstAnd*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstOr*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstXor*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstICmp*(Predicate: TLLVMIntPredicate, LHSConstant: TLLVMValueRef,
RHSConstant: TLLVMValueRef): TLLVMValueRef{.cdecl.}
proc LLVMConstFCmp*(Predicate: TLLVMRealPredicate, LHSConstant: TLLVMValueRef,
RHSConstant: TLLVMValueRef): TLLVMValueRef{.cdecl.}
proc LLVMConstShl*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstLShr*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstAShr*(LHSConstant: TLLVMValueRef, RHSConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstGEP*(ConstantVal: TLLVMValueRef, ConstantIndices: PLLVMValueRef,
NumIndices: cuint): TLLVMValueRef{.cdecl.}
proc LLVMConstTrunc*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstSExt*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstZExt*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstFPTrunc*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstFPExt*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstUIToFP*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstSIToFP*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstFPToUI*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstFPToSI*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstPtrToInt*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstIntToPtr*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstBitCast*(ConstantVal: TLLVMValueRef, ToType: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstSelect*(ConstantCondition: TLLVMValueRef,
ConstantIfTrue: TLLVMValueRef,
ConstantIfFalse: TLLVMValueRef): TLLVMValueRef{.cdecl.}
proc LLVMConstExtractElement*(VectorConstant: TLLVMValueRef,
IndexConstant: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMConstInsertElement*(VectorConstant: TLLVMValueRef,
ElementValueConstant: TLLVMValueRef,
IndexConstant: TLLVMValueRef): TLLVMValueRef{.cdecl.}
proc LLVMConstShuffleVector*(VectorAConstant: TLLVMValueRef,
VectorBConstant: TLLVMValueRef,
MaskConstant: TLLVMValueRef): TLLVMValueRef{.cdecl.}
# Operations on global variables, functions, and aliases (globals)
proc LLVMIsDeclaration*(Global: TLLVMValueRef): int32{.cdecl.}
proc LLVMGetLinkage*(Global: TLLVMValueRef): TLLVMLinkage{.cdecl.}
proc LLVMSetLinkage*(Global: TLLVMValueRef, Linkage: TLLVMLinkage){.cdecl.}
proc LLVMGetSection*(Global: TLLVMValueRef): cstring{.cdecl.}
proc LLVMSetSection*(Global: TLLVMValueRef, Section: cstring){.cdecl.}
proc LLVMGetVisibility*(Global: TLLVMValueRef): TLLVMVisibility{.cdecl.}
proc LLVMSetVisibility*(Global: TLLVMValueRef, Viz: TLLVMVisibility){.cdecl.}
proc LLVMGetAlignment*(Global: TLLVMValueRef): cuint{.cdecl.}
proc LLVMSetAlignment*(Global: TLLVMValueRef, Bytes: cuint){.cdecl.}
# Operations on global variables
# Const before type ignored
proc LLVMAddGlobal*(M: TLLVMModuleRef, Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl.}
# Const before type ignored
proc LLVMGetNamedGlobal*(M: TLLVMModuleRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMDeleteGlobal*(GlobalVar: TLLVMValueRef){.cdecl.}
proc LLVMHasInitializer*(GlobalVar: TLLVMValueRef): int32{.cdecl.}
proc LLVMGetInitializer*(GlobalVar: TLLVMValueRef): TLLVMValueRef{.cdecl.}
proc LLVMSetInitializer*(GlobalVar: TLLVMValueRef, ConstantVal: TLLVMValueRef){.
cdecl.}
proc LLVMIsThreadLocal*(GlobalVar: TLLVMValueRef): int32{.cdecl.}
proc LLVMSetThreadLocal*(GlobalVar: TLLVMValueRef, IsThreadLocal: int32){.cdecl.}
proc LLVMIsGlobalConstant*(GlobalVar: TLLVMValueRef): int32{.cdecl.}
proc LLVMSetGlobalConstant*(GlobalVar: TLLVMValueRef, IsConstant: int32){.cdecl.}
# Operations on functions
# Const before type ignored
proc LLVMAddFunction*(M: TLLVMModuleRef, Name: cstring, FunctionTy: TLLVMTypeRef): TLLVMValueRef{.
cdecl.}
# Const before type ignored
proc LLVMGetNamedFunction*(M: TLLVMModuleRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMDeleteFunction*(Fn: TLLVMValueRef){.cdecl.}
proc LLVMCountParams*(Fn: TLLVMValueRef): cuint{.cdecl.}
proc LLVMGetParams*(Fn: TLLVMValueRef, Params: PLLVMValueRef){.cdecl.}
proc LLVMGetParam*(Fn: TLLVMValueRef, Index: cuint): TLLVMValueRef{.cdecl.}
proc LLVMGetIntrinsicID*(Fn: TLLVMValueRef): cuint{.cdecl.}
proc LLVMGetFunctionCallConv*(Fn: TLLVMValueRef): cuint{.cdecl.}
proc LLVMSetFunctionCallConv*(Fn: TLLVMValueRef, CC: cuint){.cdecl.}
# Const before type ignored
proc LLVMGetCollector*(Fn: TLLVMValueRef): cstring{.cdecl.}
# Const before type ignored
proc LLVMSetCollector*(Fn: TLLVMValueRef, Coll: cstring){.cdecl.}
# Operations on basic blocks
proc LLVMBasicBlockAsValue*(Bb: TLLVMBasicBlockRef): TLLVMValueRef{.cdecl.}
proc LLVMValueIsBasicBlock*(Val: TLLVMValueRef): int32{.cdecl.}
proc LLVMValueAsBasicBlock*(Val: TLLVMValueRef): TLLVMBasicBlockRef{.cdecl.}
proc LLVMCountBasicBlocks*(Fn: TLLVMValueRef): cuint{.cdecl.}
proc LLVMGetBasicBlocks*(Fn: TLLVMValueRef, BasicBlocks: PLLVMBasicBlockRef){.
cdecl.}
proc LLVMGetEntryBasicBlock*(Fn: TLLVMValueRef): TLLVMBasicBlockRef{.cdecl.}
# Const before type ignored
proc LLVMAppendBasicBlock*(Fn: TLLVMValueRef, Name: cstring): TLLVMBasicBlockRef{.
cdecl.}
# Const before type ignored
proc LLVMInsertBasicBlock*(InsertBeforeBB: TLLVMBasicBlockRef, Name: cstring): TLLVMBasicBlockRef{.
cdecl.}
proc LLVMDeleteBasicBlock*(BB: TLLVMBasicBlockRef){.cdecl.}
# Operations on call sites
proc LLVMSetInstructionCallConv*(Instr: TLLVMValueRef, CC: cuint){.cdecl.}
proc LLVMGetInstructionCallConv*(Instr: TLLVMValueRef): cuint{.cdecl.}
# Operations on phi nodes
proc LLVMAddIncoming*(PhiNode: TLLVMValueRef, IncomingValues: PLLVMValueRef,
IncomingBlocks: PLLVMBasicBlockRef, Count: cuint){.cdecl.}
proc LLVMCountIncoming*(PhiNode: TLLVMValueRef): cuint{.cdecl.}
proc LLVMGetIncomingValue*(PhiNode: TLLVMValueRef, Index: cuint): TLLVMValueRef{.
cdecl.}
proc LLVMGetIncomingBlock*(PhiNode: TLLVMValueRef, Index: cuint): TLLVMBasicBlockRef{.
cdecl.}
#===-- Instruction builders ----------------------------------------------===
# An instruction builder represents a point within a basic block, and is the
# * exclusive means of building instructions using the C interface.
#
proc LLVMCreateBuilder*(): TLLVMBuilderRef{.cdecl.}
proc LLVMPositionBuilderBefore*(Builder: TLLVMBuilderRef, Instr: TLLVMValueRef){.
cdecl.}
proc LLVMPositionBuilderAtEnd*(Builder: TLLVMBuilderRef,
theBlock: TLLVMBasicBlockRef){.cdecl.}
proc LLVMDisposeBuilder*(Builder: TLLVMBuilderRef){.cdecl.}
# Terminators
proc LLVMBuildRetVoid*(para1: TLLVMBuilderRef): TLLVMValueRef{.cdecl.}
proc LLVMBuildRet*(para1: TLLVMBuilderRef, V: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMBuildBr*(para1: TLLVMBuilderRef, Dest: TLLVMBasicBlockRef): TLLVMValueRef{.
cdecl.}
proc LLVMBuildCondBr*(para1: TLLVMBuilderRef, IfCond: TLLVMValueRef,
ThenBranch: TLLVMBasicBlockRef,
ElseBranch: TLLVMBasicBlockRef): TLLVMValueRef{.cdecl.}
proc LLVMBuildSwitch*(para1: TLLVMBuilderRef, V: TLLVMValueRef,
ElseBranch: TLLVMBasicBlockRef, NumCases: cuint): TLLVMValueRef{.
cdecl.}
# Const before type ignored
proc LLVMBuildInvoke*(para1: TLLVMBuilderRef, Fn: TLLVMValueRef,
Args: PLLVMValueRef, NumArgs: cuint,
ThenBranch: TLLVMBasicBlockRef, Catch: TLLVMBasicBlockRef,
Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildUnwind*(para1: TLLVMBuilderRef): TLLVMValueRef{.cdecl.}
proc LLVMBuildUnreachable*(para1: TLLVMBuilderRef): TLLVMValueRef{.cdecl.}
# Add a case to the switch instruction
proc LLVMAddCase*(Switch: TLLVMValueRef, OnVal: TLLVMValueRef,
Dest: TLLVMBasicBlockRef){.cdecl.}
# Arithmetic
proc LLVMBuildAdd*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildSub*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildMul*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildUDiv*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildSDiv*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildFDiv*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildURem*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildSRem*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildFRem*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildShl*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildLShr*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildAShr*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildAnd*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildOr*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildXor*(para1: TLLVMBuilderRef, LHS: TLLVMValueRef,
RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildNeg*(para1: TLLVMBuilderRef, V: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildNot*(para1: TLLVMBuilderRef, V: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl.}
# Memory
proc LLVMBuildMalloc*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildArrayMalloc*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef,
Val: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildAlloca*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildArrayAlloca*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef,
Val: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildFree*(para1: TLLVMBuilderRef, PointerVal: TLLVMValueRef): TLLVMValueRef{.
cdecl.}
proc LLVMBuildLoad*(para1: TLLVMBuilderRef, PointerVal: TLLVMValueRef,
Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildStore*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
thePtr: TLLVMValueRef): TLLVMValueRef{.cdecl.}
proc LLVMBuildGEP*(B: TLLVMBuilderRef, Pointer: TLLVMValueRef,
Indices: PLLVMValueRef, NumIndices: cuint, Name: cstring): TLLVMValueRef{.
cdecl.}
# Casts
proc LLVMBuildTrunc*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildZExt*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildSExt*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildFPToUI*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildFPToSI*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildUIToFP*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildSIToFP*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildFPTrunc*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildFPExt*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildPtrToInt*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildIntToPtr*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildBitCast*(para1: TLLVMBuilderRef, Val: TLLVMValueRef,
DestTy: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl.}
# Comparisons
proc LLVMBuildICmp*(para1: TLLVMBuilderRef, Op: TLLVMIntPredicate,
LHS: TLLVMValueRef, RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildFCmp*(para1: TLLVMBuilderRef, Op: TLLVMRealPredicate,
LHS: TLLVMValueRef, RHS: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl.}
# Miscellaneous instructions
proc LLVMBuildPhi*(para1: TLLVMBuilderRef, Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildCall*(para1: TLLVMBuilderRef, Fn: TLLVMValueRef,
Args: PLLVMValueRef, NumArgs: cuint, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildSelect*(para1: TLLVMBuilderRef, IfCond: TLLVMValueRef,
ThenBranch: TLLVMValueRef, ElseBranch: TLLVMValueRef,
Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildVAArg*(para1: TLLVMBuilderRef, List: TLLVMValueRef,
Ty: TLLVMTypeRef, Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildExtractElement*(para1: TLLVMBuilderRef, VecVal: TLLVMValueRef,
Index: TLLVMValueRef, Name: cstring): TLLVMValueRef{.
cdecl.}
proc LLVMBuildInsertElement*(para1: TLLVMBuilderRef, VecVal: TLLVMValueRef,
EltVal: TLLVMValueRef, Index: TLLVMValueRef,
Name: cstring): TLLVMValueRef{.cdecl.}
proc LLVMBuildShuffleVector*(para1: TLLVMBuilderRef, V1: TLLVMValueRef,
V2: TLLVMValueRef, Mask: TLLVMValueRef,
Name: cstring): TLLVMValueRef{.cdecl.}
#===-- Module providers --------------------------------------------------===
# Encapsulates the module M in a module provider, taking ownership of the
# module.
# See the constructor llvm: : ExistingModuleProvider: : ExistingModuleProvider.
#
proc LLVMCreateModuleProviderForExistingModule*(M: TLLVMModuleRef): TLLVMModuleProviderRef{.
cdecl.}
# Destroys the module provider MP as well as the contained module.
# See the destructor llvm: : ModuleProvider: : ~ModuleProvider.
#
proc LLVMDisposeModuleProvider*(MP: TLLVMModuleProviderRef){.cdecl.}
#===-- Memory buffers ----------------------------------------------------===
proc LLVMCreateMemoryBufferWithContentsOfFile*(Path: cstring,
OutMemBuf: pLLVMMemoryBufferRef, OutMessage: var cstring): int32{.cdecl.}
proc LLVMCreateMemoryBufferWithSTDIN*(OutMemBuf: pLLVMMemoryBufferRef,
OutMessage: var cstring): int32{.cdecl.}
proc LLVMDisposeMemoryBuffer*(MemBuf: TLLVMMemoryBufferRef){.cdecl.}
proc LLVMWriteBitcodeToFile*(M: TLLVMModuleRef, path: cstring): int{.cdecl.}
# Writes a module to the specified path. Returns 0 on success.
# implementation

240
rod/lookups.nim Executable file
View File

@@ -0,0 +1,240 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements lookup helpers.
import
ast, astalgo, idents, semdata, types, msgs, options, rodread, rnimsyn
type
TOverloadIterMode* = enum
oimDone, oimNoQualifier, oimSelfModule, oimOtherModule, oimSymChoice
TOverloadIter*{.final.} = object
stackPtr*: int
it*: TIdentIter
m*: PSym
mode*: TOverloadIterMode
proc getSymRepr*(s: PSym): string
proc CloseScope*(tab: var TSymTab)
proc AddSym*(t: var TStrTable, n: PSym)
proc addDecl*(c: PContext, sym: PSym)
proc addDeclAt*(c: PContext, sym: PSym, at: Natural)
proc addOverloadableSymAt*(c: PContext, fn: PSym, at: Natural)
proc addInterfaceDecl*(c: PContext, sym: PSym)
proc addInterfaceOverloadableSymAt*(c: PContext, sym: PSym, at: int)
proc lookUp*(c: PContext, n: PNode): PSym
# Looks up a symbol. Generates an error in case of nil.
proc QualifiedLookUp*(c: PContext, n: PNode, ambiguousCheck: bool): PSym
proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym
proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym
# implementation
proc getSymRepr(s: PSym): string =
case s.kind
of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
else: result = s.name.s
proc CloseScope(tab: var TSymTab) =
var
it: TTabIter
s: PSym
# check if all symbols have been used and defined:
if (tab.tos > len(tab.stack)): InternalError("CloseScope")
s = InitTabIter(it, tab.stack[tab.tos - 1])
while s != nil:
if sfForward in s.flags:
liMessage(s.info, errImplOfXexpected, getSymRepr(s))
elif ({sfUsed, sfInInterface} * s.flags == {}) and
(optHints in s.options): # BUGFIX: check options in s!
if not (s.kind in {skForVar, skParam, skMethod, skUnknown}):
liMessage(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
s = NextIter(it, tab.stack[tab.tos - 1])
astalgo.rawCloseScope(tab)
proc AddSym(t: var TStrTable, n: PSym) =
if StrTableIncl(t, n): liMessage(n.info, errAttemptToRedefine, n.name.s)
proc addDecl(c: PContext, sym: PSym) =
if SymTabAddUnique(c.tab, sym) == Failure:
liMessage(sym.info, errAttemptToRedefine, sym.Name.s)
proc addDeclAt(c: PContext, sym: PSym, at: Natural) =
if SymTabAddUniqueAt(c.tab, sym, at) == Failure:
liMessage(sym.info, errAttemptToRedefine, sym.Name.s)
proc addOverloadableSymAt(c: PContext, fn: PSym, at: Natural) =
var check: PSym
if not (fn.kind in OverloadableSyms):
InternalError(fn.info, "addOverloadableSymAt")
check = StrTableGet(c.tab.stack[at], fn.name)
if (check != nil) and not (check.Kind in OverloadableSyms):
liMessage(fn.info, errAttemptToRedefine, fn.Name.s)
SymTabAddAt(c.tab, fn, at)
proc AddInterfaceDeclAux(c: PContext, sym: PSym) =
if (sfInInterface in sym.flags):
# add to interface:
if c.module == nil: InternalError(sym.info, "AddInterfaceDeclAux")
StrTableAdd(c.module.tab, sym)
if getCurrOwner().kind == skModule: incl(sym.flags, sfGlobal)
proc addInterfaceDecl(c: PContext, sym: PSym) =
# it adds the symbol to the interface if appropriate
addDecl(c, sym)
AddInterfaceDeclAux(c, sym)
proc addInterfaceOverloadableSymAt(c: PContext, sym: PSym, at: int) =
# it adds the symbol to the interface if appropriate
addOverloadableSymAt(c, sym, at)
AddInterfaceDeclAux(c, sym)
proc lookUp(c: PContext, n: PNode): PSym =
# Looks up a symbol. Generates an error in case of nil.
case n.kind
of nkAccQuoted:
result = lookup(c, n.sons[0])
of nkSym:
#
# result := SymtabGet(c.Tab, n.sym.name);
# if result = nil then
# liMessage(n.info, errUndeclaredIdentifier, n.sym.name.s);
result = n.sym
of nkIdent:
result = SymtabGet(c.Tab, n.ident)
if result == nil: liMessage(n.info, errUndeclaredIdentifier, n.ident.s)
else: InternalError(n.info, "lookUp")
if IntSetContains(c.AmbiguousSymbols, result.id):
liMessage(n.info, errUseQualifier, result.name.s)
if result.kind == skStub: loadStub(result)
proc QualifiedLookUp(c: PContext, n: PNode, ambiguousCheck: bool): PSym =
var
m: PSym
ident: PIdent
case n.kind
of nkIdent:
result = SymtabGet(c.Tab, n.ident)
if result == nil:
liMessage(n.info, errUndeclaredIdentifier, n.ident.s)
elif ambiguousCheck and IntSetContains(c.AmbiguousSymbols, result.id):
liMessage(n.info, errUseQualifier, n.ident.s)
of nkSym:
#
# result := SymtabGet(c.Tab, n.sym.name);
# if result = nil then
# liMessage(n.info, errUndeclaredIdentifier, n.sym.name.s)
# else
result = n.sym
if ambiguousCheck and IntSetContains(c.AmbiguousSymbols, result.id):
liMessage(n.info, errUseQualifier, n.sym.name.s)
of nkDotExpr:
result = nil
m = qualifiedLookUp(c, n.sons[0], false)
if (m != nil) and (m.kind == skModule):
ident = nil
if (n.sons[1].kind == nkIdent):
ident = n.sons[1].ident
elif (n.sons[1].kind == nkAccQuoted) and
(n.sons[1].sons[0].kind == nkIdent):
ident = n.sons[1].sons[0].ident
if ident != nil:
if m == c.module:
result = StrTableGet(c.tab.stack[ModuleTablePos], ident)
else:
result = StrTableGet(m.tab, ident)
if result == nil:
liMessage(n.sons[1].info, errUndeclaredIdentifier, ident.s)
else:
liMessage(n.sons[1].info, errIdentifierExpected, renderTree(n.sons[1]))
of nkAccQuoted:
result = QualifiedLookup(c, n.sons[0], ambiguousCheck)
else:
result = nil #liMessage(n.info, errIdentifierExpected, '')
if (result != nil) and (result.kind == skStub): loadStub(result)
proc InitOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
var ident: PIdent
result = nil
case n.kind
of nkIdent:
o.stackPtr = c.tab.tos
o.mode = oimNoQualifier
while (result == nil):
dec(o.stackPtr)
if o.stackPtr < 0: break
result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.ident)
of nkSym:
result = n.sym
o.mode = oimDone #
# o.stackPtr := c.tab.tos;
# o.mode := oimNoQualifier;
# while (result = nil) do begin
# dec(o.stackPtr);
# if o.stackPtr < 0 then break;
# result := InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.sym.name);
# end;
of nkDotExpr:
o.mode = oimOtherModule
o.m = qualifiedLookUp(c, n.sons[0], false)
if (o.m != nil) and (o.m.kind == skModule):
ident = nil
if (n.sons[1].kind == nkIdent):
ident = n.sons[1].ident
elif (n.sons[1].kind == nkAccQuoted) and
(n.sons[1].sons[0].kind == nkIdent):
ident = n.sons[1].sons[0].ident
if ident != nil:
if o.m == c.module:
# a module may access its private members:
result = InitIdentIter(o.it, c.tab.stack[ModuleTablePos], ident)
o.mode = oimSelfModule
else:
result = InitIdentIter(o.it, o.m.tab, ident)
else:
liMessage(n.sons[1].info, errIdentifierExpected, renderTree(n.sons[1]))
of nkAccQuoted:
result = InitOverloadIter(o, c, n.sons[0])
of nkSymChoice:
o.mode = oimSymChoice
result = n.sons[0].sym
o.stackPtr = 1
else:
nil
if (result != nil) and (result.kind == skStub): loadStub(result)
proc nextOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case o.mode
of oimDone:
result = nil
of oimNoQualifier:
if n.kind == nkAccQuoted:
result = nextOverloadIter(o, c, n.sons[0]) # BUGFIX
elif o.stackPtr >= 0:
result = nextIdentIter(o.it, c.tab.stack[o.stackPtr])
while (result == nil):
dec(o.stackPtr)
if o.stackPtr < 0: break
result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], o.it.name) # BUGFIX:
# o.it.name <-> n.ident
else:
result = nil
of oimSelfModule:
result = nextIdentIter(o.it, c.tab.stack[ModuleTablePos])
of oimOtherModule:
result = nextIdentIter(o.it, o.m.tab)
of oimSymChoice:
if o.stackPtr < sonsLen(n):
result = n.sons[o.stackPtr].sym
inc(o.stackPtr)
else:
result = nil
if (result != nil) and (result.kind == skStub): loadStub(result)

88
rod/magicsys.nim Executable file
View File

@@ -0,0 +1,88 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Built-in types and compilerprocs are registered here.
import
ast, astalgo, nhashes, msgs, platform, nversion, times, idents, rodread
var SystemModule*: PSym
proc registerSysType*(t: PType)
# magic symbols in the system module:
proc getSysType*(kind: TTypeKind): PType
proc getCompilerProc*(name: string): PSym
proc registerCompilerProc*(s: PSym)
proc InitSystem*(tab: var TSymTab)
proc FinishSystem*(tab: TStrTable)
proc getSysSym*(name: string): PSym
# implementation
var
gSysTypes: array[TTypeKind, PType]
compilerprocs: TStrTable
proc registerSysType(t: PType) =
if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t
proc newSysType(kind: TTypeKind, size: int): PType =
result = newType(kind, systemModule)
result.size = size
result.align = size
proc getSysSym(name: string): PSym =
result = StrTableGet(systemModule.tab, getIdent(name))
if result == nil: rawMessage(errSystemNeeds, name)
if result.kind == skStub: loadStub(result)
proc sysTypeFromName(name: string): PType =
result = getSysSym(name).typ
proc getSysType(kind: TTypeKind): PType =
result = gSysTypes[kind]
if result == nil:
case kind
of tyInt: result = sysTypeFromName("int")
of tyInt8: result = sysTypeFromName("int8")
of tyInt16: result = sysTypeFromName("int16")
of tyInt32: result = sysTypeFromName("int32")
of tyInt64: result = sysTypeFromName("int64")
of tyFloat: result = sysTypeFromName("float")
of tyFloat32: result = sysTypeFromName("float32")
of tyFloat64: result = sysTypeFromName("float64")
of tyBool: result = sysTypeFromName("bool")
of tyChar: result = sysTypeFromName("char")
of tyString: result = sysTypeFromName("string")
of tyCstring: result = sysTypeFromName("cstring")
of tyPointer: result = sysTypeFromName("pointer")
of tyNil: result = newSysType(tyNil, ptrSize)
else: InternalError("request for typekind: " & $kind)
gSysTypes[kind] = result
if result.kind != kind:
InternalError("wanted: " & $kind & " got: " & $result.kind)
if result == nil: InternalError("type not found: " & $kind)
proc getCompilerProc(name: string): PSym =
var ident: PIdent
ident = getIdent(name, getNormalizedHash(name))
result = StrTableGet(compilerprocs, ident)
if result == nil:
result = StrTableGet(rodCompilerProcs, ident)
if result != nil:
strTableAdd(compilerprocs, result)
if result.kind == skStub: loadStub(result)
proc registerCompilerProc(s: PSym) =
strTableAdd(compilerprocs, s)
proc InitSystem(tab: var TSymTab) = nil
proc FinishSystem(tab: TStrTable) = nil
initStrTable(compilerprocs)

309
rod/main.nim Executable file
View File

@@ -0,0 +1,309 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# implements the command dispatcher and several commands as well as the
# module handling
import
llstream, strutils, ast, astalgo, scanner, syntaxes, rnimsyn, options, msgs,
os, lists, condsyms, paslex, pasparse, rodread, rodwrite, ropes, trees,
wordrecg, sem, semdata, idents, passes, docgen, extccomp, cgen, ecmasgen,
platform, interact, nimconf, importer, passaux, depends, transf, evals, types
proc MainCommand*(cmd, filename: string)
# implementation
# ------------------ module handling -----------------------------------------
type
TFileModuleRec{.final.} = object
filename*: string
module*: PSym
TFileModuleMap = seq[TFileModuleRec]
var compMods: TFileModuleMap = @ []
proc registerModule(filename: string, module: PSym) =
# all compiled modules
var length: int
length = len(compMods)
setlen(compMods, length + 1)
compMods[length].filename = filename
compMods[length].module = module
proc getModule(filename: string): PSym =
for i in countup(0, high(compMods)):
if sameFile(compMods[i].filename, filename):
return compMods[i].module
result = nil
proc newModule(filename: string): PSym =
# We cannot call ``newSym`` here, because we have to circumvent the ID
# mechanism, which we do in order to assign each module a persistent ID.
new(result)
result.id = - 1 # for better error checking
result.kind = skModule
result.name = getIdent(splitFile(filename).name)
result.owner = result # a module belongs to itself
result.info = newLineInfo(filename, 1, 1)
incl(result.flags, sfUsed)
initStrTable(result.tab)
RegisterModule(filename, result)
StrTableAdd(result.tab, result) # a module knows itself
proc CompileModule(filename: string, isMainFile, isSystemFile: bool): PSym
proc importModule(filename: string): PSym =
# this is called by the semantic checking phase
result = getModule(filename)
if result == nil:
# compile the module
result = compileModule(filename, false, false)
elif sfSystemModule in result.flags:
liMessage(result.info, errAttemptToRedefine, result.Name.s)
proc CompileModule(filename: string, isMainFile, isSystemFile: bool): PSym =
var
rd: PRodReader
f: string
rd = nil
f = addFileExt(filename, nimExt)
result = newModule(filename)
if isMainFile: incl(result.flags, sfMainModule)
if isSystemFile: incl(result.flags, sfSystemModule)
if (gCmd == cmdCompileToC) or (gCmd == cmdCompileToCpp):
rd = handleSymbolFile(result, f)
if result.id < 0:
InternalError("handleSymbolFile should have set the module\'s ID")
else:
result.id = getID()
processModule(result, f, nil, rd)
proc CompileProject(filename: string) =
discard CompileModule(JoinPath(options.libpath, addFileExt("system", nimExt)),
false, true)
discard CompileModule(addFileExt(filename, nimExt), true, false)
proc semanticPasses() =
registerPass(verbosePass())
registerPass(sem.semPass())
registerPass(transf.transfPass())
proc CommandGenDepend(filename: string) =
semanticPasses()
registerPass(genDependPass())
registerPass(cleanupPass())
compileProject(filename)
generateDot(filename)
execExternalProgram("dot -Tpng -o" & changeFileExt(filename, "png") & ' ' &
changeFileExt(filename, "dot"))
proc CommandCheck(filename: string) =
semanticPasses() # use an empty backend for semantic checking only
compileProject(filename)
proc CommandCompileToC(filename: string) =
semanticPasses()
registerPass(cgen.cgenPass())
registerPass(rodwrite.rodwritePass()) #registerPass(cleanupPass());
compileProject(filename) #for i := low(TTypeKind) to high(TTypeKind) do
# MessageOut('kind: ' +{&} typeKindToStr[i] +{&} ' = ' +{&} toString(sameTypeA[i]));
extccomp.CallCCompiler(changeFileExt(filename, ""))
proc CommandCompileToEcmaScript(filename: string) =
incl(gGlobalOptions, optSafeCode)
setTarget(osEcmaScript, cpuEcmaScript)
initDefines()
semanticPasses()
registerPass(ecmasgenPass())
compileProject(filename)
proc CommandInteractive() =
var m: PSym
incl(gGlobalOptions, optSafeCode)
setTarget(osNimrodVM, cpuNimrodVM)
initDefines()
registerPass(verbosePass())
registerPass(sem.semPass())
registerPass(transf.transfPass())
registerPass(evals.evalPass()) # load system module:
discard CompileModule(JoinPath(options.libpath, addFileExt("system", nimExt)),
false, true)
m = newModule("stdin")
m.id = getID()
incl(m.flags, sfMainModule)
processModule(m, "stdin", LLStreamOpenStdIn(), nil)
proc exSymbols(n: PNode) =
case n.kind
of nkEmpty..nkNilLit:
nil
of nkProcDef..nkIteratorDef:
exSymbol(n.sons[namePos])
of nkWhenStmt, nkStmtList:
for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i])
of nkVarSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1): exSymbol(n.sons[i].sons[0])
of nkTypeSection:
for i in countup(0, sonsLen(n) - 1):
exSymbol(n.sons[i].sons[0])
if (n.sons[i].sons[2] != nil) and
(n.sons[i].sons[2].kind == nkObjectTy):
fixRecordDef(n.sons[i].sons[2])
else:
nil
proc CommandExportSymbols(filename: string) =
# now unused!
var module: PNode
module = parseFile(addFileExt(filename, NimExt))
if module != nil:
exSymbols(module)
renderModule(module, getOutFile(filename, "pretty." & NimExt))
proc CommandPretty(filename: string) =
var module: PNode
module = parseFile(addFileExt(filename, NimExt))
if module != nil:
renderModule(module, getOutFile(filename, "pretty." & NimExt))
proc CommandLexPas(filename: string) =
var
L: TPasLex
tok: TPasTok
f: string
stream: PLLStream
f = addFileExt(filename, "pas")
stream = LLStreamOpen(f, fmRead)
if stream != nil:
OpenLexer(L, f, stream)
getPasTok(L, tok)
while tok.xkind != pxEof:
printPasTok(tok)
getPasTok(L, tok)
else:
rawMessage(errCannotOpenFile, f)
closeLexer(L)
proc CommandPas(filename: string) =
var
p: TPasParser
module: PNode
f: string
stream: PLLStream
f = addFileExt(filename, "pas")
stream = LLStreamOpen(f, fmRead)
if stream != nil:
OpenPasParser(p, f, stream)
module = parseUnit(p)
closePasParser(p)
renderModule(module, getOutFile(filename, NimExt))
else:
rawMessage(errCannotOpenFile, f)
proc CommandScan(filename: string) =
var
L: TLexer
tok: PToken
f: string
stream: PLLStream
new(tok)
f = addFileExt(filename, nimExt)
stream = LLStreamOpen(f, fmRead)
if stream != nil:
openLexer(L, f, stream)
while true:
rawGetTok(L, tok^ )
PrintTok(tok)
if tok.tokType == tkEof: break
CloseLexer(L)
else:
rawMessage(errCannotOpenFile, f)
proc WantFile(filename: string) =
if filename == "":
liMessage(newLineInfo("command line", 1, 1), errCommandExpectsFilename)
proc MainCommand(cmd, filename: string) =
appendStr(searchPaths, options.libpath)
if filename != "":
# current path is always looked first for modules
prependStr(searchPaths, splitFile(filename).dir)
setID(100)
passes.gIncludeFile = syntaxes.parseFile
passes.gImportModule = importModule
case whichKeyword(cmd)
of wCompile, wCompileToC, wC, wCC:
# compile means compileToC currently
gCmd = cmdCompileToC
wantFile(filename)
CommandCompileToC(filename)
of wCompileToCpp:
gCmd = cmdCompileToCpp
wantFile(filename)
CommandCompileToC(filename)
of wCompileToEcmaScript:
gCmd = cmdCompileToEcmaScript
wantFile(filename)
CommandCompileToEcmaScript(filename)
of wCompileToLLVM:
gCmd = cmdCompileToLLVM
wantFile(filename)
CommandCompileToC(filename)
of wPretty:
gCmd = cmdPretty
wantFile(filename) #CommandExportSymbols(filename);
CommandPretty(filename)
of wDoc:
gCmd = cmdDoc
LoadSpecialConfig(DocConfig)
wantFile(filename)
CommandDoc(filename)
of wRst2html:
gCmd = cmdRst2html
LoadSpecialConfig(DocConfig)
wantFile(filename)
CommandRst2Html(filename)
of wRst2tex:
gCmd = cmdRst2tex
LoadSpecialConfig(DocTexConfig)
wantFile(filename)
CommandRst2TeX(filename)
of wPas:
gCmd = cmdPas
wantFile(filename)
CommandPas(filename)
of wBoot:
gCmd = cmdBoot
wantFile(filename)
CommandPas(filename)
of wGenDepend:
gCmd = cmdGenDepend
wantFile(filename)
CommandGenDepend(filename)
of wListDef:
gCmd = cmdListDef
condsyms.ListSymbols()
of wCheck:
gCmd = cmdCheck
wantFile(filename)
CommandCheck(filename)
of wParse:
gCmd = cmdParse
wantFile(filename)
discard parseFile(addFileExt(filename, nimExt))
of wScan:
gCmd = cmdScan
wantFile(filename)
CommandScan(filename)
MessageOut("Beware: Indentation tokens depend on the parser\'s state!")
of wI:
gCmd = cmdInteractive
CommandInteractive()
else: rawMessage(errInvalidCommandX, cmd)

509
rod/msgs.nim Executable file
View File

@@ -0,0 +1,509 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import #[[[cog
#from string import replace
#enum = "type\n TMsgKind = (\n"
#msgs = "const\n MsgKindToStr: array [TMsgKind] of string = (\n"
#warns = "const\n WarningsToStr: array [0..%d] of string = (\n"
#hints = "const\n HintsToStr: array [0..%d] of string = (\n"
#w = 0 # counts the warnings
#h = 0 # counts the hints
#
#for elem in eval(open('data/messages.yml').read()):
# for key, val in elem.items():
# enum = enum + ' %s,\n' % key
# v = replace(val, "'", "''")
# if key[0:4] == 'warn':
# msgs = msgs + " '%s [%s]',\n" % (v, key[4:])
# warns = warns + " '%s',\n" % key[4:]
# w = w + 1
# elif key[0:4] == 'hint':
# msgs = msgs + " '%s [%s]',\n" % (v, key[4:])
# hints = hints + " '%s',\n" % key[4:]
# h = h + 1
# else:
# msgs = msgs + " '%s',\n" % v
#
#enum = enum[:-2] + ');\n\n'
#msgs = msgs[:-2] + '\n );\n'
#warns = (warns[:-2] + '\n );\n') % (w-1)
#hints = (hints[:-2] + '\n );\n') % (h-1)
#
#cog.out(enum)
#cog.out(msgs)
#cog.out(warns)
#cog.out(hints)
#]]]
options, strutils, os
type
TMsgKind* = enum
errUnknown, errIllFormedAstX, errCannotOpenFile, errInternal, errGenerated,
errXCompilerDoesNotSupportCpp, errStringLiteralExpected,
errIntLiteralExpected, errInvalidCharacterConstant,
errClosingTripleQuoteExpected, errClosingQuoteExpected,
errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter,
errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected,
errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
errExceptionExpected, errExceptionAlreadyHandled, errYieldNotAllowedHere,
errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine,
errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound,
errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected,
errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,
errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,
errConstantDivisionByZero, errOrdinalTypeExpected,
errOrdinalOrFloatTypeExpected, errOverOrUnderflow,
errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255,
errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess,
errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,
errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,
errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,
errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected,
errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,
errAttemptToRedefineX, errMagicOnlyInSystem, errPowerOfTwoExpected,
errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,
errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected,
errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes,
errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid,
errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop,
errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue,
errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig,
errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects,
errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX,
errCannotInstantiateX, errExprHasNoAddress, errVarForOutParamNeeded,
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
errAmbiguousCallXYZ, errWrongNumberOfArguments, errXCannotBePassedToProcVar,
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed,
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValue,
errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
errInvalidOrderInEnumX, errEnumXHasWholes, errExceptExpected, errInvalidTry,
errOptionExpected, errXisNoLabel, errNotAllCasesCovered,
errUnkownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,
errNoPragmasAllowedForX, errNoGenericParamsAllowedForX,
errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent,
errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX,
errXNotAllowedHere, errInvalidControlFlowX, errATypeHasNoValue,
errXisNoType, errCircumNeedsPointer, errInvalidExpression,
errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected,
errNamedExprNotAllowed, errXExpectsOneTypeParam,
errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
errInvalidCommandX, errXOnlyAtModuleScope,
errTemplateInstantiationTooNested, errInstantiationFrom,
errInvalidIndexValueForTuple, errCommandExpectsFilename, errXExpected,
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errIsExpectsTwoArguments,
errIsExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
errXhasSideEffects, errIteratorExpected, errUser, warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
warnCannotWriteMO2, warnCannotReadMO2, warnDeprecated,
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
warnXisPassedToProcVar, warnUser, hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintUser
const
MsgKindToStr*: array[TMsgKind, string] = ["unknown error",
"illformed AST: $1", "cannot open \'$1\'", "internal error: $1", "$1",
"\'$1\' compiler does not support C++", "string literal expected",
"integer literal expected", "invalid character constant",
"closing \"\"\" expected, but end of file reached", "closing \" expected",
"tabulators are not allowed", "invalid token: $1", "line too long",
"$1 is not a valid number", "number $1 out of valid range",
"\\n not allowed in character literal",
"closing \']\' expected, but end of file reached", "missing final \'",
"identifier expected, but found \'$1\'",
"operator expected, but found \'$1\'", "\'$1\' expected",
"string after \'include\' expected", "recursive dependency: \'$1\'",
"\'on\' or \'off\' expected", "\'none\', \'speed\' or \'size\' expected",
"invalid pragma", "unknown pragma: \'$1\'", "invalid directive: \'$1\'",
"\'pop\' without a \'push\' pragma", "empty asm statement",
"invalid indentation", "exception expected", "exception already handled",
"\'yield\' only allowed in a loop of an iterator",
"invalid number of \'yield\' expresions",
"current routine cannot return an expression", "attempt to redefine \'$1\'",
"statement not allowed after \'return\', \'break\' or \'raise\'",
"statement expected", "\'$1\' is no label",
"invalid command line option: \'$1\'",
"argument for command line option expected: \'$1\'",
"invalid argument for command line option: \'$1\'",
"invalid variable substitution in \'$1\'", "unknown variable: \'$1\'",
"unknown C compiler: \'$1\'",
"\'on\' or \'off\' expected, but \'$1\' found",
"\'none\', \'boehm\' or \'refc\' expected, but \'$1\' found",
"\'none\', \'speed\' or \'size\' expected, but \'$1\' found",
"\'gui\', \'console\' or \'lib\' expected, but \'$1\' found",
"unknown OS: \'$1\'", "unknown CPU: \'$1\'",
"\'c\', \'c++\' or \'yaml\' expected, but \'$1\' found",
"arguments can only be given if the \'--run\' option is selected",
"multiple assignment is not allowed",
"\':\' or \'=\' expected, but found \'$1\'",
"expression expected, but found \'$1\'", "undeclared identifier: \'$1\'",
"ambiguous identifier: \'$1\' -- use a qualifier", "type expected",
"system module needs \'$1\'", "execution of an external program failed",
"overloaded \'$1\' leads to ambiguous calls", "invalid argument for \'$1\'",
"statement has no effect", "\'$1\' expects a type or value",
"\'$1\' expects an array type",
"\'$1\' cannot be instantiated because its body has not been compiled yet",
"expression \'$1\' ambiguous in this context", "constant division by zero",
"ordinal type expected", "ordinal or float type expected",
"over- or underflow",
"cannot evalutate \'$1\' because type is not defined completely",
"\'chr\' expects an int in the range 0..255",
"\'dynlib\' requires \'exportc\'", "undeclared field: \'$1\'",
"attempt to access a nil address", "index out of bounds",
"index types do not match", "\'[]\' operator invalid for this type",
"value out of set bounds", "field initialized twice: \'$1\'",
"field \'$1\' not initialized", "expression \'$1\' cannot be called",
"expression has no type", "expression \'$1\' has no type (or is ambiguous)",
"\'cast\' not allowed in safe mode", "expression cannot be casted to $1",
"\',\' or \')\' expected", "\'{\' or \'(\' expected",
"section (\'type\', \'proc\', etc.) expected", "range expected",
"attempt to redefine \'$1\'", "\'magic\' only allowed in system module",
"power of two expected", "string literal may not be empty",
"calling convention expected",
"a proc can only have one calling convention",
"symbol must be imported if \'lib\' pragma is used",
"expression must be of type \'bool\'", "constant expression expected",
"duplicate case label", "range is empty",
"selector must be of an ordinal type, real or string",
"selector must be of an ordinal type", "ord($1) must not be negative",
"len($1) must be less than 32768", "wrong number of variables",
"only objects can be raised", "\'break\' only allowed in loop construct",
"type \'$1\' has unknown size",
"a constant can only be initialized with a constant expression",
"a constant needs a value", "the result type cannot be on open array",
"computing the type\'s size produced an overflow", "set is too large",
"base type of a set must be an ordinal",
"inheritance only works with non-final objects",
"inheritance only works with an enum", "illegal recursion in type \'$1\'",
"cannot instantiate: \'$1\'", "expression has no address",
"for a \'var\' type a variable needs to be passed", "type mismatch",
"type mismatch: got (", "but expected one of: ", "but expected \'$1\'",
"ambiguous call; both $1 and $2 match for: $3", "wrong number of arguments",
"\'$1\' cannot be passed to a procvar",
"$1 cannot be declared in parameter declaration",
"pragmas are only in the header of a proc allowed",
"implementation of \'$1\' is not allowed",
"implementation of \'$1\' expected", "no symbol to borrow from found",
"value returned by statement has to be discarded",
"statement returns no value that can be discarded",
"conversion from $1 to $2 is invalid", "cannot bind parameter \'$1\' twice",
"invalid order in enum \'$1\'", "enum \'$1\' has wholes",
"\'except\' or \'finally\' expected",
"after catch all \'except\' or \'finally\' no section may follow",
"option expected, but found \'$1\'", "\'$1\' is not a label",
"not all cases are covered", "unknown substitution variable: \'$1\'",
"complex statement requires indentation", "\'$1\' is not callable",
"no pragmas allowed for $1", "no generic parameters allowed for $1",
"invalid param kind: \'$1\'", "default argument invalid",
"named parameter has to be an identifier", "no return type for $1 allowed",
"a type conversion needs exactly one argument", "invalid pragma: $1",
"$1 not allowed here", "invalid control flow: $1", "a type has no value",
"invalid type: \'$1\'", "\'^\' needs a pointer or reference type",
"invalid expression", "invalid expression: \'$1\'",
"enum has no value \'$1\'", "named expression expected",
"named expression not allowed here", "\'$1\' expects one type parameter",
"array expects two type parameters", "invalid visibility: \'$1\'",
"initialization not allowed here", "\'$1\' cannot be assigned to",
"iterators can only be defined at the module\'s top level",
"$1 needs a return type", "invalid command: \'$1\'",
"\'$1\' is only allowed at top level",
"template/macro instantiation too nested", "instantiation from here",
"invalid index value for tuple subscript",
"command expects a filename argument", "\'$1\' expected",
"invalid section start", "grid table is not implemented",
"general parse error", "new section expected",
"whitespace expected, got \'$1\'", "\'$1\' is no valid index file",
"cannot render reStructuredText element \'$1\'",
"type \'var var\' is not allowed", "\'is\' expects two arguments",
"\'is\' expects object types", "\'$1\' can never be of this subtype",
"interpretation requires too many iterations",
"cannot interpret node kind \'$1\'", "field \'$1\' cannot be found",
"invalid conversion from type \'$1\'", "assertion failed",
"cannot generate code for \'$1\'", "$1 requires one parameter",
"unhandled exception: $1", "macro returned a cyclic abstract syntax tree",
"\'$1\' is no macro or template", "\'$1\' can have side effects",
"iterator within for loop context expected", "$1",
"cannot open \'$1\' [CannotOpenFile]", "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
"\'$1\' is never read [XIsNeverRead]",
"\'$1\' might not have been initialized [XmightNotBeenInit]",
"cannot write file \'$1\' [CannotWriteMO2]",
"cannot read file \'$1\' [CannotReadMO2]",
"\'$1\' is deprecated [Deprecated]", "\'l\' should not be used as an identifier; may look like \'1\' (one) [SmallLshouldNotBeUsed]",
"unknown magic \'$1\' might crash the compiler [UnknownMagic]",
"redefinition of label \'$1\' [RedefinitionOfLabel]",
"unknown substitution \'$1\' [UnknownSubstitutionX]",
"language \'$1\' not supported [LanguageXNotSupported]",
"comment \'$1\' ignored [CommentXIgnored]",
"\'$1\' is passed to a procvar; deprecated [XisPassedToProcVar]",
"$1 [User]", "operation successful [Success]",
"operation successful ($1 lines compiled; $2 sec total) [SuccessX]",
"line too long [LineTooLong]",
"\'$1\' is declared but not used [XDeclaredButNotUsed]",
"conversion to base object is not needed [ConvToBaseNotNeeded]",
"conversion from $1 to itself is pointless [ConvFromXtoItselfNotNeeded]",
"expression evaluates always to \'$1\' [ExprAlwaysX]",
"quit() called [QuitCalled]", "$1 [Processing]",
"generated code listing: [CodeBegin]", "end of listing [CodeEnd]",
"used config file \'$1\' [Conf]", "$1 [User]"]
const
WarningsToStr*: array[0..14, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit", "CannotWriteMO2", "CannotReadMO2",
"Deprecated", "SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
"CommentXIgnored", "XisPassedToProcVar", "User"]
const
HintsToStr*: array[0..12, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"User"] #[[[end]]]
const
fatalMin* = errUnknown
fatalMax* = errInternal
errMin* = errUnknown
errMax* = errUser
warnMin* = warnCannotOpenFile
warnMax* = pred(hintSuccess)
hintMin* = hintSuccess
hintMax* = high(TMsgKind)
type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind]
TLineInfo*{.final.} = object # This is designed to be as small as possible, because it is used
# in syntax nodes. We safe space here by using two int16 and an int32
# on 64 bit and on 32 bit systems this is only 8 bytes.
line*, col*: int16
fileIndex*: int32
proc UnknownLineInfo*(): TLineInfo
var
gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)}
gErrorCounter*: int = 0 # counts the number of errors
gHintCounter*: int = 0
gWarnCounter*: int = 0
gErrorMax*: int = 1 # stop after gErrorMax errors
const # this format is understood by many text editors: it is the same that
# Borland and Freepascal use
PosErrorFormat* = "$1($2, $3) Error: $4"
PosWarningFormat* = "$1($2, $3) Warning: $4"
PosHintFormat* = "$1($2, $3) Hint: $4"
RawErrorFormat* = "Error: $1"
RawWarningFormat* = "Warning: $1"
RawHintFormat* = "Hint: $1"
proc MessageOut*(s: string)
proc rawMessage*(msg: TMsgKind, arg: string = "")
proc rawMessage*(msg: TMsgKind, args: openarray[string])
proc liMessage*(info: TLineInfo, msg: TMsgKind, arg: string = "")
proc InternalError*(info: TLineInfo, errMsg: string)
proc InternalError*(errMsg: string)
proc newLineInfo*(filename: string, line, col: int): TLineInfo
proc ToFilename*(info: TLineInfo): string
proc toColumn*(info: TLineInfo): int
proc ToLinenumber*(info: TLineInfo): int
proc MsgKindToString*(kind: TMsgKind): string
# checkpoints are used for debugging:
proc checkpoint*(info: TLineInfo, filename: string, line: int): bool
proc addCheckpoint*(info: TLineInfo)
proc addCheckpoint*(filename: string, line: int)
proc inCheckpoint*(current: TLineInfo): bool
# prints the line information if in checkpoint
proc pushInfoContext*(info: TLineInfo)
proc popInfoContext*()
proc includeFilename*(f: string): int
# implementation
proc UnknownLineInfo(): TLineInfo =
result.line = int16(- 1)
result.col = int16(- 1)
result.fileIndex = - 1
var
filenames: seq[string] = @ []
msgContext: seq[TLineInfo] = @ []
proc pushInfoContext(info: TLineInfo) =
var length: int
length = len(msgContext)
setlen(msgContext, length + 1)
msgContext[length] = info
proc popInfoContext() =
setlen(msgContext, len(msgContext) - 1)
proc includeFilename(f: string): int =
for i in countdown(high(filenames), low(filenames)):
if filenames[i] == f:
return i
result = len(filenames)
setlen(filenames, result + 1)
filenames[result] = f
proc checkpoint(info: TLineInfo, filename: string, line: int): bool =
result = (int(info.line) == line) and
(ChangeFileExt(extractFilename(filenames[info.fileIndex]), "") ==
filename)
var checkPoints: seq[TLineInfo] = @ []
proc addCheckpoint(info: TLineInfo) =
var length: int
length = len(checkPoints)
setlen(checkPoints, length + 1)
checkPoints[length] = info
proc addCheckpoint(filename: string, line: int) =
addCheckpoint(newLineInfo(filename, line, - 1))
proc newLineInfo(filename: string, line, col: int): TLineInfo =
result.fileIndex = includeFilename(filename)
result.line = int16(line)
result.col = int16(col)
proc ToFilename(info: TLineInfo): string =
if info.fileIndex == - 1: result = "???"
else: result = filenames[info.fileIndex]
proc ToLinenumber(info: TLineInfo): int =
result = info.line
proc toColumn(info: TLineInfo): int =
result = info.col
proc MessageOut(s: string) =
# change only this proc to put it elsewhere
Writeln(stdout, s)
proc coordToStr(coord: int): string =
if coord == - 1: result = "???"
else: result = $(coord)
proc MsgKindToString(kind: TMsgKind): string =
# later versions may provide translated error messages
result = msgKindToStr[kind]
proc getMessageStr(msg: TMsgKind, arg: string): string =
result = `%`(msgKindToString(msg), [arg])
proc inCheckpoint(current: TLineInfo): bool =
result = false
if not (optCheckpoints in gOptions):
return # ignore all checkpoints
for i in countup(0, high(checkPoints)):
if (current.line == checkPoints[i].line) and
(current.fileIndex == (checkPoints[i].fileIndex)):
MessageOut(`%`("$1($2, $3) Checkpoint: ", [toFilename(current),
coordToStr(current.line), coordToStr(current.col)]))
return true
proc handleError(msg: TMsgKind) =
if msg == errInternal:
assert(false) # we want a stack trace here
if (msg >= fatalMin) and (msg <= fatalMax):
if gVerbosity >= 3: assert(false)
quit(1)
if (msg >= errMin) and (msg <= errMax):
inc(gErrorCounter)
if gErrorCounter >= gErrorMax:
if gVerbosity >= 3: assert(false)
quit(1) # one error stops the compiler
proc sameLineInfo(a, b: TLineInfo): bool =
result = (a.line == b.line) and (a.fileIndex == b.fileIndex)
proc writeContext(lastinfo: TLineInfo) =
var info: TLineInfo
info = lastInfo
for i in countup(0, len(msgContext) - 1):
if not sameLineInfo(msgContext[i], lastInfo) and
not sameLineInfo(msgContext[i], info):
MessageOut(`%`(posErrorFormat, [toFilename(msgContext[i]),
coordToStr(msgContext[i].line),
coordToStr(msgContext[i].col),
getMessageStr(errInstantiationFrom, "")]))
info = msgContext[i]
proc rawMessage(msg: TMsgKind, args: openarray[string]) =
var frmt: string
case msg
of errMin..errMax:
writeContext(unknownLineInfo())
frmt = rawErrorFormat
of warnMin..warnMax:
if not (optWarns in gOptions): return
if not (msg in gNotes): return
frmt = rawWarningFormat
inc(gWarnCounter)
of hintMin..hintMax:
if not (optHints in gOptions): return
if not (msg in gNotes): return
frmt = rawHintFormat
inc(gHintCounter)
else:
assert(false) # cannot happen
MessageOut(`%`(frmt, `%`(msgKindToString(msg), args)))
handleError(msg)
proc rawMessage(msg: TMsgKind, arg: string = "") =
rawMessage(msg, [arg])
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string = "") =
var frmt: string
case msg
of errMin..errMax:
writeContext(info)
frmt = posErrorFormat
of warnMin..warnMax:
if not (optWarns in gOptions): return
if not (msg in gNotes): return
frmt = posWarningFormat
inc(gWarnCounter)
of hintMin..hintMax:
if not (optHints in gOptions): return
if not (msg in gNotes): return
frmt = posHintFormat
inc(gHintCounter)
else:
assert(false) # cannot happen
MessageOut(`%`(frmt, [toFilename(info), coordToStr(info.line),
coordToStr(info.col), getMessageStr(msg, arg)]))
handleError(msg)
proc InternalError(info: TLineInfo, errMsg: string) =
writeContext(info)
liMessage(info, errInternal, errMsg)
proc InternalError(errMsg: string) =
writeContext(UnknownLineInfo())
rawMessage(errInternal, errMsg)

161
rod/nhashes.nim Executable file
View File

@@ -0,0 +1,161 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import
strutils
const
SmallestSize* = (1 shl 3) - 1
DefaultSize* = (1 shl 11) - 1
BiggestSize* = (1 shl 28) - 1
type
THash* = int
PHash* = ref THash
THashFunc* = proc (str: cstring): THash
proc GetHash*(str: cstring): THash
proc GetHashCI*(str: cstring): THash
proc GetDataHash*(Data: Pointer, Size: int): THash
proc hashPtr*(p: Pointer): THash
proc GetHashStr*(s: string): THash
proc GetHashStrCI*(s: string): THash
proc getNormalizedHash*(s: string): THash
#function nextPowerOfTwo(x: int): int;
proc concHash*(h: THash, val: int): THash
proc finishHash*(h: THash): THash
# implementation
proc nextPowerOfTwo(x: int): int =
result = x -% 1 # complicated, to make it a nop if sizeof(int) == 4,
# because shifting more than 31 bits is undefined in C
result = result or (result shr ((sizeof(int) - 4) * 8))
result = result or (result shr 16)
result = result or (result shr 8)
result = result or (result shr 4)
result = result or (result shr 2)
result = result or (result shr 1)
Inc(result)
proc concHash(h: THash, val: int): THash =
result = h +% val
result = result +% result shl 10
result = result xor (result shr 6)
proc finishHash(h: THash): THash =
result = h +% h shl 3
result = result xor (result shr 11)
result = result +% result shl 15
proc GetDataHash(Data: Pointer, Size: int): THash =
var
h: THash
p: cstring
i, s: int
h = 0
p = cast[cstring](Data)
i = 0
s = size
while s > 0:
h = h +% ord(p[i])
h = h +% h shl 10
h = h xor (h shr 6)
Inc(i)
Dec(s)
h = h +% h shl 3
h = h xor (h shr 11)
h = h +% h shl 15
result = THash(h)
proc hashPtr(p: Pointer): THash =
result = (cast[THash](p)) shr 3 # skip the alignment
proc GetHash(str: cstring): THash =
var
h: THash
i: int
h = 0
i = 0
while str[i] != '\0':
h = h +% ord(str[i])
h = h +% h shl 10
h = h xor (h shr 6)
Inc(i)
h = h +% h shl 3
h = h xor (h shr 11)
h = h +% h shl 15
result = THash(h)
proc GetHashStr(s: string): THash =
var h: THash
h = 0
for i in countup(1, len(s)):
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 = THash(h)
proc getNormalizedHash(s: string): THash =
var
h: THash
c: Char
h = 0
for i in countup(0, len(s) + 0 - 1):
c = s[i]
if c == '_':
continue # skip _
if c in {'A'..'Z'}:
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h +% ord(c)
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 = THash(h)
proc GetHashStrCI(s: string): THash =
var
h: THash
c: Char
h = 0
for i in countup(0, len(s) + 0 - 1):
c = s[i]
if c in {'A'..'Z'}:
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h +% ord(c)
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 = THash(h)
proc GetHashCI(str: cstring): THash =
var
h: THash
c: Char
i: int
h = 0
i = 0
while str[i] != '\0':
c = str[i]
if c in {'A'..'Z'}:
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h +% ord(c)
h = h +% h shl 10
h = h xor (h shr 6)
Inc(i)
h = h +% h shl 3
h = h xor (h shr 11)
h = h +% h shl 15
result = THash(h)

257
rod/nimconf.nim Executable file
View File

@@ -0,0 +1,257 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module handles the reading of the config file.
import
llstream, nversion, commands, os, strutils, msgs, platform, condsyms, scanner,
options, idents, wordrecg
proc LoadConfig*(project: string)
proc LoadSpecialConfig*(configfilename: string)
# implementation
# ---------------- configuration file parser -----------------------------
# we use Nimrod's scanner here to safe space and work
proc ppGetTok(L: var TLexer, tok: PToken) =
# simple filter
rawGetTok(L, tok^ )
while (tok.tokType == tkInd) or (tok.tokType == tkSad) or
(tok.tokType == tkDed) or (tok.tokType == tkComment):
rawGetTok(L, tok^ )
proc parseExpr(L: var TLexer, tok: PToken): bool
proc parseAtom(L: var TLexer, tok: PToken): bool =
if tok.tokType == tkParLe:
ppGetTok(L, tok)
result = parseExpr(L, tok)
if tok.tokType == tkParRi: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\')\'")
elif tok.ident.id == ord(wNot):
ppGetTok(L, tok)
result = not parseAtom(L, tok)
else:
result = isDefined(tok.ident) #condsyms.listSymbols();
#writeln(tok.ident.s + ' has the value: ', result);
ppGetTok(L, tok)
proc parseAndExpr(L: var TLexer, tok: PToken): bool =
var b: bool
result = parseAtom(L, tok)
while tok.ident.id == ord(wAnd):
ppGetTok(L, tok) # skip "and"
b = parseAtom(L, tok)
result = result and b
proc parseExpr(L: var TLexer, tok: PToken): bool =
var b: bool
result = parseAndExpr(L, tok)
while tok.ident.id == ord(wOr):
ppGetTok(L, tok) # skip "or"
b = parseAndExpr(L, tok)
result = result or b
proc EvalppIf(L: var TLexer, tok: PToken): bool =
ppGetTok(L, tok) # skip 'if' or 'elif'
result = parseExpr(L, tok)
if tok.tokType == tkColon: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\':\'")
var condStack: seq[bool]
condStack = @ []
proc doEnd(L: var TLexer, tok: PToken) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
ppGetTok(L, tok) # skip 'end'
setlen(condStack, high(condStack))
type
TJumpDest = enum
jdEndif, jdElseEndif
proc jumpToDirective(L: var TLexer, tok: PToken, dest: TJumpDest)
proc doElse(L: var TLexer, tok: PToken) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
ppGetTok(L, tok)
if tok.tokType == tkColon: ppGetTok(L, tok)
if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif)
proc doElif(L: var TLexer, tok: PToken) =
var res: bool
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
res = EvalppIf(L, tok)
if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif)
else: condStack[high(condStack)] = true
proc jumpToDirective(L: var TLexer, tok: PToken, dest: TJumpDest) =
var nestedIfs: int
nestedIfs = 0
while True:
if (tok.ident != nil) and (tok.ident.s == "@"):
ppGetTok(L, tok)
case whichKeyword(tok.ident)
of wIf:
Inc(nestedIfs)
of wElse:
if (dest == jdElseEndif) and (nestedIfs == 0):
doElse(L, tok)
break
of wElif:
if (dest == jdElseEndif) and (nestedIfs == 0):
doElif(L, tok)
break
of wEnd:
if nestedIfs == 0:
doEnd(L, tok)
break
if nestedIfs > 0: Dec(nestedIfs)
else:
nil
ppGetTok(L, tok)
elif tok.tokType == tkEof:
lexMessage(L, errTokenExpected, "@end")
else:
ppGetTok(L, tok)
proc parseDirective(L: var TLexer, tok: PToken) =
var
res: bool
key: string
ppGetTok(L, tok) # skip @
case whichKeyword(tok.ident)
of wIf:
setlen(condStack, len(condStack) + 1)
res = EvalppIf(L, tok)
condStack[high(condStack)] = res
if not res:
jumpToDirective(L, tok, jdElseEndif)
of wElif:
doElif(L, tok)
of wElse:
doElse(L, tok)
of wEnd:
doEnd(L, tok)
of wWrite:
ppGetTok(L, tok)
msgs.MessageOut(tokToStr(tok))
ppGetTok(L, tok)
of wPutEnv:
ppGetTok(L, tok)
key = tokToStr(tok)
ppGetTok(L, tok)
os.putEnv(key, tokToStr(tok))
ppGetTok(L, tok)
of wPrependEnv:
ppGetTok(L, tok)
key = tokToStr(tok)
ppGetTok(L, tok)
os.putEnv(key, tokToStr(tok) & os.getenv(key))
ppGetTok(L, tok)
of wAppendenv:
ppGetTok(L, tok)
key = tokToStr(tok)
ppGetTok(L, tok)
os.putEnv(key, os.getenv(key) & tokToStr(tok))
ppGetTok(L, tok)
else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
proc confTok(L: var TLexer, tok: PToken) =
ppGetTok(L, tok)
while (tok.ident != nil) and (tok.ident.s == "@"):
parseDirective(L, tok) # else: give the token to the parser
proc checkSymbol(L: TLexer, tok: PToken) =
if not (tok.tokType in {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}):
lexMessage(L, errIdentifierExpected, tokToStr(tok))
proc parseAssignment(L: var TLexer, tok: PToken) =
var
s, val: string
info: TLineInfo
if (tok.ident.id == getIdent("-").id) or (tok.ident.id == getIdent("--").id):
confTok(L, tok) # skip unnecessary prefix
info = getLineInfo(L) # safe for later in case of an error
checkSymbol(L, tok)
s = tokToStr(tok)
confTok(L, tok) # skip symbol
val = ""
while tok.tokType == tkDot:
add(s, '.')
confTok(L, tok)
checkSymbol(L, tok)
add(s, tokToStr(tok))
confTok(L, tok)
if tok.tokType == tkBracketLe:
# BUGFIX: val, not s!
# BUGFIX: do not copy '['!
confTok(L, tok)
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok)
if tok.tokType == tkBracketRi: confTok(L, tok)
else: lexMessage(L, errTokenExpected, "\']\'")
add(val, ']')
if (tok.tokType == tkColon) or (tok.tokType == tkEquals):
if len(val) > 0:
add(val, ':') # BUGFIX
confTok(L, tok) # skip ':' or '='
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok) # skip symbol
while (tok.ident != nil) and (tok.ident.id == getIdent("&").id):
confTok(L, tok)
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok)
processSwitch(s, val, passPP, info)
proc readConfigFile(filename: string) =
var
L: TLexer
tok: PToken
stream: PLLStream
new(tok)
stream = LLStreamOpen(filename, fmRead)
if stream != nil:
openLexer(L, filename, stream)
tok.tokType = tkEof # to avoid a pointless warning
confTok(L, tok) # read in the first token
while tok.tokType != tkEof: parseAssignment(L, tok)
if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
closeLexer(L)
if gVerbosity >= 1: rawMessage(hintConf, filename)
proc getConfigPath(filename: string): string =
# try local configuration file:
result = joinPath(getConfigDir(), filename)
if not ExistsFile(result):
# try standard configuration file (installation did not distribute files
# the UNIX way)
result = joinPath([getPrefixDir(), "config", filename])
if not ExistsFile(result):
result = "/etc/" & filename
proc LoadSpecialConfig(configfilename: string) =
if not (optSkipConfigFile in gGlobalOptions):
readConfigFile(getConfigPath(configfilename))
proc LoadConfig(project: string) =
var conffile, prefix: string
# set default value (can be overwritten):
if libpath == "":
# choose default libpath:
prefix = getPrefixDir()
if (prefix == "/usr"): libpath = "/usr/lib/nimrod"
elif (prefix == "/usr/local"): libpath = "/usr/local/lib/nimrod"
else: libpath = joinPath(prefix, "lib")
LoadSpecialConfig("nimrod.cfg") # read project config file:
if not (optSkipProjConfigFile in gGlobalOptions) and (project != ""):
conffile = changeFileExt(project, "cfg")
if existsFile(conffile): readConfigFile(conffile)

89
rod/nimrod.nim Executable file
View File

@@ -0,0 +1,89 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import
times, commands, scanner, condsyms, options, msgs, nversion, nimconf, ropes,
extccomp, strutils, os, platform, main, parseopt
var
arguments: string = "" # the arguments to be passed to the program that
# should be run
cmdLineInfo: TLineInfo
proc ProcessCmdLine(pass: TCmdLinePass, command, filename: var string) =
var
p: TOptParser
bracketLe: int
key, val: string
p = parseopt.init()
while true:
parseopt.next(p)
case p.kind
of cmdEnd:
break
of cmdLongOption, cmdShortOption:
# hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
# we fix this here
bracketLe = strutils.find(p.key, '[')
if bracketLe >= 0:
key = copy(p.key, 0, bracketLe - 1)
val = copy(p.key, bracketLe + 1) & ':' & p.val
ProcessSwitch(key, val, pass, cmdLineInfo)
else:
ProcessSwitch(p.key, p.val, pass, cmdLineInfo)
of cmdArgument:
if command == "":
command = p.key
elif filename == "":
filename = unixToNativePath(p.key) # BUGFIX for portable build scripts
break
if pass == passCmd2:
arguments = getRestOfCommandLine(p)
if not (optRun in gGlobalOptions) and (arguments != ""):
rawMessage(errArgsNeedRunOption)
proc HandleCmdLine() =
var
command, filename, prog: string
start: TTime
start = getTime()
if paramCount() == 0:
writeCommandLineUsage()
else:
# Process command line arguments:
command = ""
filename = ""
ProcessCmdLine(passCmd1, command, filename)
if filename != "": options.projectPath = splitFile(filename).dir
nimconf.LoadConfig(filename) # load the right config file
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
command = ""
filename = ""
ProcessCmdLine(passCmd2, command, filename)
MainCommand(command, filename)
if gVerbosity >= 2: echo(GC_getStatistics())
if (gCmd != cmdInterpret) and (msgs.gErrorCounter == 0):
rawMessage(hintSuccessX, [$(gLinesCompiled), $(getTime() - start)])
if optRun in gGlobalOptions:
when defined(unix):
prog = "./" & quoteIfContainsWhite(changeFileExt(filename, ""))
else:
prog = quoteIfContainsWhite(changeFileExt(filename, ""))
execExternalProgram(prog & ' ' & arguments)
#{@emit
# GC_disableMarkAndSweep();
#}
cmdLineInfo = newLineInfo("command line", - 1, - 1)
condsyms.InitDefines()
HandleCmdLine()
quit(options.gExitcode)

175
rod/nimsets.nim Executable file
View File

@@ -0,0 +1,175 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this unit handles Nimrod sets; it implements symbolic sets
import
ast, astalgo, trees, nversion, msgs, platform, bitsets, types, rnimsyn
proc toBitSet*(s: PNode, b: var TBitSet)
# this function is used for case statement checking:
proc overlap*(a, b: PNode): bool
proc inSet*(s: PNode, elem: PNode): bool
proc someInSet*(s: PNode, a, b: PNode): bool
proc emptyRange*(a, b: PNode): bool
proc SetHasRange*(s: PNode): bool
# returns true if set contains a range (needed by the code generator)
# these are used for constant folding:
proc unionSets*(a, b: PNode): PNode
proc diffSets*(a, b: PNode): PNode
proc intersectSets*(a, b: PNode): PNode
proc symdiffSets*(a, b: PNode): PNode
proc containsSets*(a, b: PNode): bool
proc equalSets*(a, b: PNode): bool
proc cardSet*(s: PNode): BiggestInt
# implementation
proc inSet(s: PNode, elem: PNode): bool =
if s.kind != nkCurly: InternalError(s.info, "inSet")
for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange:
if leValue(s.sons[i].sons[0], elem) and
leValue(elem, s.sons[i].sons[1]):
return true
else:
if sameValue(s.sons[i], elem):
return true
result = false
proc overlap(a, b: PNode): bool =
if a.kind == nkRange:
if b.kind == nkRange:
result = leValue(a.sons[0], b.sons[1]) and
leValue(b.sons[1], a.sons[1]) or
leValue(a.sons[0], b.sons[0]) and leValue(b.sons[0], a.sons[1])
else:
result = leValue(a.sons[0], b) and leValue(b, a.sons[1])
else:
if b.kind == nkRange:
result = leValue(b.sons[0], a) and leValue(a, b.sons[1])
else:
result = sameValue(a, b)
proc SomeInSet(s: PNode, a, b: PNode): bool =
# checks if some element of a..b is in the set s
if s.kind != nkCurly: InternalError(s.info, "SomeInSet")
for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange:
if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or
leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]):
return true
else:
# a <= elem <= b
if leValue(a, s.sons[i]) and leValue(s.sons[i], b):
return true
result = false
proc toBitSet(s: PNode, b: var TBitSet) =
var first, j: BiggestInt
first = firstOrd(s.typ.sons[0])
bitSetInit(b, int(getSize(s.typ)))
for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange:
j = getOrdValue(s.sons[i].sons[0])
while j <= getOrdValue(s.sons[i].sons[1]):
BitSetIncl(b, j - first)
inc(j)
else:
BitSetIncl(b, getOrdValue(s.sons[i]) - first)
proc ToTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
var
a, b, e, first: BiggestInt # a, b are interval borders
elemType: PType
n: PNode
elemType = settype.sons[0]
first = firstOrd(elemType)
result = newNodeI(nkCurly, info)
result.typ = settype
result.info = info
e = 0
while e < high(s) * elemSize:
if bitSetIn(s, e):
a = e
b = e
while true:
Inc(b)
if (b > high(s) * elemSize) or not bitSetIn(s, b): break
Dec(b)
if a == b:
addSon(result, newIntTypeNode(nkIntLit, a + first, elemType))
else:
n = newNodeI(nkRange, info)
n.typ = elemType
addSon(n, newIntTypeNode(nkIntLit, a + first, elemType))
addSon(n, newIntTypeNode(nkIntLit, b + first, elemType))
addSon(result, n)
e = b
Inc(e)
type
TSetOP = enum
soUnion, soDiff, soSymDiff, soIntersect
proc nodeSetOp(a, b: PNode, op: TSetOp): PNode =
var x, y: TBitSet
toBitSet(a, x)
toBitSet(b, y)
case op
of soUnion: BitSetUnion(x, y)
of soDiff: BitSetDiff(x, y)
of soSymDiff: BitSetSymDiff(x, y)
of soIntersect: BitSetIntersect(x, y)
result = toTreeSet(x, a.typ, a.info)
proc unionSets(a, b: PNode): PNode =
result = nodeSetOp(a, b, soUnion)
proc diffSets(a, b: PNode): PNode =
result = nodeSetOp(a, b, soDiff)
proc intersectSets(a, b: PNode): PNode =
result = nodeSetOp(a, b, soIntersect)
proc symdiffSets(a, b: PNode): PNode =
result = nodeSetOp(a, b, soSymDiff)
proc containsSets(a, b: PNode): bool =
var x, y: TBitSet
toBitSet(a, x)
toBitSet(b, y)
result = bitSetContains(x, y)
proc equalSets(a, b: PNode): bool =
var x, y: TBitSet
toBitSet(a, x)
toBitSet(b, y)
result = bitSetEquals(x, y)
proc cardSet(s: PNode): BiggestInt =
# here we can do better than converting it into a compact set
# we just count the elements directly
result = 0
for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange:
result = result + getOrdValue(s.sons[i].sons[1]) -
getOrdValue(s.sons[i].sons[0]) + 1
else:
Inc(result)
proc SetHasRange(s: PNode): bool =
if s.kind != nkCurly: InternalError(s.info, "SetHasRange")
for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange:
return true
result = false
proc emptyRange(a, b: PNode): bool =
result = not leValue(a, b) # a > b iff not (a <= b)

182
rod/nstrtabs.nim Executable file
View File

@@ -0,0 +1,182 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# String tables.
import
os, nhashes, strutils
type
TStringTableMode* = enum
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
proc newStringTable*(keyValuePairs: openarray[string],
mode: TStringTableMode = modeCaseSensitive): PStringTable
proc put*(t: PStringTable, key, val: string)
proc get*(t: PStringTable, key: string): string
proc hasKey*(t: PStringTable, key: string): bool
proc length*(t: PStringTable): int
type
TFormatFlag* = enum
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)
TFormatFlags* = set[TFormatFlag]
proc `%`*(f: string, t: PStringTable, flags: TFormatFlags = {}): string
# implementation
const
growthFactor = 2
startSize = 64
proc newStringTable(keyValuePairs: openarray[string],
mode: TStringTableMode = modeCaseSensitive): PStringTable =
var i: int
new(result)
result.mode = mode
result.counter = 0
newSeq(result.data, startSize)
i = 0
while i < high(keyValuePairs):
put(result, keyValuePairs[i], keyValuePairs[i + 1])
inc(i, 2)
proc myhash(t: PStringTable, key: string): THash =
case t.mode
of modeCaseSensitive: result = nhashes.GetHashStr(key)
of modeCaseInsensitive: result = nhashes.GetHashStrCI(key)
of modeStyleInsensitive: result = nhashes.getNormalizedHash(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)
proc length(t: PStringTable): int =
result = t.counter
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 get(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
newSeq(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 Put(t: PStringTable, key, val: string) =
var index: int
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: TFormatFlags, key: string): string =
if hasKey(t, key):
return get(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: TFormatFlags = {}): string =
const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
var
i, j: int
key: string
result = ""
i = 0
while i <= len(f) + 0 - 1:
if f[i] == '$':
case f[i + 1]
of '$':
add(result, '$')
inc(i, 2)
of '{':
j = i + 1
while (j <= len(f) + 0 - 1) and (f[j] != '}'): inc(j)
key = copy(f, i + 2 + 0 - 1, j - 1 + 0 - 1)
add(result, getValue(t, flags, key))
i = j + 1
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_':
j = i + 1
while (j <= len(f) + 0 - 1) and (f[j] in PatternChars): inc(j)
key = copy(f, i + 1 + 0 - 1, j - 1 + 0 - 1)
add(result, getValue(t, flags, key))
i = j
else:
add(result, f[i])
inc(i)
else:
add(result, f[i])
inc(i)

20
rod/nversion.nim Executable file
View File

@@ -0,0 +1,20 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module contains Nimrod's version. It is the only place where it needs
# to be changed.
const
MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets?
defaultAsmMarkerSymbol* = '!'
VersionMajor* = 0
VersionMinor* = 8
VersionPatch* = 5
VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch

196
rod/options.nim Executable file
View File

@@ -0,0 +1,196 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import
os, lists, strutils, nstrtabs
type # please make sure we have under 32 options
# (improves code efficiency a lot!)
TOption* = enum # **keep binary compatible**
optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
optOverflowCheck, optNilCheck, optAssert, optLineDir, optWarns, optHints,
optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
optLineTrace, # line tracing support (includes stack tracing)
optEndb, # embedded debugger
optByRef, # use pass by ref for objects (for interfacing with C)
optCheckpoints, # check for checkpoints (used for debugging)
optProfiler # profiler turned on
TOptions* = set[TOption]
TGlobalOption* = enum
gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim,
optListCmd, optCompileOnly, optNoLinking, optSafeCode, # only allow safe code
optCDebug, # turn on debugging information
optGenDynLib, # generate a dynamic library
optGenGuiApp, # generate a GUI application
optGenScript, # generate a script file to compile the *.c files
optGenMapping, # generate a mapping file
optRun, # run the compiled project
optSymbolFiles, # use symbol files for speeding up compilation
optSkipConfigFile, # skip the general config file
optSkipProjConfigFile, # skip the project's config file
optNoMain # do not generate a "main" proc
TGlobalOptions* = set[TGlobalOption]
TCommands* = enum # Nimrod's commands
cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToEcmaScript,
cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, cmdPas, cmdBoot,
cmdGenDepend, cmdListDef, cmdCheck, # semantic checking for whole project
cmdParse, # parse a single file (for debugging)
cmdScan, # scan a single file (for debugging)
cmdDebugTrans, # debug a transformation pass
cmdRst2html, # convert a reStructuredText file to HTML
cmdRst2tex, # convert a reStructuredText file to TeX
cmdInteractive # start interactive session
TStringSeq* = seq[string]
const
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
optOverflowCheck, optBoundsCheck, optAssert}
optionToStr*: array[TOption, string] = ["optNone", "optObjCheck",
"optFieldCheck", "optRangeCheck", "optBoundsCheck", "optOverflowCheck",
"optNilCheck", "optAssert", "optLineDir", "optWarns", "optHints",
"optOptimizeSpeed", "optOptimizeSize", "optStackTrace", "optLineTrace",
"optEmdb", "optByRef", "optCheckpoints", "optProfiler"]
var
gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck,
optBoundsCheck, optOverflowCheck, optAssert, optWarns,
optHints, optStackTrace, optLineTrace}
gGlobalOptions*: TGlobalOptions = {optRefcGC}
gExitcode*: int8
searchPaths*: TLinkedList
outFile*: string = ""
gIndexFile*: string = ""
gCmd*: TCommands = cmdNone # the command
gVerbosity*: int # how verbose the compiler is
gNumberOfProcessors*: int # number of processors
proc FindFile*(f: string): string
const
genSubDir* = "nimcache"
NimExt* = "nim"
RodExt* = "rod"
HtmlExt* = "html"
TexExt* = "tex"
IniExt* = "ini"
DocConfig* = "nimdoc.cfg"
DocTexConfig* = "nimdoc.tex.cfg"
proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string
proc toGeneratedFile*(path, ext: string): string
# converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
proc getPrefixDir*(): string
# gets the application directory
# additional configuration variables:
var
gConfigVars*: PStringTable
libpath*: string = ""
projectPath*: string = ""
gKeepComments*: bool = true # whether the parser needs to keep comments
gImplicitMods*: TStringSeq = @ [] # modules that are to be implicitly imported
proc existsConfigVar*(key: string): bool
proc getConfigVar*(key: string): string
proc setConfigVar*(key, val: string)
proc addImplicitMod*(filename: string)
proc getOutFile*(filename, ext: string): string
proc binaryStrSearch*(x: openarray[string], y: string): int
# implementation
proc existsConfigVar(key: string): bool =
result = hasKey(gConfigVars, key)
proc getConfigVar(key: string): string =
result = nstrtabs.get(gConfigVars, key)
proc setConfigVar(key, val: string) =
nstrtabs.put(gConfigVars, key, val)
proc getOutFile(filename, ext: string): string =
if options.outFile != "": result = options.outFile
else: result = changeFileExt(filename, ext)
proc addImplicitMod(filename: string) =
var length: int
length = len(gImplicitMods)
setlen(gImplicitMods, length + 1)
gImplicitMods[length] = filename
proc getPrefixDir(): string =
result = SplitPath(getApplicationDir()).head
proc shortenDir(dir: string): string =
# returns the interesting part of a dir
var prefix = getPrefixDir() & dirSep
if startsWith(dir, prefix):
return copy(dir, len(prefix) + 0)
prefix = getCurrentDir() & dirSep
if startsWith(dir, prefix):
return copy(dir, len(prefix) + 0)
prefix = projectPath & dirSep #writeln(output, prefix);
#writeln(output, dir);
if startsWith(dir, prefix):
return copy(dir, len(prefix) + 0)
result = dir
proc removeTrailingDirSep(path: string): string =
if (len(path) > 0) and (path[len(path) + 0 - 1] == dirSep):
result = copy(path, 0, len(path) + 0 - 2)
else:
result = path
proc toGeneratedFile(path, ext: string): string =
var (head, tail) = splitPath(path)
if len(head) > 0: head = shortenDir(head & dirSep)
result = joinPath([projectPath, genSubDir, head, changeFileExt(tail, ext)])
proc completeGeneratedFilePath(f: string, createSubDir: bool = true): string =
var (head, tail) = splitPath(f)
if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
var subdir = joinPath([projectPath, genSubDir, head])
if createSubDir:
try:
createDir(subdir)
except EOS:
writeln(stdout, "cannot create directory: " & subdir)
quit(1)
result = joinPath(subdir, tail)
proc rawFindFile(f: string): string =
var it: PStrEntry
if ExistsFile(f):
result = f
else:
it = PStrEntry(SearchPaths.head)
while it != nil:
result = JoinPath(it.data, f)
if ExistsFile(result): return
it = PStrEntry(it.Next)
result = ""
proc FindFile(f: string): string =
result = rawFindFile(f)
if len(result) == 0: result = rawFindFile(toLower(f))
proc binaryStrSearch(x: openarray[string], y: string): int =
var a = 0
var b = len(x) - 1
while a <= b:
var mid = (a + b) div 2
var c = cmpIgnoreCase(x[mid], y)
if c < 0:
a = mid + 1
elif c > 0:
b = mid - 1
else:
return mid
result = - 1
gConfigVars = newStringTable([], modeStyleInsensitive)

346
rod/parsecfg.nim Executable file
View File

@@ -0,0 +1,346 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# A HIGH-PERFORMANCE configuration file parser;
# the Nimrod version of this file is part of the
# standard library.
import
llstream, nhashes, strutils, lexbase
type
TCfgEventKind* = enum
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; msg contains the
# error message
TCfgEvent* = object of TObject
case kind*: TCfgEventKind
of cfgEof:
nil
of cfgSectionStart:
section*: string
of cfgKeyValuePair, cfgOption:
key*, value*: string
of cfgError:
msg*: string
TTokKind* = enum
tkInvalid, tkEof, # order is important here!
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
tok*: TToken
state*: TParserState
filename*: string
proc Open*(c: var TCfgParser, filename: string, inputStream: PLLStream)
proc Close*(c: var TCfgParser)
proc next*(c: var TCfgParser): TCfgEvent
proc getColumn*(c: TCfgParser): int
proc getLine*(c: TCfgParser): int
proc getFilename*(c: TCfgParser): string
proc errorStr*(c: TCfgParser, msg: string): string
# 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, inputStream: PLLStream) =
openBaseLexer(c, inputStream)
c.filename = filename
c.state = startState
c.tok.kind = tkInvalid
c.tok.literal = ""
rawGetTok(c, c.tok)
proc close(c: var TCfgParser) =
closeBaseLexer(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 & "\n"
Inc(c.bufpos)
of 'r', 'R', 'c', 'C':
add(tok.literal, CR)
Inc(c.bufpos)
of 'l', 'L':
add(tok.literal, LF)
Inc(c.bufpos)
of 'f', 'F':
add(tok.literal, FF)
inc(c.bufpos)
of 'e', 'E':
add(tok.literal, ESC)
Inc(c.bufpos)
of 'a', 'A':
add(tok.literal, BEL)
Inc(c.bufpos)
of 'b', 'B':
add(tok.literal, BACKSPACE)
Inc(c.bufpos)
of 'v', 'V':
add(tok.literal, VT)
Inc(c.bufpos)
of 't', 'T':
add(tok.literal, Tabulator)
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 CR: result = lexbase.HandleCR(c, pos)
of LF: 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)
buf = c.buf
while true:
case buf[pos]
of '\"':
if (buf[pos + 1] == '\"') and (buf[pos + 2] == '\"'): break
add(tok.literal, '\"')
Inc(pos)
of CR, LF:
pos = HandleCRLF(c, pos)
buf = c.buf
tok.literal = tok.literal & "\n"
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 {CR, LF, 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 ' ':
Inc(pos)
of Tabulator:
inc(pos)
of '#', ';':
while not (buf[pos] in {CR, LF, lexbase.EndOfFile}): inc(pos)
of CR, LF:
pos = HandleCRLF(c, pos)
buf = c.buf
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)
tok.literal = "="
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, $(getLine(c)), $(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)
while c.tok.literal == ".":
add(result.key, '.')
rawGetTok(c, c.tok)
if c.tok.kind == tkSymbol:
add(result.key, c.tok.literal)
rawGetTok(c, c.tok)
else:
result.kind = cfgError
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
break
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, tkBracketRi, tkEquals, tkColon:
result.kind = cfgError
result.msg = errorStr(c, "invalid token: " & c.tok.literal)
rawGetTok(c, c.tok)

115
rod/parseopt.nim Executable file
View File

@@ -0,0 +1,115 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# A command line parser; the Nimrod version of this file
# will become part of the standard library.
import
os, strutils
type
TCmdLineKind* = enum
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
cmd*: string
pos*: int
inShortState*: bool
kind*: TCmdLineKind
key*, val*: string
proc init*(cmdline: string = ""): TOptParser
proc next*(p: var TOptParser)
proc getRestOfCommandLine*(p: TOptParser): string
# implementation
proc init(cmdline: string = ""): TOptParser =
result.pos = 0
result.inShortState = false
if cmdline != "":
result.cmd = cmdline
else:
result.cmd = ""
for i in countup(1, ParamCount()):
result.cmd = result.cmd & quoteIfContainsWhite(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 + 0, len(p.cmd) - 1)) # always -1, because Pascal version uses a trailing zero here

671
rod/paslex.nim Executable file
View File

@@ -0,0 +1,671 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements a FreePascal scanner. This is a adaption from
# the scanner module.
import
nhashes, options, msgs, strutils, platform, idents, lexbase, wordrecg, scanner
const
MaxLineLength* = 80 # lines longer than this lead to a warning
numChars*: TCharSet = {'0'..'9', 'a'..'z', 'A'..'Z'} # we support up to base 36
SymChars*: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
SymStartChars*: TCharSet = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
OpChars*: TCharSet = {'+', '-', '*', '/', '<', '>', '!', '?', '^', '.', '|',
'=', ':', '%', '&', '$', '@', '~', '\x80'..'\xFF'}
type # order is important for TPasTokKind
TPasTokKind* = enum
pxInvalid, pxEof, # keywords:
#[[[cog
#from string import capitalize
#keywords = eval(open("data/pas_keyw.yml").read())
#idents = ""
#strings = ""
#i = 1
#for k in keywords:
# idents = idents + "px" + capitalize(k) + ", "
# strings = strings + "'" + k + "', "
# if i % 4 == 0:
# idents = idents + "\n"
# strings = strings + "\n"
# i = i + 1
#cog.out(idents)
#]]]
pxAnd, pxArray, pxAs, pxAsm, pxBegin, pxCase, pxClass, pxConst,
pxConstructor, pxDestructor, pxDiv, pxDo, pxDownto, pxElse, pxEnd, pxExcept,
pxExports, pxFinalization, pxFinally, pxFor, pxFunction, pxGoto, pxIf,
pxImplementation, pxIn, pxInherited, pxInitialization, pxInline,
pxInterface, pxIs, pxLabel, pxLibrary, pxMod, pxNil, pxNot, pxObject, pxOf,
pxOr, pxOut, pxPacked, pxProcedure, pxProgram, pxProperty, pxRaise,
pxRecord, pxRepeat, pxResourcestring, pxSet, pxShl, pxShr, pxThen,
pxThreadvar, pxTo, pxTry, pxType, pxUnit, pxUntil, pxUses, pxVar, pxWhile,
pxWith, pxXor, #[[[end]]]
pxComment, # ordinary comment
pxCommand, # {@}
pxAmp, # {&}
pxPer, # {%}
pxStrLit, pxSymbol, # a symbol
pxIntLit, pxInt64Lit, # long constant like 0x00000070fffffff or out of int range
pxFloatLit, pxParLe, pxParRi, pxBracketLe, pxBracketRi, pxComma,
pxSemiColon, pxColon, # operators
pxAsgn, pxEquals, pxDot, pxDotDot, pxHat, pxPlus, pxMinus, pxStar, pxSlash,
pxLe, pxLt, pxGe, pxGt, pxNeq, pxAt, pxStarDirLe, pxStarDirRi, pxCurlyDirLe,
pxCurlyDirRi
TPasTokKinds* = set[TPasTokKind]
const
PasTokKindToStr*: array[TPasTokKind, string] = ["pxInvalid", "[EOF]", #[[[cog
#cog.out(strings)
#]]]
"and", "array", "as", "asm", "begin", "case", "class", "const",
"constructor", "destructor", "div", "do", "downto", "else", "end", "except",
"exports", "finalization", "finally", "for", "function", "goto", "if",
"implementation", "in", "inherited", "initialization", "inline",
"interface", "is", "label", "library", "mod", "nil", "not", "object", "of",
"or", "out", "packed", "procedure", "program", "property", "raise",
"record", "repeat", "resourcestring", "set", "shl", "shr", "then",
"threadvar", "to", "try", "type", "unit", "until", "uses", "var", "while",
"with", "xor", #[[[end]]]
"pxComment", "pxCommand", "{&}", "{%}", "pxStrLit", "[IDENTIFIER]",
"pxIntLit", "pxInt64Lit", "pxFloatLit", "(", ")", "[", "]", ",", ";", ":",
":=", "=", ".", "..", "^", "+", "-", "*", "/", "<=", "<", ">=", ">", "<>",
"@", "(*$", "*)", "{$", "}"]
type
TPasTok* = object of TToken # a Pascal token
xkind*: TPasTokKind # the type of the token
TPasLex* = object of TLexer
proc getPasTok*(L: var TPasLex, tok: var TPasTok)
proc PrintPasTok*(tok: TPasTok)
proc pasTokToStr*(tok: TPasTok): string
# implementation
proc pastokToStr(tok: TPasTok): string =
case tok.xkind
of pxIntLit, pxInt64Lit: result = $(tok.iNumber)
of pxFloatLit: result = $(tok.fNumber)
of pxInvalid, pxComment..pxStrLit: result = tok.literal
else:
if (tok.ident.s != ""): result = tok.ident.s
else: result = pasTokKindToStr[tok.xkind]
proc PrintPasTok(tok: TPasTok) =
write(stdout, pasTokKindToStr[tok.xkind])
write(stdout, ' ')
writeln(stdout, pastokToStr(tok))
proc setKeyword(L: var TPasLex, tok: var TPasTok) =
case tok.ident.id #[[[cog
#for k in keywords:
# m = capitalize(k)
# cog.outl("ord(w%s):%s tok.xkind := px%s;" % (m, ' '*(18-len(m)), m))
#]]]
of ord(wAnd):
tok.xkind = pxAnd
of ord(wArray):
tok.xkind = pxArray
of ord(wAs):
tok.xkind = pxAs
of ord(wAsm):
tok.xkind = pxAsm
of ord(wBegin):
tok.xkind = pxBegin
of ord(wCase):
tok.xkind = pxCase
of ord(wClass):
tok.xkind = pxClass
of ord(wConst):
tok.xkind = pxConst
of ord(wConstructor):
tok.xkind = pxConstructor
of ord(wDestructor):
tok.xkind = pxDestructor
of ord(wDiv):
tok.xkind = pxDiv
of ord(wDo):
tok.xkind = pxDo
of ord(wDownto):
tok.xkind = pxDownto
of ord(wElse):
tok.xkind = pxElse
of ord(wEnd):
tok.xkind = pxEnd
of ord(wExcept):
tok.xkind = pxExcept
of ord(wExports):
tok.xkind = pxExports
of ord(wFinalization):
tok.xkind = pxFinalization
of ord(wFinally):
tok.xkind = pxFinally
of ord(wFor):
tok.xkind = pxFor
of ord(wFunction):
tok.xkind = pxFunction
of ord(wGoto):
tok.xkind = pxGoto
of ord(wIf):
tok.xkind = pxIf
of ord(wImplementation):
tok.xkind = pxImplementation
of ord(wIn):
tok.xkind = pxIn
of ord(wInherited):
tok.xkind = pxInherited
of ord(wInitialization):
tok.xkind = pxInitialization
of ord(wInline):
tok.xkind = pxInline
of ord(wInterface):
tok.xkind = pxInterface
of ord(wIs):
tok.xkind = pxIs
of ord(wLabel):
tok.xkind = pxLabel
of ord(wLibrary):
tok.xkind = pxLibrary
of ord(wMod):
tok.xkind = pxMod
of ord(wNil):
tok.xkind = pxNil
of ord(wNot):
tok.xkind = pxNot
of ord(wObject):
tok.xkind = pxObject
of ord(wOf):
tok.xkind = pxOf
of ord(wOr):
tok.xkind = pxOr
of ord(wOut):
tok.xkind = pxOut
of ord(wPacked):
tok.xkind = pxPacked
of ord(wProcedure):
tok.xkind = pxProcedure
of ord(wProgram):
tok.xkind = pxProgram
of ord(wProperty):
tok.xkind = pxProperty
of ord(wRaise):
tok.xkind = pxRaise
of ord(wRecord):
tok.xkind = pxRecord
of ord(wRepeat):
tok.xkind = pxRepeat
of ord(wResourcestring):
tok.xkind = pxResourcestring
of ord(wSet):
tok.xkind = pxSet
of ord(wShl):
tok.xkind = pxShl
of ord(wShr):
tok.xkind = pxShr
of ord(wThen):
tok.xkind = pxThen
of ord(wThreadvar):
tok.xkind = pxThreadvar
of ord(wTo):
tok.xkind = pxTo
of ord(wTry):
tok.xkind = pxTry
of ord(wType):
tok.xkind = pxType
of ord(wUnit):
tok.xkind = pxUnit
of ord(wUntil):
tok.xkind = pxUntil
of ord(wUses):
tok.xkind = pxUses
of ord(wVar):
tok.xkind = pxVar
of ord(wWhile):
tok.xkind = pxWhile
of ord(wWith):
tok.xkind = pxWith
of ord(wXor):
tok.xkind = pxXor #[[[end]]]
else: tok.xkind = pxSymbol
proc matchUnderscoreChars(L: var TPasLex, tok: var TPasTok, chars: TCharSet) =
# matches ([chars]_)*
var
pos: int
buf: cstring
pos = L.bufpos # use registers for pos, buf
buf = L.buf
while true:
if buf[pos] in chars:
add(tok.literal, buf[pos])
Inc(pos)
else:
break
if buf[pos] == '_':
add(tok.literal, '_')
Inc(pos)
L.bufPos = pos
proc isFloatLiteral(s: string): bool =
for i in countup(0, len(s) + 0 - 1):
if s[i] in {'.', 'e', 'E'}:
return true
result = false
proc getNumber2(L: var TPasLex, tok: var TPasTok) =
var
pos, bits: int
xi: biggestInt
pos = L.bufpos + 1 # skip %
if not (L.buf[pos] in {'0'..'1'}):
# BUGFIX for %date%
tok.xkind = pxInvalid
add(tok.literal, '%')
inc(L.bufpos)
return
tok.base = base2
xi = 0
bits = 0
while true:
case L.buf[pos]
of 'A'..'Z', 'a'..'z', '2'..'9', '.':
lexMessage(L, errInvalidNumber)
inc(pos)
of '_':
inc(pos)
of '0', '1':
xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0'))
inc(pos)
inc(bits)
else: break
tok.iNumber = xi
if (bits > 32):
tok.xkind = pxInt64Lit
else:
tok.xkind = pxIntLit
L.bufpos = pos
proc getNumber16(L: var TPasLex, tok: var TPasTok) =
var
pos, bits: int
xi: biggestInt
pos = L.bufpos + 1 # skip $
tok.base = base16
xi = 0
bits = 0
while true:
case L.buf[pos]
of 'G'..'Z', 'g'..'z', '.':
lexMessage(L, errInvalidNumber)
inc(pos)
of '_':
inc(pos)
of '0'..'9':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0'))
inc(pos)
inc(bits, 4)
of 'a'..'f':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10)
inc(pos)
inc(bits, 4)
of 'A'..'F':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
inc(pos)
inc(bits, 4)
else: break
tok.iNumber = xi
if (bits > 32):
tok.xkind = pxInt64Lit
else:
tok.xkind = pxIntLit
L.bufpos = pos
proc getNumber10(L: var TPasLex, tok: var TPasTok) =
tok.base = base10
matchUnderscoreChars(L, tok, {'0'..'9'})
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
add(tok.literal, '.')
inc(L.bufpos)
matchUnderscoreChars(L, tok, {'e', 'E', '+', '-', '0'..'9'})
try:
if isFloatLiteral(tok.literal):
tok.fnumber = parseFloat(tok.literal)
tok.xkind = pxFloatLit
else:
tok.iNumber = ParseInt(tok.literal)
if (tok.iNumber < low(int32)) or (tok.iNumber > high(int32)):
tok.xkind = pxInt64Lit
else:
tok.xkind = pxIntLit
except EInvalidValue:
lexMessage(L, errInvalidNumber, tok.literal)
except EOverflow:
lexMessage(L, errNumberOutOfRange, tok.literal)
proc HandleCRLF(L: var TLexer, pos: int): int =
case L.buf[pos]
of CR: result = lexbase.HandleCR(L, pos)
of LF: result = lexbase.HandleLF(L, pos)
else: result = pos
proc getString(L: var TPasLex, tok: var TPasTok) =
var
pos, xi: int
buf: cstring
pos = L.bufPos
buf = L.buf
while true:
if buf[pos] == '\'':
inc(pos)
while true:
case buf[pos]
of CR, LF, lexbase.EndOfFile:
lexMessage(L, errClosingQuoteExpected)
break
of '\'':
inc(pos)
if buf[pos] == '\'':
inc(pos)
add(tok.literal, '\'')
else:
break
else:
add(tok.literal, buf[pos])
inc(pos)
elif buf[pos] == '#':
inc(pos)
xi = 0
case buf[pos]
of '$':
inc(pos)
xi = 0
while true:
case buf[pos]
of '0'..'9': xi = (xi shl 4) or (ord(buf[pos]) - ord('0'))
of 'a'..'f': xi = (xi shl 4) or (ord(buf[pos]) - ord('a') + 10)
of 'A'..'F': xi = (xi shl 4) or (ord(buf[pos]) - ord('A') + 10)
else: break
inc(pos)
of '0'..'9':
xi = 0
while buf[pos] in {'0'..'9'}:
xi = (xi * 10) + (ord(buf[pos]) - ord('0'))
inc(pos)
else: lexMessage(L, errInvalidCharacterConstant)
if (xi <= 255): add(tok.literal, Chr(xi))
else: lexMessage(L, errInvalidCharacterConstant)
else:
break
tok.xkind = pxStrLit
L.bufpos = pos
proc getSymbol(L: var TPasLex, tok: var TPasTok) =
var
pos: int
c: Char
buf: cstring
h: THash # hashing algorithm inlined
h = 0
pos = L.bufpos
buf = L.buf
while true:
c = buf[pos]
case c
of 'a'..'z', '0'..'9', '\x80'..'\xFF':
h = h +% Ord(c)
h = h +% h shl 10
h = h xor (h shr 6)
of 'A'..'Z':
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h +% Ord(c)
h = h +% h shl 10
h = h xor (h shr 6)
of '_':
nil
else: break
Inc(pos)
h = h +% h shl 3
h = h xor (h shr 11)
h = h +% h shl 15
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
L.bufpos = pos
setKeyword(L, tok)
proc scanLineComment(L: var TPasLex, tok: var TPasTok) =
var
buf: cstring
pos, col: int
indent: int
pos = L.bufpos
buf = L.buf # a comment ends if the next line does not start with the // on the same
# column after only whitespace
tok.xkind = pxComment
col = getColNumber(L, pos)
while true:
inc(pos, 2) # skip //
add(tok.literal, '#')
while not (buf[pos] in {CR, LF, lexbase.EndOfFile}):
add(tok.literal, buf[pos])
inc(pos)
pos = handleCRLF(L, pos)
buf = L.buf
indent = 0
while buf[pos] == ' ':
inc(pos)
inc(indent)
if (col == indent) and (buf[pos] == '/') and (buf[pos + 1] == '/'):
tok.literal = tok.literal & "\n"
else:
break
L.bufpos = pos
proc scanCurlyComment(L: var TPasLex, tok: var TPasTok) =
var
buf: cstring
pos: int
pos = L.bufpos
buf = L.buf
tok.literal = "#"
tok.xkind = pxComment
while true:
case buf[pos]
of CR, LF:
pos = HandleCRLF(L, pos)
buf = L.buf
tok.literal = tok.literal & "\n" & '#'
of '}':
inc(pos)
break
of lexbase.EndOfFile:
lexMessage(L, errTokenExpected, "}")
else:
add(tok.literal, buf[pos])
inc(pos)
L.bufpos = pos
proc scanStarComment(L: var TPasLex, tok: var TPasTok) =
var
buf: cstring
pos: int
pos = L.bufpos
buf = L.buf
tok.literal = "#"
tok.xkind = pxComment
while true:
case buf[pos]
of CR, LF:
pos = HandleCRLF(L, pos)
buf = L.buf
tok.literal = tok.literal & "\n" & '#'
of '*':
inc(pos)
if buf[pos] == ')':
inc(pos)
break
else:
add(tok.literal, '*')
of lexbase.EndOfFile:
lexMessage(L, errTokenExpected, "*)")
else:
add(tok.literal, buf[pos])
inc(pos)
L.bufpos = pos
proc skip(L: var TPasLex, tok: var TPasTok) =
var
buf: cstring
pos: int
pos = L.bufpos
buf = L.buf
while true:
case buf[pos]
of ' ', Tabulator:
Inc(pos) # newline is special:
of CR, LF:
pos = HandleCRLF(L, pos)
buf = L.buf
else:
break # EndOfFile also leaves the loop
L.bufpos = pos
proc getPasTok(L: var TPasLex, tok: var TPasTok) =
var c: Char
tok.xkind = pxInvalid
fillToken(tok)
skip(L, tok)
c = L.buf[L.bufpos]
if c in SymStartChars:
getSymbol(L, tok)
elif c in {'0'..'9'}:
getNumber10(L, tok)
else:
case c
of ';':
tok.xkind = pxSemicolon
Inc(L.bufpos)
of '/':
if L.buf[L.bufpos + 1] == '/':
scanLineComment(L, tok)
else:
tok.xkind = pxSlash
inc(L.bufpos)
of ',':
tok.xkind = pxComma
Inc(L.bufpos)
of '(':
Inc(L.bufpos)
if (L.buf[L.bufPos] == '*'):
if (L.buf[L.bufPos + 1] == '$'):
Inc(L.bufpos, 2)
skip(L, tok)
getSymbol(L, tok)
tok.xkind = pxStarDirLe
else:
inc(L.bufpos)
scanStarComment(L, tok)
else:
tok.xkind = pxParLe
of '*':
inc(L.bufpos)
if L.buf[L.bufpos] == ')':
inc(L.bufpos)
tok.xkind = pxStarDirRi
else:
tok.xkind = pxStar
of ')':
tok.xkind = pxParRi
Inc(L.bufpos)
of '[':
Inc(L.bufpos)
tok.xkind = pxBracketLe
of ']':
Inc(L.bufpos)
tok.xkind = pxBracketRi
of '.':
inc(L.bufpos)
if L.buf[L.bufpos] == '.':
tok.xkind = pxDotDot
inc(L.bufpos)
else:
tok.xkind = pxDot
of '{':
Inc(L.bufpos)
case L.buf[L.bufpos]
of '$':
Inc(L.bufpos)
skip(L, tok)
getSymbol(L, tok)
tok.xkind = pxCurlyDirLe
of '&':
Inc(L.bufpos)
tok.xkind = pxAmp
of '%':
Inc(L.bufpos)
tok.xkind = pxPer
of '@':
Inc(L.bufpos)
tok.xkind = pxCommand
else: scanCurlyComment(L, tok)
of '+':
tok.xkind = pxPlus
inc(L.bufpos)
of '-':
tok.xkind = pxMinus
inc(L.bufpos)
of ':':
inc(L.bufpos)
if L.buf[L.bufpos] == '=':
inc(L.bufpos)
tok.xkind = pxAsgn
else:
tok.xkind = pxColon
of '<':
inc(L.bufpos)
if L.buf[L.bufpos] == '>':
inc(L.bufpos)
tok.xkind = pxNeq
elif L.buf[L.bufpos] == '=':
inc(L.bufpos)
tok.xkind = pxLe
else:
tok.xkind = pxLt
of '>':
inc(L.bufpos)
if L.buf[L.bufpos] == '=':
inc(L.bufpos)
tok.xkind = pxGe
else:
tok.xkind = pxGt
of '=':
tok.xkind = pxEquals
inc(L.bufpos)
of '@':
tok.xkind = pxAt
inc(L.bufpos)
of '^':
tok.xkind = pxHat
inc(L.bufpos)
of '}':
tok.xkind = pxCurlyDirRi
Inc(L.bufpos)
of '\'', '#':
getString(L, tok)
of '$':
getNumber16(L, tok)
of '%':
getNumber2(L, tok)
of lexbase.EndOfFile:
tok.xkind = pxEof
else:
tok.literal = c & ""
tok.xkind = pxInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
Inc(L.bufpos)

1572
rod/pasparse.nim Executable file

File diff suppressed because it is too large Load Diff

52
rod/passaux.nim Executable file
View File

@@ -0,0 +1,52 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# implements some little helper passes
import
strutils, ast, astalgo, passes, msgs, options
proc verbosePass*(): TPass
proc cleanupPass*(): TPass
# implementation
proc verboseOpen(s: PSym, filename: string): PPassContext =
#MessageOut('compiling ' + s.name.s);
result = nil # we don't need a context
if gVerbosity > 0: rawMessage(hintProcessing, s.name.s)
proc verboseProcess(context: PPassContext, n: PNode): PNode =
result = n
if context != nil: InternalError("logpass: context is not nil")
if gVerbosity == 3: liMessage(n.info, hintProcessing, $(ast.gid))
proc verbosePass(): TPass =
initPass(result)
result.open = verboseOpen
result.process = verboseProcess
proc cleanUp(c: PPassContext, n: PNode): PNode =
var s: PSym
result = n # we cannot clean up if dead code elimination is activated
if (optDeadCodeElim in gGlobalOptions): return
case n.kind
of nkStmtList:
for i in countup(0, sonsLen(n) - 1): discard cleanup(c, n.sons[i])
of nkProcDef, nkMethodDef:
if (n.sons[namePos].kind == nkSym):
s = n.sons[namePos].sym
if not (sfDeadCodeElim in getModule(s).flags) and not astNeeded(s):
s.ast.sons[codePos] = nil # free the memory
else:
nil
proc cleanupPass(): TPass =
initPass(result)
result.process = cleanUp
result.close = cleanUp

172
rod/passes.nim Executable file
View File

@@ -0,0 +1,172 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements the passes functionality. A pass must implement the
# `TPass` interface.
import
strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, rnimsyn, types, extccomp, math, magicsys, nversion, nimsets,
syntaxes, times, rodread
type
TPassContext* = object of TObject # the pass's context
PPassContext* = ref TPassContext
TPass* = tuple[open: proc (module: PSym, filename: string): PPassContext,
openCached: proc (module: PSym, filename: string, rd: PRodReader): PPassContext,
close: proc (p: PPassContext, n: PNode): PNode,
process: proc (p: PPassContext, topLevelStmt: PNode): PNode] # a
# pass is a
# tuple of
# procedure
# vars
#
# ``TPass.close``
# may
# produce
# additional
# nodes.
# These
# are
# passed to
# the
# other
#
# close
# procedures.
# This
# mechanism is
# needed
# for
# the
# instantiation of
#
# generics.
proc registerPass*(p: TPass)
proc initPass*(p: var TPass)
# This implements a memory preserving scheme: Top level statements are
# processed in a pipeline. The compiler never looks at a whole module
# any longer. However, this is simple to change, as new passes may perform
# whole program optimizations. For now, we avoid it to save a lot of memory.
proc processModule*(module: PSym, filename: string, stream: PLLStream,
rd: PRodReader)
proc astNeeded*(s: PSym): bool
# The ``rodwrite`` module uses this to determine if the body of a proc
# needs to be stored. The passes manager frees s.sons[codePos] when
# appropriate to free the procedure body's memory. This is important
# to keep memory usage down.
# the semantic checker needs these:
var
gImportModule*: proc (filename: string): PSym
gIncludeFile*: proc (filename: string): PNode
# implementation
proc astNeeded(s: PSym): bool =
if (s.kind in {skMethod, skProc}) and
({sfCompilerProc, sfCompileTime} * s.flags == {}) and
(s.typ.callConv != ccInline) and (s.ast.sons[genericParamsPos] == nil):
result = false
else:
result = true
const
maxPasses = 10
type
TPassContextArray = array[0..maxPasses - 1, PPassContext]
var
gPasses: array[0..maxPasses - 1, TPass]
gPassesLen: int
proc registerPass(p: TPass) =
gPasses[gPassesLen] = p
inc(gPassesLen)
proc openPasses(a: var TPassContextArray, module: PSym, filename: string) =
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].open): a[i] = gPasses[i].open(module, filename)
else: a[i] = nil
proc openPassesCached(a: var TPassContextArray, module: PSym, filename: string,
rd: PRodReader) =
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached):
a[i] = gPasses[i].openCached(module, filename, rd)
else:
a[i] = nil
proc closePasses(a: var TPassContextArray) =
var m: PNode
m = nil
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here
proc processTopLevelStmt(n: PNode, a: var TPassContextArray) =
var m: PNode
# this implements the code transformation pipeline
m = n
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].process): m = gPasses[i].process(a[i], m)
proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
var m: PNode
# this implements the code transformation pipeline
m = n
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m)
proc closePassesCached(a: var TPassContextArray) =
var m: PNode
m = nil
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here
proc processModule(module: PSym, filename: string, stream: PLLStream,
rd: PRodReader) =
var
p: TParsers
n: PNode
a: TPassContextArray
s: PLLStream
if rd == nil:
openPasses(a, module, filename)
if stream == nil:
s = LLStreamOpen(filename, fmRead)
if s == nil:
rawMessage(errCannotOpenFile, filename)
return
else:
s = stream
while true:
openParsers(p, filename, s)
while true:
n = parseTopLevelStmt(p)
if n == nil: break
processTopLevelStmt(n, a)
closeParsers(p)
if s.kind != llsStdIn: break
closePasses(a) # id synchronization point for more consistent code generation:
IDsynchronizationPoint(1000)
else:
openPassesCached(a, module, filename, rd)
n = loadInitSection(rd) #MessageOut('init section' + renderTree(n));
for i in countup(0, sonsLen(n) - 1): processTopLevelStmtCached(n.sons[i], a)
closePassesCached(a)
proc initPass(p: var TPass) =
p.open = nil
p.openCached = nil
p.close = nil
p.process = nil

1194
rod/pbraces.nim Executable file

File diff suppressed because it is too large Load Diff

23
rod/pendx.nim Executable file
View File

@@ -0,0 +1,23 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import
llstream, scanner, idents, strutils, ast, msgs, pnimsyn
proc ParseAll*(p: var TParser): PNode
proc parseTopLevelStmt*(p: var TParser): PNode
# implements an iterator. Returns the next top-level statement or nil if end
# of stream.
# implementation
proc ParseAll(p: var TParser): PNode =
result = nil
proc parseTopLevelStmt(p: var TParser): PNode =
result = nil

212
rod/platform.nim Executable file
View File

@@ -0,0 +1,212 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module contains data about the different processors
# and operating systems.
# Note: Unfortunately if an OS or CPU is listed here this does not mean that
# Nimrod has been tested on this platform or that the RTL has been ported.
# Feel free to test for your excentric platform!
import
strutils
type
TSystemOS* = enum # Also add OS in initialization section and alias
# conditionals to condsyms (end of module).
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
osIrix, osNetbsd, osFreebsd, osOpenbsd, osAix, osPalmos, osQnx, osAmiga,
osAtari, osNetware, osMacos, osMacosx, osEcmaScript, osNimrodVM
type
TInfoOSProp* = enum
ospNeedsPIC, # OS needs PIC for libraries
ospCaseInsensitive, # OS filesystem is case insensitive
ospPosix # OS is posix-like
TInfoOSProps* = set[TInfoOSProp]
TInfoOS* = tuple[name: string, parDir: string, dllFrmt: string,
altDirSep: string, objExt: string, newLine: string,
pathSep: string, dirSep: string, scriptExt: string,
curDir: string, exeExt: string, extSep: string,
props: TInfoOSProps]
const
OS*: array[succ(low(TSystemOS))..high(TSystemOS), TInfoOS] = [
(name: "DOS",
parDir: "..", dllFrmt: "$1.dll", altDirSep: "/", objExt: ".obj",
newLine: "\x0D\x0A", pathSep: ";", dirSep: "\\", scriptExt: ".bat",
curDir: ".", exeExt: ".exe", extSep: ".", props: {ospCaseInsensitive}),
(name: "Windows", parDir: "..", dllFrmt: "$1.dll", altDirSep: "/",
objExt: ".obj", newLine: "\x0D\x0A", pathSep: ";", dirSep: "\\",
scriptExt: ".bat", curDir: ".", exeExt: ".exe", extSep: ".",
props: {ospCaseInsensitive}),
(name: "OS2", parDir: "..",
dllFrmt: "$1.dll", altDirSep: "/",
objExt: ".obj", newLine: "\x0D\x0A",
pathSep: ";", dirSep: "\\",
scriptExt: ".bat", curDir: ".",
exeExt: ".exe", extSep: ".",
props: {ospCaseInsensitive}),
(name: "Linux", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "MorphOS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "SkyOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "Solaris", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "Irix", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "NetBSD", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "FreeBSD", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "OpenBSD", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "AIX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "PalmOS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC}),
(name: "QNX",
parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o",
newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {ospNeedsPIC, ospPosix}),
(name: "Amiga",
parDir: "..", dllFrmt: "$1.library", altDirSep: "/", objExt: ".o",
newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {ospNeedsPIC}),
(name: "Atari",
parDir: "..", dllFrmt: "$1.dll", altDirSep: "/", objExt: ".o",
newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: "", curDir: ".",
exeExt: ".tpp", extSep: ".", props: {ospNeedsPIC}),
(name: "Netware",
parDir: "..", dllFrmt: "$1.nlm", altDirSep: "/", objExt: "",
newLine: "\x0D\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh",
curDir: ".", exeExt: ".nlm", extSep: ".", props: {ospCaseInsensitive}),
(name: "MacOS", parDir: "::", dllFrmt: "$1Lib", altDirSep: ":",
objExt: ".o", newLine: "\x0D", pathSep: ",", dirSep: ":", scriptExt: "",
curDir: ":", exeExt: "", extSep: ".", props: {ospCaseInsensitive}),
(name: "MacOSX", parDir: "..", dllFrmt: "lib$1.dylib", altDirSep: ":",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "EcmaScript", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {}),
(name: "NimrodVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {})]
type
TSystemCPU* = enum # Also add CPU for in initialization section and
# alias conditionals to condsyms (end of module).
cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuSparc, cpuVm, cpuIa64,
cpuAmd64, cpuMips, cpuArm, cpuEcmaScript, cpuNimrodVM
type
TEndian* = enum
littleEndian, bigEndian
TInfoCPU* = tuple[name: string, intSize: int, endian: TEndian, floatSize: int,
bit: int]
const
EndianToStr*: array[TEndian, string] = ["littleEndian", "bigEndian"]
CPU*: array[succ(low(TSystemCPU))..high(TSystemCPU), TInfoCPU] = [
(name: "i386", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "m68k", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "powerpc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "ecmascript", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32)]
var
targetCPU*, hostCPU*: TSystemCPU
targetOS*, hostOS*: TSystemOS
proc NameToOS*(name: string): TSystemOS
proc NameToCPU*(name: string): TSystemCPU
var
IntSize*: int
floatSize*: int
PtrSize*: int
tnl*: string # target newline
proc setTarget*(o: TSystemOS, c: TSystemCPU) =
assert(c != cpuNone)
assert(o != osNone)
targetCPU = c
targetOS = o
intSize = cpu[c].intSize div 8
floatSize = cpu[c].floatSize div 8
ptrSize = cpu[c].bit div 8
tnl = os[o].newLine
proc NameToOS(name: string): TSystemOS =
for i in countup(succ(osNone), high(TSystemOS)):
if cmpIgnoreStyle(name, OS[i].name) == 0:
return i
result = osNone
proc NameToCPU(name: string): TSystemCPU =
for i in countup(succ(cpuNone), high(TSystemCPU)):
if cmpIgnoreStyle(name, CPU[i].name) == 0:
return i
result = cpuNone
proc nimCPU(): cstring{.importc, noconv.}
proc nimOS(): cstring{.importc, noconv.}
hostCPU = nameToCPU($(nimCPU()))
hostOS = nameToOS($(nimOS()))
setTarget(hostOS, hostCPU) # assume no cross-compiling

1391
rod/pnimsyn.nim Executable file

File diff suppressed because it is too large Load Diff

509
rod/pragmas.nim Executable file
View File

@@ -0,0 +1,509 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements semantic checking for pragmas
import
os, platform, condsyms, ast, astalgo, idents, semdata, msgs, rnimsyn,
wordrecg, ropes, options, strutils, lists, extccomp, math, magicsys
const
FirstCallConv* = wNimcall
LastCallConv* = wNoconv
const
procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wCompilerProc, wPure, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideEffect, wCompilerProc, wDeprecated, wTypeCheck}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir,
wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal,
wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop, wBreakpoint,
wCheckpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wPure, wDeprecated}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerProc, wFinal}
fieldPragmas* = {wImportc, wExportc, wDeprecated}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl}
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect}
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
proc pragmaAsm*(c: PContext, n: PNode): char
# implementation
proc invalidPragma(n: PNode) =
liMessage(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments}))
proc pragmaAsm(c: PContext, n: PNode): char =
var it: PNode
result = '\0'
if n != nil:
for i in countup(0, sonsLen(n) - 1):
it = n.sons[i]
if (it.kind == nkExprColonExpr) and (it.sons[0].kind == nkIdent):
case whichKeyword(it.sons[0].ident)
of wSubsChar:
if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal))
else: invalidPragma(it)
else: invalidPragma(it)
else:
invalidPragma(it)
const
FirstPragmaWord = wMagic
LastPragmaWord = wNoconv
proc MakeExternImport(s: PSym, extname: string) =
s.loc.r = toRope(extname)
incl(s.flags, sfImportc)
excl(s.flags, sfForward)
proc MakeExternExport(s: PSym, extname: string) =
s.loc.r = toRope(extname)
incl(s.flags, sfExportc)
proc expectStrLit(c: PContext, n: PNode): string =
if n.kind != nkExprColonExpr:
liMessage(n.info, errStringLiteralExpected)
result = ""
else:
n.sons[1] = c.semConstExpr(c, n.sons[1])
case n.sons[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit: result = n.sons[1].strVal
else:
liMessage(n.info, errStringLiteralExpected)
result = ""
proc expectIntLit(c: PContext, n: PNode): int =
if n.kind != nkExprColonExpr:
liMessage(n.info, errIntLiteralExpected)
result = 0
else:
n.sons[1] = c.semConstExpr(c, n.sons[1])
case n.sons[1].kind
of nkIntLit..nkInt64Lit: result = int(n.sons[1].intVal)
else:
liMessage(n.info, errIntLiteralExpected)
result = 0
proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
if n.kind == nkExprColonExpr: result = expectStrLit(c, n)
else: result = defaultStr
proc processMagic(c: PContext, n: PNode, s: PSym) =
var v: string
#if not (sfSystemModule in c.module.flags) then
# liMessage(n.info, errMagicOnlyInSystem);
if n.kind != nkExprColonExpr: liMessage(n.info, errStringLiteralExpected)
if n.sons[1].kind == nkIdent: v = n.sons[1].ident.s
else: v = expectStrLit(c, n)
incl(s.flags, sfImportc) # magics don't need an implementation, so we
# treat them as imported, instead of modifing a lot of working code
# BUGFIX: magic does not imply ``lfNoDecl`` anymore!
for m in countup(low(TMagic), high(TMagic)):
if magicToStr[m] == v:
s.magic = m
return
liMessage(n.info, warnUnknownMagic, v)
proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
# this assumes that the order of special words and calling conventions is
# the same
result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall))
proc onOff(c: PContext, n: PNode, op: TOptions) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
case whichKeyword(n.sons[1].ident)
of wOn: gOptions = gOptions + op
of wOff: gOptions = gOptions - op
else: liMessage(n.info, errOnOrOffExpected)
else:
liMessage(n.info, errOnOrOffExpected)
proc pragmaDeadCodeElim(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
case whichKeyword(n.sons[1].ident)
of wOn: incl(c.module.flags, sfDeadCodeElim)
of wOff: excl(c.module.flags, sfDeadCodeElim)
else: liMessage(n.info, errOnOrOffExpected)
else:
liMessage(n.info, errOnOrOffExpected)
proc processCallConv(c: PContext, n: PNode) =
var sw: TSpecialWord
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
sw = whichKeyword(n.sons[1].ident)
case sw
of firstCallConv..lastCallConv:
POptionEntry(c.optionStack.tail).defaultCC = wordToCallConv(sw)
else: liMessage(n.info, errCallConvExpected)
else:
liMessage(n.info, errCallConvExpected)
proc getLib(c: PContext, kind: TLibKind, path: string): PLib =
var it: PLib
it = PLib(c.libs.head)
while it != nil:
if it.kind == kind:
if ospCaseInsensitive in platform.OS[targetOS].props:
if cmpIgnoreCase(it.path, path) == 0:
return it
else:
if it.path == path:
return it
it = PLib(it.next)
result = newLib(kind)
result.path = path
Append(c.libs, result)
proc processDynLib(c: PContext, n: PNode, sym: PSym) =
var lib: PLib
if (sym == nil) or (sym.kind == skModule):
POptionEntry(c.optionStack.tail).dynlib = getLib(c, libDynamic,
expectStrLit(c, n))
elif n.kind == nkExprColonExpr:
lib = getLib(c, libDynamic, expectStrLit(c, n))
addToLib(lib, sym)
incl(sym.loc.flags, lfDynamicLib)
else:
incl(sym.loc.flags, lfExportLib)
proc processNote(c: PContext, n: PNode) =
var
x: int
nk: TNoteKind
if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and
(n.sons[0].kind == nkBracketExpr) and
(n.sons[0].sons[1].kind == nkIdent) and
(n.sons[0].sons[0].kind == nkIdent) and (n.sons[1].kind == nkIdent):
case whichKeyword(n.sons[0].sons[0].ident)
of wHint:
x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s)
if x >= 0: nk = TNoteKind(x + ord(hintMin))
else: invalidPragma(n)
of wWarning:
x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
if x >= 0: nk = TNoteKind(x + ord(warnMin))
else: InvalidPragma(n)
else:
invalidPragma(n)
return
case whichKeyword(n.sons[1].ident)
of wOn: incl(gNotes, nk)
of wOff: excl(gNotes, nk)
else: liMessage(n.info, errOnOrOffExpected)
else:
invalidPragma(n)
proc processOption(c: PContext, n: PNode) =
var sw: TSpecialWord
if n.kind != nkExprColonExpr:
invalidPragma(n)
elif n.sons[0].kind == nkBracketExpr:
processNote(c, n)
elif n.sons[0].kind != nkIdent:
invalidPragma(n)
else:
sw = whichKeyword(n.sons[0].ident)
case sw
of wChecks:
OnOff(c, n, checksOptions)
of wObjChecks:
OnOff(c, n, {optObjCheck})
of wFieldchecks:
OnOff(c, n, {optFieldCheck})
of wRangechecks:
OnOff(c, n, {optRangeCheck})
of wBoundchecks:
OnOff(c, n, {optBoundsCheck})
of wOverflowchecks:
OnOff(c, n, {optOverflowCheck})
of wNilchecks:
OnOff(c, n, {optNilCheck})
of wAssertions:
OnOff(c, n, {optAssert})
of wWarnings:
OnOff(c, n, {optWarns})
of wHints:
OnOff(c, n, {optHints})
of wCallConv:
processCallConv(c, n) # ------ these are not in the Nimrod spec: -------------
of wLinedir:
OnOff(c, n, {optLineDir})
of wStacktrace:
OnOff(c, n, {optStackTrace})
of wLinetrace:
OnOff(c, n, {optLineTrace})
of wDebugger:
OnOff(c, n, {optEndb})
of wProfiler:
OnOff(c, n, {optProfiler})
of wByRef:
OnOff(c, n, {optByRef})
of wDynLib:
processDynLib(c, n, nil) #
# -------------------------------------------------------
of wOptimization:
if n.sons[1].kind != nkIdent:
invalidPragma(n)
else:
case whichKeyword(n.sons[1].ident)
of wSpeed:
incl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
of wSize:
excl(gOptions, optOptimizeSpeed)
incl(gOptions, optOptimizeSize)
of wNone:
excl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
else: liMessage(n.info, errNoneSpeedOrSizeExpected)
else: liMessage(n.info, errOptionExpected)
proc processPush(c: PContext, n: PNode, start: int) =
var x, y: POptionEntry
x = newOptionEntry()
y = POptionEntry(c.optionStack.tail)
x.options = gOptions
x.defaultCC = y.defaultCC
x.dynlib = y.dynlib
x.notes = gNotes
append(c.optionStack, x)
for i in countup(start, sonsLen(n) - 1):
processOption(c, n.sons[i]) #liMessage(n.info, warnUser, ropeToStr(optionsToStr(gOptions)));
proc processPop(c: PContext, n: PNode) =
if c.optionStack.counter <= 1:
liMessage(n.info, errAtPopWithoutPush)
else:
gOptions = POptionEntry(c.optionStack.tail).options #liMessage(n.info, warnUser, ropeToStr(optionsToStr(gOptions)));
gNotes = POptionEntry(c.optionStack.tail).notes
remove(c.optionStack, c.optionStack.tail)
proc processDefine(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
DefineSymbol(n.sons[1].ident.s)
liMessage(n.info, warnDeprecated, "define")
else:
invalidPragma(n)
proc processUndef(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
UndefSymbol(n.sons[1].ident.s)
liMessage(n.info, warnDeprecated, "undef")
else:
invalidPragma(n)
type
TLinkFeature = enum
linkNormal, linkSys
proc processCompile(c: PContext, n: PNode) =
var s, found, trunc: string
s = expectStrLit(c, n)
found = findFile(s)
if found == "": found = s
trunc = ChangeFileExt(found, "")
extccomp.addExternalFileToCompile(trunc)
extccomp.addFileToLink(completeCFilePath(trunc, false))
proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
var f, found: string
f = expectStrLit(c, n)
if splitFile(f).ext == "": f = toObjFile(f)
found = findFile(f)
if found == "":
found = f # use the default
case feature
of linkNormal:
extccomp.addFileToLink(found)
of linkSys:
extccomp.addFileToLink(joinPath(libpath, completeCFilePath(found, false)))
else: internalError(n.info, "processCommonLink")
proc PragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "")
proc PragmaCheckpoint(c: PContext, n: PNode) =
# checkpoints can be used to debug the compiler; they are not documented
var info: TLineInfo
info = n.info
inc(info.line) # next line is affected!
msgs.addCheckpoint(info)
proc noVal(n: PNode) =
if n.kind == nkExprColonExpr: invalidPragma(n)
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
var
key, it: PNode
k: TSpecialWord
lib: PLib
if n == nil: return
for i in countup(0, sonsLen(n) - 1):
it = n.sons[i]
if it.kind == nkExprColonExpr: key = it.sons[0]
else: key = it
if key.kind == nkIdent:
k = whichKeyword(key.ident)
if k in validPragmas:
case k
of wExportc:
makeExternExport(sym, getOptionalStr(c, it, sym.name.s))
incl(sym.flags, sfUsed) # avoid wrong hints
of wImportc:
makeExternImport(sym, getOptionalStr(c, it, sym.name.s))
of wAlign:
if sym.typ == nil: invalidPragma(it)
sym.typ.align = expectIntLit(c, it)
if not IsPowerOfTwo(sym.typ.align) and (sym.typ.align != 0):
liMessage(it.info, errPowerOfTwoExpected)
of wNodecl:
noVal(it)
incl(sym.loc.Flags, lfNoDecl)
of wPure:
noVal(it)
if sym != nil: incl(sym.flags, sfPure)
of wVolatile:
noVal(it)
incl(sym.flags, sfVolatile)
of wRegister:
noVal(it)
incl(sym.flags, sfRegister)
of wThreadVar:
noVal(it)
incl(sym.flags, sfThreadVar)
of wDeadCodeElim:
pragmaDeadCodeElim(c, it)
of wMagic:
processMagic(c, it, sym)
of wCompileTime:
noVal(it)
incl(sym.flags, sfCompileTime)
incl(sym.loc.Flags, lfNoDecl)
of wMerge:
noval(it)
incl(sym.flags, sfMerge)
of wHeader:
lib = getLib(c, libHeader, expectStrLit(c, it))
addToLib(lib, sym)
incl(sym.flags, sfImportc)
incl(sym.loc.flags, lfHeader)
incl(sym.loc.Flags, lfNoDecl) # implies nodecl, because
# otherwise header would not make sense
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
of wNosideeffect:
noVal(it)
incl(sym.flags, sfNoSideEffect)
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
of wSideEffect:
noVal(it)
incl(sym.flags, sfSideEffect)
of wNoReturn:
noVal(it)
incl(sym.flags, sfNoReturn)
of wDynLib:
processDynLib(c, it, sym)
of wCompilerProc:
noVal(it) # compilerproc may not get a string!
makeExternExport(sym, sym.name.s)
incl(sym.flags, sfCompilerProc)
incl(sym.flags, sfUsed) # suppress all those stupid warnings
registerCompilerProc(sym)
of wProcvar:
noVal(it)
incl(sym.flags, sfProcVar)
of wDeprecated:
noVal(it)
if sym != nil: incl(sym.flags, sfDeprecated)
else: incl(c.module.flags, sfDeprecated)
of wVarargs:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfVarargs)
of wBorrow:
noVal(it)
incl(sym.flags, sfBorrow)
of wFinal:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfFinal)
of wAcyclic:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfAcyclic)
of wTypeCheck:
noVal(it)
incl(sym.flags, sfTypeCheck)
of wHint:
liMessage(it.info, hintUser, expectStrLit(c, it))
of wWarning:
liMessage(it.info, warnUser, expectStrLit(c, it))
of wError:
liMessage(it.info, errUser, expectStrLit(c, it))
of wFatal:
liMessage(it.info, errUser, expectStrLit(c, it))
quit(1)
of wDefine:
processDefine(c, it)
of wUndef:
processUndef(c, it)
of wCompile:
processCompile(c, it)
of wLink:
processCommonLink(c, it, linkNormal)
of wLinkSys:
processCommonLink(c, it, linkSys)
of wPassL:
extccomp.addLinkOption(expectStrLit(c, it))
of wPassC:
extccomp.addCompileOption(expectStrLit(c, it))
of wBreakpoint:
PragmaBreakpoint(c, it)
of wCheckpoint:
PragmaCheckpoint(c, it)
of wPush:
processPush(c, n, i + 1)
break
of wPop:
processPop(c, it)
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wByRef, wCallConv,
wDebugger, wProfiler:
processOption(c, it) # calling conventions (boring...):
of firstCallConv..lastCallConv:
assert(sym != nil)
if sym.typ == nil: invalidPragma(it)
sym.typ.callConv = wordToCallConv(k)
else: invalidPragma(it)
else:
invalidPragma(it)
else:
processNote(c, it)
if (sym != nil) and (sym.kind != skModule):
if (lfExportLib in sym.loc.flags) and not (sfExportc in sym.flags):
liMessage(n.info, errDynlibRequiresExportc)
lib = POptionEntry(c.optionstack.tail).dynlib
if ({lfDynamicLib, lfHeader} * sym.loc.flags == {}) and
(sfImportc in sym.flags) and (lib != nil):
incl(sym.loc.flags, lfDynamicLib)
addToLib(lib, sym)
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)

86
rod/procfind.nim Executable file
View File

@@ -0,0 +1,86 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements the searching for procs and iterators.
# This is needed for proper handling of forward declarations.
import
ast, astalgo, msgs, semdata, types, trees
proc SearchForProc*(c: PContext, fn: PSym, tos: int): PSym
# Searchs for the fn in the symbol table. If the parameter lists are exactly
# the same the sym in the symbol table is returned, else nil.
proc SearchForBorrowProc*(c: PContext, fn: PSym, tos: int): PSym
# Searchs for the fn in the symbol table. If the parameter lists are suitable
# for borrowing the sym in the symbol table is returned, else nil.
# implementation
proc equalGenericParams(procA, procB: PNode): bool =
var a, b: PSym
result = procA == procB
if result: return
if (procA == nil) or (procB == nil): return
if sonsLen(procA) != sonsLen(procB): return
for i in countup(0, sonsLen(procA) - 1):
if procA.sons[i].kind != nkSym:
InternalError(procA.info, "equalGenericParams")
if procB.sons[i].kind != nkSym:
InternalError(procB.info, "equalGenericParams")
a = procA.sons[i].sym
b = procB.sons[i].sym
if (a.name.id != b.name.id) or not sameTypeOrNil(a.typ, b.typ): return
if (a.ast != nil) and (b.ast != nil):
if not ExprStructuralEquivalent(a.ast, b.ast): return
result = true
proc SearchForProc(c: PContext, fn: PSym, tos: int): PSym =
var it: TIdentIter
result = initIdentIter(it, c.tab.stack[tos], fn.Name)
while result != nil:
if (result.Kind == fn.kind):
if equalGenericParams(result.ast.sons[genericParamsPos],
fn.ast.sons[genericParamsPos]):
case equalParams(result.typ.n, fn.typ.n)
of paramsEqual:
return
of paramsIncompatible:
liMessage(fn.info, errNotOverloadable, fn.name.s)
return
of paramsNotEqual:
nil
result = NextIdentIter(it, c.tab.stack[tos])
proc paramsFitBorrow(a, b: PNode): bool =
var
length: int
m, n: PSym
length = sonsLen(a)
result = false
if length == sonsLen(b):
for i in countup(1, length - 1):
m = a.sons[i].sym
n = b.sons[i].sym
assert((m.kind == skParam) and (n.kind == skParam))
if not equalOrDistinctOf(m.typ, n.typ): return
if not equalOrDistinctOf(a.sons[0].typ, b.sons[0].typ): return
result = true
proc SearchForBorrowProc(c: PContext, fn: PSym, tos: int): PSym =
# Searchs for the fn in the symbol table. If the parameter lists are suitable
# for borrowing the sym in the symbol table is returned, else nil.
var it: TIdentIter
for scope in countdown(tos, 0):
result = initIdentIter(it, c.tab.stack[scope], fn.Name)
while result != nil:
# watchout! result must not be the same as fn!
if (result.Kind == fn.kind) and (result.id != fn.id):
if equalGenericParams(result.ast.sons[genericParamsPos],
fn.ast.sons[genericParamsPos]):
if paramsFitBorrow(fn.typ.n, result.typ.n): return
result = NextIdentIter(it, c.tab.stack[scope])

185
rod/ptmplsyn.nim Executable file
View File

@@ -0,0 +1,185 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements Nimrod's standard template filter.
import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
rnimsyn, filters
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
# #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
# implementation
type
TParseState = enum
psDirective, psTempl
TTmplParser{.final.} = object
inp*: PLLStream
state*: TParseState
info*: TLineInfo
indent*, par*: int
x*: string # the current input line
outp*: PLLStream # the ouput will be parsed by pnimsyn
subsChar*, NimDirective*: Char
emit*, conc*, toStr*: string
const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
proc newLine(p: var TTmplParser) =
LLStreamWrite(p.outp, repeatChar(p.par, ')'))
p.par = 0
if p.info.line > int16(1): LLStreamWrite(p.outp, "\n")
proc parseLine(p: var TTmplParser) =
var
d, j, curly: int
keyw: string
j = 0
while p.x[j] == ' ': inc(j)
if (p.x[0] == p.NimDirective) and (p.x[0 + 1] == '!'):
newLine(p)
elif (p.x[j] == p.NimDirective):
newLine(p)
inc(j)
while p.x[j] == ' ': inc(j)
d = j
keyw = ""
while p.x[j] in PatternChars:
add(keyw, p.x[j])
inc(j)
case whichKeyword(keyw)
of wEnd:
if p.indent >= 2:
dec(p.indent, 2)
else:
p.info.col = int16(j)
liMessage(p.info, errXNotAllowedHere, "end")
LLStreamWrite(p.outp, repeatChar(p.indent))
LLStreamWrite(p.outp, "#end")
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate, wMethod:
LLStreamWrite(p.outp, repeatChar(p.indent))
LLStreamWrite(p.outp, copy(p.x, d))
inc(p.indent, 2)
of wElif, wOf, wElse, wExcept, wFinally:
LLStreamWrite(p.outp, repeatChar(p.indent - 2))
LLStreamWrite(p.outp, copy(p.x, d))
else:
LLStreamWrite(p.outp, repeatChar(p.indent))
LLStreamWrite(p.outp, copy(p.x, d))
p.state = psDirective
else:
# data line
j = 0
case p.state
of psTempl:
# next line of string literal:
LLStreamWrite(p.outp, p.conc)
LLStreamWrite(p.outp, "\n")
LLStreamWrite(p.outp, repeatChar(p.indent + 2))
LLStreamWrite(p.outp, "\"")
of psDirective:
newLine(p)
LLStreamWrite(p.outp, repeatChar(p.indent))
LLStreamWrite(p.outp, p.emit)
LLStreamWrite(p.outp, "(\"")
inc(p.par)
p.state = psTempl
while true:
case p.x[j]
of '\0':
break
of '\x01'..'\x1F', '\x80'..'\xFF':
LLStreamWrite(p.outp, "\\x")
LLStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
inc(j)
of '\\':
LLStreamWrite(p.outp, "\\\\")
inc(j)
of '\'':
LLStreamWrite(p.outp, "\\\'")
inc(j)
of '\"':
LLStreamWrite(p.outp, "\\\"")
inc(j)
else:
if p.x[j] == p.subsChar:
# parse Nimrod expression:
inc(j)
case p.x[j]
of '{':
p.info.col = int16(j)
LLStreamWrite(p.outp, '\"')
LLStreamWrite(p.outp, p.conc)
LLStreamWrite(p.outp, p.toStr)
LLStreamWrite(p.outp, '(')
inc(j)
curly = 0
while true:
case p.x[j]
of '\0':
liMessage(p.info, errXExpected, "}")
of '{':
inc(j)
inc(curly)
LLStreamWrite(p.outp, '{')
of '}':
inc(j)
if curly == 0: break
if curly > 0: dec(curly)
LLStreamWrite(p.outp, '}')
else:
LLStreamWrite(p.outp, p.x[j])
inc(j)
LLStreamWrite(p.outp, ')')
LLStreamWrite(p.outp, p.conc)
LLStreamWrite(p.outp, '\"')
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
LLStreamWrite(p.outp, '\"')
LLStreamWrite(p.outp, p.conc)
LLStreamWrite(p.outp, p.toStr)
LLStreamWrite(p.outp, '(')
while p.x[j] in PatternChars:
LLStreamWrite(p.outp, p.x[j])
inc(j)
LLStreamWrite(p.outp, ')')
LLStreamWrite(p.outp, p.conc)
LLStreamWrite(p.outp, '\"')
else:
if p.x[j] == p.subsChar:
LLStreamWrite(p.outp, p.subsChar)
inc(j)
else:
p.info.col = int16(j)
liMessage(p.info, errInvalidExpression, "$")
else:
LLStreamWrite(p.outp, p.x[j])
inc(j)
LLStreamWrite(p.outp, "\\n\"")
proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var p: TTmplParser
p.info = newLineInfo(filename, 0, 0)
p.outp = LLStreamOpen("")
p.inp = stdin
p.subsChar = charArg(call, "subschar", 1, '$')
p.nimDirective = charArg(call, "metachar", 2, '#')
p.emit = strArg(call, "emit", 3, "result.add")
p.conc = strArg(call, "conc", 4, " & ")
p.toStr = strArg(call, "tostring", 5, "$")
while not LLStreamAtEnd(p.inp):
p.x = LLStreamReadLine(p.inp)
p.info.line = p.info.line + int16(1)
parseLine(p)
newLine(p)
result = p.outp
LLStreamClose(p.inp)

1194
rod/rnimsyn.nim Executable file

File diff suppressed because it is too large Load Diff

913
rod/rodread.nim Executable file
View File

@@ -0,0 +1,913 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module is responsible for loading of rod files.
#
# Reading and writing binary files are really hard to debug. Therefore we use
# a special text format. ROD-files only describe the interface of a module.
# Thus they are smaller than the source files most of the time. Even if they
# are bigger, they are more efficient to process because symbols are only
# loaded on demand.
# It consists of:
#
# - a header:
# NIM:$fileversion\n
# - the module's id (even if the module changed, its ID will not!):
# ID:Ax3\n
# - CRC value of this module:
# CRC:CRC-val\n
# - a section containing the compiler options and defines this
# module has been compiled with:
# OPTIONS:options\n
# DEFINES:defines\n
# - FILES(
# myfile.inc
# lib/mymodA
# )
# - a include file dependency section:
# INCLUDES(
# <fileidx> <CRC of myfile.inc>\n # fileidx is the LINE in the file section!
# )
# - a module dependency section:
# DEPS: <fileidx> <fileidx>\n
# - an interface section:
# INTERF(
# identifier1 id\n # id is the symbol's id
# identifier2 id\n
# )
# - a compiler proc section:
# COMPILERPROCS(
# identifier1 id\n # id is the symbol's id
# )
# - an index consisting of (ID, linenumber)-pairs:
# INDEX(
# id-diff idx-diff\n
# id-diff idx-diff\n
# )
# - an import index consisting of (ID, moduleID)-pairs:
# IMPORTS(
# id-diff moduleID-diff\n
# id-diff moduleID-diff\n
# )
# - a list of all exported type converters because they are needed for correct
# semantic checking:
# CONVERTERS:id id\n # position of the symbol in the DATA section
# - an AST section that contains the module's AST:
# INIT(
# idx\n # position of the node in the DATA section
# idx\n
# )
# - a data section, where each type, symbol or AST is stored.
# DATA(
# type
# (node)
# sym
# )
#
# We now also do index compression, because an index always needs to be read.
#
import
os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
ropes, idents, crc
type
TReasonForRecompile* = enum
rrEmpty, # used by moddeps module
rrNone, # no need to recompile
rrRodDoesNotExist, # rod file does not exist
rrRodInvalid, # rod file is invalid
rrCrcChange, # file has been edited since last recompilation
rrDefines, # defines have changed
rrOptions, # options have changed
rrInclDeps, # an include has changed
rrModDeps # a module this module depends on has been changed
const
reasonToFrmt*: array[TReasonForRecompile, string] = ["",
"no need to recompile: $1", "symbol file for $1 does not exist",
"symbol file for $1 has the wrong version",
"file edited since last compilation: $1",
"list of conditional symbols changed for: $1",
"list of options changed for: $1", "an include file edited: $1",
"a module $1 depends on has changed"]
type
TIndex*{.final.} = object # an index with compression
lastIdxKey*, lastIdxVal*: int
tab*: TIITable
r*: PRope # writers use this
offset*: int # readers use this
TRodReader* = object of TObject
pos*: int # position; used for parsing
s*: string # the whole file in memory
options*: TOptions
reason*: TReasonForRecompile
modDeps*: TStringSeq
files*: TStringSeq
dataIdx*: int # offset of start of data section
convertersIdx*: int # offset of start of converters section
initIdx*, interfIdx*, compilerProcsIdx*, cgenIdx*: int
filename*: string
index*, imports*: TIndex
readerIndex*: int
line*: int # only used for debugging, but is always in the code
moduleID*: int
syms*: TIdTable # already processed symbols
PRodReader* = ref TRodReader
const
FileVersion* = "1012" # modify this if the rod-format changes!
var rodCompilerprocs*: TStrTable
proc handleSymbolFile*(module: PSym, filename: string): PRodReader
# global because this is needed by magicsys
proc GetCRC*(filename: string): TCrc32
proc loadInitSection*(r: PRodReader): PNode
proc loadStub*(s: PSym)
proc encodeInt*(x: BiggestInt): PRope
proc encode*(s: string): PRope
# implementation
var gTypeTable: TIdTable
proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym
# `info` is only used for debugging purposes
proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType
proc decode(r: PRodReader): string
proc decodeInt(r: PRodReader): int
proc decodeBInt(r: PRodReader): biggestInt
proc encode(s: string): PRope =
var res: string
res = ""
for i in countup(0, len(s) + 0 - 1):
case s[i]
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(res, s[i])
else: res = res & '\\' & toHex(ord(s[i]), 2)
result = toRope(res)
proc encodeIntAux(str: var string, x: BiggestInt) =
const
chars: string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var
v, rem: biggestInt
d: char
idx: int
v = x
rem = v mod 190
if (rem < 0):
str = str & '-'
v = - (v div 190)
rem = - rem
else:
v = v div 190
idx = int(rem)
if idx < 62: d = chars[idx + 0]
else: d = chr(idx - 62 + 128)
if (v != 0): encodeIntAux(str, v)
add(str, d)
proc encodeInt(x: BiggestInt): PRope =
var res: string
res = ""
encodeIntAux(res, x)
result = toRope(res)
proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
if r.s[r.pos] == '?':
inc(r.pos)
if r.s[r.pos] == ',': info.col = int16(- 1)
else: info.col = int16(decodeInt(r))
if r.s[r.pos] == ',':
inc(r.pos)
if r.s[r.pos] == ',': info.line = int16(- 1)
else: info.line = int16(decodeInt(r))
if r.s[r.pos] == ',':
inc(r.pos)
info = newLineInfo(r.files[decodeInt(r)], info.line, info.col)
proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
var
id: int
fl: string
result = nil
if r.s[r.pos] == '(':
inc(r.pos)
if r.s[r.pos] == ')':
inc(r.pos)
return # nil node
result = newNodeI(TNodeKind(decodeInt(r)), fInfo)
decodeLineInfo(r, result.info)
if r.s[r.pos] == '$':
inc(r.pos)
result.flags = cast[TNodeFlags](int32(decodeInt(r)))
if r.s[r.pos] == '^':
inc(r.pos)
id = decodeInt(r)
result.typ = rrGetType(r, id, result.info)
case result.kind
of nkCharLit..nkInt64Lit:
if r.s[r.pos] == '!':
inc(r.pos)
result.intVal = decodeBInt(r)
of nkFloatLit..nkFloat64Lit:
if r.s[r.pos] == '!':
inc(r.pos)
fl = decode(r)
result.floatVal = parseFloat(fl)
of nkStrLit..nkTripleStrLit:
if r.s[r.pos] == '!':
inc(r.pos)
result.strVal = decode(r)
else:
result.strVal = "" # BUGFIX
of nkIdent:
if r.s[r.pos] == '!':
inc(r.pos)
fl = decode(r)
result.ident = getIdent(fl)
else:
internalError(result.info, "decodeNode: nkIdent")
of nkSym:
if r.s[r.pos] == '!':
inc(r.pos)
id = decodeInt(r)
result.sym = rrGetSym(r, id, result.info)
else:
internalError(result.info, "decodeNode: nkSym")
else:
while r.s[r.pos] != ')': addSon(result, decodeNode(r, result.info))
if r.s[r.pos] == ')': inc(r.pos)
else: internalError(result.info, "decodeNode")
else:
InternalError(result.info, "decodeNode " & r.s[r.pos])
proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
if r.s[r.pos] == '<':
inc(r.pos)
if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
loc.k = TLocKind(decodeInt(r))
else:
loc.k = low(loc.k)
if r.s[r.pos] == '*':
inc(r.pos)
loc.s = TStorageLoc(decodeInt(r))
else:
loc.s = low(loc.s)
if r.s[r.pos] == '$':
inc(r.pos)
loc.flags = cast[TLocFlags](int32(decodeInt(r)))
else:
loc.flags = {}
if r.s[r.pos] == '^':
inc(r.pos)
loc.t = rrGetType(r, decodeInt(r), info)
else:
loc.t = nil
if r.s[r.pos] == '!':
inc(r.pos)
loc.r = toRope(decode(r))
else:
loc.r = nil
if r.s[r.pos] == '?':
inc(r.pos)
loc.a = decodeInt(r)
else:
loc.a = 0
if r.s[r.pos] == '>': inc(r.pos)
else: InternalError(info, "decodeLoc " & r.s[r.pos])
proc decodeType(r: PRodReader, info: TLineInfo): PType =
var d: int
result = nil
if r.s[r.pos] == '[':
inc(r.pos)
if r.s[r.pos] == ']':
inc(r.pos)
return # nil type
new(result)
result.kind = TTypeKind(decodeInt(r))
if r.s[r.pos] == '+':
inc(r.pos)
result.id = decodeInt(r)
setId(result.id)
if debugIds: registerID(result)
else:
InternalError(info, "decodeType: no id")
IdTablePut(gTypeTable, result, result) # here this also
# avoids endless recursion for recursive type
if r.s[r.pos] == '(': result.n = decodeNode(r, UnknownLineInfo())
if r.s[r.pos] == '$':
inc(r.pos)
result.flags = cast[TTypeFlags](int32(decodeInt(r)))
if r.s[r.pos] == '?':
inc(r.pos)
result.callConv = TCallingConvention(decodeInt(r))
if r.s[r.pos] == '*':
inc(r.pos)
result.owner = rrGetSym(r, decodeInt(r), info)
if r.s[r.pos] == '&':
inc(r.pos)
result.sym = rrGetSym(r, decodeInt(r), info)
if r.s[r.pos] == '/':
inc(r.pos)
result.size = decodeInt(r)
else:
result.size = - 1
if r.s[r.pos] == '=':
inc(r.pos)
result.align = decodeInt(r)
else:
result.align = 2
if r.s[r.pos] == '@':
inc(r.pos)
result.containerID = decodeInt(r)
decodeLoc(r, result.loc, info)
while r.s[r.pos] == '^':
inc(r.pos)
if r.s[r.pos] == '(':
inc(r.pos)
if r.s[r.pos] == ')': inc(r.pos)
else: InternalError(info, "decodeType ^(" & r.s[r.pos])
addSon(result, nil)
else:
d = decodeInt(r)
addSon(result, rrGetType(r, d, info))
proc decodeLib(r: PRodReader): PLib =
result = nil
if r.s[r.pos] == '|':
new(result)
inc(r.pos)
result.kind = TLibKind(decodeInt(r))
if r.s[r.pos] != '|': InternalError("decodeLib: 1")
inc(r.pos)
result.name = toRope(decode(r))
if r.s[r.pos] != '|': InternalError("decodeLib: 2")
inc(r.pos)
result.path = decode(r)
proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
var
k: TSymKind
id: int
ident: PIdent
result = nil
if r.s[r.pos] == '{':
inc(r.pos)
if r.s[r.pos] == '}':
inc(r.pos)
return # nil sym
k = TSymKind(decodeInt(r))
if r.s[r.pos] == '+':
inc(r.pos)
id = decodeInt(r)
setId(id)
else:
InternalError(info, "decodeSym: no id")
if r.s[r.pos] == '&':
inc(r.pos)
ident = getIdent(decode(r))
else:
InternalError(info, "decodeSym: no ident")
result = PSym(IdTableGet(r.syms, id))
if result == nil:
new(result)
result.id = id
IdTablePut(r.syms, result, result)
if debugIds: registerID(result)
elif (result.id != id):
InternalError(info, "decodeSym: wrong id")
result.kind = k
result.name = ident # read the rest of the symbol description:
if r.s[r.pos] == '^':
inc(r.pos)
result.typ = rrGetType(r, decodeInt(r), info)
decodeLineInfo(r, result.info)
if r.s[r.pos] == '*':
inc(r.pos)
result.owner = rrGetSym(r, decodeInt(r), result.info)
if r.s[r.pos] == '$':
inc(r.pos)
result.flags = cast[TSymFlags](int32(decodeInt(r)))
if r.s[r.pos] == '@':
inc(r.pos)
result.magic = TMagic(decodeInt(r))
if r.s[r.pos] == '(': result.ast = decodeNode(r, result.info)
if r.s[r.pos] == '!':
inc(r.pos)
result.options = cast[TOptions](int32(decodeInt(r)))
else:
result.options = r.options
if r.s[r.pos] == '%':
inc(r.pos)
result.position = decodeInt(r)
else:
result.position = 0 # BUGFIX: this may have been misused as reader index!
if r.s[r.pos] == '`':
inc(r.pos)
result.offset = decodeInt(r)
else:
result.offset = - 1
decodeLoc(r, result.loc, result.info)
result.annex = decodeLib(r)
proc decodeInt(r: PRodReader): int =
# base 190 numbers
var
i: int
sign: int
i = r.pos
sign = - 1
assert(r.s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
if r.s[i] == '-':
inc(i)
sign = 1
result = 0
while true:
case r.s[i]
of '0'..'9': result = result * 190 - (ord(r.s[i]) - ord('0'))
of 'a'..'z': result = result * 190 - (ord(r.s[i]) - ord('a') + 10)
of 'A'..'Z': result = result * 190 - (ord(r.s[i]) - ord('A') + 36)
of '\x80'..'\xFF': result = result * 190 - (ord(r.s[i]) - 128 + 62)
else: break
inc(i)
result = result * sign
r.pos = i
proc decodeBInt(r: PRodReader): biggestInt =
var
i: int
sign: biggestInt
i = r.pos
sign = - 1
assert(r.s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
if r.s[i] == '-':
inc(i)
sign = 1
result = 0
while true:
case r.s[i]
of '0'..'9': result = result * 190 - (ord(r.s[i]) - ord('0'))
of 'a'..'z': result = result * 190 - (ord(r.s[i]) - ord('a') + 10)
of 'A'..'Z': result = result * 190 - (ord(r.s[i]) - ord('A') + 36)
of '\x80'..'\xFF': result = result * 190 - (ord(r.s[i]) - 128 + 62)
else: break
inc(i)
result = result * sign
r.pos = i
proc hexChar(c: char, xi: var int) =
case c
of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
of 'A'..'F': xi = (xi shl 4) or (ord(c) - ord('A') + 10)
else:
nil
proc decode(r: PRodReader): string =
var i, xi: int
i = r.pos
result = ""
while true:
case r.s[i]
of '\\':
inc(i, 3)
xi = 0
hexChar(r.s[i - 2], xi)
hexChar(r.s[i - 1], xi)
add(result, chr(xi))
of 'a'..'z', 'A'..'Z', '0'..'9', '_':
add(result, r.s[i])
inc(i)
else: break
r.pos = i
proc skipSection(r: PRodReader) =
var c: int
if r.s[r.pos] == ':':
while r.s[r.pos] > '\x0A': inc(r.pos)
elif r.s[r.pos] == '(':
c = 0 # count () pairs
inc(r.pos)
while true:
case r.s[r.pos]
of '\x0A':
inc(r.line)
of '(':
inc(c)
of ')':
if c == 0:
inc(r.pos)
break
elif c > 0:
dec(c)
of '\0':
break # end of file
else:
nil
inc(r.pos)
else:
InternalError("skipSection " & $(r.line))
proc rdWord(r: PRodReader): string =
result = ""
while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}:
add(result, r.s[r.pos])
inc(r.pos)
proc newStub(r: PRodReader, name: string, id: int): PSym =
new(result)
result.kind = skStub
result.id = id
result.name = getIdent(name)
result.position = r.readerIndex
setID(id) #MessageOut(result.name.s);
if debugIds: registerID(result)
proc processInterf(r: PRodReader, module: PSym) =
var
s: PSym
w: string
key: int
if r.interfIdx == 0: InternalError("processInterf")
r.pos = r.interfIdx
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
w = decode(r)
inc(r.pos)
key = decodeInt(r)
inc(r.pos) # #10
s = newStub(r, w, key)
s.owner = module
StrTableAdd(module.tab, s)
IdTablePut(r.syms, s, s)
proc processCompilerProcs(r: PRodReader, module: PSym) =
var
s: PSym
w: string
key: int
if r.compilerProcsIdx == 0: InternalError("processCompilerProcs")
r.pos = r.compilerProcsIdx
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
w = decode(r)
inc(r.pos)
key = decodeInt(r)
inc(r.pos) # #10
s = PSym(IdTableGet(r.syms, key))
if s == nil:
s = newStub(r, w, key)
s.owner = module
IdTablePut(r.syms, s, s)
StrTableAdd(rodCompilerProcs, s)
proc processIndex(r: PRodReader, idx: var TIndex) =
var key, val, tmp: int
inc(r.pos, 2) # skip "(\10"
inc(r.line)
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
tmp = decodeInt(r)
if r.s[r.pos] == ' ':
inc(r.pos)
key = idx.lastIdxKey + tmp
val = decodeInt(r) + idx.lastIdxVal
else:
key = idx.lastIdxKey + 1
val = tmp + idx.lastIdxVal
IITablePut(idx.tab, key, val)
idx.lastIdxKey = key
idx.lastIdxVal = val
setID(key) # ensure that this id will not be used
if r.s[r.pos] == '\x0A':
inc(r.pos)
inc(r.line)
if r.s[r.pos] == ')': inc(r.pos)
proc processRodFile(r: PRodReader, crc: TCrc32) =
var
section, w: string
d, L, inclCrc: int
while r.s[r.pos] != '\0':
section = rdWord(r)
if r.reason != rrNone:
break # no need to process this file further
if section == "CRC":
inc(r.pos) # skip ':'
if int(crc) != decodeInt(r): r.reason = rrCrcChange
elif section == "ID":
inc(r.pos) # skip ':'
r.moduleID = decodeInt(r)
setID(r.moduleID)
elif section == "OPTIONS":
inc(r.pos) # skip ':'
r.options = cast[TOptions](int32(decodeInt(r)))
if options.gOptions != r.options: r.reason = rrOptions
elif section == "DEFINES":
inc(r.pos) # skip ':'
d = 0
while r.s[r.pos] > '\x0A':
w = decode(r)
inc(d)
if not condsyms.isDefined(getIdent(w)):
r.reason = rrDefines #MessageOut('not defined, but should: ' + w);
if r.s[r.pos] == ' ': inc(r.pos)
if (d != countDefinedSymbols()): r.reason = rrDefines
elif section == "FILES":
inc(r.pos, 2) # skip "(\10"
inc(r.line)
L = 0
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
setlen(r.files, L + 1)
r.files[L] = decode(r)
inc(r.pos) # skip #10
inc(r.line)
inc(L)
if r.s[r.pos] == ')': inc(r.pos)
elif section == "INCLUDES":
inc(r.pos, 2) # skip "(\10"
inc(r.line)
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
w = r.files[decodeInt(r)]
inc(r.pos) # skip ' '
inclCrc = decodeInt(r)
if r.reason == rrNone:
if not ExistsFile(w) or (inclCrc != int(crcFromFile(w))):
r.reason = rrInclDeps
if r.s[r.pos] == '\x0A':
inc(r.pos)
inc(r.line)
if r.s[r.pos] == ')': inc(r.pos)
elif section == "DEPS":
inc(r.pos) # skip ':'
L = 0
while (r.s[r.pos] > '\x0A'):
setlen(r.modDeps, L + 1)
r.modDeps[L] = r.files[decodeInt(r)]
inc(L)
if r.s[r.pos] == ' ': inc(r.pos)
elif section == "INTERF":
r.interfIdx = r.pos + 2
skipSection(r)
elif section == "COMPILERPROCS":
r.compilerProcsIdx = r.pos + 2
skipSection(r)
elif section == "INDEX":
processIndex(r, r.index)
elif section == "IMPORTS":
processIndex(r, r.imports)
elif section == "CONVERTERS":
r.convertersIdx = r.pos + 1
skipSection(r)
elif section == "DATA":
r.dataIdx = r.pos + 2 # "(\10"
# We do not read the DATA section here! We read the needed objects on
# demand.
skipSection(r)
elif section == "INIT":
r.initIdx = r.pos + 2 # "(\10"
skipSection(r)
elif section == "CGEN":
r.cgenIdx = r.pos + 2
skipSection(r)
else:
MessageOut("skipping section: " & $(r.pos))
skipSection(r)
if r.s[r.pos] == '\x0A':
inc(r.pos)
inc(r.line)
proc newRodReader(modfilename: string, crc: TCrc32, readerIndex: int): PRodReader =
var
version: string
r: PRodReader
new(result)
result.files = @ []
result.modDeps = @ []
r = result
r.reason = rrNone
r.pos = 0
r.line = 1
r.readerIndex = readerIndex
r.filename = modfilename
InitIdTable(r.syms)
r.s = readFile(modfilename)
if startsWith(r.s, "NIM:"):
initIITable(r.index.tab)
initIITable(r.imports.tab) # looks like a ROD file
inc(r.pos, 4)
version = ""
while not (r.s[r.pos] in {'\0', '\x0A'}):
add(version, r.s[r.pos])
inc(r.pos)
if r.s[r.pos] == '\x0A': inc(r.pos)
if version == FileVersion:
# since ROD files are only for caching, no backwarts compability is
# needed
processRodFile(r, crc)
else:
result = nil
else:
result = nil
proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
var oldPos, d: int
result = PType(IdTableGet(gTypeTable, id))
if result == nil:
# load the type:
oldPos = r.pos
d = IITableGet(r.index.tab, id)
if d == invalidKey: InternalError(info, "rrGetType")
r.pos = d + r.dataIdx
result = decodeType(r, info)
r.pos = oldPos
type
TFileModuleRec{.final.} = object
filename*: string
reason*: TReasonForRecompile
rd*: PRodReader
crc*: TCrc32
TFileModuleMap = seq[TFileModuleRec]
var gMods: TFileModuleMap = @ []
proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
# all compiled modules
var oldPos: int
if rd.dataIdx == 0: InternalError(info, "dataIdx == 0")
oldPos = rd.pos
rd.pos = offset + rd.dataIdx
result = decodeSym(rd, info)
rd.pos = oldPos
proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
var
d, moduleID: int
rd: PRodReader
result = PSym(IdTableGet(r.syms, id))
if result == nil:
# load the symbol:
d = IITableGet(r.index.tab, id)
if d == invalidKey:
moduleID = IiTableGet(r.imports.tab, id)
if moduleID < 0:
InternalError(info,
"missing from both indexes: +" & ropeToStr(encodeInt(id))) #
# find
# the
# reader
# with
# the
# correct
# moduleID:
for i in countup(0, high(gMods)):
rd = gMods[i].rd
if (rd != nil):
if (rd.moduleID == moduleID):
d = IITableGet(rd.index.tab, id)
if d != invalidKey:
result = decodeSymSafePos(rd, d, info)
break
else:
InternalError(info, "rrGetSym: no reader found: +" &
ropeToStr(encodeInt(id)))
else:
#if IiTableGet(rd.index.tab, id) <> invalidKey then
# XXX expensive check!
#InternalError(info,
#'id found in other module: +' + ropeToStr(encodeInt(id)))
else:
# own symbol:
result = decodeSymSafePos(r, d, info)
if (result != nil) and (result.kind == skStub): loadStub(result)
proc loadInitSection(r: PRodReader): PNode =
var d, oldPos, p: int
if (r.initIdx == 0) or (r.dataIdx == 0): InternalError("loadInitSection")
oldPos = r.pos
r.pos = r.initIdx
result = newNode(nkStmtList)
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
d = decodeInt(r)
inc(r.pos) # #10
p = r.pos
r.pos = d + r.dataIdx
addSon(result, decodeNode(r, UnknownLineInfo()))
r.pos = p
r.pos = oldPos
proc loadConverters(r: PRodReader) =
var d: int
# We have to ensure that no exported converter is a stub anymore.
if (r.convertersIdx == 0) or (r.dataIdx == 0):
InternalError("importConverters")
r.pos = r.convertersIdx
while (r.s[r.pos] > '\x0A'):
d = decodeInt(r)
discard rrGetSym(r, d, UnknownLineInfo())
if r.s[r.pos] == ' ': inc(r.pos)
proc getModuleIdx(filename: string): int =
for i in countup(0, high(gMods)):
if sameFile(gMods[i].filename, filename):
return i
result = len(gMods)
setlen(gMods, result + 1)
proc checkDep(filename: string): TReasonForRecompile =
var
crc: TCrc32
r: PRodReader
rodfile: string
idx: int
res: TReasonForRecompile
idx = getModuleIdx(filename)
if gMods[idx].reason != rrEmpty:
# reason has already been computed for this module:
return gMods[idx].reason
crc = crcFromFile(filename)
gMods[idx].reason = rrNone # we need to set it here to avoid cycles
gMods[idx].filename = filename
gMods[idx].crc = crc
result = rrNone
r = nil
rodfile = toGeneratedFile(filename, RodExt)
if ExistsFile(rodfile):
r = newRodReader(rodfile, crc, idx)
if r == nil:
result = rrRodInvalid
else:
result = r.reason
if result == rrNone:
# check modules it depends on
# NOTE: we need to process the entire module graph so that no ID will
# be used twice! However, compilation speed does not suffer much from
# this, since results are cached.
res = checkDep(JoinPath(options.libpath, addFileExt("system", nimExt)))
if res != rrNone: result = rrModDeps
for i in countup(0, high(r.modDeps)):
res = checkDep(r.modDeps[i])
if res != rrNone:
result = rrModDeps #break // BUGFIX: cannot break here!
else:
result = rrRodDoesNotExist
if (result != rrNone) and (gVerbosity > 0):
MessageOut(`%`(reasonToFrmt[result], [filename]))
if (result != rrNone) or (optForceFullMake in gGlobalOptions):
# recompilation is necessary:
r = nil
gMods[idx].rd = r
gMods[idx].reason = result # now we know better
proc handleSymbolFile(module: PSym, filename: string): PRodReader =
var idx: int
if not (optSymbolFiles in gGlobalOptions):
module.id = getID()
return nil
discard checkDep(filename)
idx = getModuleIdx(filename)
if gMods[idx].reason == rrEmpty: InternalError("handleSymbolFile")
result = gMods[idx].rd
if result != nil:
module.id = result.moduleID
IdTablePut(result.syms, module, module)
processInterf(result, module)
processCompilerProcs(result, module)
loadConverters(result)
else:
module.id = getID()
proc GetCRC(filename: string): TCrc32 =
var idx: int
idx = getModuleIdx(filename)
result = gMods[idx].crc
proc loadStub(s: PSym) =
var
rd: PRodReader
d, theId: int
rs: PSym
if s.kind != skStub:
InternalError("loadStub") #MessageOut('loading stub: ' + s.name.s);
rd = gMods[s.position].rd
theId = s.id # used for later check
d = IITableGet(rd.index.tab, s.id)
if d == invalidKey: InternalError("loadStub: invalid key")
rs = decodeSymSafePos(rd, d, UnknownLineInfo())
if rs != s:
InternalError(rs.info, "loadStub: wrong symbol")
elif rs.id != theId:
InternalError(rs.info, "loadStub: wrong ID") #MessageOut('loaded stub: ' + s.name.s);
InitIdTable(gTypeTable)
InitStrTable(rodCompilerProcs)

457
rod/rodwrite.nim Executable file
View File

@@ -0,0 +1,457 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module is responsible for writing of rod files. Note that writing of
# rod files is a pass, reading of rod files is not! This is why reading and
# writing of rod files is split into two different modules.
import
os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
ropes, idents, crc, rodread, passes, importer
proc rodwritePass*(): TPass
# implementation
type
TRodWriter = object of TPassContext
module*: PSym
crc*: TCrc32
options*: TOptions
defines*: PRope
inclDeps*: PRope
modDeps*: PRope
interf*: PRope
compilerProcs*: PRope
index*, imports*: TIndex
converters*: PRope
init*: PRope
data*: PRope
filename*: string
sstack*: TSymSeq # a stack of symbols to process
tstack*: TTypeSeq # a stack of types to process
files*: TStringSeq
PRodWriter = ref TRodWriter
proc newRodWriter(modfilename: string, crc: TCrc32, module: PSym): PRodWriter
proc addModDep(w: PRodWriter, dep: string)
proc addInclDep(w: PRodWriter, dep: string)
proc addInterfaceSym(w: PRodWriter, s: PSym)
proc addStmt(w: PRodWriter, n: PNode)
proc writeRod(w: PRodWriter)
proc encodeStr(w: PRodWriter, s: string): PRope =
result = encode(s)
proc processStacks(w: PRodWriter)
proc getDefines(): PRope =
var
it: TTabIter
s: PSym
s = InitTabIter(it, gSymbols)
result = nil
while s != nil:
if s.position == 1:
if result != nil: app(result, " ")
app(result, s.name.s)
s = nextIter(it, gSymbols)
proc fileIdx(w: PRodWriter, filename: string): int =
for i in countup(0, high(w.files)):
if w.files[i] == filename:
return i
result = len(w.files)
setlen(w.files, result + 1)
w.files[result] = filename
proc newRodWriter(modfilename: string, crc: TCrc32, module: PSym): PRodWriter =
new(result)
result.sstack = @ []
result.tstack = @ []
InitIITable(result.index.tab)
InitIITable(result.imports.tab)
result.filename = modfilename
result.crc = crc
result.module = module
result.defines = getDefines()
result.options = options.gOptions
result.files = @ []
proc addModDep(w: PRodWriter, dep: string) =
if w.modDeps != nil: app(w.modDeps, " ")
app(w.modDeps, encodeInt(fileIdx(w, dep)))
const
rodNL = "\x0A"
proc addInclDep(w: PRodWriter, dep: string) =
app(w.inclDeps, encodeInt(fileIdx(w, dep)))
app(w.inclDeps, " ")
app(w.inclDeps, encodeInt(crcFromFile(dep)))
app(w.inclDeps, rodNL)
proc pushType(w: PRodWriter, t: PType) =
var L: int
# check so that the stack does not grow too large:
if IiTableGet(w.index.tab, t.id) == invalidKey:
L = len(w.tstack)
setlen(w.tstack, L + 1)
w.tstack[L] = t
proc pushSym(w: PRodWriter, s: PSym) =
var L: int
# check so that the stack does not grow too large:
if IiTableGet(w.index.tab, s.id) == invalidKey:
L = len(w.sstack)
setlen(w.sstack, L + 1)
w.sstack[L] = s
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode): PRope =
var f: TNodeFlags
if n == nil:
# nil nodes have to be stored too:
return toRope("()")
result = toRope("(")
app(result, encodeInt(ord(n.kind))) # we do not write comments for now
# Line information takes easily 20% or more of the filesize! Therefore we
# omit line information if it is the same as the father's line information:
if (finfo.fileIndex != n.info.fileIndex):
appf(result, "?$1,$2,$3", [encodeInt(n.info.col), encodeInt(n.info.line),
encodeInt(fileIdx(w, toFilename(n.info)))])
elif (finfo.line != n.info.line):
appf(result, "?$1,$2", [encodeInt(n.info.col), encodeInt(n.info.line)])
elif (finfo.col != n.info.col):
appf(result, "?$1", [encodeInt(n.info.col)]) # No need to output the file index, as this is the serialization of one
# file.
f = n.flags * PersistentNodeFlags
if f != {}: appf(result, "$$$1", [encodeInt(cast[int32](f))])
if n.typ != nil:
appf(result, "^$1", [encodeInt(n.typ.id)])
pushType(w, n.typ)
case n.kind
of nkCharLit..nkInt64Lit:
if n.intVal != 0: appf(result, "!$1", [encodeInt(n.intVal)])
of nkFloatLit..nkFloat64Lit:
if n.floatVal != 0.0: appf(result, "!$1", [encodeStr(w, $(n.floatVal))])
of nkStrLit..nkTripleStrLit:
if n.strVal != "": appf(result, "!$1", [encodeStr(w, n.strVal)])
of nkIdent:
appf(result, "!$1", [encodeStr(w, n.ident.s)])
of nkSym:
appf(result, "!$1", [encodeInt(n.sym.id)])
pushSym(w, n.sym)
else:
for i in countup(0, sonsLen(n) - 1):
app(result, encodeNode(w, n.info, n.sons[i]))
app(result, ")")
proc encodeLoc(w: PRodWriter, loc: TLoc): PRope =
result = nil
if loc.k != low(loc.k): app(result, encodeInt(ord(loc.k)))
if loc.s != low(loc.s): appf(result, "*$1", [encodeInt(ord(loc.s))])
if loc.flags != {}: appf(result, "$$$1", [encodeInt(cast[int32](loc.flags))])
if loc.t != nil:
appf(result, "^$1", [encodeInt(loc.t.id)])
pushType(w, loc.t)
if loc.r != nil: appf(result, "!$1", [encodeStr(w, ropeToStr(loc.r))])
if loc.a != 0: appf(result, "?$1", [encodeInt(loc.a)])
if result != nil: result = ropef("<$1>", [result])
proc encodeType(w: PRodWriter, t: PType): PRope =
if t == nil:
# nil nodes have to be stored too:
return toRope("[]")
result = nil
if t.kind == tyForward: InternalError("encodeType: tyForward")
app(result, encodeInt(ord(t.kind)))
appf(result, "+$1", [encodeInt(t.id)])
if t.n != nil: app(result, encodeNode(w, UnknownLineInfo(), t.n))
if t.flags != {}: appf(result, "$$$1", [encodeInt(cast[int32](t.flags))])
if t.callConv != low(t.callConv):
appf(result, "?$1", [encodeInt(ord(t.callConv))])
if t.owner != nil:
appf(result, "*$1", [encodeInt(t.owner.id)])
pushSym(w, t.owner)
if t.sym != nil:
appf(result, "&$1", [encodeInt(t.sym.id)])
pushSym(w, t.sym)
if t.size != - 1: appf(result, "/$1", [encodeInt(t.size)])
if t.align != 2: appf(result, "=$1", [encodeInt(t.align)])
if t.containerID != 0: appf(result, "@$1", [encodeInt(t.containerID)])
app(result, encodeLoc(w, t.loc))
for i in countup(0, sonsLen(t) - 1):
if t.sons[i] == nil:
app(result, "^()")
else:
appf(result, "^$1", [encodeInt(t.sons[i].id)])
pushType(w, t.sons[i])
proc encodeLib(w: PRodWriter, lib: PLib): PRope =
result = nil
appf(result, "|$1", [encodeInt(ord(lib.kind))])
appf(result, "|$1", [encodeStr(w, ropeToStr(lib.name))])
appf(result, "|$1", [encodeStr(w, lib.path)])
proc encodeSym(w: PRodWriter, s: PSym): PRope =
var
codeAst: PNode
col, line: PRope
codeAst = nil
if s == nil:
# nil nodes have to be stored too:
return toRope("{}")
result = nil
app(result, encodeInt(ord(s.kind)))
appf(result, "+$1", [encodeInt(s.id)])
appf(result, "&$1", [encodeStr(w, s.name.s)])
if s.typ != nil:
appf(result, "^$1", [encodeInt(s.typ.id)])
pushType(w, s.typ)
if s.info.col == int16(- 1): col = nil
else: col = encodeInt(s.info.col)
if s.info.line == int16(- 1): line = nil
else: line = encodeInt(s.info.line)
appf(result, "?$1,$2,$3",
[col, line, encodeInt(fileIdx(w, toFilename(s.info)))])
if s.owner != nil:
appf(result, "*$1", [encodeInt(s.owner.id)])
pushSym(w, s.owner)
if s.flags != {}: appf(result, "$$$1", [encodeInt(cast[int32](s.flags))])
if s.magic != mNone: appf(result, "@$1", [encodeInt(ord(s.magic))])
if (s.ast != nil):
if not astNeeded(s):
codeAst = s.ast.sons[codePos]
s.ast.sons[codePos] = nil
app(result, encodeNode(w, s.info, s.ast))
if codeAst != nil:
s.ast.sons[codePos] = codeAst
if s.options != w.options:
appf(result, "!$1", [encodeInt(cast[int32](s.options))])
if s.position != 0: appf(result, "%$1", [encodeInt(s.position)])
if s.offset != - 1: appf(result, "`$1", [encodeInt(s.offset)])
app(result, encodeLoc(w, s.loc))
if s.annex != nil: app(result, encodeLib(w, s.annex))
proc addToIndex(w: var TIndex, key, val: int) =
if key - w.lastIdxKey == 1:
# we do not store a key-diff of 1 to safe space
app(w.r, encodeInt(val - w.lastIdxVal))
app(w.r, rodNL)
else:
appf(w.r, "$1 $2" & rodNL,
[encodeInt(key - w.lastIdxKey), encodeInt(val - w.lastIdxVal)])
w.lastIdxKey = key
w.lastIdxVal = val
IiTablePut(w.tab, key, val)
var debugWritten: TIntSet
proc symStack(w: PRodWriter) =
var
i, L: int
s, m: PSym
i = 0
while i < len(w.sstack):
s = w.sstack[i]
if IiTableGet(w.index.tab, s.id) == invalidKey:
m = getModule(s)
if m == nil: InternalError("symStack: module nil: " & s.name.s)
if (m.id == w.module.id) or (sfFromGeneric in s.flags):
# put definition in here
L = ropeLen(w.data)
addToIndex(w.index, s.id, L) #intSetIncl(debugWritten, s.id);
app(w.data, encodeSym(w, s))
app(w.data, rodNL)
if sfInInterface in s.flags:
appf(w.interf, "$1 $2" & rodNL, [encode(s.name.s), encodeInt(s.id)])
if sfCompilerProc in s.flags:
appf(w.compilerProcs, "$1 $2" & rodNL,
[encode(s.name.s), encodeInt(s.id)])
if s.kind == skConverter:
if w.converters != nil: app(w.converters, " ")
app(w.converters, encodeInt(s.id))
elif IiTableGet(w.imports.tab, s.id) == invalidKey:
addToIndex(w.imports, s.id, m.id) #if not IntSetContains(debugWritten, s.id) then begin
# MessageOut(w.filename);
# debug(s.owner);
# debug(s);
# InternalError('BUG!!!!');
#end
inc(i)
setlen(w.sstack, 0)
proc typeStack(w: PRodWriter) =
var i, L: int
i = 0
while i < len(w.tstack):
if IiTableGet(w.index.tab, w.tstack[i].id) == invalidKey:
L = ropeLen(w.data)
addToIndex(w.index, w.tstack[i].id, L)
app(w.data, encodeType(w, w.tstack[i]))
app(w.data, rodNL)
inc(i)
setlen(w.tstack, 0)
proc processStacks(w: PRodWriter) =
while (len(w.tstack) > 0) or (len(w.sstack) > 0):
symStack(w)
typeStack(w)
proc rawAddInterfaceSym(w: PRodWriter, s: PSym) =
pushSym(w, s)
processStacks(w)
proc addInterfaceSym(w: PRodWriter, s: PSym) =
if w == nil: return
if {sfInInterface, sfCompilerProc} * s.flags != {}:
rawAddInterfaceSym(w, s)
proc addStmt(w: PRodWriter, n: PNode) =
app(w.init, encodeInt(ropeLen(w.data)))
app(w.init, rodNL)
app(w.data, encodeNode(w, UnknownLineInfo(), n))
app(w.data, rodNL)
processStacks(w)
proc writeRod(w: PRodWriter) =
var content: PRope
processStacks(w) # write header:
content = toRope("NIM:")
app(content, toRope(FileVersion))
app(content, rodNL)
app(content, toRope("ID:"))
app(content, encodeInt(w.module.id))
app(content, rodNL)
app(content, toRope("CRC:"))
app(content, encodeInt(w.crc))
app(content, rodNL)
app(content, toRope("OPTIONS:"))
app(content, encodeInt(cast[int32](w.options)))
app(content, rodNL)
app(content, toRope("DEFINES:"))
app(content, w.defines)
app(content, rodNL)
app(content, toRope("FILES(" & rodNL))
for i in countup(0, high(w.files)):
app(content, encode(w.files[i]))
app(content, rodNL)
app(content, toRope(')' & rodNL))
app(content, toRope("INCLUDES(" & rodNL))
app(content, w.inclDeps)
app(content, toRope(')' & rodNL))
app(content, toRope("DEPS:"))
app(content, w.modDeps)
app(content, rodNL)
app(content, toRope("INTERF(" & rodNL))
app(content, w.interf)
app(content, toRope(')' & rodNL))
app(content, toRope("COMPILERPROCS(" & rodNL))
app(content, w.compilerProcs)
app(content, toRope(')' & rodNL))
app(content, toRope("INDEX(" & rodNL))
app(content, w.index.r)
app(content, toRope(')' & rodNL))
app(content, toRope("IMPORTS(" & rodNL))
app(content, w.imports.r)
app(content, toRope(')' & rodNL))
app(content, toRope("CONVERTERS:"))
app(content, w.converters)
app(content, toRope(rodNL))
app(content, toRope("INIT(" & rodNL))
app(content, w.init)
app(content, toRope(')' & rodNL))
app(content, toRope("DATA(" & rodNL))
app(content, w.data)
app(content, toRope(')' & rodNL)) #MessageOut('interf ' + ToString(ropeLen(w.interf)));
#MessageOut('index ' + ToString(ropeLen(w.indexRope)));
#MessageOut('init ' + ToString(ropeLen(w.init)));
#MessageOut('data ' + ToString(ropeLen(w.data)));
writeRope(content, completeGeneratedFilePath(changeFileExt(w.filename, "rod")))
proc process(c: PPassContext, n: PNode): PNode =
var
w: PRodWriter
a: PNode
s: PSym
result = n
if c == nil: return
w = PRodWriter(c)
case n.kind
of nkStmtList:
for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i])
of nkTemplateDef, nkMacroDef:
s = n.sons[namePos].sym
addInterfaceSym(w, s)
of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef:
s = n.sons[namePos].sym
if s == nil: InternalError(n.info, "rodwrite.process")
if (n.sons[codePos] != nil) or (s.magic != mNone) or
not (sfForward in s.flags):
addInterfaceSym(w, s)
of nkVarSection:
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.kind != nkIdentDefs: InternalError(a.info, "rodwrite.process")
addInterfaceSym(w, a.sons[0].sym)
of nkConstSection:
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.kind != nkConstDef: InternalError(a.info, "rodwrite.process")
addInterfaceSym(w, a.sons[0].sym)
of nkTypeSection:
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.sons[0].kind != nkSym: InternalError(a.info, "rodwrite.process")
s = a.sons[0].sym
addInterfaceSym(w, s) # this takes care of enum fields too
# Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum
# type aliasing! Otherwise the same enum symbol would be included
# several times!
#
# if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin
# a := s.typ.n;
# for j := 0 to sonsLen(a)-1 do
# addInterfaceSym(w, a.sons[j].sym);
# end
of nkImportStmt:
for i in countup(0, sonsLen(n) - 1): addModDep(w, getModuleFile(n.sons[i]))
addStmt(w, n)
of nkFromStmt:
addModDep(w, getModuleFile(n.sons[0]))
addStmt(w, n)
of nkIncludeStmt:
for i in countup(0, sonsLen(n) - 1): addInclDep(w, getModuleFile(n.sons[i]))
of nkPragma:
addStmt(w, n)
else:
nil
proc myOpen(module: PSym, filename: string): PPassContext =
var w: PRodWriter
if module.id < 0: InternalError("rodwrite: module ID not set")
w = newRodWriter(filename, rodread.GetCRC(filename), module)
rawAddInterfaceSym(w, module)
result = w
proc myClose(c: PPassContext, n: PNode): PNode =
var w: PRodWriter
w = PRodWriter(c)
writeRod(w)
result = n
proc rodwritePass(): TPass =
initPass(result)
if optSymbolFiles in gGlobalOptions:
result.open = myOpen
result.close = myClose
result.process = process
IntSetInit(debugWritten)

497
rod/ropes.nim Executable file
View File

@@ -0,0 +1,497 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Ropes for the C code generator
#
# Ropes are a data structure that represents a very long string
# efficiently; especially concatenation is done in O(1) instead of O(N).
# Ropes make use a lazy evaluation: They are essentially concatenation
# trees that are only flattened when converting to a native Nimrod
# string or when written to disk. The empty string is represented by a
# nil pointer.
# A little picture makes everything clear:
#
# "this string" & " is internally " & "represented as"
#
# con -- inner nodes do not contain raw data
# / \
# / \
# / \
# con "represented as"
# / \
# / \
# / \
# / \
# / \
#"this string" " is internally "
#
# Note that this is the same as:
# "this string" & (" is internally " & "represented as")
#
# con
# / \
# / \
# / \
# "this string" con
# / \
# / \
# / \
# / \
# / \
#" is internally " "represented as"
#
# The 'con' operator is associative! This does not matter however for
# the algorithms we use for ropes.
#
# Note that the left and right pointers are not needed for leafs.
# Leafs have relatively high memory overhead (~30 bytes on a 32
# bit machines) and we produce many of them. This is why we cache and
# share leafs accross different rope trees.
# To cache them they are inserted in another tree, a splay tree for best
# performance. But for the caching tree we use the leafs' left and right
# pointers.
#
import
msgs, strutils, platform, nhashes, crc
const
CacheLeafs* = true
countCacheMisses* = False # see what our little optimization gives
type
TFormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments copy the format strings
# though it is not necessary)
PRope* = ref TRope
TRope*{.acyclic.} = object of TObject # the empty rope is represented by nil to safe space
left*, right*: PRope
length*: int
data*: string # != nil if a leaf
TRopeSeq* = seq[PRope]
proc con*(a, b: PRope): PRope
proc con*(a: PRope, b: string): PRope
proc con*(a: string, b: PRope): PRope
proc con*(a: openarray[PRope]): PRope
proc app*(a: var PRope, b: PRope)
proc app*(a: var PRope, b: string)
proc prepend*(a: var PRope, b: PRope)
proc toRope*(s: string): PRope
proc toRopeF*(r: BiggestFloat): PRope
proc toRope*(i: BiggestInt): PRope
proc ropeLen*(a: PRope): int
proc WriteRope*(head: PRope, filename: string)
proc writeRopeIfNotEqual*(r: PRope, filename: string): bool
proc ropeToStr*(p: PRope): string
proc ropef*(frmt: TFormatStr, args: openarray[PRope]): PRope
proc appf*(c: var PRope, frmt: TFormatStr, args: openarray[PRope])
proc getCacheStats*(): string
proc RopeEqualsFile*(r: PRope, f: string): bool
# returns true if the rope r is the same as the contents of file f
proc RopeInvariant*(r: PRope): bool
# exported for debugging
# implementation
proc ropeLen(a: PRope): int =
if a == nil: result = 0
else: result = a.length
proc newRope(data: string = nil): PRope =
new(result)
if data != nil:
result.length = len(data)
result.data = data
var
cache: PRope # the root of the cache tree
misses, hits: int
N: PRope # dummy rope needed for splay algorithm
proc getCacheStats(): string =
if hits + misses != 0:
result = "Misses: " & $(misses) & " total: " & $(hits + misses) & " quot: " &
$(toFloat(misses) / toFloat(hits + misses))
else:
result = ""
proc splay(s: string, tree: PRope, cmpres: var int): PRope =
var
le, r, y, t: PRope
c: int
t = tree
N.left = nil
N.right = nil # reset to nil
le = N
r = N
while true:
c = cmp(s, t.data)
if c < 0:
if (t.left != nil) and (s < t.left.data):
y = t.left
t.left = y.right
y.right = t
t = y
if t.left == nil: break
r.left = t
r = t
t = t.left
elif c > 0:
if (t.right != nil) and (s > t.right.data):
y = t.right
t.right = y.left
y.left = t
t = y
if t.right == nil: break
le.right = t
le = t
t = t.right
else:
break
cmpres = c
le.right = t.left
r.left = t.right
t.left = N.right
t.right = N.left
result = t
proc insertInCache(s: string, tree: PRope): PRope =
# Insert i into the tree t, unless it's already there.
# Return a pointer to the resulting tree.
var
t: PRope
cmp: int
t = tree
if t == nil:
result = newRope(s)
if countCacheMisses: inc(misses)
return
t = splay(s, t, cmp)
if cmp == 0:
# We get here if it's already in the Tree
# Don't add it again
result = t
if countCacheMisses: inc(hits)
else:
if countCacheMisses: inc(misses)
result = newRope(s)
if cmp < 0:
result.left = t.left
result.right = t
t.left = nil
else:
# i > t.item:
result.right = t.right
result.left = t
t.right = nil
proc RopeInvariant(r: PRope): bool =
if r == nil:
result = true
else:
result = true #
# if r.data <> snil then
# result := true
# else begin
# result := (r.left <> nil) and (r.right <> nil);
# if result then result := ropeInvariant(r.left);
# if result then result := ropeInvariant(r.right);
# end
proc toRope(s: string): PRope =
if s == "":
result = nil
elif cacheLeafs:
result = insertInCache(s, cache)
cache = result
else:
result = newRope(s)
assert(RopeInvariant(result))
proc RopeSeqInsert(rs: var TRopeSeq, r: PRope, at: Natural) =
var length: int
length = len(rs)
if at > length:
setlen(rs, at + 1)
else:
setlen(rs, length + 1) # move old rope elements:
for i in countdown(length, at + 1):
rs[i] = rs[i - 1] # this is correct, I used pen and paper to validate it
rs[at] = r
proc con(a, b: PRope): PRope =
assert(RopeInvariant(a))
assert(RopeInvariant(b))
if a == nil:
result = b
elif b == nil:
result = a
else:
result = newRope()
result.length = a.length + b.length
result.left = a
result.right = b
assert(RopeInvariant(result))
proc con(a: PRope, b: string): PRope =
var r: PRope
assert(RopeInvariant(a))
if b == "":
result = a
else:
r = toRope(b)
if a == nil:
result = r
else:
result = newRope()
result.length = a.length + r.length
result.left = a
result.right = r
assert(RopeInvariant(result))
proc con(a: string, b: PRope): PRope =
var r: PRope
assert(RopeInvariant(b))
if a == "":
result = b
else:
r = toRope(a)
if b == nil:
result = r
else:
result = newRope()
result.length = b.length + r.length
result.left = r
result.right = b
assert(RopeInvariant(result))
proc con(a: openarray[PRope]): PRope =
result = nil
for i in countup(0, high(a)): result = con(result, a[i])
assert(RopeInvariant(result))
proc toRope(i: BiggestInt): PRope =
result = toRope($(i))
proc toRopeF(r: BiggestFloat): PRope =
result = toRope($(r))
proc app(a: var PRope, b: PRope) =
a = con(a, b)
assert(RopeInvariant(a))
proc app(a: var PRope, b: string) =
a = con(a, b)
assert(RopeInvariant(a))
proc prepend(a: var PRope, b: PRope) =
a = con(b, a)
assert(RopeInvariant(a))
proc InitStack(stack: var TRopeSeq) =
stack = @ []
proc push(stack: var TRopeSeq, r: PRope) =
var length: int
length = len(stack)
setlen(stack, length + 1)
stack[length] = r
proc pop(stack: var TRopeSeq): PRope =
var length: int
length = len(stack)
result = stack[length - 1]
setlen(stack, length - 1)
proc WriteRopeRec(f: var tfile, c: PRope) =
assert(RopeInvariant(c))
if c == nil: return
if (c.data != nil):
write(f, c.data)
else:
writeRopeRec(f, c.left)
writeRopeRec(f, c.right)
proc newWriteRopeRec(f: var tfile, c: PRope) =
var
stack: TRopeSeq
it: PRope
assert(RopeInvariant(c))
initStack(stack)
push(stack, c)
while len(stack) > 0:
it = pop(stack)
while it.data == nil:
push(stack, it.right)
it = it.left
assert(it != nil)
assert(it.data != nil)
write(f, it.data)
proc WriteRope(head: PRope, filename: string) =
var f: tfile # we use a textfile for automatic buffer handling
if open(f, filename, fmWrite):
if head != nil: newWriteRopeRec(f, head)
close(f)
else:
rawMessage(errCannotOpenFile, filename)
proc recRopeToStr(result: var string, resultLen: var int, p: PRope) =
if p == nil:
return # do not add to result
if (p.data == nil):
recRopeToStr(result, resultLen, p.left)
recRopeToStr(result, resultLen, p.right)
else:
CopyMem(addr(result[resultLen + 0]), addr(p.data[0]), p.length)
Inc(resultLen, p.length)
assert(resultLen <= len(result))
proc newRecRopeToStr(result: var string, resultLen: var int, r: PRope) =
var
stack: TRopeSeq
it: PRope
initStack(stack)
push(stack, r)
while len(stack) > 0:
it = pop(stack)
while it.data == nil:
push(stack, it.right)
it = it.left
assert(it.data != nil)
CopyMem(addr(result[resultLen + 0]), addr(it.data[0]), it.length)
Inc(resultLen, it.length)
assert(resultLen <= len(result))
proc ropeToStr(p: PRope): string =
var resultLen: int
assert(RopeInvariant(p))
if p == nil:
result = ""
else:
result = newString(p.length)
resultLen = 0
newRecRopeToStr(result, resultLen, p)
proc ropef(frmt: TFormatStr, args: openarray[PRope]): PRope =
var i, j, length, start, num: int
i = 0
length = len(frmt)
result = nil
num = 0
while i <= length + 0 - 1:
if frmt[i] == '$':
inc(i) # skip '$'
case frmt[i]
of '$':
app(result, "$")
inc(i)
of '#':
inc(i)
app(result, args[num])
inc(num)
of '0'..'9':
j = 0
while true:
j = (j * 10) + Ord(frmt[i]) - ord('0')
inc(i)
if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
num = j
if j > high(args) + 1:
internalError("ropes: invalid format string $" & $(j))
app(result, args[j - 1])
of 'N', 'n':
app(result, tnl)
inc(i)
else: InternalError("ropes: invalid format string $" & frmt[i])
start = i
while (i <= length + 0 - 1):
if (frmt[i] != '$'): inc(i)
else: break
if i - 1 >= start:
app(result, copy(frmt, start, i - 1))
assert(RopeInvariant(result))
proc appf(c: var PRope, frmt: TFormatStr, args: openarray[PRope]) =
app(c, ropef(frmt, args))
const
bufSize = 1024 # 1 KB is reasonable
proc auxRopeEqualsFile(r: PRope, bin: var tfile, buf: Pointer): bool =
var readBytes: int
if (r.data != nil):
if r.length > bufSize:
internalError("ropes: token too long")
readBytes = readBuffer(bin, buf, r.length)
result = (readBytes == r.length) and
equalMem(buf, addr(r.data[0]), r.length) # BUGFIX
else:
result = auxRopeEqualsFile(r.left, bin, buf)
if result: result = auxRopeEqualsFile(r.right, bin, buf)
proc RopeEqualsFile(r: PRope, f: string): bool =
var
bin: tfile
buf: Pointer
result = open(bin, f)
if not result:
return # not equal if file does not exist
buf = alloc(BufSize)
result = auxRopeEqualsFile(r, bin, buf)
if result:
result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file?
dealloc(buf)
close(bin)
proc crcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 =
if r.data != nil:
result = startVal
for i in countup(0, len(r.data) + 0 - 1):
result = updateCrc32(r.data[i], result)
else:
result = crcFromRopeAux(r.left, startVal)
result = crcFromRopeAux(r.right, result)
proc newCrcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 =
var
stack: TRopeSeq
it: PRope
L, i: int
initStack(stack)
push(stack, r)
result = startVal
while len(stack) > 0:
it = pop(stack)
while it.data == nil:
push(stack, it.right)
it = it.left
assert(it.data != nil)
i = 0
L = len(it.data) + 0
while i < L:
result = updateCrc32(it.data[i], result)
inc(i)
proc crcFromRope(r: PRope): TCrc32 =
result = newCrcFromRopeAux(r, initCrc32)
proc writeRopeIfNotEqual(r: PRope, filename: string): bool =
# returns true if overwritten
var c: TCrc32
c = crcFromFile(filename)
if c != crcFromRope(r):
writeRope(r, filename)
result = true
else:
result = false
new(N)
# init dummy node for splay algorithm

1680
rod/rst.nim Executable file

File diff suppressed because it is too large Load Diff

789
rod/scanner.nim Executable file
View File

@@ -0,0 +1,789 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This scanner is handwritten for efficiency. I used an elegant buffering
# scheme which I have not seen anywhere else:
# We guarantee that a whole line is in the buffer. Thus only when scanning
# the \n or \r character we have to check wether we need to read in the next
# chunk. (\n or \r already need special handling for incrementing the line
# counter; choosing both \n and \r allows the scanner to properly read Unix,
# DOS or Macintosh text files, even when it is not the native format.
import
nhashes, options, msgs, strutils, platform, idents, lexbase, llstream,
wordrecg
const
MaxLineLength* = 80 # lines longer than this lead to a warning
numChars*: TCharSet = {'0'..'9', 'a'..'z', 'A'..'Z'}
SymChars*: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
SymStartChars*: TCharSet = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
OpChars*: TCharSet = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
'|', '=', '%', '&', '$', '@', '~', '\x80'..'\xFF'}
type
TTokType* = enum
tkInvalid, tkEof, # order is important here!
tkSymbol, # keywords:
#[[[cog
#from string import split, capitalize
#keywords = split(open("data/keywords.txt").read())
#idents = ""
#strings = ""
#i = 1
#for k in keywords:
# idents = idents + "tk" + capitalize(k) + ", "
# strings = strings + "'" + k + "', "
# if i % 4 == 0:
# idents = idents + "\n"
# strings = strings + "\n"
# i = i + 1
#cog.out(idents)
#]]]
tkAddr, tkAnd, tkAs, tkAsm, tkBind, tkBlock, tkBreak, tkCase, tkCast,
tkConst, tkContinue, tkConverter, tkDiscard, tkDistinct, tkDiv, tkElif,
tkElse, tkEnd, tkEnum, tkExcept, tkFinally, tkFor, tkFrom, tkGeneric, tkIf,
tkImplies, tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda,
tkMacro, tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr,
tkOut, tkProc, tkPtr, tkRaise, tkRef, tkReturn, tkShl, tkShr, tkTemplate,
tkTry, tkTuple, tkType, tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor, tkYield, #[[[end]]]
tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit,
tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit,
tkCallRStrLit, tkCallTripleStrLit, tkCharLit, tkParLe, tkParRi, tkBracketLe,
tkBracketRi, tkCurlyLe, tkCurlyRi, tkBracketDotLe, tkBracketDotRi, # [. and .]
tkCurlyDotLe, tkCurlyDotRi, # {. and .}
tkParDotLe, tkParDotRi, # (. and .)
tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr,
tkComment, tkAccent, tkInd, tkSad, tkDed, # pseudo token types used by the source renderers:
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
TTokTypes* = set[TTokType]
const
tokKeywordLow* = succ(tkSymbol)
tokKeywordHigh* = pred(tkIntLit)
tokOperators*: TTokTypes = {tkOpr, tkSymbol, tkBracketLe, tkBracketRi, tkIn,
tkIs, tkIsNot, tkEquals, tkDot, tkHat, tkNot, tkAnd, tkOr, tkXor, tkShl,
tkShr, tkDiv, tkMod, tkNotIn}
TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]", "tkSymbol", #[[[cog
#cog.out(strings)
#]]]
"addr", "and", "as", "asm", "bind", "block", "break", "case", "cast",
"const", "continue", "converter", "discard", "distinct", "div", "elif",
"else", "end", "enum", "except", "finally", "for", "from", "generic", "if",
"implies", "import", "in", "include", "is", "isnot", "iterator", "lambda",
"macro", "method", "mod", "nil", "not", "notin", "object", "of", "or",
"out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "template",
"try", "tuple", "type", "var", "when", "while", "with", "without", "xor", "yield", #[[[end]]]
"tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit",
"tkFloatLit", "tkFloat32Lit", "tkFloat64Lit", "tkStrLit", "tkRStrLit",
"tkTripleStrLit", "tkCallRStrLit", "tkCallTripleStrLit", "tkCharLit", "(",
")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)", ",", ";", ":",
"=", ".", "..", "^", "tkOpr", "tkComment", "`", "[new indentation]",
"[same indentation]", "[dedentation]", "tkSpaces", "tkInfixOpr",
"tkPrefixOpr", "tkPostfixOpr"]
type
TNumericalBase* = enum
base10, # base10 is listed as the first element,
# so that it is the correct default value
base2, base8, base16
PToken* = ref TToken
TToken* = object # a Nimrod token
tokType*: TTokType # the type of the token
indent*: int # the indentation; only valid if tokType = tkIndent
ident*: PIdent # the parsed identifier
iNumber*: BiggestInt # the parsed integer literal
fNumber*: BiggestFloat # the parsed floating point literal
base*: TNumericalBase # the numerical base; only valid for int
# or float literals
literal*: string # the parsed (string) literal; and
# documentation comments are here too
next*: PToken # next token; can be used for arbitrary look-ahead
PLexer* = ref TLexer
TLexer* = object of TBaseLexer
filename*: string
indentStack*: seq[int] # the indentation stack
dedent*: int # counter for DED token generation
indentAhead*: int # if > 0 an indendation has already been read
# this is needed because scanning comments
# needs so much look-ahead
var gLinesCompiled*: int
proc pushInd*(L: var TLexer, indent: int)
# all lines that have been compiled
proc popInd*(L: var TLexer)
proc isKeyword*(kind: TTokType): bool
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream)
proc rawGetTok*(L: var TLexer, tok: var TToken)
# reads in the next token into tok and skips it
proc getColumn*(L: TLexer): int
proc getLineInfo*(L: TLexer): TLineInfo
proc closeLexer*(lex: var TLexer)
proc PrintTok*(tok: PToken)
proc tokToStr*(tok: PToken): string
# auxiliary functions:
proc lexMessage*(L: TLexer, msg: TMsgKind, arg: string = "")
# the Pascal scanner uses this too:
proc fillToken*(L: var TToken)
# implementation
proc isKeyword(kind: TTokType): bool =
result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
proc pushInd(L: var TLexer, indent: int) =
var length: int
length = len(L.indentStack)
setlen(L.indentStack, length + 1)
if (indent > L.indentStack[length - 1]):
L.indentstack[length] = indent
else:
InternalError("pushInd") #writeln('push indent ', indent);
proc popInd(L: var TLexer) =
var length: int
length = len(L.indentStack)
setlen(L.indentStack, length - 1)
proc findIdent(L: TLexer, indent: int): bool =
for i in countdown(len(L.indentStack) - 1, 0):
if L.indentStack[i] == indent:
return true
result = false
proc tokToStr(tok: PToken): string =
case tok.tokType
of tkIntLit..tkInt64Lit:
result = $(tok.iNumber)
of tkFloatLit..tkFloat64Lit:
result = $(tok.fNumber)
of tkInvalid, tkStrLit..tkCharLit, tkComment:
result = tok.literal
of tkParLe..tkColon, tkEof, tkInd, tkSad, tkDed, tkAccent:
result = tokTypeToStr[tok.tokType]
else:
if (tok.ident != nil):
result = tok.ident.s
else:
InternalError("tokToStr")
result = ""
proc PrintTok(tok: PToken) =
write(stdout, TokTypeToStr[tok.tokType])
write(stdout, " ")
writeln(stdout, tokToStr(tok))
var dummyIdent: PIdent
proc fillToken(L: var TToken) =
L.TokType = tkInvalid
L.iNumber = 0
L.Indent = 0
L.literal = ""
L.fNumber = 0.0
L.base = base10
L.ident = dummyIdent # this prevents many bugs!
proc openLexer(lex: var TLexer, filename: string, inputstream: PLLStream) =
openBaseLexer(lex, inputstream)
lex.indentStack = @ [0]
lex.filename = filename
lex.indentAhead = - 1
proc closeLexer(lex: var TLexer) =
inc(gLinesCompiled, lex.LineNumber)
closeBaseLexer(lex)
proc getColumn(L: TLexer): int =
result = getColNumber(L, L.bufPos)
proc getLineInfo(L: TLexer): TLineInfo =
result = newLineInfo(L.filename, L.linenumber, getColNumber(L, L.bufpos))
proc lexMessage(L: TLexer, msg: TMsgKind, arg: string = "") =
msgs.liMessage(getLineInfo(L), msg, arg)
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg: string = "") =
var info: TLineInfo
info = newLineInfo(L.filename, L.linenumber, pos - L.lineStart)
msgs.liMessage(info, msg, arg)
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) =
# matches ([chars]_)*
var
pos: int
buf: cstring
pos = L.bufpos # use registers for pos, buf
buf = L.buf
while true:
if buf[pos] in chars:
add(tok.literal, buf[pos])
Inc(pos)
else:
break
if buf[pos] == '_':
add(tok.literal, '_')
Inc(pos)
L.bufPos = pos
proc matchTwoChars(L: TLexer, first: Char, second: TCharSet): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in Second)
proc isFloatLiteral(s: string): bool =
for i in countup(0, len(s) + 0 - 1):
if s[i] in {'.', 'e', 'E'}:
return true
result = false
proc GetNumber(L: var TLexer): TToken =
var
pos, endpos: int
xi: biggestInt
# get the base:
result.tokType = tkIntLit # int literal until we know better
result.literal = ""
result.base = base10 # BUGFIX
pos = L.bufpos # make sure the literal is correct for error messages:
matchUnderscoreChars(L, result, {'A'..'Z', 'a'..'z', '0'..'9'})
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
add(result.literal, '.')
inc(L.bufpos) #matchUnderscoreChars(L, result, ['A'..'Z', 'a'..'z', '0'..'9'])
matchUnderscoreChars(L, result, {'0'..'9'})
if L.buf[L.bufpos] in {'e', 'E'}:
add(result.literal, 'e')
inc(L.bufpos)
if L.buf[L.bufpos] in {'+', '-'}:
add(result.literal, L.buf[L.bufpos])
inc(L.bufpos)
matchUnderscoreChars(L, result, {'0'..'9'})
endpos = L.bufpos
if L.buf[endpos] == '\'':
#matchUnderscoreChars(L, result, ['''', 'f', 'F', 'i', 'I', '0'..'9']);
inc(endpos)
L.bufpos = pos # restore position
case L.buf[endpos]
of 'f', 'F':
inc(endpos)
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
result.tokType = tkFloat64Lit
inc(endpos, 2)
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
result.tokType = tkFloat32Lit
inc(endpos, 2)
else:
lexMessage(L, errInvalidNumber, result.literal)
of 'i', 'I':
inc(endpos)
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
result.tokType = tkInt64Lit
inc(endpos, 2)
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
result.tokType = tkInt32Lit
inc(endpos, 2)
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'):
result.tokType = tkInt16Lit
inc(endpos, 2)
elif (L.buf[endpos] == '8'):
result.tokType = tkInt8Lit
inc(endpos)
else:
lexMessage(L, errInvalidNumber, result.literal)
else: lexMessage(L, errInvalidNumber, result.literal)
else:
L.bufpos = pos # restore position
try:
if (L.buf[pos] == '0') and
(L.buf[pos + 1] in {'x', 'X', 'b', 'B', 'o', 'O', 'c', 'C'}):
inc(pos, 2)
xi = 0 # it may be a base prefix
case L.buf[pos - 1] # now look at the optional type suffix:
of 'b', 'B':
result.base = base2
while true:
case L.buf[pos]
of 'A'..'Z', 'a'..'z', '2'..'9', '.':
lexMessage(L, errInvalidNumber, result.literal)
inc(pos)
of '_':
inc(pos)
of '0', '1':
xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0'))
inc(pos)
else: break
of 'o', 'c', 'C':
result.base = base8
while true:
case L.buf[pos]
of 'A'..'Z', 'a'..'z', '8'..'9', '.':
lexMessage(L, errInvalidNumber, result.literal)
inc(pos)
of '_':
inc(pos)
of '0'..'7':
xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0'))
inc(pos)
else: break
of 'O':
lexMessage(L, errInvalidNumber, result.literal)
of 'x', 'X':
result.base = base16
while true:
case L.buf[pos]
of 'G'..'Z', 'g'..'z', '.':
lexMessage(L, errInvalidNumber, result.literal)
inc(pos)
of '_':
inc(pos)
of '0'..'9':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0'))
inc(pos)
of 'a'..'f':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10)
inc(pos)
of 'A'..'F':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
inc(pos)
else: break
else: InternalError(getLineInfo(L), "getNumber")
case result.tokType
of tkIntLit, tkInt64Lit:
result.iNumber = xi
of tkInt8Lit:
result.iNumber = biggestInt(int8(toU8(int(xi))))
of tkInt16Lit:
result.iNumber = biggestInt(toU16(int(xi)))
of tkInt32Lit:
result.iNumber = biggestInt(toU32(xi))
of tkFloat32Lit:
result.fNumber = (cast[PFloat32](addr(xi)))^ # note: this code is endian neutral!
# XXX: Test this on big endian machine!
of tkFloat64Lit:
result.fNumber = (cast[PFloat64](addr(xi)))^
else: InternalError(getLineInfo(L), "getNumber")
elif isFloatLiteral(result.literal) or (result.tokType == tkFloat32Lit) or
(result.tokType == tkFloat64Lit):
result.fnumber = parseFloat(result.literal)
if result.tokType == tkIntLit: result.tokType = tkFloatLit
else:
result.iNumber = ParseBiggestInt(result.literal)
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
if result.tokType == tkIntLit:
result.tokType = tkInt64Lit
elif result.tokType != tkInt64Lit:
lexMessage(L, errInvalidNumber, result.literal)
except EInvalidValue:
lexMessage(L, errInvalidNumber, result.literal)
except EOverflow:
lexMessage(L, errNumberOutOfRange, result.literal)
except EOutOfRange:
lexMessage(L, errNumberOutOfRange, result.literal)
L.bufpos = endpos
proc handleHexChar(L: var TLexer, xi: var int) =
case L.buf[L.bufpos]
of '0'..'9':
xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('0'))
inc(L.bufpos)
of 'a'..'f':
xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('a') + 10)
inc(L.bufpos)
of 'A'..'F':
xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('A') + 10)
inc(L.bufpos)
else:
nil
proc handleDecChars(L: var TLexer, xi: var int) =
while L.buf[L.bufpos] in {'0'..'9'}:
xi = (xi * 10) + (ord(L.buf[L.bufpos]) - ord('0'))
inc(L.bufpos)
proc getEscapedChar(L: var TLexer, tok: var TToken) =
var xi: int
inc(L.bufpos) # skip '\'
case L.buf[L.bufpos]
of 'n', 'N':
if tok.toktype == tkCharLit: lexMessage(L, errNnotAllowedInCharacter)
tok.literal = tok.literal & tnl
Inc(L.bufpos)
of 'r', 'R', 'c', 'C':
add(tok.literal, CR)
Inc(L.bufpos)
of 'l', 'L':
add(tok.literal, LF)
Inc(L.bufpos)
of 'f', 'F':
add(tok.literal, FF)
inc(L.bufpos)
of 'e', 'E':
add(tok.literal, ESC)
Inc(L.bufpos)
of 'a', 'A':
add(tok.literal, BEL)
Inc(L.bufpos)
of 'b', 'B':
add(tok.literal, BACKSPACE)
Inc(L.bufpos)
of 'v', 'V':
add(tok.literal, VT)
Inc(L.bufpos)
of 't', 'T':
add(tok.literal, Tabulator)
Inc(L.bufpos)
of '\'', '\"':
add(tok.literal, L.buf[L.bufpos])
Inc(L.bufpos)
of '\\':
add(tok.literal, '\\')
Inc(L.bufpos)
of 'x', 'X':
inc(L.bufpos)
xi = 0
handleHexChar(L, xi)
handleHexChar(L, xi)
add(tok.literal, Chr(xi))
of '0'..'9':
if matchTwoChars(L, '0', {'0'..'9'}):
lexMessage(L, warnOctalEscape)
xi = 0
handleDecChars(L, xi)
if (xi <= 255): add(tok.literal, Chr(xi))
else: lexMessage(L, errInvalidCharacterConstant)
else: lexMessage(L, errInvalidCharacterConstant)
proc HandleCRLF(L: var TLexer, pos: int): int =
case L.buf[pos]
of CR:
if getColNumber(L, pos) > MaxLineLength:
lexMessagePos(L, hintLineTooLong, pos)
result = lexbase.HandleCR(L, pos)
of LF:
if getColNumber(L, pos) > MaxLineLength:
lexMessagePos(L, hintLineTooLong, pos)
result = lexbase.HandleLF(L, pos)
else: result = pos
proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
var
line, line2, pos: int
c: Char
buf: cstring
pos = L.bufPos + 1 # skip "
buf = L.buf # put `buf` in a register
line = L.linenumber # save linenumber for better error message
if (buf[pos] == '\"') and (buf[pos + 1] == '\"'):
tok.tokType = tkTripleStrLit # long string literal:
inc(pos, 2) # skip ""
# skip leading newline:
pos = HandleCRLF(L, pos)
buf = L.buf
while true:
case buf[pos]
of '\"':
if (buf[pos + 1] == '\"') and (buf[pos + 2] == '\"'): break
add(tok.literal, '\"')
Inc(pos)
of CR, LF:
pos = HandleCRLF(L, pos)
buf = L.buf
tok.literal = tok.literal & tnl
of lexbase.EndOfFile:
line2 = L.linenumber
L.LineNumber = line
lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
L.LineNumber = line2
break
else:
add(tok.literal, buf[pos])
Inc(pos)
L.bufpos = pos +
3 # skip the three """
else:
# ordinary string literal
if rawMode: tok.tokType = tkRStrLit
else: tok.tokType = tkStrLit
while true:
c = buf[pos]
if c == '\"':
inc(pos) # skip '"'
break
if c in {CR, LF, lexbase.EndOfFile}:
lexMessage(L, errClosingQuoteExpected)
break
if (c == '\\') and not rawMode:
L.bufPos = pos
getEscapedChar(L, tok)
pos = L.bufPos
else:
add(tok.literal, c)
Inc(pos)
L.bufpos = pos
proc getCharacter(L: var TLexer, tok: var TToken) =
var c: Char
Inc(L.bufpos) # skip '
c = L.buf[L.bufpos]
case c
of '\0'..Pred(' '), '\'': lexMessage(L, errInvalidCharacterConstant)
of '\\': getEscapedChar(L, tok)
else:
tok.literal = c & ""
Inc(L.bufpos)
if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
inc(L.bufpos) # skip '
proc getSymbol(L: var TLexer, tok: var TToken) =
var
pos: int
c: Char
buf: cstring
h: THash # hashing algorithm inlined
h = 0
pos = L.bufpos
buf = L.buf
while true:
c = buf[pos]
case c
of 'a'..'z', '0'..'9', '\x80'..'\xFF':
h = h +% Ord(c)
h = h +% h shl 10
h = h xor (h shr 6)
of 'A'..'Z':
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h +% Ord(c)
h = h +% h shl 10
h = h xor (h shr 6)
of '_':
nil
else: break
Inc(pos)
h = h +% h shl 3
h = h xor (h shr 11)
h = h +% h shl 15
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
L.bufpos = pos
if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
(tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
tok.tokType = tkSymbol
else:
tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
if buf[pos] == '\"':
getString(L, tok, true)
if tok.tokType == tkRStrLit: tok.tokType = tkCallRStrLit
else: tok.tokType = tkCallTripleStrLit
proc getOperator(L: var TLexer, tok: var TToken) =
var
pos: int
c: Char
buf: cstring
h: THash # hashing algorithm inlined
pos = L.bufpos
buf = L.buf
h = 0
while true:
c = buf[pos]
if c in OpChars:
h = h +% Ord(c)
h = h +% h shl 10
h = h xor (h shr 6)
else:
break
Inc(pos)
h = h +% h shl 3
h = h xor (h shr 11)
h = h +% h shl 15
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
else: tok.tokType = TTokType(tok.ident.id - oprLow + ord(tkColon))
L.bufpos = pos
proc handleIndentation(L: var TLexer, tok: var TToken, indent: int) =
var i: int
tok.indent = indent
i = high(L.indentStack)
if indent > L.indentStack[i]:
tok.tokType = tkInd
elif indent == L.indentStack[i]:
tok.tokType = tkSad
else:
# check we have the indentation somewhere in the stack:
while (i >= 0) and (indent != L.indentStack[i]):
dec(i)
inc(L.dedent)
dec(L.dedent)
tok.tokType = tkDed
if i < 0:
tok.tokType = tkSad # for the parser it is better as SAD
lexMessage(L, errInvalidIndentation)
proc scanComment(L: var TLexer, tok: var TToken) =
var
buf: cstring
pos, col: int
indent: int
pos = L.bufpos
buf = L.buf # a comment ends if the next line does not start with the # on the same
# column after only whitespace
tok.tokType = tkComment
col = getColNumber(L, pos)
while true:
while not (buf[pos] in {CR, LF, lexbase.EndOfFile}):
add(tok.literal, buf[pos])
inc(pos)
pos = handleCRLF(L, pos)
buf = L.buf
indent = 0
while buf[pos] == ' ':
inc(pos)
inc(indent)
if (buf[pos] == '#') and (col == indent):
tok.literal = tok.literal & "\n"
else:
if buf[pos] > ' ':
L.indentAhead = indent
inc(L.dedent)
break
L.bufpos = pos
proc skip(L: var TLexer, tok: var TToken) =
var
buf: cstring
indent, pos: int
pos = L.bufpos
buf = L.buf
while true:
case buf[pos]
of ' ':
Inc(pos)
of Tabulator:
lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
inc(pos) # BUGFIX
of CR, LF:
pos = HandleCRLF(L, pos)
buf = L.buf
indent = 0
while buf[pos] == ' ':
Inc(pos)
Inc(indent)
if (buf[pos] > ' '):
handleIndentation(L, tok, indent)
break
else:
break # EndOfFile also leaves the loop
L.bufpos = pos
proc rawGetTok(L: var TLexer, tok: var TToken) =
var c: Char
fillToken(tok)
if L.dedent > 0:
dec(L.dedent)
if L.indentAhead >= 0:
handleIndentation(L, tok, L.indentAhead)
L.indentAhead = - 1
else:
tok.tokType = tkDed
return
skip(L, tok) # skip
# got an documentation comment or tkIndent, return that:
if tok.toktype != tkInvalid: return
c = L.buf[L.bufpos]
if c in SymStartChars - {'r', 'R', 'l'}:
getSymbol(L, tok)
elif c in {'0'..'9'}:
tok = getNumber(L)
else:
case c
of '#':
scanComment(L, tok)
of ':':
tok.tokType = tkColon
inc(L.bufpos)
of ',':
tok.toktype = tkComma
Inc(L.bufpos)
of 'l':
# if we parsed exactly one character and its a small L (l), this
# is treated as a warning because it may be confused with the number 1
if not (L.buf[L.bufpos + 1] in (SymChars + {'_'})):
lexMessage(L, warnSmallLshouldNotBeUsed)
getSymbol(L, tok)
of 'r', 'R':
if L.buf[L.bufPos + 1] == '\"':
Inc(L.bufPos)
getString(L, tok, true)
else:
getSymbol(L, tok)
of '(':
Inc(L.bufpos)
if (L.buf[L.bufPos] == '.') and (L.buf[L.bufPos + 1] != '.'):
tok.toktype = tkParDotLe
Inc(L.bufpos)
else:
tok.toktype = tkParLe
of ')':
tok.toktype = tkParRi
Inc(L.bufpos)
of '[':
Inc(L.bufpos)
if (L.buf[L.bufPos] == '.') and (L.buf[L.bufPos + 1] != '.'):
tok.toktype = tkBracketDotLe
Inc(L.bufpos)
else:
tok.toktype = tkBracketLe
of ']':
tok.toktype = tkBracketRi
Inc(L.bufpos)
of '.':
if L.buf[L.bufPos + 1] == ']':
tok.tokType = tkBracketDotRi
Inc(L.bufpos, 2)
elif L.buf[L.bufPos + 1] == '}':
tok.tokType = tkCurlyDotRi
Inc(L.bufpos, 2)
elif L.buf[L.bufPos + 1] == ')':
tok.tokType = tkParDotRi
Inc(L.bufpos, 2)
else:
getOperator(L, tok)
of '{':
Inc(L.bufpos)
if (L.buf[L.bufPos] == '.') and (L.buf[L.bufPos + 1] != '.'):
tok.toktype = tkCurlyDotLe
Inc(L.bufpos)
else:
tok.toktype = tkCurlyLe
of '}':
tok.toktype = tkCurlyRi
Inc(L.bufpos)
of ';':
tok.toktype = tkSemiColon
Inc(L.bufpos)
of '`':
tok.tokType = tkAccent
Inc(L.bufpos)
of '\"':
getString(L, tok, false)
of '\'':
getCharacter(L, tok)
tok.tokType = tkCharLit
of lexbase.EndOfFile:
tok.toktype = tkEof
else:
if c in OpChars:
getOperator(L, tok)
else:
tok.literal = c & ""
tok.tokType = tkInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
Inc(L.bufpos)
dummyIdent = getIdent("")

218
rod/sem.nim Executable file
View File

@@ -0,0 +1,218 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements the semantic checking pass.
import #var
# point: array [0..3] of int;
strutils, nhashes, lists, options, scanner, ast, astalgo, trees, treetab,
wordrecg, ropes, msgs, os, condsyms, idents, rnimsyn, types, platform, math,
magicsys, pnimsyn, nversion, nimsets, semdata, evals, semfold, importer,
procfind, lookups, rodread, pragmas, passes
proc semPass*(): TPass
# implementation
proc considerAcc(n: PNode): PIdent =
var x: PNode
x = n
if x.kind == nkAccQuoted: x = x.sons[0]
case x.kind
of nkIdent: result = x.ident
of nkSym: result = x.sym.name
else:
liMessage(n.info, errIdentifierExpected, renderTree(n))
result = nil
proc isTopLevel(c: PContext): bool =
result = c.tab.tos <= 2
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerAcc(n), getCurrOwner())
result.info = n.info
proc markUsed(n: PNode, s: PSym) =
incl(s.flags, sfUsed)
if sfDeprecated in s.flags: liMessage(n.info, warnDeprecated, s.name.s)
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym
# identifier with visability
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym
proc semStmtScope(c: PContext, n: PNode): PNode
type
TExprFlag = enum
efAllowType, efLValue, efWantIterator
TExprFlags = set[TExprFlag]
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc fitNode(c: PContext, formal: PType, arg: PNode): PNode
proc semLambda(c: PContext, n: PNode): PNode
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType
proc semStmt(c: PContext, n: PNode): PNode
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
proc addParams(c: PContext, n: PNode)
proc addResult(c: PContext, t: PType, info: TLineInfo)
proc addResultNode(c: PContext, n: PNode)
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
proc semConstExpr(c: PContext, n: PNode): PNode =
result = semExprWithType(c, n)
if result == nil:
liMessage(n.info, errConstExprExpected)
return
result = getConstExpr(c.module, result)
if result == nil: liMessage(n.info, errConstExprExpected)
proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
var e: PNode
e = semExprWithType(c, n)
if e == nil:
liMessage(n.info, errConstExprExpected)
return nil
result = getConstExpr(c.module, e)
if result == nil:
#writeln(output, renderTree(n));
result = evalConstExpr(c.module, e)
if (result == nil) or (result.kind == nkEmpty):
liMessage(n.info, errConstExprExpected)
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
result = n
case s.typ.sons[0].kind
of tyExpr: result = semExprWithType(c, result)
of tyStmt: result = semStmt(c, result)
of tyTypeDesc: result.typ = semTypeNode(c, result, nil)
else: liMessage(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
include
"semtempl.nim"
proc semMacroExpr(c: PContext, n: PNode, sym: PSym, semCheck: bool = true): PNode =
var
p: PEvalContext
s: PStackFrame
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
liMessage(n.info, errTemplateInstantiationTooNested)
markUsed(n, sym)
p = newEvalContext(c.module, "", false)
s = newStackFrame()
s.call = n
setlen(s.params, 2)
s.params[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0])
s.params[1] = n
pushStackFrame(p, s)
discard eval(p, sym.ast.sons[codePos])
result = s.params[0]
popStackFrame(p)
if cyclicTree(result): liMessage(n.info, errCyclicTree)
if semCheck: result = semAfterMacroCall(c, result, sym)
dec(evalTemplateCounter)
include
"seminst.nim"
include
"sigmatch.nim"
proc CheckBool(t: PNode) =
if (t.Typ == nil) or
(skipTypes(t.Typ, {tyGenericInst, tyVar, tyOrdinal}).kind != tyBool):
liMessage(t.Info, errExprMustBeBool)
proc typeMismatch(n: PNode, formal, actual: PType) =
liMessage(n.Info, errGenerated, msgKindToString(errTypeMismatch) &
typeToString(actual) & ") " &
`%`(msgKindToString(errButExpectedX), [typeToString(formal)]))
include
"semtypes.nim"
include
"semexprs.nim"
include
"semgnrc.nim"
include
"semstmts.nim"
proc addCodeForGenerics(c: PContext, n: PNode) =
var
prc: PSym
it: PNode
for i in countup(c.lastGenericIdx, sonsLen(c.generics) - 1):
it = c.generics.sons[i].sons[1]
if it.kind != nkSym: InternalError("addCodeForGenerics")
prc = it.sym
if (prc.kind in {skProc, skMethod, skConverter}) and (prc.magic == mNone):
if (prc.ast == nil) or (prc.ast.sons[codePos] == nil):
InternalError(prc.info, "no code for " & prc.name.s)
addSon(n, prc.ast)
c.lastGenericIdx = sonsLen(c.generics)
proc myOpen(module: PSym, filename: string): PPassContext =
var c: PContext
c = newContext(module, filename)
if (c.p != nil): InternalError(module.info, "sem.myOpen")
c.semConstExpr = semConstExpr
c.p = newProcCon(module)
pushOwner(c.module)
openScope(c.tab) # scope for imported symbols
SymTabAdd(c.tab, module) # a module knows itself
if sfSystemModule in module.flags:
magicsys.SystemModule = module # set global variable!
InitSystem(c.tab) # currently does nothing
else:
SymTabAdd(c.tab, magicsys.SystemModule) # import the "System" identifier
importAllSymbols(c, magicsys.SystemModule)
openScope(c.tab) # scope for the module's symbols
result = c
proc myOpenCached(module: PSym, filename: string, rd: PRodReader): PPassContext =
var c: PContext
c = PContext(myOpen(module, filename))
c.fromCache = true
result = c
proc myProcess(context: PPassContext, n: PNode): PNode =
var
c: PContext
a: PNode
result = nil
c = PContext(context)
result = semStmt(c, n) # BUGFIX: process newly generated generics here, not at the end!
if sonsLen(c.generics) > 0:
a = newNodeI(nkStmtList, n.info)
addCodeForGenerics(c, a)
if sonsLen(a) > 0:
# a generic has been added to `a`:
addSonIfNotNil(a, result)
result = a
proc myClose(context: PPassContext, n: PNode): PNode =
var c: PContext
c = PContext(context)
closeScope(c.tab) # close module's scope
rawCloseScope(c.tab) # imported symbols; don't check for unused ones!
if n == nil:
result = newNode(nkStmtList)
else:
InternalError(n.info, "n is not nil") #result := n;
addCodeForGenerics(c, result)
popOwner()
c.p = nil
proc semPass(): TPass =
initPass(result)
result.open = myOpen
result.openCached = myOpenCached
result.close = myClose
result.process = myProcess

190
rod/semdata.nim Executable file
View File

@@ -0,0 +1,190 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module contains the data structures for the semantic checking phase.
import
strutils, lists, options, scanner, ast, astalgo, trees, treetab, wordrecg,
ropes, msgs, platform, os, condsyms, idents, rnimsyn, types, extccomp, math,
magicsys, nversion, nimsets, pnimsyn, times, passes, rodread
type
TOptionEntry* = object of lists.TListEntry # entries to put on a stack for pragma parsing
options*: TOptions
defaultCC*: TCallingConvention
dynlib*: PLib
Notes*: TNoteKinds
POptionEntry* = ref TOptionEntry
TProcCon*{.final.} = object # procedure context; also used for top-level
# statements
owner*: PSym # the symbol this context belongs to
resultSym*: PSym # the result symbol (if we are in a proc)
nestedLoopCounter*: int # whether we are in a loop or not
nestedBlockCounter*: int # whether we are in a block or not
PProcCon* = ref TProcCon
PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module
module*: PSym # the module sym belonging to the context
p*: PProcCon # procedure context
InstCounter*: int # to prevent endless instantiations
generics*: PNode # a list of the things to compile; list of
# nkExprEqExpr nodes which contain the
# generic symbol and the instantiated symbol
lastGenericIdx*: int # used for the generics stack
tab*: TSymTab # each module has its own symbol table
AmbiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!)
converters*: TSymSeq # sequence of converters
optionStack*: TLinkedList
libs*: TLinkedList # all libs used by this module
fromCache*: bool # is the module read from a cache?
semConstExpr*: proc (c: PContext, n: PNode): PNode # for the pragmas module
includedFiles*: TIntSet # used to detect recursive include files
filename*: string # the module's filename
var gInstTypes*: TIdTable
proc newContext*(module: PSym, nimfile: string): PContext
# map PType to PType
proc newProcCon*(owner: PSym): PProcCon
proc lastOptionEntry*(c: PContext): POptionEntry
proc newOptionEntry*(): POptionEntry
proc addConverter*(c: PContext, conv: PSym)
proc newLib*(kind: TLibKind): PLib
proc addToLib*(lib: PLib, sym: PSym)
proc makePtrType*(c: PContext, baseType: PType): PType
proc makeVarType*(c: PContext, baseType: PType): PType
proc newTypeS*(kind: TTypeKind, c: PContext): PType
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext)
proc makeRangeType*(c: PContext, first, last: biggestInt, info: TLineInfo): PType
proc illFormedAst*(n: PNode)
proc getSon*(n: PNode, indx: int): PNode
proc checkSonsLen*(n: PNode, length: int)
proc checkMinSonsLen*(n: PNode, length: int)
# owner handling:
proc getCurrOwner*(): PSym
proc PushOwner*(owner: PSym)
proc PopOwner*()
# implementation
var gOwners: seq[PSym]
gOwners = @ []
proc getCurrOwner(): PSym =
# owner stack (used for initializing the
# owner field of syms)
# the documentation comment always gets
# assigned to the current owner
# BUGFIX: global array is needed!
result = gOwners[high(gOwners)]
proc PushOwner(owner: PSym) =
var length: int
length = len(gOwners)
setlen(gOwners, length + 1)
gOwners[length] = owner
proc PopOwner() =
var length: int
length = len(gOwners)
if (length <= 0): InternalError("popOwner")
setlen(gOwners, length - 1)
proc lastOptionEntry(c: PContext): POptionEntry =
result = POptionEntry(c.optionStack.tail)
proc newProcCon(owner: PSym): PProcCon =
if owner == nil: InternalError("owner is nil")
new(result)
result.owner = owner
proc newOptionEntry(): POptionEntry =
new(result)
result.options = gOptions
result.defaultCC = ccDefault
result.dynlib = nil
result.notes = gNotes
proc newContext(module: PSym, nimfile: string): PContext =
new(result)
InitSymTab(result.tab)
IntSetInit(result.AmbiguousSymbols)
initLinkedList(result.optionStack)
initLinkedList(result.libs)
append(result.optionStack, newOptionEntry())
result.module = module
result.generics = newNode(nkStmtList)
result.converters = @ []
result.filename = nimfile
IntSetInit(result.includedFiles)
proc addConverter(c: PContext, conv: PSym) =
var L: int
L = len(c.converters)
for i in countup(0, L - 1):
if c.converters[i].id == conv.id: return
setlen(c.converters, L + 1)
c.converters[L] = conv
proc newLib(kind: TLibKind): PLib =
new(result)
result.kind = kind #initObjectSet(result.syms)
proc addToLib(lib: PLib, sym: PSym) =
#ObjectSetIncl(lib.syms, sym);
if sym.annex != nil: liMessage(sym.info, errInvalidPragma)
sym.annex = lib
proc makePtrType(c: PContext, baseType: PType): PType =
if (baseType == nil): InternalError("makePtrType")
result = newTypeS(tyPtr, c)
addSon(result, baseType)
proc makeVarType(c: PContext, baseType: PType): PType =
if (baseType == nil): InternalError("makeVarType")
result = newTypeS(tyVar, c)
addSon(result, baseType)
proc newTypeS(kind: TTypeKind, c: PContext): PType =
result = newType(kind, getCurrOwner())
proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
dest.kind = kind
dest.owner = getCurrOwner()
dest.size = - 1
proc makeRangeType(c: PContext, first, last: biggestInt, info: TLineInfo): PType =
var n: PNode
n = newNodeI(nkRange, info)
addSon(n, newIntNode(nkIntLit, first))
addSon(n, newIntNode(nkIntLit, last))
result = newTypeS(tyRange, c)
result.n = n
addSon(result, getSysType(tyInt)) # basetype of range
proc illFormedAst(n: PNode) =
liMessage(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc getSon(n: PNode, indx: int): PNode =
if (n != nil) and (indx < sonsLen(n)):
result = n.sons[indx]
else:
illFormedAst(n)
result = nil
proc checkSonsLen(n: PNode, length: int) =
if (n == nil) or (sonsLen(n) != length): illFormedAst(n)
proc checkMinSonsLen(n: PNode, length: int) =
if (n == nil) or (sonsLen(n) < length): illFormedAst(n)
initIdTable(gInstTypes)

1113
rod/semexprs.nim Executable file

File diff suppressed because it is too large Load Diff

500
rod/semfold.nim Executable file
View File

@@ -0,0 +1,500 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this module folds constants; used by semantic checking phase
# and evaluation phase
import
strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times,
nversion, platform, math, msgs, os, condsyms, idents, rnimsyn, types
proc getConstExpr*(module: PSym, n: PNode): PNode
# evaluates the constant expression or returns nil if it is no constant
# expression
proc evalOp*(m: TMagic, n, a, b, c: PNode): PNode
proc leValueConv*(a, b: PNode): bool
proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode
proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode): PNode
proc newStrNodeT*(strVal: string, n: PNode): PNode
proc getInt*(a: PNode): biggestInt
proc getFloat*(a: PNode): biggestFloat
proc getStr*(a: PNode): string
proc getStrOrChar*(a: PNode): string
# implementation
proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
if skipTypes(n.typ, abstractVarRange).kind == tyChar:
result = newIntNode(nkCharLit, intVal)
else:
result = newIntNode(nkIntLit, intVal)
result.typ = n.typ
result.info = n.info
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
result = newFloatNode(nkFloatLit, floatVal)
result.typ = n.typ
result.info = n.info
proc newStrNodeT(strVal: string, n: PNode): PNode =
result = newStrNode(nkStrLit, strVal)
result.typ = n.typ
result.info = n.info
proc getInt(a: PNode): biggestInt =
case a.kind
of nkIntLit..nkInt64Lit: result = a.intVal
else:
internalError(a.info, "getInt")
result = 0
proc getFloat(a: PNode): biggestFloat =
case a.kind
of nkFloatLit..nkFloat64Lit: result = a.floatVal
else:
internalError(a.info, "getFloat")
result = 0.0
proc getStr(a: PNode): string =
case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal
else:
internalError(a.info, "getStr")
result = ""
proc getStrOrChar(a: PNode): string =
case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal
of nkCharLit: result = chr(int(a.intVal)) & ""
else:
internalError(a.info, "getStrOrChar")
result = ""
proc enumValToString(a: PNode): string =
var
n: PNode
field: PSym
x: biggestInt
x = getInt(a)
n = skipTypes(a.typ, abstractInst).n
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkSym: InternalError(a.info, "enumValToString")
field = n.sons[i].sym
if field.position == x:
return field.name.s
InternalError(a.info, "no symbol for ordinal value: " & $(x))
proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
# b and c may be nil
result = nil
case m
of mOrd:
result = newIntNodeT(getOrdValue(a), n)
of mChr:
result = newIntNodeT(getInt(a), n)
of mUnaryMinusI, mUnaryMinusI64:
result = newIntNodeT(- getInt(a), n)
of mUnaryMinusF64:
result = newFloatNodeT(- getFloat(a), n)
of mNot:
result = newIntNodeT(1 - getInt(a), n)
of mCard:
result = newIntNodeT(nimsets.cardSet(a), n)
of mBitnotI, mBitnotI64:
result = newIntNodeT(not getInt(a), n)
of mLengthStr:
result = newIntNodeT(len(getStr(a)), n)
of mLengthArray:
result = newIntNodeT(lengthOrd(a.typ), n)
of mLengthSeq, mLengthOpenArray:
result = newIntNodeT(sonsLen(a), n) # BUGFIX
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64:
result = a # throw `+` away
of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n)
of mToInt, mToBiggestInt:
result = newIntNodeT(system.toInt(getFloat(a)), n)
of mAbsF64:
result = newFloatNodeT(abs(getFloat(a)), n)
of mAbsI, mAbsI64:
if getInt(a) >= 0: result = a
else: result = newIntNodeT(- getInt(a), n)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
# byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n)
of mToU8:
result = newIntNodeT(getInt(a) and 0x000000FF, n)
of mToU16:
result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
of mToU32:
result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
of mSucc:
result = newIntNodeT(getOrdValue(a) + getInt(b), n)
of mPred:
result = newIntNodeT(getOrdValue(a) - getInt(b), n)
of mAddI, mAddI64:
result = newIntNodeT(getInt(a) + getInt(b), n)
of mSubI, mSubI64:
result = newIntNodeT(getInt(a) - getInt(b), n)
of mMulI, mMulI64:
result = newIntNodeT(getInt(a) * getInt(b), n)
of mMinI, mMinI64:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n)
of mMaxI, mMaxI64:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
else: result = newIntNodeT(getInt(b), n)
of mShlI, mShlI64:
case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n)
of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n)
of tyInt64, tyInt: result = newIntNodeT(`shl`(getInt(a), getInt(b)), n)
else: InternalError(n.info, "constant folding for shl")
of mShrI, mShrI64:
case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n)
of tyInt32: result = newIntNodeT(int32(getInt(a)) shr int32(getInt(b)), n)
of tyInt64, tyInt: result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
else: InternalError(n.info, "constant folding for shl")
of mDivI, mDivI64:
result = newIntNodeT(getInt(a) div getInt(b), n)
of mModI, mModI64:
result = newIntNodeT(getInt(a) mod getInt(b), n)
of mAddF64:
result = newFloatNodeT(getFloat(a) + getFloat(b), n)
of mSubF64:
result = newFloatNodeT(getFloat(a) - getFloat(b), n)
of mMulF64:
result = newFloatNodeT(getFloat(a) * getFloat(b), n)
of mDivF64:
if getFloat(b) == 0.0:
if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n)
else: result = newFloatNodeT(Inf, n)
else:
result = newFloatNodeT(getFloat(a) / getFloat(b), n)
of mMaxF64:
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n)
else: result = newFloatNodeT(getFloat(b), n)
of mMinF64:
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n)
else: result = newFloatNodeT(getFloat(a), n)
of mIsNil:
result = newIntNodeT(ord(a.kind == nkNilLit), n)
of mLtI, mLtI64, mLtB, mLtEnum, mLtCh:
result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n)
of mLeI, mLeI64, mLeB, mLeEnum, mLeCh:
result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n)
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n) # operators for floats
of mLtF64:
result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n)
of mLeF64:
result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n)
of mEqF64:
result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n) # operators for strings
of mLtStr:
result = newIntNodeT(ord(getStr(a) < getStr(b)), n)
of mLeStr:
result = newIntNodeT(ord(getStr(a) <= getStr(b)), n)
of mEqStr:
result = newIntNodeT(ord(getStr(a) == getStr(b)), n)
of mLtU, mLtU64:
result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n)
of mLeU, mLeU64:
result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n)
of mBitandI, mBitandI64, mAnd:
result = newIntNodeT(getInt(a) and getInt(b), n)
of mBitorI, mBitorI64, mOr:
result = newIntNodeT(getInt(a) or getInt(b), n)
of mBitxorI, mBitxorI64, mXor:
result = newIntNodeT(getInt(a) xor getInt(b), n)
of mAddU, mAddU64:
result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
of mSubU, mSubU64:
result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
of mMulU, mMulU64:
result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
of mModU, mModU64:
result = newIntNodeT(`%%`(getInt(a), getInt(b)), n)
of mDivU, mDivU64:
result = newIntNodeT(`/%`(getInt(a), getInt(b)), n)
of mLeSet:
result = newIntNodeT(Ord(containsSets(a, b)), n)
of mEqSet:
result = newIntNodeT(Ord(equalSets(a, b)), n)
of mLtSet:
result = newIntNodeT(Ord(containsSets(a, b) and not equalSets(a, b)), n)
of mMulSet:
result = nimsets.intersectSets(a, b)
result.info = n.info
of mPlusSet:
result = nimsets.unionSets(a, b)
result.info = n.info
of mMinusSet:
result = nimsets.diffSets(a, b)
result.info = n.info
of mSymDiffSet:
result = nimsets.symdiffSets(a, b)
result.info = n.info
of mConStrStr:
result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n)
of mInSet:
result = newIntNodeT(Ord(inSet(a, b)), n)
of mRepr:
# BUGFIX: we cannot eval mRepr here. But this means that it is not
# available for interpretation. I don't know how to fix this.
#result := newStrNodeT(renderTree(a, {@set}[renderNoComments]), n);
of mIntToStr, mInt64ToStr:
result = newStrNodeT($(getOrdValue(a)), n)
of mBoolToStr:
if getOrdValue(a) == 0: result = newStrNodeT("false", n)
else: result = newStrNodeT("true", n)
of mCopyStr:
result = newStrNodeT(copy(getStr(a), int(getOrdValue(b)) + 0), n)
of mCopyStrLast:
result = newStrNodeT(copy(getStr(a), int(getOrdValue(b)) + 0,
int(getOrdValue(c)) + 0), n)
of mFloatToStr:
result = newStrNodeT($(getFloat(a)), n)
of mCStrToStr, mCharToStr:
result = newStrNodeT(getStrOrChar(a), n)
of mStrToStr:
result = a
of mEnumToStr:
result = newStrNodeT(enumValToString(a), n)
of mArrToSeq:
result = copyTree(a)
result.typ = n.typ
of mNewString, mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mNLen..mNError:
nil
else: InternalError(a.info, "evalOp(" & magicToStr[m] & ')')
proc getConstIfExpr(c: PSym, n: PNode): PNode =
var it, e: PNode
result = nil
for i in countup(0, sonsLen(n) - 1):
it = n.sons[i]
case it.kind
of nkElifExpr:
e = getConstExpr(c, it.sons[0])
if e == nil:
return nil
if getOrdValue(e) != 0:
if result == nil:
result = getConstExpr(c, it.sons[1])
if result == nil: return
of nkElseExpr:
if result == nil: result = getConstExpr(c, it.sons[0])
else: internalError(it.info, "getConstIfExpr()")
proc partialAndExpr(c: PSym, n: PNode): PNode =
# partial evaluation
var a, b: PNode
result = n
a = getConstExpr(c, n.sons[1])
b = getConstExpr(c, n.sons[2])
if a != nil:
if getInt(a) == 0: result = a
elif b != nil: result = b
else: result = n.sons[2]
elif b != nil:
if getInt(b) == 0: result = b
else: result = n.sons[1]
proc partialOrExpr(c: PSym, n: PNode): PNode =
# partial evaluation
var a, b: PNode
result = n
a = getConstExpr(c, n.sons[1])
b = getConstExpr(c, n.sons[2])
if a != nil:
if getInt(a) != 0: result = a
elif b != nil: result = b
else: result = n.sons[2]
elif b != nil:
if getInt(b) != 0: result = b
else: result = n.sons[1]
proc leValueConv(a, b: PNode): bool =
result = false
case a.kind
of nkCharLit..nkInt64Lit:
case b.kind
of nkCharLit..nkInt64Lit: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat64Lit: result = a.intVal <= round(b.floatVal)
else: InternalError(a.info, "leValueConv")
of nkFloatLit..nkFloat64Lit:
case b.kind
of nkFloatLit..nkFloat64Lit: result = a.floatVal <= b.floatVal
of nkCharLit..nkInt64Lit: result = a.floatVal <= toFloat(int(b.intVal))
else: InternalError(a.info, "leValueConv")
else: InternalError(a.info, "leValueConv")
proc getConstExpr(module: PSym, n: PNode): PNode =
var
s: PSym
a, b, c: PNode
result = nil
case n.kind
of nkSym:
s = n.sym
if s.kind == skEnumField:
result = newIntNodeT(s.position, n)
elif (s.kind == skConst):
case s.magic
of mIsMainModule: result = newIntNodeT(ord(sfMainModule in module.flags),
n)
of mCompileDate: result = newStrNodeT(times.getDateStr(), n)
of mCompileTime: result = newStrNodeT(times.getClockStr(), n)
of mNimrodVersion: result = newStrNodeT(VersionAsString, n)
of mNimrodMajor: result = newIntNodeT(VersionMajor, n)
of mNimrodMinor: result = newIntNodeT(VersionMinor, n)
of mNimrodPatch: result = newIntNodeT(VersionPatch, n)
of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n)
of mHostOS: result = newStrNodeT(toLower(platform.OS[targetOS].name), n)
of mHostCPU: result = newStrNodeT(toLower(platform.CPU[targetCPU].name), n)
of mNaN: result = newFloatNodeT(NaN, n)
of mInf: result = newFloatNodeT(Inf, n)
of mNegInf: result = newFloatNodeT(NegInf, n)
else:
result = copyTree(s.ast) # BUGFIX
elif s.kind in {skProc, skMethod}: # BUGFIX
result = n
of nkCharLit..nkNilLit:
result = copyNode(n)
of nkIfExpr:
result = getConstIfExpr(module, n)
of nkCall, nkCommand, nkCallStrLit:
if (n.sons[0].kind != nkSym): return
s = n.sons[0].sym
if (s.kind != skProc): return
try:
case s.magic
of mNone:
return # XXX: if it has no sideEffect, it should be evaluated
of mSizeOf:
a = n.sons[1]
if computeSize(a.typ) < 0:
liMessage(a.info, errCannotEvalXBecauseIncompletelyDefined, "sizeof")
if a.typ.kind in {tyArray, tyObject, tyTuple}:
result = nil # XXX: size computation for complex types
# is still wrong
else:
result = newIntNodeT(getSize(a.typ), n)
of mLow:
result = newIntNodeT(firstOrd(n.sons[1].typ), n)
of mHigh:
if not (skipTypes(n.sons[1].typ, abstractVar).kind in
{tyOpenArray, tySequence, tyString}):
result = newIntNodeT(lastOrd(skipTypes(n.sons[1].typ, abstractVar)), n)
else:
a = getConstExpr(module, n.sons[1])
if a == nil: return
if sonsLen(n) > 2:
b = getConstExpr(module, n.sons[2])
if b == nil: return
if sonsLen(n) > 3:
c = getConstExpr(module, n.sons[3])
if c == nil: return
else:
b = nil
result = evalOp(s.magic, n, a, b, c)
except EOverflow:
liMessage(n.info, errOverOrUnderflow)
except EDivByZero:
liMessage(n.info, errConstantDivisionByZero)
of nkAddr:
a = getConstExpr(module, n.sons[0])
if a != nil:
result = n
n.sons[0] = a
of nkBracket:
result = copyTree(n)
for i in countup(0, sonsLen(n) - 1):
a = getConstExpr(module, n.sons[i])
if a == nil:
return nil
result.sons[i] = a
incl(result.flags, nfAllConst)
of nkRange:
a = getConstExpr(module, n.sons[0])
if a == nil: return
b = getConstExpr(module, n.sons[1])
if b == nil: return
result = copyNode(n)
addSon(result, a)
addSon(result, b)
of nkCurly:
result = copyTree(n)
for i in countup(0, sonsLen(n) - 1):
a = getConstExpr(module, n.sons[i])
if a == nil:
return nil
result.sons[i] = a
incl(result.flags, nfAllConst)
of nkPar:
# tuple constructor
result = copyTree(n)
if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr):
for i in countup(0, sonsLen(n) - 1):
a = getConstExpr(module, n.sons[i].sons[1])
if a == nil:
return nil
result.sons[i].sons[1] = a
else:
for i in countup(0, sonsLen(n) - 1):
a = getConstExpr(module, n.sons[i])
if a == nil:
return nil
result.sons[i] = a
incl(result.flags, nfAllConst)
of nkChckRangeF, nkChckRange64, nkChckRange:
a = getConstExpr(module, n.sons[0])
if a == nil: return
if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]):
result = a # a <= x and x <= b
result.typ = n.typ
else:
liMessage(n.info, errGenerated, `%`(
msgKindToString(errIllegalConvFromXtoY),
[typeToString(n.sons[0].typ), typeToString(n.typ)]))
of nkStringToCString, nkCStringToString:
a = getConstExpr(module, n.sons[0])
if a == nil: return
result = a
result.typ = n.typ
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast:
a = getConstExpr(module, n.sons[1])
if a == nil: return
case skipTypes(n.typ, abstractRange).kind
of tyInt..tyInt64:
case skipTypes(a.typ, abstractRange).kind
of tyFloat..tyFloat64: result = newIntNodeT(system.toInt(getFloat(a)), n)
of tyChar: result = newIntNodeT(getOrdValue(a), n)
else:
result = a
result.typ = n.typ
of tyFloat..tyFloat64:
case skipTypes(a.typ, abstractRange).kind
of tyInt..tyInt64, tyEnum, tyBool, tyChar:
result = newFloatNodeT(toFloat(int(getOrdValue(a))), n)
else:
result = a
result.typ = n.typ
of tyOpenArray, tyProc:
nil
else:
#n.sons[1] := a;
#result := n;
result = a
result.typ = n.typ
else:
nil

232
rod/semgnrc.nim Executable file
View File

@@ -0,0 +1,232 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This implements the first pass over the generic body; it resolves some
# symbols. Thus for generics there is a two-phase symbol lookup just like
# in C++.
# A problem is that it cannot be detected if the symbol is introduced
# as in ``var x = ...`` or used because macros/templates can hide this!
# So we have to eval templates/macros right here so that symbol
# lookup can be accurate.
type
TSemGenericFlag = enum
withinBind, withinTypeDesc
TSemGenericFlags = set[TSemGenericFlag]
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode
proc semGenericStmtScope(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode =
openScope(c.tab)
result = semGenericStmt(c, n, flags)
closeScope(c.tab)
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
case s.kind
of skUnknown:
# Introduced in this pass! Leave it as an identifier.
result = n
of skProc, skMethod, skIterator, skConverter:
result = symChoice(c, n, s)
of skTemplate:
result = semTemplateExpr(c, n, s, false)
of skMacro:
result = semMacroExpr(c, n, s, false)
of skGenericParam:
result = newSymNode(s)
of skParam:
result = n
of skType:
if (s.typ != nil) and (s.typ.kind != tyGenericParam): result = newSymNode(s)
else: result = n
else: result = newSymNode(s)
proc getIdentNode(n: PNode): PNode =
case n.kind
of nkPostfix: result = getIdentNode(n.sons[1])
of nkPragmaExpr, nkAccQuoted: result = getIdentNode(n.sons[0])
of nkIdent: result = n
else:
illFormedAst(n)
result = nil
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode =
var
L: int
a: PNode
s: PSym
result = n
if n == nil: return
case n.kind
of nkIdent, nkAccQuoted:
s = lookUp(c, n)
if withinBind in flags: result = symChoice(c, n, s)
else: result = semGenericStmtSymbol(c, n, s)
of nkDotExpr:
s = QualifiedLookUp(c, n, true)
if s != nil: result = semGenericStmtSymbol(c, n, s)
of nkSym..nkNilLit:
nil
of nkBind:
result = semGenericStmt(c, n.sons[0], {withinBind})
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1)
s = qualifiedLookup(c, n.sons[0], false)
if (s != nil):
case s.kind
of skMacro:
return semMacroExpr(c, n, s, false)
of skTemplate:
return semTemplateExpr(c, n, s, false)
of skUnknown, skParam:
# Leave it as an identifier.
of skProc, skMethod, skIterator, skConverter:
n.sons[0] = symChoice(c, n.sons[0], s)
of skGenericParam:
n.sons[0] = newSymNode(s)
of skType:
# bad hack for generics:
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
n.sons[0] = newSymNode(s)
else: n.sons[0] = newSymNode(s)
for i in countup(1, sonsLen(n) - 1):
n.sons[i] = semGenericStmt(c, n.sons[i], flags)
of nkMacroStmt:
result = semMacroStmt(c, n, false)
of nkIfStmt:
for i in countup(0, sonsLen(n) - 1):
n.sons[i] = semGenericStmtScope(c, n.sons[i])
of nkWhileStmt:
openScope(c.tab)
for i in countup(0, sonsLen(n) - 1): n.sons[i] = semGenericStmt(c, n.sons[i])
closeScope(c.tab)
of nkCaseStmt:
openScope(c.tab)
n.sons[0] = semGenericStmt(c, n.sons[0])
for i in countup(1, sonsLen(n) - 1):
a = n.sons[i]
checkMinSonsLen(a, 1)
L = sonsLen(a)
for j in countup(0, L - 2): a.sons[j] = semGenericStmt(c, a.sons[j])
a.sons[L - 1] = semGenericStmtScope(c, a.sons[L - 1])
closeScope(c.tab)
of nkForStmt:
L = sonsLen(n)
openScope(c.tab)
n.sons[L - 2] = semGenericStmt(c, n.sons[L - 2])
for i in countup(0, L - 3): addDecl(c, newSymS(skUnknown, n.sons[i], c))
n.sons[L - 1] = semGenericStmt(c, n.sons[L - 1])
closeScope(c.tab)
of nkBlockStmt, nkBlockExpr, nkBlockType:
checkSonsLen(n, 2)
openScope(c.tab)
if n.sons[0] != nil: addDecl(c, newSymS(skUnknown, n.sons[0], c))
n.sons[1] = semGenericStmt(c, n.sons[1])
closeScope(c.tab)
of nkTryStmt:
checkMinSonsLen(n, 2)
n.sons[0] = semGenericStmtScope(c, n.sons[0])
for i in countup(1, sonsLen(n) - 1):
a = n.sons[i]
checkMinSonsLen(a, 1)
L = sonsLen(a)
for j in countup(0, L - 2):
a.sons[j] = semGenericStmt(c, a.sons[j], {withinTypeDesc})
a.sons[L - 1] = semGenericStmtScope(c, a.sons[L - 1])
of nkVarSection:
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): IllFormedAst(a)
checkMinSonsLen(a, 3)
L = sonsLen(a)
a.sons[L - 2] = semGenericStmt(c, a.sons[L - 2], {withinTypeDesc})
a.sons[L - 1] = semGenericStmt(c, a.sons[L - 1])
for j in countup(0, L - 3):
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
of nkGenericParams:
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if (a.kind != nkIdentDefs): IllFormedAst(a)
checkMinSonsLen(a, 3)
L = sonsLen(a)
a.sons[L - 2] = semGenericStmt(c, a.sons[L - 2], {withinTypeDesc}) # do not perform symbol lookup for default
# expressions
for j in countup(0, L - 3):
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
of nkConstSection:
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkConstDef): IllFormedAst(a)
checkSonsLen(a, 3)
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[0]), c))
a.sons[1] = semGenericStmt(c, a.sons[1], {withinTypeDesc})
a.sons[2] = semGenericStmt(c, a.sons[2])
of nkTypeSection:
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): IllFormedAst(a)
checkSonsLen(a, 3)
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[0]), c))
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): IllFormedAst(a)
checkSonsLen(a, 3)
if a.sons[1] != nil:
openScope(c.tab)
a.sons[1] = semGenericStmt(c, a.sons[1])
a.sons[2] = semGenericStmt(c, a.sons[2], {withinTypeDesc})
closeScope(c.tab)
else:
a.sons[2] = semGenericStmt(c, a.sons[2], {withinTypeDesc})
of nkEnumTy:
checkMinSonsLen(n, 1)
if n.sons[0] != nil:
n.sons[0] = semGenericStmt(c, n.sons[0], {withinTypeDesc})
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkEnumFieldDef: a = n.sons[i].sons[0]
of nkIdent: a = n.sons[i]
else: illFormedAst(n)
addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos -
1)
of nkObjectTy, nkTupleTy:
nil
of nkFormalParams:
checkMinSonsLen(n, 1)
if n.sons[0] != nil:
n.sons[0] = semGenericStmt(c, n.sons[0], {withinTypeDesc})
for i in countup(1, sonsLen(n) - 1):
a = n.sons[i]
if (a.kind != nkIdentDefs): IllFormedAst(a)
checkMinSonsLen(a, 3)
L = sonsLen(a)
a.sons[L - 1] = semGenericStmt(c, a.sons[L - 2], {withinTypeDesc})
a.sons[L - 1] = semGenericStmt(c, a.sons[L - 1])
for j in countup(0, L - 3):
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkIteratorDef, nkLambda:
checkSonsLen(n, codePos + 1)
addDecl(c, newSymS(skUnknown, getIdentNode(n.sons[0]), c))
openScope(c.tab)
n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos])
if n.sons[paramsPos] != nil:
if n.sons[paramsPos].sons[0] != nil:
addDecl(c, newSym(skUnknown, getIdent("result"), nil))
n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos])
n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos])
n.sons[codePos] = semGenericStmtScope(c, n.sons[codePos])
closeScope(c.tab)
else:
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semGenericStmt(c, n.sons[i], flags)

254
rod/seminst.nim Executable file
View File

@@ -0,0 +1,254 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module does the instantiation of generic procs and types.
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym
# generates an instantiated proc
proc searchInstTypes(tab: TIdTable, key: PType): PType =
var
t: PType
match: bool
# returns nil if we need to declare this type
result = PType(IdTableGet(tab, key))
if (result == nil) and (tab.counter > 0):
# we have to do a slow linear search because types may need
# to be compared by their structure:
for h in countup(0, high(tab.data)):
t = PType(tab.data[h].key)
if t != nil:
if key.containerId == t.containerID:
match = true
for j in countup(0, sonsLen(t) - 1):
# XXX sameType is not really correct for nested generics?
if not sameType(t.sons[j], key.sons[j]):
match = false
break
if match:
return PType(tab.data[h].val)
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
result = t.kind in GenericTypes
proc containsGenericType(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil)
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable) =
var
s, q: PSym
t: PType
a: PNode
if (n.kind != nkGenericParams):
InternalError(n.info, "instantiateGenericParamList; no generic params")
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind != nkSym:
InternalError(a.info, "instantiateGenericParamList; no symbol")
q = a.sym
if not (q.typ.kind in {tyTypeDesc, tyGenericParam}): continue
s = newSym(skType, q.name, getCurrOwner())
t = PType(IdTableGet(pt, q.typ))
if t == nil: liMessage(a.info, errCannotInstantiateX, s.name.s)
if (t.kind == tyGenericParam):
InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
s.typ = t
addDecl(c, s)
proc GenericCacheGet(c: PContext, genericSym, instSym: PSym): PSym =
var a, b: PSym
result = nil
for i in countup(0, sonsLen(c.generics) - 1):
if c.generics.sons[i].kind != nkExprEqExpr:
InternalError(genericSym.info, "GenericCacheGet")
a = c.generics.sons[i].sons[0].sym
if genericSym.id == a.id:
b = c.generics.sons[i].sons[1].sym
if equalParams(b.typ.n, instSym.typ.n) == paramsEqual:
#if gVerbosity > 0 then
# MessageOut('found in cache: ' + getProcHeader(instSym));
return b
proc GenericCacheAdd(c: PContext, genericSym, instSym: PSym) =
var n: PNode
n = newNode(nkExprEqExpr)
addSon(n, newSymNode(genericSym))
addSon(n, newSymNode(instSym))
addSon(c.generics, n)
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym =
# generates an instantiated proc
var
oldPrc, oldMod: PSym
oldP: PProcCon
n: PNode
if c.InstCounter > 1000: InternalError(fn.ast.info, "nesting too deep")
inc(c.InstCounter)
oldP = c.p # restore later
# NOTE: for access of private fields within generics from a different module
# and other identifiers we fake the current module temporarily!
oldMod = c.module
c.module = getModule(fn)
result = copySym(fn, false)
incl(result.flags, sfFromGeneric)
result.owner = getCurrOwner().owner
n = copyTree(fn.ast)
result.ast = n
pushOwner(result)
openScope(c.tab)
if (n.sons[genericParamsPos] == nil):
InternalError(n.info, "generateInstance")
n.sons[namePos] = newSymNode(result)
pushInfoContext(info)
instantiateGenericParamList(c, n.sons[genericParamsPos], pt)
n.sons[genericParamsPos] = nil # semantic checking for the parameters:
if n.sons[paramsPos] != nil:
semParamList(c, n.sons[ParamsPos], nil, result)
addParams(c, result.typ.n)
else:
result.typ = newTypeS(tyProc, c)
addSon(result.typ, nil)
oldPrc = GenericCacheGet(c, fn, result)
if oldPrc == nil:
# add it here, so that recursive generic procs are possible:
GenericCacheAdd(c, fn, result)
addDecl(c, result)
if n.sons[codePos] != nil:
c.p = newProcCon(result)
if result.kind in {skProc, skMethod, skConverter}:
addResult(c, result.typ.sons[0], n.info)
addResultNode(c, n)
n.sons[codePos] = semStmtScope(c, n.sons[codePos])
else:
result = oldPrc
popInfoContext()
closeScope(c.tab) # close scope for parameters
popOwner()
c.p = oldP # restore
c.module = oldMod
dec(c.InstCounter)
proc checkConstructedType(info: TLineInfo, t: PType) =
if (tfAcyclic in t.flags) and (skipTypes(t, abstractInst).kind != tyObject):
liMessage(info, errInvalidPragmaX, "acyclic")
if computeSize(t) < 0:
liMessage(info, errIllegalRecursionInTypeX, typeToString(t))
if (t.kind == tyVar) and (t.sons[0].kind == tyVar):
liMessage(info, errVarVarTypeNotAllowed)
type
TReplTypeVars{.final.} = object
c*: PContext
typeMap*: TIdTable # map PType to PType
symMap*: TIdTable # map PSym to PSym
info*: TLineInfo
proc ReplaceTypeVarsT(cl: var TReplTypeVars, t: PType): PType
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
var length: int
result = nil
if n != nil:
result = copyNode(n)
result.typ = ReplaceTypeVarsT(cl, n.typ)
case n.kind
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
nil
of nkSym:
result.sym = ReplaceTypeVarsS(cl, n.sym)
else:
length = sonsLen(n)
if length > 0:
newSons(result, length)
for i in countup(0, length - 1):
result.sons[i] = ReplaceTypeVarsN(cl, n.sons[i])
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
if s == nil:
return nil
result = PSym(idTableGet(cl.symMap, s))
if (result == nil):
result = copySym(s, false)
incl(result.flags, sfFromGeneric)
idTablePut(cl.symMap, s, result)
result.typ = ReplaceTypeVarsT(cl, s.typ)
result.owner = s.owner
result.ast = ReplaceTypeVarsN(cl, s.ast)
proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.typeMap, t))
if result == nil:
liMessage(t.sym.info, errCannotInstantiateX, typeToString(t))
elif result.kind == tyGenericParam:
InternalError(cl.info, "substitution with generic parameter")
proc ReplaceTypeVarsT(cl: var TReplTypeVars, t: PType): PType =
var body, newbody, x, header: PType
result = t
if t == nil: return
case t.kind
of tyGenericParam:
result = lookupTypeVar(cl, t)
of tyGenericInvokation:
body = t.sons[0]
if body.kind != tyGenericBody: InternalError(cl.info, "no generic body")
header = nil
for i in countup(1, sonsLen(t) - 1):
if t.sons[i].kind == tyGenericParam:
x = lookupTypeVar(cl, t.sons[i])
if header == nil: header = copyType(t, t.owner, false)
header.sons[i] = x
else:
x = t.sons[i]
idTablePut(cl.typeMap, body.sons[i - 1], x)
if header == nil: header = t
result = searchInstTypes(gInstTypes, header)
if result != nil: return
result = newType(tyGenericInst, t.sons[0].owner)
for i in countup(0, sonsLen(t) - 1):
# if one of the params is not concrete, we cannot do anything
# but we already raised an error!
addSon(result, header.sons[i])
idTablePut(gInstTypes, header, result)
newbody = ReplaceTypeVarsT(cl, lastSon(body))
newbody.n = ReplaceTypeVarsN(cl, lastSon(body).n)
addSon(result, newbody) #writeln(output, ropeToStr(Typetoyaml(newbody)));
checkConstructedType(cl.info, newbody)
of tyGenericBody:
InternalError(cl.info, "ReplaceTypeVarsT: tyGenericBody")
result = ReplaceTypeVarsT(cl, lastSon(t))
else:
if containsGenericType(t):
result = copyType(t, t.owner, false)
for i in countup(0, sonsLen(result) - 1):
result.sons[i] = ReplaceTypeVarsT(cl, result.sons[i])
result.n = ReplaceTypeVarsN(cl, result.n)
if result.Kind in GenericTypes:
liMessage(cl.info, errCannotInstantiateX, TypeToString(t, preferName)) #writeln(output, ropeToStr(Typetoyaml(result)));
#checkConstructedType(cl.info, result);
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
var cl: TReplTypeVars
InitIdTable(cl.symMap)
InitIdTable(cl.typeMap)
cl.info = n.info
cl.c = c
result = ReplaceTypeVarsT(cl, header)
proc generateTypeInstance(p: PContext, pt: TIdTable, arg: PNode, t: PType): PType =
var cl: TReplTypeVars
InitIdTable(cl.symMap)
copyIdTable(cl.typeMap, pt)
cl.info = arg.info
cl.c = p
pushInfoContext(arg.info)
result = ReplaceTypeVarsT(cl, t)
popInfoContext()
proc partialSpecialization(c: PContext, n: PNode, s: PSym): PNode =
result = n

897
rod/semstmts.nim Executable file
View File

@@ -0,0 +1,897 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this module does the semantic checking of statements
proc semWhen(c: PContext, n: PNode): PNode =
var it, e: PNode
result = nil
for i in countup(0, sonsLen(n) - 1):
it = n.sons[i]
if it == nil: illFormedAst(n)
case it.kind
of nkElifBranch:
checkSonsLen(it, 2)
e = semConstExpr(c, it.sons[0])
checkBool(e)
if (e.kind != nkIntLit): InternalError(n.info, "semWhen")
if (e.intVal != 0) and (result == nil):
result = semStmt(c, it.sons[1]) # do not open a new scope!
of nkElse:
checkSonsLen(it, 1)
if result == nil:
result = semStmt(c, it.sons[0]) # do not open a new scope!
else: illFormedAst(n)
if result == nil:
result = newNodeI(nkNilLit, n.info) # The ``when`` statement implements the mechanism for platform dependant
# code. Thus we try to ensure here consistent ID allocation after the
# ``when`` statement.
IDsynchronizationPoint(200)
proc semIf(c: PContext, n: PNode): PNode =
var it: PNode
result = n
for i in countup(0, sonsLen(n) - 1):
it = n.sons[i]
if it == nil: illFormedAst(n)
case it.kind
of nkElifBranch:
checkSonsLen(it, 2)
openScope(c.tab)
it.sons[0] = semExprWithType(c, it.sons[0])
checkBool(it.sons[0])
it.sons[1] = semStmt(c, it.sons[1])
closeScope(c.tab)
of nkElse:
if sonsLen(it) == 1: it.sons[0] = semStmtScope(c, it.sons[0])
else: illFormedAst(it)
else: illFormedAst(n)
proc semDiscard(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1)
n.sons[0] = semExprWithType(c, n.sons[0])
if n.sons[0].typ == nil: liMessage(n.info, errInvalidDiscard)
proc semBreakOrContinue(c: PContext, n: PNode): PNode =
var
s: PSym
x: PNode
result = n
checkSonsLen(n, 1)
if n.sons[0] != nil:
case n.sons[0].kind
of nkIdent: s = lookUp(c, n.sons[0])
of nkSym: s = n.sons[0].sym
else: illFormedAst(n)
if (s.kind == skLabel) and (s.owner.id == c.p.owner.id):
x = newSymNode(s)
x.info = n.info
incl(s.flags, sfUsed)
n.sons[0] = x
else:
liMessage(n.info, errInvalidControlFlowX, s.name.s)
elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0):
liMessage(n.info, errInvalidControlFlowX, renderTree(n, {renderNoComments}))
proc semBlock(c: PContext, n: PNode): PNode =
var labl: PSym
result = n
Inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2)
openScope(c.tab) # BUGFIX: label is in the scope of block!
if n.sons[0] != nil:
labl = newSymS(skLabel, n.sons[0], c)
addDecl(c, labl)
n.sons[0] = newSymNode(labl) # BUGFIX
n.sons[1] = semStmt(c, n.sons[1])
closeScope(c.tab)
Dec(c.p.nestedBlockCounter)
proc semAsm(con: PContext, n: PNode): PNode =
var
str, sub: string
a, b, c: int
e: PSym
marker: char
result = n
checkSonsLen(n, 2)
marker = pragmaAsm(con, n.sons[0])
if marker == '\0':
marker = '`' # default marker
case n.sons[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
result = copyNode(n)
str = n.sons[1].strVal
if str == "":
liMessage(n.info, errEmptyAsm) # now parse the string literal and substitute symbols:
a = 0
while true:
b = strutils.find(str, marker, a)
if b < 0: sub = copy(str, a)
else: sub = copy(str, a, b - 1)
if sub != "": addSon(result, newStrNode(nkStrLit, sub))
if b < 0: break
c = strutils.find(str, marker, b + 1)
if c < 0: sub = copy(str, b + 1)
else: sub = copy(str, b + 1, c - 1)
if sub != "":
e = SymtabGet(con.tab, getIdent(sub))
if e != nil:
if e.kind == skStub: loadStub(e)
addSon(result, newSymNode(e))
else:
addSon(result, newStrNode(nkStrLit, sub))
if c < 0: break
a = c + 1
else: illFormedAst(n)
proc semWhile(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 2)
openScope(c.tab)
n.sons[0] = semExprWithType(c, n.sons[0])
CheckBool(n.sons[0])
inc(c.p.nestedLoopCounter)
n.sons[1] = semStmt(c, n.sons[1])
dec(c.p.nestedLoopCounter)
closeScope(c.tab)
proc semCase(c: PContext, n: PNode): PNode =
var
length: int
covered: biggestint # for some types we count to check if all cases have been covered
chckCovered: bool
x: PNode
# check selector:
result = n
checkMinSonsLen(n, 2)
openScope(c.tab)
n.sons[0] = semExprWithType(c, n.sons[0])
chckCovered = false
covered = 0
case skipTypes(n.sons[0].Typ, abstractVarRange).Kind
of tyInt..tyInt64, tyChar, tyEnum:
chckCovered = true
of tyFloat..tyFloat128, tyString:
nil
else: liMessage(n.info, errSelectorMustBeOfCertainTypes)
for i in countup(1, sonsLen(n) - 1):
x = n.sons[i]
case x.kind
of nkOfBranch:
checkMinSonsLen(x, 2)
semCaseBranch(c, n, x, i, covered)
length = sonsLen(x)
x.sons[length - 1] = semStmtScope(c, x.sons[length - 1])
of nkElifBranch:
chckCovered = false
checkSonsLen(x, 2)
x.sons[0] = semExprWithType(c, x.sons[0])
checkBool(x.sons[0])
x.sons[1] = semStmtScope(c, x.sons[1])
of nkElse:
chckCovered = false
checkSonsLen(x, 1)
x.sons[0] = semStmtScope(c, x.sons[0])
else: illFormedAst(x)
if chckCovered and (covered != lengthOrd(n.sons[0].typ)):
liMessage(n.info, errNotAllCasesCovered)
closeScope(c.tab)
proc semAsgn(c: PContext, n: PNode): PNode =
var
le: PType
a: PNode
id: PIdent
checkSonsLen(n, 2)
a = n.sons[0]
case a.kind
of nkDotExpr:
# r.f = x
# --> `f=` (r, x)
checkSonsLen(a, 2)
id = considerAcc(a.sons[1])
result = newNodeI(nkCall, n.info)
addSon(result, newIdentNode(getIdent(id.s & '='), n.info))
addSon(result, semExpr(c, a.sons[0]))
addSon(result, semExpr(c, n.sons[1]))
result = semDirectCallAnalyseEffects(c, result, {})
if result != nil:
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
return
of nkBracketExpr:
# a[i..j] = x
# --> `[..]=`(a, i, j, x)
result = newNodeI(nkCall, n.info)
checkSonsLen(a, 2)
if a.sons[1].kind == nkRange:
checkSonsLen(a.sons[1], 2)
addSon(result,
newIdentNode(getIdent(whichSliceOpr(a.sons[1]) & '='), n.info))
addSon(result, semExpr(c, a.sons[0]))
addSonIfNotNil(result, semExpr(c, a.sons[1].sons[0]))
addSonIfNotNil(result, semExpr(c, a.sons[1].sons[1]))
addSon(result, semExpr(c, n.sons[1]))
result = semDirectCallAnalyseEffects(c, result, {})
if result != nil:
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
return
else:
addSon(result, newIdentNode(getIdent("[]="), n.info))
addSon(result, semExpr(c, a.sons[0]))
addSon(result, semExpr(c, a.sons[1]))
addSon(result, semExpr(c, n.sons[1]))
result = semDirectCallAnalyseEffects(c, result, {})
if result != nil:
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
return
else:
nil
n.sons[0] = semExprWithType(c, n.sons[0], {efLValue})
n.sons[1] = semExprWithType(c, n.sons[1])
le = n.sons[0].typ
if (skipTypes(le, {tyGenericInst}).kind != tyVar) and
(IsAssignable(n.sons[0]) == arNone):
# Direct assignment to a discriminant is allowed!
liMessage(n.sons[0].info, errXCannotBeAssignedTo,
renderTree(n.sons[0], {renderNoComments}))
else:
n.sons[1] = fitNode(c, le, n.sons[1])
fixAbstractType(c, n)
result = n
proc SemReturn(c: PContext, n: PNode): PNode =
var
restype: PType
a: PNode # temporary assignment for code generator
result = n
checkSonsLen(n, 1)
if not (c.p.owner.kind in {skConverter, skMethod, skProc, skMacro}):
liMessage(n.info, errXNotAllowedHere, "\'return\'")
if (n.sons[0] != nil):
n.sons[0] = SemExprWithType(c, n.sons[0]) # check for type compatibility:
restype = c.p.owner.typ.sons[0]
if (restype != nil):
a = newNodeI(nkAsgn, n.sons[0].info)
n.sons[0] = fitNode(c, restype, n.sons[0]) # optimize away ``return result``, because it would be transformed
# to ``result = result; return``:
if (n.sons[0].kind == nkSym) and (sfResult in n.sons[0].sym.flags):
n.sons[0] = nil
else:
if (c.p.resultSym == nil): InternalError(n.info, "semReturn")
addSon(a, semExprWithType(c, newSymNode(c.p.resultSym)))
addSon(a, n.sons[0])
n.sons[0] = a
else:
liMessage(n.info, errCannotReturnExpr)
proc SemYield(c: PContext, n: PNode): PNode =
var restype: PType
result = n
checkSonsLen(n, 1)
if (c.p.owner == nil) or (c.p.owner.kind != skIterator):
liMessage(n.info, errYieldNotAllowedHere)
if (n.sons[0] != nil):
n.sons[0] = SemExprWithType(c, n.sons[0]) # check for type compatibility:
restype = c.p.owner.typ.sons[0]
if (restype != nil):
n.sons[0] = fitNode(c, restype, n.sons[0])
if (n.sons[0].typ == nil): InternalError(n.info, "semYield")
else:
liMessage(n.info, errCannotReturnExpr)
proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode =
result = fitNode(c, typ, n)
if (result.kind in {nkHiddenStdConv, nkHiddenSubConv}):
changeType(result.sons[1], typ)
result = result.sons[1]
elif not sameType(result.typ, typ):
changeType(result, typ)
proc semVar(c: PContext, n: PNode): PNode =
var
length: int
a, b, def: PNode
typ, tup: PType
v: PSym
result = copyNode(n)
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): IllFormedAst(a)
checkMinSonsLen(a, 3)
length = sonsLen(a)
if a.sons[length - 2] != nil: typ = semTypeNode(c, a.sons[length - 2], nil)
else: typ = nil
if a.sons[length - 1] != nil:
def = semExprWithType(c, a.sons[length - 1]) # BUGFIX: ``fitNode`` is needed here!
# check type compability between def.typ and typ:
if (typ != nil): def = fitNode(c, typ, def)
else: typ = def.typ
else:
def = nil
if not typeAllowed(typ, skVar):
#debug(typ);
liMessage(a.info, errXisNoType, typeToString(typ))
tup = skipTypes(typ, {tyGenericInst})
if a.kind == nkVarTuple:
if tup.kind != tyTuple: liMessage(a.info, errXExpected, "tuple")
if length - 2 != sonsLen(tup):
liMessage(a.info, errWrongNumberOfVariables)
b = newNodeI(nkVarTuple, a.info)
newSons(b, length)
b.sons[length - 2] = nil # no type desc
b.sons[length - 1] = def
addSon(result, b)
for j in countup(0, length - 3):
if (c.p.owner.kind == skModule):
v = semIdentWithPragma(c, skVar, a.sons[j], {sfStar, sfMinus})
incl(v.flags, sfGlobal)
else:
v = semIdentWithPragma(c, skVar, a.sons[j], {})
if v.flags * {sfStar, sfMinus} != {}: incl(v.flags, sfInInterface)
addInterfaceDecl(c, v)
if a.kind != nkVarTuple:
v.typ = typ
b = newNodeI(nkIdentDefs, a.info)
addSon(b, newSymNode(v))
addSon(b, nil) # no type description
addSon(b, copyTree(def))
addSon(result, b)
else:
v.typ = tup.sons[j]
b.sons[j] = newSymNode(v)
proc semConst(c: PContext, n: PNode): PNode =
var
a, def, b: PNode
v: PSym
typ: PType
result = copyNode(n)
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkConstDef): IllFormedAst(a)
checkSonsLen(a, 3)
if (c.p.owner.kind == skModule):
v = semIdentWithPragma(c, skConst, a.sons[0], {sfStar, sfMinus})
incl(v.flags, sfGlobal)
else:
v = semIdentWithPragma(c, skConst, a.sons[0], {})
if a.sons[1] != nil: typ = semTypeNode(c, a.sons[1], nil)
else: typ = nil
def = semAndEvalConstExpr(c, a.sons[2]) # check type compability between def.typ and typ:
if (typ != nil):
def = fitRemoveHiddenConv(c, typ, def)
else:
typ = def.typ
if not typeAllowed(typ, skConst):
liMessage(a.info, errXisNoType, typeToString(typ))
v.typ = typ
v.ast = def # no need to copy
if v.flags * {sfStar, sfMinus} != {}: incl(v.flags, sfInInterface)
addInterfaceDecl(c, v)
b = newNodeI(nkConstDef, a.info)
addSon(b, newSymNode(v))
addSon(b, nil) # no type description
addSon(b, copyTree(def))
addSon(result, b)
proc semFor(c: PContext, n: PNode): PNode =
var
length: int
v, countup: PSym
iter: PType
countupNode, call: PNode
result = n
checkMinSonsLen(n, 3)
length = sonsLen(n)
openScope(c.tab)
if n.sons[length - 2].kind == nkRange:
checkSonsLen(n.sons[length - 2], 2) # convert ``in 3..5`` to ``in countup(3, 5)``
countupNode = newNodeI(nkCall, n.sons[length - 2].info)
countUp = StrTableGet(magicsys.systemModule.Tab, getIdent("countup"))
if (countUp == nil): liMessage(countupNode.info, errSystemNeeds, "countup")
newSons(countupNode, 3)
countupnode.sons[0] = newSymNode(countup)
countupNode.sons[1] = n.sons[length - 2].sons[0]
countupNode.sons[2] = n.sons[length - 2].sons[1]
n.sons[length - 2] = countupNode
n.sons[length - 2] = semExprWithType(c, n.sons[length - 2], {efWantIterator})
call = n.sons[length - 2]
if (call.kind != nkCall) or (call.sons[0].kind != nkSym) or
(call.sons[0].sym.kind != skIterator):
liMessage(n.sons[length - 2].info, errIteratorExpected)
iter = skipTypes(n.sons[length - 2].typ, {tyGenericInst})
if iter.kind != tyTuple:
if length != 3: liMessage(n.info, errWrongNumberOfVariables)
v = newSymS(skForVar, n.sons[0], c)
v.typ = iter
n.sons[0] = newSymNode(v)
addDecl(c, v)
else:
if length - 2 != sonsLen(iter): liMessage(n.info, errWrongNumberOfVariables)
for i in countup(0, length - 3):
v = newSymS(skForVar, n.sons[i], c)
v.typ = iter.sons[i]
n.sons[i] = newSymNode(v)
addDecl(c, v)
Inc(c.p.nestedLoopCounter)
n.sons[length - 1] = SemStmt(c, n.sons[length - 1])
closeScope(c.tab)
Dec(c.p.nestedLoopCounter)
proc semRaise(c: PContext, n: PNode): PNode =
var typ: PType
result = n
checkSonsLen(n, 1)
if n.sons[0] != nil:
n.sons[0] = semExprWithType(c, n.sons[0])
typ = n.sons[0].typ
if (typ.kind != tyRef) or (typ.sons[0].kind != tyObject):
liMessage(n.info, errExprCannotBeRaised)
proc semTry(c: PContext, n: PNode): PNode =
var
length: int
a: PNode
typ: PType
check: TIntSet
result = n
checkMinSonsLen(n, 2)
n.sons[0] = semStmtScope(c, n.sons[0])
IntSetInit(check)
for i in countup(1, sonsLen(n) - 1):
a = n.sons[i]
checkMinSonsLen(a, 1)
length = sonsLen(a)
if a.kind == nkExceptBranch:
for j in countup(0, length - 2):
typ = semTypeNode(c, a.sons[j], nil)
if typ.kind == tyRef: typ = typ.sons[0]
if (typ.kind != tyObject):
liMessage(a.sons[j].info, errExprCannotBeRaised)
a.sons[j] = newNodeI(nkType, a.sons[j].info)
a.sons[j].typ = typ
if IntSetContainsOrIncl(check, typ.id):
liMessage(a.sons[j].info, errExceptionAlreadyHandled)
elif a.kind != nkFinally:
illFormedAst(n) # last child of an nkExcept/nkFinally branch is a statement:
a.sons[length - 1] = semStmtScope(c, a.sons[length - 1])
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
var
L: int
s: PSym
a, def: PNode
typ: PType
result = copyNode(n)
if n.kind != nkGenericParams: InternalError(n.info, "semGenericParamList")
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind != nkIdentDefs: illFormedAst(n)
L = sonsLen(a)
def = a.sons[L - 1]
if a.sons[L - 2] != nil: typ = semTypeNode(c, a.sons[L - 2], nil)
elif def != nil: typ = newTypeS(tyExpr, c)
else: typ = nil
for j in countup(0, L - 3):
if (typ == nil) or (typ.kind == tyTypeDesc):
s = newSymS(skType, a.sons[j], c)
s.typ = newTypeS(tyGenericParam, c)
else:
s = newSymS(skGenericParam, a.sons[j], c)
s.typ = typ
s.ast = def
s.typ.sym = s
if father != nil: addSon(father, s.typ)
s.position = i
addSon(result, newSymNode(s))
addDecl(c, s)
proc addGenericParamListToScope(c: PContext, n: PNode) =
var a: PNode
if n.kind != nkGenericParams:
InternalError(n.info, "addGenericParamListToScope")
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind != nkSym: internalError(a.info, "addGenericParamListToScope")
addDecl(c, a.sym)
proc SemTypeSection(c: PContext, n: PNode): PNode =
var
s: PSym
t, body: PType
a: PNode
result = n # process the symbols on the left side for the whole type section, before
# we even look at the type definitions on the right
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): IllFormedAst(a)
checkSonsLen(a, 3)
if (c.p.owner.kind == skModule):
s = semIdentWithPragma(c, skType, a.sons[0], {sfStar, sfMinus})
incl(s.flags, sfGlobal)
else:
s = semIdentWithPragma(c, skType, a.sons[0], {})
if s.flags * {sfStar, sfMinus} != {}: incl(s.flags, sfInInterface)
s.typ = newTypeS(tyForward, c)
s.typ.sym = s # process pragmas:
if a.sons[0].kind == nkPragmaExpr:
pragma(c, s, a.sons[0].sons[1], typePragmas) # add it here, so that recursive types are possible:
addInterfaceDecl(c, s)
a.sons[0] = newSymNode(s)
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): IllFormedAst(a)
checkSonsLen(a, 3)
if (a.sons[0].kind != nkSym): IllFormedAst(a)
s = a.sons[0].sym
if (s.magic == mNone) and (a.sons[2] == nil):
liMessage(a.info, errImplOfXexpected, s.name.s)
if s.magic != mNone: processMagicType(c, s)
if a.sons[1] != nil:
# We have a generic type declaration here. In generic types,
# symbol lookup needs to be done here.
openScope(c.tab)
pushOwner(s)
s.typ.kind = tyGenericBody
if s.typ.containerID != 0:
InternalError(a.info, "semTypeSection: containerID")
s.typ.containerID = getID()
a.sons[1] = semGenericParamList(c, a.sons[1], s.typ)
addSon(s.typ, nil) # to be filled out later
s.ast = a
body = semTypeNode(c, a.sons[2], nil)
if body != nil: body.sym = s
s.typ.sons[sonsLen(s.typ) - 1] = body #debug(s.typ);
popOwner()
closeScope(c.tab)
elif a.sons[2] != nil:
# process the type's body:
pushOwner(s)
t = semTypeNode(c, a.sons[2], s.typ)
if (t != s.typ) and (s.typ != nil):
internalError(a.info, "semTypeSection()")
s.typ = t
s.ast = a
popOwner()
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.sons[0].kind != nkSym): IllFormedAst(a)
s = a.sons[0].sym # compute the type's size and check for illegal recursions:
if a.sons[1] == nil:
if (a.sons[2] != nil) and
(a.sons[2].kind in {nkSym, nkIdent, nkAccQuoted}):
# type aliases are hard:
#MessageOut('for type ' + typeToString(s.typ));
t = semTypeNode(c, a.sons[2], nil)
if t.kind in {tyObject, tyEnum}:
assignType(s.typ, t)
s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ)
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
s.typ = semProcTypeNode(c, n, genericParams, nil)
proc addParams(c: PContext, n: PNode) =
for i in countup(1, sonsLen(n) - 1):
if (n.sons[i].kind != nkSym): InternalError(n.info, "addParams")
addDecl(c, n.sons[i].sym)
proc semBorrow(c: PContext, n: PNode, s: PSym) =
var b: PSym
# search for the correct alias:
b = SearchForBorrowProc(c, s, c.tab.tos - 2)
if b == nil:
liMessage(n.info, errNoSymbolToBorrowFromFound) # store the alias:
n.sons[codePos] = newSymNode(b)
proc sideEffectsCheck(c: PContext, s: PSym) =
if {sfNoSideEffect, sfSideEffect} * s.flags ==
{sfNoSideEffect, sfSideEffect}:
liMessage(s.info, errXhasSideEffects, s.name.s)
proc addResult(c: PContext, t: PType, info: TLineInfo) =
var s: PSym
if t != nil:
s = newSym(skVar, getIdent("result"), getCurrOwner())
s.info = info
s.typ = t
incl(s.flags, sfResult)
incl(s.flags, sfUsed)
addDecl(c, s)
c.p.resultSym = s
proc addResultNode(c: PContext, n: PNode) =
if c.p.resultSym != nil: addSon(n, newSymNode(c.p.resultSym))
proc semLambda(c: PContext, n: PNode): PNode =
var
s: PSym
oldP: PProcCon
result = n
checkSonsLen(n, codePos + 1)
s = newSym(skProc, getIdent(":anonymous"), getCurrOwner())
s.info = n.info
oldP = c.p # restore later
s.ast = n
n.sons[namePos] = newSymNode(s)
pushOwner(s)
openScope(c.tab)
if (n.sons[genericParamsPos] != nil):
illFormedAst(n) # process parameters:
if n.sons[paramsPos] != nil:
semParamList(c, n.sons[ParamsPos], nil, s)
addParams(c, s.typ.n)
else:
s.typ = newTypeS(tyProc, c)
addSon(s.typ, nil)
s.typ.callConv = ccClosure
if n.sons[pragmasPos] != nil: pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
s.options = gOptions
if n.sons[codePos] != nil:
if sfImportc in s.flags:
liMessage(n.sons[codePos].info, errImplOfXNotAllowed, s.name.s)
c.p = newProcCon(s)
addResult(c, s.typ.sons[0], n.info)
n.sons[codePos] = semStmtScope(c, n.sons[codePos])
addResultNode(c, n)
else:
liMessage(n.info, errImplOfXexpected, s.name.s)
closeScope(c.tab) # close scope for parameters
popOwner()
c.p = oldP # restore
result.typ = s.typ
proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
validPragmas: TSpecialWords): PNode =
var
s, proto: PSym
oldP: PProcCon
gp: PNode
result = n
checkSonsLen(n, codePos + 1)
if c.p.owner.kind == skModule:
s = semIdentVis(c, kind, n.sons[0], {sfStar})
incl(s.flags, sfGlobal)
else:
s = semIdentVis(c, kind, n.sons[0], {})
n.sons[namePos] = newSymNode(s)
oldP = c.p # restore later
if sfStar in s.flags: incl(s.flags, sfInInterface)
s.ast = n
pushOwner(s)
openScope(c.tab)
if n.sons[genericParamsPos] != nil:
n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos]
else:
gp = newNodeI(nkGenericParams, n.info) # process parameters:
if n.sons[paramsPos] != nil:
semParamList(c, n.sons[ParamsPos], gp, s)
if sonsLen(gp) > 0: n.sons[genericParamsPos] = gp
addParams(c, s.typ.n)
else:
s.typ = newTypeS(tyProc, c)
addSon(s.typ, nil)
proto = SearchForProc(c, s, c.tab.tos - 2) # -2 because we have a scope open
# for parameters
if proto == nil:
if oldP.owner.kind != skModule:
s.typ.callConv = ccClosure
else:
s.typ.callConv = lastOptionEntry(c).defaultCC # add it here, so that recursive procs are possible:
# -2 because we have a scope open for parameters
if kind in OverloadableSyms:
addInterfaceOverloadableSymAt(c, s, c.tab.tos - 2)
else:
addDeclAt(c, s, c.tab.tos - 2)
if n.sons[pragmasPos] != nil: pragma(c, s, n.sons[pragmasPos], validPragmas)
else:
if n.sons[pragmasPos] != nil:
liMessage(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc)
if not (sfForward in proto.flags):
liMessage(n.info, errAttemptToRedefineX, proto.name.s)
excl(proto.flags, sfForward)
closeScope(c.tab) # close scope with wrong parameter symbols
openScope(c.tab) # open scope for old (correct) parameter symbols
if proto.ast.sons[genericParamsPos] != nil:
addGenericParamListToScope(c, proto.ast.sons[genericParamsPos])
addParams(c, proto.typ.n)
proto.info = s.info # more accurate line information
s.typ = proto.typ
s = proto
n.sons[genericParamsPos] = proto.ast.sons[genericParamsPos]
n.sons[paramsPos] = proto.ast.sons[paramsPos]
if (n.sons[namePos].kind != nkSym): InternalError(n.info, "semProcAux")
n.sons[namePos].sym = proto
proto.ast = n # needed for code generation
popOwner()
pushOwner(s)
s.options = gOptions
if n.sons[codePos] != nil:
if {sfImportc, sfBorrow} * s.flags != {}:
liMessage(n.sons[codePos].info, errImplOfXNotAllowed, s.name.s)
if (n.sons[genericParamsPos] == nil):
c.p = newProcCon(s)
if (s.typ.sons[0] != nil) and (kind != skIterator):
addResult(c, s.typ.sons[0], n.info)
n.sons[codePos] = semStmtScope(c, n.sons[codePos])
if (s.typ.sons[0] != nil) and (kind != skIterator): addResultNode(c, n)
else:
if (s.typ.sons[0] != nil) and (kind != skIterator):
addDecl(c, newSym(skUnknown, getIdent("result"), nil))
n.sons[codePos] = semGenericStmtScope(c, n.sons[codePos])
else:
if proto != nil: liMessage(n.info, errImplOfXexpected, proto.name.s)
if {sfImportc, sfBorrow} * s.flags == {}: incl(s.flags, sfForward)
elif sfBorrow in s.flags: semBorrow(c, n, s)
sideEffectsCheck(c, s)
closeScope(c.tab) # close scope for parameters
popOwner()
c.p = oldP # restore
proc semIterator(c: PContext, n: PNode): PNode =
var
t: PType
s: PSym
result = semProcAux(c, n, skIterator, iteratorPragmas)
s = result.sons[namePos].sym
t = s.typ
if t.sons[0] == nil: liMessage(n.info, errXNeedsReturnType, "iterator")
if n.sons[codePos] == nil: liMessage(n.info, errImplOfXexpected, s.name.s)
proc semProc(c: PContext, n: PNode): PNode =
result = semProcAux(c, n, skProc, procPragmas)
proc semMethod(c: PContext, n: PNode): PNode =
if not isTopLevel(c): liMessage(n.info, errXOnlyAtModuleScope, "method")
result = semProcAux(c, n, skMethod, methodPragmas)
proc semConverterDef(c: PContext, n: PNode): PNode =
var
t: PType
s: PSym
if not isTopLevel(c): liMessage(n.info, errXOnlyAtModuleScope, "converter")
checkSonsLen(n, codePos + 1)
if n.sons[genericParamsPos] != nil:
liMessage(n.info, errNoGenericParamsAllowedForX, "converter")
result = semProcAux(c, n, skConverter, converterPragmas)
s = result.sons[namePos].sym
t = s.typ
if t.sons[0] == nil: liMessage(n.info, errXNeedsReturnType, "converter")
if sonsLen(t) != 2: liMessage(n.info, errXRequiresOneArgument, "converter")
addConverter(c, s)
proc semMacroDef(c: PContext, n: PNode): PNode =
var
t: PType
s: PSym
checkSonsLen(n, codePos + 1)
if n.sons[genericParamsPos] != nil:
liMessage(n.info, errNoGenericParamsAllowedForX, "macro")
result = semProcAux(c, n, skMacro, macroPragmas)
s = result.sons[namePos].sym
t = s.typ
if t.sons[0] == nil: liMessage(n.info, errXNeedsReturnType, "macro")
if sonsLen(t) != 2: liMessage(n.info, errXRequiresOneArgument, "macro")
if n.sons[codePos] == nil: liMessage(n.info, errImplOfXexpected, s.name.s)
proc evalInclude(c: PContext, n: PNode): PNode =
var
fileIndex: int
f: string
result = newNodeI(nkStmtList, n.info)
addSon(result, n) # the rodwriter needs include information!
for i in countup(0, sonsLen(n) - 1):
f = getModuleFile(n.sons[i])
fileIndex = includeFilename(f)
if IntSetContainsOrIncl(c.includedFiles, fileIndex):
liMessage(n.info, errRecursiveDependencyX, f)
addSon(result, semStmt(c, gIncludeFile(f)))
IntSetExcl(c.includedFiles, fileIndex)
proc semCommand(c: PContext, n: PNode): PNode =
result = semExpr(c, n)
if result.typ != nil: liMessage(n.info, errDiscardValue)
proc SemStmt(c: PContext, n: PNode): PNode =
const # must be last statements in a block:
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
var length: int
result = n
if n == nil: return
if nfSem in n.flags: return
case n.kind
of nkAsgn:
result = semAsgn(c, n)
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkMacroStmt, nkCallStrLit:
result = semCommand(c, n)
of nkEmpty, nkCommentStmt, nkNilLit:
nil
of nkBlockStmt:
result = semBlock(c, n)
of nkStmtList:
length = sonsLen(n)
for i in countup(0, length - 1):
n.sons[i] = semStmt(c, n.sons[i])
if (n.sons[i].kind in LastBlockStmts):
for j in countup(i + 1, length - 1):
case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty:
nil
else: liMessage(n.sons[j].info, errStmtInvalidAfterReturn)
of nkRaiseStmt:
result = semRaise(c, n)
of nkVarSection:
result = semVar(c, n)
of nkConstSection:
result = semConst(c, n)
of nkTypeSection:
result = SemTypeSection(c, n)
of nkIfStmt:
result = SemIf(c, n)
of nkWhenStmt:
result = semWhen(c, n)
of nkDiscardStmt:
result = semDiscard(c, n)
of nkWhileStmt:
result = semWhile(c, n)
of nkTryStmt:
result = semTry(c, n)
of nkBreakStmt, nkContinueStmt:
result = semBreakOrContinue(c, n)
of nkForStmt:
result = semFor(c, n)
of nkCaseStmt:
result = semCase(c, n)
of nkReturnStmt:
result = semReturn(c, n)
of nkAsmStmt:
result = semAsm(c, n)
of nkYieldStmt:
result = semYield(c, n)
of nkPragma:
pragma(c, c.p.owner, n, stmtPragmas)
of nkIteratorDef:
result = semIterator(c, n)
of nkProcDef:
result = semProc(c, n)
of nkMethodDef:
result = semMethod(c, n)
of nkConverterDef:
result = semConverterDef(c, n)
of nkMacroDef:
result = semMacroDef(c, n)
of nkTemplateDef:
result = semTemplateDef(c, n)
of nkImportStmt:
if not isTopLevel(c): liMessage(n.info, errXOnlyAtModuleScope, "import")
result = evalImport(c, n)
of nkFromStmt:
if not isTopLevel(c): liMessage(n.info, errXOnlyAtModuleScope, "from")
result = evalFrom(c, n)
of nkIncludeStmt:
if not isTopLevel(c): liMessage(n.info, errXOnlyAtModuleScope, "include")
result = evalInclude(c, n)
else: liMessage(n.info, errStmtExpected)
if result == nil: InternalError(n.info, "SemStmt: result = nil")
incl(result.flags, nfSem)
proc semStmtScope(c: PContext, n: PNode): PNode =
openScope(c.tab)
result = semStmt(c, n)
closeScope(c.tab)

204
rod/semtempl.nim Executable file
View File

@@ -0,0 +1,204 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
proc isExpr(n: PNode): bool =
# returns true if ``n`` looks like an expression
if n == nil:
return false
case n.kind
of nkIdent..nkNilLit:
result = true
of nkCall..nkPassAsOpenArray:
for i in countup(0, sonsLen(n) - 1):
if not isExpr(n.sons[i]):
return false
result = true
else: result = false
proc isTypeDesc(n: PNode): bool =
# returns true if ``n`` looks like a type desc
if n == nil:
return false
case n.kind
of nkIdent, nkSym, nkType:
result = true
of nkDotExpr, nkBracketExpr:
for i in countup(0, sonsLen(n) - 1):
if not isTypeDesc(n.sons[i]):
return false
result = true
of nkTypeOfExpr..nkEnumTy:
result = true
else: result = false
proc evalTemplateAux(c: PContext, templ, actual: PNode, sym: PSym): PNode =
var p: PSym
if templ == nil:
return nil
case templ.kind
of nkSym:
p = templ.sym
if (p.kind == skParam) and (p.owner.id == sym.id):
result = copyTree(actual.sons[p.position])
else:
result = copyNode(templ)
of nkNone..nkIdent, nkType..nkNilLit: # atom
result = copyNode(templ)
else:
result = copyNode(templ)
newSons(result, sonsLen(templ))
for i in countup(0, sonsLen(templ) - 1):
result.sons[i] = evalTemplateAux(c, templ.sons[i], actual, sym)
var evalTemplateCounter: int = 0
proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode =
# to prevend endless recursion in templates
# instantation
var
f, a: int
arg: PNode
f = sonsLen(s.typ) # if the template has zero arguments, it can be called without ``()``
# `n` is then a nkSym or something similar
case n.kind
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: a = sonsLen(
n)
else: a = 0
if a > f: liMessage(n.info, errWrongNumberOfArguments)
result = copyNode(n)
for i in countup(1, f - 1):
if i < a: arg = n.sons[i]
else: arg = copyTree(s.typ.n.sons[i].sym.ast)
if arg == nil: liMessage(n.info, errWrongNumberOfArguments)
if not (s.typ.sons[i].kind in {tyTypeDesc, tyStmt, tyExpr}):
# concrete type means semantic checking for argument:
arg = fitNode(c, s.typ.sons[i], semExprWithType(c, arg))
addSon(result, arg)
proc evalTemplate(c: PContext, n: PNode, sym: PSym): PNode =
var args: PNode
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
liMessage(n.info, errTemplateInstantiationTooNested) # replace each param by the corresponding node:
args = evalTemplateArgs(c, n, sym)
result = evalTemplateAux(c, sym.ast.sons[codePos], args, sym)
dec(evalTemplateCounter)
proc symChoice(c: PContext, n: PNode, s: PSym): PNode =
var
a: PSym
o: TOverloadIter
i: int
i = 0
a = initOverloadIter(o, c, n)
while a != nil:
a = nextOverloadIter(o, c, n)
inc(i)
if i <= 1:
result = newSymNode(s)
result.info = n.info
markUsed(n, s)
else:
# semantic checking requires a type; ``fitNode`` deals with it
# appropriately
result = newNodeIT(nkSymChoice, n.info, newTypeS(tyNone, c))
a = initOverloadIter(o, c, n)
while a != nil:
addSon(result, newSymNode(a))
a = nextOverloadIter(o, c, n)
proc resolveTemplateParams(c: PContext, n: PNode, withinBind: bool,
toBind: var TIntSet): PNode =
var s: PSym
if n == nil:
return nil
case n.kind
of nkIdent:
if not withinBind and not IntSetContains(toBind, n.ident.id):
s = SymTabLocalGet(c.Tab, n.ident)
if (s != nil):
result = newSymNode(s)
result.info = n.info
else:
result = n
else:
IntSetIncl(toBind, n.ident.id)
result = symChoice(c, n, lookup(c, n))
of nkSym..nkNilLit: # atom
result = n
of nkBind:
result = resolveTemplateParams(c, n.sons[0], true, toBind)
else:
result = n
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = resolveTemplateParams(c, n.sons[i], withinBind, toBind)
proc transformToExpr(n: PNode): PNode =
var realStmt: int
result = n
case n.kind
of nkStmtList:
realStmt = - 1
for i in countup(0, sonsLen(n) - 1):
case n.sons[i].kind
of nkCommentStmt, nkEmpty, nkNilLit:
nil
else:
if realStmt == - 1: realStmt = i
else: realStmt = - 2
if realStmt >= 0: result = transformToExpr(n.sons[realStmt])
else: n.kind = nkStmtListExpr
of nkBlockStmt:
n.kind = nkBlockExpr #nkIfStmt: n.kind := nkIfExpr; // this is not correct!
else:
nil
proc semTemplateDef(c: PContext, n: PNode): PNode =
var
s: PSym
toBind: TIntSet
if c.p.owner.kind == skModule:
s = semIdentVis(c, skTemplate, n.sons[0], {sfStar})
incl(s.flags, sfGlobal)
else:
s = semIdentVis(c, skTemplate, n.sons[0], {})
if sfStar in s.flags:
incl(s.flags, sfInInterface) # check parameter list:
pushOwner(s)
openScope(c.tab)
n.sons[namePos] = newSymNode(s) # check that no pragmas exist:
if n.sons[pragmasPos] != nil:
liMessage(n.info, errNoPragmasAllowedForX, "template") # check that no generic parameters exist:
if n.sons[genericParamsPos] != nil:
liMessage(n.info, errNoGenericParamsAllowedForX, "template")
if (n.sons[paramsPos] == nil):
# use ``stmt`` as implicit result type
s.typ = newTypeS(tyProc, c)
s.typ.n = newNodeI(nkFormalParams, n.info)
addSon(s.typ, newTypeS(tyStmt, c))
addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0]))
else:
semParamList(c, n.sons[ParamsPos], nil, s)
if n.sons[paramsPos].sons[0] == nil:
# use ``stmt`` as implicit result type
s.typ.sons[0] = newTypeS(tyStmt, c)
s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0])
addParams(c, s.typ.n) # resolve parameters:
IntSetInit(toBind)
n.sons[codePos] = resolveTemplateParams(c, n.sons[codePos], false, toBind)
if not (s.typ.sons[0].kind in {tyStmt, tyTypeDesc}):
n.sons[codePos] = transformToExpr(n.sons[codePos]) # only parameters are resolved, no type checking is performed
closeScope(c.tab)
popOwner()
s.ast = n
result = n
if n.sons[codePos] == nil:
liMessage(n.info, errImplOfXexpected, s.name.s) # add identifier of template as a last step to not allow
# recursive templates
addInterfaceDecl(c, s)

690
rod/semtypes.nim Executable file
View File

@@ -0,0 +1,690 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this module does the semantic checking of type declarations
proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
result = IndexTypesMatch(c, formal, arg.typ, arg)
if result == nil: typeMismatch(arg, formal, arg.typ)
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
if prev == nil:
result = newTypeS(kind, c)
else:
result = prev
if result.kind == tyForward: result.kind = kind
proc semEnum(c: PContext, n: PNode, prev: PType): PType =
var
counter, x: BiggestInt
e: PSym
base: PType
v: PNode
counter = 0
base = nil
result = newOrPrevType(tyEnum, prev, c)
result.n = newNodeI(nkEnumTy, n.info)
checkMinSonsLen(n, 1)
if n.sons[0] != nil:
base = semTypeNode(c, n.sons[0].sons[0], nil)
if base.kind != tyEnum:
liMessage(n.sons[0].info, errInheritanceOnlyWithEnums)
counter = lastOrd(base) + 1
addSon(result, base)
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkEnumFieldDef:
e = newSymS(skEnumField, n.sons[i].sons[0], c)
v = semConstExpr(c, n.sons[i].sons[1])
x = getOrdValue(v)
if i != 1:
if (x != counter): incl(result.flags, tfEnumHasWholes)
if x < counter:
liMessage(n.sons[i].info, errInvalidOrderInEnumX, e.name.s)
counter = x
of nkSym:
e = n.sons[i].sym
of nkIdent:
e = newSymS(skEnumField, n.sons[i], c)
else: illFormedAst(n)
e.typ = result
e.position = int(counter)
if (result.sym != nil) and (sfInInterface in result.sym.flags):
incl(e.flags, sfUsed) # BUGFIX
incl(e.flags, sfInInterface) # BUGFIX
StrTableAdd(c.module.tab, e) # BUGFIX
addSon(result.n, newSymNode(e))
addDeclAt(c, e, c.tab.tos - 1)
inc(counter)
proc semSet(c: PContext, n: PNode, prev: PType): PType =
var base: PType
result = newOrPrevType(tySet, prev, c)
if sonsLen(n) == 2:
base = semTypeNode(c, n.sons[1], nil)
addSon(result, base)
if base.kind == tyGenericInst: base = lastSon(base)
if base.kind != tyGenericParam:
if not isOrdinalType(base): liMessage(n.info, errOrdinalTypeExpected)
if lengthOrd(base) > MaxSetElements: liMessage(n.info, errSetTooBig)
else:
liMessage(n.info, errXExpectsOneTypeParam, "set")
proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
prev: PType): PType =
var base: PType
result = newOrPrevType(kind, prev, c)
if sonsLen(n) == 2:
base = semTypeNode(c, n.sons[1], nil)
addSon(result, base)
else:
liMessage(n.info, errXExpectsOneTypeParam, kindStr)
proc semAnyRef(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
prev: PType): PType =
var base: PType
result = newOrPrevType(kind, prev, c)
if sonsLen(n) == 1:
base = semTypeNode(c, n.sons[0], nil)
addSon(result, base)
else:
liMessage(n.info, errXExpectsOneTypeParam, kindStr)
proc semVarType(c: PContext, n: PNode, prev: PType): PType =
var base: PType
result = newOrPrevType(tyVar, prev, c)
if sonsLen(n) == 1:
base = semTypeNode(c, n.sons[0], nil)
if base.kind == tyVar: liMessage(n.info, errVarVarTypeNotAllowed)
addSon(result, base)
else:
liMessage(n.info, errXExpectsOneTypeParam, "var")
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyDistinct, prev, c)
if sonsLen(n) == 1: addSon(result, semTypeNode(c, n.sons[0], nil))
else: liMessage(n.info, errXExpectsOneTypeParam, "distinct")
proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
var a, b: PNode
if (n.kind != nkRange): InternalError(n.info, "semRangeAux")
checkSonsLen(n, 2)
result = newOrPrevType(tyRange, prev, c)
result.n = newNodeI(nkRange, n.info)
if (n.sons[0] == nil) or (n.sons[1] == nil):
liMessage(n.Info, errRangeIsEmpty)
a = semConstExpr(c, n.sons[0])
b = semConstExpr(c, n.sons[1])
if not sameType(a.typ, b.typ): liMessage(n.info, errPureTypeMismatch)
if not (a.typ.kind in
{tyInt..tyInt64, tyEnum, tyBool, tyChar, tyFloat..tyFloat128}):
liMessage(n.info, errOrdinalTypeExpected)
if enumHasWholes(a.typ):
liMessage(n.info, errEnumXHasWholes, a.typ.sym.name.s)
if not leValue(a, b): liMessage(n.Info, errRangeIsEmpty)
addSon(result.n, a)
addSon(result.n, b)
addSon(result, b.typ)
proc semRange(c: PContext, n: PNode, prev: PType): PType =
result = nil
if sonsLen(n) == 2:
if n.sons[1].kind == nkRange: result = semRangeAux(c, n.sons[1], prev)
else: liMessage(n.sons[0].info, errRangeExpected)
else:
liMessage(n.info, errXExpectsOneTypeParam, "range")
proc semArray(c: PContext, n: PNode, prev: PType): PType =
var indx, base: PType
result = newOrPrevType(tyArray, prev, c)
if sonsLen(n) == 3:
# 3 = length(array indx base)
if n.sons[1].kind == nkRange: indx = semRangeAux(c, n.sons[1], nil)
else: indx = semTypeNode(c, n.sons[1], nil)
addSon(result, indx)
if indx.kind == tyGenericInst: indx = lastSon(indx)
if indx.kind != tyGenericParam:
if not isOrdinalType(indx):
liMessage(n.sons[1].info, errOrdinalTypeExpected)
if enumHasWholes(indx):
liMessage(n.sons[1].info, errEnumXHasWholes, indx.sym.name.s)
base = semTypeNode(c, n.sons[2], nil)
addSon(result, base)
else:
liMessage(n.info, errArrayExpectsTwoTypeParams)
proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
var base: PType
result = newOrPrevType(tyOrdinal, prev, c)
if sonsLen(n) == 2:
base = semTypeNode(c, n.sons[1], nil)
if base.kind != tyGenericParam:
if not isOrdinalType(base):
liMessage(n.sons[1].info, errOrdinalTypeExpected)
addSon(result, base)
else:
liMessage(n.info, errXExpectsOneTypeParam, "ordinal")
proc semTypeIdent(c: PContext, n: PNode): PSym =
result = qualifiedLookup(c, n, true)
if (result != nil):
markUsed(n, result)
if result.kind != skType: liMessage(n.info, errTypeExpected)
else:
liMessage(n.info, errIdentifierExpected)
proc semTuple(c: PContext, n: PNode, prev: PType): PType =
var
length, counter: int
typ: PType
check: TIntSet
a: PNode
field: PSym
result = newOrPrevType(tyTuple, prev, c)
result.n = newNodeI(nkRecList, n.info)
IntSetInit(check)
counter = 0
for i in countup(0, sonsLen(n) - 1):
a = n.sons[i]
if (a.kind != nkIdentDefs): IllFormedAst(a)
checkMinSonsLen(a, 3)
length = sonsLen(a)
if a.sons[length - 2] != nil: typ = semTypeNode(c, a.sons[length - 2], nil)
else: liMessage(a.info, errTypeExpected)
if a.sons[length - 1] != nil:
liMessage(a.sons[length - 1].info, errInitHereNotAllowed)
for j in countup(0, length - 3):
field = newSymS(skField, a.sons[j], c)
field.typ = typ
field.position = counter
inc(counter)
if IntSetContainsOrIncl(check, field.name.id):
liMessage(a.sons[j].info, errAttemptToRedefine, field.name.s)
addSon(result.n, newSymNode(field))
addSon(result, typ)
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
var
elem: PType
isConcrete: bool
if (s.typ == nil) or (s.typ.kind != tyGenericBody):
liMessage(n.info, errCannotInstantiateX, s.name.s)
result = newOrPrevType(tyGenericInvokation, prev, c)
if (s.typ.containerID == 0): InternalError(n.info, "semtypes.semGeneric")
if sonsLen(n) != sonsLen(s.typ): liMessage(n.info, errWrongNumberOfArguments)
addSon(result, s.typ)
isConcrete = true # iterate over arguments:
for i in countup(1, sonsLen(n) - 1):
elem = semTypeNode(c, n.sons[i], nil)
if elem.kind == tyGenericParam: isConcrete = false
addSon(result, elem)
if isConcrete:
if s.ast == nil: liMessage(n.info, errCannotInstantiateX, s.name.s)
result = instGenericContainer(c, n, result)
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym =
# identifier with visibility
var v: PIdent
result = nil
if n.kind == nkPostfix:
if (sonsLen(n) == 2) and (n.sons[0].kind == nkIdent):
result = newSymS(kind, n.sons[1], c)
v = n.sons[0].ident
if (sfStar in allowed) and (v.id == ord(wStar)):
incl(result.flags, sfStar)
elif (sfMinus in allowed) and (v.id == ord(wMinus)):
incl(result.flags, sfMinus)
else:
liMessage(n.sons[0].info, errInvalidVisibilityX, v.s)
else:
illFormedAst(n)
else:
result = newSymS(kind, n, c)
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym =
if n.kind == nkPragmaExpr:
checkSonsLen(n, 2)
result = semIdentVis(c, kind, n.sons[0], allowed)
case kind
of skType:
# process pragmas later, because result.typ has not been set yet
of skField:
pragma(c, result, n.sons[1], fieldPragmas)
of skVar:
pragma(c, result, n.sons[1], varPragmas)
of skConst:
pragma(c, result, n.sons[1], constPragmas)
else:
nil
else:
result = semIdentVis(c, kind, n, allowed)
proc checkForOverlap(c: PContext, t, ex: PNode, branchIndex: int) =
for i in countup(1, branchIndex - 1):
for j in countup(0, sonsLen(t.sons[i]) - 2):
if overlap(t.sons[i].sons[j], ex):
#MessageOut(renderTree(t));
liMessage(ex.info, errDuplicateCaseLabel)
proc semBranchExpr(c: PContext, t: PNode, ex: var PNode) =
ex = semConstExpr(c, ex)
checkMinSonsLen(t, 1)
if (cmpTypes(t.sons[0].typ, ex.typ) <= isConvertible):
typeMismatch(ex, t.sons[0].typ, ex.typ)
proc SemCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
covered: var biggestInt) =
var b: PNode
for i in countup(0, sonsLen(branch) - 2):
b = branch.sons[i]
if b.kind == nkRange:
checkSonsLen(b, 2)
semBranchExpr(c, t, b.sons[0])
semBranchExpr(c, t, b.sons[1])
if emptyRange(b.sons[0], b.sons[1]):
#MessageOut(renderTree(t));
liMessage(b.info, errRangeIsEmpty)
covered = covered + getOrdValue(b.sons[1]) - getOrdValue(b.sons[0]) + 1
else:
semBranchExpr(c, t, branch.sons[i]) # NOT: `b`, because of var-param!
inc(covered)
checkForOverlap(c, t, branch.sons[i], branchIndex)
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
father: PNode, rectype: PSym)
proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
father: PNode, rectype: PSym) =
var
covered: biggestint
chckCovered: bool
a, b: PNode
typ: PType
a = copyNode(n)
checkMinSonsLen(n, 2)
semRecordNodeAux(c, n.sons[0], check, pos, a, rectype)
if a.sons[0].kind != nkSym:
internalError("semRecordCase: dicriminant is no symbol")
incl(a.sons[0].sym.flags, sfDiscriminant)
covered = 0
typ = skipTypes(a.sons[0].Typ, abstractVar)
if not isOrdinalType(typ): liMessage(n.info, errSelectorMustBeOrdinal)
if firstOrd(typ) < 0:
liMessage(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s)
if lengthOrd(typ) > 0x00007FFF:
liMessage(n.info, errLenXinvalid, a.sons[0].sym.name.s)
chckCovered = true
for i in countup(1, sonsLen(n) - 1):
b = copyTree(n.sons[i])
case n.sons[i].kind
of nkOfBranch:
checkMinSonsLen(b, 2)
semCaseBranch(c, a, b, i, covered)
of nkElse:
chckCovered = false
checkSonsLen(b, 1)
else: illFormedAst(n)
delSon(b, sonsLen(b) - 1)
semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype)
addSon(a, b)
if chckCovered and (covered != lengthOrd(a.sons[0].typ)):
liMessage(a.info, errNotAllCasesCovered)
addSon(father, a)
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
father: PNode, rectype: PSym) =
var
length: int
f: PSym # new field
a, it, e, branch: PNode
typ: PType
if n == nil:
return # BUGFIX: nil is possible
case n.kind
of nkRecWhen:
branch = nil # the branch to take
for i in countup(0, sonsLen(n) - 1):
it = n.sons[i]
if it == nil: illFormedAst(n)
case it.kind
of nkElifBranch:
checkSonsLen(it, 2)
e = semConstExpr(c, it.sons[0])
checkBool(e)
if (e.kind != nkIntLit): InternalError(e.info, "semRecordNodeAux")
if (e.intVal != 0) and (branch == nil): branch = it.sons[1]
of nkElse:
checkSonsLen(it, 1)
if branch == nil: branch = it.sons[0]
else: illFormedAst(n)
if branch != nil: semRecordNodeAux(c, branch, check, pos, father, rectype)
of nkRecCase:
semRecordCase(c, n, check, pos, father, rectype)
of nkNilLit:
if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info))
of nkRecList:
# attempt to keep the nesting at a sane level:
if father.kind == nkRecList: a = father
else: a = copyNode(n)
for i in countup(0, sonsLen(n) - 1):
semRecordNodeAux(c, n.sons[i], check, pos, a, rectype)
if a != father: addSon(father, a)
of nkIdentDefs:
checkMinSonsLen(n, 3)
length = sonsLen(n)
if (father.kind != nkRecList) and (length >= 4):
a = newNodeI(nkRecList, n.info)
else:
a = nil
if n.sons[length - 1] != nil:
liMessage(n.sons[length - 1].info, errInitHereNotAllowed)
if n.sons[length - 2] == nil: liMessage(n.info, errTypeExpected)
typ = semTypeNode(c, n.sons[length - 2], nil)
for i in countup(0, sonsLen(n) - 3):
f = semIdentWithPragma(c, skField, n.sons[i], {sfStar, sfMinus})
f.typ = typ
f.position = pos
if (rectype != nil) and ({sfImportc, sfExportc} * rectype.flags != {}) and
(f.loc.r == nil):
f.loc.r = toRope(f.name.s)
f.flags = f.flags + ({sfImportc, sfExportc} * rectype.flags)
inc(pos)
if IntSetContainsOrIncl(check, f.name.id):
liMessage(n.sons[i].info, errAttemptToRedefine, f.name.s)
if a == nil: addSon(father, newSymNode(f))
else: addSon(a, newSymNode(f))
if a != nil: addSon(father, a)
else: illFormedAst(n)
proc addInheritedFieldsAux(c: PContext, check: var TIntSet, pos: var int,
n: PNode) =
case n.kind
of nkRecCase:
if (n.sons[0].kind != nkSym): InternalError(n.info, "addInheritedFieldsAux")
addInheritedFieldsAux(c, check, pos, n.sons[0])
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
addInheritedFieldsAux(c, check, pos, lastSon(n.sons[i]))
else: internalError(n.info, "addInheritedFieldsAux(record case branch)")
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
addInheritedFieldsAux(c, check, pos, n.sons[i])
of nkSym:
IntSetIncl(check, n.sym.name.id)
inc(pos)
else: InternalError(n.info, "addInheritedFieldsAux()")
proc addInheritedFields(c: PContext, check: var TIntSet, pos: var int,
obj: PType) =
if (sonsLen(obj) > 0) and (obj.sons[0] != nil):
addInheritedFields(c, check, pos, obj.sons[0])
addInheritedFieldsAux(c, check, pos, obj.n)
proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
var
check: TIntSet
base: PType
pos: int
IntSetInit(check)
pos = 0 # n.sons[0] contains the pragmas (if any). We process these later...
checkSonsLen(n, 3)
if n.sons[1] != nil:
base = semTypeNode(c, n.sons[1].sons[0], nil)
if base.kind == tyObject: addInheritedFields(c, check, pos, base)
else: liMessage(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects)
else:
base = nil
if n.kind != nkObjectTy: InternalError(n.info, "semObjectNode")
result = newOrPrevType(tyObject, prev, c)
addSon(result, base)
result.n = newNodeI(nkRecList, n.info)
semRecordNodeAux(c, n.sons[2], check, pos, result.n, result.sym)
if (base != nil) and (tfFinal in base.flags):
liMessage(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects)
proc addTypeVarsOfGenericBody(c: PContext, t: PType, genericParams: PNode,
cl: var TIntSet): PType =
var
L: int
s: PSym
result = t
if (t == nil): return
if IntSetContainsOrIncl(cl, t.id): return
case t.kind
of tyGenericBody:
result = newTypeS(tyGenericInvokation, c)
addSon(result, t)
for i in countup(0, sonsLen(t) - 2):
if t.sons[i].kind != tyGenericParam:
InternalError("addTypeVarsOfGenericBody")
s = copySym(t.sons[i].sym)
s.position = sonsLen(genericParams)
addDecl(c, s)
addSon(genericParams, newSymNode(s))
addSon(result, t.sons[i])
of tyGenericInst:
L = sonsLen(t) - 1
t.sons[L] = addTypeVarsOfGenericBody(c, t.sons[L], genericParams, cl)
of tyGenericInvokation:
for i in countup(1, sonsLen(t) - 1):
t.sons[i] = addTypeVarsOfGenericBody(c, t.sons[i], genericParams, cl)
else:
for i in countup(0, sonsLen(t) - 1):
t.sons[i] = addTypeVarsOfGenericBody(c, t.sons[i], genericParams, cl)
proc paramType(c: PContext, n, genericParams: PNode, cl: var TIntSet): PType =
result = semTypeNode(c, n, nil)
if (genericParams != nil) and (sonsLen(genericParams) == 0):
result = addTypeVarsOfGenericBody(c, result, genericParams, cl)
proc semProcTypeNode(c: PContext, n, genericParams: PNode, prev: PType): PType =
var
length, counter: int
a, def, res: PNode
typ: PType
arg: PSym
check, cl: TIntSet
checkMinSonsLen(n, 1)
result = newOrPrevType(tyProc, prev, c)
result.callConv = lastOptionEntry(c).defaultCC
result.n = newNodeI(nkFormalParams, n.info)
if (genericParams != nil) and (sonsLen(genericParams) == 0): IntSetInit(cl)
if n.sons[0] == nil:
addSon(result, nil) # return type
addSon(result.n, newNodeI(nkType, n.info)) # BUGFIX: nkType must exist!
# XXX but it does not, if n.sons[paramsPos] == nil?
else:
addSon(result, nil)
res = newNodeI(nkType, n.info)
addSon(result.n, res)
IntSetInit(check)
counter = 0
for i in countup(1, sonsLen(n) - 1):
a = n.sons[i]
if (a.kind != nkIdentDefs): IllFormedAst(a)
checkMinSonsLen(a, 3)
length = sonsLen(a)
if a.sons[length - 2] != nil:
typ = paramType(c, a.sons[length - 2], genericParams, cl)
else:
typ = nil
if a.sons[length - 1] != nil:
def = semExprWithType(c, a.sons[length - 1]) # check type compability between def.typ and typ:
if (typ != nil):
if (cmpTypes(typ, def.typ) < isConvertible):
typeMismatch(a.sons[length - 1], typ, def.typ)
def = fitNode(c, typ, def)
else:
typ = def.typ
else:
def = nil
for j in countup(0, length - 3):
arg = newSymS(skParam, a.sons[j], c)
arg.typ = typ
arg.position = counter
inc(counter)
arg.ast = copyTree(def)
if IntSetContainsOrIncl(check, arg.name.id):
liMessage(a.sons[j].info, errAttemptToRedefine, arg.name.s)
addSon(result.n, newSymNode(arg))
addSon(result, typ)
if n.sons[0] != nil:
result.sons[0] = paramType(c, n.sons[0], genericParams, cl)
res.typ = result.sons[0]
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
var length: int
checkMinSonsLen(n, 1)
length = sonsLen(n)
for i in countup(0, length - 2):
n.sons[i] = semStmt(c, n.sons[i])
if length > 0:
result = semTypeNode(c, n.sons[length - 1], prev)
n.typ = result
n.sons[length - 1].typ = result
else:
result = nil
proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
Inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2)
openScope(c.tab)
if n.sons[0] != nil:
addDecl(c, newSymS(skLabel, n.sons[0], c))
result = semStmtListType(c, n.sons[1], prev)
n.sons[1].typ = result
n.typ = result
closeScope(c.tab)
Dec(c.p.nestedBlockCounter)
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
var
s: PSym
t: PType
result = nil
if n == nil: return
case n.kind
of nkTypeOfExpr:
result = semExprWithType(c, n, {efAllowType}).typ
of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else: liMessage(n.info, errTypeExpected)
of nkBracketExpr:
checkMinSonsLen(n, 2)
s = semTypeIdent(c, n.sons[0])
case s.magic
of mArray: result = semArray(c, n, prev)
of mOpenArray: result = semContainer(c, n, tyOpenArray, "openarray", prev)
of mRange: result = semRange(c, n, prev)
of mSet: result = semSet(c, n, prev)
of mOrdinal: result = semOrdinal(c, n, prev)
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
else: result = semGeneric(c, n, s, prev)
of nkIdent, nkDotExpr, nkAccQuoted:
s = semTypeIdent(c, n)
if s.typ == nil: liMessage(n.info, errTypeExpected)
if prev == nil:
result = s.typ
else:
assignType(prev, s.typ)
prev.id = s.typ.id
result = prev
of nkSym:
if (n.sym.kind == skType) and (n.sym.typ != nil):
t = n.sym.typ
if prev == nil:
result = t
else:
assignType(prev, t)
result = prev
markUsed(n, n.sym)
else:
liMessage(n.info, errTypeExpected)
of nkObjectTy:
result = semObjectNode(c, n, prev)
of nkTupleTy:
result = semTuple(c, n, prev)
of nkRefTy:
result = semAnyRef(c, n, tyRef, "ref", prev)
of nkPtrTy:
result = semAnyRef(c, n, tyPtr, "ptr", prev)
of nkVarTy:
result = semVarType(c, n, prev)
of nkDistinctTy:
result = semDistinct(c, n, prev)
of nkProcTy:
checkSonsLen(n, 2)
result = semProcTypeNode(c, n.sons[0], nil, prev) # dummy symbol for `pragma`:
s = newSymS(skProc, newIdentNode(getIdent("dummy"), n.info), c)
s.typ = result
pragma(c, s, n.sons[1], procTypePragmas)
of nkEnumTy:
result = semEnum(c, n, prev)
of nkType:
result = n.typ
of nkStmtListType:
result = semStmtListType(c, n, prev)
of nkBlockType:
result = semBlockType(c, n, prev)
else:
liMessage(n.info, errTypeExpected) #internalError(n.info, 'semTypeNode(' +{&} nodeKindToStr[n.kind] +{&} ')');
proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
m.typ.kind = kind
m.typ.align = size
m.typ.size = size #m.typ.sym := nil;
proc processMagicType(c: PContext, m: PSym) =
case m.magic #registerSysType(m.typ);
of mInt:
setMagicType(m, tyInt, intSize)
of mInt8:
setMagicType(m, tyInt8, 1)
of mInt16:
setMagicType(m, tyInt16, 2)
of mInt32:
setMagicType(m, tyInt32, 4)
of mInt64:
setMagicType(m, tyInt64, 8)
of mFloat:
setMagicType(m, tyFloat, floatSize)
of mFloat32:
setMagicType(m, tyFloat32, 4)
of mFloat64:
setMagicType(m, tyFloat64, 8)
of mBool:
setMagicType(m, tyBool, 1)
of mChar:
setMagicType(m, tyChar, 1)
of mString:
setMagicType(m, tyString, ptrSize)
addSon(m.typ, getSysType(tyChar))
of mCstring:
setMagicType(m, tyCString, ptrSize)
addSon(m.typ, getSysType(tyChar))
of mPointer:
setMagicType(m, tyPointer, ptrSize)
of mEmptySet:
setMagicType(m, tySet, 1)
addSon(m.typ, newTypeS(tyEmpty, c))
of mIntSetBaseType:
setMagicType(m, tyRange, intSize) #intSetBaseType := m.typ;
return
of mNil:
setMagicType(m, tyNil, ptrSize)
of mExpr:
setMagicType(m, tyExpr, 0)
of mStmt:
setMagicType(m, tyStmt, 0)
of mTypeDesc:
setMagicType(m, tyTypeDesc, 0)
of mArray, mOpenArray, mRange, mSet, mSeq, mOrdinal:
return
else: liMessage(m.info, errTypeExpected)

750
rod/sigmatch.nim Executable file
View File

@@ -0,0 +1,750 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements the signature matching for resolving
# the call to overloaded procs, generic procs and operators.
type
TCandidateState = enum
csEmpty, csMatch, csNoMatch
TCandidate{.final.} = object
exactMatches*: int
subtypeMatches*: int
intConvMatches*: int # conversions to int are not as expensive
convMatches*: int
genericMatches*: int
state*: TCandidateState
callee*: PType # may not be nil!
calleeSym*: PSym # may be nil
call*: PNode # modified call
bindings*: TIdTable # maps sym-ids to types
baseTypeMatch*: bool # needed for conversions from T to openarray[T]
# for example
TTypeRelation = enum # order is important!
isNone, isConvertible, isIntConv, isSubtype, isGeneric, isEqual
proc initCandidate(c: var TCandidate, callee: PType) =
c.exactMatches = 0
c.subtypeMatches = 0
c.convMatches = 0
c.intConvMatches = 0
c.genericMatches = 0
c.state = csEmpty
c.callee = callee
c.calleeSym = nil
c.call = nil
c.baseTypeMatch = false
initIdTable(c.bindings) #assert(c.callee <> nil);
proc copyCandidate(a: var TCandidate, b: TCandidate) =
a.exactMatches = b.exactMatches
a.subtypeMatches = b.subtypeMatches
a.convMatches = b.convMatches
a.intConvMatches = b.intConvMatches
a.genericMatches = b.genericMatches
a.state = b.state
a.callee = b.callee
a.calleeSym = b.calleeSym
a.call = copyTree(b.call)
a.baseTypeMatch = b.baseTypeMatch
copyIdTable(a.bindings, b.bindings)
proc cmpCandidates(a, b: TCandidate): int =
result = a.exactMatches - b.exactMatches
if result != 0: return
result = a.genericMatches - b.genericMatches
if result != 0: return
result = a.subtypeMatches - b.subtypeMatches
if result != 0: return
result = a.intConvMatches - b.intConvMatches
if result != 0: return
result = a.convMatches - b.convMatches
proc writeMatches(c: TCandidate) =
Writeln(stdout, "exact matches: " & $(c.exactMatches))
Writeln(stdout, "subtype matches: " & $(c.subtypeMatches))
Writeln(stdout, "conv matches: " & $(c.convMatches))
Writeln(stdout, "intconv matches: " & $(c.intConvMatches))
Writeln(stdout, "generic matches: " & $(c.genericMatches))
proc getNotFoundError(c: PContext, n: PNode): string =
# Gives a detailed error message; this is seperated from semDirectCall,
# as semDirectCall is already pretty slow (and we need this information only
# in case of an error).
var
sym: PSym
o: TOverloadIter
candidates: string
result = msgKindToString(errTypeMismatch)
for i in countup(1, sonsLen(n) - 1):
#debug(n.sons[i].typ);
add(result, typeToString(n.sons[i].typ))
if i != sonsLen(n) - 1: add(result, ", ")
add(result, ')')
candidates = ""
sym = initOverloadIter(o, c, n.sons[0])
while sym != nil:
if sym.kind in {skProc, skMethod, skIterator, skConverter}:
add(candidates, getProcHeader(sym))
add(candidates, "\n")
sym = nextOverloadIter(o, c, n.sons[0])
if candidates != "":
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation
proc concreteType(mapping: TIdTable, t: PType): PType =
case t.kind
of tyArrayConstr:
# make it an array
result = newType(tyArray, t.owner)
addSon(result, t.sons[0]) # XXX: t.owner is wrong for ID!
addSon(result, t.sons[1]) # XXX: semantic checking for the type?
of tyNil:
result = nil # what should it be?
of tyGenericParam:
result = t
while true:
result = PType(idTableGet(mapping, t))
if result == nil: InternalError("lookup failed")
if result.kind != tyGenericParam: break
else:
result = t # Note: empty is valid here
proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
var k: TTypeKind
if a.kind == f.kind:
result = isEqual
else:
k = skipTypes(a, {tyRange}).kind
if k == f.kind: result = isSubtype
elif (f.kind == tyInt) and (k in {tyInt..tyInt32}): result = isIntConv
elif (k >= min) and (k <= max): result = isConvertible
else: result = isNone
proc handleFloatRange(f, a: PType): TTypeRelation =
var k: TTypeKind
if a.kind == f.kind:
result = isEqual
else:
k = skipTypes(a, {tyRange}).kind
if k == f.kind: result = isSubtype
elif (k >= tyFloat) and (k <= tyFloat128): result = isConvertible
else: result = isNone
proc isObjectSubtype(a, f: PType): bool =
var t: PType
t = a
while (t != nil) and (t.id != f.id): t = base(t)
result = t != nil
proc minRel(a, b: TTypeRelation): TTypeRelation =
if a <= b: result = a
else: result = b
proc tupleRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
var
x, y: PSym
m: TTypeRelation
result = isNone
if sonsLen(a) == sonsLen(f):
result = isEqual
for i in countup(0, sonsLen(f) - 1):
m = typeRel(mapping, f.sons[i], a.sons[i])
if m < isSubtype:
return isNone
result = minRel(result, m)
if (f.n != nil) and (a.n != nil):
for i in countup(0, sonsLen(f.n) - 1):
# check field names:
if f.n.sons[i].kind != nkSym: InternalError(f.n.info, "tupleRel")
if a.n.sons[i].kind != nkSym: InternalError(a.n.info, "tupleRel")
x = f.n.sons[i].sym
y = a.n.sons[i].sym
if x.name.id != y.name.id:
return isNone
proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
var
x, concrete: PType
m: TTypeRelation
# is a subtype of f?
result = isNone
assert(f != nil)
assert(a != nil)
if (a.kind == tyGenericInst) and
not (skipTypes(f, {tyVar}).kind in {tyGenericBody, tyGenericInvokation}):
return typeRel(mapping, f, lastSon(a))
if (a.kind == tyVar) and (f.kind != tyVar):
return typeRel(mapping, f, a.sons[0])
case f.kind
of tyEnum:
if (a.kind == f.kind) and (a.id == f.id): result = isEqual
elif (skipTypes(a, {tyRange}).id == f.id): result = isSubtype
of tyBool, tyChar:
if (a.kind == f.kind): result = isEqual
elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype
of tyRange:
if (a.kind == f.kind):
result = typeRel(mapping, base(a), base(f))
if result < isGeneric: result = isNone
elif skipTypes(f, {tyRange}).kind == a.kind:
result = isConvertible # a convertible to f
of tyInt:
result = handleRange(f, a, tyInt8, tyInt32)
of tyInt8:
result = handleRange(f, a, tyInt8, tyInt8)
of tyInt16:
result = handleRange(f, a, tyInt8, tyInt16)
of tyInt32:
result = handleRange(f, a, tyInt, tyInt32)
of tyInt64:
result = handleRange(f, a, tyInt, tyInt64)
of tyFloat:
result = handleFloatRange(f, a)
of tyFloat32:
result = handleFloatRange(f, a)
of tyFloat64:
result = handleFloatRange(f, a)
of tyFloat128:
result = handleFloatRange(f, a)
of tyVar:
if (a.kind == f.kind): result = typeRel(mapping, base(f), base(a))
else: result = typeRel(mapping, base(f), a)
of tyArray, tyArrayConstr:
# tyArrayConstr cannot happen really, but
# we wanna be safe here
case a.kind
of tyArray:
result = minRel(typeRel(mapping, f.sons[0], a.sons[0]),
typeRel(mapping, f.sons[1], a.sons[1]))
if result < isGeneric: result = isNone
of tyArrayConstr:
result = typeRel(mapping, f.sons[1], a.sons[1])
if result < isGeneric:
result = isNone
else:
if (result != isGeneric) and (lengthOrd(f) != lengthOrd(a)):
result = isNone
elif f.sons[0].kind in GenericTypes:
result = minRel(result, typeRel(mapping, f.sons[0], a.sons[0]))
else:
nil
of tyOpenArray:
case a.Kind
of tyOpenArray:
result = typeRel(mapping, base(f), base(a))
if result < isGeneric: result = isNone
of tyArrayConstr:
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
result = isSubtype # [] is allowed here
elif typeRel(mapping, base(f), a.sons[1]) >= isGeneric:
result = isSubtype
of tyArray:
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
result = isSubtype
elif typeRel(mapping, base(f), a.sons[1]) >= isGeneric:
result = isConvertible
of tySequence:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
result = isConvertible
elif typeRel(mapping, base(f), a.sons[0]) >= isGeneric:
result = isConvertible
else:
nil
of tySequence:
case a.Kind
of tyNil:
result = isSubtype
of tySequence:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
result = isSubtype
else:
result = typeRel(mapping, f.sons[0], a.sons[0])
if result < isGeneric: result = isNone
else:
nil
of tyOrdinal:
if isOrdinalType(a):
if a.kind == tyOrdinal: x = a.sons[0]
else: x = a
result = typeRel(mapping, f.sons[0], x)
if result < isGeneric: result = isNone
of tyForward:
InternalError("forward type in typeRel()")
of tyNil:
if a.kind == f.kind: result = isEqual
of tyTuple:
if a.kind == tyTuple: result = tupleRel(mapping, f, a)
of tyObject:
if a.kind == tyObject:
if a.id == f.id: result = isEqual
elif isObjectSubtype(a, f): result = isSubtype
of tyDistinct:
if (a.kind == tyDistinct) and (a.id == f.id): result = isEqual
of tySet:
if a.kind == tySet:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
result = isSubtype
else:
result = typeRel(mapping, f.sons[0], a.sons[0])
if result <= isConvertible:
result = isNone # BUGFIX!
of tyPtr:
case a.kind
of tyPtr:
result = typeRel(mapping, base(f), base(a))
if result <= isConvertible: result = isNone
of tyNil:
result = isSubtype
else:
nil
of tyRef:
case a.kind
of tyRef:
result = typeRel(mapping, base(f), base(a))
if result <= isConvertible: result = isNone
of tyNil:
result = isSubtype
else:
nil
of tyProc:
case a.kind
of tyNil:
result = isSubtype
of tyProc:
if (sonsLen(f) == sonsLen(a)) and (f.callconv == a.callconv):
# Note: We have to do unification for the parameters before the
# return type!
result = isEqual # start with maximum; also correct for no
# params at all
for i in countup(1, sonsLen(f) - 1):
m = typeRel(mapping, f.sons[i], a.sons[i])
if (m == isNone) and
(typeRel(mapping, a.sons[i], f.sons[i]) == isSubtype):
# allow ``f.son`` as subtype of ``a.son``!
result = isConvertible
elif m < isSubtype:
return isNone
else:
result = minRel(m, result)
if f.sons[0] != nil:
if a.sons[0] != nil:
m = typeRel(mapping, f.sons[0], a.sons[0]) # Subtype is sufficient for return types!
if m < isSubtype: result = isNone
elif m == isSubtype: result = isConvertible
else: result = minRel(m, result)
else:
result = isNone
elif a.sons[0] != nil:
result = isNone
if (tfNoSideEffect in f.flags) and not (tfNoSideEffect in a.flags):
result = isNone
else:
nil
of tyPointer:
case a.kind
of tyPointer: result = isEqual
of tyNil: result = isSubtype
of tyRef, tyPtr, tyProc, tyCString: result = isConvertible
else:
nil
of tyString:
case a.kind
of tyString: result = isEqual
of tyNil: result = isSubtype
else:
nil
of tyCString:
# conversion from string to cstring is automatic:
case a.Kind
of tyCString:
result = isEqual
of tyNil:
result = isSubtype
of tyString:
result = isConvertible
of tyPtr:
if a.sons[0].kind == tyChar: result = isConvertible
of tyArray:
if (firstOrd(a.sons[0]) == 0) and
(skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and
(a.sons[1].kind == tyChar):
result = isConvertible
else:
nil
of tyEmpty:
if a.kind == tyEmpty: result = isEqual
of tyGenericInst:
result = typeRel(mapping, lastSon(f), a)
of tyGenericBody:
result = typeRel(mapping, lastSon(f), a)
of tyGenericInvokation:
assert(f.sons[0].kind == tyGenericBody)
if a.kind == tyGenericInvokation:
InternalError("typeRel: tyGenericInvokation -> tyGenericInvokation")
if (a.kind == tyGenericInst):
if (f.sons[0].containerID == a.sons[0].containerID) and
(sonsLen(a) - 1 == sonsLen(f)):
assert(a.sons[0].kind == tyGenericBody)
for i in countup(1, sonsLen(f) - 1):
if a.sons[i].kind == tyGenericParam:
InternalError("wrong instantiated type!")
if typeRel(mapping, f.sons[i], a.sons[i]) < isGeneric: return
result = isGeneric
else:
result = typeRel(mapping, f.sons[0], a)
if result != isNone:
# we steal the generic parameters from the tyGenericBody:
for i in countup(1, sonsLen(f) - 1):
x = PType(idTableGet(mapping, f.sons[0].sons[i - 1]))
if (x == nil) or (x.kind == tyGenericParam):
InternalError("wrong instantiated type!")
idTablePut(mapping, f.sons[i], x)
of tyGenericParam:
x = PType(idTableGet(mapping, f))
if x == nil:
if sonsLen(f) == 0:
# no constraints
concrete = concreteType(mapping, a)
if concrete != nil:
#MessageOut('putting: ' + f.sym.name.s);
idTablePut(mapping, f, concrete)
result = isGeneric
else:
InternalError(f.sym.info, "has constraints: " & f.sym.name.s) # check
# constraints:
for i in countup(0, sonsLen(f) - 1):
if typeRel(mapping, f.sons[i], a) >= isSubtype:
concrete = concreteType(mapping, a)
if concrete != nil:
idTablePut(mapping, f, concrete)
result = isGeneric
break
elif a.kind == tyEmpty:
result = isGeneric
elif x.kind == tyGenericParam:
result = isGeneric
else:
result = typeRel(mapping, x, a) # check if it fits
of tyExpr, tyStmt, tyTypeDesc:
if a.kind == f.kind:
result = isEqual
else:
case a.kind
of tyExpr, tyStmt, tyTypeDesc: result = isGeneric
of tyNil: result = isSubtype
else:
nil
else: internalError("typeRel(" & $f.kind & ')')
proc cmpTypes(f, a: PType): TTypeRelation =
var mapping: TIdTable
InitIdTable(mapping)
result = typeRel(mapping, f, a)
proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
f: PType): PType =
result = PType(idTableGet(m.bindings, f))
if result == nil:
result = generateTypeInstance(c, m.bindings, arg, f)
if result == nil: InternalError(arg.info, "getInstantiatedType")
proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
c: PContext): PNode =
result = newNodeI(kind, arg.info)
if containsGenericType(f): result.typ = getInstantiatedType(c, arg, m, f)
else: result.typ = f
if result.typ == nil: InternalError(arg.info, "implicitConv")
addSon(result, nil)
addSon(result, arg)
proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
arg: PNode): PNode =
result = nil
for i in countup(0, len(c.converters) - 1):
var src = c.converters[i].typ.sons[1]
var dest = c.converters[i].typ.sons[0]
if (typeRel(m.bindings, f, dest) == isEqual) and
(typeRel(m.bindings, src, a) == isEqual):
var s = newSymNode(c.converters[i])
s.typ = c.converters[i].typ
s.info = arg.info
result = newNodeIT(nkHiddenCallConv, arg.info, s.typ.sons[0])
addSon(result, s)
addSon(result, copyTree(arg))
inc(m.convMatches)
return
proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
arg: PNode): PNode =
var r = typeRel(m.bindings, f, a)
case r
of isConvertible:
inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isIntConv:
inc(m.intConvMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isSubtype:
inc(m.subtypeMatches)
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
of isGeneric:
inc(m.genericMatches)
result = copyTree(arg)
result.typ = getInstantiatedType(c, arg, m, f)
# BUG: f may not be the right key!
if (skipTypes(result.typ, abstractVar).kind in {tyTuple, tyOpenArray}):
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
# BUGFIX: use ``result.typ`` and not `f` here
of isEqual:
inc(m.exactMatches)
result = copyTree(arg)
if (skipTypes(f, abstractVar).kind in {tyTuple, tyOpenArray}):
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isNone:
result = userConvMatch(c, m, f, a, arg)
# check for a base type match, which supports openarray[T] without []
# constructor in a call:
if (result == nil) and (f.kind == tyOpenArray):
r = typeRel(m.bindings, base(f), a)
if r >= isGeneric:
inc(m.convMatches)
result = copyTree(arg)
if r == isGeneric: result.typ = getInstantiatedType(c, arg, m, base(f))
m.baseTypeMatch = true
else:
result = userConvMatch(c, m, base(f), a, arg)
proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
arg: PNode): PNode =
var
cmp, best: int
x, y, z: TCandidate
r: TTypeRelation
if (arg == nil) or (arg.kind != nkSymChoice):
result = ParamTypesMatchAux(c, m, f, a, arg)
else:
# CAUTION: The order depends on the used hashing scheme. Thus it is
# incorrect to simply use the first fitting match. However, to implement
# this correctly is inefficient. We have to copy `m` here to be able to
# roll back the side effects of the unification algorithm.
initCandidate(x, m.callee)
initCandidate(y, m.callee)
initCandidate(z, m.callee)
x.calleeSym = m.calleeSym
y.calleeSym = m.calleeSym
z.calleeSym = m.calleeSym
best = - 1
for i in countup(0, sonsLen(arg) - 1):
# iterators are not first class yet, so ignore them
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}:
copyCandidate(z, m)
r = typeRel(z.bindings, f, arg.sons[i].typ)
if r != isNone:
case x.state
of csEmpty, csNoMatch:
x = z
best = i
x.state = csMatch
of csMatch:
cmp = cmpCandidates(x, z)
if cmp < 0:
best = i
x = z
elif cmp == 0:
y = z # z is as good as x
else:
nil
if x.state == csEmpty:
result = nil
elif (y.state == csMatch) and (cmpCandidates(x, y) == 0):
if x.state != csMatch:
InternalError(arg.info, "x.state is not csMatch")
# ambiguous: more than one symbol fits
result = nil
else:
# only one valid interpretation found:
markUsed(arg, arg.sons[best].sym)
result = ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best])
proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
var m: TCandidate
initCandidate(m, f)
result = paramTypesMatch(c, m, f, a, arg)
proc setSon(father: PNode, at: int, son: PNode) =
if sonsLen(father) <= at: setlen(father.sons, at + 1)
father.sons[at] = son
proc matches(c: PContext, n: PNode, m: var TCandidate) =
var f = 1 # iterates over formal parameters
var a = 1 # iterates over the actual given arguments
m.state = csMatch # until proven otherwise
m.call = newNodeI(nkCall, n.info)
m.call.typ = base(m.callee) # may be nil
var formalLen = sonsLen(m.callee.n)
addSon(m.call, copyTree(n.sons[0]))
var marker: TIntSet
IntSetInit(marker)
var container: PNode = nil # constructed container
var formal: PSym = nil
while a < sonsLen(n):
if n.sons[a].kind == nkExprEqExpr:
# named param
# check if m.callee has such a param:
if n.sons[a].sons[0].kind != nkIdent:
liMessage(n.sons[a].info, errNamedParamHasToBeIdent)
m.state = csNoMatch
return
formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1)
if formal == nil:
# no error message!
m.state = csNoMatch
return
if IntSetContainsOrIncl(marker, formal.position):
# already in namedParams:
liMessage(n.sons[a].info, errCannotBindXTwice, formal.name.s)
m.state = csNoMatch
return
m.baseTypeMatch = false
var arg = ParamTypesMatch(c, m, formal.typ,
n.sons[a].typ, n.sons[a].sons[1])
if (arg == nil):
m.state = csNoMatch
return
if m.baseTypeMatch:
assert(container == nil)
container = newNodeI(nkBracket, n.sons[a].info)
addSon(container, arg)
setSon(m.call, formal.position + 1, container)
if f != formalLen - 1: container = nil
else:
setSon(m.call, formal.position + 1, arg)
else:
# unnamed param
if f >= formalLen:
# too many arguments?
if tfVarArgs in m.callee.flags:
# is ok... but don't increment any counters...
if skipTypes(n.sons[a].typ, abstractVar).kind == tyString:
addSon(m.call, implicitConv(nkHiddenStdConv, getSysType(tyCString),
copyTree(n.sons[a]), m, c))
else:
addSon(m.call, copyTree(n.sons[a]))
elif formal != nil:
m.baseTypeMatch = false
var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a])
if (arg != nil) and m.baseTypeMatch and (container != nil):
addSon(container, arg)
else:
m.state = csNoMatch
return
else:
m.state = csNoMatch
return
else:
if m.callee.n.sons[f].kind != nkSym:
InternalError(n.sons[a].info, "matches")
formal = m.callee.n.sons[f].sym
if IntSetContainsOrIncl(marker, formal.position):
# already in namedParams:
liMessage(n.sons[a].info, errCannotBindXTwice, formal.name.s)
m.state = csNoMatch
return
m.baseTypeMatch = false
var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a])
if (arg == nil):
m.state = csNoMatch
return
if m.baseTypeMatch:
assert(container == nil)
container = newNodeI(nkBracket, n.sons[a].info)
addSon(container, arg)
setSon(m.call, formal.position + 1,
implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
if f != formalLen - 1: container = nil
else:
setSon(m.call, formal.position + 1, arg)
inc(a)
inc(f)
f = 1
while f < sonsLen(m.callee.n):
formal = m.callee.n.sons[f].sym
if not IntSetContainsOrIncl(marker, formal.position):
if formal.ast == nil:
# no default value
m.state = csNoMatch
break
else:
# use default value:
setSon(m.call, formal.position + 1, copyTree(formal.ast))
inc(f)
proc sameMethodDispatcher(a, b: PSym): bool =
result = false
if (a.kind == skMethod) and (b.kind == skMethod):
var aa = lastSon(a.ast)
var bb = lastSon(b.ast)
if (aa.kind == nkSym) and (bb.kind == nkSym) and (aa.sym == bb.sym):
result = true
proc semDirectCall(c: PContext, n: PNode, filter: TSymKinds): PNode =
var
o: TOverloadIter
x, y, z: TCandidate
#liMessage(n.info, warnUser, renderTree(n));
var sym = initOverloadIter(o, c, n.sons[0])
result = nil
if sym == nil: return
initCandidate(x, sym.typ)
x.calleeSym = sym
initCandidate(y, sym.typ)
y.calleeSym = sym
while sym != nil:
if sym.kind in filter:
initCandidate(z, sym.typ)
z.calleeSym = sym
matches(c, n, z)
if z.state == csMatch:
case x.state
of csEmpty, csNoMatch:
x = z
of csMatch:
var cmp = cmpCandidates(x, z)
if cmp < 0:
x = z # z is better than x
elif cmp == 0:
y = z # z is as good as x
else:
nil
sym = nextOverloadIter(o, c, n.sons[0])
if x.state == csEmpty:
# no overloaded proc found
# do not generate an error yet; the semantic checking will check for
# an overloaded () operator
elif (y.state == csMatch) and (cmpCandidates(x, y) == 0) and
not sameMethodDispatcher(x.calleeSym, y.calleeSym):
if x.state != csMatch:
InternalError(n.info, "x.state is not csMatch") #writeMatches(x);
#writeMatches(y);
liMessage(n.Info, errGenerated, `%`(msgKindToString(errAmbiguousCallXYZ), [
getProcHeader(x.calleeSym), getProcHeader(y.calleeSym),
x.calleeSym.Name.s]))
else:
# only one valid interpretation found:
markUsed(n, x.calleeSym)
if x.calleeSym.ast == nil:
internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
if x.calleeSym.ast.sons[genericParamsPos] != nil:
# a generic proc!
x.calleeSym = generateInstance(c, x.calleeSym, x.bindings, n.info)
x.callee = x.calleeSym.typ
result = x.call
result.sons[0] = newSymNode(x.calleeSym)
result.typ = x.callee.sons[0]

177
rod/syntaxes.nim Executable file
View File

@@ -0,0 +1,177 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Implements the dispatcher for the different parsers.
import
strutils, llstream, ast, astalgo, idents, scanner, options, msgs, pnimsyn,
pbraces, ptmplsyn, filters, rnimsyn
type
TFilterKind* = enum
filtNone, filtTemplate, filtReplace, filtStrip
TParserKind* = enum
skinStandard, skinBraces, skinEndX
const
parserNames*: array[TParserKind, string] = ["standard", "braces", "endx"]
filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
"strip"]
type
TParsers*{.final.} = object
skin*: TParserKind
parser*: TParser
proc ParseFile*(filename: string): PNode{.procvar.}
proc openParsers*(p: var TParsers, filename: string, inputstream: PLLStream)
proc closeParsers*(p: var TParsers)
proc parseAll*(p: var TParsers): PNode
proc parseTopLevelStmt*(p: var TParsers): PNode
# implements an iterator. Returns the next top-level statement or nil if end
# of stream.
# implementation
proc ParseFile(filename: string): PNode =
var
p: TParsers
f: tfile
if not open(f, filename):
rawMessage(errCannotOpenFile, filename)
return
OpenParsers(p, filename, LLStreamOpen(f))
result = ParseAll(p)
CloseParsers(p)
proc parseAll(p: var TParsers): PNode =
case p.skin
of skinStandard:
result = pnimsyn.parseAll(p.parser)
of skinBraces:
result = pbraces.parseAll(p.parser)
of skinEndX:
InternalError("parser to implement") # skinEndX: result := pendx.parseAll(p.parser);
proc parseTopLevelStmt(p: var TParsers): PNode =
case p.skin
of skinStandard:
result = pnimsyn.parseTopLevelStmt(p.parser)
of skinBraces:
result = pbraces.parseTopLevelStmt(p.parser)
of skinEndX:
InternalError("parser to implement") #skinEndX: result := pendx.parseTopLevelStmt(p.parser);
proc UTF8_BOM(s: string): int =
if (s[0] == '\xEF') and (s[0 + 1] == '\xBB') and (s[0 + 2] == '\xBF'):
result = 3
else:
result = 0
proc containsShebang(s: string, i: int): bool =
var j: int
result = false
if (s[i] == '#') and (s[i + 1] == '!'):
j = i + 2
while s[j] in WhiteSpace: inc(j)
result = s[j] == '/'
proc parsePipe(filename: string, inputStream: PLLStream): PNode =
var
line: string
s: PLLStream
i: int
q: TParser
result = nil
s = LLStreamOpen(filename, fmRead)
if s != nil:
line = LLStreamReadLine(s)
i = UTF8_Bom(line) + 0
if containsShebang(line, i):
line = LLStreamReadLine(s)
i = 0
if (line[i] == '#') and (line[i + 1] == '!'):
inc(i, 2)
while line[i] in WhiteSpace: inc(i)
OpenParser(q, filename, LLStreamOpen(copy(line, i)))
result = pnimsyn.parseAll(q)
CloseParser(q)
LLStreamClose(s)
proc getFilter(ident: PIdent): TFilterKind =
for i in countup(low(TFilterKind), high(TFilterKind)):
if IdentEq(ident, filterNames[i]):
return i
result = filtNone
proc getParser(ident: PIdent): TParserKind =
for i in countup(low(TParserKind), high(TParserKind)):
if IdentEq(ident, parserNames[i]):
return i
rawMessage(errInvalidDirectiveX, ident.s)
proc getCallee(n: PNode): PIdent =
if (n.kind == nkCall) and (n.sons[0].kind == nkIdent):
result = n.sons[0].ident
elif n.kind == nkIdent:
result = n.ident
else:
rawMessage(errXNotAllowedHere, renderTree(n))
proc applyFilter(p: var TParsers, n: PNode, filename: string, stdin: PLLStream): PLLStream =
var
ident: PIdent
f: TFilterKind
ident = getCallee(n)
f = getFilter(ident)
case f
of filtNone:
p.skin = getParser(ident)
result = stdin
of filtTemplate:
result = filterTmpl(stdin, filename, n)
of filtStrip:
result = filterStrip(stdin, filename, n)
of filtReplace:
result = filterReplace(stdin, filename, n)
if f != filtNone:
if gVerbosity >= 2:
rawMessage(hintCodeBegin)
messageOut(result.s)
rawMessage(hintCodeEnd)
proc evalPipe(p: var TParsers, n: PNode, filename: string, start: PLLStream): PLLStream =
result = start
if n == nil: return
if (n.kind == nkInfix) and (n.sons[0].kind == nkIdent) and
IdentEq(n.sons[0].ident, "|"):
for i in countup(1, 2):
if n.sons[i].kind == nkInfix:
result = evalPipe(p, n.sons[i], filename, result)
else:
result = applyFilter(p, n.sons[i], filename, result)
elif n.kind == nkStmtList:
result = evalPipe(p, n.sons[0], filename, result)
else:
result = applyFilter(p, n, filename, result)
proc openParsers(p: var TParsers, filename: string, inputstream: PLLStream) =
var
pipe: PNode
s: PLLStream
p.skin = skinStandard
pipe = parsePipe(filename, inputStream)
if pipe != nil: s = evalPipe(p, pipe, filename, inputStream)
else: s = inputStream
case p.skin
of skinStandard, skinBraces, skinEndX: pnimsyn.openParser(p.parser, filename,
s)
proc closeParsers(p: var TParsers) =
pnimsyn.closeParser(p.parser)

33
rod/tigen.nim Executable file
View File

@@ -0,0 +1,33 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Type information generator. It transforms types into the AST of walker
# procs. This is used by the code generators.
import
ast, astalgo, strutils, nhashes, trees, treetab, platform, magicsys, options,
msgs, crc, idents, lists, types, rnimsyn
proc gcWalker*(t: PType): PNode
proc initWalker*(t: PType): PNode
proc asgnWalker*(t: PType): PNode
proc reprWalker*(t: PType): PNode
# implementation
proc gcWalker(t: PType): PNode =
nil
proc initWalker(t: PType): PNode =
nil
proc asgnWalker(t: PType): PNode =
nil
proc reprWalker(t: PType): PNode =
nil

683
rod/transf.nim Executable file
View File

@@ -0,0 +1,683 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements the transformator. It transforms the syntax tree
# to ease the work of the code generators. Does some transformations:
#
# * inlines iterators
# * inlines constants
# * performes contant folding
# * introduces nkHiddenDeref, nkHiddenSubConv, etc.
# * introduces method dispatchers
import
strutils, lists, options, ast, astalgo, trees, treetab, evals, msgs, os,
idents, rnimsyn, types, passes, semfold, magicsys, cgmeth
const
genPrefix* = ":tmp" # prefix for generated names
proc transfPass*(): TPass
# implementation
type
PTransCon = ref TTransCon
TTransCon{.final.} = object # part of TContext; stackable
mapping*: TIdNodeTable # mapping from symbols to nodes
owner*: PSym # current owner
forStmt*: PNode # current for stmt
next*: PTransCon # for stacking
TTransfContext = object of passes.TPassContext
module*: PSym
transCon*: PTransCon # top of a TransCon stack
PTransf = ref TTransfContext
proc newTransCon(): PTransCon =
new(result)
initIdNodeTable(result.mapping)
proc pushTransCon(c: PTransf, t: PTransCon) =
t.next = c.transCon
c.transCon = t
proc popTransCon(c: PTransf) =
if (c.transCon == nil): InternalError("popTransCon")
c.transCon = c.transCon.next
proc getCurrOwner(c: PTransf): PSym =
if c.transCon != nil: result = c.transCon.owner
else: result = c.module
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c))
result.info = info
result.typ = skipTypes(typ, {tyGenericInst})
incl(result.flags, sfFromGeneric)
proc transform(c: PTransf, n: PNode): PNode
#
#
#Transforming iterators into non-inlined versions is pretty hard, but
#unavoidable for not bloating the code too much. If we had direct access to
#the program counter, things'd be much easier.
#::
#
# iterator items(a: string): char =
# var i = 0
# while i < length(a):
# yield a[i]
# inc(i)
#
# for ch in items("hello world"): # `ch` is an iteration variable
# echo(ch)
#
#Should be transformed into::
#
# type
# TItemsClosure = record
# i: int
# state: int
# proc items(a: string, c: var TItemsClosure): char =
# case c.state
# of 0: goto L0 # very difficult without goto!
# of 1: goto L1 # can be implemented by GCC's computed gotos
#
# block L0:
# c.i = 0
# while c.i < length(a):
# c.state = 1
# return a[i]
# block L1: inc(c.i)
#
#More efficient, but not implementable::
#
# type
# TItemsClosure = record
# i: int
# pc: pointer
#
# proc items(a: string, c: var TItemsClosure): char =
# goto c.pc
# c.i = 0
# while c.i < length(a):
# c.pc = label1
# return a[i]
# label1: inc(c.i)
#
proc newAsgnStmt(c: PTransf, le, ri: PNode): PNode =
result = newNodeI(nkFastAsgn, ri.info)
addSon(result, le)
addSon(result, ri)
proc transformSym(c: PTransf, n: PNode): PNode =
var
tc: PTransCon
b: PNode
if (n.kind != nkSym): internalError(n.info, "transformSym")
tc = c.transCon
if sfBorrow in n.sym.flags:
# simply exchange the symbol:
b = n.sym.ast.sons[codePos]
if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
b = newSymNode(b.sym)
b.info = n.info
else:
b = n #writeln('transformSym', n.sym.id : 5);
while tc != nil:
result = IdNodeTableGet(tc.mapping, b.sym)
if result != nil:
return #write('not found in: ');
#writeIdNodeTable(tc.mapping);
tc = tc.next
result = b
case b.sym.kind
of skConst, skEnumField:
# BUGFIX: skEnumField was missing
if not (skipTypes(b.sym.typ, abstractInst).kind in ConstantDataTypes):
result = getConstExpr(c.module, b)
if result == nil: InternalError(b.info, "transformSym: const")
else:
nil
proc transformContinueAux(c: PTransf, n: PNode, labl: PSym, counter: var int) =
if n == nil: return
case n.kind
of nkEmpty..nkNilLit, nkForStmt, nkWhileStmt:
nil
of nkContinueStmt:
n.kind = nkBreakStmt
addSon(n, newSymNode(labl))
inc(counter)
else:
for i in countup(0, sonsLen(n) - 1):
transformContinueAux(c, n.sons[i], labl, counter)
proc transformContinue(c: PTransf, n: PNode): PNode =
# we transform the continue statement into a block statement
var
counter: int
x: PNode
labl: PSym
result = n
for i in countup(0, sonsLen(n) - 1): result.sons[i] = transform(c, n.sons[i])
counter = 0
labl = newSym(skLabel, nil, getCurrOwner(c))
labl.name = getIdent(genPrefix & $(labl.id))
labl.info = result.info
transformContinueAux(c, result, labl, counter)
if counter > 0:
x = newNodeI(nkBlockStmt, result.info)
addSon(x, newSymNode(labl))
addSon(x, result)
result = x
proc skipConv(n: PNode): PNode =
case n.kind
of nkObjUpConv, nkObjDownConv, nkPassAsOpenArray, nkChckRange, nkChckRangeF,
nkChckRange64:
result = n.sons[0]
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = n.sons[1]
else: result = n
proc newTupleAccess(tup: PNode, i: int): PNode =
var lit: PNode
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.sons[i])
addSon(result, copyTree(tup))
lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt))
lit.intVal = i
addSon(result, lit)
proc unpackTuple(c: PTransf, n, father: PNode) =
# XXX: BUG: what if `n` is an expression with side-effects?
for i in countup(0, sonsLen(n) - 1):
addSon(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, newTupleAccess(n, i))))
proc transformYield(c: PTransf, n: PNode): PNode =
var e: PNode
result = newNodeI(nkStmtList, n.info)
e = n.sons[0]
if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple:
e = skipConv(e)
if e.kind == nkPar:
for i in countup(0, sonsLen(e) - 1):
addSon(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, copyTree(e.sons[i]))))
else:
unpackTuple(c, e, result)
else:
e = transform(c, copyTree(e))
addSon(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], e))
addSon(result, transform(c, lastSon(c.transCon.forStmt)))
proc inlineIter(c: PTransf, n: PNode): PNode =
var
L: int
it: PNode
newVar: PSym
result = n
if n == nil: return
case n.kind
of nkEmpty..nkNilLit:
result = transform(c, copyTree(n))
of nkYieldStmt:
result = transformYield(c, n)
of nkVarSection:
result = copyTree(n)
for i in countup(0, sonsLen(result) - 1):
it = result.sons[i]
if it.kind == nkCommentStmt: continue
if it.kind == nkIdentDefs:
if (it.sons[0].kind != nkSym): InternalError(it.info, "inlineIter")
newVar = copySym(it.sons[0].sym)
incl(newVar.flags, sfFromGeneric) # fixes a strange bug for rodgen:
#include(it.sons[0].sym.flags, sfFromGeneric);
newVar.owner = getCurrOwner(c)
IdNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar))
it.sons[0] = newSymNode(newVar)
it.sons[2] = transform(c, it.sons[2])
else:
if it.kind != nkVarTuple:
InternalError(it.info, "inlineIter: not nkVarTuple")
L = sonsLen(it)
for j in countup(0, L - 3):
newVar = copySym(it.sons[j].sym)
incl(newVar.flags, sfFromGeneric)
newVar.owner = getCurrOwner(c)
IdNodeTablePut(c.transCon.mapping, it.sons[j].sym, newSymNode(newVar))
it.sons[j] = newSymNode(newVar)
assert(it.sons[L - 2] == nil)
it.sons[L - 1] = transform(c, it.sons[L - 1])
else:
result = copyNode(n)
for i in countup(0, sonsLen(n) - 1): addSon(result, inlineIter(c, n.sons[i]))
result = transform(c, result)
proc addVar(father, v: PNode) =
var vpart: PNode
vpart = newNodeI(nkIdentDefs, v.info)
addSon(vpart, v)
addSon(vpart, nil)
addSon(vpart, nil)
addSon(father, vpart)
proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
var m: PNode
case n.sons[0].kind
of nkObjUpConv, nkObjDownConv, nkPassAsOpenArray, nkChckRange, nkChckRangeF,
nkChckRange64:
m = n.sons[0].sons[0]
if (m.kind == a) or (m.kind == b):
# addr ( nkPassAsOpenArray ( deref ( x ) ) ) --> nkPassAsOpenArray(x)
n.sons[0].sons[0] = m.sons[0]
return transform(c, n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
m = n.sons[0].sons[1]
if (m.kind == a) or (m.kind == b):
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
n.sons[0].sons[1] = m.sons[0]
return transform(c, n.sons[0])
else:
if (n.sons[0].kind == a) or (n.sons[0].kind == b):
# addr ( deref ( x )) --> x
return transform(c, n.sons[0].sons[0])
n.sons[0] = transform(c, n.sons[0])
result = n
proc transformConv(c: PTransf, n: PNode): PNode =
var
source, dest: PType
diff: int
n.sons[1] = transform(c, n.sons[1])
result = n # numeric types need range checks:
dest = skipTypes(n.typ, abstractVarRange)
source = skipTypes(n.sons[1].typ, abstractVarRange)
case dest.kind
of tyInt..tyInt64, tyEnum, tyChar, tyBool:
if (firstOrd(dest) <= firstOrd(source)) and
(lastOrd(source) <= lastOrd(dest)):
# BUGFIX: simply leave n as it is; we need a nkConv node,
# but no range check:
result = n
else:
# generate a range check:
if (dest.kind == tyInt64) or (source.kind == tyInt64):
result = newNodeIT(nkChckRange64, n.info, n.typ)
else:
result = newNodeIT(nkChckRange, n.info, n.typ)
dest = skipTypes(n.typ, abstractVar)
addSon(result, n.sons[1])
addSon(result, newIntTypeNode(nkIntLit, firstOrd(dest), source))
addSon(result, newIntTypeNode(nkIntLit, lastOrd(dest), source))
of tyFloat..tyFloat128:
if skipTypes(n.typ, abstractVar).kind == tyRange:
result = newNodeIT(nkChckRangeF, n.info, n.typ)
dest = skipTypes(n.typ, abstractVar)
addSon(result, n.sons[1])
addSon(result, copyTree(dest.n.sons[0]))
addSon(result, copyTree(dest.n.sons[1]))
of tyOpenArray:
result = newNodeIT(nkPassAsOpenArray, n.info, n.typ)
addSon(result, n.sons[1])
of tyCString:
if source.kind == tyString:
result = newNodeIT(nkStringToCString, n.info, n.typ)
addSon(result, n.sons[1])
of tyString:
if source.kind == tyCString:
result = newNodeIT(nkCStringToString, n.info, n.typ)
addSon(result, n.sons[1])
of tyRef, tyPtr:
dest = skipTypes(dest, abstractPtrs)
source = skipTypes(source, abstractPtrs)
if source.kind == tyObject:
diff = inheritanceDiff(dest, source)
if diff < 0:
result = newNodeIT(nkObjUpConv, n.info, n.typ)
addSon(result, n.sons[1])
elif diff > 0:
result = newNodeIT(nkObjDownConv, n.info, n.typ)
addSon(result, n.sons[1])
else:
result = n.sons[1]
of tyObject:
diff = inheritanceDiff(dest, source)
if diff < 0:
result = newNodeIT(nkObjUpConv, n.info, n.typ)
addSon(result, n.sons[1])
elif diff > 0:
result = newNodeIT(nkObjDownConv, n.info, n.typ)
addSon(result, n.sons[1])
else:
result = n.sons[1]
of tyGenericParam, tyOrdinal:
result = n.sons[1] # happens sometimes for generated assignments, etc.
else:
nil
proc skipPassAsOpenArray(n: PNode): PNode =
result = n
while result.kind == nkPassAsOpenArray: result = result.sons[0]
type
TPutArgInto = enum
paDirectMapping, paFastAsgn, paVarAsgn
proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
# This analyses how to treat the mapping "formal <-> arg" in an
# inline context.
if skipTypes(formal, abstractInst).kind == tyOpenArray:
return paDirectMapping # XXX really correct?
# what if ``arg`` has side-effects?
case arg.kind
of nkEmpty..nkNilLit:
result = paDirectMapping
of nkPar, nkCurly, nkBracket:
result = paFastAsgn
for i in countup(0, sonsLen(arg) - 1):
if putArgInto(arg.sons[i], formal) != paDirectMapping: return
result = paDirectMapping
else:
if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn
else: result = paFastAsgn
proc transformFor(c: PTransf, n: PNode): PNode =
# generate access statements for the parameters (unless they are constant)
# put mapping from formal parameters to actual parameters
var
length: int
call, v, body, arg: PNode
newC: PTransCon
temp, formal: PSym
if (n.kind != nkForStmt): InternalError(n.info, "transformFor")
result = newNodeI(nkStmtList, n.info)
length = sonsLen(n)
n.sons[length - 1] = transformContinue(c, n.sons[length - 1])
v = newNodeI(nkVarSection, n.info)
for i in countup(0, length - 3):
addVar(v, copyTree(n.sons[i])) # declare new vars
addSon(result, v)
newC = newTransCon()
call = n.sons[length - 2]
if (call.kind != nkCall) or (call.sons[0].kind != nkSym):
InternalError(call.info, "transformFor")
newC.owner = call.sons[0].sym
newC.forStmt = n
if (newC.owner.kind != skIterator):
InternalError(call.info, "transformFor") # generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC)
for i in countup(1, sonsLen(call) - 1):
arg = skipPassAsOpenArray(transform(c, call.sons[i]))
formal = skipTypes(newC.owner.typ, abstractInst).n.sons[i].sym #if IdentEq(newc.Owner.name, 'items') then
# liMessage(arg.info, warnUser, 'items: ' + nodeKindToStr[arg.kind]);
case putArgInto(arg, formal.typ)
of paDirectMapping:
IdNodeTablePut(newC.mapping, formal, arg)
of paFastAsgn:
# generate a temporary and produce an assignment statement:
temp = newTemp(c, formal.typ, formal.info)
addVar(v, newSymNode(temp))
addSon(result, newAsgnStmt(c, newSymNode(temp), arg))
IdNodeTablePut(newC.mapping, formal, newSymNode(temp))
of paVarAsgn:
assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
InternalError(arg.info, "not implemented: pass to var parameter")
body = newC.owner.ast.sons[codePos]
pushInfoContext(n.info)
addSon(result, inlineIter(c, body))
popInfoContext()
popTransCon(c)
proc getMagicOp(call: PNode): TMagic =
if (call.sons[0].kind == nkSym) and
(call.sons[0].sym.kind in {skProc, skMethod, skConverter}):
result = call.sons[0].sym.magic
else:
result = mNone
proc gatherVars(c: PTransf, n: PNode, marked: var TIntSet, owner: PSym,
container: PNode) =
# gather used vars for closure generation
var
s: PSym
found: bool
if n == nil: return
case n.kind
of nkSym:
s = n.sym
found = false
case s.kind
of skVar: found = not (sfGlobal in s.flags)
of skTemp, skForVar, skParam: found = true
else:
nil
if found and (owner.id != s.owner.id) and
not IntSetContainsOrIncl(marked, s.id):
incl(s.flags, sfInClosure)
addSon(container, copyNode(n)) # DON'T make a copy of the symbol!
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
nil
else:
for i in countup(0, sonsLen(n) - 1):
gatherVars(c, n.sons[i], marked, owner, container)
proc addFormalParam(routine: PSym, param: PSym) =
addSon(routine.typ, param.typ)
addSon(routine.ast.sons[paramsPos], newSymNode(param))
proc indirectAccess(a, b: PSym): PNode =
# returns a^ .b as a node
var x, y, deref: PNode
x = newSymNode(a)
y = newSymNode(b)
deref = newNodeI(nkDerefExpr, x.info)
deref.typ = x.typ.sons[0]
addSon(deref, x)
result = newNodeI(nkDotExpr, x.info)
addSon(result, deref)
addSon(result, y)
result.typ = y.typ
proc transformLambda(c: PTransf, n: PNode): PNode =
var
marked: TIntSet
closure: PNode
s, param: PSym
cl, p: PType
newC: PTransCon
result = n
IntSetInit(marked)
if (n.sons[namePos].kind != nkSym): InternalError(n.info, "transformLambda")
s = n.sons[namePos].sym
closure = newNodeI(nkRecList, n.sons[codePos].info)
gatherVars(c, n.sons[codePos], marked, s, closure) # add closure type to the param list (even if closure is empty!):
cl = newType(tyObject, s)
cl.n = closure
addSon(cl, nil) # no super class
p = newType(tyRef, s)
addSon(p, cl)
param = newSym(skParam, getIdent(genPrefix & "Cl"), s)
param.typ = p
addFormalParam(s, param) # all variables that are accessed should be accessed by the new closure
# parameter:
if sonsLen(closure) > 0:
newC = newTransCon()
for i in countup(0, sonsLen(closure) - 1):
IdNodeTablePut(newC.mapping, closure.sons[i].sym,
indirectAccess(param, closure.sons[i].sym))
pushTransCon(c, newC)
n.sons[codePos] = transform(c, n.sons[codePos])
popTransCon(c)
proc transformCase(c: PTransf, n: PNode): PNode =
# removes `elif` branches of a case stmt
# adds ``else: nil`` if needed for the code generator
var
length, i: int
ifs, elsen: PNode
length = sonsLen(n)
i = length - 1
if n.sons[i].kind == nkElse: dec(i)
if n.sons[i].kind == nkElifBranch:
while n.sons[i].kind == nkElifBranch: dec(i)
if (n.sons[i].kind != nkOfBranch):
InternalError(n.sons[i].info, "transformCase")
ifs = newNodeI(nkIfStmt, n.sons[i + 1].info)
elsen = newNodeI(nkElse, ifs.info)
for j in countup(i + 1, length - 1): addSon(ifs, n.sons[j])
setlen(n.sons, i + 2)
addSon(elsen, ifs)
n.sons[i + 1] = elsen
elif (n.sons[length - 1].kind != nkElse) and
not (skipTypes(n.sons[0].Typ, abstractVarRange).Kind in
{tyInt..tyInt64, tyChar, tyEnum}):
#MessageOut(renderTree(n));
elsen = newNodeI(nkElse, n.info)
addSon(elsen, newNodeI(nkNilLit, n.info))
addSon(n, elsen)
result = n
for j in countup(0, sonsLen(n) - 1): result.sons[j] = transform(c, n.sons[j])
proc transformArrayAccess(c: PTransf, n: PNode): PNode =
result = copyTree(n)
result.sons[0] = skipConv(result.sons[0])
result.sons[1] = skipConv(result.sons[1])
for i in countup(0, sonsLen(result) - 1):
result.sons[i] = transform(c, result.sons[i])
proc getMergeOp(n: PNode): PSym =
result = nil
case n.kind
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
if (n.sons[0].Kind == nkSym) and (n.sons[0].sym.kind == skProc) and
(sfMerge in n.sons[0].sym.flags):
result = n.sons[0].sym
else:
nil
proc flattenTreeAux(d, a: PNode, op: PSym) =
var op2: PSym
op2 = getMergeOp(a)
if (op2 != nil) and
((op2.id == op.id) or (op.magic != mNone) and (op2.magic == op.magic)): # a is a
# "leaf", so
# add
# it:
for i in countup(1, sonsLen(a) - 1): flattenTreeAux(d, a.sons[i], op)
else:
addSon(d, copyTree(a))
proc flattenTree(root: PNode): PNode =
var op: PSym
op = getMergeOp(root)
if op != nil:
result = copyNode(root)
addSon(result, copyTree(root.sons[0]))
flattenTreeAux(result, root, op)
else:
result = root
proc transformCall(c: PTransf, n: PNode): PNode =
var
j: int
m, a: PNode
op: PSym
result = flattenTree(n)
for i in countup(0, sonsLen(result) - 1):
result.sons[i] = transform(c, result.sons[i])
op = getMergeOp(result)
if (op != nil) and (op.magic != mNone) and (sonsLen(result) >= 3):
m = result
result = newNodeIT(nkCall, m.info, m.typ)
addSon(result, copyTree(m.sons[0]))
j = 1
while j < sonsLen(m):
a = m.sons[j]
inc(j)
if isConstExpr(a):
while (j < sonsLen(m)) and isConstExpr(m.sons[j]):
a = evalOp(op.magic, m, a, m.sons[j], nil)
inc(j)
addSon(result, a)
if sonsLen(result) == 2: result = result.sons[1]
elif (result.sons[0].kind == nkSym) and
(result.sons[0].sym.kind == skMethod):
# use the dispatcher for the call:
result = methodCall(result)
proc transform(c: PTransf, n: PNode): PNode =
var cnst: PNode
result = n
if n == nil:
return #if ToLinenumber(n.info) = 32 then
# MessageOut(RenderTree(n));
case n.kind
of nkSym:
return transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves
of nkBracketExpr:
result = transformArrayAccess(c, n)
of nkLambda:
result = transformLambda(c, n)
of nkForStmt:
result = transformFor(c, n)
of nkCaseStmt:
result = transformCase(c, n)
of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef:
if n.sons[genericParamsPos] == nil:
n.sons[codePos] = transform(c, n.sons[codePos])
if n.kind == nkMethodDef: methodDef(n.sons[namePos].sym)
of nkWhileStmt:
if (sonsLen(n) != 2): InternalError(n.info, "transform")
n.sons[0] = transform(c, n.sons[0])
n.sons[1] = transformContinue(c, n.sons[1])
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
result = transformCall(c, result)
of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
of nkDerefExpr, nkHiddenDeref:
result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = transformConv(c, n)
of nkDiscardStmt:
for i in countup(0, sonsLen(n) - 1): result.sons[i] = transform(c, n.sons[i])
if isConstExpr(result.sons[0]): result = newNode(nkCommentStmt)
of nkCommentStmt, nkTemplateDef:
return
of nkConstSection:
return # do not replace ``const c = 3`` with ``const 3 = 3``
else:
for i in countup(0, sonsLen(n) - 1): result.sons[i] = transform(c, n.sons[i])
cnst = getConstExpr(c.module, result)
if cnst != nil:
result = cnst # do not miss an optimization
proc processTransf(context: PPassContext, n: PNode): PNode =
var c: PTransf
c = PTransf(context)
result = transform(c, n)
proc openTransf(module: PSym, filename: string): PPassContext =
var n: PTransf
new(n)
n.module = module
result = n
proc transfPass(): TPass =
initPass(result)
result.open = openTransf
result.process = processTransf
result.close = processTransf # we need to process generics too!

111
rod/transtmp.nim Executable file
View File

@@ -0,0 +1,111 @@
#
#
# 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 transformator. It transforms the syntax tree
# to ease the work of the code generators. Does the transformation to
# introduce temporaries to split up complex expressions.
# THIS MODULE IS NOT USED!
proc transInto(c: PContext, dest: var PNode, father, src: PNode)
# transforms the expression `src` into the destination `dest`. Uses `father`
# for temorary statements. If dest = nil, the expression is put into a
# temporary.
proc transTmp(c: PContext, father, src: PNode): PNode =
# convienence proc
result = nil
transInto(c, result, father, src)
proc newLabel(c: PContext): PSym =
inc(gTmpId)
result = newSym(skLabel, getIdent(genPrefix & $(gTmpId), c.transCon.owner))
proc fewCmps(s: PNode): bool =
# this function estimates whether it is better to emit code
# for constructing the set or generating a bunch of comparisons directly
assert(s.kind in {nkSetConstr, nkConstSetConstr})
if (s.typ.size <= platform.intSize) and (s.kind == nkConstSetConstr):
result = false # it is better to emit the set generation code
elif skipRange(s.typ.sons[0]).Kind in {tyInt..tyInt64}:
result = true # better not emit the set if int is basetype!
else:
result = sonsLen(s) <=
8 # 8 seems to be a good value
proc transformIn(c: PContext, father, n: PNode): PNode =
var
a, b, e, setc: PNode
destLabel, label2: PSym
if (n.sons[1].kind == nkSetConstr) and fewCmps(n.sons[1]):
# a set constructor but not a constant set:
# do not emit the set, but generate a bunch of comparisons
result = newSymNode(newTemp(c, n.typ, n.info))
e = transTmp(c, father, n.sons[2])
setc = n.sons[1]
destLabel = newLabel(c)
for i in countup(0, sonsLen(setc) - 1):
if setc.sons[i].kind == nkRange:
a = transTmp(c, father, setc.sons[i].sons[0])
b = transTmp(c, father, setc.sons[i].sons[1])
label2 = newLabel(c)
addSon(father, newLt(result, e, a)) # e < a? --> goto end
addSon(father, newCondJmp(result, label2))
addSon(father, newLe(result, e, b)) # e <= b? --> goto set end
addSon(father, newCondJmp(result, destLabel))
addSon(father, newLabelNode(label2))
else:
a = transTmp(c, father, setc.sons[i])
addSon(father, newEq(result, e, a))
addSon(father, newCondJmp(result, destLabel))
addSon(father, newLabelNode(destLabel))
else:
result = n
proc transformOp2(c: PContext, dest: var PNode, father, n: PNode) =
var a, b: PNode
if dest == nil: dest = newSymNode(newTemp(c, n.typ, n.info))
a = transTmp(c, father, n.sons[1])
b = transTmp(c, father, n.sons[2])
addSon(father, newAsgnStmt(dest, newOp2(n, a, b)))
proc transformOp1(c: PContext, dest: var PNode, father, n: PNode) =
var a: PNode
if dest == nil: dest = newSymNode(newTemp(c, n.typ, n.info))
a = transTmp(c, father, n.sons[1])
addSon(father, newAsgnStmt(dest, newOp1(n, a)))
proc genTypeInfo(c: PContext, initSection: PNode) =
nil
proc genNew(c: PContext, father, n: PNode) =
# how do we handle compilerprocs?
proc transformCase(c: PContext, father, n: PNode): PNode =
var
ty: PType
e: PNode
ty = skipGeneric(n.sons[0].typ)
if ty.kind == tyString:
# transform a string case to a bunch of comparisons:
result = newNodeI(nkIfStmt, n)
e = transTmp(c, father, n.sons[0])
else:
result = n
proc transInto(c: PContext, dest: var PNode, father, src: PNode) =
if src == nil: return
if (src.typ != nil) and (src.typ.kind == tyGenericInst):
src.typ = skipGeneric(src.typ)
case src.kind
of nkIdent..nkNilLit:
if dest == nil:
dest = copyTree(src)
else:
# generate assignment:
addSon(father, newAsgnStmt(dest, src))
of nkCall, nkCommand, nkCallStrLit:
nil

152
rod/trees.nim Executable file
View File

@@ -0,0 +1,152 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# tree helper routines
import
ast, astalgo, scanner, msgs, strutils
proc getMagic*(op: PNode): TMagic
# function getConstExpr(const t: TNode; out res: TNode): Boolean;
proc isConstExpr*(n: PNode): bool
proc flattenTree*(root: PNode, op: TMagic): PNode
proc TreeToSym*(t: PNode): PSym
proc SwapOperands*(op: PNode)
proc getOpSym*(op: PNode): PSym
proc getProcSym*(call: PNode): PSym
proc ExprStructuralEquivalent*(a, b: PNode): bool
proc sameTree*(a, b: PNode): bool
proc cyclicTree*(n: PNode): bool
# implementation
proc hasSon(father, son: PNode): bool =
for i in countup(0, sonsLen(father) - 1):
if father.sons[i] == son:
return true
result = false
proc cyclicTreeAux(n, s: PNode): bool =
var m: int
if n == nil:
return false
if hasSon(s, n):
return true
m = sonsLen(s)
addSon(s, n)
if not (n.kind in {nkEmpty..nkNilLit}):
for i in countup(0, sonsLen(n) - 1):
if cyclicTreeAux(n.sons[i], s):
return true
result = false
delSon(s, m)
proc cyclicTree(n: PNode): bool =
var s: PNode
s = newNodeI(nkEmpty, n.info)
result = cyclicTreeAux(n, s)
proc ExprStructuralEquivalent(a, b: PNode): bool =
result = false
if a == b:
result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind):
case a.kind
of nkSym: # don't go nuts here: same symbol as string is enough:
result = a.sym.name.id == b.sym.name.id
of nkIdent:
result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit:
result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit:
result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit:
result = a.strVal == b.strVal
of nkEmpty, nkNilLit, nkType:
result = true
else:
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not ExprStructuralEquivalent(a.sons[i], b.sons[i]): return
result = true
proc sameTree(a, b: PNode): bool =
result = false
if a == b:
result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind):
if a.flags != b.flags: return
if a.info.line != b.info.line: return
if a.info.col != b.info.col:
return #if a.info.fileIndex <> b.info.fileIndex then exit;
case a.kind
of nkSym: # don't go nuts here: same symbol as string is enough:
result = a.sym.name.id == b.sym.name.id
of nkIdent:
result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit:
result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit:
result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit:
result = a.strVal == b.strVal
of nkEmpty, nkNilLit, nkType:
result = true
else:
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not sameTree(a.sons[i], b.sons[i]): return
result = true
proc getProcSym(call: PNode): PSym =
result = call.sons[0].sym
proc getOpSym(op: PNode): PSym =
if not (op.kind in {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}):
result = nil
else:
if (sonsLen(op) <= 0): InternalError(op.info, "getOpSym")
if op.sons[0].Kind == nkSym: result = op.sons[0].sym
else: result = nil
proc getMagic(op: PNode): TMagic =
case op.kind
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit:
case op.sons[0].Kind
of nkSym:
result = op.sons[0].sym.magic
else: result = mNone
else: result = mNone
proc TreeToSym(t: PNode): PSym =
result = t.sym
proc isConstExpr(n: PNode): bool =
result = (n.kind in
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)
proc flattenTreeAux(d, a: PNode, op: TMagic) =
if (getMagic(a) == op): # a is a "leaf", so add it:
for i in countup(1, sonsLen(a) - 1): # BUGFIX
flattenTreeAux(d, a.sons[i], op)
else:
addSon(d, copyTree(a))
proc flattenTree(root: PNode, op: TMagic): PNode =
result = copyNode(root)
if (getMagic(root) == op):
# BUGFIX: forget to copy prc
addSon(result, copyNode(root.sons[0]))
flattenTreeAux(result, root, op)
proc SwapOperands(op: PNode) =
var tmp: PNode
tmp = op.sons[1]
op.sons[1] = op.sons[2]
op.sons[2] = tmp

125
rod/treetab.nim Executable file
View File

@@ -0,0 +1,125 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Implements a table from trees to trees. Does structural equavilent checking.
import
nhashes, ast, astalgo, types
proc NodeTableGet*(t: TNodeTable, key: PNode): int
proc NodeTablePut*(t: var TNodeTable, key: PNode, val: int)
proc NodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int
# implementation
proc hashTree(n: PNode): THash =
result = 0
if n == nil: return
result = ord(n.kind)
case n.kind
of nkEmpty, nkNilLit, nkType:
nil
of nkIdent:
result = concHash(result, n.ident.h)
of nkSym:
result = concHash(result, n.sym.name.h)
of nkCharLit..nkInt64Lit:
if (n.intVal >= low(int)) and (n.intVal <= high(int)):
result = concHash(result, int(n.intVal))
of nkFloatLit..nkFloat64Lit:
if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
result = concHash(result, toInt(n.floatVal))
of nkStrLit..nkTripleStrLit:
result = concHash(result, GetHashStr(n.strVal))
else:
for i in countup(0, sonsLen(n) - 1):
result = concHash(result, hashTree(n.sons[i]))
proc TreesEquivalent(a, b: PNode): bool =
result = false
if a == b:
result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind):
case a.kind
of nkEmpty, nkNilLit, nkType: result = true
of nkSym: result = a.sym.id == b.sym.id
of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
else:
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not TreesEquivalent(a.sons[i], b.sons[i]): return
result = true
if result: result = sameTypeOrNil(a.typ, b.typ)
proc NodeTableRawGet(t: TNodeTable, k: THash, key: PNode): int =
var h: THash
h = k and high(t.data)
while t.data[h].key != nil:
if (t.data[h].h == k) and TreesEquivalent(t.data[h].key, key):
return h
h = nextTry(h, high(t.data))
result = - 1
proc NodeTableGet(t: TNodeTable, key: PNode): int =
var index: int
index = NodeTableRawGet(t, hashTree(key), key)
if index >= 0: result = t.data[index].val
else: result = low(int)
proc NodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode, val: int) =
var h: THash
h = k and high(data)
while data[h].key != nil: h = nextTry(h, high(data))
assert(data[h].key == nil)
data[h].h = k
data[h].key = key
data[h].val = val
proc NodeTablePut(t: var TNodeTable, key: PNode, val: int) =
var
index: int
n: TNodePairSeq
k: THash
k = hashTree(key)
index = NodeTableRawGet(t, k, key)
if index >= 0:
assert(t.data[index].key != nil)
t.data[index].val = val
else:
if mustRehash(len(t.data), t.counter):
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)):
if t.data[i].key != nil:
NodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
swap(t.data, n)
NodeTableRawInsert(t.data, k, key, val)
inc(t.counter)
proc NodeTableTestOrSet(t: var TNodeTable, key: PNode, val: int): int =
var
index: int
n: TNodePairSeq
k: THash
k = hashTree(key)
index = NodeTableRawGet(t, k, key)
if index >= 0:
assert(t.data[index].key != nil)
result = t.data[index].val
else:
if mustRehash(len(t.data), t.counter):
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)):
if t.data[i].key != nil:
NodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
swap(t.data, n)
NodeTableRawInsert(t.data, k, key, val)
result = val
inc(t.counter)

978
rod/types.nim Executable file
View File

@@ -0,0 +1,978 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this module contains routines for accessing and iterating over types
import
ast, astalgo, trees, msgs, strutils, platform
proc firstOrd*(t: PType): biggestInt
proc lastOrd*(t: PType): biggestInt
proc lengthOrd*(t: PType): biggestInt
type
TPreferedDesc* = enum
preferName, preferDesc
proc TypeToString*(typ: PType, prefer: TPreferedDesc = preferName): string
proc getProcHeader*(sym: PSym): string
proc base*(t: PType): PType
# ------------------- type iterator: ----------------------------------------
type
TTypeIter* = proc (t: PType, closure: PObject): bool # should return true if the iteration should stop
TTypeMutator* = proc (t: PType, closure: PObject): PType # copy t and mutate it
TTypePredicate* = proc (t: PType): bool
proc IterOverType*(t: PType, iter: TTypeIter, closure: PObject): bool
# Returns result of `iter`.
proc mutateType*(t: PType, iter: TTypeMutator, closure: PObject): PType
# Returns result of `iter`.
proc SameType*(x, y: PType): bool
proc SameTypeOrNil*(a, b: PType): bool
proc equalOrDistinctOf*(x, y: PType): bool
type
TParamsEquality* = enum # they are equal, but their
# identifiers or their return
# type differ (i.e. they cannot be
# overloaded)
# this used to provide better error messages
paramsNotEqual, # parameters are not equal
paramsEqual, # parameters are equal
paramsIncompatible
proc equalParams*(a, b: PNode): TParamsEquality
# returns whether the parameter lists of the procs a, b are exactly the same
proc isOrdinalType*(t: PType): bool
proc enumHasWholes*(t: PType): bool
const
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal}
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal}
abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal}
abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal}
abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal}
proc skipTypes*(t: PType, kinds: TTypeKinds): PType
proc elemType*(t: PType): PType
proc containsObject*(t: PType): bool
proc containsGarbageCollectedRef*(typ: PType): bool
proc containsHiddenPointer*(typ: PType): bool
proc canFormAcycle*(typ: PType): bool
proc isCompatibleToCString*(a: PType): bool
proc getOrdValue*(n: PNode): biggestInt
proc computeSize*(typ: PType): biggestInt
proc getSize*(typ: PType): biggestInt
proc isPureObject*(typ: PType): bool
proc inheritanceDiff*(a, b: PType): int
# | returns: 0 iff `a` == `b`
# | returns: -x iff `a` is the x'th direct superclass of `b`
# | returns: +x iff `a` is the x'th direct subclass of `b`
# | returns: `maxint` iff `a` and `b` are not compatible at all
proc InvalidGenericInst*(f: PType): bool
# for debugging
type
TTypeFieldResult* = enum
frNone, # type has no object type field
frHeader, # type has an object type field only in the header
frEmbedded # type has an object type field somewhere embedded
proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult
# this does a complex analysis whether a call to ``objectInit`` needs to be
# made or intializing of the type field suffices or if there is no type field
# at all in this type.
proc typeAllowed*(t: PType, kind: TSymKind): bool
# implementation
proc InvalidGenericInst(f: PType): bool =
result = (f.kind == tyGenericInst) and (lastSon(f) == nil)
proc inheritanceDiff(a, b: PType): int =
var x, y: PType
# conversion to superclass?
x = a
result = 0
while (x != nil):
if x.id == b.id: return
x = x.sons[0]
dec(result)
y = b
result = 0
while (y != nil):
if y.id == a.id: return
y = y.sons[0]
inc(result)
result = high(int)
proc isPureObject(typ: PType): bool =
var t: PType
t = typ
while t.sons[0] != nil: t = t.sons[0]
result = (t.sym != nil) and (sfPure in t.sym.flags)
proc getOrdValue(n: PNode): biggestInt =
case n.kind
of nkCharLit..nkInt64Lit: result = n.intVal
of nkNilLit: result = 0
else:
liMessage(n.info, errOrdinalTypeExpected)
result = 0
proc isCompatibleToCString(a: PType): bool =
result = false
if a.kind == tyArray:
if (firstOrd(a.sons[0]) == 0) and
(skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and
(a.sons[1].kind == tyChar):
result = true
proc getProcHeader(sym: PSym): string =
var n, p: PNode
result = sym.name.s & '('
n = sym.typ.n
for i in countup(1, sonsLen(n) - 1):
p = n.sons[i]
if (p.kind != nkSym): InternalError("getProcHeader")
add(result, p.sym.name.s)
add(result, ": ")
add(result, typeToString(p.sym.typ))
if i != sonsLen(n) - 1: add(result, ", ")
add(result, ')')
if n.sons[0].typ != nil: result = result & ": " & typeToString(n.sons[0].typ)
proc elemType(t: PType): PType =
assert(t != nil)
case t.kind
of tyGenericInst, tyDistinct: result = elemType(lastSon(t))
of tyArray, tyArrayConstr: result = t.sons[1]
else: result = t.sons[0]
assert(result != nil)
proc skipGeneric(t: PType): PType =
result = t
while result.kind == tyGenericInst: result = lastSon(result)
proc skipRange(t: PType): PType =
result = t
while result.kind == tyRange: result = base(result)
proc skipAbstract(t: PType): PType =
result = t
while result.kind in {tyRange, tyGenericInst}: result = lastSon(result)
proc skipVar(t: PType): PType =
result = t
while result.kind == tyVar: result = result.sons[0]
proc skipVarGeneric(t: PType): PType =
result = t
while result.kind in {tyGenericInst, tyVar}: result = lastSon(result)
proc skipPtrsGeneric(t: PType): PType =
result = t
while result.kind in {tyGenericInst, tyVar, tyPtr, tyRef}:
result = lastSon(result)
proc skipVarGenericRange(t: PType): PType =
result = t
while result.kind in {tyGenericInst, tyVar, tyRange}: result = lastSon(result)
proc skipGenericRange(t: PType): PType =
result = t
while result.kind in {tyGenericInst, tyVar, tyRange}: result = lastSon(result)
proc skipTypes(t: PType, kinds: TTypeKinds): PType =
result = t
while result.kind in kinds: result = lastSon(result)
proc isOrdinalType(t: PType): bool =
assert(t != nil)
result = (t.Kind in {tyChar, tyInt..tyInt64, tyBool, tyEnum}) or
(t.Kind in {tyRange, tyOrdinal}) and isOrdinalType(t.sons[0])
proc enumHasWholes(t: PType): bool =
var b: PType
b = t
while b.kind == tyRange: b = b.sons[0]
result = (b.Kind == tyEnum) and (tfEnumHasWholes in b.flags)
proc iterOverTypeAux(marker: var TIntSet, t: PType, iter: TTypeIter,
closure: PObject): bool
proc iterOverNode(marker: var TIntSet, n: PNode, iter: TTypeIter,
closure: PObject): bool =
result = false
if n != nil:
case n.kind
of nkNone..nkNilLit:
# a leaf
result = iterOverTypeAux(marker, n.typ, iter, closure)
else:
for i in countup(0, sonsLen(n) - 1):
result = iterOverNode(marker, n.sons[i], iter, closure)
if result: return
proc iterOverTypeAux(marker: var TIntSet, t: PType, iter: TTypeIter,
closure: PObject): bool =
result = false
if t == nil: return
result = iter(t, closure)
if result: return
if not IntSetContainsOrIncl(marker, t.id):
case t.kind
of tyGenericInst, tyGenericBody:
result = iterOverTypeAux(marker, lastSon(t), iter, closure)
else:
for i in countup(0, sonsLen(t) - 1):
result = iterOverTypeAux(marker, t.sons[i], iter, closure)
if result: return
if t.n != nil: result = iterOverNode(marker, t.n, iter, closure)
proc IterOverType(t: PType, iter: TTypeIter, closure: PObject): bool =
var marker: TIntSet
IntSetInit(marker)
result = iterOverTypeAux(marker, t, iter, closure)
proc searchTypeForAux(t: PType, predicate: TTypePredicate, marker: var TIntSet): bool
proc searchTypeNodeForAux(n: PNode, p: TTypePredicate, marker: var TIntSet): bool =
result = false
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
result = searchTypeNodeForAux(n.sons[i], p, marker)
if result: return
of nkRecCase:
assert(n.sons[0].kind == nkSym)
result = searchTypeNodeForAux(n.sons[0], p, marker)
if result: return
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
result = searchTypeNodeForAux(lastSon(n.sons[i]), p, marker)
if result: return
else: internalError("searchTypeNodeForAux(record case branch)")
of nkSym:
result = searchTypeForAux(n.sym.typ, p, marker)
else: internalError(n.info, "searchTypeNodeForAux()")
proc searchTypeForAux(t: PType, predicate: TTypePredicate, marker: var TIntSet): bool =
# iterates over VALUE types!
result = false
if t == nil: return
if IntSetContainsOrIncl(marker, t.id): return
result = Predicate(t)
if result: return
case t.kind
of tyObject:
result = searchTypeForAux(t.sons[0], predicate, marker)
if not result: result = searchTypeNodeForAux(t.n, predicate, marker)
of tyGenericInst, tyDistinct:
result = searchTypeForAux(lastSon(t), predicate, marker)
of tyArray, tyArrayConstr, tySet, tyTuple:
for i in countup(0, sonsLen(t) - 1):
result = searchTypeForAux(t.sons[i], predicate, marker)
if result: return
else:
nil
proc searchTypeFor(t: PType, predicate: TTypePredicate): bool =
var marker: TIntSet
IntSetInit(marker)
result = searchTypeForAux(t, predicate, marker)
proc isObjectPredicate(t: PType): bool =
result = t.kind == tyObject
proc containsObject(t: PType): bool =
result = searchTypeFor(t, isObjectPredicate)
proc isObjectWithTypeFieldPredicate(t: PType): bool =
result = (t.kind == tyObject) and (t.sons[0] == nil) and
not ((t.sym != nil) and (sfPure in t.sym.flags)) and
not (tfFinal in t.flags)
proc analyseObjectWithTypeFieldAux(t: PType, marker: var TIntSet): TTypeFieldResult =
var res: TTypeFieldResult
result = frNone
if t == nil: return
case t.kind
of tyObject:
if (t.n != nil):
if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker):
return frEmbedded
for i in countup(0, sonsLen(t) - 1):
res = analyseObjectWithTypeFieldAux(t.sons[i], marker)
if res == frEmbedded:
return frEmbedded
if res == frHeader: result = frHeader
if result == frNone:
if isObjectWithTypeFieldPredicate(t): result = frHeader
of tyGenericInst, tyDistinct:
result = analyseObjectWithTypeFieldAux(lastSon(t), marker)
of tyArray, tyArrayConstr, tyTuple:
for i in countup(0, sonsLen(t) - 1):
res = analyseObjectWithTypeFieldAux(t.sons[i], marker)
if res != frNone:
return frEmbedded
else:
nil
proc analyseObjectWithTypeField(t: PType): TTypeFieldResult =
var marker: TIntSet
IntSetInit(marker)
result = analyseObjectWithTypeFieldAux(t, marker)
proc isGBCRef(t: PType): bool =
result = t.kind in {tyRef, tySequence, tyString}
proc containsGarbageCollectedRef(typ: PType): bool =
# returns true if typ contains a reference, sequence or string (all the things
# that are garbage-collected)
result = searchTypeFor(typ, isGBCRef)
proc isHiddenPointer(t: PType): bool =
result = t.kind in {tyString, tySequence}
proc containsHiddenPointer(typ: PType): bool =
# returns true if typ contains a string, table or sequence (all the things
# that need to be copied deeply)
result = searchTypeFor(typ, isHiddenPointer)
proc canFormAcycleAux(marker: var TIntSet, typ: PType, startId: int): bool
proc canFormAcycleNode(marker: var TIntSet, n: PNode, startId: int): bool =
result = false
if n != nil:
result = canFormAcycleAux(marker, n.typ, startId)
if not result:
case n.kind
of nkNone..nkNilLit:
nil
else:
for i in countup(0, sonsLen(n) - 1):
result = canFormAcycleNode(marker, n.sons[i], startId)
if result: return
proc canFormAcycleAux(marker: var TIntSet, typ: PType, startId: int): bool =
var t: PType
result = false
if typ == nil: return
if tfAcyclic in typ.flags: return
t = skipTypes(typ, abstractInst)
if tfAcyclic in t.flags: return
case t.kind
of tyTuple, tyObject, tyRef, tySequence, tyArray, tyArrayConstr, tyOpenArray:
if not IntSetContainsOrIncl(marker, t.id):
for i in countup(0, sonsLen(t) - 1):
result = canFormAcycleAux(marker, t.sons[i], startId)
if result: return
if t.n != nil: result = canFormAcycleNode(marker, t.n, startId)
else:
result = t.id == startId
else:
nil
proc canFormAcycle(typ: PType): bool =
var marker: TIntSet
IntSetInit(marker)
result = canFormAcycleAux(marker, typ, typ.id)
proc mutateTypeAux(marker: var TIntSet, t: PType, iter: TTypeMutator,
closure: PObject): PType
proc mutateNode(marker: var TIntSet, n: PNode, iter: TTypeMutator,
closure: PObject): PNode =
result = nil
if n != nil:
result = copyNode(n)
result.typ = mutateTypeAux(marker, n.typ, iter, closure)
case n.kind
of nkNone..nkNilLit:
# a leaf
else:
for i in countup(0, sonsLen(n) - 1):
addSon(result, mutateNode(marker, n.sons[i], iter, closure))
proc mutateTypeAux(marker: var TIntSet, t: PType, iter: TTypeMutator,
closure: PObject): PType =
result = nil
if t == nil: return
result = iter(t, closure)
if not IntSetContainsOrIncl(marker, t.id):
for i in countup(0, sonsLen(t) - 1):
result.sons[i] = mutateTypeAux(marker, result.sons[i], iter, closure)
if (result.sons[i] == nil) and (result.kind == tyGenericInst):
assert(false)
if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure)
assert(result != nil)
proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType =
var marker: TIntSet
IntSetInit(marker)
result = mutateTypeAux(marker, t, iter, closure)
proc rangeToStr(n: PNode): string =
assert(n.kind == nkRange)
result = ValueToString(n.sons[0]) & ".." & ValueToString(n.sons[1])
proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
const
typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty",
"Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc",
"GenericInvokation", "GenericBody", "GenericInst", "GenericParam",
"distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
"set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
"pointer", "OpenArray[$1]", "string", "CString", "Forward", "int", "int8",
"int16", "int32", "int64", "float", "float32", "float64", "float128"]
var
t: PType
prag: string
t = typ
result = ""
if t == nil: return
if (prefer == preferName) and (t.sym != nil):
return t.sym.Name.s
case t.Kind
of tyGenericInst:
result = typeToString(lastSon(t), prefer)
of tyArray:
if t.sons[0].kind == tyRange:
result = "array[" & rangeToStr(t.sons[0].n) & ", " &
typeToString(t.sons[1]) & ']'
else:
result = "array[" & typeToString(t.sons[0]) & ", " &
typeToString(t.sons[1]) & ']'
of tyGenericInvokation, tyGenericBody:
result = typeToString(t.sons[0]) & '['
for i in countup(1, sonsLen(t) - 1):
if i > 1: add(result, ", ")
add(result, typeToString(t.sons[i]))
add(result, ']')
of tyArrayConstr:
result = "Array constructor[" & rangeToStr(t.sons[0].n) & ", " &
typeToString(t.sons[1]) & ']'
of tySequence:
result = "seq[" & typeToString(t.sons[0]) & ']'
of tyOrdinal:
result = "ordinal[" & typeToString(t.sons[0]) & ']'
of tySet:
result = "set[" & typeToString(t.sons[0]) & ']'
of tyOpenArray:
result = "openarray[" & typeToString(t.sons[0]) & ']'
of tyDistinct:
result = "distinct " & typeToString(t.sons[0], preferName)
of tyTuple:
# we iterate over t.sons here, because t.n may be nil
result = "tuple["
if t.n != nil:
assert(sonsLen(t.n) == sonsLen(t))
for i in countup(0, sonsLen(t.n) - 1):
assert(t.n.sons[i].kind == nkSym)
add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i]))
if i < sonsLen(t.n) - 1: add(result, ", ")
else:
for i in countup(0, sonsLen(t) - 1):
add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ")
add(result, ']')
of tyPtr, tyRef, tyVar:
result = typeToStr[t.kind] & typeToString(t.sons[0])
of tyRange:
result = "range " & rangeToStr(t.n)
of tyProc:
result = "proc ("
for i in countup(1, sonsLen(t) - 1):
add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ")
add(result, ')')
if t.sons[0] != nil: add(result, ": " & TypeToString(t.sons[0]))
if t.callConv != ccDefault: prag = CallingConvToStr[t.callConv]
else: prag = ""
if tfNoSideEffect in t.flags:
addSep(prag)
add(prag, "noSideEffect")
if len(prag) != 0: add(result, "{." & prag & ".}")
else:
result = typeToStr[t.kind]
proc resultType(t: PType): PType =
assert(t.kind == tyProc)
result = t.sons[0] # nil is allowed
proc base(t: PType): PType =
result = t.sons[0]
proc firstOrd(t: PType): biggestInt =
case t.kind
of tyBool, tyChar, tySequence, tyOpenArray:
result = 0
of tySet, tyVar:
result = firstOrd(t.sons[0])
of tyArray, tyArrayConstr:
result = firstOrd(t.sons[0])
of tyRange:
assert(t.n != nil) # range directly given:
assert(t.n.kind == nkRange)
result = getOrdValue(t.n.sons[0])
of tyInt:
if platform.intSize == 4: result = - (2147483646) - 2
else: result = 0x8000000000000000'i64
of tyInt8:
result = - 128
of tyInt16:
result = - 32768
of tyInt32:
result = - 2147483646 - 2
of tyInt64:
result = 0x8000000000000000'i64
of tyEnum:
# if basetype <> nil then return firstOrd of basetype
if (sonsLen(t) > 0) and (t.sons[0] != nil):
result = firstOrd(t.sons[0])
else:
assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct:
result = firstOrd(lastSon(t))
else:
InternalError("invalid kind for first(" & $t.kind & ')')
result = 0
proc lastOrd(t: PType): biggestInt =
case t.kind
of tyBool:
result = 1
of tyChar:
result = 255
of tySet, tyVar:
result = lastOrd(t.sons[0])
of tyArray, tyArrayConstr:
result = lastOrd(t.sons[0])
of tyRange:
assert(t.n != nil) # range directly given:
assert(t.n.kind == nkRange)
result = getOrdValue(t.n.sons[1])
of tyInt:
if platform.intSize == 4: result = 0x7FFFFFFF
else: result = 0x7FFFFFFFFFFFFFFF'i64
of tyInt8:
result = 0x0000007F
of tyInt16:
result = 0x00007FFF
of tyInt32:
result = 0x7FFFFFFF
of tyInt64:
result = 0x7FFFFFFFFFFFFFFF'i64
of tyEnum:
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position
of tyGenericInst, tyDistinct:
result = firstOrd(lastSon(t))
else:
InternalError("invalid kind for last(" & $t.kind & ')')
result = 0
proc lengthOrd(t: PType): biggestInt =
case t.kind
of tyInt64, tyInt32, tyInt: result = lastOrd(t)
of tyDistinct: result = lengthOrd(t.sons[0])
else: result = lastOrd(t) - firstOrd(t) + 1
proc equalParam(a, b: PSym): TParamsEquality =
if SameTypeOrNil(a.typ, b.typ):
if (a.ast == b.ast):
result = paramsEqual
elif (a.ast != nil) and (b.ast != nil):
if ExprStructuralEquivalent(a.ast, b.ast): result = paramsEqual
else: result = paramsIncompatible
elif (a.ast != nil):
result = paramsEqual
elif (b.ast != nil):
result = paramsIncompatible
else:
result = paramsNotEqual
proc equalParams(a, b: PNode): TParamsEquality =
var
length: int
m, n: PSym
result = paramsEqual
length = sonsLen(a)
if length != sonsLen(b):
result = paramsNotEqual
else:
for i in countup(1, length - 1):
m = a.sons[i].sym
n = b.sons[i].sym
assert((m.kind == skParam) and (n.kind == skParam))
case equalParam(m, n)
of paramsNotEqual:
return paramsNotEqual
of paramsEqual:
nil
of paramsIncompatible:
result = paramsIncompatible
if (m.name.id != n.name.id):
# BUGFIX
return paramsNotEqual # paramsIncompatible;
# continue traversal! If not equal, we can return immediately; else
# it stays incompatible
if not SameTypeOrNil(a.sons[0].typ, b.sons[0].typ):
if (a.sons[0].typ == nil) or (b.sons[0].typ == nil):
result = paramsNotEqual # one proc has a result, the other not is OK
else:
result = paramsIncompatible # overloading by different
# result types does not work
proc SameTypeOrNil(a, b: PType): bool =
if a == b:
result = true
else:
if (a == nil) or (b == nil): result = false
else: result = SameType(a, b)
proc SameLiteral(x, y: PNode): bool =
result = false
if x.kind == y.kind:
case x.kind
of nkCharLit..nkInt64Lit: result = x.intVal == y.intVal
of nkFloatLit..nkFloat64Lit: result = x.floatVal == y.floatVal
of nkNilLit: result = true
else: assert(false)
proc SameRanges(a, b: PNode): bool =
result = SameLiteral(a.sons[0], b.sons[0]) and
SameLiteral(a.sons[1], b.sons[1])
proc sameTuple(a, b: PType, DistinctOf: bool): bool =
# two tuples are equivalent iff the names, types and positions are the same;
# however, both types may not have any field names (t.n may be nil) which
# complicates the matter a bit.
var x, y: PSym
if sonsLen(a) == sonsLen(b):
result = true
for i in countup(0, sonsLen(a) - 1):
if DistinctOf: result = equalOrDistinctOf(a.sons[i], b.sons[i])
else: result = SameType(a.sons[i], b.sons[i])
if not result: return
if (a.n != nil) and (b.n != nil):
for i in countup(0, sonsLen(a.n) - 1):
# check field names:
if a.n.sons[i].kind != nkSym: InternalError(a.n.info, "sameTuple")
if b.n.sons[i].kind != nkSym: InternalError(b.n.info, "sameTuple")
x = a.n.sons[i].sym
y = b.n.sons[i].sym
result = x.name.id == y.name.id
if not result: break
else:
result = false
proc SameType(x, y: PType): bool =
var a, b: PType
if x == y:
return true
a = skipTypes(x, {tyGenericInst})
b = skipTypes(y, {tyGenericInst})
assert(a != nil)
assert(b != nil)
if a.kind != b.kind:
return false
case a.Kind
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyFloat128, tyExpr, tyStmt, tyTypeDesc:
result = true
of tyEnum, tyForward, tyObject, tyDistinct:
result = (a.id == b.id)
of tyTuple:
result = sameTuple(a, b, false)
of tyGenericInst:
result = sameType(lastSon(a), lastSon(b))
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc:
if sonsLen(a) == sonsLen(b):
result = true
for i in countup(0, sonsLen(a) - 1):
result = SameTypeOrNil(a.sons[i], b.sons[i]) # BUGFIX
if not result: return
if result and (a.kind == tyProc):
result = a.callConv == b.callConv # BUGFIX
else:
result = false
of tyRange:
result = SameTypeOrNil(a.sons[0], b.sons[0]) and
SameValue(a.n.sons[0], b.n.sons[0]) and
SameValue(a.n.sons[1], b.n.sons[1])
of tyNone:
result = false
proc equalOrDistinctOf(x, y: PType): bool =
var a, b: PType
if x == y:
return true
if (x == nil) or (y == nil):
return false
a = skipTypes(x, {tyGenericInst})
b = skipTypes(y, {tyGenericInst})
assert(a != nil)
assert(b != nil)
if a.kind != b.kind:
if a.kind == tyDistinct: a = a.sons[0]
if a.kind != b.kind:
return false
case a.Kind
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyFloat128, tyExpr, tyStmt, tyTypeDesc:
result = true
of tyEnum, tyForward, tyObject, tyDistinct:
result = (a.id == b.id)
of tyTuple:
result = sameTuple(a, b, true)
of tyGenericInst:
result = equalOrDistinctOf(lastSon(a), lastSon(b))
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc:
if sonsLen(a) == sonsLen(b):
result = true
for i in countup(0, sonsLen(a) - 1):
result = equalOrDistinctOf(a.sons[i], b.sons[i])
if not result: return
if result and (a.kind == tyProc): result = a.callConv == b.callConv
else:
result = false
of tyRange:
result = equalOrDistinctOf(a.sons[0], b.sons[0]) and
SameValue(a.n.sons[0], b.n.sons[0]) and
SameValue(a.n.sons[1], b.n.sons[1])
of tyNone:
result = false
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool
proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool =
result = true
if n != nil:
result = typeAllowedAux(marker, n.typ, kind)
if not result: debug(n.typ)
if result:
case n.kind
of nkNone..nkNilLit:
nil
else:
for i in countup(0, sonsLen(n) - 1):
result = typeAllowedNode(marker, n.sons[i], kind)
if not result: return
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
var t, t2: PType
assert(kind in {skVar, skConst, skParam})
result = true
if typ == nil:
return # if we have already checked the type, return true, because we stop the
# evaluation if something is wrong:
if IntSetContainsOrIncl(marker, typ.id): return
t = skipTypes(typ, abstractInst)
case t.kind
of tyVar:
t2 = skipTypes(t.sons[0], abstractInst)
case t2.kind
of tyVar:
result = false # ``var var`` is always an invalid type:
of tyOpenArray:
result = (kind == skParam) and typeAllowedAux(marker, t2, kind)
else: result = (kind != skConst) and typeAllowedAux(marker, t2, kind)
of tyProc:
for i in countup(1, sonsLen(t) - 1):
result = typeAllowedAux(marker, t.sons[i], skParam)
if not result: return
if t.sons[0] != nil: result = typeAllowedAux(marker, t.sons[0], skVar)
of tyExpr, tyStmt, tyTypeDesc:
result = true
of tyGenericBody, tyGenericParam, tyForward, tyNone, tyGenericInvokation:
result = false #InternalError('shit found');
of tyEmpty, tyNil:
result = kind == skConst
of tyString, tyBool, tyChar, tyEnum, tyInt..tyFloat128, tyCString, tyPointer:
result = true
of tyOrdinal:
result = kind == skParam
of tyGenericInst, tyDistinct:
result = typeAllowedAux(marker, lastSon(t), kind)
of tyRange:
result = skipTypes(t.sons[0], abstractInst).kind in
{tyChar, tyEnum, tyInt..tyFloat128}
of tyOpenArray:
result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar)
of tySequence:
result = (kind != skConst) and typeAllowedAux(marker, t.sons[0], skVar) or
(t.sons[0].kind == tyEmpty)
of tyArray:
result = typeAllowedAux(marker, t.sons[1], skVar)
of tyPtr, tyRef:
result = typeAllowedAux(marker, t.sons[0], skVar)
of tyArrayConstr, tyTuple, tySet:
for i in countup(0, sonsLen(t) - 1):
result = typeAllowedAux(marker, t.sons[i], kind)
if not result: return
of tyObject:
for i in countup(0, sonsLen(t) - 1):
result = typeAllowedAux(marker, t.sons[i], skVar)
if not result: return
if t.n != nil: result = typeAllowedNode(marker, t.n, skVar)
proc typeAllowed(t: PType, kind: TSymKind): bool =
var marker: TIntSet
IntSetInit(marker)
result = typeAllowedAux(marker, t, kind)
proc align(address, alignment: biggestInt): biggestInt =
result = (address + (alignment - 1)) and not (alignment - 1)
proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt
proc computeRecSizeAux(n: PNode, a, currOffset: var biggestInt): biggestInt =
var maxAlign, maxSize, b, res: biggestInt
case n.kind
of nkRecCase:
assert(n.sons[0].kind == nkSym)
result = computeRecSizeAux(n.sons[0], a, currOffset)
maxSize = 0
maxAlign = 1
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
res = computeRecSizeAux(lastSon(n.sons[i]), b, currOffset)
if res < 0:
return res
maxSize = max(maxSize, res)
maxAlign = max(maxAlign, b)
else: internalError("computeRecSizeAux(record case branch)")
currOffset = align(currOffset, maxAlign) + maxSize
result = align(result, maxAlign) + maxSize
a = maxAlign
of nkRecList:
result = 0
maxAlign = 1
for i in countup(0, sonsLen(n) - 1):
res = computeRecSizeAux(n.sons[i], b, currOffset)
if res < 0:
return res
currOffset = align(currOffset, b) + res
result = align(result, b) + res
if b > maxAlign: maxAlign = b
a = maxAlign
of nkSym:
result = computeSizeAux(n.sym.typ, a)
n.sym.offset = int(currOffset)
else:
InternalError("computeRecSizeAux()")
a = 1
result = - 1
proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
var res, maxAlign, length, currOffset: biggestInt
if typ.size == - 2:
# we are already computing the size of the type
# --> illegal recursion in type
return - 2
if typ.size >= 0:
# size already computed
result = typ.size
a = typ.align
return
typ.size = - 2 # mark as being computed
case typ.kind
of tyInt:
result = IntSize
a = result
of tyInt8, tyBool, tyChar:
result = 1
a = result
of tyInt16:
result = 2
a = result
of tyInt32, tyFloat32:
result = 4
a = result
of tyInt64, tyFloat64:
result = 8
a = result
of tyFloat:
result = floatSize
a = result
of tyProc:
if typ.callConv == ccClosure: result = 2 * ptrSize
else: result = ptrSize
a = ptrSize
of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyOpenArray:
result = ptrSize
a = result
of tyArray, tyArrayConstr:
result = lengthOrd(typ.sons[0]) * computeSizeAux(typ.sons[1], a)
of tyEnum:
if firstOrd(typ) < 0:
result = 4 # use signed int32
else:
length = lastOrd(typ) # BUGFIX: use lastOrd!
if length + 1 < `shl`(1, 8): result = 1
elif length + 1 < `shl`(1, 16): result = 2
elif length + 1 < `shl`(biggestInt(1), 32): result = 4
else: result = 8
a = result
of tySet:
length = lengthOrd(typ.sons[0])
if length <= 8:
result = 1
elif length <= 16:
result = 2
elif length <= 32:
result = 4
elif length <= 64:
result = 8
elif align(length, 8) mod 8 == 0:
result = align(length, 8) div 8
else:
result = align(length, 8) div 8 + 1 # BUGFIX!
a = result
of tyRange:
result = computeSizeAux(typ.sons[0], a)
of tyTuple:
result = 0
maxAlign = 1
for i in countup(0, sonsLen(typ) - 1):
res = computeSizeAux(typ.sons[i], a)
if res < 0:
return res
maxAlign = max(maxAlign, a)
result = align(result, a) + res
result = align(result, maxAlign)
a = maxAlign
of tyObject:
if typ.sons[0] != nil:
result = computeSizeAux(typ.sons[0], a)
if result < 0: return
maxAlign = a
elif isObjectWithTypeFieldPredicate(typ):
result = intSize
maxAlign = result
else:
result = 0
maxAlign = 1
currOffset = result
result = computeRecSizeAux(typ.n, a, currOffset)
if result < 0: return
if a < maxAlign: a = maxAlign
result = align(result, a)
of tyGenericInst, tyDistinct, tyGenericBody:
result = computeSizeAux(lastSon(typ), a)
else:
#internalError('computeSizeAux()');
result = - 1
typ.size = result
typ.align = int(a)
proc computeSize(typ: PType): biggestInt =
var a: biggestInt = 1
result = computeSizeAux(typ, a)
proc getSize(typ: PType): biggestInt =
result = computeSize(typ)
if result < 0: InternalError("getSize(" & $typ.kind & ')')

147
rod/wordrecg.nim Executable file
View File

@@ -0,0 +1,147 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module contains a word recognizer, i.e. a simple
# procedure which maps special words to an enumeration.
# It is primarily needed because Pascal's case statement
# does not support strings. Without this the code would
# be slow and unreadable.
import
nhashes, strutils, idents
type
TSpecialWord* = enum
wInvalid, # these are mapped to Nimrod keywords:
#[[[cog
#from string import split, capitalize
#keywords = split(open("data/keywords.txt").read())
#idents = ""
#strings = ""
#i = 1
#for k in keywords:
# idents = idents + "w" + capitalize(k) + ", "
# strings = strings + "'" + k + "', "
# if i % 4 == 0:
# idents = idents + "\n"
# strings = strings + "\n"
# i = i + 1
#cog.out(idents)
#]]]
wAddr, wAnd, wAs, wAsm, wBind, wBlock, wBreak, wCase, wCast, wConst,
wContinue, wConverter, wDiscard, wDistinct, wDiv, wElif, wElse, wEnd, wEnum,
wExcept, wFinally, wFor, wFrom, wGeneric, wIf, wImplies, wImport, wIn,
wInclude, wIs, wIsnot, wIterator, wLambda, wMacro, wMethod, wMod, wNil,
wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
wShl, wShr, wTemplate, wTry, wTuple, wType, wVar, wWhen, wWhile, wWith,
wWithout, wXor, wYield, #[[[end]]]
# other special tokens:
wColon, wEquals, wDot, wDotDot, wHat, wStar, wMinus, # pragmas and command line options:
wMagic, wTypeCheck, wFinal, wProfiler, wObjChecks, wImportc, wExportc,
wAlign, wNodecl, wPure, wVolatile, wRegister, wSideeffect, wHeader,
wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar,
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
wLinedir, wStacktrace, wLinetrace, wParallelBuild, wLink, wCompile,
wLinksys, wDeprecated, wVarargs, wByref, wCallconv, wBreakpoint, wDebugger,
wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline,
wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wW,
wHints, wOptimization, wSpeed, wSize, wNone, wPath, wP, wD, wU, wDebuginfo,
wCompileonly, wNolinking, wForcebuild, wF, wDeadCodeElim, wSafecode,
wCompileTime, wGc, wRefc, wBoehm, wA, wOpt, wO, wApp, wConsole, wGui,
wPassc, wT, wPassl, wL, wListcmd, wGendoc, wGenmapping, wOs, wCpu,
wGenerate, wG, wC, wCpp, wBorrow, wRun, wR, wVerbosity, wV, wHelp, wH,
wSymbolFiles, wFieldChecks, wX, wVersion, wAdvanced, wSkipcfg, wSkipProjCfg,
wCc, wGenscript, wCheckPoint, wCheckPoints, wNoMain, wSubsChar, wAcyclic, wIndex, #
# commands:
wCompileToC, wCompileToCpp, wCompileToEcmaScript, wCompileToLLVM, wPretty,
wDoc, wPas, wGenDepend, wListDef, wCheck, wParse, wScan, wBoot, wLazy,
wRst2html, wRst2tex, wI, # special for the preprocessor of configuration files:
wWrite, wPutEnv, wPrependEnv, wAppendEnv, # additional Pascal keywords:
wArray, wBegin, wClass, wConstructor, wDestructor, wDo, wDownto, wExports,
wFinalization, wFunction, wGoto, wImplementation, wInherited,
wInitialization, wInterface, wLabel, wLibrary, wPacked, wProcedure,
wProgram, wProperty, wRecord, wRepeat, wResourcestring, wSet, wThen,
wThreadvar, wTo, wUnit, wUntil, wUses, # Pascal special tokens:
wExternal, wOverload, wFar, wAssembler, wForward, wIfdef, wIfndef, wEndif
TSpecialWords* = set[TSpecialWord]
const
oprLow* = ord(wColon)
oprHigh* = ord(wHat)
specialWords*: array[low(TSpecialWord)..high(TSpecialWord), string] = ["", #
# keywords:
#
#[[[cog
#
#cog.out(strings)
#]]]
"addr", "and", "as", "asm", "bind", "block", "break", "case", "cast",
"const", "continue", "converter", "discard", "distinct", "div", "elif",
"else", "end", "enum", "except", "finally", "for", "from", "generic", "if",
"implies", "import", "in", "include", "is", "isnot", "iterator", "lambda",
"macro", "method", "mod", "nil", "not", "notin", "object", "of", "or",
"out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "template",
"try", "tuple", "type", "var", "when", "while", "with", "without", "xor", "yield", #[[[end]]]
# other special tokens:
":", "=", ".", "..", "^", "*", "-", # pragmas and command line options:
"magic", "typecheck", "final", "profiler", "objchecks", "importc",
"exportc", "align", "nodecl", "pure", "volatile", "register", "sideeffect",
"header", "nosideeffect", "noreturn", "merge", "lib", "dynlib",
"compilerproc", "procvar", "fatal", "error", "warning", "hint", "line",
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
"parallelbuild", "link", "compile", "linksys", "deprecated", "varargs",
"byref", "callconv", "breakpoint", "debugger", "nimcall", "stdcall",
"cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure",
"noconv", "on", "off", "checks", "rangechecks", "boundchecks",
"overflowchecks", "nilchecks", "assertions", "warnings", "w", "hints",
"optimization", "speed", "size", "none", "path", "p", "d", "u", "debuginfo",
"compileonly", "nolinking", "forcebuild", "f", "deadcodeelim", "safecode",
"compiletime", "gc", "refc", "boehm", "a", "opt", "o", "app", "console",
"gui", "passc", "t", "passl", "l", "listcmd", "gendoc", "genmapping", "os",
"cpu", "generate", "g", "c", "cpp", "borrow", "run", "r", "verbosity", "v",
"help", "h", "symbolfiles", "fieldchecks", "x", "version", "advanced",
"skipcfg", "skipprojcfg", "cc", "genscript", "checkpoint", "checkpoints",
"nomain", "subschar", "acyclic", "index", # commands:
"compiletoc", "compiletocpp", "compiletoecmascript", "compiletollvm",
"pretty", "doc", "pas", "gendepend", "listdef", "check", "parse", "scan",
"boot", "lazy", "rst2html", "rst2tex", "i", # special for the preprocessor of configuration files:
"write", "putenv", "prependenv", "appendenv", "array", "begin", "class",
"constructor", "destructor", "do", "downto", "exports", "finalization",
"function", "goto", "implementation", "inherited", "initialization",
"interface", "label", "library", "packed", "procedure", "program",
"property", "record", "repeat", "resourcestring", "set", "then",
"threadvar", "to", "unit", "until", "uses", # Pascal special tokens
"external", "overload", "far", "assembler", "forward", "ifdef", "ifndef",
"endif"]
proc whichKeyword*(id: PIdent): TSpecialWord
proc whichKeyword*(id: String): TSpecialWord
proc findStr*(a: openarray[string], s: string): int
# implementation
proc findStr(a: openarray[string], s: string): int =
for i in countup(low(a), high(a)):
if cmpIgnoreStyle(a[i], s) == 0:
return i
result = - 1
proc whichKeyword(id: String): TSpecialWord =
result = whichKeyword(getIdent(id))
proc whichKeyword(id: PIdent): TSpecialWord =
if id.id < 0: result = wInvalid
else: result = TSpecialWord(id.id)
proc initSpecials() =
# initialize the keywords:
for s in countup(succ(low(specialWords)), high(specialWords)):
getIdent(specialWords[s], getNormalizedHash(specialWords[s])).id = ord(s)
initSpecials()