/******************************************************************************/
/*                                                                            */
/* Expressions.cls -- Clone expressions (part of the identity compiler)       */
/* ====================================================================       */
/*                                                                            */
/*                                                                            */
/* This program is part of the Rexx Parser package                            */
/* [See https://rexx.epbcn.com/rexx-parser/]                                  */
/*                                                                            */
/* Copyright (c) 2024-2026 Josep Maria Blasco <josep.maria.blasco@epbcn.com>  */
/*                                                                            */
/* License: Apache License 2.0 (https://www.apache.org/licenses/LICENSE-2.0)  */
/*                                                                            */
/* Version history:                                                           */
/*                                                                            */
/* Date     Version Details                                                   */
/* -------- ------- --------------------------------------------------------- */
/* 20251116    0.3a First version                                             */
/* 20251209         (Executor) Implement named arguments                      */
/* 20251212         (Executor) Implement message terms                        */
/*                                                                            */
/******************************************************************************/

  Call "modules/Load.Parser.Module.rex"

  pkgLocal = .context~package~local

  -- Set to 1 to activate debug
  pkgLocal~DEBUG = 0

::Requires "modules/identity/compile.cls"

/******************************************************************************/
/* ADDITION EXPRESSIONS                                                       */
/******************************************************************************/

::Method "Addition.Expression::Compile"
  Use Strict Arg element, stream, context

  args  = self~args
  nArgs = args~items

  Loop i = 1 By 2 To nArgs
    -- Compile args[i]
    element = args[i]~compile( element, stream, context )
    -- Now if i < nArgs, args[i+1] is an addition operator
    If i < nArgs Then Do
      -- Clone everything until the start of the next argument
      begin = args[i+2]~begin
      Loop While element \== begin
        element = Clone( element, stream, context )
      End
    End
  End

  Return element

/******************************************************************************/
/* AND EXPRESSIONS                                                            */
/******************************************************************************/

::Method "And.Expression::Compile"
  Use Strict Arg element, stream, context

  args  = self~args
  nArgs = args~items

  Loop i = 1 By 2 To nArgs
    -- Compile args[i]
    element = args[i]~compile( element, stream, context )
    -- Now if i < nArgs, args[i+1] is an and operator
    If i < nArgs Then Do
      -- Clone everything until the start of the next argument
      begin = args[i+2]~begin
      Loop While element \== begin
        element = Clone( element, stream, context )
      End
    End
  End

  Return element

/******************************************************************************/
/* ARGUMENTS                                                                  */
/******************************************************************************/

::Method "Arguments::Compile"
  Use Strict Arg element, stream, context

  namedArguments = self~namedArguments

  If self~isEmpty, namedArguments~isEmpty == .Nil Then Return element

  first = 1
  Loop arg Over self~args
    first = 0
    Loop While element~ignorable
      element = Clone( element, stream, context )
    End
    If arg~isA( .Special.Character.Sequence ) Then
      element = Clone( element, stream, context )
    Else element = arg~compile( element, stream, context )
  End

  If \namedArguments~isEmpty Then Do
    If \first Then -- Clone a comma
      element = Clone( element, stream, context )
    nNamed = namedArguments~items
    Loop Counter C named Over namedArguments

      Loop While element~ignorable
        element = Clone( element, stream, context )
      End

      -- name
      name       = named[1]
      If name \== .Nil Then Do
        element = name~compile( element, stream, context )
        Loop While element~ignorable
          element = Clone( element, stream, context )
        End
      End

      -- ":"
      element = Clone( element, stream, context )

      Loop While element~ignorable
        element = Clone( element, stream, context )
      End

      expression = named[2]
      If expression \== .Nil Then Do
        element = expression~compile( element, stream, context )
        Loop While element~ignorable
          element = Clone( element, stream, context )
        End
      End

      If c < nNamed Then Do -- Clone ","
        element = Clone( element, stream, context )
        Loop While element~ignorable
          element = Clone( element, stream, context )
        End
      End
    End
  End

  Return element

/******************************************************************************/
/* ARRAY TERMS                                                                */
/******************************************************************************/

