about summary refs log tree commit diff stats
path: root/js/games/nluqo.github.io/~bh/part6.html
blob: 47b87021ad1bc4b9bd41f6132c361406700963dc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<HTML>
<HEAD>
<TITLE>Simply Scheme part VI introduction</TITLE>
</HEAD>
<BODY>
<CITE>Simply Scheme</CITE> 2/e Copyright (C) 1999 MIT
<H1>Sequential Programming</H1>

<TABLE><TR><TD>
<P><IMG SRC="simply.jpg" ALT="cover photo">
<TD valign="center">
<CITE><A HREF="http://www.cs.berkeley.edu/~bh/">Brian
Harvey</A><BR><A HREF="http://www.cnmat.berkeley.edu/~matt">Matthew
Wright</A><BR>University of California, Berkeley</CITE>
<BR><BR><A HREF="http://www-mitpress.mit.edu/book-home.tcl?isbn=0262082810">MIT
Press web page for Simply Scheme</A>
</TABLE>

<P><A HREF="simply-toc.html">(back to Table of Contents)</A>

<HR>

<P>The three big ideas in this part are <EM>effect, sequence,</EM> and <EM>state.</EM>

<P>Until now, we've been doing functional programming, where the focus is on
functions and their return values.  Invoking a function is like asking a
question: "What's two plus two?" In this part of the book we're going to
talk about giving commands to the computer as well as asking it questions.
That is, we'll invoke procedures that tell Scheme to <EM>do</EM> something,
such as <CODE>wash-the-dishes</CODE>.  (Unfortunately, the Scheme standard
leaves out this primitive.)  Instead of merely computing a value, such a
procedure has an <EM>effect,</EM> an action that changes something.

<P>Once we're thinking about actions, it's very natural to consider
a <EM>sequence</EM> of actions.  First cooking dinner, then eating, and then washing
the dishes is one sequence.  First eating, then washing the dishes, and then
cooking is a much less sensible sequence.

<P>Although these ideas of sequence and effect are coming near the end of our
book, they're the ideas with which almost every introduction to programming
begins. Most books compare a program to a recipe or a sequence of
instructions, along the lines of

<PRE>
to go-to-work
  get-dressed
  eat-breakfast
  catch-the-bus
</PRE>

<P>This sequential programming style is simple and natural, and it does
a good job of modeling computations in which the problem concerns
a sequence of events.  If you're writing an airline reservation system, a
sequential program with <CODE>reserve-seat</CODE> and <CODE>issue-ticket</CODE> commands
makes sense.  But if you want to know the acronym of a phrase, that's
not inherently sequential, and a question-asking approach is best.

<P>Some actions that Scheme can take affect the "outside" world, such as
printing something on the computer screen.  But Scheme can also carry out
internal actions, invisible outside the computer, but changing the
environment in which Scheme itself carries out computations.  Defining a new
variable with <CODE>define</CODE> is an example; before the definition, Scheme
wouldn't understand what that name means, but once the definition has been
made, the name can be used in evaluating later expressions.  Scheme's
knowledge about the leftover effects of past computations is called
its <EM>state.</EM> The third big idea in this part of the book is that we can write
programs that maintain state information and use it to determine their
results.

<P>Like sequence, the notion of state contradicts functional programming.
Earlier in the book, we emphasized that every time a function is invoked
with the same arguments, it must return the same value.  But a procedure
whose returned value depends on state--on the past history of the
computation--might return a different value on each invocation, even with
identical arguments.

<P>We'll explore several situations in which effects,
sequence, and state are useful:

<UL>
<LI>Interactive, question-and-answer programs that involve keyboard input
while the computation is in progress;
<LI>Programs that must read and write long-term data file storage;
<LI>Computations that <EM>model</EM> an actual sequence of events in time
and use the state of the program to model information about the state of
the simulated events.
</UL>

<P>After introducing Scheme's mechanisms for sequential programming, we'll use
those mechanisms to implement versions of two commonly used types of
business computer applications, a spreadsheet and a database program.

<P><A HREF="simply-toc.html">(back to Table of Contents)</A>
</BODY>
</HTML>
0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#
#
#           The Nimrod Compiler
#        (c) Copyright 2014 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# implements the command dispatcher and several commands

import
  llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
  os, condsyms, rodread, rodwrite, times,
  wordrecg, sem, semdata, idents, passes, docgen, extccomp,
  cgen, jsgen, json, nversion,
  platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
  tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists,
  pretty

