<commit_before>#
#
#           The Nim Compiler
#        (c) Copyright 2015 <NAME>
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# this module folds constants; used by semantic checking phase
# and evaluation phase

import
  strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times,
  nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
  commands, magicsys, saturate

proc getConstExpr*(m: PSym, n: PNode): PNode
  # evaluates the constant expression or returns nil if it is no constant
  # expression
proc evalOp*(m: TMagic, n, a, b, c: PNode): PNode
proc leValueConv*(a, b: PNode): bool
proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode
proc newStrNodeT*(strVal: string, n: PNode): PNode

# implementation

proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
  case skipTypes(n.typ, abstractVarRange).kind
  of tyInt:
    result = newIntNode(nkIntLit, intVal)
    result.typ = getIntLitType(result)
    # hrm, this is not correct: 1 + high(int) shouldn't produce tyInt64 ...
    #setIntLitType(result)
  of tyChar:
    result = newIntNode(nkCharLit, intVal)
    result.typ = n.typ
  else:
    result = newIntNode(nkIntLit, intVal)
    result.typ = n.typ
  result.info = n.info

proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
  result = newFloatNode(nkFloatLit, floatVal)
  if skipTypes(n.typ, abstractVarRange).kind == tyFloat:
    result.typ = getFloatLitType(result)
  else:
    result.typ = n.typ
  result.info = n.info

proc newStrNodeT(strVal: string, n: PNode): PNode =
  result = newStrNode(nkStrLit, strVal)
  result.typ = n.typ
  result.info = n.info

proc ordinalValToString*(a: PNode): string =
  # because $ has the param ordinal[T], `a` is not necessarily an enum, but an
  # ordinal
  var x = getInt(a)

  var t = skipTypes(a.typ, abstractRange)
  case t.kind
  of tyChar:
    result = $chr(int(x) and 0xff)
  of tyEnum:
    var n = t.n
    for i in countup(0, sonsLen(n) - 1):
      if n.sons[i].kind != nkSym: internalError(a.info, "ordinalValToString")
      var field = n.sons[i].sym
      if field.position == x:
        if field.ast == nil:
          return field.name.s
        else:
          return field.ast.strVal
    internalError(a.info, "no symbol for ordinal value: " & $x)
  else:
    result = $x

proc isFloatRange(t: PType): bool {.inline.} =
  result = t.kind == tyRange and t.sons[0].kind in {tyFloat..tyFloat128}

proc isIntRange(t: PType): bool {.inline.} =
  result = t.kind == tyRange and t.sons[0].kind in {
      tyInt..tyInt64, tyUInt8..tyUInt32}

proc pickIntRange(a, b: PType): PType =
  if isIntRange(a): result = a
  elif isIntRange(b): result = b
  else: result = a

proc isIntRangeOrLit(t: PType): bool =
  result = isIntRange(t) or isIntLit(t)

proc pickMinInt(n: PNode): BiggestInt =
  if n.kind in {nkIntLit..nkUInt64Lit}:
    result = n.intVal
  elif isIntLit(n.typ):
    result = n.typ.n.intVal
  elif isIntRange(n.typ):
    result = firstOrd(n.typ)
  else:
    internalError(n.info, "pickMinInt")

proc pickMaxInt(n: PNode): BiggestInt =
  if n.kind in {nkIntLit..nkUInt64Lit}:
    result = n.intVal
  elif isIntLit(n.typ):
    result = n.typ.n.intVal
  elif isIntRange(n.typ):
    result = lastOrd(n.typ)
  else:
    internalError(n.info, "pickMaxInt")

proc makeRange(typ: PType, first, last: BiggestInt): PType =
  let minA = min(first, last)
  let maxA = max(first, last)
  let lowerNode = newIntNode(nkIntLit, minA)
  if typ.kind == tyInt and minA == maxA:
    result = getIntLitType(lowerNode)
  else:
    var n = newNode(nkRange)
    addSon(n, lowerNode)
    addSon(n, newIntNode(nkIntLit, maxA))
    result = newType(tyRange, typ.owner)
    result.n = n
    addSonSkipIntLit(result, skipTypes(typ, {tyRange}))

proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
  var n = newNode(nkRange)
  addSon(n, newFloatNode(nkFloatLit, min(first.float, last.float)))
  addSon(n, newFloatNode(nkFloatLit, max(first.float, last.float)))
  result = newType(tyRange, typ.owner)
  result.n = n
  addSonSkipIntLit(result, skipTypes(typ, {tyRange}))

proc getIntervalType*(m: TMagic, n: PNode): PType =
  # Nimrod requires interval arithmetic for ``range`` types. Lots of tedious
  # work but the feature is very nice for reducing explicit conversions.
  result = n.typ

  template commutativeOp(opr: expr) {.immediate.} =
    let a = n.sons[1]
    let b = n.sons[2]
    if isIntRangeOrLit(a.typ) and isIntRangeOrLit(b.typ):
      result = makeRange(pickIntRange(a.typ, b.typ),
                         opr(pickMinInt(a), pickMinInt(b)),
                         opr(pickMaxInt(a), pickMaxInt(b)))

  template binaryOp(opr: expr) {.immediate.} =
    let a = n.sons[1]
    let b = n.sons[2]
    if isIntRange(a.typ) and b.kind in {nkIntLit..nkUInt64Lit}:
      result = makeRange(a.typ,
                         opr(pickMinInt(a), pickMinInt(b)),
                         opr(pickMaxInt(a), pickMaxInt(b)))

  case m
  of mUnaryMinusI, mUnaryMinusI64:
    let a = n.sons[1].typ
    if isIntRange(a):
      # (1..3) * (-1) == (-3.. -1)
      result = makeRange(a, 0|-|lastOrd(a), 0|-|firstOrd(a))
  of mUnaryMinusF64:
    let a = n.sons[1].typ
    if isFloatRange(a):
      result = makeRangeF(a, -getFloat(a.n.sons[1]),
                             -getFloat(a.n.sons[0]))
  of mAbsF64:
    let a = n.sons[1].typ
    if isFloatRange(a):
      # abs(-5.. 1) == (1..5)
      result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
                             abs(getFloat(a.n.sons[0])))
  of mAbsI, mAbsI64:
    let a = n.sons[1].typ
    if isIntRange(a):
      result = makeRange(a, `|abs|`(getInt(a.n.sons[1])),
                            `|abs|`(getInt(a.n.sons[0])))
  of mSucc:
    let a = n.sons[1].typ
    let b = n.sons[2].typ
    if isIntRange(a) and isIntLit(b):
      # (-5.. 1) + 6 == (-5 + 6)..(-1 + 6)
      result = makeRange(a, pickMinInt(n.sons[1]) |+| pickMinInt(n.sons[2]),
                            pickMaxInt(n.sons[1]) |+| pickMaxInt(n.sons[2]))
  of mPred:
    let a = n.sons[1].typ
    let b = n.sons[2].typ
    if isIntRange(a) and isIntLit(b):
      result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]),
                            pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2]))
  of mAddI, mAddI64, mAddU:
    commutativeOp(`|+|`)
  of mMulI, mMulI64, mMulU:
    commutativeOp(`|*|`)
  of mSubI, mSubI64, mSubU:
    binaryOp(`|-|`)
  of mBitandI, mBitandI64:
    # since uint64 is still not even valid for 'range' (since it's no ordinal<commit_msg>fixes #2448<commit_after>#
#
#           The Nim Compiler
#        (c) Copyright 2015 <NAME>
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# this module folds constants; used by semantic checking phase
# and evaluation phase

import
  strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times,
  nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
  commands, magicsys, saturate

