summary refs log tree commit diff stats
path: root/compiler/sem.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/sem.nim')
-rwxr-xr-xcompiler/sem.nim6
1 files changed, 5 insertions, 1 deletions
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 8d0e4e168..8052d8b00 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -104,7 +104,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
 
 proc applyPatterns(c: PContext, n: PNode): PNode =
   # fast exit:
-  if c.patterns.len == 0: return n
+  if c.patterns.len == 0 or optPatterns notin gOptions: return n
   result = n
   # we apply the last pattern first, so that pattern overriding is possible;
   # however the resulting AST would better not trigger the old rule then
@@ -113,7 +113,11 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
     let x = applyRule(c, c.patterns[i], result)
     if not isNil(x):
       assert x.kind == nkCall
+      inc(evalTemplateCounter)
+      if evalTemplateCounter > 100:
+        GlobalError(n.info, errTemplateInstantiationTooNested)
       result = semExpr(c, x)
+      dec(evalTemplateCounter)
 
 include seminst, semcall
 
18 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
#
#
#            Nimrod's Runtime Library
#        (c) Copyright 2009 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module implements helper procs for CGI applictions. Example:
## 
## .. code-block:: Nimrod
##
##    import strtabs, cgi
##
##    # Fill the values when debugging:
##    when debug: 
##      setTestData("name", "Klaus", "password", "123456")
##    # read the data into `myData`
##    var myData = readData()
##    # check that the data's variable names are "name" or "passwort" 
##    validateData(myData, "name", "password")
##    # start generating content:
##    writeContentType()
##    # generate content:
##    write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
##    write(stdout, "<html><head><title>Test</title></head><body>\n")
##    writeln(stdout, "your name: " & myData["name"])
##    writeln(stdout, "your password: " & myData["password"])
##    writeln(stdout, "</body></html>")

import strutils, os, strtabs

proc URLencode*(s: string): string =
  ## Encodes a value to be HTTP safe: This means that characters in the set
  ## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result,
  ## a space is converted to ``'+'`` and every other character is encoded as
  ## ``'%xx'`` where ``xx`` denotes its hexadecimal value. 
  result = ""
  for i in 0..s.len-1:
    case s[i]
    of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
    of ' ': add(result, '+')
    else: 
      add(result, '%')
      add(result, toHex(ord(s[i]), 2))

proc handleHexChar(c: char, x: var int) {.inline.} = 
  case c
  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
  else: assert(false)

proc URLdecode*(s: string): string = 
  ## Decodes a value from its HTTP representation: This means that a ``'+'`` 
  ## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
  ## value) is converted to the character with ordinal number ``xx``, and  
  ## and every other character is carried over. 
  result = ""
  var i = 0
  while i < s.len:
    case s[i]
    of '%': 
      var x = 0
      handleHexChar(s[i+1], x)
      handleHexChar(s[i+2], x)
      inc(i, 2)
      add(result, chr(x))
    of '+': add(result, ' ')
    else: add(result, s[i])
    inc(i)

proc addXmlChar(dest: var string, c: Char) {.inline.} = 
  case c
  of '&': add(dest, "&amp;")
  of '<': add(dest, "&lt;")
  of '>': add(dest, "&gt;")
  of '\"': add(dest, "&quot;")
  else: add(dest, c)
  
proc XMLencode*(s: string): string = 
  ## Encodes a value to be XML safe:
  ## * ``"`` is replaced by ``&quot;``
  ## * ``<`` is replaced by ``&lt;``
  ## * ``>`` is replaced by ``&gt;``
  ## * ``&`` is replaced by ``&amp;``
  ## * every other character is carried over.
  result = ""
  for i in 0..len(s)-1: addXmlChar(result, s[i])

type
  ECgi* = object of EIO  ## the exception that is raised, if a CGI error occurs
  TRequestMethod* = enum ## the used request method
    methodNone,          ## no REQUEST_METHOD environment variable
    methodPost,          ## query uses the POST method
    methodGet            ## query uses the GET method

proc cgiError*(msg: string) {.noreturn.} = 
  ## raises an ECgi exception with message `msg`.
  var e: ref ECgi
  new(e)
  e.msg = msg
  raise e

