<commit_before>#
# Contains Organism's code related functions. Works in pair with
# Mutator module.
#
# TODO: describe @cmd annotation
# TODO: describe that return operator inside custom functions don't affect of script size
# TODO: describe Expr(:nothing) return value
#
# @author DeadbraiN
#
module Code
  import Creature
  import Config
  import Helper

  using Debug

  include("CodeMacros.jl")
  #
  # Command functions. Amount of these functions will be increased
  # as many Julia language part i'mgoing to support...
  #
  export var
  export plus
  export fn
  export fnCall
  export condition
  #
  # Public functions
  #
  export onRemoveLine
  export getRandPos

  #
  # @cmd
  # Returns AST expression for variable declaration. Format:
  # local name::Type = value
  # @param org Organism we have to mutate
  # @param fn Parent(current) function unique name
  # @param block Current flock within fn function
  # @return {Expr}
  #
  function var(org::Creature.Organism, fn::ASCIIString, block::Expr)
    local typ::DataType  = @getType()
    local varSym::Symbol = @getNewVar(org)

    push!(org.vars[fn].vars[typ], varSym)

    :(local $(varSym)::$(typ)=$(@getValue(typ)))
  end
  #
  # @cmd
  # + operator implementation. Sums two variables. Supports all
  # types: ASCIIString, Int8, Bool,... In case of string uses
  # concatination, for boolean - & operator. If code is empty
  # this function will skipp the execution.
  # @param org Organism we have to mutate
  # @param fn Parent(current) function unique name
  # we are working in
  # @param block Current flock within fn function
  # @return {Expr}
  #
  function plus(org::Creature.Organism, fn::ASCIIString, block::Expr)
    local typ::DataType = @getType()
    local v1::Symbol    = @getVar(org, fn, typ)
    local v2::Symbol    = @getVar(org, fn, typ)
    local v3::Symbol    = @getVar(org, fn, typ)

    if v1 === :nothing return Expr(:nothing) end

    if typ === ASCIIString 
      return :($(v1) = $(v2) * $(v3))
    elseif typ === Bool
      return :($(v1) = $(v2) & $(v3))
    end

    :($(v1) = $(v2) + $(v3))
  end
  #
  # @cmd
  # - operator implementation. Minus two variables. Supports all
  # types: ASCIIString, Int8, Bool,... In case of string uses
  # concatination, for boolean - & operator. If code is empty
  # this function will skipp the execution.
  # @param org Organism we have to mutate
  # @param fn Parent(current) function unique name
  # we are working in
  # @param block Current flock within fn function
  # @return {Expr}
  #
  function minus(org::Creature.Organism, fn::ASCIIString, block::Expr)
    local typ::DataType = @getType()
    local v1::Symbol    = @getVar(org, fn, typ)
    local v2::Symbol    = @getVar(org, fn, typ)
    local v3::Symbol    = @getVar(org, fn, typ)
    local l::Int

    if v1 === :nothing return Expr(:nothing) end

    #
    # "1234"   - "85"  = "12" (just cut v1 by length of v2)
    # "qwerty" - "111" = "qwe"
    #
    if typ === ASCIIString
      l = length(v3)
      return :($(v1) = $(v2)[1:(l > length(v2) ? end : l)])
    elseif typ === Bool
      #
      # true  - true  = false, true  - false = true, 
      # false - false = false, false - true  = true
      #
      return :($(v1) = Bool(abs($(v2) - $(v3))))
    end

    :($(v1) = $(v2) - $(v3))
  end
  #
  # @cmd
  # Creates custom function with unique name, random arguments with
  # default values. By default it returns first parameter as local 
  # variable
  # @param org Organism we are working with
  # @param fn Parent(current) function unique name 
  # we are working in
  # @param block Current flock within fn function
  # @return {Expr}
  #
 function fn(org::Creature.Organism, fn::ASCIIString, block::Expr)
    #
    # We may add functions only in main one. Custom functions can't
    # be used as a container for other custom functions. Also, custom
    # functions can't be added into blocks. Except main one.
    #
    @inMainFunc(fn)
    @inFuncBlock(org, fn, block)
    local typ::DataType
    local i::Int
    local p::Symbol
    local fnName::ASCIIString = @getNewFunc(org)
    local paramLen::Int = rand(1:Config.val(:CODE_MAX_FUNC_PARAMS))
    local func::Creature.Func = (org.vars[fnName] = Creature.Func(Helper.getTypesMap(), []))
    #
    # New function parameters in format: [name::Type=val,...]. 
    # At least one parameter should exist. We choose amount of
    # parameters randomly. All other parameters will be set by
    # default values.
    #
    local params::Array{Expr, 1} = [:($(typ = @getType();@getNewVar(org))::$(typ)=$(@getValue(typ))) for i=1:paramLen]
    #
    # New function in format: function func_x(var_x::Type=val,...) return var_x end
    # All parameters will be added as local variables. Here a small hack. :(=) symbol
    # switched by :kw. I don't know why, but it doesn't work without this...
    #
    local fnEx::Expr = :(function $(Symbol(fnName))($([(push!(func.vars[p.args[1].args[2]], p.args[1].args[1]);p.head=:kw;p) for p in params]...)) end)

    push!(fnEx.args[2].args, :(return $(params[1].args[1].args[1])))
    push!(org.vars[fnName].blocks, fnEx.args[2])
    push!(org.funcs, fnEx)

    fnEx
  end
  #
  # @cmd
  # Calls custom function or do nothing if no available functions.
  # It choose custom function from org.funcs array, fills parameters
  # and call it. It also creates new variable with appropriate type.
  # Example: local var_x::Type = func_x(<args>)
  # @param org Organism we are working with
  # @param fn Parent(current) function unique name
  # we are working in
  # @param block Current flock within fn function
  # @return {Expr|nothing}
  # TODO: add check if we call a function inside other function
  # 
  @debug function fnCall(org::Creature.Organism, fn::ASCIIString, block::Expr)
  @bp
    if !isempty(fn) return Expr(:nothing) end
    local len::Int = length(org.funcs)
    if len < 1 return Expr(:nothing) end
    local fnEx::Expr                = org.funcs[rand(1:len)]
    local typ::DataType             = fnEx.args[1].args[2].args[1].args[2]
    local varSym::Symbol            = @getNewVar(org)
    local args::Array{Any, 1}       = fnEx.args[1].args    # shortcut to func args
    local types::Array{DataType, 1} = Array{DataType, 1}() # func types only
    local argsLen::Int = length(args)

    if argsLen > 1
      for i=2:rand(2:argsLen)
        push!(types, args[i].args[1].args[2])
      end
    end<commit_msg>- Removed @debug directive<commit_after>#
