summary refs log tree commit diff stats
path: root/tests/showoff/tdrdobbs_examples.nim
blob: 78f7113254c607c77cf3eb12997bbb9379bc3310 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
discard """
  output: '''108
11 -1 1936
0.4
true
truefalse'''
"""

proc `++`(x: var int; y: int = 1; z: int = 0) =
  x = x + y + z

var g = 70
++g
g ++ 7
g.`++`(10, 20)
echo g


#let lv = stdin.readline
#var vv = stdin.readline
#vv = "abc" # valid, reassignment allowed
#lv = "abc" # fails at compile time

#proc square(x: int): int = x*x

template square(x: int): int =
  # ensure 'x' is only evaluated once:
  let y = x
  y * y

proc mostSignificantBit(n: int): int =
  # naive algorithm:
  var n = n
  while n != 0:
    n = n shr 1
    result += 1
  result -= 1

const msb3999 = mostSignificantBit(3999)

echo msb3999, " ", mostSignificantBit(0), " ", square(44)

proc filter[T](a: openarray[T], predicate: proc (x: T): bool): seq[T] =
  result = @[] # @[] constructs the empty seq
  for x in a:
    if predicate(x): result.add(x)

proc map[T, S](a: openarray[T], fn: proc (x: T): S): seq[S] =
  newSeq(result, a.len)
  for i in 0 .. <a.len: result[i] = fn(a[i])


type
  FormulaKind = enum
    fkVar,        ## element is a variable like 'X'
    fkLit,        ## element is a literal like 0.1
    fkAdd,        ## element is an addition operation
    fkMul,        ## element is a multiplication operation
    fkExp         ## element is an exponentiation operation

type
  Formula = ref object
    case kind: FormulaKind
    of fkVar: name: string
    of fkLit: value: float
    of fkAdd, fkMul, fkExp: left, right: Formula

from math import pow

proc evaluate(n: Formula, varToVal: proc (name: string): float): float =
  case n.kind
  of fkVar: varToVal(n.name)
  of fkLit: n.value
  of fkAdd: evaluate(n.left, varToVal) + evaluate(n.right, varToVal)
  of fkMul: evaluate(n.left, varToVal) * evaluate(n.right, varToVal)
  of fkExp: pow(evaluate(n.left, varToVal), evaluate(n.right, varToVal))

echo evaluate(Formula(kind: fkLit, value: 0.4), nil)

proc isPolyTerm(n: Formula): bool =
  n.kind == fkMul and n.left.kind == fkLit and (let e = n.right;
    e.kind == fkExp and e.left.kind == fkVar and e.right.kind == fkLit)

proc isPolynomial(n: Formula): bool =
  isPolyTerm(n) or
    (n.kind == fkAdd and isPolynomial(n.left) and isPolynomial(n.right))

let myFormula = Formula(kind: fkMul,
                        left: Formula(kind: fkLit, value: 2.0),
                        right: Formula(kind: fkExp,
                          left: Formula(kind: fkVar, name: "x"),
                          right: Formula(kind: fkLit, value: 5.0)))

echo isPolyTerm(myFormula)

proc pat2kind(pattern: string): FormulaKind =
  case pattern
  of "^": fkExp
  of "*": fkMul
  of "+": fkAdd
  of "x": fkVar
  of "c": fkLit
  else:   fkVar # no error reporting for reasons of simplicity

import macros

proc matchAgainst(n, pattern: NimNode): NimNode {.compileTime.} =
  template `@`(current, field: expr): expr =
    newDotExpr(current, newIdentNode(astToStr(field)))

  template `==@`(n, pattern: expr): expr =
    newCall("==", n@kind, newIdentNode($pat2kind($pattern.ident)))

  case pattern.kind
  of CallNodes:
    result = newCall("and",
      n ==@ pattern[0],
      matchAgainst(n@left, pattern[1]))
    if pattern.len == 3:
      result = newCall("and", result.copy,
        matchAgainst(n@right, pattern[2]))
  of nnkIdent:
    result = n ==@ pattern
  of nnkPar:
    result = matchAgainst(n, pattern[0])
  else:
    error "invalid pattern"

macro `=~` (n: Formula, pattern: expr): bool =
  result = matchAgainst(n, pattern)

proc isPolyTerm2(n: Formula): bool = n =~ c * x^c

echo isPolyTerm2(myFormula), isPolyTerm2(Formula(kind: fkLit, value: 0.7))