proc getEncodedData(allowedMethods: set[TRequestMethod]): string = 
  case getenv("REQUEST_METHOD") 
  of "POST": 
    if methodPost notin allowedMethods: 
      cgiError("'REQUEST_METHOD' 'POST' is not supported")
    var L = parseInt(getenv("CONTENT_LENGTH"))
    result = newString(L)
    if readBuffer(stdin, addr(result[0]), L) != L:
      cgiError("cannot read from stdin")
  of "GET":
    if methodGet notin allowedMethods: 
      cgiError("'REQUEST_METHOD' 'GET' is not supported")
    result = getenv("QUERY_STRING")
  else: 
    if methodNone notin allowedMethods:
      cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")

iterator decodeData*(allowedMethods: set[TRequestMethod] = 
       {methodNone, methodPost, methodGet}): tuple[key, value: string] = 
  ## Reads and decodes CGI data and yields the (name, value) pairs the
  ## data consists of. If the client does not use a method listed in the
  ## `allowedMethods` set, an `ECgi` exception is raised.
  var enc = getEncodedData(allowedMethods)
  if not isNil(enc): 
    # decode everything in one pass:
    var i = 0
    var name = ""
    var value = ""
    while enc[i] != '\0':
      setLen(name, 0) # reuse memory
      while true:
        case enc[i]
        of '\0': break
        of '%': 
          var x = 0
          handleHexChar(enc[i+1], x)
          handleHexChar(enc[i+2], x)
          inc(i, 2)
          add(name, chr(x))
        of '+': add(name, ' ')
        of '=', '&': break
        else: add(name, enc[i])
        inc(i)
      if enc[i] != '=': cgiError("'=' expected")
      inc(i) # skip '='
      setLen(value, 0) # reuse memory
      while true:
        case enc[i]
        of '%': 
          var x = 0
          handleHexChar(enc[i+1], x)
          handleHexChar(enc[i+2], x)
          inc(i, 2)
          add(value, chr(x))
        of '+': add(value, ' ')
        of '&', '\0': break
        else: add(value, enc[i])
        inc(i)
      yield (name, value)
      if enc[i] == '&': inc(i)
      elif enc[i] == '\0': break
      else: cgiError("'&' expected")

proc readData*(allowedMethods: set[TRequestMethod] = 
               {methodNone, methodPost, methodGet}): PStringTable = 
  ## Read CGI data. If the client does not use a method listed in the
  ## `allowedMethods` set, an `ECgi` exception is raised.
  result = newStringTable()
  for name, value in decodeData(allowedMethods): 
    result[name] = value
  
proc validateData*(data: PStringTable, validKeys: openarray[string]) = 
  ## validates data; raises `ECgi` if this fails. This checks that each variable
  ## name of the CGI `data` occurs in the `validKeys` array.
  for key, val in pairs(data):
    if find(validKeys, key) < 0: 
      cgiError("unknown variable name: " & key)

proc getContentLength*(): string =
  ## returns contents of the ``CONTENT_LENGTH`` environment variable
  return getenv("CONTENT_LENGTH")

proc getContentType*(): string =
  ## returns contents of the ``CONTENT_TYPE`` environment variable
  return getenv("CONTENT_Type")

proc getDocumentRoot*(): string =
  ## returns contents of the ``DOCUMENT_ROOT`` environment variable
  return getenv("DOCUMENT_ROOT")

proc getGatewayInterface*(): string =
  ## returns contents of the ``GATEWAY_INTERFACE`` environment variable
  return getenv("GATEWAY_INTERFACE")

proc getHttpAccept*(): string =
  ## returns contents of the ``HTTP_ACCEPT`` environment variable
  return getenv("HTTP_ACCEPT")

proc getHttpAcceptCharset*(): string =
  ## returns contents of the ``HTTP_ACCEPT_CHARSET`` environment variable
  return getenv("HTTP_ACCEPT_CHARSET")

proc getHttpAcceptEncoding*(): string =
  ## returns contents of the ``HTTP_ACCEPT_ENCODING`` environment variable
  return getenv("HTTP_ACCEPT_ENCODING")

proc getHttpAcceptLanguage*(): string =
  ## returns contents of the ``HTTP_ACCEPT_LANGUAGE`` environment variable
  return getenv("HTTP_ACCEPT_LANGUAGE")

proc getHttpConnection*(): string =
  ## returns contents of the ``HTTP_CONNECTION`` environment variable
  return getenv("HTTP_CONNECTION")

proc getHttpCookie*(): string =
  ## returns contents of the ``HTTP_COOKIE`` environment variable
  return getenv("HTTP_COOKIE")