::Method "Array.Term::Compile"
  Use Strict Arg element, stream, context

  Return self~expression_list~compile( element, stream, context )

/******************************************************************************/
/* COMPOUND VARIABLE TERMS                                                    */
/******************************************************************************/

::Method "Compound.Variable.Term::Compile"
  Use Strict Arg element, stream, context

  element = Clone( element, stream, context )

  Return element

/******************************************************************************/
/* COMPARISON EXPRESSIONS                                                     */
/******************************************************************************/

::Method "Comparison.Expression::Compile"
  Use Strict Arg element, stream, context

  args  = self~args
  nArgs = args~items

  Loop i = 1 By 2 To nArgs
    -- Compile args[i]
    element = args[i]~compile( element, stream, context )
    -- Now if i < nArgs, args[i+1] is a comparison operator
    If i < nArgs Then Do
      -- Clone everything until the start of the next argument
      begin = args[i+2]~begin
      Loop While element \== begin
        element = Clone( element, stream, context )
      End
    End
  End

  Return element

/******************************************************************************/
/* CONCATENATION EXPRESSIONS                                                  */
/******************************************************************************/

::Method "Concatenation.Expression::Compile"
  Use Strict Arg element, stream, context

  args  = self~args
  nArgs = args~items

  Loop i = 1 By 2 To nArgs
    -- Compile args[i]
    element = args[i]~compile( element, stream, context )
    -- Now if i < nArgs, args[i+1] is a concatenation operator
    If i < nArgs Then Do
      -- Clone everything until the start of the next argument
      begin = args[i+2]~begin
      Loop While element \== begin
        element = Clone( element, stream, context )
      End
    End
  End

  Return element

/******************************************************************************/
/* EXPRESSION LISTS                                                           */
/******************************************************************************/

::Method "Expression.List::Compile"
  Use Strict Arg element, stream, context

  If self~isEmpty Then Return element

  Loop arg Over self~args
    Loop While element~ignorable
      element = Clone( element, stream, context )
    End
    If arg~isA( .Special.Character.Sequence ) Then
      element = Clone( element, stream, context )
    Else element = arg~compile( element, stream, context )
  End

  Return element

/******************************************************************************/
/* FUNCTION CALL TERMS                                                        */
/******************************************************************************/

::Method "Function.Call.Term::Compile"

  Use Strict Arg element, stream, context

  name          = self~name
  argumentList  = self~argumentList
  trailingBlock = self~trailingBlock

  -- Name
  element = name~compile( element, stream, context )

  If argumentList \== .Nil Then Do
    -- "("
    element = Clone( element, stream, context )
    -- Ignorable stuff
    Loop While element~ignorable
      element = Clone( element, stream, context )
    End
    -- Argument list
    element = argumentList~compile( element, stream, context )
    -- More ignorable stuff
    Loop While element~ignorable
      element = Clone( element, stream, context )
    End
    -- ")"
    element = Clone( element, stream, context )
  End

  If trailingBlock \== .Nil Then Do
    element = trailingBlock~compile( element, stream, context )
  End

  Return element

/******************************************************************************/
/* INDEXED OR INVOKED TERMS                                                   */
/******************************************************************************/

::Method "Indexed.Or.Invoked.Term::Compile"
  Use Strict Arg element, stream, context

  term      = self~term
  bracket   = self~bracket
  arguments = self~arguments

  -- term
  element = term~compile( element, stream, context )
  -- Left bracket
  element = Clone( element, stream, context )
  -- Arguments
  element = arguments~compile( element, stream, context )
  -- Right bracket
  element = Clone( element, stream, context )

  Return element

/******************************************************************************/
/* LITERAL STRING TERMS                                                       */
/******************************************************************************/

::Method "Literal.String.Term::Compile"
  Use Strict Arg element, stream, context

  element = Clone( element, stream, context )

  Return element

/******************************************************************************/
/* MESSAGE TERMS                                                              */
/******************************************************************************/

