tiny C backend for a much faster REPL

This commit is contained in:
Andreas Rumpf 2010-04-02 19:34:57 +02:00
commit f530bbd631
12 changed files with 208 additions and 53 deletions

View file

@ -17,7 +17,7 @@ elif defined(darwin):
lib = "gdk-x11-2.0" lib = "gdk-x11-2.0"
else: else:
const const
lib = "libgdk-x11-2.0.so" lib = "libgdk-x11-2.0.so(|.0)"
const const
NUMPTSTOBUFFER* = 200 NUMPTSTOBUFFER* = 200
MAX_TIMECOORD_AXES* = 128 MAX_TIMECOORD_AXES* = 128

118
lib/wrappers/tinyc.nim Normal file
View file

@ -0,0 +1,118 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
type
TccState {.pure, final.} = object
PccState* = ptr TccState
TErrorFunc* = proc (opaque: pointer, msg: cstring) {.cdecl.}
proc openCCState*(): PccState {.importc: "tcc_new", cdecl.}
## create a new TCC compilation context
proc closeCCState*(s: PccState) {.importc: "tcc_delete", cdecl.}
## free a TCC compilation context
proc enableDebug*(s: PccState) {.importc: "tcc_enable_debug", cdecl.}
## add debug information in the generated code
proc setErrorFunc*(s: PccState, errorOpaque: pointer, errorFun: TErrorFunc) {.
cdecl, importc: "tcc_set_error_func".}
## set error/warning display callback
proc setWarning*(s: PccState, warningName: cstring, value: int) {.cdecl,
importc: "tcc_set_warning".}
## set/reset a warning
# preprocessor
proc addIncludePath*(s: PccState, pathname: cstring) {.cdecl,
importc: "tcc_add_include_path".}
## add include path
proc addSysincludePath*(s: PccState, pathname: cstring) {.cdecl,
importc: "tcc_add_sysinclude_path".}
## add in system include path
proc defineSymbol*(s: PccState, sym, value: cstring) {.cdecl,
importc: "tcc_define_symbol".}
## define preprocessor symbol 'sym'. Can put optional value
proc undefineSymbol*(s: PccState, sym: cstring) {.cdecl,
importc: "tcc_undefine_symbol".}
## undefine preprocess symbol 'sym'
# compiling
proc addFile*(s: PccState, filename: cstring): cint {.cdecl,
importc: "tcc_add_file".}
## add a file (either a C file, dll, an object, a library or an ld
## script). Return -1 if error.
proc compileString*(s: PccState, buf: cstring): cint {.cdecl,
importc: "tcc_compile_string".}
## compile a string containing a C source. Return non zero if error.
# linking commands
const
OutputMemory*: cint = 0 ## output will be ran in memory (no
## output file) (default)
OutputExe*: cint = 1 ## executable file
OutputDll*: cint = 2 ## dynamic library
OutputObj*: cint = 3 ## object file
OutputPreprocess*: cint = 4 ## preprocessed file (used internally)
OutputFormatElf*: cint = 0 ## default output format: ELF
OutputFormatBinary*: cint = 1 ## binary image output
OutputFormatCoff*: cint = 2 ## COFF
proc setOutputType*(s: PCCState, outputType: cint): cint {.cdecl,
importc: "tcc_set_output_type".}
## set output type. MUST BE CALLED before any compilation
proc addLibraryPath*(s: PccState, pathname: cstring): cint {.cdecl,
importc: "tcc_add_library_path".}
## equivalent to -Lpath option
proc addLibrary*(s: PCCState, libraryname: cstring): cint {.cdecl,
importc: "tcc_add_library".}
## the library name is the same as the argument of the '-l' option
proc addSymbol*(s: PccState, name: cstring, val: pointer): cint {.cdecl,
importc: "tcc_add_symbol".}
## add a symbol to the compiled program
proc outputFile*(s: PccState, filename: cstring): cint {.cdecl,
importc: "tcc_output_file".}
## output an executable, library or object file. DO NOT call
## tcc_relocate() before.
proc run*(s: PccState, argc: cint, argv: cstringArray): cint {.cdecl,
importc: "tcc_run".}
## link and run main() function and return its value. DO NOT call
## tcc_relocate() before.
proc relocate*(s: PccState, p: pointer): cint {.cdecl,
importc: "tcc_relocate".}
## copy code into memory passed in by the caller and do all relocations
## (needed before using tcc_get_symbol()).
## returns -1 on error and required size if ptr is NULL
proc getSymbol*(s: PccState, name: cstring): pointer {.cdecl,
importc: "tcc_get_symbol".}
## return symbol value or NULL if not found
proc setLibPath*(s: PccState, path: cstring) {.cdecl,
importc: "tcc_set_lib_path".}
## set CONFIG_TCCDIR at runtime

