summary refs log tree commit diff stats
path: root/tests/showoff/tdrdobbs_examples.nim
blob: c61e177dc3cf05b228d698e1cbffe4c034aa5612 (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: untyped): untyped =
    newDotExpr(current, newIdentNode(astToStr(field)))

  template `==@`(n, pattern: untyped): untyped =
    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: untyped): bool =
  result = matchAgainst(n, pattern)

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

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

                            
   
               
              


                                         
                 

             
           
 
 

                                                    
                                                                              



                                                                 
       
                         



                                            
          

                   
                      

 
                                  




                                                                    

                                               







                                                             
                        
                   
//: A simple test harness. To create new tests, define functions starting with
//: 'test_'. To run all tests so defined, run:
//:   $ ./bootstrap test
//:
//: Every layer should include tests, and can reach into previous layers.
//: However, it seems like a good idea never to reach into tests from previous
//: layers. Every test should be a contract that always passes as originally
//: written, regardless of any later layers. Avoid writing 'temporary' tests
//: that are only meant to work until some layer.

:(before "End Types")
typedef void (*test_fn)(void);
:(before "Globals")
// move a global ahead into types that we can't generate an extern declaration for
const test_fn Tests[] = {
  #include "test_list"  // auto-generated; see 'build*' scripts
};

:(before "End Globals")
bool Run_tests = false;
bool Passed = true;  // set this to false inside any test to indicate failure

:(before "End Includes")
#define CHECK(X) \
  if (Passed && !(X)) { \
    cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << '\n'; \
    Passed = false; \
    return;  /* Currently we stop at the very first failure. */ \
  }

#define CHECK_EQ(X, Y) \
  if (Passed && (X) != (Y)) { \
    cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << " == " << #Y << '\n'; \
    cerr << "  got " << (X) << '\n';  /* BEWARE: multiple eval */ \
    Passed = false; \
    return;  /* Currently we stop at the very first failure. */ \
  }

:(before "End Reset")
Passed = true;

:(before "End Commandline Parsing")
if (argc > 1 && is_equal(argv[1], "test")) {
  Run_tests = true;  --argc;  ++argv;  // shift 'test' out of commandline args
}

:(before "End Main")
if (Run_tests) {
  // Test Runs
  // we run some tests and then exit; assume no state need be maintained afterward

  long num_failures = 0;
  // End Test Run Initialization
  time_t t;  time(&t);
  cerr << "C tests: " << ctime(&t);
  for (size_t i=0;  i < sizeof(Tests)/sizeof(Tests[0]);  ++i) {
//?     cerr << "running " << Test_names[i] << '\n';
    run_test(i);
    if (Passed) cerr << '.';
    else ++num_failures;
  }
  cerr << '\n';
  // End Tests
  if (num_failures > 0) {
    cerr << num_failures << " failure"
         << (num_failures > 1 ? "s" : "")
         << '\n';
    return 1;
  }
  return 0;
}

:(after "End Main")
//: Raise other unrecognized sub-commands as errors.
//: We couldn't do this until now because we want `./bootstrap test` to always
//: succeed, no matter how many layers are included in the build.
cerr << "nothing to do\n";
return 1;

:(code)
void run_test(size_t i) {
  if (i >= sizeof(Tests)/sizeof(Tests[0])) {
    cerr << "no test " << i << '\n';
    return;
  }
  reset();
  // End Test Setup
  (*Tests[i])();
  // End Test Teardown
}

//: Convenience: run a single test
:(before "Globals")
// Names for each element of the 'Tests' global, respectively.
const string Test_names[] = {
  #include "test_name_list"  // auto-generated; see 'build*' scripts
};
:(after "Test Runs")
string maybe_single_test_to_run = argv[argc-1];
for (size_t i=0;  i < sizeof(Tests)/sizeof(Tests[0]);  ++i) {
  if (Test_names[i] == maybe_single_test_to_run) {
    run_test(i);
    if (Passed) cerr << ".\n";
    return 0;
  }
}

:(before "End Includes")
#include <stdlib.h>