Source: ast-build.js

/**
 * Universidad de La Laguna
 * Escuela Superior de Ingeniería y Tecnología
 * Grado en Ingeniería Informática
 * Procesadores de Lenguajes
 *
 * @author Juan Rodríguez Suárez
 * @since Mar 04 2024
 * @desc Contains all the functions to build the AST.
 */

const { $ } = require('./utils.js')

/**
 * @brief Builds the root of the AST
 * @param {object} child - The child node
 * @returns {object} The root node
 */
function buildRoot(child) {
  return {
    type: 'Program',
    body: [
      {
        type: 'ExpressionStatement',
        expression: child,
      }
    ],
    sourceType: 'script'
  };
}

/**
 * @brief Builds a literal node
 * @param {string} value - The value
 * @returns {object} The literal node
 */
function buildLiteral(value) {
  return {
    type: 'Literal',
    value,
    raw: `\"${value}\"`
  }
}

/**
 * @brief Builds a call expression node
 * @param {string} functionName - The function name
 * @param {Array} args - The arguments
 * @param {boolean} reservedWord - The reserved word
 * @returns {object} The call expression node
 */
function buildCallExpression(functionName, args, reservedWord = false) {
  return {
    type: 'CallExpression',
    callee: {
      type: 'Identifier',
      name: reservedWord ? functionName : $(functionName)
    },
    arguments: args
  }
}

/**
 * @brief Builds an identifier node
 * @param {string} name - The name
 * @returns {object} The identifier node
 */
function buildIdentifier(name) {
  return {
    type: 'Identifier',
    name: name,
  };
}

/**
 * @brief Builds a variable declaration node
 * @param {Array} declarations - The declarations
 * @returns {object} The variable declaration node
 */
function buildVariableDeclaration(declarations) {
  return {
    type: 'VariableDeclaration',
    declarations,
    kind: 'let',
  };
}

/**
 * @brief Builds a variable declarator node
 * @param {object} id - The id
 * @returns {object} The variable declarator node
 */
function buildVariableDeclarator(id) {
  return {
    type: 'VariableDeclarator',
    id: id,
    init: null,
  };
}

/**
 * @brief Builds an assignment expression node
 * @param {string} name - The name
 * @param {string} operator - The operator
 * @param {object} right - The right node
 * @returns {object} The assignment expression node
 */
function buildAssignmentExpression(name, operator, right) {
  return {
    type: 'AssignmentExpression',
    operator,
    left: buildIdentifier(name),
    right: right,
  };
}

/**
 * @brief Builds a sequence expression node
 * @param {Array} expressions - The expressions
 * @returns {object} The sequence expression node
 */
function buildSequenceExpression(expressions) {
  return {
    type: 'SequenceExpression',
    expressions,
  };
}

/**
 * @brief Builds a member expression node
 * @param {object} object - The object
 * @param {string} operator - The operator
 * @param {Array} args - The arguments
 * @returns {object} The member expression node
 */
function buildMemberExpression(object, operator, args) {
  return {
    type: 'CallExpression',
    callee: {
      type: 'MemberExpression',
      object,
      property: {
        type: 'Identifier',
        name: operator
      }
    },
    arguments: args
  };
}

/**
 * @brief Builds a function declaration node
 * @param {Array} params - The parameters
 * @param {object} expression - The return expression
 * @returns {object} The function declaration node
 */
function buildFunctionExpression(params, expression) {
  return {
    type: 'FunctionExpression',
    id: null,
    params,
    body: {
      type: 'BlockStatement',
      body: [
        {
          type: 'ReturnStatement',
          argument: expression
        }
      ]
    },
    generator: false,
    expression: false,
    async: false
  };
}

/**
 * @brief Builds a logical expression node
 * @param {object} left - The left node
 * @param {string} operator - The operator
 * @param {object} right - The right node
 * @returns {object} The logical expression node
 */
function buildLogicalExpression(left, operator, right) {
  return {
    type: 'LogicalExpression',
    operator,
    left,
    right
  }
}

/**
 * @brief Builds a unary expression node
 * @param {string} operator - The operator
 * @param {object} argument - The argument
 * @param {boolean} prefix - The prefix
 * @returns {object} The unary expression node
 */
function buildUnaryExpression(operator, argument, prefix) {
  return {
    type: 'UnaryExpression',
    operator,
    argument,
    prefix
  }
}

/**
 * @brief Builds a sequence of calls to an identifier
 * @param {string} id - The identifier
 * @param {Array} calls - The calls
 * @returns {object} The sequence of calls
 */
function buildIdCalls(id, calls) {
  let node = id;
  calls.forEach(ast => {
    let parent = {
      type: 'CallExpression',
      callee: node,
      arguments: ast
    };
    node = parent;
  });
  return node;
}

module.exports = {
  buildRoot,
  buildLiteral,
  buildCallExpression,
  buildIdentifier,
  buildVariableDeclaration,
  buildVariableDeclarator,
  buildAssignmentExpression,
  buildSequenceExpression,
  buildMemberExpression,
  buildFunctionExpression,
  buildLogicalExpression,
  buildUnaryExpression,
  buildIdCalls
}