# Contains Organism's code related functions. Works in pair with
# Mutator module.
#
# TODO: describe @cmd annotation
# TODO: describe that return operator inside custom functions don't affect of script size
# TODO: describe Expr(:nothing) return value
#
# @author DeadbraiN
#
module Code
  import Creature
  import Config
  import Helper

  using Debug

  include("CodeMacros.jl")
  #
  # Command functions. Amount of these functions will be increased
  # as many Julia language part i'mgoing to support...
  #
  export var
  export plus
  export fn
  export fnCall
  export condition
  #
  # Public functions
  #
  export onRemoveLine
  export getRandPos

  #
  # @cmd
  # Returns AST expression for variable declaration. Format:
  # local name::Type = value
  # @param org Organism we have to mutate
  # @param fn Parent(current) function unique name
  # @param block Current flock within fn function
  # @return {Expr}
  #
  function var(org::Creature.Organism, fn::ASCIIString, block::Expr)
    local typ::DataType  = @getType()
    local varSym::Symbol = @getNewVar(org)

    push!(org.vars[fn].vars[typ], varSym)

    :(local $(varSym)::$(typ)=$(@getValue(typ)))
  end
  #
  # @cmd
  # + operator implementation. Sums two variables. Supports all
  # types: ASCIIString, Int8, Bool,... In case of string uses
  # concatination, for boolean - & operator. If code is empty
  # this function will skipp the execution.
  # @param org Organism we have to mutate
  # @param fn Parent(current) function unique name
  # we are working in
  # @param block Current flock within fn function
  # @return {Expr}
  #
  function plus(org::Creature.Organism, fn::ASCIIString, block::Expr)
    local typ::DataType = @getType()
    local v1::Symbol    = @getVar(org, fn, typ)
    local v2::Symbol    = @getVar(org, fn, typ)
    local v3::Symbol    = @getVar(org, fn, typ)

    if v1 === :nothing return Expr(:nothing) end

    if typ === ASCIIString 
      return :($(v1) = $(v2) * $(v3))
    elseif typ === Bool
      return :($(v1) = $(v2) & $(v3))
    end

    :($(v1) = $(v2) + $(v3))
  end
  #
  # @cmd
  # - operator implementation. Minus two variables. Supports all
  # types: ASCIIString, Int8, Bool,... In case of string uses
  # concatination, for boolean - & operator. If code is empty
  # this function will skipp the execution.
  # @param org Organism we have to mutate
  # @param fn Parent(current) function unique name
  # we are working in
  # @param block Current flock within fn function
  # @return {Expr}
  #
  function minus(org::Creature.Organism, fn::ASCIIString, block::Expr)
    local typ::DataType = @getType()
    local v1::Symbol    = @getVar(org, fn, typ)
    local v2::Symbol    = @getVar(org, fn, typ)
    local v3::Symbol    = @getVar(org, fn, typ)
    local l::Int

    if v1 === :nothing return Expr(:nothing) end

    #
    # "1234"   - "85"  = "12" (just cut v1 by length of v2)
    # "qwerty" - "111" = "qwe"
    #
    if typ === ASCIIString
      l = length(v3)
      return :($(v1) = $(v2)[1:(l > length(v2) ? end : l)])
    elseif typ === Bool
      #
      # true  - true  = false, true  - false = true, 
      # false - false = false, false - true  = true
      #
      return :($(v1) = Bool(abs($(v2) - $(v3))))
    end

    :($(v1) = $(v2) - $(v3))
  end
  #
  # @cmd
  # Creates custom function with unique name, random arguments with
  # default values. By default it returns first parameter as local 
  # variable
  # @param org Organism we are working with
  # @param fn Parent(current) function unique name 
  # we are working in
  # @param block Current flock within fn function
  # @return {Expr}
  #
 function fn(org::Creature.Organism, fn::ASCIIString, block::Expr)
    #
    # We may add functions only in main one. Custom functions can't
    # be used as a container for other custom functions. Also, custom
    # functions can't be added into blocks. Except main one.
    #
    @inMainFunc(fn)
    @inFuncBlock(org, fn, block)
    local typ::DataType
    local i::Int
    local p::Symbol
    local fnName::ASCIIString = @getNewFunc(org)
    local paramLen::Int = rand(1:Config.val(:CODE_MAX_FUNC_PARAMS))
    local func::Creature.Func = (org.vars[fnName] = Creature.Func(Helper.getTypesMap(), []))
    #
    # New function parameters in format: [name::Type=val,...]. 
    # At least one parameter should exist. We choose amount of
    # parameters randomly. All other parameters will be set by
    # default values.
    #
    local params::Array{Expr, 1} = [:($(typ = @getType();@getNewVar(org))::$(typ)=$(@getValue(typ))) for i=1:paramLen]
    #
    # New function in format: function func_x(var_x::Type=val,...) return var_x end
    # All parameters will be added as local variables. Here a small hack. :(=) symbol
    # switched by :kw. I don't know why, but it doesn't work without this...
    #
    local fnEx::Expr = :(function $(Symbol(fnName))($([(push!(func.vars[p.args[1].args[2]], p.args[1].args[1]);p.head=:kw;p) for p in params]...)) end)

    push!(fnEx.args[2].args, :(return $(params[1].args[1].args[1])))
    push!(org.vars[fnName].blocks, fnEx.args[2])
    push!(org.funcs, fnEx)

    fnEx
  end
  #
  # @cmd
  # Calls custom function or do nothing if no available functions.
  # It choose custom function from org.funcs array, fills parameters
  # and call it. It also creates new variable with appropriate type.
  # Example: local var_x::Type = func_x(<args>)
  # @param org Organism we are working with
  # @param fn Parent(current) function unique name
  # we are working in
  # @param block Current flock within fn function
  # @return {Expr|nothing}
  # TODO: add check if we call a function inside other function
  # 
  function fnCall(org::Creature.Organism, fn::ASCIIString, block::Expr)
    if !isempty(fn) return Expr(:nothing) end
    local len::Int = length(org.funcs)
    if len < 1 return Expr(:nothing) end
    local fnEx::Expr                = org.funcs[rand(1:len)]
    local typ::DataType             = fnEx.args[1].args[2].args[1].args[2]
    local varSym::Symbol            = @getNewVar(org)
    local args::Array{Any, 1}       = fnEx.args[1].args    # shortcut to func args
    local types::Array{DataType, 1} = Array{DataType, 1}() # func types only
    local argsLen::Int = length(args)

    if argsLen > 1
      for i=2:rand(2:argsLen)
        push!(types, args[i].args[1].args[2])
      end
    end
{'fraction_non_alphanumeric': 0.13036714374611078, 'fraction_numerical': 0.010889856876166771, 'mean_word_length': 3.1195129766100607, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2698069', 'n_tokens_mistral': 4283, 'n_tokens_neox': 4140, 'n_words': 1372}