::Method "Message.Term::Compile"
  Use Strict Arg element, stream, context

  term          = self~term
  messageName   = self~messageName
  scope         = self~scope
  arguments     = self~arguments
  trailingBlock = self~trailingBlock

  element = term~compile( element, stream, context )

  Loop While element~ignorable
    element = Clone( element, stream, context )
  End

  -- ~ or ~~
  element = Clone( element, stream, context )

  Loop While element~ignorable
    element = Clone( element, stream, context )
  End

  If messageName \== .Nil Then Do
    Loop While element \== messageName
      element = Clone( element, stream, context )
    End

    element = messageName~compile( element, stream, context )
  End

  If scope \== .Nil Then Do
    Loop While element \< .EL.COLON
      element = Clone( element, stream, context )
    End
    element = Clone( element, stream, context )
    Loop While element~ignorable
      element = Clone( element, stream, context )
    End
    element = scope~compile( element, stream, context )
  End

  If element < .EL.LEFT_PARENTHESIS Then Do
    element = Clone( element, stream, context )
    Loop While element~ignorable
      element = Clone( element, stream, context )
    End
    element = arguments~compile( element, stream, context )
    Loop While element~ignorable
      element = Clone( element, stream, context )
    End
    element = Clone( element, stream, context )
  End

  If element < .EL.LEFT_CURLY_BRACKET Then Do
    element = trailingBlock~compile( element, stream, context )
  End

  Return element

/******************************************************************************/
/* MULTIPLICATION EXPRESSIONS                                                 */
/******************************************************************************/

::Method "Multiplication.Expression::Compile"
  Use Strict Arg element, stream, context

  args  = self~args
  nArgs = args~items

  Loop i = 1 By 2 To nArgs
    -- Compile args[i]
    element = args[i]~compile( element, stream, context )
    -- Now if i < nArgs, args[i+1] is a multiplication operator
    If i < nArgs Then Do
      -- Clone everything until the start of the next argument
      begin = args[i+2]~begin
      Loop While element \== begin
        element = Clone( element, stream, context )
      End
    End
  End

  Return element

/******************************************************************************/
/* NAMESPACE QUALIFIED CLASSNAME                                              */
/******************************************************************************/

::Method "Namespace.Qualified.Classname::Compile"
  Use Strict Arg element, stream, context

  classname     = self~classname
  nameSpace     = self~nameSpace

  -- Namespace
  element = nameSpace~compile( element, stream, context )

  -- Ignorable stuff
  Loop While element~ignorable
    element = Clone( element, stream, context )
  End

  -- ":"
  element = Clone( element, stream, context )

  -- Ignorable stuff
  Loop While element~ignorable
    element = Clone( element, stream, context )
  End

  -- Class name
  element = classname~compile( element, stream, context )

  Return element

/******************************************************************************/
/* NAMESPACE QUALIFIED FUNCTION CALLS                                         */
/******************************************************************************/

::Method "Namespace.Qualified.Function.Call::Compile"
  Use Strict Arg element, stream, context

  name          = self~name
  nameSpace     = self~nameSpace
  arguments     = self~arguments
  trailingBlock = self~trailingBlock

  -- Namespace
  element = nameSpace~compile( element, stream, context )

  -- Ignorable stuff
  Loop While element~ignorable
    element = Clone( element, stream, context )
  End

  -- ":"
  element = Clone( element, stream, context )

  -- Ignorable stuff
  Loop While element~ignorable
    element = Clone( element, stream, context )
  End

  -- Name
  element = name~compile( element, stream, context )

  If arguments \== .Nil Then Do
    -- "("
    element = Clone( element, stream, context )
    -- Ignorable stuff
    Loop While element~ignorable
      element = Clone( element, stream, context )
    End
    -- Argument list
    element = arguments~compile( element, stream, context )
    -- More ignorable stuff
    Loop While element~ignorable
      element = Clone( element, stream, context )
    End
    -- ")"
    element = Clone( element, stream, context )
  End

  If trailingBlock \== .Nil Then Do
    element = trailingBlock~compile( element, stream, context )
  End

  Return element

