# # # Nim's Runtime Library # (c) Copyright 2010 Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. # import strutils ## This module implements XML DOM Level 2 Core ## specification (http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html) #http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html #Exceptions type EDOMException* = object of ValueError ## Base exception object for all DOM Exceptions EDOMStringSizeErr* = object of EDOMException ## If the specified range of text does not fit into a DOMString ## Currently not used(Since DOMString is just string) EHierarchyRequestErr* = object of EDOMException ## If any node is inserted somewhere it doesn't belong EIndexSizeErr* = object of EDOMException ## If index or size is negative, or greater than the allowed value EInuseAttributeErr* = object of EDOMException ## If an attempt is made to add an attribute that is already in use elsewhere EInvalidAccessErr* = object of EDOMException ## If a parameter or an operation is not supported by the underlying object. EInvalidCharacterErr* = object of EDOMException ## This exception is raised when a string parameter contains an illegal character EInvalidModificationErr* = object of EDOMException ## If an attempt is made to modify the type of the underlying object. EInvalidStateErr* = object of EDOMException ## If an attempt is made to use an object that is not, or is no longer, usable. ENamespaceErr* = object of EDOMException ## If an attempt is made to create or change an object in a way which is incorrect with regard to namespaces. ENotFoundErr* = object of EDOMException ## If an attempt is made to reference a node in a context where it does not exist ENotSupportedErr* = object of EDOMException ## If the implementation does not support the requested type of object or operation. ENoDataAllowedErr* = object of EDOMException ## If data is specified for a node which does not support data ENoModificationAllowedErr* = object of EDOMException ## If an attempt is made to modify an object where modifications are not allowed ESyntaxErr* = object of EDOMException ## If an invalid or illegal string is specified. EWrongDocumentErr* = object of EDOMException ## If a node is used in a different document than the one that created it (that doesn't support it) const ElementNode* = 1 AttributeNode* = 2 TextNode* = 3 CDataSectionNode* = 4 ProcessingInstructionNode* = 7 CommentNode* = 8 DocumentNode* = 9 DocumentFragmentNode* = 11 # Nodes which are childless - Not sure about AttributeNode childlessObjects = {DocumentNode, AttributeNode, TextNode, CDataSectionNode, ProcessingInstructionNode, CommentNode} # Illegal characters illegalChars = {'>', '<', '&', '"'} # standard xml: attribute names # see https://www.w3.org/XML/1998/namespace stdattrnames = ["lang", "space", "base", "id"] type Feature = tuple[name: string, version: string] PDOMImplementation* = ref DOMImplementation DOMImplementation = object features: seq[Feature] # Read-Only PNode* = ref Node Node = object of RootObj attributes*: seq[PAttr] childNodes*: seq[PNode] fLocalName: string # Read-only fNamespaceURI: string # Read-only fNodeName: string # Read-only nodeValue*: string fNodeType: int # Read-only fOwnerDocument: PDocument # Read-Only fParentNode: PNode # Read-Only prefix*: string # Setting this should change some values... TODO! PElement* = ref Element Element = object of Node fTagName: string # Read-only PCharacterData* = ref CharacterData CharacterData = object of Node data*: string PDocument* = ref Document Document = object of Node fImplementation: PDOMImplementation # Read-only fDocumentElement: PElement # Read-only PAttr* = ref Attr Attr = object of Node fName: string # Read-only fSpecified: bool # Read-only value*: string fOwnerElement: PElement # Read-only PDocumentFragment* = ref DocumentFragment DocumentFragment = object of Node PText* = ref Text Text = object of CharacterData PComment* = ref Comment Comment = object of CharacterData PCDataSection* = ref CDataSection CDataSection = object of Text PProcessingInstruction* = ref ProcessingInstruction ProcessingInstruction = object of Node data*: string fTarget: string # Read-only # DOMImplementation proc getDOM*(): PDOMImplementation = ## Returns a DOMImplementation new(result) result.features = @[(name: "core", version: "2.0"), (name: "core", version: "1.0"), (name: "XML", version: "2.0")] proc createDocument*(dom: PDOMImplementation, namespaceURI: string, qualifiedName: string): PDocument = ## Creates an XML Document object of the specified type with its document element. var doc: PDocument new(doc) doc.fNamespaceURI = namespaceURI doc.fImplementation = dom var elTag: PElement new(elTag) elTag.fTagName = qualifiedName elTag.fNodeName = qualifiedName doc.fDocumentElement = elTag doc.fNodeType = DocumentNode return doc proc createDocument*(dom: PDOMImplementation, n: PElement): PDocument = ## Creates an XML Document object of the specified type with its document element. # This procedure is not in the specification, it's provided for the parser. var doc: PDocument new(doc) doc.fDocumentElement = n doc.fImplementation = dom doc.fNodeType = DocumentNode return doc proc hasFeature*(dom: PDOMImplementation, feature: string, version: string = ""): bool = ## Returns ``true`` if this ``version`` of the DomImplementation implements ``feature``, otherwise ``false`` for iName, iVersion in items(dom.features): if iName == feature: if version == "": return true else: if iVersion == version: return true return false # Document # Attributes proc implementation*(doc: PDocument): PDOMImplementation = return doc.fImplementation proc documentElement*(doc: PDocument): PElement = return doc.fDocumentElement # Internal procedures proc findNodes(nl: PNode, name: string): seq[PNode] = # Made for getElementsByTagName var r: seq[PNode] = @[] if isNil(nl.childNodes): return @[] if nl.childNodes.len() == 0: return @[] for i in items(nl.childNodes): if i.fNodeType == ElementNode: if i.fNodeName == name or name == "*": r.add(i) if not isNil(i.childNodes): if i.childNodes.len() != 0: r.add(findNodes(i, name)) return r proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode] = # Made for getElementsByTagNameNS var r: seq[PNode] = @[] if isNil(nl.childNodes): return @[] if nl.childNodes.len() == 0: return @[] for i in items(nl.childNodes): if i.fNodeType == ElementNode: if (i.fNamespaceURI == namespaceURI or namespaceURI == "*") and (i.fLocalName == localName or localName == "*
#
#
#           The Nimrod Compiler
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# This module implements the symbol importing mechanism.

