-- Copyright (c) 1991-2002, The Numerical Algorithms Group Ltd.
-- All rights reserved.
-- Copyright (C) 2007-2011, Gabriel Dos Reis.
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are
-- met:
--
--     - Redistributions of source code must retain the above copyright
--       notice, this list of conditions and the following disclaimer.
--
--     - Redistributions in binary form must reproduce the above copyright
--       notice, this list of conditions and the following disclaimer in
--       the documentation and/or other materials provided with the
--       distribution.
--
--     - Neither the name of The Numerical Algorithms Group Ltd. nor the
--       names of its contributors may be used to endorse or promote products
--       derived from this software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import i_-util
namespace BOOT

++ true when the interpreter should evaluate forms to values, as
++ opposed to just generating code to compute values.
$genValue := true

++ true if we are about to generate a function definition
$definingMap := false

++ List variables locat to the current function.
$localVars := []

++ declared mode of the current entity being processed.
$declaredMode := nil

++
$useIntegerSubdomain := true

--% Functions on interpreter objects

-- Interpreter objects used to be called triples because they had the
-- structure [value, type, environment].  For many years, the environment
-- was not used, so finally in January, 1990, the structure of objects
-- was changed to be (type . value).  This was chosen because it was the
-- structure of objects of type Any.  Sometimes the values are wrapped
-- (see the function isWrapped to see what this means physically).
-- Wrapped values are not actual values belonging to their types.  An
-- unwrapped value must be evaluated to get an actual value.  A wrapped
-- value must be unwrapped before being passed to a library function.
-- Typically, an unwrapped value in the interpreter consists of LISP
-- code, e.g., parts of a function that is being constructed.
--                 RSS 1/14/90

-- These are the new structure functions.

objNew(val, mode) == [mode,:val]           -- new names as of 10/14/93
objNewWrap(val, mode) == [mode,:wrap val]
objNewCode(val, mode) == ["CONS", MKQ mode,val ]
objSetVal(obj,val) == obj.rest := val
objSetMode(obj,mode) == obj.first := mode

objVal obj == rest obj
objValUnwrap obj == unwrap rest obj
objMode obj == first obj
objEnv obj == $EmptyEnvironment

++ Return a newly constructed interpreter object, with fully evaluated
++ underlying value if in evaluation context.
object(v,m) ==
  $genValue => objNewWrap(timedEVALFUN v,m)
  objNew(v,m)

objCodeVal obj == third obj
objCodeMode obj == second obj

--% Utility Functions Used Only by the Intepreter
 
wrap x ==
  isWrapped x => x
  ["WRAPPED",:x]
 