/******************************************************************************/
/* OR EXPRESSIONS                                                             */
/******************************************************************************/

::Method "Or.Expression::Compile"
  Use Strict Arg element, stream, context

  args  = self~args
  nArgs = args~items

  Loop i = 1 By 2 To nArgs
    -- Compile args[i]
    element = args[i]~compile( element, stream, context )
    -- Now if i < nArgs, args[i+1] is a or operator
    If i < nArgs Then Do
      -- Clone everything until the start of the next argument
      begin = args[i+2]~begin
      Loop While element \== begin
        element = Clone( element, stream, context )
      End
    End
  End

  Return element

/******************************************************************************/
/* POWER EXPRESSIONS                                                          */
/******************************************************************************/

::Method "Power.Expression::Compile"
  Use Strict Arg element, stream, context

  args  = self~args
  nArgs = args~items

  Loop i = 1 By 2 To nArgs
    -- Compile args[i]
    element = args[i]~compile( element, stream, context )
    -- Now if i < nArgs, args[i+1] is a power operator
    If i < nArgs Then Do
      -- Clone everything until the start of the next argument
      begin = args[i+2]~begin
      Loop While element \== begin
        element = Clone( element, stream, context )
      End
    End
  End

  Return element

/******************************************************************************/
/* PREFIX EXPRESSIONS                                                         */
/******************************************************************************/

::Method "Prefix.Expression::Compile"
  Use Strict Arg element, stream, context

  term  = self~term
  begin = term~begin

  -- Skip over prefixes
  Loop While element \== begin
    element = Clone( element, stream, context )
  End

  element = term~compile( element, stream, context )

  Return element

/******************************************************************************/
/* SOURCE LITERALS                                                            */
/******************************************************************************/

::Method "Source.Literal::Compile"

  Use Strict Arg element, stream, context

  begin        = self~begin
  end          = self~end
  instructions = self~instructions

  element = Clone( begin,   stream, context)  -- "{" -> (inserted) ";"
  element = Clone( element, stream, context ) -- Clone ";"

  Loop instruction Over instructions
    element = instruction~compile( element, stream, context )
  End

  Loop While element \== end
    element = Clone( element, stream, context )
  End

  Return element

/******************************************************************************/
/* STEM VARIABLE TERMS                                                        */
/******************************************************************************/

::Method "Stem.Variable.Term::Compile"
  Use Strict Arg element, stream, context

  element = Clone( element, stream, context )

  Return element

/******************************************************************************/
/* STRING OR SYMBOL ELEMENTS                                                  */
/******************************************************************************/

::Method "StringOrSymbol.Element::Compile"
  Use Strict Arg element, stream, context

  element = Clone( element, stream, context )

  Return element

/******************************************************************************/
/* SUBEXPRESSIONS                                                             */
/******************************************************************************/

::Method "SubExpression::Compile"
  Use Strict Arg element, stream, context

  subExpression = self~subExpression

  -- "("
  element = Clone( element, stream, context )

  -- Skip until the subexpression proper begins
  begin = subExpression~begin
  Loop While element \== begin
    element = Clone( element, stream, context )
  End

  -- Compile the subexpression
  element = subExpression~compile( element, stream, context )

  -- Skip until ")"
  Loop While element~ignorable
    element = Clone( element, stream, context )
  End

  -- ")"
  element = Clone( element, stream, context )

  Return element

/******************************************************************************/
/* SYMBOL TERMS                                                               */
/******************************************************************************/

::Method "Symbol.Term::Compile"
  Use Strict Arg element, stream, context

  element = Clone( element, stream, context )

  Return element

/******************************************************************************/
/* VARIABLE REFERENCE TERMS                                                   */
/******************************************************************************/

::Method "Variable.Reference.Term::Compile"
  Use Strict Arg element, stream, context

  -- Clone the operator
  element = Clone( element, stream, context )

  -- Skip all ignorables until we find the variable
  Loop While element~ignorable
    element = Clone( element, stream, context )
  End

  -- Clone the variable
  element = Clone( element, stream, context )

  Return element
