---
Summary: Notes on where context should and should not be
Updated: August 2016
git-hash: 7319521e44d149c565f6bed61c28b3a64421da8e
---
Context
=======
A context is a type oriented equivalent for a python namespace.

That is a python namespace maps names to values. A context maps names to value types.

It's yet to be decided if it is name to a value-type or value-type-set.
It's likely to be the latter with the constraint of either: the set being
a singleton, OR, the set being constructued solely of types that are
compatible. (That is subtyped to be the same. That's unclear at present
whether a good or bad idea - especially given all types are subclasses/
subtypes of class "object")

It's likely any enforcement here wants to actually be done at the analysis
stage.

Summary
-------
Analysed the current approach - what's in each node now, and what info
they need to determine their state - directly or child nodes. Child nodes
*could* be decorated by an external pass. On some levels, an external pass
handling this sounds like a good idea. It's certainly a good idea that
the external pass CAN do this.

However, logically a context is inherited by child nodes, and this sounds
like something that parent nodes can probably handle better than an
external agency.

As a result, the decision is this:

 - When a node is created, and given a context, that context will
   be inherited by child nodes.

 - Child nodes can refused to inherit the context if appropriate
   (a function definition may want to not have its context overridden,
   but that function definition may well want to have access to the
   parent context) 

 - As a result, each node will have a context.

 - Each node will have a function called "set_environment_context"

 - That set_environment_context can either override the node's context
   or be captured by the pynode

 - If the pynode's local context is NOT overridden by the parent's
   environment context then the pynode's context will have it's *parent*
   list updated to include the parent node (and maybe the parent node's
   context)

This should allow us to do the type analysis we've been wanting to do.

Furthermore, the following is probably going to happen (but not decided):

 - Objects will be defined to have a context
 - Classes will be defined to have a context, and a class's context would
   be an environment context for objects.
   
   How we use that is up for grabs at present but that's the core.

This is largely noted so we can bear this in mind as we work through how
contexts work in practice, rather than theory.

Current Approach
----------------

We only add a context to identifiers.

global_context = Context()
for node in depth_walk(self):
    if node.tag == "identifier":
        node.context = global_context
        print("NODE", node)

I think this approach is wrong. Seemed like a good idea at the time though.

What makes sense?
-----------------

Unclear, so worked through what would make sense for each sort of 
PyNode - each part of the grammar at present. Note that at present we
do not have a concept of global/local/non-local context, nor are objects
implemented. We know they're coming though so the following bears this
in mind even though it's not directly stated. 

In particular while a PyProgram talks about a global_context, other pynodes
like PyBlock and PyStatements talk about *a* context in recognition that
classes, objects and functions (including potentially nested ones) will
come into existance.

There's a logical issue in that nested functions do not exist in C++
(though a little bizarrely, nested classes/structs do... Which is
potentially useful in a python/C++ context).

STRUCTURAL PYNODES -- All inherit PyNode

  PyProgram --
    - Has the global context
    - Provides access to the global context to child nodes

  PyBlock --PyNode
    - Is a collection of statements
    - Does not create a new context.
    - Is provided with a context. (sits in a context)
    - Provides that context to child nodes

  PyStatements --PyNode
    - Is a collection of statements
    - Does not create a new context.
    - Is provided with a context. (sits in a context)
    - Provides that context to child nodes

  PyExprList --PyNode
    - Is a collection of expressions
    - Does not create a new context.
    - Is provided with a context. (sits in a context)
    - Provides that context to child nodes

STATEMENT PYNODES -- All inherit PyStatement(PyNode)

  PyAssignment
    - Updates an lvalue
    - Indicates a need to update a context
    - SHOULD have access to a context
    - DOESN'T have access to a context
    - Does not create a new context.

    - In fact, the context that the assignment sits in matters.
      Why?

      If a value is inside a scope or not affects whether you get/set

      in the current context or an enclosing context.

      Also, there is the issue of "global" or "nonlocal" keywordds.
      
      This really suggests that contexts do need to be accessed this low in the tree.

  PyExpressionStatement
    - Values may require access to a context to evaluate
    - Does not create a new context
    - Does not update a context

  PyFunctionCall
    - May require access to a context to evaluate
    - Does not create a new context
    - Does not update a context

  PyForLoop
    - Similar to assignment effectively
    - Has a block that requires a context
    - Has an lvalue that gets updated, implying access to a context
    - Indicates a need to update a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.

  PyWhileStatement
    - Has a block that requires a context
    - Has an expression that requires a context to evaluate
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.

  PyIfStatement
    - Has a block that requires a context
    - Has an expression that requires a context to evaluate
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.


  PyElIfClause
    - Has a block that requires a context
    - Has an expression that requires a context to evaluate
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.

  PyElseClause
    - Has a block that requires a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.

  PyEmptyStatement
    - Shouldn't need anything...
    - Symbolises blank lines.
    - Should be potentially filtered out better.
    - Feels like a kludge
  
  PyPassStatement
    - Shouldn't need anything...

  PyBreakStatement
    - Shouldn't need anything...

  PyContinueStatement
    - Shouldn't need anything...

  PyPrintStatement
    - Has a collection of expressions that require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Actual type of result does NOT depend on context
    
