; a simple line editor for reading lisp expressions. ; colors strings and comments. nested parens get different colors. ; ; needs to do its own raw keyboard/screen management since we need to decide ; how to color each key right as it is printed. ; lots of logic devoted to handling backspace correctly. ; keyboard screen abort continuation -> string (function read-expression [ (default-space:space-address <- new space:literal 60:literal) (k:keyboard-address <- next-input) (screen:terminal-address <- next-input) (abort:continuation <- next-input) (history:buffer-address <- next-input) ; buffer of strings (history-length:integer <- get history:buffer-address/deref length:offset) (current-history-index:integer <- copy history-length:integer) (result:buffer-address <- init-buffer 10:literal) ; string to maybe add to (open-parens:integer <- copy 0:literal) ; for balancing parens and tracking nesting depth ; we can change color when backspacing over parens or comments or strings, ; but we need to know that they aren't escaped (escapes:buffer-address <- init-buffer 5:literal) ; to not return after just a comment (not-empty?:boolean <- copy nil:literal) { begin ; repeatedly read keys from the keyboard ; test: 34 (done?:boolean <- process-key default-space:space-address k:keyboard-address screen:terminal-address) (loop-unless done?:boolean) } ; trim trailing newline in result (easier history management below) { begin (l:character <- last result:buffer-address) (trailing-newline?:boolean <- equal l:character ((#\newline literal))) (break-unless trailing-newline?:boolean) (len:integer-address <- get-address result:buffer-address/deref length:offset) (len:integer-address/deref <- subtract len:integer-address/deref 1:literal) } ; test: 3 => size of s is 2 (s:string-address <- to-array result:buffer-address) (reply s:string-address) ]) (function process-key [ ; return t to signal end of expression (default-space:space-address <- new space:literal 60:literal) (0:space-address/names:read-expression <- next-input) (k:keyboard-address <- next-input) (screen:terminal-address <- next-input) (c:character <- wait-for-key k:keyboard-address silent:literal/terminal) (len:integer-address <- get-address result:buffer-address/space:1/deref length:offset) (maybe-cancel-this-expression c:character abort:continuation/space:1) ; check for ctrl-d and exit { begin (eof?:boolean <- equal c:character ((ctrl-d literal))) (break-unless eof?:boolean) ; return empty expression (s:string-address-address <- get-address result:buffer-address/space:1/deref data:offset) (s:string-address-address/deref <- copy nil:literal) (reply t:literal) } ; check for backspace ; test: 34 ; todo: backspace past newline { begin (backspace?:boolean <- equal c:character ((#\backspace literal))) (break-unless backspace?:boolean) (print-character screen:terminal-address c:character/backspace) { begin ; delete last character if any (zero?:boolean <- lesser-or-equal len:integer-address/deref 0:literal) (break-if zero?:boolean) (len:integer-address/deref <- subtract len:integer-address/deref 1:literal) ; switch colors ; test: "a"bc" ; test: "a\"bc" { begin (backspaced-over-close-quote?:boolean <- backspaced-over-unescaped? result:buffer-address/space:1 ((#\" literal)) escapes:buffer-address/space:1) ; " (break-unless backspaced-over-close-quote?:boolean) (slurp-string result:buffer-address/space:1 escapes:buffer-address/space:1 abort:continuation/space:1 k:keyboard-address screen:terminal-address) (reply nil:literal) } ; test: (+ 1 (2) ; test: (+ 1 #\(2) { begin (backspaced-over-open-paren?:boolean <- backspaced-over-unescaped? result:buffer-address/space:1 ((#\( literal)) escapes:buffer-address/space:1) (break-unless backspaced-over-open-paren?:boolean) (open-parens:integer/space:1 <- subtract open-parens:integer/space:1 1:literal) (reply nil:literal) } ; test: (+ 1 2) 3) ; test: (+ 1 2#\) 3) { begin (backspaced-over-close-paren?:boolean <- backspaced-over-unescaped? result:buffer-address/space:1 ((#\) literal)) escapes:buffer-address/space:1) (break-unless backspaced-over-close-paren?:boolean) (open-parens:integer/space:1 <- add open-parens:integer/space:1 1:literal) (reply nil:literal) } } (reply nil:literal) } ; up arrow; switch to previous item in history { begin (up-arrow?:boolean <- equal c:character ((up literal))) (break-unless up-arrow?:boolean) ; if history exists ; test: up without history has no effect { begin (empty-history?:boolean <- lesser-or-equal history-length:integer/space:1 0:literal) (break-unless empty-history?:boolean) (reply nil:literal) } ; if pointer not already at start of history ; test: 34 up past history has no effect { begin (at-history-start?:boolean <- lesser-or-equal current-history-index:integer/space:1 0:literal) (break-unless at-history-start?:boolean) (reply nil:literal) } ; then update history index, copy into current buffer ; test: 34 up restores previous command ; test todo: 342334 up doesn't mess up typing on current line ; test todo: 345 commands don't modify history ; test todo: multi-line expressions ; identify the history item (current-history-index:integer/space:1 <- subtract current-history-index:integer/space:1 1:literal) (switch-to-history 0:space-address screen:terminal-address) ; is trimmed in the history expression, so wait for the human to ; hit again or backspace to make edits (reply nil:literal) } ; down arrow; switch to next item in history { begin (down-arrow?:boolean <- equal c:character ((down literal))) (break-unless down-arrow?:boolean) ; if history exists ; test: down without history has no effect { begin (empty-history?:boolean <- lesser-or-equal history-length:integer/space:1 0:literal) (break-unless empty-history?:boolean) (reply nil:literal) } ; if pointer not already at end of history ; test: 34 up past history has no effect { begin (x:integer <- subtract history-length:integer/space:1 1:literal) (before-history-end?:boolean <- greater-or-equal current-history-index:integer/space:1 x:integer) (break-unless before-history-end?:boolean) (reply nil:literal) } ; then update history index, copy into current buffer ; test: 34 up restores previous command ; test todo: 342334 up doesn't mess up typing on current line ; test todo: 345 commands don't modify history ; test todo: multi-line expressions ; identify the history item (current-history-index:integer/space:1 <- add current-history-index:integer/space:1 1:literal) (switch-to-history 0:space-address screen:terminal-address) ; is trimmed in the history expression, so wait for the human to ; hit again or backspace to make edits (reply nil:literal) } ; if it's a newline, decide whether to return ; test:
#
#
#           The Nim Compiler
#        (c) Copyright 2014 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# This module handles the conditional symbols.

import 
  strtabs, platform, strutils, idents

# We need to use a PStringTable here as defined symbols are always guaranteed
# to be style insensitive. Otherwise hell would break lose.
var gSymbols: StringTableRef

proc defineSymbol*(symbol: string) = 
  gSymbols[symbol] = "true"

proc declareSymbol*(symbol: string) = 
  gSymbols[symbol] = "unknown"

proc undefSymbol*(symbol: string) = 
  gSymbols[symbol] = "false"

proc isDefined*(symbol: string): bool = 
  if gSymbols.hasKey(symbol):
    result = gSymbols[symbol] == "true"
  
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
proc isDeclared*(symbol: PIdent): bool = gSymbols.hasKey(symbol.s)

iterator definedSymbolNames*: string =
  for key, val in pairs(gSymbols):
    if val == "true": yield key

proc countDefinedSymbols*(): int = 
  result = 0
  for key, val in pairs(gSymbols):
    if val == "true": inc(result)

# For ease of bootstrapping, we keep them here and not in the global config
# file for now:
const
  additionalSymbols = """
    x86 itanium x8664
    msdos mswindows win32 unix posix sunos bsd macintosh RISCOS hpux
    mac

    hppa hp9000 hp9000s300 hp9000s700 hp9000s800 hp9000s820 ELATE sparcv9

    ecmascript js nimrodvm nimffi nimdoc cpp objc
    gcc llvmgcc clang lcc bcc dmc wcc vcc tcc pcc ucc icl
    boehmgc gcmarkandsweep gcgenerational nogc gcUseBitvectors
    endb profiler
    executable guiapp consoleapp library dll staticlib

    quick
    release debug
    useWinAnsi useFork useNimRtl useMalloc useRealtimeGC ssl memProfiler
    nodejs kwin nimfix

    usesysassert usegcassert tinyC useFFI
    useStdoutAsStdmsg createNimRtl
    booting fulldebug corruption nimsuperops noSignalHandler useGnuReadline
    noCaas noDocGen noBusyWaiting nativeStackTrace useNodeIds selftest
    reportMissedDeadlines avoidTimeMachine useClone ignoreAllocationSize
    debugExecProcesses pcreDll useLipzipSrc
    preventDeadlocks UNICODE winUnicode trackGcHeaders posixRealtime

    nimStdSetjmp nimRawSetjmp nimSigSetjmp
  """.split

proc initDefines*() = 
  gSymbols = newStringTable(modeStyleInsensitive)
  defineSymbol("nimrod") # 'nimrod' is always defined
  # for bootstrapping purposes and old code:
  defineSymbol("nimhygiene")
  defineSymbol("niminheritable")