summary refs log blame commit diff stats
path: root/compiler/syntaxes.nim
blob: 1124e6444bf68c8165773336570cf16af1050863 (plain) (tree)
1
2
3
4
5
6
7
8
9


                               
                                         




                                                   
                                                       

       

                                                                          
























                                                                              
 















                                           
                                      


                                       
                                         
                          
                                                   



                                                 
                                               


                                                
                                         
                          
                                                           

                                
                                                                 




                                                
                                          
                 



                                                                  
                        
                                        
               

                                     
                          

                                       
           
                                           

                                         
                    
                                                            
                                 















                                                         
                                                          





                                                 



                                                              











                                              
                                   
                          
                                 
 

                                                           
                
                               












                                                                              
                  
                       
                                             


                                                              
                                         
                                            

                                     
                              
#
#
#           The Nimrod Compiler
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## Implements the dispatcher for the different parsers.

import 
  strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser, 
  pbraces, filters, filter_tmpl, renderer

type 
  TFilterKind* = enum 
    filtNone, filtTemplate, filtReplace, filtStrip
  TParserKind* = enum 
    skinStandard, skinBraces, skinEndX

const 
  parserNames*: array[TParserKind, string] = ["standard", "braces", "endx"]
  filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", 
    "strip"]

type 
  TParsers*{.final.} = object 
    skin*: TParserKind
    parser*: TParser


proc ParseFile*(filename: string): PNode{.procvar.}
proc openParsers*(p: var TParsers, filename: string, inputstream: PLLStream)
proc closeParsers*(p: var TParsers)
proc parseAll*(p: var TParsers): PNode
proc parseTopLevelStmt*(p: var TParsers): PNode
  # implements an iterator. Returns the next top-level statement or nil if end
  # of stream.

# implementation

proc ParseFile(filename: string): PNode = 
  var 
    p: TParsers
    f: tfile
  if not open(f, filename): 
    rawMessage(errCannotOpenFile, filename)
    return 
  OpenParsers(p, filename, LLStreamOpen(f))
  result = ParseAll(p)
  CloseParsers(p)

proc parseAll(p: var TParsers): PNode = 
  case p.skin
  of skinStandard: 
    result = parser.parseAll(p.parser)
  of skinBraces: 
    result = pbraces.parseAll(p.parser)
  of skinEndX: 
    InternalError("parser to implement") 
    result = ast.emptyNode
    # skinEndX: result := pendx.parseAll(p.parser);
  
proc parseTopLevelStmt(p: var TParsers): PNode = 
  case p.skin
  of skinStandard: 
    result = parser.parseTopLevelStmt(p.parser)
  of skinBraces: 
    result = pbraces.parseTopLevelStmt(p.parser)
  of skinEndX: 
    InternalError("parser to implement") 
    result = ast.emptyNode
    #skinEndX: result := pendx.parseTopLevelStmt(p.parser);
  
proc UTF8_BOM(s: string): int = 
  if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'): 
    result = 3
  else: 
    result = 0
  
proc containsShebang(s: string, i: int): bool = 
  if (s[i] == '#') and (s[i + 1] == '!'): 
    var j = i + 2
    while s[j] in WhiteSpace: inc(j)
    result = s[j] == '/'

proc parsePipe(filename: string, inputStream: PLLStream): PNode = 
  result = ast.emptyNode
  var s = LLStreamOpen(filename, fmRead)
  if s != nil: 
    var line = newStringOfCap(80)
    discard LLStreamReadLine(s, line)
    var i = UTF8_Bom(line)
    if containsShebang(line, i):
      discard LLStreamReadLine(s, line)
      i = 0
    if line[i] == '#' and line[i+1] == '!':
      inc(i, 2)
      while line[i] in WhiteSpace: inc(i)
      var q: TParser
      OpenParser(q, filename, LLStreamOpen(substr(line, i)))
      result = parser.parseAll(q)
      CloseParser(q)
    LLStreamClose(s)

proc getFilter(ident: PIdent): TFilterKind = 
  for i in countup(low(TFilterKind), high(TFilterKind)): 
    if IdentEq(ident, filterNames[i]): 
      return i
  result = filtNone

proc getParser(ident: PIdent): TParserKind = 
  for i in countup(low(TParserKind), high(TParserKind)): 
    if IdentEq(ident, parserNames[i]): 
      return i
  rawMessage(errInvalidDirectiveX, ident.s)

proc getCallee(n: PNode): PIdent = 
  if n.kind in nkCallKinds and n.sons[0].kind == nkIdent: 
    result = n.sons[0].ident
  elif n.kind == nkIdent: 
    result = n.ident
  else: 
    rawMessage(errXNotAllowedHere, renderTree(n))
  
proc applyFilter(p: var TParsers, n: PNode, filename: string, 
                 stdin: PLLStream): PLLStream = 
  var ident = getCallee(n)
  var f = getFilter(ident)
  case f
  of filtNone: 
    p.skin = getParser(ident)
    result = stdin
  of filtTemplate: 
    result = filterTmpl(stdin, filename, n)
  of filtStrip: 
    result = filterStrip(stdin, filename, n)
  of filtReplace: 
    result = filterReplace(stdin, filename, n)
  if f != filtNone: 
    if gVerbosity >= 2: 
      rawMessage(hintCodeBegin, [])
      MsgWriteln(result.s)
      rawMessage(hintCodeEnd, [])

proc evalPipe(p: var TParsers, n: PNode, filename: string, 
              start: PLLStream): PLLStream = 
  result = start
  if n.kind == nkEmpty: return 
  if (n.kind == nkInfix) and (n.sons[0].kind == nkIdent) and
      IdentEq(n.sons[0].ident, "|"): 
    for i in countup(1, 2): 
      if n.sons[i].kind == nkInfix: 
        result = evalPipe(p, n.sons[i], filename, result)
      else: 
        result = applyFilter(p, n.sons[i], filename, result)
  elif n.kind == nkStmtList: 
    result = evalPipe(p, n.sons[0], filename, result)
  else: 
    result = applyFilter(p, n, filename, result)
  
proc openParsers(p: var TParsers, filename: string, inputstream: PLLStream) = 
  var s: PLLStream
  p.skin = skinStandard
  var pipe = parsePipe(filename, inputStream)
  if pipe != nil: s = evalPipe(p, pipe, filename, inputStream)
  else: s = inputStream
  case p.skin
  of skinStandard, skinBraces, skinEndX: 
    parser.openParser(p.parser, filename, s)
  
proc closeParsers(p: var TParsers) = 
  parser.closeParser(p.parser)