structure.pl 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. use strict;
  2. use warnings;
  3. # whitespace
  4. my $WS = " \n";
  5. # game structure object :
  6. # % structures : hash containing all the structures in the file
  7. # each structure is an array with all its data
  8. # @ pointers_stack : stack of arrays containing all the pointers inside the structures
  9. # the last array in the stack, is the current array
  10. # the last pointer in the array is the current pointer
  11. # pointer format: [ "struct_name", index ]
  12. # reads the `structure` file and returns its elements one-by-one
  13. # TODO - remove
  14. my @counters;
  15. my $counter;
  16. my %struct = ();
  17. # opens file
  18. sub game_structure_open {
  19. # get structure filename and open it
  20. my $filename = shift;
  21. open(my $in, "<", $filename) or die "structure: $filename: $!";
  22. # structure object to return
  23. my $game_structure = {};
  24. # contains the last created structure, any new element is part of this struct
  25. my $last_struct;
  26. # count characters in each line, in case of error
  27. my $line_chars;
  28. my $current_char;
  29. # parse line-by-line
  30. while (<$in>) {
  31. # get total line characters and set current character to first character
  32. $line_chars = length;
  33. $current_char = 1;
  34. # parse until line is empty
  35. while (length > 0) {
  36. # each iteration should parse at least 1 character
  37. # calculate current character and amount of characters left
  38. $current_char += $line_chars -length;
  39. $line_chars = length;
  40. # whitespace found - remove it
  41. if (/^[$WS]+/) {
  42. s/^[$WS]+//;
  43. }
  44. # new structure found - add it to the object
  45. elsif (/^\./) {
  46. # get name
  47. s/^\.([^$WS]+)//;
  48. # create new structure of data
  49. $last_struct = [];
  50. # save structure to object
  51. $game_structure->{"structures"}{$1} = $last_struct;
  52. }
  53. # strings
  54. elsif (/^\^/) {
  55. # remove string
  56. s/^(\^)//;
  57. # add it to array - default at 1 quantity
  58. push @$last_struct, [$1, 1];
  59. }
  60. # numbers
  61. elsif (/^\$/) {
  62. # remove number symbol
  63. s/^(\$)//;
  64. # add it to array
  65. push @$last_struct, [$1, 1];
  66. }
  67. # another structure
  68. elsif (/^@/) {
  69. # remove structure symbol and its name
  70. s/^(@[a-zA-Z]+)//;
  71. # oush it to array
  72. push @$last_struct, [$1, 1];
  73. }
  74. # quantity
  75. elsif (/^[0-9~]+/) {
  76. # remove quantity
  77. s/^([0-9~]+)//;
  78. # add it on the last element of structure
  79. # (on block number 1 which holds quantity)
  80. my $size = @$last_struct -1;
  81. $last_struct->[$size][1] = $1;
  82. }
  83. # ignore comments
  84. elsif (/^#/) {
  85. # remove comment from input
  86. s/^(#.*$)//;
  87. }
  88. # unable to parse symbol - exit with error
  89. else {
  90. # get next symbol only, show error and exit
  91. s/^(.)//;
  92. print("open_structure: cannot parse '$1' on $filename:$.:$current_char\n");
  93. return;
  94. }
  95. } # while length
  96. } # while lines
  97. # initialize pointers
  98. game_structure_init_pointers($game_structure);
  99. # return game structure
  100. return $game_structure;
  101. } # open structure
  102. # initialize pointers
  103. # can be called on a new or already existing game structure
  104. # to reset its pointers from the beginning
  105. sub game_structure_init_pointers {
  106. # get game structure
  107. my $game_structure = shift;
  108. # reset its pointers with the initial one
  109. $game_structure->{"pointers_stack"} = [];
  110. push @{$game_structure->{"pointers_stack"}}, [["game", 0]];
  111. } # game structure init pointers
  112. # get last pointer array on the stack
  113. sub get_last_pointer_array {
  114. my $game_structure = shift;
  115. return $game_structure->{"pointers_stack"}[ @{ $game_structure->{"pointers_stack"} } -1 ];
  116. }
  117. # push pointer array to stack
  118. sub push_pointer_array {
  119. my $game_structure = shift;
  120. my $pointers = get_last_pointer_array($game_structure);
  121. my $ar = [];
  122. foreach (@$pointers) {
  123. push @$ar, [ $_->[0], $_->[1] ];
  124. }
  125. push @{$game_structure->{"pointers_stack"}}, $ar;
  126. } # push structure
  127. # pop pointer array from stack
  128. sub pop_pointer_array {
  129. my $game_structure = shift;
  130. pop @{ $game_structure->{"pointers_stack"} };
  131. } # pop pointer array
  132. # return the next element based on last pointer (format [$element, $quantity])
  133. sub next_element {
  134. # get game structure and its data
  135. my $game_structure = shift;
  136. my $pointers = get_last_pointer_array($game_structure);#->{"pointers_stack"};
  137. my $structures = $game_structure->{"structures"};
  138. # no more pointers == reached EOG (end of game)
  139. if (@$pointers == 0) {
  140. return "EOG";
  141. }
  142. # get current pointer data
  143. my $c_pointer = $pointers->[@$pointers -1];
  144. my $pointer_point = $c_pointer->[1];
  145. # pointed structure data
  146. my $pointed_struct = $structures->{$c_pointer->[0]};
  147. my $pointed_struct_size = @$pointed_struct +0;
  148. # reached EOS (end of struct)
  149. if ($pointer_point >= $pointed_struct_size) {
  150. # remove current counter
  151. pop @$pointers;
  152. return "EOS";
  153. }
  154. # find element pointed by pointer (format: [element, quantity])
  155. my $element = $pointed_struct->[$pointer_point];
  156. my $element_type = $element->[0];
  157. my $element_quantity = $element->[1];
  158. # new struct - create new counter
  159. if ($element_type =~ /^@([a-zA-Z]+)/) {
  160. push @$pointers, [$1, 0];
  161. }
  162. # move to next element
  163. $c_pointer->[1]++;
  164. # return element
  165. return $element;
  166. } # next element [$element, $quantity]
  167. # checks if game data reflects structure data
  168. sub check_data {
  169. # game data
  170. my $data = shift;
  171. my $game_structure = shift;
  172. my $data_counter = 0;
  173. # process elements until structure reaches the end
  174. while ( (my $c_element = next_element($game_structure)) ne "EOG" ) {
  175. # found End Of Structure, stop parsing
  176. if ($c_element eq "EOS") {
  177. # successfully parsed this structure
  178. return 1;
  179. }
  180. # expecting more data, but data array has no more
  181. if ($data_counter >= @$data) {
  182. printf("EXPECTING MORE DATA, BUT NOOOOO\n");
  183. last;
  184. }
  185. # get current element data
  186. my $expect_type = $c_element->[0];
  187. my $expect_quantity = $c_element->[1];
  188. #printf("expecting $expect_type : $expect_quantity\n");
  189. # next variable in data is a single variable
  190. if ($expect_quantity eq "1") {
  191. # expecting more data, but data array has no more
  192. if ($data_counter >= @$data) {
  193. printf("EXPECTING MORE DATA, BUT NOOOOO\n");
  194. last;
  195. }
  196. # expecting a new structure
  197. if ($expect_type =~ /^@/) {
  198. my $sub_struct = $data->[$data_counter];
  199. if (ref $sub_struct ne ref []) {
  200. printf("was expecting an array of data, instead got $sub_struct\n");
  201. last;
  202. }
  203. #printf("~~~ time to parse a structure ~~~\n\n");
  204. if (!check_data($sub_struct, $game_structure)) {
  205. printf("error parsing sub-structure!\n");
  206. last;
  207. }
  208. #printf("~~~ sub-structure read successfuly ~~~\n");
  209. }
  210. # expect number or string
  211. elsif ($expect_type =~ /^[\$\^]/) {
  212. # game data values
  213. my $data_type = $data->[$data_counter][0];
  214. my $data_value = $data->[$data_counter][1];
  215. #printf("found $data_type : $data_value\n");
  216. if ($expect_type eq $data_type) {
  217. #printf("fits\n");
  218. }
  219. else {
  220. printf("doesn't fit\n");
  221. last;
  222. }
  223. }
  224. # expecting unrecognized data
  225. else {
  226. printf("cannot parse $expect_type : $expect_quantity\n");
  227. last;
  228. }
  229. #printf("\n");
  230. $data_counter++;
  231. }
  232. # next variable in data is an array of variables
  233. elsif ($expect_quantity eq "~" || $expect_quantity >= 1) {
  234. # get array of data
  235. my $ar = $data->[$data_counter];
  236. # counter
  237. my $i = 0;
  238. # ~ array
  239. if ($expect_quantity eq "~") {
  240. #printf("parsing ~ array\n");
  241. while ($ar->[$i] ne "~") {
  242. # structure
  243. if ($expect_type =~ /^@/) {
  244. # check if last element
  245. my $is_last = 0;
  246. if ($i+1 >= @$ar or $ar->[$i+1] eq "~") {
  247. $is_last = 1;
  248. }
  249. if (!$is_last) {
  250. push_pointer_array($game_structure);
  251. }
  252. #printf("~~~ parse struct ~~~\n");
  253. my $ar2 = $ar->[$i];
  254. if (!check_data($ar2, $game_structure)) {
  255. printf("struct data is wrong\n");
  256. }
  257. #printf("~~~ done ~~~\n");
  258. if (!$is_last) {
  259. pop_pointer_array($game_structure);
  260. }
  261. if ($is_last) {
  262. #printf("last ~ element\n");
  263. }
  264. }
  265. # number/string
  266. else {
  267. my $type = $ar->[$i][0];
  268. my $value = $ar->[$i][1];
  269. #printf("array type: $type\n");
  270. #printf("array value: $value\n");
  271. if ($type ne $expect_type) {
  272. printf("something in the ~ array is wrong\n");
  273. return 0;
  274. }
  275. }
  276. $i++;
  277. } # while not ~
  278. } # ~ array
  279. # numbered array
  280. else {
  281. while ($i < $expect_quantity) {
  282. if ($expect_type =~ /^@/) {
  283. my $is_last = 0;
  284. if ($i+1 >= $expect_quantity) {
  285. $is_last = 1;
  286. }
  287. if (!$is_last) {
  288. push_pointer_array($game_structure);
  289. }
  290. #printf("~~~ array of structs ~~~\n");
  291. my $ar2 = $ar->[$i];
  292. if (!check_data($ar2, $game_structure)) {
  293. printf("struct data is wrong\n");
  294. }
  295. #printf("~~~ done ~~~\n");
  296. if (!$is_last) {
  297. pop_pointer_array($game_structure);
  298. }
  299. }
  300. else {
  301. my $type = $ar->[$i][0];
  302. my $value = $ar->[$i][1];
  303. if ($type ne $expect_type) {
  304. printf("something in the array is wrong\n");
  305. return 0;
  306. }
  307. }
  308. $i++;
  309. }
  310. }
  311. $data_counter++;
  312. }
  313. }
  314. # something failed
  315. return 0;
  316. }
  317. # everything is ok
  318. return 1;