-- 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 postpar
namespace BOOT

module parse

--% Transformation of Parser Output

++ If non nil, holds the operator being being defined.
$defOp := nil

++ When true, means that we are building a compile time value.  For
++ the parse tree transformer, this means that some assumtpions
++ are made about certain operators, regardless of their types
++ and semantics.  For example, in `a and b => x' the guard `a and b'
++ is assumed to have the standard semantics of (short-circuted)
++ conjunction of two Boolean expressions.
++ That usage is being phased out though.
$normalizeTree := false

++ True if we know we are parsing a form supposed to designate a type.
$parsingType := false

--%

washOperatorName x ==
  string? x =>
    stackWarning('"String syntax for %1b in signature is deprecated.",[x])
    makeSymbol x
  x

parseTransform: %ParseForm -> %Form 
parseTransform x ==
  $defOp: local:= nil
  parseTran substitute('$,'%,x) -- for new compiler compatibility

parseTran: %ParseForm -> %Form
parseTran x ==
  x isnt [.,:.] => x
  [op,:argl]:= x
  u := g(op) where g op == (op is ["elt",op,x] => g x; op)
  u="construct" =>
    r:= parseConstruct ["construct",:argl]
    op is ["elt",:.] => [parseTran op,:rest r]
    r
  symbol? u and (fn:= property(u,'parseTran)) => FUNCALL(fn,x)
  [parseTran op,:parseTranList argl]

parseType t ==
  $parsingType: local := true
  parseTran t

parseTypeList l ==
  l = nil => nil
  [parseType first l, :parseTypeList rest l]

parseTranList: %List %Form -> %List %Form
parseTranList l ==
  l isnt [.,:.] => parseTran l
  [parseTran first l,:parseTranList rest l]
 
parseConstruct: %ParseForm -> %Form
parseConstruct u ==
  $insideConstructIfTrue: local:= true
  [first u,:parseTranList rest u]

parseIs: %ParseForm -> %Form 
parseIs t == 
  t isnt ["is",a,b] => systemErrorHere ["parseIs",t]
  ["is",parseTran a,transIs parseTran b]
 
parseIsnt: %ParseForm -> %Form
parseIsnt t == 
  t isnt ["isnt",a,b] => systemErrorHere ["parseIsnt",t]
  ["isnt",parseTran a,transIs parseTran b]
 

transIs: %ParseForm -> %Form
transIs u ==
  isListConstructor u => ["construct",:transIs1 u]
  u

isListConstructor: %ParseForm -> %Boolean 
isListConstructor u == 
  u is [op,:.] and (not null (op in '(construct append cons)))

transIs1: %ParseForm -> %Form 
transIs1 u ==
  u is ["construct",:l] => [transIs x for x in l]
  u is ["append",x,y] =>
    h:= [":",transIs x]
    (v:= transIs1 y) is [":",z] => [h,z]
    v="nil" => second h
    v isnt [.,:.] => [h,[":",v]]
    [h,:v]
  u is ["cons",x,y] =>
    h:= transIs x
    (v:= transIs1 y) is [":",z] => [h,z]
    v="nil" => [h]
    v isnt [.,:.] => [h,[":",v]]
    [h,:v]
  u
 
parseAssign: %ParseForm -> %Form
parseAssign t ==
  t isnt [":=",x,y] => systemErrorHere ["parseAssign",t]
  p := [":=",parseTran x,parseTranCheckForRecord(y,opOf x)]
  opOf x = "cons" => [":=",transIs p.1,p.2]
  p
 

parseColon: %ParseForm -> %Form 
parseColon u ==
  u isnt [":",:.] => systemErrorHere ["parseColon",u]
  u is [":",x] => [":",parseTran x]
  u is [":",x,typ] => [":",parseTran x,parseType typ]
  u

-- ??? This parser is unused at the moment.
parseBigelt: %ParseForm -> %Form
parseBigelt t ==
  t isnt [.,typ,consForm] => systemErrorHere ["parseBigelt",t]
  [["elt",typ,"makeRecord"],:transUnCons consForm]

transUnCons: %ParseForm -> %Form 
transUnCons u ==
  u isnt [.,:.] => systemErrorHere ["transUnCons",u]
  u is ["APPEND",x,y] =>
    y = nil => x
    systemErrorHere ["transUnCons",u]
  u is ["CONS",x,y] =>
    y isnt [.,:.] => [x,:y]
    [x,:transUnCons y]

parseCoerce: %ParseForm -> %Form 
parseCoerce t ==
  t isnt [.,x,typ] => systemErrorHere ["parseCoerce",t]
  ["::",parseTran x,parseType typ]

parseAtSign: %ParseForm -> %Form 
parseAtSign t ==
  t isnt [.,x,typ] => systemErrorHere ["parseAtSign",t]
  ["@",parseTran x,parseType typ]
 

parsePretend: %ParseForm -> %Form
parsePretend t ==
  t isnt ["pretend",x,typ] => systemErrorHere ["parsePretend",t]
  ["pretend",parseTran x,parseType typ]
 
parseHas: %ParseForm -> %Form 
parseHas t ==
  t isnt ["has",x,y] => systemErrorHere ["parseHas",t]
  ["has",x,fn y] where
    fn y ==
      y is [":" ,op,["Mapping",:map]] =>
         ["SIGNATURE",washOperatorName op,map]
      y is ["Join",:u] => ["Join",:[fn z for z in u]]
      y is ["CATEGORY",kind,:u] => ["CATEGORY",kind,:[fn z for z in u]]
      kk:= getConstructorKindFromDB opOf y
      kk = "domain" or kk = "category" => makeNonAtomic y
      y is ["ATTRIBUTE",:.] => y
      y is ["SIGNATURE",:.] => y
      y is [":",op,type] => ["SIGNATURE",washOperatorName op,[type],"constant"]
      ["ATTRIBUTE",y]
 
parseDEF: %ParseForm -> %Form
parseDEF t ==
  t isnt ["DEF",$lhs,tList,body] => systemErrorHere ["parseDEF",t]
  setDefOp $lhs
  ["DEF",parseLhs $lhs,parseTypeList tList,
    parseTranCheckForRecord(body,opOf $lhs)]
 
parseLhs: %ParseForm -> %Form
parseLhs x ==
  x isnt [.,:.] => parseTran x
  first x isnt [.,:.] => [parseTran first x,:[transIs parseTran y for y in rest x]]
  parseTran x
 

parseMDEF: %ParseForm -> %Form
parseMDEF t ==
  t isnt ["MDEF",$lhs,tList,body] => systemErrorHere ["parseMDEF",t]
  ["MDEF",$lhs,parseTypeList tList,parseTranCheckForRecord(body,opOf $lhs)]

parseTranCheckForRecord: (%ParseForm,%ParseForm) -> %Form
parseTranCheckForRecord(x,op) ==
  x := parseTran x
  x is ["Record",:l] =>
    or/[y for y in l | y isnt [":",.,.]] =>
      postError ['"   Constructor",:bright x,'"has missing label"]
    x
  x
 
doParseCategory: %ParseForm -> %Form
doParseCategory t ==
  t isnt ["CATEGORY",:x] => systemErrorHere ["doParseCategory",t]
  $parsingType: local := true
  l := parseTranList x
  key :=
    CONTAINED("$",l) => 'domain
    'package
  ["CATEGORY",key,:l]
 
parseAnd: %ParseForm -> %Form 
parseAnd t ==
  t isnt ["and",:u] => systemErrorHere ["parseAnd",t]
  u = nil => "true"
  u is [a] => a
  parseIf ["IF",parseTran first u,parseAnd ["and",:rest u],"false"]
 

parseOr: %ParseForm -> %Form
parseOr t ==
  t isnt ["or",:u] => systemErrorHere ["parseOr",t]
  u = nil => "false"
  u is [a] => a
  (x:= parseTran first u) is ["not",y] => 
    parseIf ["IF",y,parseOr ["or",:rest u],"true"]
  parseIf ["IF",x,"true",parseOr ["or",:rest u]]
 
doParseExit: %ParseForm -> %Form
doParseExit t ==
  t isnt ["exit",a,:b] => systemErrorHere ["doParseExit",t]
  --  note: I wanted to convert 1s to 0s here to facilitate indexing in
  --   comp code; unfortunately, parseTran-ning is sometimes done more
  --   than once so that the count can be decremented more than once
  a:= parseTran a
  b:= parseTran b
  b =>
    not integer? a =>
      (MOAN('"first arg ",a,'" for exit must be integer"); ["exit",1,a])
    ["exit",a,:b]
  ["exit",1,a]
 

doParseLeave: %ParseForm -> %Form
doParseLeave t ==
  t isnt ["leave",a,:b] => systemErrorHere ["doParseLeave",t]
  a:= parseTran a
  b:= parseTran b
  b =>
    not integer? a =>
      (MOAN('"first arg ",a,'" for 'leave' must be integer"); ["leave",1,a])
    ["leave",a,:b]
  ["leave",1,a]
 

parseJoin: %ParseForm -> %Form
parseJoin t ==
  t isnt ["Join",:l] => systemErrorHere ["parseJoin",t]
  ["Join",:fn parseTypeList l] where
    fn l ==
      l = nil => nil
      l is [["Join",:x],:y] => [:x,:fn y]
      [first l,:fn rest l]

parseInBy: %ParseForm -> %Form
parseInBy t ==
  t isnt ["INBY",i,n,inc] => systemErrorHere ["parseInBy",t]
  (u:= parseIn ["IN",i,n]) isnt ["STEP",i,a,j,:r] =>
    postError ["   You cannot use",:bright '"by",
      '"except for an explicitly indexed sequence."]
  inc:= parseTran inc
  ["STEP",i,a,parseTran inc,:r]

parseSegment: %ParseForm -> %Form 
parseSegment p ==
  p is ["SEGMENT",a,:b] =>
    b => ["SEGMENT",parseTran a, parseTran first b]
    ["SEGMENT",parseTran a]
  -- SEGMENT is being elted from a domain
  ["SEGMENT",:rest p]
 
parseIn: %ParseForm -> %Form
parseIn t ==
  t isnt ["IN",i,n] => systemErrorHere ["parseIn",t]
  i:= parseTran i
  n:= parseTran n
  n is ["SEGMENT",a] => ["STEP",i,a,1]
  n is ["reverse",["SEGMENT",a]] =>
    postError ['"  You cannot reverse an infinite sequence."]
  n is ["SEGMENT",a,b] => (b => ["STEP",i,a,1,b]; ["STEP",i,a,1])
  n is ["reverse",["SEGMENT",a,b]] =>
    b => ["STEP",i,b,-1,a]
    postError ['"  You cannot reverse an infinite sequence."]
  n is ["tails",s] => ["ON",i,s]
  ["IN",i,n]
 
parseIf: %ParseForm -> %Form
parseIf t ==
  t isnt ["IF",p,a,b] => t
  ifTran(parseTran p,parseTran a,parseTran b) where
    ifTran(p,a,b) ==
      p="true"  => a
      p="false"  => b
      p is ["not",p'] => ifTran(p',b,a)
      p is ["IF",p',a',b'] => ifTran(p',ifTran(a',copyTree a,copyTree b),ifTran(b',a,b))
      p is ["SEQ",:l,["exit",1,p']] =>
        ["SEQ",:l,["exit",1,ifTran(p',incExitLevel a,incExitLevel b)]]
         --this assumes that l has no exits
      a is ["IF", =p,a',.] => ["IF",p,a',b]
      b is ["IF", =p,.,b'] => ["IF",p,a,b']
      not $parsingType and
        (makeSimplePredicateOrNil p is ["SEQ",:s,["exit",1,val]]) =>
           parseTran ["SEQ",:s,["exit",1,incExitLevel ["IF",val,a,b]]]
      ["IF",p,a,b]
 
makeSimplePredicateOrNil: %ParseForm -> %Form
makeSimplePredicateOrNil p ==
  isSimple p => nil
  u:= isAlmostSimple p => u
  wrapSEQExit [[":=",g:= gensym(),p],g]
 

parseWhere: %List %Form -> %Form
parseWhere t == 
  t isnt ["where",:l] => systemErrorHere ["parseWhere",t]
  ["where",:parseTranList l]
 
 
parseSeq: %List %Form -> %Form
parseSeq t ==
  t isnt ["SEQ",:l] => systemErrorHere ["parseSeq",t]
  l isnt [:.,["exit",:.]] =>
    postError ['"   Invalid ending to block: ",last l]
  transSeq parseTranList l
 

transSeq: %List %Form -> %Form
transSeq l ==
  l = nil => nil
  l is [x] => decExitLevel x
  [item,:tail] := l
  item is ["SEQ",:l,["exit",1,["IF",p,["exit", =2,q],"%noBranch"]]] and
    (and/[x is [":=",:.] for x in l]) =>
      ["SEQ",:[decExitLevel x for x in l],["exit",1,["IF",decExitLevel p,
        decExitLevel q,transSeq tail]]]
  item is ["IF",a,["exit",1,b],"%noBranch"] =>
    ["IF",decExitLevel a,decExitLevel b,transSeq tail]
  item is ["IF",a,"%noBranch",["exit",1,b]] =>
    ["IF",decExitLevel a,transSeq tail,decExitLevel b]
  item is ["IF",a,["exit",1,b],c] =>
    ["IF",decExitLevel a,decExitLevel b,transSeq [c,:tail]]
  (y:= transSeq tail) is ["SEQ",:s] => ["SEQ",item,:s]
  ["SEQ",item,["exit",1,incExitLevel y]]
 
superSub: (%Symbol, %List %Form ) -> %Form
superSub(name,x) ==
  for u in x repeat y:= [:y,:u]
  code:=
    x is [[u]] => $quadSymbol
    strconc('"_(",scriptTranRow first x,scriptTran rest x,'"_)")
  [makeSymbol strconc(symbolName name,"$",code),:y]
 
scriptTran: %List %Form -> %String
scriptTran x ==
  x = nil => '""
  strconc('";",scriptTranRow first x,scriptTran rest x)
 
scriptTranRow: %List %Form -> %String
scriptTranRow x ==
  x = nil => '""
  strconc($quadSymbol,scriptTranRow1 rest x)

scriptTranRow1: %List %Form -> %String 
scriptTranRow1 x ==
  x = nil => '""
  strconc('",",$quadSymbol,scriptTranRow1 rest x)
 
parseVCONS: %List %Form -> %Form
parseVCONS l == 
  ["VECTOR",:parseTranList rest l]

--% Register special parsers.

for x in [[":", :"parseColon"],_
	  ["::", :"parseCoerce"],_
	  ["@", :"parseAtSign"],_
	  ["and", :"parseAnd"],_
	  ["CATEGORY", :"doParseCategory"],_
	  ["construct", :"parseConstruct"],_
	  ["DEF", :"parseDEF"],_
	  ["exit", :"doParseExit"],_
	  ["has", :"parseHas"],_
	  ["IF", :"parseIf"],_
	  ["IN", :"parseIn"],_
	  ["INBY", :"parseInBy"],_
	  ["is", :"parseIs"],_
	  ["isnt", :"parseIsnt"],_
	  ["Join", :"parseJoin"],_
	  ["leave", :"doParseLeave"],_
	  [":=", :"parseAssign"],_
	  ["MDEF", :"parseMDEF"],_
	  ["or", :"parseOr"],_
	  ["pretend", :"parsePretend"],_
	  ["SEGMENT", :"parseSegment"],_
	  ["SEQ", :"parseSeq"],_
	  ["VCONS", :"parseVCONS"],_
	  ["where", :"parseWhere"]] repeat
  property(first x,'parseTran) := rest x