pl_proto.pl 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use Data::Dumper;
  5. my $SOURCE_FILE = $ARGV[0];
  6. sub read_to_var($) {
  7. my $file_path = shift;
  8. my $content;
  9. open(my $fh, '<', $file_path) or die "cannot open file $file_path";
  10. {
  11. local $/;
  12. $content = <$fh>;
  13. }
  14. close($fh);
  15. return $content;
  16. }
  17. my $src_content = read_to_var($SOURCE_FILE);
  18. sub trim($) {
  19. my $str = shift;
  20. $str =~ s/^\s+|\s+$//g;
  21. return $str;
  22. }
  23. my @module_library_paths = (
  24. ".",
  25. );
  26. my %modules;
  27. sub parse_module_file {
  28. my $src = shift;
  29. my %module;
  30. my $last_proc_type = "null";
  31. my $last_input = "null";
  32. my $last_output = "null";
  33. foreach my $line ( split("\n", $src) ) {
  34. chomp $line;
  35. if ( $line =~ m/^#/ ) {
  36. next;
  37. }
  38. if ( $line =~ m/^Manufacturer:(.*)/ ) {
  39. my $manu = $1;
  40. $manu = trim($manu);
  41. $module{'Manufacturer'} = $manu;
  42. }
  43. if ( $line =~ m/^Module:(.*)/ ) {
  44. my $mod = $1;
  45. $mod = trim($mod);
  46. $module{'Module'} = $mod;
  47. }
  48. if ( $line =~ m/^Revision:(.*)/ ) {
  49. my $rev = $1;
  50. $rev = trim($rev);
  51. $module{'Rev'} = $rev;
  52. }
  53. if ( $line =~ m/^-\ / ) {
  54. if ( $line =~ m/^-\ Input:(.*)/ ) {
  55. my $input = $1;
  56. $input = trim($input);
  57. my %input_chars;
  58. $last_input = $input;
  59. $last_proc_type = "input";
  60. $module{'Inputs'}->{$input} = \%input_chars;
  61. }
  62. if ( $line =~ m/^-\ Knob:(.*)/ ) {
  63. my $knob = $1;
  64. $knob = trim($knob);
  65. my %knob_chars;
  66. $last_input = $knob;
  67. $last_proc_type = "input";
  68. $module{'Inputs'}->{$knob} = \%knob_chars;
  69. }
  70. if ( $line =~ m/^-\ Output:(.*)/ ) {
  71. my $output = $1;
  72. $output = trim($output);
  73. my %output_chars;
  74. $last_output = $output;
  75. $last_proc_type = "output";
  76. $module{'Outputs'}->{$output} = \%output_chars;
  77. }
  78. }
  79. if ( $line =~ m/^--\ / ) {
  80. if ( $line =~ m/^--\ Position:(.*)/ ) {
  81. my $pos_args = $1;
  82. $pos_args = trim($pos_args);
  83. if ( $last_proc_type eq "input" ) {
  84. $module{'Inputs'}->{$last_input}->{'pos'} = $pos_args;
  85. } elsif ( $last_proc_type eq "output" ) {
  86. $module{'Outputs'}->{$last_output}->{'pos'} = $pos_args;
  87. }
  88. }
  89. }
  90. }
  91. return \%module;
  92. }
  93. my %AST;
  94. my %PARSE_TABLE = (
  95. 'comment' => '^#.*$',
  96. 'title' => '^Title: (.*)$',
  97. 'mod_path' => '^ModuleDir\ "(.*)"$',
  98. 'import' => '^import (Module)::([a-zA-Z0-9]{1,})::([a-zA-Z0-9]{1,})(.*$)',
  99. 'set' => '^set\ (.*)$',
  100. 'connect' => '^connect(.*)$',
  101. );
  102. my %PARSE_RULES = (
  103. 'comment' => sub {
  104. # Do nothing, throw this line out
  105. },
  106. 'title' => sub {
  107. my $title = shift;
  108. $AST{'Title'} = $title;
  109. },
  110. 'mod_path' => sub {
  111. my $file_path = shift;
  112. if ( ! -d $file_path ) {
  113. die "Path: $file_path doesn't look like a directory, exiting";
  114. }
  115. push(@module_library_paths, $file_path);
  116. },
  117. 'import' => sub {
  118. my $module_import = shift;
  119. my $import_manu = shift;
  120. my $import_mod = shift;
  121. my $import_as = shift;
  122. my @module_files = sub {
  123. my @files;
  124. foreach my $path ( @module_library_paths ) {
  125. my @f = split("\n", `find $path`);
  126. foreach my $file ( @f ) {
  127. if ( $file =~ m/.module$/ ) {
  128. my $f_bn = `basename $file`;
  129. chomp $f_bn;
  130. if ( ! grep(/$f_bn/, @files) ) {
  131. push(@files, $file);
  132. }
  133. }
  134. }
  135. }
  136. return @files;
  137. }->();
  138. foreach my $mod_file ( @module_files ) {
  139. my $mod_file_content = read_to_var($mod_file);
  140. my $mod_ref = parse_module_file($mod_file_content);
  141. if ( $import_mod eq $$mod_ref{'Module'} ) {
  142. if ( defined $AST{'Modules'} ) {
  143. my $r = grep { $import_mod eq $_->{'Module'} } @{$AST{'Modules'}};
  144. if ( $r == 0 ) {
  145. push(@{$AST{'Modules'}}, $mod_ref);
  146. }
  147. } else {
  148. push(@{$AST{'Modules'}}, $mod_ref);
  149. }
  150. } else {
  151. next;
  152. }
  153. }
  154. },
  155. 'set' => sub {
  156. my $set_line = shift;
  157. my $mod_to_set;
  158. my $attr_to_set;
  159. my $attr_param;
  160. my $value;
  161. my $setter = sub {
  162. my $mod_to_set = shift;
  163. my $attr_to_set = shift;
  164. my $attr_param = shift;
  165. if ( $attr_param eq "position" ) {
  166. $attr_param = "pos";
  167. }
  168. my $value = shift;
  169. my %set_params = (
  170. 'Param' => $attr_param,
  171. 'Value' => $value,
  172. );
  173. # Check values against mod definition
  174. # Pull mod ref out of AST for straight forward checking
  175. my $mod_ref;
  176. # Check we have module in AST
  177. my $r = grep { $mod_to_set eq $_->{'Module'} } @{$AST{'Modules'}};
  178. if ( $r eq 0 ) {
  179. die "Can't set value on module that is not imported: $mod_to_set\n";
  180. } else {
  181. foreach my $module_ref ( @{$AST{'Modules'}} ) {
  182. if ( $mod_to_set eq $$module_ref{'Module'} ) {
  183. $mod_ref = $module_ref;
  184. last;
  185. }
  186. }
  187. }
  188. # Check that module has param we want to set
  189. if ( ! $attr_to_set eq $$mod_ref{'Inputs'}->{$attr_to_set} ) {
  190. die "Can't set a param that doesn't existing in the module spec: $attr_to_set";
  191. }
  192. # If the set has an attr param, check that it's in the allowed range on the attr
  193. if ( $attr_param ne "null" ) {
  194. my $attr_range = $$mod_ref{'Inputs'}->{$attr_to_set}->{'pos'};
  195. if ( $attr_range =~ m/([0-9]{1,2})\-([0-9]{1,2})/ ) {
  196. my $r_begin = $1;
  197. my $r_end = $2;
  198. if ( $value > $r_end || $value < $r_begin ) {
  199. die "Parse error: attr_param value: $value for $attr_to_set : $attr_param is outside of range: $r_begin $r_end";
  200. }
  201. } else {
  202. die "Somehow encountered parse error in setter for module file $$mod_ref{'Module'}\n";
  203. }
  204. }
  205. $AST{'Sets'}->{$mod_to_set}->{$attr_to_set} = \%set_params;
  206. };
  207. if ( $set_line =~ m/(^[A-Z]{1}[a-z]{1,})\.{1}([A-Za-z0-9]{1,})\ \=\ (.*)$/ ) {
  208. $mod_to_set = $1;
  209. $attr_to_set = $2;
  210. $value = $3;
  211. } elsif ( $set_line =~ m/(^[A-Z]{1}[a-z]{1,})\.{1}([A-Za-z0-9]{1,})\.([A-Za-z0-9]{1,})\ \=\ (.*)$/ ) {
  212. $mod_to_set = $1;
  213. $attr_to_set = $2;
  214. $attr_param = $3;
  215. $value = $4;
  216. } else {
  217. die "Parse error at $set_line";
  218. }
  219. if ( ! defined $attr_param || $attr_param eq "" ) {
  220. $attr_param = "null",
  221. };
  222. $setter->($mod_to_set,$attr_to_set,$attr_param,$value);
  223. },
  224. 'connect' => sub {
  225. my $connect_line = shift;
  226. $connect_line = trim($connect_line);
  227. },
  228. );
  229. # Basic line parser
  230. sub line_parse($) {
  231. my $line = shift;
  232. my $line_type = "null";
  233. my @line_caps;
  234. foreach my $key ( keys %PARSE_TABLE ) {
  235. if ( $line =~ m/$PARSE_TABLE{$key}/ ) {
  236. $line_type = $key;
  237. }
  238. }
  239. if ( $line_type ne "null" && $line =~ m/$PARSE_TABLE{$line_type}/) {
  240. if ( defined $1 && ! defined $2 ) {
  241. $PARSE_RULES{$line_type}->($1);
  242. } elsif ( defined $1 && defined $2 && defined $3 && defined $4 ) {
  243. # This is for `import`
  244. $PARSE_RULES{$line_type}->($1,$2,$3,$4);
  245. } else {
  246. $PARSE_RULES{$line_type}->();
  247. }
  248. } else {
  249. print("$line\n");
  250. }
  251. }
  252. foreach my $line ( split("\n", $src_content) ) {
  253. chomp $line;
  254. if ( $line eq "" ) {
  255. next;
  256. }
  257. line_parse($line);
  258. }
  259. print Dumper %AST;