from magicsys import systemModule, resetSysTypes

const
  hasLLVM_Backend = false

when hasLLVM_Backend:
  import llvmgen

proc rodPass =
  if optSymbolFiles in gGlobalOptions:
    registerPass(rodwritePass)

proc codegenPass =
  registerPass cgenPass

proc semanticPasses =
  registerPass verbosePass
  registerPass semPass

proc commandGenDepend =
  semanticPasses()
  registerPass(gendependPass)
  registerPass(cleanupPass)
  compileProject()
  generateDot(gProjectFull)
  execExternalProgram("dot -Tpng -o" & changeFileExt(gProjectFull, "png") &
      ' ' & changeFileExt(gProjectFull, "dot"))

proc commandCheck =
  msgs.gErrorMax = high(int)  # do not stop after first error
  semanticPasses()            # use an empty backend for semantic checking only
  rodPass()
  compileProject()

proc commandDoc2 =
  msgs.gErrorMax = high(int)  # do not stop after first error
  semanticPasses()
  registerPass(docgen2Pass)
  #registerPass(cleanupPass())
  compileProject()
  finishDoc2Pass(gProjectName)

proc commandCompileToC =
  semanticPasses()
  registerPass(cgenPass)
  rodPass()
  #registerPass(cleanupPass())
  if optCaasEnabled in gGlobalOptions:
    # echo "BEFORE CHECK DEP"
    # discard checkDepMem(gProjectMainIdx)
    # echo "CHECK DEP COMPLETE"

  compileProject()
  cgenWriteModules()
  if gCmd != cmdRun:
    extccomp.callCCompiler(changeFileExt(gProjectFull, ""))

  if isServing:
    # caas will keep track only of the compilation commands
    lastCaasCmd = curCaasCmd
    resetCgenModules()
    for i in 0 .. <gMemCacheData.len:
      gMemCacheData[i].crcStatus = crcCached
      gMemCacheData[i].needsRecompile = Maybe

      # XXX: clean these global vars
      # ccgstmts.gBreakpoints
      # ccgthreadvars.nimtv
      # ccgthreadvars.nimtVDeps
      # ccgthreadvars.nimtvDeclared
      # cgendata
      # cgmeth?
      # condsyms?
      # depends?
      # lexer.gLinesCompiled
      # msgs - error counts
      # magicsys, when system.nim changes
      # rodread.rodcompilerProcs
      # rodread.gTypeTable
      # rodread.gMods

      # !! ropes.cache
      # semthreads.computed?
      #
      # suggest.usageSym
      #
      # XXX: can we run out of IDs?
      # XXX: detect config reloading (implement as error/require restart)
      # XXX: options are appended (they will accumulate over time)
    resetCompilationLists()
    ccgutils.resetCaches()
    GC_fullCollect()

when hasLLVM_Backend:
  proc commandCompileToLLVM =
    semanticPasses()
    registerPass(llvmgen.llvmgenPass())
    rodPass()
    #registerPass(cleanupPass())
    compileProject()

proc commandCompileToJS =
  #incl(gGlobalOptions, optSafeCode)
  setTarget(osJS, cpuJS)
  #initDefines()
  defineSymbol("nimrod") # 'nimrod' is always defined
  defineSymbol("ecmascript") # For backward compatibility
  defineSymbol("js")
  semanticPasses()
  registerPass(JSgenPass)
  compileProject()

proc interactivePasses =
  #incl(gGlobalOptions, optSafeCode)
  #setTarget(osNimrodVM, cpuNimrodVM)
  initDefines()
  defineSymbol("nimrodvm")
  when hasFFI: DefineSymbol("nimffi")
  registerPass(verbosePass)
  registerPass(semPass)
  registerPass(evalPass)

proc commandInteractive =
  msgs.gErrorMax = high(int)  # do not stop after first error
  interactivePasses()
  compileSystemModule()
  if commandArgs.len > 0:
    discard compileModule(fileInfoIdx(gProjectFull), {})
  else:
    var m = makeStdinModule()
    incl(m.flags, sfMainModule)
    processModule(m, llStreamOpenStdIn(), nil)

const evalPasses = [verbosePass, semPass, evalPass]

proc evalNim(nodes: PNode, module: PSym) =
  carryPasses(nodes, module, evalPasses)

proc commandEval(exp: string) =
  if systemModule == nil:
    interactivePasses()
    compileSystemModule()
  var echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")"
  evalNim(echoExp.parseString, makeStdinModule())