isWrapped x ==
  x is ['WRAPPED,:.] or integer? x or float? x or string? x
 
unwrap x ==
  integer? x or float? x or string? x => x
  x is ["WRAPPED",:y] => y
  x
 
quote2Wrapped x ==
  x is ['QUOTE,y] => wrap y
  x
 
removeQuote x ==
  x is ['QUOTE,y] => y
  x
 
++ returns the normal form of `obj''s value, suitable for use as
++ argument to a (library) function call.
getValueNormalForm obj ==
  val := objVal obj
  val isnt [.,:.] => val
  [op,:argl] := val
  op is "WRAPPED" => MKQ argl
  ident? op and isConstructorName op => 
    isConceptualCategory objMode obj => instantiationNormalForm(op,argl)
    MKQ val
  -- This is not the final value of `obj', rather something that needs
  -- further evaluation, e.g. generated code to compute the value.
  val

instantiationNormalForm(op,argl) ==
  [op,:[normalVal for arg in argl]] where normalVal() ==
     arg isnt [.,:.] => arg
     [h,:t] := arg
     ident? h and isConstructorName h => instantiationNormalForm(h,t)
     MKQ arg


-- addQuote x ==
--   integer? x => x
--   quote x
 
--% Library compiler structures needed by the interpreter

-- Tuples and Crosses

asTupleNew(eltType,size,listOfElts) == 
  [size,:makeSimpleArrayFromList(eltType,listOfElts)]

asTupleNew0(eltType,listOfElts) == 
  [#listOfElts,:makeSimpleArrayFromList(eltType,listOfElts)]

asTupleNewCode(eltType, size, listOfElts) == 
  ["asTupleNew", quote getVMType eltType, size, ['%list, :listOfElts]]

asTupleNewCode0(eltType,listForm) == 
  ["asTupleNew0", quote getVMType eltType, listForm]

asTupleSize(at) == first at
asTupleAsVector(at) == rest at
asTupleAsList(at) == VEC2LIST asTupleAsVector at

--% Basic Object Type Identification

++ The VAT class for literal values.  Also used in the
++ algebra interface to the interpreter.
$immediateDataSymbol ==
  "--immediateData--"

++ If x is a literal of the basic types (Integer String DoubleFloat) then
++ this function returns its type, and nil otherwise.
macro getBasicMode x ==
  getBasicMode0(x,$useIntegerSubdomain)

++ Subroutine of getBasicMode.
getBasicMode0(x,useIntegerSubdomain) ==
  x is nil => $EmptyMode
  string? x => $String
  integer? x =>
    useIntegerSubdomain =>
      x > 0 => $PositiveInteger
      x = 0 => $NonNegativeInteger
      $Integer
    $Integer
  FLOATP x => $DoubleFloat
  (x='%noBranch) or (x='noValue) => $NoValueMode
  nil

++ If x is a literal of the basic types then returns
++ an interpreter object denoting x, and nil otherwise.
getBasicObject x ==
  integer?    x =>
    t :=
      not $useIntegerSubdomain => $Integer
      x > 0 => $PositiveInteger
      x = 0 => $NonNegativeInteger
      $Integer
    objNewWrap(x,t)
  string? x => objNewWrap(x,$String)
  FLOATP  x => objNewWrap(x,$DoubleFloat)
  nil


--%% Vectorized Attributed Trees

--% The interpreter translates parse forms into vats for analysis.
--% These contain a number of slots in each node for information.
--% The leaves are now all vectors, though the leaves for basic types
--% such as integers and strings used to just be the objects themselves.
--% The vectors for the leaves with such constants now have the value
--% of $immediateDataSymbol as their name. Their are undoubtably still
--% some functions that still check whether a leaf is a constant. Note
--% that if it is not a vector it is a subtree.

--% attributed tree nodes have the following form:
--% slot         description
--% ----         -----------------------------------------------------
--%  0           operation name or literal
--%  1           declared mode of variable
--%  2           computed value of subtree from this node
--%  3           modeset: list of single computed mode of subtree
--%  4           prop list for extra things


++ create a leaf VAT node.
mkAtreeNode x ==
  -- maker of attrib tree node
  v := newShell 5
  vectorRef(v,0) := x
  v

++ remove mode, value, and misc. info from attrib tree
emptyAtree expr ==
  vector? expr =>
    symbolEq?($immediateDataSymbol,vectorRef(expr,0)) => nil
    vectorRef(expr,1) := nil
    vectorRef(expr,2) := nil
    vectorRef(expr,3) := nil
    -- kill proplist too?
  expr isnt [.,:.] => nil
  for e in expr repeat
    emptyAtree e


++ returns true if x is a leaf VAT object.
isLeaf x == 
  x isnt [.,:.]     --may be a number or a vector

++ returns the mode of the VAT node x.
++ Also used by the algebra interface to the interpreter.
getMode x ==
  x is [op,:.] => getMode op
  vector? x => x.1
  m := getBasicMode x => m
  keyedSystemError("S2II0001",[x])

++ sets the mode for the VAT node x to y.
putMode(x,y) ==
  x is [op,:.] => putMode(op,y)
  not vector? x => keyedSystemError("S2II0001",[x])
  vectorRef(x,1) := y

++ returns an interpreter object that represents the value of node x.
++ Note that an interpreter object is a pair of mode and value.
++ Also used by the algebra interface to the interperter.
getValue x ==
  vector? x => vectorRef(x,2)
  x isnt [.,:.] =>
    t := getBasicObject x => t
    keyedSystemError("S2II0001",[x])
  getValue first x

++ sets the value of VAT node x to interpreter object y.
putValue(x,y) ==
  x is [op,:.] => putValue(op,y)
  not vector? x => keyedSystemError("S2II0001",[x])
  vectorRef(x,2) := y

++ same as putValue(vec, val), except that vec is returned instead of val.
putValueValue(vec,val) ==
  putValue(vec,val)
  vec

++ Returns the node class of x, if possible; otherwise nil.
++ Also used by the algebra interface to the interpreter.
getUnnameIfCan x ==
  vector? x => vectorRef(x,0)
  x is [op,:.] => getUnnameIfCan op
  x isnt [.,:.] => x
  nil

++ Returns the node class of x; otherwise raise an error.
getUnname x ==
  x is [op,:.] => getUnname op
  getUnname1 x

++ Subroutine of getUnname.
getUnname1 x ==
  vector? x => vectorRef(x,0)
  cons? x => keyedSystemError("S2II0001",[x])
  x

++ returns the mode-set of VAT node x.
getModeSet x ==
  x and cons? x => getModeSet first x
  vector? x =>
    y:= x.aModeSet =>
      (y = [$EmptyMode]) and ((m := getMode x) is ['Mapping,:.]) =>
        [m]
      y
    keyedSystemError("S2GE0016",['"getModeSet",'"no mode set"])
  m:= getBasicMode x => [m]
  cons? x => getModeSet first x
  keyedSystemError("S2GE0016",['"getModeSet",
    '"not an attributed tree"])

++ Sets the mode-set of VAT node x to y.
putModeSet(x,y) ==
  x is [op,:.] => putModeSet(op,y)
  not vector? x => keyedSystemError("S2II0001",[x])
  vectorRef(x,3) := y
  y

getModeOrFirstModeSetIfThere x ==
  x is [op,:.] => getModeOrFirstModeSetIfThere op
  vector? x =>
    m := vectorRef(x,1) => m
    val := vectorRef(x,2) => objMode val
    y := x.aModeSet =>
      (y = [$EmptyMode]) and ((m := getMode x) is ['Mapping,:.]) => m
      first y
    nil
  m := getBasicMode x => m
  nil

getModeSetUseSubdomain x ==
  cons? x => getModeSetUseSubdomain first x
  vector? x =>
    -- don't play subdomain games with retracted args
    getAtree(x,'retracted) => getModeSet x
    y := x.aModeSet =>
      (y = [$EmptyMode]) and ((m := getMode x) is ['Mapping,:.]) =>
        [m]
      val := getValue x
      (vectorRef(x,0) = $immediateDataSymbol) and (y = [$Integer]) =>
        val := objValUnwrap val
        m := getBasicMode0(val,true)
        vectorRef(x,2) := objNewWrap(val,m)
        x.aModeSet := [m]
        [m]
      null val => y
      isEqualOrSubDomain(objMode(val),$Integer) and
        integer?(f := objValUnwrap val) =>
          [getBasicMode0(f,true)]
      y
    keyedSystemError("S2GE0016",
      ['"getModeSetUseSubomain",'"no mode set"])
  m := getBasicMode0(x,true) => [m]
  cons? x => getModeSetUseSubdomain first x
  keyedSystemError("S2GE0016",
    ['"getModeSetUseSubomain",'"not an attributed tree"])


computedMode t ==
  getModeSet t is [m] => m
  keyedSystemError("S2GE0016",['"computedMode",'"non-singleton modeset"])

--% Other VAT properties

insertShortAlist(prop,val,al) ==
  pair := objectAssoc(prop,al) =>
    pair.rest := val
    al
  [[prop,:val],:al]

putAtree(x,prop,val) ==
  x is [op,:.] =>
    -- only willing to add property if op is a vector
    -- otherwise will be pushing to deeply into calling structure
    if vector? op then putAtree(op,prop,val)
    x
  not vector? x => x     -- just ignore it
  n := symbolTarget(prop,'((mode . 1) (value . 2) (modeSet . 3)))
    => vectorRef(x,n) := val
  vectorRef(x,4) := insertShortAlist(prop,val,x.4)
  x

getAtree(x,prop) ==
  x is [op,:.] =>
    -- only willing to get property if op is a vector
    -- otherwise will be pushing to deeply into calling structure
    vector? op => getAtree(op,prop)
    nil
  not vector? x => nil     -- just ignore it
  n := symbolTarget(prop,'((mode . 1) (value . 2) (modeSet . 3)))
    => vectorRef(x,n)
  symbolTarget(prop,vectorRef(x,4))

putTarget(x, targ) ==
  -- want to put nil modes perhaps to clear old target
  if targ = $EmptyMode then targ := nil
  putAtree(x,'target,targ)

getTarget(x) == 
  getAtree(x,'target)

--% Source and position information

-- In the following, src is a string containing an original input line,
-- line is the line number of the string within the source file,
-- and col is the index within src of the start of the form represented
-- by x. x is a VAT.

++ returns source position information for VAT node x.
getSrcPos(x) == 
  getAtree(x, 'srcAndPos)

++ sets the source location information for VAT node x.
putSrcPos(x, file, src, line, col) ==
  putAtree(x, 'srcAndPos, srcPosNew(file, src, line, col))

srcPosNew(file, src, line, col) == 
  vector [file, src, line, col]

++ returns the name of source file for source location `sp'.
srcPosFile(sp) ==
  sp ~= nil => vectorRef(sp,0)
  nil

++ returns the input source string for source location `sp'.
srcPosSource(sp) ==
  sp ~= nil => vectorRef(sp,1)
  nil

++ returns the line number for source location `sp'.
srcPosLine(sp) ==
  sp ~= nil => vectorRef(sp,2)
  nil

++ returns the column number for source location `sp'.
srcPosColumn(sp) ==
  sp ~= nil => vectorRef(sp,3)
  nil

srcPosDisplay(sp) ==
  null sp => nil
  s := strconc('"_"", srcPosFile sp, '"_", line ",
      toString srcPosLine sp, '": ")
  sayBrightly [s, srcPosSource sp]
  col  := srcPosColumn sp
  dots :=
      col = 0 => '""
      fillerSpaces(col, char ".")
  sayBrightly [fillerSpaces(#s, char " "), dots, '"^"]
  true


++ Returns the calling convention vector for an operation
++ represented by the VAT `t'.
getFlagArgsPos t ==
  vector? t => getAtree(t, 'flagArgsPos)
  t isnt [.,:.] => keyedSystemError("S2II0001",[t])
  getFlagArgsPos first t

--% Transfer of VAT properties.


transferPropsToNode(x,t) ==
  propList := getProplist(x,$env)
  symbolTarget('Led,propList) or symbolTarget('Nud,propList) => nil
  node :=
    vector? t => t
    first t
  for prop in '(mode localModemap value name generatedCode)
    repeat transfer(x,node,prop)
      where
        transfer(x,node,prop) ==
          u := get(x,prop,$env) => putAtree(node,prop,u)
          (not member(x,$localVars)) and (u := get(x,prop,$e)) =>
            putAtree(node,prop,u)
  if not getMode(t) and (am := get(x,'automode,$env)) then
    putModeSet(t,[am])
    putMode(t,am)
  t