-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparse.php
More file actions
executable file
·301 lines (293 loc) · 10.8 KB
/
parse.php
File metadata and controls
executable file
·301 lines (293 loc) · 10.8 KB
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
<?php
/**
* IPPcode22 parser
* @author Aleksandr Verevkin (xverev00), VUT FIT IPP 2021/2022
*/
// setting to show errors on stderr
ini_set('display_errors', 'stderr');
/**
* Class with utilities
*/
final class Utils
{
// errors
const PARAM_ERR = 10;
const HEADER_ERR = 21;
const OPCODE_ERR = 22;
const LEX_SYN_ERR = 23;
/**
* Print error message and exit program with given return code
*
* @param int $err error code
* @param string $msg error message
*/
function error(int $err, string $msg) {
fwrite(STDERR, "Error: " . $msg . "\n");
exit($err);
}
/**
* Check if symbol is variable or constant representation
*
* @param string $operand symbol to be checked
* @param DOMDocument $xml output XML document
* @param int $arg_num number of argument
* @return DOMElement new argument element
*/
function check_symb(string $operand, DOMDocument $xml, int $arg_num) : DOMElement {
$type = explode("@", $operand, 2);
if ($type[0] == "GF" || $type[0] == "LF" || $type[0] == "TF") {
$arg = $xml->createElement("arg$arg_num", htmlspecialchars($operand));
$arg->setAttribute("type", "var");
} else {
$arg = $xml->createElement("arg$arg_num", htmlspecialchars($type[1]));
$arg->setAttribute("type", $type[0]);
}
return $arg;
}
/**
* Check if given program arguments are supported
*
* @param int $argc count of given arguments
* @param array $argv array of given arguments
*/
function check_args(int $argc, array $argv) {
switch ($argc) {
// program don't have additional arguments
case 1:
break;
// program has 1 argument
case 2:
// check if it's --help argument
if ($argv[1] == "--help") {
echo HELP;
exit;
// else return error
} else {
$this->error(UTILS::PARAM_ERR, "unknown parameter");
}
// program has more than 1 additional arguments -> error
default:
$this->error(UTILS::PARAM_ERR, "wrong amount of parameters");
}
}
}
// help message
const HELP = <<<EOD
usage: php8.1 parse.php < file [--help]
The script of filter type reads the source code in IPPcode22,
checks the lexical and syntactic correctness of the code and
prints to standard output XML representation of the program.
options:
--help prints short help message\n
EOD;
// regex representation of identifiers
const SPEC_CHAR = "_\-$&%*!?";
const HEADER = "%^\.IPPcode22$%i";
const FRAMES = "(GF|LF|TF)";
const NIL = "(nil@nil)";
const BOOL = "(bool@(true|false))";
const DECIMAL = "([0-9]+)";
const OCTAL = "(0[oO]?[0-7]+)";
const HEXADECIMAL = "(0[xX][0-9a-fA-F]+)";
const INT = "(int@(\+|\-)?(" . DECIMAL . "|" . OCTAL . "|" . HEXADECIMAL . "))";
const STRING = "((string)@((\\\\[0-9]{3})|[^\s\#\\\\])*)";
const TYPE = "~^(bool|int|string)$~";
const CONSTANT = "(" . NIL . "|" . BOOL . "|" . INT . "|" . STRING . ")";
const IDENTIFIER_SOLO = "([A-Za-z" . SPEC_CHAR . "][0-9A-Za-z" . SPEC_CHAR . "]*)";
const IDENTIFIER = "~^" . IDENTIFIER_SOLO . "$~";
const VARIABLE_SOLO = "(" . FRAMES . "@" . IDENTIFIER_SOLO . ")";
const VARIABLE = "~^" . VARIABLE_SOLO . "$~";
const SYMBOL = "~^(" . CONSTANT . "|" . VARIABLE_SOLO . ")$~";
// create Utils instance for latter use
$utils = new Utils();
// check arguments
$utils->check_args($argc, $argv);
// create output xml document
$xml = new DOMDocument("1.0", "UTF-8");
$xml->formatOutput = true;
// create header element
$xml_program = $xml->createElement("program");
$xml_program = $xml->appendChild($xml_program);
$xml_lang = $xml->createAttribute("language");
$xml_lang->value = "IPPcode22";
$xml_program->appendChild($xml_lang);
$ln = 0;
$header = false;
// go through input file, line by line
while ($line = fgets(STDIN)) {
# cut line from possible comments
$line = trim(explode("#", $line)[0]);
if ($line == "") {
continue;
}
// check input for .ippcode22 header
if (!$header) {
if(preg_match(HEADER, $line)) {
$header = true;
continue;
} else {
$utils->error(UTILS::HEADER_ERR, "input file should start with '.IPPcode22' header");
}
}
// split line into an operand and operands
$line_args = preg_split("/\s+/", $line);
$ln++;
$xmlInstruction = $xml->createElement("instruction");
$xmlInstruction->setAttribute("order", $ln);
$xmlInstruction->setAttribute("opcode", strtoupper($line_args[0]));
// check first line argument on operand
switch (strtoupper($line_args[0])) {
// no operands
case "CREATEFRAME":
case "PUSHFRAME":
case "POPFRAME":
case "RETURN":
case "BREAK":
if (count($line_args) != 1) {
$utils->error(UTILS::LEX_SYN_ERR, "wrong amount of operands");
}
break;
// <var>
case "DEFVAR":
case "POPS":
if (count($line_args) != 2) {
$utils->error(UTILS::LEX_SYN_ERR, "wrong amount of operands");
}
if (!preg_match(VARIABLE, $line_args[1])) {
$utils->error(UTILS::LEX_SYN_ERR, "variable identifier");
}
$arg1 = $xml->createElement("arg1", htmlspecialchars($line_args[1]));
$arg1->setAttribute("type", "var");
$xmlInstruction->appendChild($arg1);
break;
// <label>
case "CALL":
case "LABEL":
case "JUMP":
if (count($line_args) != 2) {
$utils->error(UTILS::LEX_SYN_ERR, "wrong amount of operands");
}
if (!preg_match(IDENTIFIER, $line_args[1])) {
$utils->error(UTILS::LEX_SYN_ERR, "label identifier");
}
$arg1 = $xml->createElement("arg1", htmlspecialchars($line_args[1]));
$arg1->setAttribute("type", "label");
$xmlInstruction->appendChild($arg1);
break;
// <symb>
case "PUSHS":
case "WRITE":
case "EXIT":
case "DPRINT":
if (count($line_args) != 2) {
$utils->error(UTILS::LEX_SYN_ERR, "wrong amount of operands");
}
if (!preg_match(SYMBOL, $line_args[1])) {
$utils->error(UTILS::LEX_SYN_ERR, "symbol identifier");
}
$arg1 = $utils->check_symb($line_args[1], $xml, 1);
$xmlInstruction->appendChild($arg1);
break;
// <var> <symb>
case "MOVE":
case "INT2CHAR":
case "STRLEN":
case "TYPE":
case "NOT":
if (count($line_args) != 3) {
$utils->error(UTILS::LEX_SYN_ERR, "wrong amount of operands");
}
if (!preg_match(VARIABLE, $line_args[1])) {
$utils->error(UTILS::LEX_SYN_ERR, "variable identifier");
}
if (!preg_match(SYMBOL, $line_args[2])) {
$utils->error(UTILS::LEX_SYN_ERR, "symbol identifier");
}
$arg1 = $xml->createElement("arg1", htmlspecialchars($line_args[1]));
$arg1->setAttribute("type", "var");
$arg2 = $utils->check_symb($line_args[2], $xml, 2);
$xmlInstruction->appendChild($arg1);
$xmlInstruction->appendChild($arg2);
break;
// <var> <type>
case "READ":
if (count($line_args) != 3) {
$utils->error(UTILS::LEX_SYN_ERR, "wrong amount of operands");
}
if (!preg_match(VARIABLE, $line_args[1])) {
$utils->error(UTILS::LEX_SYN_ERR, "variable identifier");
}
if (!preg_match(TYPE, $line_args[2])) {
$utils->error(UTILS::LEX_SYN_ERR, "type identifier");
}
$arg1 = $xml->createElement("arg1", htmlspecialchars($line_args[1]));
$arg1->setAttribute("type", "var");
$arg2 = $xml->createElement("arg2", htmlspecialchars($line_args[2]));
$arg2->setAttribute("type", "type");
$xmlInstruction->appendChild($arg1);
$xmlInstruction->appendChild($arg2);
break;
// <var> <symb1> <symb2>
case "ADD":
case "SUB":
case "MUL":
case "IDIV":
case "LT":
case "GT":
case "EQ":
case "AND":
case "OR":
case "STRI2INT":
case "CONCAT":
case "GETCHAR":
case "SETCHAR":
if (count($line_args) != 4) {
$utils->error(UTILS::LEX_SYN_ERR, "wrong amount of operands");
}
if (!preg_match(VARIABLE, $line_args[1])) {
$utils->error(UTILS::LEX_SYN_ERR, "variable identifier");
}
if (!preg_match(SYMBOL, $line_args[2])) {
$utils->error(UTILS::LEX_SYN_ERR, "symbol identifier");
}
if (!preg_match(SYMBOL, $line_args[3])) {
$utils->error(UTILS::LEX_SYN_ERR, "symbol identifier");
}
$arg1 = $xml->createElement("arg1", htmlspecialchars($line_args[1]));
$arg1->setAttribute("type", "var");
$arg2 = $utils->check_symb($line_args[2], $xml, 2);
$arg3 = $utils->check_symb($line_args[3], $xml, 3);
$xmlInstruction->appendChild($arg1);
$xmlInstruction->appendChild($arg2);
$xmlInstruction->appendChild($arg3);
break;
// <label> <symb1> <symb2>
case "JUMPIFEQ":
case "JUMPIFNEQ":
if (count($line_args) != 4) {
$utils->error(UTILS::LEX_SYN_ERR, "wrong amount of operands");
}
if (!preg_match(IDENTIFIER, $line_args[1])) {
$utils->error(UTILS::LEX_SYN_ERR, "label identifier");
}
if (!preg_match(SYMBOL, $line_args[2])) {
$utils->error(UTILS::LEX_SYN_ERR, "symbol identifier");
}
if (!preg_match(SYMBOL, $line_args[3])) {
$utils->error(UTILS::LEX_SYN_ERR, "symbol identifier");
}
$arg1 = $xml->createElement("arg1", htmlspecialchars($line_args[1]));
$arg1->setAttribute("type", "label");
$arg2 = $utils->check_symb($line_args[2], $xml, 2);
$arg3 = $utils->check_symb($line_args[3], $xml, 3);
$xmlInstruction->appendChild($arg1);
$xmlInstruction->appendChild($arg2);
$xmlInstruction->appendChild($arg3);
break;
default:
$utils->error(UTILS::OPCODE_ERR, "unknown opcode");
}
$xml_program->appendChild($xmlInstruction);
}
echo $xml->saveXML();
exit;