# Primitives are functions that are implemented directly in Mu. # They always evaluate all their arguments. fn initialize-primitives _self: (addr global-table) { var self/esi: (addr global-table) <- copy _self # for numbers append-primitive self, "+" append-primitive self, "-" append-primitive self, "*" append-primitive self, "/" append-primitive self, "%" append-primitive self, "sqrt" append-primitive self, "abs" append-primitive self, "sgn" append-primitive self, "<" append-primitive self, ">" append-primitive self, "<=" append-primitive self, ">=" # generic append-primitive self, "apply" append-primitive self, "=" append-primitive self, "no" append-primitive self, "not" append-primitive self, "dbg" append-primitive self, "len" # for pairs append-primitive self, "car" append-primitive self, "cdr" append-primitive self, "cons" append-primitive self, "cons?" # for screens append-primitive self, "print" append-primitive self, "clear" append-primitive self, "lines" append-primitive self, "columns" append-primitive self, "up" append-primitive self, "down" append-primitive self, "left" append-primitive self, "right" append-primitive self, "cr" append-primitive self, "pixel" append-primitive self, "line" append-primitive self, "hline" append-primitive self, "vline" append-primitive self, "circle" append-primitive self, "bezier" append-primitive self, "width" append-primitive self, "height" append-primitive self, "new_screen" append-primitive self, "blit" # for keyboards append-primitive self, "key" # for streams append-primitive self, "stream" append-primitive self, "write" append-primitive self, "read" append-primitive self, "rewind" # for arrays append-primitive self, "array" append-primitive self, "populate" append-primitive self, "index" append-primitive self, "iset" # for images append-primitive self, "img" # misc append-primitive self, "abort" # keep sync'd with render-primitives } # Slightly misnamed; renders primitives as well as special forms that don't # evaluate all their arguments. fn render-primitives screen: (addr screen), xmin: int, xmax: int, ymax: int { var y/ecx: int <- copy ymax y <- subtract 0x11/primitives-border clear-rect screen, xmin, y, xmax, ymax, 0xdc/bg=green-bg y <- increment var right-min/edx: int <- copy xmax right-min <- subtract 0x1e/primitives-divider set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, "primitives", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, " fn apply set if while", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, "booleans", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, " = and or not", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, "lists", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, " cons car cdr no cons? len", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, "numbers", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, " + - * / %", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, " < > <= >=", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, " sqrt abs sgn", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, "arrays", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, " array index iset len", right-min, y, xmax, ymax, 0x2a/fg=orange, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy right-min tmpx <- draw-text-rightward screen, " populate", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": int _ -> array", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment set-cursor-position screen, right-min, y draw-text-wrapping-right-then-down-from-cursor screen, "images", right-min, y, xmax, ymax, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy right-min tmpx <- draw-text-rightward screen, " img", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": screen stream x y w h", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg #? { #? compare screen, 0 #? break-if-!= #? var foo/eax: byte <- read-key 0/keyboard #? compare foo, 0 #? loop-if-= #? } y <- copy ymax y <- subtract 0x10/primitives-border var left-max/edx: int <- copy xmax left-max <- subtract 0x20/primitives-divider var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, "cursor graphics", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " print", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": screen _ -> _", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " lines columns", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": screen -> number", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " up down left right", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": screen", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " cr", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": screen ", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, "# move cursor down and to left margin", tmpx, left-max, y, 0x38/fg=trace, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, "pixel graphics", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " circle bezier line hline vline pixel", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " width height", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": screen -> number", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " clear", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": screen", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, "input", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " key", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": keyboard -> code-point-utf8?", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, "streams", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " stream", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": -> stream ", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " write", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": stream code-point-utf8 -> stream", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " rewind clear", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": stream", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg y <- increment var tmpx/eax: int <- copy xmin tmpx <- draw-text-rightward screen, " read", tmpx, left-max, y, 0x2a/fg=orange, 0xdc/bg=green-bg tmpx <- draw-text-rightward screen, ": stream -> code-point-utf8", tmpx, left-max, y, 7/fg=grey, 0xdc/bg=green-bg } fn primitive-global? _x: (addr global) -> _/eax: boolean { var x/eax: (addr global) <- copy _x var value-ah/eax: (addr handle cell) <- get x, value var value/eax: (addr cell) <- lookup *value-ah compare value, 0/null { break-if-!= return 0/false } var primitive?/eax: boolean <- primitive? value return primitive? } fn append-primitive _self: (addr global-table), name: (addr array byte) { var self/esi: (addr global-table) <- copy _self compare self, 0 { break-if-!= abort "append primitive" return } var final-index-addr/ecx: (addr int) <- get self, final-index increment *final-index-addr var curr-index/ecx: int <- copy *final-index-addr var data-ah/eax: (addr handle array global) <- get self, data var data/eax: (addr array global) <- lookup *data-ah var curr-offset/esi: (offset global) <- compute-offset data, curr-index var curr/esi: (addr global) <- index data, curr-offset var curr-name-ah/eax: (addr handle array byte) <- get curr, name copy-array-object name, curr-name-ah var curr-value-ah/eax: (addr handle cell) <- get curr, value new-primitive-function curr-value-ah, curr-index } # a little strange; goes from value to name and selects primitive based on name fn apply-primitive _f: (addr cell), args-ah: (addr handle cell), out: (addr handle cell), _globals: (addr global-table), trace: (addr trace) { var f/esi: (addr cell) <- copy _f var f-index-a/ecx: (addr int) <- get f, index-data var f-index/ecx: int <- copy *f-index-a var globals/eax: (addr global-table) <- copy _globals compare globals, 0 { break-if-!= abort "apply primitive" return } var global-data-ah/eax: (addr handle array global) <- get globals, data var global-data/eax: (addr array global) <- lookup *global-data-ah var f-offset/ecx: (offset global) <- compute-offset global-data, f-index var f-value/ecx: (addr global) <- index global-data, f-offset var f-name-ah/ecx: (addr handle array byte) <- get f-value, name var f-name/eax: (addr array byte) <- lookup *f-name-ah { var add?/eax: boolean <- string-equal? f-name, "+" compare add?, 0/false break-if-= apply-add args-ah, out, trace return } { var subtract?/eax: boolean <- string-equal? f-name, "-" compare subtract?, 0/false break-if-= apply-subtract args-ah, out, trace return } { var multiply?/eax: boolean <- string-equal? f-name, "*" compare multiply?, 0/false break-if-= apply-multiply args-ah, out, trace return } { var divide?/eax: boolean <- string-equal? f-name, "/" compare divide?, 0/false break-if-= apply-divide args-ah, out, trace return } # '%' is the remainder operator, because modulo isn't really meaningful for # non-integers # # I considered calling this operator 'rem', but I want to follow Arc in # using 'rem' for filtering out elements from lists. # https://arclanguage.github.io/ref/list.html#rem { var remainder?/eax: boolean <- string-equal? f-name, "%" compare remainder?, 0/false break-if-= apply-remainder args-ah, out, trace return } { var square-root?/eax: boolean <- string-equal? f-name, "sqrt" compare square-root?, 0/false break-if-= apply-square-root args-ah, out, trace return } { var abs?/eax: boolean <- string-equal? f-name, "abs" compare abs?, 0/false break-i
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the merge operation of 2 different C files. This
## is needed for incremental compilation.
import
ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
intsets, platform, llstream
# Careful! Section marks need to contain a tabulator so that they cannot
# be part of C string literals.
const
CFileSectionNames: array[TCFileSection, string] = [
cfsMergeInfo: "",
cfsHeaders: "NIM_merge_HEADERS",
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
cfsTypes: "NIM_merge_TYPES",
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
cfsFieldInfo: "NIM_merge_FIELD_INFO",
cfsTypeInfo: "NIM_merge_TYPE_INFO",
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
cfsData: "NIM_merge_DATA",
cfsVars: "NIM_merge_VARS",
cfsProcs: "NIM_merge_PROCS",
cfsInitProc: "NIM_merge_INIT_PROC",
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
cfsDebugInit: "NIM_merge_DEBUG_INIT",
cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
]
CProcSectionNames: array[TCProcSection, string] = [
cpsLocals: "NIM_merge_PROC_LOCALS",
cpsInit: "NIM_merge_PROC_INIT",
cpsStmts: "NIM_merge_PROC_BODY"
]
NimMergeEndMark = "/*\tNIM_merge_END:*/"
proc genSectionStart*(fs: TCFileSection): PRope =
if compilationCachePresent:
result = toRope(tnl)
app(result, "/*\t")
app(result, CFileSectionNames[fs])
app(result, ":*/")
app(result, tnl)
proc genSectionEnd*(fs: TCFileSection): PRope =
if compilationCachePresent:
result = toRope(NimMergeEndMark & tnl)
proc genSectionStart*(ps: TCProcSection): PRope =
if compilationCachePresent:
result = toRope(tnl)
app(result, "/*\t")
app(result, CProcSectionNames[ps])
app(result, ":*/")
app(result, tnl)
proc genSectionEnd*(ps: TCProcSection): PRope =
if compilationCachePresent:
result = toRope(NimMergeEndMark & tnl)
proc writeTypeCache(a: TIdTable, s: var string) =
var i = 0
for id, value in pairs(a):
if i == 10:
i = 0
s.add(tnl)
else:
s.add(' ')
encodeVInt(id, s)
s.add(':')
encodeStr(PRope(value).ropeToStr, s)
inc i
s.add('}')
proc writeIntSet(a: IntSet, s: var string) =
var i = 0
for x in items(a):
if i == 10:
i = 0
s.add(tnl)
else:
s.add(' ')
encodeVInt(x, s)
inc i
s.add('}')
proc genMergeInfo*(m: BModule): PRope =
if optSymbolFiles notin gGlobalOptions: return nil
var s = "/*\tNIM_merge_INFO:"
s.add(tnl)
s.add("typeCache:{")
writeTypeCache(m.typeCache, s)
s.add("declared:{")
writeIntSet(m.declaredThings, s)
s.add("typeInfo:{")
writeIntSet(m.typeInfoMarker, s)
s.add("labels:")
encodeVInt(m.labels, s)
s.add(" hasframe:")
encodeVInt(ord(m.frameDeclared), s)
s.add(tnl)
s.add("*/")
result = s.toRope
template `^`(pos: expr): expr = L.buf[pos]
proc skipWhite(L: var TBaseLexer) =
var pos = L.bufpos
while true:
case ^pos
of CR: pos = nimlexbase.handleCR(L, pos)
of LF: pos = nimlexbase.handleLF(L, pos)
of ' ': inc pos
else: break
L.bufpos = pos
proc skipUntilCmd(L: var TBaseLexer) =
var pos = L.bufpos
while true:
case ^pos
of CR: pos = nimlexbase.handleCR(L, pos)
of LF: pos = nimlexbase.handleLF(L, pos)
of '\0': break
of '/':
if ^(pos+1) == '*' and ^(pos+2) == '\t':
inc pos, 3
break
inc pos
else: inc pos
L.bufpos = pos
proc atEndMark(buf: cstring, pos: int): bool =
var s = 0
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
result = s == NimMergeEndMark.len
proc readVerbatimSection(L: var TBaseLexer): PRope =
var pos = L.bufpos
var buf = L.buf
var r = newStringOfCap(30_000)
while true:
case buf[pos]
of CR:
pos = nimlexbase.handleCR(L, pos)
buf = L.buf
r.add(tnl)
of LF:
pos = nimlexbase.handleLF(L, pos)
buf = L.buf
r.add(tnl)
of '\0':
internalError("ccgmerge: expected: " & NimMergeEndMark)
break
else:
if atEndMark(buf, pos):
inc pos, NimMergeEndMark.len
break
r.add(buf[pos])
inc pos
L.bufpos = pos
result = r.toRope
proc readKey(L: var TBaseLexer, result: var string) =
var pos = L.bufpos
var buf = L.buf
setLen(result, 0)
while buf[pos] in IdentChars:
result.add(buf[pos])
inc pos
if buf[pos] != ':': internalError("ccgmerge: ':' expected")
L.bufpos = pos + 1 # skip ':'
proc newFakeType(id: int): PType =
new(result)
result.id = id
proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
inc L.bufpos
while ^L.bufpos != '}':
skipWhite(L)
var key = decodeVInt(L.buf, L.bufpos)
if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
inc L.bufpos
var value = decodeStr(L.buf, L.bufpos)
# XXX little hack: we create a "fake" type object with the correct Id
# better would be to adapt the data structure to not even store the
# object as key, but only the Id
idTablePut(result, newFakeType(key), value.toRope)
inc L.bufpos
proc readIntSet(L: var TBaseLexer, result: var IntSet) =
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
inc L.bufpos
while ^L.bufpos != '}':
skipWhite(L)
var key = decodeVInt(L.buf, L.bufpos)
result.incl(key)
inc L.bufpos
proc processMergeInfo(L: var TBaseLexer, m: BModule) =
var k = newStringOfCap("typeCache".len)
while true:
skipWhite(L)
if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
inc(L.bufpos, 2)
break
readKey(L, k)
case k
of "typeCache": readTypeCache(L, m.typeCache)
of "declared": readIntSet(L, m.declaredThings)
of "typeInfo": readIntSet(L, m.typeInfoMarker)
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
of "hasframe": m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0
else: internalError("ccgmerge: unknown key: " & k)
when not defined(nimhygiene):
{.pragma: inject.}
template withCFile(cfilename: string, body: stmt) {.immediate.} =
var s = llStreamOpen(cfilename, fmRead)
if s == nil: return
var L {.inject.}: TBaseLexer
openBaseLexer(L, s)
var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
while true:
skipUntilCmd(L)
if ^L.bufpos == '\0': break
body
closeBaseLexer(L)
proc readMergeInfo*(cfilename: string, m: BModule) =
## reads the merge meta information into `m`.
withCFile(cfilename):
readKey(L, k)
if k == "NIM_merge_INFO":
processMergeInfo(L, m)
break
type
TMergeSections = object
f: TCFileSections
p: TCProcSections
proc readMergeSections(cfilename: string, m: var TMergeSections) =
## reads the merge sections into `m`.
withCFile(cfilename):
readKey(L, k)
if k == "NIM_merge_INFO":
discard
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
inc(L.bufpos, 2)
# read back into section
skipWhite(L)
var verbatim = readVerbatimSection(L)
skipWhite(L)
var sectionA = CFileSectionNames.find(k)
if sectionA > 0 and sectionA <= high(TCFileSection).int:
m.f[TCFileSection(sectionA)] = verbatim
else:
var sectionB = CProcSectionNames.find(k)
if sectionB >= 0 and sectionB <= high(TCProcSection).int:
m.p[TCProcSection(sectionB)] = verbatim
else:
internalError("ccgmerge: unknown section: " & k)
else:
internalError("ccgmerge: '*/' expected")
proc mergeRequired*(m: BModule): bool =
for i in cfsHeaders..cfsProcs:
if m.s[i] != nil:
#echo "not empty: ", i, " ", ropeToStr(m.s[i])
return true
for i in low(TCProcSection)..high(TCProcSection):
if m.initProc.s(i) != nil:
#echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i])
return true
proc mergeFiles*(cfilename: string, m: BModule) =
## merges the C file with the old version on hard disc.
var old: TMergeSections
readMergeSections(cfilename, old)
# do the merge; old section before new section:
for i in low(TCFileSection)..high(TCFileSection):
m.s[i] = con(old.f[i], m.s[i])
for i in low(TCProcSection)..high(TCProcSection):
m.initProc.s(i) = con(old.p[i], m.initProc.s(i))