OPERATOR PYNODES -- All inherit PyOperation(PyNode)

  PyOperator(PyOperation):
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Type of result DOES depend on context

  PyBoolOperator(PyOperation):
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Type of result DOES NOT depend on context - Always TRUE/FALSE (Bool)

  PyComparisonOperator(PyOperation):
    NTS:: NOT CONVINCED THESE ARE ACTUALLY CORRECT
  
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Type of result may depend on context (though always eval to true/false)


BOOL OPERATORS -- All inherit PyBoolOperator(PyOperation(PyNode))

  PyAndOperator(PyBoolOperator): (Binary)
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - *Type* of result DOES NOT depend on context - Always TRUE/FALSE (Bool)

  PyOrOperator(PyAndOperator): (Binary)
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - *Type* of result DOES NOT depend on context - Always TRUE/FALSE (Bool)

  PyNotOperator(PyBoolOperator):   (Unary operator)
    - Takes 1 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Type of result DOES NOT depend on context - Always TRUE/FALSE (Bool)

OTHER OPERATORS -- All inherit PyOperator(PyOperation(PyNode))

  PyTimesOperator
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Type of result DOES depend on context

  PyDivideOperator
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Type of result DOES depend on context

  PyPowerOperator
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Type of result DOES depend on context

  PyPlusOperator
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Type of result DOES depend on context

  PyMinusOperator
    - Takes 2 expressions that each may require a context
    - SHOULD have access to a context
    - DOESN'T have access to a context (except indirectly)
    - Does not create a new context.
    - Type of result DOES depend on context

VALUE NODES - All inherit PyNode

class PyValueLiteral(PyNode):
     - Base class -- not all subclasses have need for context
     - In fact most don't need it
     - No need to be enforced here

class PyIdentifier(PyValueLiteral):
     - Represents a name for a value.
     - That value has a type
     - Requires access to a context to extract this
     - Does not necessarily update the context
     - Does not create a context
     - HOWEVER, identifier MAY be used during assignment which could mean
       a context MAY need updating here.

class PyAttributeAccess(PyNode):
     - Represents extraction of a value by name from a value
     - That extracted value has a type
     - Requires access to a context to extract this
     - Does not necessarily update the context
         - But could ?
         - Does this depend on context it's extracted *into* ?
         - Yes, could actually.
     - Does not create a context
          - Is that true?
          - not in the general case it's true.
          - Could cause update of the context *within* the value
     - Attribute access MAY be used during assignment

     - Attributes are essentially accessors to specific contexts that sit within objects.
     - Some of those attributes may be functions.
     - Some will be plain values.
     - But the object IS a context
        - But unlike Pascal NOT one that can be opened (via with) - but one that remains closed.

class PyAttribute(PyNode):
     - Represents a name for a value.
     - That value has a type
     - Requires access to a context to extract this - BUT that is handled by attribute access
     - So this does NOT update or any way impact into a context directly. Rather it is
     - used by something else to do that.
     - So at least in theory does not need access to a context.


   --- PyAttribute is used IN PyAttributeAccess to represent the name to extract or update



class PyString(PyValueLiteral):
     - As a literal has no need for context in anyway

class PyCharacter(PyValueLiteral):
     - As a literal has no need for context in anyway
class PyBoolean(PyValueLiteral):
     - As a literal has no need for context in anyway

class PyNumber(PyValueLiteral):
     - As a literal has no need for context in anyway
     - true for all subclasses

class PyFloat(PyNumber):
     - As a literal has no need for context in anyway
class PyInteger(PyNumber):
     - As a literal has no need for context in anyway
class PySignedLong(PyNumber):
     - As a literal has no need for context in anyway
class PyUnSignedLong(PyNumber):
     - As a literal has no need for context in anyway
class PyHex(PyNumber):
     - As a literal has no need for context in anyway
class PyOctal(PyNumber):
     - As a literal has no need for context in anyway
class PyBinary(PyNumber):
     - As a literal has no need for context in anyway