proc commandPrettyOld =
  var projectFile = addFileExt(mainCommandArg(), NimExt)
  var module = parseFile(projectFile.fileInfoIdx)
  if module != nil:
    renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt))

proc commandPretty =
  msgs.gErrorMax = high(int)  # do not stop after first error
  semanticPasses()
  registerPass(prettyPass)
  compileProject()
  pretty.overwriteFiles()

proc commandScan =
  var f = addFileExt(mainCommandArg(), NimExt)
  var stream = llStreamOpen(f, fmRead)
  if stream != nil:
    var
      L: TLexer
      tok: TToken
    initToken(tok)
    openLexer(L, f, stream)
    while true:
      rawGetTok(L, tok)
      printTok(tok)
      if tok.tokType == tkEof: break
    closeLexer(L)
  else:
    rawMessage(errCannotOpenFile, f)

proc commandSuggest =
  if isServing:
    # XXX: hacky work-around ahead
    # Currently, it's possible to issue a idetools command, before
    # issuing the first compile command. This will leave the compiler
    # cache in a state where "no recompilation is necessary", but the
    # cgen pass was never executed at all.
    commandCompileToC()
    if gDirtyBufferIdx != 0:
      discard compileModule(gDirtyBufferIdx, {sfDirty})
      resetModule(gDirtyBufferIdx)
    if optDef in gGlobalOptions:
      defFromSourceMap(optTrackPos)
  else:
    msgs.gErrorMax = high(int)  # do not stop after first error
    semanticPasses()
    rodPass()
    # XXX: this handles the case when the dirty buffer is the main file,
    # but doesn't handle the case when it's imported module
    var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
                   else: gProjectMainIdx
    compileProject(projFile)

proc wantMainModule =
  if gProjectFull.len == 0:
    if optMainModule.len == 0:
      fatal(gCmdLineInfo, errCommandExpectsFilename)
    else:
      gProjectName = optMainModule
      gProjectFull = gProjectPath / gProjectName

  gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx

proc requireMainModuleOption =
  if optMainModule.len == 0:
    fatal(gCmdLineInfo, errMainModuleMustBeSpecified)
  else:
    gProjectName = optMainModule
    gProjectFull = gProjectPath / gProjectName

  gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx

proc resetMemory =
  resetCompilationLists()
  ccgutils.resetCaches()
  resetAllModules()
  resetRopeCache()
  resetSysTypes()
  gOwners = @[]
  rangeDestructorProc = nil
  for i in low(buckets)..high(buckets):
    buckets[i] = nil
  idAnon = nil

  # XXX: clean these global vars
  # ccgstmts.gBreakpoints
  # ccgthreadvars.nimtv
  # ccgthreadvars.nimtVDeps
  # ccgthreadvars.nimtvDeclared
  # cgendata
  # cgmeth?
  # condsyms?
  # depends?
  # lexer.gLinesCompiled
  # msgs - error counts
  # magicsys, when system.nim changes
  # rodread.rodcompilerProcs
  # rodread.gTypeTable
  # rodread.gMods

  # !! ropes.cache
  # semthreads.computed?
  #
  # suggest.usageSym
  #
  # XXX: can we run out of IDs?
  # XXX: detect config reloading (implement as error/require restart)
  # XXX: options are appended (they will accumulate over time)
  # vis = visimpl
  when compileOption("gc", "v2"):
    gcDebugging = true
    echo "COLLECT 1"
    GC_fullCollect()
    echo "COLLECT 2"
    GC_fullCollect()
    echo "COLLECT 3"
    GC_fullCollect()
    echo GC_getStatistics()

const
  SimiluateCaasMemReset = false
  PrintRopeCacheStats = false