import 
  intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
  semdata, passes

proc evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(c: PContext, n: PNode): PNode
proc importAllSymbols*(c: PContext, fromMod: PSym)

proc getModuleName*(n: PNode): string =
  # This returns a short relative module name without the nim extension
  # e.g. like "system", "importer" or "somepath/module"
  # The proc won't perform any checks that the path is actually valid
  case n.kind
  of nkStrLit, nkRStrLit, nkTripleStrLit:
    result = UnixToNativePath(n.strVal)
  of nkIdent:
    result = n.ident.s
  of nkSym:
    result = n.sym.name.s
  else:
    internalError(n.info, "getModuleName")
    result = ""

proc checkModuleName*(n: PNode): string =
  # This returns the full canonical path for a given module import
  var modulename = n.getModuleName
  result = findModule(modulename)
  if result.len == 0:
    Fatal(n.info, errCannotOpenFile, modulename)

proc rawImportSymbol(c: PContext, s: PSym) = 
  # This does not handle stubs, because otherwise loading on demand would be
  # pointless in practice. So importing stubs is fine here!
  var copy = s # do not copy symbols when importing!
  # check if we have already a symbol of the same name:
  var check = StrTableGet(c.tab.stack[importTablePos], s.name)
  if (check != nil) and (check.id != copy.id): 
    if not (s.kind in OverloadableSyms): 
      # s and check need to be qualified:
      Incl(c.AmbiguousSymbols, copy.id)
      Incl(c.AmbiguousSymbols, check.id)
  StrTableAdd(c.tab.stack[importTablePos], copy)
  if s.kind == skType: 
    var etyp = s.typ
    if etyp.kind in {tyBool, tyEnum}: 
      for j in countup(0, sonsLen(etyp.n) - 1): 
        var e = etyp.n.sons[j].sym
        if (e.Kind != skEnumField): 
          InternalError(s.info, "rawImportSymbol") 
          # BUGFIX: because of aliases for enums the symbol may already
          # have been put into the symbol table
          # BUGFIX: but only iff they are the same symbols!
        var it: TIdentIter 
        check = InitIdentIter(it, c.tab.stack[importTablePos], e.name)
        while check != nil: 
          if check.id == e.id: 
            e = nil
            break 
          check = NextIdentIter(it, c.tab.stack[importTablePos])
        if e != nil: 
          rawImportSymbol(c, e)
  elif s.kind == skConverter: 
    addConverter(c, s)        # rodgen assures that converters are no stubs
  
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = 
  let ident = lookups.considerAcc(n)
  let s = StrTableGet(fromMod.tab, ident)
  if s == nil:
    LocalError(n.info, errUndeclaredIdentifier, ident.s)
  else:
    if s.kind == skStub: loadStub(s)
    if s.Kind notin ExportableSymKinds:
      InternalError(n.info, "importSymbol: 2")
    # for an enumeration we have to add all identifiers
    case s.Kind
    of skProc, skMethod, skIterator, skMacro, skTemplate, skConverter:
      # for a overloadable syms add all overloaded routines
      var it: TIdentIter
      var e = InitIdentIter(it, fromMod.tab, s.name)
      while e != nil:
        if e.name.id != s.Name.id: InternalError(n.info, "importSymbol: 3")
        rawImportSymbol(c, e)
        e = NextIdentIter(it, fromMod.tab)
    else: rawImportSymbol(c, s)
  
proc importAllSymbols(c: PContext, fromMod: PSym) = 
  var i: TTabIter
  var s = InitTabIter(i, fromMod.tab)
  while s != nil: 
    if s.kind != skModule: 
      if s.kind != skEnumField: 
        if not (s.Kind in ExportableSymKinds): 
          InternalError(s.info, "importAllSymbols: " & $s.kind)
        rawImportSymbol(c, s) # this is correct!
    s = NextIter(