proc getConstExpr*(m: PSym, n: PNode): PNode
  # evaluates the constant expression or returns nil if it is no constant
  # expression
proc evalOp*(m: TMagic, n, a, b, c: PNode): PNode
proc leValueConv*(a, b: PNode): bool
proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode
proc newStrNodeT*(strVal: string, n: PNode): PNode

# implementation

proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
  case skipTypes(n.typ, abstractVarRange).kind
  of tyInt:
    result = newIntNode(nkIntLit, intVal)
    result.typ = getIntLitType(result)
    # hrm, this is not correct: 1 + high(int) shouldn't produce tyInt64 ...
    #setIntLitType(result)
  of tyChar:
    result = newIntNode(nkCharLit, intVal)
    result.typ = n.typ
  else:
    result = newIntNode(nkIntLit, intVal)
    result.typ = n.typ
  result.info = n.info

proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
  result = newFloatNode(nkFloatLit, floatVal)
  if skipTypes(n.typ, abstractVarRange).kind == tyFloat:
    result.typ = getFloatLitType(result)
  else:
    result.typ = n.typ
  result.info = n.info

proc newStrNodeT(strVal: string, n: PNode): PNode =
  result = newStrNode(nkStrLit, strVal)
  result.typ = n.typ
  result.info = n.info

proc ordinalValToString*(a: PNode): string =
  # because $ has the param ordinal[T], `a` is not necessarily an enum, but an
  # ordinal
  var x = getInt(a)

  var t = skipTypes(a.typ, abstractRange)
  case t.kind
  of tyChar:
    result = $chr(int(x) and 0xff)
  of tyEnum:
    var n = t.n
    for i in countup(0, sonsLen(n) - 1):
      if n.sons[i].kind != nkSym: internalError(a.info, "ordinalValToString")
      var field = n.sons[i].sym
      if field.position == x:
        if field.ast == nil:
          return field.name.s
        else:
          return field.ast.strVal
    internalError(a.info, "no symbol for ordinal value: " & $x)
  else:
    result = $x

proc isFloatRange(t: PType): bool {.inline.} =
  result = t.kind == tyRange and t.sons[0].kind in {tyFloat..tyFloat128}

proc isIntRange(t: PType): bool {.inline.} =
  result = t.kind == tyRange and t.sons[0].kind in {
      tyInt..tyInt64, tyUInt8..tyUInt32}

proc pickIntRange(a, b: PType): PType =
  if isIntRange(a): result = a
  elif isIntRange(b): result = b
  else: result = a

proc isIntRangeOrLit(t: PType): bool =
  result = isIntRange(t) or isIntLit(t)

proc pickMinInt(n: PNode): BiggestInt =
  if n.kind in {nkIntLit..nkUInt64Lit}:
    result = n.intVal
  elif isIntLit(n.typ):
    result = n.typ.n.intVal
  elif isIntRange(n.typ):
    result = firstOrd(n.typ)
  else:
    internalError(n.info, "pickMinInt")

proc pickMaxInt(n: PNode): BiggestInt =
  if n.kind in {nkIntLit..nkUInt64Lit}:
    result = n.intVal
  elif isIntLit(n.typ):
    result = n.typ.n.intVal
  elif isIntRange(n.typ):
    result = lastOrd(n.typ)
  else:
    internalError(n.info, "pickMaxInt")

proc makeRange(typ: PType, first, last: BiggestInt): PType =
  let minA = min(first, last)
  let maxA = max(first, last)
  let lowerNode = newIntNode(nkIntLit, minA)
  if typ.kind == tyInt and minA == maxA:
    result = getIntLitType(lowerNode)
  else:
    var n = newNode(nkRange)
    addSon(n, lowerNode)
    addSon(n, newIntNode(nkIntLit, maxA))
    result = newType(tyRange, typ.owner)
    result.n = n
    addSonSkipIntLit(result, skipTypes(typ, {tyRange}))

proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
  var n = newNode(nkRange)
  addSon(n, newFloatNode(nkFloatLit, min(first.float, last.float)))
  addSon(n, newFloatNode(nkFloatLit, max(first.float, last.float)))
  result = newType(tyRange, typ.owner)
  result.n = n
  addSonSkipIntLit(result, skipTypes(typ, {tyRange}))

proc getIntervalType*(m: TMagic, n: PNode): PType =
  # Nimrod requires interval arithmetic for ``range`` types. Lots of tedious
  # work but the feature is very nice for reducing explicit conversions.
  result = n.typ

  template commutativeOp(opr: expr) {.immediate.} =
    let a = n.sons[1]
    let b = n.sons[2]
    if isIntRangeOrLit(a.typ) and isIntRangeOrLit(b.typ):
      result = makeRange(pickIntRange(a.typ, b.typ),
                         opr(pickMinInt(a), pickMinInt(b)),
                         opr(pickMaxInt(a), pickMaxInt(b)))

  template binaryOp(opr: expr) {.immediate.} =
    let a = n.sons[1]
    let b = n.sons[2]
    if isIntRange(a.typ) and b.kind in {nkIntLit..nkUInt64Lit}:
      result = makeRange(a.typ,
                         opr(pickMinInt(a), pickMinInt(b)),
                         opr(pickMaxInt(a), pickMaxInt(b)))

  case m
  of mUnaryMinusI, mUnaryMinusI64:
    let a = n.sons[1].typ
    if isIntRange(a):
      # (1..3) * (-1) == (-3.. -1)
      result = makeRange(a, 0|-|lastOrd(a), 0|-|firstOrd(a))
  of mUnaryMinusF64:
    let a = n.sons[1].typ
    if isFloatRange(a):
      result = makeRangeF(a, -getFloat(a.n.sons[1]),
                             -getFloat(a.n.sons[0]))
  of mAbsF64:
    let a = n.sons[1].typ
    if isFloatRange(a):
      # abs(-5.. 1) == (1..5)
      if a.n[0].floatVal <= 0.0:
        result = makeRangeF(a, 0.0, abs(getFloat(a.n.sons[0])))
      else:
        result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
                               abs(getFloat(a.n.sons[0])))
  of mAbsI, mAbsI64:
    let a = n.sons[1].typ
    if isIntRange(a):
      if a.n[0].intVal <= 0:
        result = makeRange(a, 0, `|abs|`(getInt(a.n.sons[0])))
      else:
        result = makeRange(a, `|abs|`(getInt(a.n.sons[1])),
                              `|abs|`(getInt(a.n.sons[0])))
  of mSucc:
    let a = n.sons[1].typ
    let b = n.sons[2].typ
    if isIntRange(a) and isIntLit(b):
      # (-5.. 1) + 6 == (-5 + 6)..(-1 + 6)
      result = makeRange(a, pickMinInt(n.sons[1]) |+| pickMinInt(n.sons[2]),
                            pickMaxInt(n.sons[1]) |+| pickMaxInt(n.sons[2]))
  of mPred:
    let a = n.sons[1].typ
    let b = n.sons[2].typ
    if isIntRange(a) and isIntLit(b):
      result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]),
                            pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2]))
  of mAddI, mAddI64, mAddU:
    commutativeOp(`|+|`)
  of mMulI, mMulI64, mMulU:
    commutativeOp(`|*|`)
  of mSubI, mSubI64, mSubU:
    binaryOp(`|-|`)
  of mBitandI, mBitandI64:
    # since uint64 is still not even valid for 'range' (since it's no ordinal
{'fraction_non_alphanumeric': 0.14211502782931354, 'fraction_numerical': 0.014100185528756958, 'mean_word_length': 2.9403508771929823, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, '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': '3176929', 'n_tokens_mistral': 5347, 'n_tokens_neox': 5097, 'n_words': 1248}