proc getHttpHost*(): string =
  ## returns contents of the ``HTTP_HOST`` environment variable
  return getenv("HTTP_HOST")

proc getHttpReferer*(): string =
  ## returns contents of the ``HTTP_REFERER`` environment variable
  return getenv("HTTP_REFERER")

proc getHttpUserAgent*(): string =
  ## returns contents of the ``HTTP_USER_AGENT`` environment variable
  return getenv("HTTP_USER_AGENT")

proc getPathInfo*(): string =
  ## returns contents of the ``PATH_INFO`` environment variable
  return getenv("PATH_INFO")

proc getPathTranslated*(): string =
  ## returns contents of the ``PATH_TRANSLATED`` environment variable
  return getenv("PATH_TRANSLATED")

proc getQueryString*(): string =
  ## returns contents of the ``QUERY_STRING`` environment variable
  return getenv("QUERY_STRING")

proc getRemoteAddr*(): string =
  ## returns contents of the ``REMOTE_ADDR`` environment variable
  return getenv("REMOTE_ADDR")

proc getRemoteHost*(): string =
  ## returns contents of the ``REMOTE_HOST`` environment variable
  return getenv("REMOTE_HOST")

proc getRemoteIdent*(): string =
  ## returns contents of the ``REMOTE_IDENT`` environment variable
  return getenv("REMOTE_IDENT")

proc getRemotePort*(): string =
  ## returns contents of the ``REMOTE_PORT`` environment variable
  return getenv("REMOTE_PORT")

proc getRemoteUser*(): string =
  ## returns contents of the ``REMOTE_USER`` environment variable
  return getenv("REMOTE_USER")

proc getRequestMethod*(): string =
  ## returns contents of the ``REQUEST_METHOD`` environment variable
  return getenv("REQUEST_METHOD")

proc getRequestURI*(): string =
  ## returns contents of the ``REQUEST_URI`` environment variable
  return getenv("REQUEST_URI")

proc getScriptFilename*(): string =
  ## returns contents of the ``SCRIPT_FILENAME`` environment variable
  return getenv("SCRIPT_FILENAME")

proc getScriptName*(): string =
  ## returns contents of the ``SCRIPT_NAME`` environment variable
  return getenv("SCRIPT_NAME")

proc getServerAddr*(): string =
  ## returns contents of the ``SERVER_ADDR`` environment variable
  return getenv("SERVER_ADDR")

proc getServerAdmin*(): string =
  ## returns contents of the ``SERVER_ADMIN`` environment variable
  return getenv("SERVER_ADMIN")

proc getServerName*(): string =
  ## returns contents of the ``SERVER_NAME`` environment variable
  return getenv("SERVER_NAME")

proc getServerPort*(): string =
  ## returns contents of the ``SERVER_PORT`` environment variable
  return getenv("SERVER_PORT")

proc getServerProtocol*(): string =
  ## returns contents of the ``SERVER_PROTOCOL`` environment variable
  return getenv("SERVER_PROTOCOL")

proc getServerSignature*(): string =
  ## returns contents of the ``SERVER_SIGNATURE`` environment variable
  return getenv("SERVER_SIGNATURE")

proc getServerSoftware*(): string =
  ## returns contents of the ``SERVER_SOFTWARE`` environment variable
  return getenv("SERVER_SOFTWARE")

proc setTestData*(keysvalues: openarray[string]) = 
  ## fills the appropriate environment variables to test your CGI application.
  ## This can only simulate the 'GET' request method. `keysvalues` should
  ## provide embedded (name, value)-pairs. Example:
  ##
  ## .. code-block:: Nimrod
  ##    setTestData("name", "Hanz", "password", "12345")
  putenv("REQUEST_METHOD", "GET")
  var i = 0
  var query = ""
  while i < keysvalues.len:
    add(query, URLencode(keysvalues[i]))
    add(query, '=')
    add(query, URLencode(keysvalues[i+1]))
    add(query, '&')
    inc(i, 2)
  putenv("QUERY_STRING", query)

proc writeContentType*() = 
  ## call this before starting to send your HTML data to `stdout`. This
  ## implements this part of the CGI protocol: 
  ##
  ## .. code-block:: Nimrod
  ##     write(stdout, "Content-type: text/html\n\n")
  ## 
  ## It also modifies the debug stack traces so that they contain
  ## ``<br />`` and are easily readable in a browser.  
  write(stdout, "Content-type: text/html\n\n")
  system.stackTraceNewLine = "<br />\n"