123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package SugarTrail::Template::Condition;
  2. # condition
  3. # new()
  4. # parse(string)
  5. # match(data)
  6. use strict;
  7. use warnings;
  8. sub _equals {
  9. my $a = shift or return;
  10. my $b = $_[0]->[0];
  11. return $a eq $b;
  12. }
  13. sub _regex {
  14. my $a = shift or return;
  15. my $b = $_[0]->[0];
  16. return $a =~ m/$b/;
  17. }
  18. sub _in {
  19. my $a = shift or return;
  20. my %b = map { $_ => 1 } @_;
  21. return exists $b{$a};
  22. }
  23. sub new {
  24. my $class = shift;
  25. my $self = {};
  26. $self->{string} = shift;
  27. $self->{handlers} = {
  28. '==' => \&_equals,
  29. '>=' => \&_greater_or_eq,
  30. '<=' => \&_less_or_eq,
  31. '=@' => \&_in,
  32. '=~' => \&_regex,
  33. };
  34. $self->{parsed} = 0;
  35. $self->{warnings} = [];
  36. return bless $self, $class;
  37. }
  38. # parse condition from its string version
  39. sub parse {
  40. my $self = shift;
  41. my $string = $self->{string};
  42. return if $self->{parsed};
  43. # cut spaces
  44. $string =~ s/^\s+//;
  45. $string =~ s/\s+$//;
  46. # get name, operator, value(s)
  47. my ($n, $o, $v) = $string =~ m/([a-zA-Z0-9_ ]+)([<>=~+\-*@]*)(.*)/;
  48. # cut inner spaces
  49. $n =~ s/\s+$//;
  50. $o =~ s/\s+$//;
  51. $o =~ s/^\s+//;
  52. $v =~ s/\s+$//;
  53. # split vlues
  54. my @v = split m/\s*,\s*/, $v;
  55. unless ($string and $n and $o and @v) {
  56. $self->{error} = "syntax error: missing param name" unless $n;
  57. $self->{error} = "syntax error: missing operator" unless $o;
  58. $self->{error} = "syntax error: no values" unless @v;
  59. $self->{error} = "syntax error: no condition string" unless $string;
  60. return
  61. }
  62. # assign
  63. $self->{n} = $n;
  64. $self->{o} = $o;
  65. $self->{v} = \@v;
  66. $self->{parsed}++;
  67. }
  68. # match the cond against params
  69. sub __match {
  70. my $self = shift;
  71. my $params = shift;
  72. return if $self->{error};
  73. my $name = $self->{n}; # what is the topic
  74. my $op = $self->{o}; # what op to use
  75. my $have = $self->{v}; # what we have in this step
  76. my $want = $params->{$name}; # what they want
  77. # warn if condition cannot be decided due to missing param
  78. unless ($want) {
  79. $self->warn( sprintf (
  80. "undecided condition: %s; did you forget to specify %s?",
  81. $self->{string},
  82. $name
  83. ));
  84. }
  85. # execute the right handler or warn it does not exist
  86. if (exists $self->{handlers}->{$op}) {
  87. return &{ $self->{handlers}->{$op} }($want, $have);
  88. } else {
  89. $self->warn( sprintf (
  90. "unknown operator: '%s' in '%s'",
  91. $op,
  92. $self->{string}
  93. ));
  94. }
  95. }
  96. # wrapper to __match(); adds warning list
  97. sub match {
  98. my $self = shift;
  99. my $params = shift;
  100. $self->{error} = "";
  101. $self->parse();
  102. $self->{result} = $self->__match($params);
  103. return if $self->{error};
  104. return $self->{result}, $self->{warnings};
  105. }
  106. sub warn {
  107. my $self = shift;
  108. my $text = shift;
  109. push @{$self->{warnings}}, $text;
  110. }
  111. 1;