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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
.help prcode
Parser code generator
The parser generates code for all the equations in the symbol table in
Reverse Polish Notation (RPN). Under this notation, operands are pushed
into a stack, until an operation comes up. The operation takes as many stack
places (from the stack top) it needs as arguments, and places the result
in the top of the stack. The final result will be always in the top of
the stack.
.sp
In the current implemantation, arguments can be either constants, catalog
variables, observational variables, parameters, and equations (extinction
and transformation). The latter argument is a recursive call to other equation.
Operations can take only one or two stack places as arguments.
.sp
The instructions generated by the parser can be of one or two words.
The first word is always an integer, and identifies the operation to be
performed. If the operation is a "push" of a quantity into the stack,
the second word must contain the value (real) or index (integer) of
the quantity. The real value is used for constants, and index is used
for variables and parameters.
.sp
The RPN instructions are stored into memory as a dinamically allocated
buffer of structure type (TY_STRUCT), since it may contain integer and
real numbers.
.sp
The procedures provided here are called by the parser, to generate code
for each expression (formula) found. The following entry points are
defined:
.nf
pr_calloc () Allocate space for temp. code buffer
pr_cfree () Deallocate code buffer
pr_cinit () Begin code generation
pr_cend (ptr) End code generation
pr_cgen (token, id, value) Generate code for parser token
pointer pr_cput (code, len) Create and copy code buffer
.endhelp
include "../lib/lexer.h"
include "../lib/parser.h"
include "../lib/prtoken.h"
include "../lib/preval.h"
# PR_CALLOC - Allocate code buffer
procedure pr_calloc ()
include "prcode.com"
begin
# Allocate code buffer
call malloc (code, LEN_CODE, TY_STRUCT)
end
# PR_CFREE - Free code buffer
procedure pr_cfree ()
include "prcode.com"
begin
# Free buffer in common
call mfree (code, TY_STRUCT)
end
# PR_CINIT - Start code generation
procedure pr_cinit ()
include "prcode.com"
begin
# Set next free instruction be the first
cp = 1
end
# PR_CEND - Finish code generation
procedure pr_cend (ptr)
pointer ptr # lexer symbol pointer
include "prcode.com"
begin
# Put the end-of-code marker in
# the next instruction
Memi[code + cp - 1] = PEV_EOC
# Set code length
LEX_CLEN (ptr) = cp
# Copy the code buffer into the lexer symbol
call amovi (Memi[code], Memi[LEX_CODE (ptr)], cp)
# Reset code counter to the first
# instruction
cp = 1
end
# PR_CGEN - Generate RPN code.
procedure pr_cgen (token, id, value)
int token # lexer token
char id[ARB] # lexer identifier
real value # lexer value
char aux[SZ_LINE]
int offset, sym, type
include "prcode.com"
int pr_geti(), pr_gsymi()
pointer pr_getsym()
begin
# Generate code for the current instruction according
# with token value returned by the lexer
switch (token) {
case IDENTIFIER:
# Find the identifier in the symbol table, and store
# the appropiate instruction code, and number, according
# with the symbol type. If the identifier is not found in
# the symbol table no code is generated, and no error
# action is taken. The latter is to avoid stopping the
# parser and allow some error recovery.
# Also compute an offset to add later to the symbol number.
# In catalog variables an offset is necessary because the
# expression evaluator has only ONE table with variable
# values, with catalog variables at the end.
sym = pr_getsym (id)
if (!IS_INDEFI (sym)) {
# Get symbol type and take action acordingly
type = pr_gsymi (sym, PSYMTYPE)
switch (type) {
case PTY_OBSVAR:
Memi[code + cp - 1] = PEV_OBSVAR
offset = 0
case PTY_CATVAR:
Memi[code + cp - 1] = PEV_CATVAR
offset = pr_geti (NOBSVARS)
case PTY_FITPAR, PTY_CONST:
Memi[code + cp - 1] = PEV_PARAM
offset = 0
case PTY_SETEQ:
Memi[code + cp - 1] = PEV_SETEQ
offset = 0
case PTY_EXTEQ:
Memi[code + cp - 1] = PEV_EXTEQ
offset = 0
case PTY_TRNEQ:
Memi[code + cp - 1] = PEV_TRNEQ
offset = 0
default:
call sprintf (aux, SZ_LINE,
"pr_cgen: Illegal symbol type (%d)")
call pargi (type)
call error (0, aux)
}
# Store symbol number, plus the offset, in next instruction
cp = cp + 1
Memi[code + cp - 1] = pr_gsymi (sym, PSYMNUM) + offset
}
case INUMBER, RNUMBER:
# Store number instruction code and the number
# value in the next instruction
Memi[code + cp - 1] = PEV_NUMBER
cp = cp + 1
Memr[code + cp - 1] = value
case UPLUS:
Memi[code + cp - 1] = PEV_UPLUS
case UMINUS:
Memi[code + cp - 1] = PEV_UMINUS
case PLUS:
Memi[code + cp - 1] = PEV_PLUS
case MINUS:
Memi[code + cp - 1] = PEV_MINUS
case STAR:
Memi[code + cp - 1] = PEV_STAR
case SLASH:
Memi[code + cp - 1] = PEV_SLASH
case EXPON:
Memi[code + cp - 1] = PEV_EXPON
case F_ABS:
Memi[code + cp - 1] = PEV_ABS
case F_ACOS:
Memi[code + cp - 1] = PEV_ACOS
case F_ASIN:
Memi[code + cp - 1] = PEV_ASIN
case F_ATAN:
Memi[code + cp - 1] = PEV_ATAN
case F_COS:
Memi[code + cp - 1] = PEV_COS
case F_EXP:
Memi[code + cp - 1] = PEV_EXP
case F_LOG:
Memi[code + cp - 1] = PEV_LOG
case F_LOG10:
Memi[code + cp - 1] = PEV_LOG10
case F_SIN:
Memi[code + cp - 1] = PEV_SIN
case F_SQRT:
Memi[code + cp - 1] = PEV_SQRT
case F_TAN:
Memi[code + cp - 1] = PEV_TAN
default:
call error (0, "pr_cgen: Illegal instruction")
}
# Count codes, and check boundaries. Reserve at
# least three places: two for the next instruction,
# and one for the end-of-code marker
cp = cp + 1
if (cp > LEN_CODE - 2)
call error (0, "pr_cgen: Too much code")
end
# PR_CPUT - Allocate space for a code buffer, copy given code bufer into
# it, and return pointer to it
pointer procedure pr_cput (code, len)
pointer code # code buffer
int len # code length
pointer aux
begin
# Check pointer
if (code == NULL)
call error (0, "pr_cput: Null code pointer")
# Allocate memory for code and copy code buffer into it
call malloc (aux, len, TY_STRUCT)
call amovi (Memi[code], Memi[aux], len)
# Return new buffer pointer
return (aux)
end
|