proc mainCommand* =
  when SimiluateCaasMemReset:
    gGlobalOptions.incl(optCaasEnabled)

  # In "nimrod serve" scenario, each command must reset the registered passes
  clearPasses()
  gLastCmdTime = epochTime()
  appendStr(searchPaths, options.libpath)
  if gProjectFull.len != 0:
    # current path is always looked first for modules
    prependStr(searchPaths, gProjectPath)
  setId(100)
  passes.gIncludeFile = includeModule
  passes.gImportModule = importModule
  case command.normalize
  of "c", "cc", "compile", "compiletoc":
    # compile means compileToC currently
    gCmd = cmdCompileToC
    wantMainModule()
    commandCompileToC()
  of "cpp", "compiletocpp":
    extccomp.cExt = ".cpp"
    gCmd = cmdCompileToCpp
    if cCompiler == ccGcc: setCC("gpp")
    wantMainModule()
    defineSymbol("cpp")
    commandCompileToC()
  of "objc", "compiletooc":
    extccomp.cExt = ".m"
    gCmd = cmdCompileToOC
    wantMainModule()
    defineSymbol("objc")
    commandCompileToC()
  of "run":
    gCmd = cmdRun
    wantMainModule()
    when hasTinyCBackend:
      extccomp.setCC("tcc")
      CommandCompileToC()
    else:
      rawMessage(errInvalidCommandX, command)
  of "js", "compiletojs":
    gCmd = cmdCompileToJS
    wantMainModule()
    commandCompileToJS()
  of "compiletollvm":
    gCmd = cmdCompileToLLVM
    wantMainModule()
    when hasLLVM_Backend:
      CommandCompileToLLVM()
    else:
      rawMessage(errInvalidCommandX, command)
  of "pretty":
    gCmd = cmdPretty
    wantMainModule()
    commandPretty()
  of "doc":
    gCmd = cmdDoc
    loadConfigs(DocConfig)
    wantMainModule()
    commandDoc()
  of "doc2":
    gCmd = cmdDoc
    loadConfigs(DocConfig)
    wantMainModule()
    defineSymbol("nimdoc")
    commandDoc2()
  of "rst2html":
    gCmd = cmdRst2html
    loadConfigs(DocConfig)
    wantMainModule()
    commandRst2Html()
  of "rst2tex":
    gCmd = cmdRst2tex
    loadConfigs(DocTexConfig)
    wantMainModule()
    commandRst2TeX()
  of "jsondoc":
    gCmd = cmdDoc
    loadConfigs(DocConfig)
    wantMainModule()
    defineSymbol("nimdoc")
    commandJSON()
  of "buildindex":
    gCmd = cmdDoc
    loadConfigs(DocConfig)
    commandBuildIndex()
  of "gendepend":
    gCmd = cmdGenDepend
    wantMainModule()
    commandGenDepend()
  of "dump":
    gCmd = cmdDump
    if getConfigVar("dump.format") == "json":
      requireMainModuleOption()

      var definedSymbols = newJArray()
      for s in definedSymbolNames(): definedSymbols.elems.add(%s)

      var libpaths = newJArray()
      for dir in iterSearchPath(searchPaths): libpaths.elems.add(%dir)

      var dumpdata = % [
        (key: "version", val: %VersionAsString),
        (key: "project_path", val: %gProjectFull),
        (key: "defined_symbols", val: definedSymbols),
        (key: "lib_paths", val: libpaths)
      ]

      outWriteln($dumpdata)
    else:
      outWriteln("-- list of currently defined symbols --")
      for s in definedSymbolNames(): outWriteln(s)
      outWriteln("-- end of list --")

      for it in iterSearchPath(searchPaths): msgWriteln(it)
  of "check":
    gCmd = cmdCheck
    wantMainModule()
    commandCheck()
  of "parse":
    gCmd = cmdParse
    wantMainModule()
    discard parseFile(gProjectMainIdx)
  of "scan":
    gCmd = cmdScan
    wantMainModule()
    commandScan()
    msgWriteln("Beware: Indentation tokens depend on the parser\'s state!")
  of "i":
    gCmd = cmdInteractive
    commandInteractive()
  of "e":
    # XXX: temporary command for easier testing
    commandEval(mainCommandArg())
  of "reset":
    resetMemory()
  of "idetools":
    gCmd = cmdIdeTools
    if gEvalExpr != "":
      commandEval(gEvalExpr)
    else:
      wantMainModule()
      commandSuggest()
  of "serve":
    isServing = true
    gGlobalOptions.incl(optCaasEnabled)
    msgs.gErrorMax = high(int)  # do not stop after first error
    serve(mainCommand)
  else:
    rawMessage(errInvalidCommandX, command)

  if (msgs.gErrorCounter == 0 and
      gCmd notin {cmdInterpret, cmdRun, cmdDump} and
      gVerbosity > 0):
    rawMessage(hintSuccessX, [$gLinesCompiled,
               formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
               formatSize(getTotalMem())])

  when PrintRopeCacheStats:
    echo "rope cache stats: "
    echo "  tries : ", gCacheTries
    echo "  misses: ", gCacheMisses
    echo "  int tries: ", gCacheIntTries
    echo "  efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float), ffDecimal, 3)

  when SimiluateCaasMemReset:
    resetMemory()