1 | # Evaluate a user defined function
|
2 |
|
3 |
|
4 | #define F p3 # F is the function body
|
5 | #define A p4 # A is the formal argument list
|
6 | #define B p5 # B is the calling argument list
|
7 | #define S p6 # S is the argument substitution list
|
8 |
|
9 | # we got here because there was a function invocation and
|
10 | # it's not been parsed (and consequently tagged) as any
|
11 | # system function.
|
12 | # So we are dealing with another function.
|
13 | # The function could be actually defined, or not yet,
|
14 | # so we'll deal with both cases.
|
15 |
|
16 | ### d =====================================================================
|
17 |
|
18 | Tags
|
19 | ----
|
20 | scripting, JS, internal, treenode, general concept
|
21 |
|
22 | Parameters
|
23 | ----------
|
24 | f,x
|
25 |
|
26 | General description
|
27 | -------------------
|
28 | Returns the partial derivative of f with respect to x. x can be a vector e.g. [x,y].
|
29 |
|
30 | ###
|
31 |
|
32 | Eval_user_function = ->
|
33 |
|
34 | # Use "derivative" instead of "d" if there is no user function "d"
|
35 |
|
36 | if DEBUG then console.log "Eval_user_function evaluating: " + car(p1)
|
37 | if (car(p1) == symbol(SYMBOL_D) && get_binding(symbol(SYMBOL_D)) == symbol(SYMBOL_D))
|
38 | Eval_derivative()
|
39 | return
|
40 |
|
41 | # normally car(p1) is a symbol with the function name
|
42 | # but it could be something that has to be
|
43 | # evaluated to get to the function definition instead
|
44 | # (e.g. the function is an element of an array)
|
45 | # so we do an eval to sort it all out.
|
46 | push(car(p1))
|
47 | Eval()
|
48 |
|
49 | # we expect to find either the body and
|
50 | # formula arguments, OR, if the function
|
51 | # has not been defined yet, then the
|
52 | # function will just contain its own name, as
|
53 | # all undefined variables do.
|
54 | bodyAndFormalArguments = pop()
|
55 |
|
56 | p3 = car(cdr(bodyAndFormalArguments)) # p3 is function body F
|
57 | # p4 is the formal argument list
|
58 | # that is also contained here in the FUNCTION node
|
59 | p4 = car(cdr(cdr(bodyAndFormalArguments)))
|
60 |
|
61 | p5 = cdr(p1); # p5 is B
|
62 |
|
63 | # example:
|
64 | # f(x) = x+2
|
65 | # then:
|
66 | # p3.toString() = "x + 2"
|
67 | # p4 = x
|
68 | # p5 = 2
|
69 |
|
70 | # Undefined function?
|
71 | if (bodyAndFormalArguments == car(p1)) # p3 is F
|
72 | h = tos
|
73 | push(bodyAndFormalArguments); # p3 is F
|
74 | p1 = p5; # p5 is B
|
75 | while (iscons(p1))
|
76 | push(car(p1))
|
77 | Eval()
|
78 | p1 = cdr(p1)
|
79 | list(tos - h)
|
80 | return
|
81 |
|
82 | # Create the argument substitution list p6(S)
|
83 |
|
84 | p1 = p4; # p4 is A
|
85 | p2 = p5; # p5 is B
|
86 | h = tos
|
87 | while (iscons(p1) && iscons(p2))
|
88 | push(car(p1))
|
89 | push(car(p2))
|
90 | # why explicitly Eval the parameters when
|
91 | # the body of the function is
|
92 | # evalled anyways? Commenting it out. All tests pass...
|
93 | #Eval()
|
94 | p1 = cdr(p1)
|
95 | p2 = cdr(p2)
|
96 |
|
97 | list(tos - h)
|
98 | p6 = pop(); # p6 is S
|
99 |
|
100 | # Evaluate the function body
|
101 |
|
102 | push(p3); # p3 is F
|
103 | if (iscons(p6)) # p6 is S
|
104 | push(p6); # p6 is S
|
105 | rewrite_args()
|
106 | #console.log "rewritten body: " + stack[tos-1]
|
107 | Eval()
|
108 |
|
109 | # Rewrite by expanding symbols that contain args
|
110 |
|
111 | rewrite_args = ->
|
112 | n = 0
|
113 | save()
|
114 |
|
115 | # subst. list which is a list
|
116 | # where each consecutive pair
|
117 | # is what needs to be substituted and with what
|
118 | p2 = pop();
|
119 | #console.log "subst. list " + p2
|
120 |
|
121 | # expr to substitute in i.e. the
|
122 | # function body
|
123 | p1 = pop();
|
124 | #console.log "expr: " + p1
|
125 |
|
126 | if (istensor(p1))
|
127 | n = rewrite_args_tensor()
|
128 | restore()
|
129 | return n
|
130 |
|
131 | if (iscons(p1))
|
132 | h = tos
|
133 | if (car(p1) == car(p2))
|
134 | # rewrite a function in
|
135 | # the body with the one
|
136 | # passed from the paramaters
|
137 | push_symbol(EVAL)
|
138 | push(car(cdr(p2)));
|
139 | list(2)
|
140 | else
|
141 | # if there is no match
|
142 | # then no substitution necessary
|
143 | push(car(p1));
|
144 |
|
145 | # continue recursively to
|
146 | # rewrite the rest of the body
|
147 | p1 = cdr(p1)
|
148 | while (iscons(p1))
|
149 | push(car(p1))
|
150 | push(p2)
|
151 | n += rewrite_args()
|
152 | p1 = cdr(p1)
|
153 | list(tos - h)
|
154 | restore()
|
155 | return n
|
156 |
|
157 | # ground cases here
|
158 | # (apart from function name which has
|
159 | # already been substituted as it's in the head
|
160 | # of the cons)
|
161 | # -----------------
|
162 |
|
163 | # If not a symbol then no
|
164 | # substitution to be done
|
165 | if (!issymbol(p1))
|
166 | push(p1)
|
167 | restore()
|
168 | return 0
|
169 |
|
170 | # Here we are in a symbol case
|
171 | # so we need to substitute
|
172 |
|
173 | # Check if there is a direct match
|
174 | # of symbols right away
|
175 | p3 = p2
|
176 | while (iscons(p3))
|
177 | if (p1 == car(p3))
|
178 | push(cadr(p3))
|
179 | restore()
|
180 | return 1
|
181 | p3 = cddr(p3)
|
182 |
|
183 | # Get the symbol's content, if _that_
|
184 | # matches then do the substitution
|
185 | p3 = get_binding(p1)
|
186 | push(p3)
|
187 | if (p1 != p3)
|
188 | push(p2); # subst. list
|
189 | n = rewrite_args()
|
190 | if (n == 0)
|
191 | pop()
|
192 | push(p1); # restore if not rewritten with arg
|
193 |
|
194 | restore()
|
195 | return n
|
196 |
|
197 | rewrite_args_tensor = ->
|
198 | n = 0
|
199 | i = 0
|
200 | push(p1)
|
201 | copy_tensor()
|
202 | p1 = pop()
|
203 | for i in [0...p1.tensor.nelem]
|
204 | push(p1.tensor.elem[i])
|
205 | push(p2)
|
206 | n += rewrite_args()
|
207 | p1.tensor.elem[i] = pop()
|
208 |
|
209 | check_tensor_dimensions p1
|
210 |
|
211 | push(p1)
|
212 | return n
|