View file

@ -1,7 +1,7 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -15,6 +15,9 @@ import
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, rnimsyn, treetab, cgmeth times, ropes, math, passes, rodread, wordrecg, rnimsyn, treetab, cgmeth
when options.hasTinyCBackend:
import tccgen
proc cgenPass*(): TPass proc cgenPass*(): TPass
# implementation # implementation
@ -518,7 +521,8 @@ proc genProcAux(m: BModule, prc: PSym) =
if p.beforeRetNeeded: app(generatedProc, "BeforeRet: ;" & tnl) if p.beforeRetNeeded: app(generatedProc, "BeforeRet: ;" & tnl)
if optStackTrace in prc.options: app(generatedProc, deinitFrame(p)) if optStackTrace in prc.options: app(generatedProc, deinitFrame(p))
if (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM): if (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM):
appf(generatedProc, "profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n", appf(generatedProc,
"profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n",
[toRope(prc.loc.a)]) [toRope(prc.loc.a)])
app(generatedProc, returnStmt) app(generatedProc, returnStmt)
app(generatedProc, '}' & tnl) app(generatedProc, '}' & tnl)
@ -600,13 +604,15 @@ proc genConstPrototype(m: BModule, sym: PSym) =
proc getFileHeader(cfilenoext: string): PRope = proc getFileHeader(cfilenoext: string): PRope =
if optCompileOnly in gGlobalOptions: if optCompileOnly in gGlobalOptions:
result = ropeff("/* Generated by Nimrod Compiler v$1 */$n" & result = ropeff("/* Generated by Nimrod Compiler v$1 */$n" &
"/* (c) 2009 Andreas Rumpf */$n", "; Generated by Nimrod Compiler v$1$n" & "/* (c) 2010 Andreas Rumpf */$n",
"; (c) 2009 Andreas Rumpf$n", [toRope(versionAsString)]) "; Generated by Nimrod Compiler v$1$n" &
"; (c) 2010 Andreas Rumpf$n", [toRope(versionAsString)])
else: else:
result = ropeff("/* Generated by Nimrod Compiler v$1 */$n" & result = ropeff("/* Generated by Nimrod Compiler v$1 */$n" &
"/* (c) 2009 Andreas Rumpf */$n" & "/* Compiled for: $2, $3, $4 */$n" & "/* (c) 2010 Andreas Rumpf */$n" & "/* Compiled for: $2, $3, $4 */$n" &
"/* Command for C compiler:$n $5 */$n", "; Generated by Nimrod Compiler v$1$n" & "/* Command for C compiler:$n $5 */$n",
"; (c) 2009 Andreas Rumpf$n" & "; Compiled for: $2, $3, $4$n" & "; Generated by Nimrod Compiler v$1$n" &
"; (c) 2010 Andreas Rumpf$n" & "; Compiled for: $2, $3, $4$n" &
"; Command for LLVM compiler:$n $5$n", [toRope(versionAsString), "; Command for LLVM compiler:$n $5$n", [toRope(versionAsString),
toRope(platform.OS[targetOS].name), toRope(platform.OS[targetOS].name),
toRope(platform.CPU[targetCPU].name), toRope(platform.CPU[targetCPU].name),
@ -668,7 +674,8 @@ proc genMainProc(m: BModule) =
" LPVOID lpvReserved) {$n" & " NimMain();$n" & " LPVOID lpvReserved) {$n" & " NimMain();$n" &
" return 1;$n" & "}$n" " return 1;$n" & "}$n"
WinNimDllMainLLVM = WinNimMainLLVM WinNimDllMainLLVM = WinNimMainLLVM
WinCDllMainLLVM = "define stdcall i32 @DllMain(i32 %hinstDLL, i32 %fwdreason, $n" & WinCDllMainLLVM =
"define stdcall i32 @DllMain(i32 %hinstDLL, i32 %fwdreason, $n" &
" i8* %lpvReserved) {$n" & " i8* %lpvReserved) {$n" &
" call void @NimMain()$n" & " ret i32 1$n" & "}$n" " call void @NimMain()$n" & " ret i32 1$n" & "}$n"
var nimMain, otherMain: TFormatStr var nimMain, otherMain: TFormatStr
@ -795,13 +802,13 @@ proc myOpen(module: PSym, filename: string): PPassContext =
if gNimDat == nil: registerTypeInfoModule() if gNimDat == nil: registerTypeInfoModule()
result = newModule(module, filename) result = newModule(module, filename)
proc myOpenCached(module: PSym, filename: string, rd: PRodReader): PPassContext = proc myOpenCached(module: PSym, filename: string,
var cfile, cfilenoext, objFile: string rd: PRodReader): PPassContext =
if gNimDat == nil: if gNimDat == nil:
registerTypeInfoModule() registerTypeInfoModule()
#MessageOut('cgen.myOpenCached has been called ' + filename); #MessageOut('cgen.myOpenCached has been called ' + filename);
cfile = changeFileExt(completeCFilePath(filename), cExt) var cfile = changeFileExt(completeCFilePath(filename), cExt)
cfilenoext = changeFileExt(cfile, "") var cfilenoext = changeFileExt(cfile, "")
addFileToLink(cfilenoext) addFileToLink(cfilenoext)
registerModuleToMain(module) registerModuleToMain(module)
# XXX: this cannot be right here, initalization has to be appended during # XXX: this cannot be right here, initalization has to be appended during
@ -838,18 +845,21 @@ proc finishModule(m: BModule) =
setlen(m.forwardedProcs, 0) setlen(m.forwardedProcs, 0)
proc writeModule(m: BModule) = proc writeModule(m: BModule) =
var
cfile, cfilenoext: string
code: PRope
# generate code for the init statements of the module: # generate code for the init statements of the module:
genInitCode(m) genInitCode(m)
finishTypeDescriptions(m) finishTypeDescriptions(m)
cfile = completeCFilePath(m.cfilename) var cfile = completeCFilePath(m.cfilename)
cfilenoext = changeFileExt(cfile, "") var cfilenoext = changeFileExt(cfile, "")
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
# generate main file: # generate main file:
app(m.s[cfsProcHeaders], mainModProcs) app(m.s[cfsProcHeaders], mainModProcs)
code = genModule(m, cfilenoext) var code = genModule(m, cfilenoext)
when hasTinyCBackend:
if gCmd == cmdRun:
tccgen.compileCCode(ropeToStr(code))
return
if shouldRecompile(code, changeFileExt(cfile, cExt), cfilenoext): if shouldRecompile(code, changeFileExt(cfile, cExt), cfilenoext):
addFileToCompile(cfilenoext) addFileToCompile(cfilenoext)
addFileToLink(cfilenoext) addFileToLink(cfilenoext)
@ -867,7 +877,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
finishModule(m) finishModule(m)
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
var disp = generateMethodDispatchers() var disp = generateMethodDispatchers()
for i in countup(0, sonsLen(disp) - 1): genProcAux(gNimDat, disp.sons[i].sym) for i in 0..sonsLen(disp)-1: genProcAux(gNimDat, disp.sons[i].sym)
genMainProc(m) genMainProc(m)
# we need to process the transitive closure because recursive module # we need to process the transitive closure because recursive module
# deps are allowed (and the system module is processed in the wrong # deps are allowed (and the system module is processed in the wrong

View file

@ -36,6 +36,7 @@ Usage::
Command:: Command::
compile, c compile project with default code generator (C) compile, c compile project with default code generator (C)
compileToC, cc compile project with C code generator compileToC, cc compile project with C code generator
run compile the project in memory and run it
doc generate the documentation for inputfile doc generate the documentation for inputfile
rst2html converts a reStructuredText file to HTML rst2html converts a reStructuredText file to HTML
rst2tex converts a reStructuredText file to TeX rst2tex converts a reStructuredText file to TeX
@ -70,7 +71,6 @@ Options:
AdvancedUsage = """ AdvancedUsage = """
Advanced commands:: Advanced commands::
pas convert a Pascal file to Nimrod syntax
pretty pretty print the inputfile pretty pretty print the inputfile
genDepend generate a DOT file containing the genDepend generate a DOT file containing the
module dependency graph module dependency graph

View file

@ -1014,9 +1014,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
case result.kind case result.kind
of nkExceptBranch, nkReturnToken, nkBreakStmt: break of nkExceptBranch, nkReturnToken, nkBreakStmt: break
else: nil else: nil
of nkProcDef, nkMethodDef, nkMacroDef, nkCommentStmt, nkPragma, nkTypeSection, of nkProcDef, nkMethodDef, nkMacroDef, nkCommentStmt, nkPragma,
nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef, nkTypeSection, nkTemplateDef, nkConstSection, nkIteratorDef,
nkIncludeStmt, nkImportStmt, nkFromStmt: nkConverterDef, nkIncludeStmt, nkImportStmt, nkFromStmt:
nil nil
of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
nkLambda, nkContinueStmt, nkIdent: nkLambda, nkContinueStmt, nkIdent:

View file

@ -1,7 +1,7 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -480,8 +480,8 @@ proc genMappingFiles(list: TLinkedList): PRope =
it = PStrEntry(it.next) it = PStrEntry(it.next)
proc writeMapping(gSymbolMapping: PRope) = proc writeMapping(gSymbolMapping: PRope) =
if not (optGenMapping in gGlobalOptions): return if optGenMapping notin gGlobalOptions: return
var code = toRope("[C_Files]" & "\n") var code = toRope("[C_Files]\n")
app(code, genMappingFiles(toCompile)) app(code, genMappingFiles(toCompile))
app(code, genMappingFiles(externalToCompile)) app(code, genMappingFiles(externalToCompile))
appf(code, "[Symbols]$n$1", [gSymbolMapping]) appf(code, "[Symbols]$n$1", [gSymbolMapping])

View file

@ -118,7 +118,8 @@ proc CommandCompileToC(filename: string) =
registerPass(rodwrite.rodwritePass()) registerPass(rodwrite.rodwritePass())
#registerPass(cleanupPass()) #registerPass(cleanupPass())
compileProject(filename) compileProject(filename)
extccomp.CallCCompiler(changeFileExt(filename, "")) if gCmd != cmdRun:
extccomp.CallCCompiler(changeFileExt(filename, ""))
when has_LLVM_Backend: when has_LLVM_Backend:
proc CommandCompileToLLVM(filename: string) = proc CommandCompileToLLVM(filename: string) =
@ -191,6 +192,13 @@ proc MainCommand(cmd, filename: string) =
gCmd = cmdCompileToC gCmd = cmdCompileToC
wantFile(filename) wantFile(filename)
CommandCompileToC(filename) CommandCompileToC(filename)
of wRun:
gCmd = cmdRun
wantFile(filename)
when hasTinyCBackend:
CommandCompileToC(filename)
else:
rawMessage(errInvalidCommandX, cmd)
of wCompileToCpp: of wCompileToCpp:
gCmd = cmdCompileToCpp gCmd = cmdCompileToCpp
wantFile(filename) wantFile(filename)
@ -204,6 +212,8 @@ proc MainCommand(cmd, filename: string) =
wantFile(filename) wantFile(filename)
when has_LLVM_Backend: when has_LLVM_Backend:
CommandCompileToLLVM(filename) CommandCompileToLLVM(filename)
else:
rawMessage(errInvalidCommandX, cmd)
of wPretty: of wPretty:
gCmd = cmdPretty gCmd = cmdPretty
wantFile(filename) #CommandExportSymbols(filename); wantFile(filename) #CommandExportSymbols(filename);

View file

@ -1,7 +1,7 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -298,14 +298,18 @@ const
type type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind] TNoteKinds* = set[TNoteKind]
TLineInfo*{.final.} = object # This is designed to be as small as possible, because it is used TLineInfo*{.final.} = object # This is designed to be as small as possible,
# in syntax nodes. We safe space here by using two int16 and an int32 # because it is used
# on 64 bit and on 32 bit systems this is only 8 bytes. # 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 line*, col*: int16
fileIndex*: int32 fileIndex*: int32
proc UnknownLineInfo*(): TLineInfo proc UnknownLineInfo*(): TLineInfo
var var
gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)}
gErrorCounter*: int = 0 # counts the number of errors gErrorCounter*: int = 0 # counts the number of errors

View file

@ -11,6 +11,9 @@ import
times, commands, scanner, condsyms, options, msgs, nversion, nimconf, ropes, times, commands, scanner, condsyms, options, msgs, nversion, nimconf, ropes,
extccomp, strutils, os, platform, main, parseopt extccomp, strutils, os, platform, main, parseopt
when hasTinyCBackend:
import tccgen
var var
arguments: string = "" # the arguments to be passed to the program that arguments: string = "" # the arguments to be passed to the program that
# should be run # should be run
@ -21,8 +24,7 @@ proc ProcessCmdLine(pass: TCmdLinePass, command, filename: var string) =
while true: while true:
parseopt.next(p) parseopt.next(p)
case p.kind case p.kind
of cmdEnd: of cmdEnd: break
break
of cmdLongOption, cmdShortOption: of cmdLongOption, cmdShortOption:
# hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off") # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
# we fix this here # we fix this here
@ -41,7 +43,7 @@ proc ProcessCmdLine(pass: TCmdLinePass, command, filename: var string) =
break break
if pass == passCmd2: if pass == passCmd2:
arguments = cmdLineRest(p) arguments = cmdLineRest(p)
if not (optRun in gGlobalOptions) and (arguments != ""): if optRun notin gGlobalOptions and arguments != "":
rawMessage(errArgsNeedRunOption, []) rawMessage(errArgsNeedRunOption, [])
proc HandleCmdLine() = proc HandleCmdLine() =
@ -63,8 +65,11 @@ proc HandleCmdLine() =
ProcessCmdLine(passCmd2, command, filename) ProcessCmdLine(passCmd2, command, filename)
MainCommand(command, filename) MainCommand(command, filename)
if gVerbosity >= 2: echo(GC_getStatistics()) if gVerbosity >= 2: echo(GC_getStatistics())
if (gCmd != cmdInterpret) and (msgs.gErrorCounter == 0): when hasTinyCBackend:
rawMessage(hintSuccessX, [$(gLinesCompiled), $(getTime() - start)]) if gCmd == cmdRun:
tccgen.run()
if gCmd notin {cmdInterpret, cmdRun} and msgs.gErrorCounter == 0:
rawMessage(hintSuccessX, [$gLinesCompiled, $(getTime() - start)])
if optRun in gGlobalOptions: if optRun in gGlobalOptions:
when defined(unix): when defined(unix):
var prog = "./" & quoteIfContainsWhite(changeFileExt(filename, "")) var prog = "./" & quoteIfContainsWhite(changeFileExt(filename, ""))

View file

@ -10,6 +10,9 @@
import import
os, lists, strutils, nstrtabs os, lists, strutils, nstrtabs
const
hasTinyCBackend* = true
type # please make sure we have under 32 options type # please make sure we have under 32 options
# (improves code efficiency a lot!) # (improves code efficiency a lot!)
TOption* = enum # **keep binary compatible** TOption* = enum # **keep binary compatible**
@ -48,7 +51,8 @@ type # please make sure we have under 32 options
cmdDebugTrans, # debug a transformation pass cmdDebugTrans, # debug a transformation pass
cmdRst2html, # convert a reStructuredText file to HTML cmdRst2html, # convert a reStructuredText file to HTML
cmdRst2tex, # convert a reStructuredText file to TeX cmdRst2tex, # convert a reStructuredText file to TeX
cmdInteractive # start interactive session cmdInteractive, # start interactive session
cmdRun # run the project via TCC backend
TStringSeq* = seq[string] TStringSeq* = seq[string]
const const

View file

@ -67,10 +67,12 @@ const
type type
TFormatStr* = string # later we may change it to CString for better TFormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments copy the format strings # performance of the code generator (assignments
# copy the format strings
# though it is not necessary) # though it is not necessary)
PRope* = ref TRope PRope* = ref TRope
TRope*{.acyclic.} = object of TObject # the empty rope is represented by nil to safe space TRope*{.acyclic.} = object of TObject # the empty rope is represented
# by nil to safe space
left*, right*: PRope left*, right*: PRope
length*: int length*: int
data*: string # != nil if a leaf data*: string # != nil if a leaf

View file

@ -1,20 +1,22 @@
#
#
# Nimrod REPL
# (c) Copyright 2010 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import glib2, gtk2, gdk2, osproc, dialogs, strutils import glib2, gtk2, gdk2, osproc, dialogs, strutils
type proc execCode(code: string): string =
output = tuple[compiler, app: string]
proc execCode(code: string): output =
var f: TFile var f: TFile
if open(f, "temp.nim", fmWrite): if open(f, "temp.nim", fmWrite):
f.write(code) f.write(code)
f.close() f.close()
else: else:
raise newException(EIO, "Unable to open file") raise newException(EIO, "Unable to open file")
result = osproc.execProcess("nimrod run --verbosity:0 temp.nim")
var compilerOutput = osproc.execProcess("nimrod c temp.nim")
var appOutput = osproc.execProcess("temp.exe")
return (compilerOutput, appOutput)
var shiftPressed = False var shiftPressed = False
var w: gtk2.PWindow var w: gtk2.PWindow
@ -83,8 +85,8 @@ proc inputKeyReleased(widget: PWidget, event: PEventKey,
try: try:
var r = execCode($InputText) var r = execCode($InputText)
set_text(OutputTextBuffer, r[0] & r[1], len(r[0] & r[1])) set_text(OutputTextBuffer, r, len(r))
except: except EIO:
setError("Error: Could not open file temp.nim") setError("Error: Could not open file temp.nim")