#!/usr/bin/perl # # macros.pl # # This converts bml (Bridge Macro Language) code # into HTML. It is somewhat patterned after a version # done in m4 in early 1996. This one doesn't do all # the same things and has different problems, but is # much more portable. # # Version: 0.1 # April 11, 1996, initial write # Version: 0.2 # April 17, 1996 # Added escaped parentheses # Version: 0.3 # January 5, 1998 # Many changes, including sizes for pips, Smileys, more. # Version: 0.4 # April 14, 1998 # Fixed missing quotes # Version: 0.5 # September 8, 1998 # Added SPADE, HEART, DIAMOND, CLUB macros # for Tom Carmichael. # Version: 0.6 # Unknown date # VOID # 13-card hand counter # Version: 0.7 # October 24, 2002 # VOID # Improved panelist # Table tool # Version: 0.8 # January 29, 2003 # HEADER and FOOTER # Version: 0.9 # October 29, 2014 # HHAND14 # Fixed shebang line # Version: 0.10 # February 26, 2020 # Replace smart quotes # # Macros: # CARD(text) # changes 1S or SA to what you expect. # HHAND(s,h,d,c) # prints a hand horizontally # HHAND14(s,h,d,c) # allows non-13-card-hands # VHAND(s,h,d,c) # prints a hand vertically # HAND(north,west,east,south) # prints a hand diagram # BIDDING(player1,player2,bid,bid,bid,...) # draws a bidding sequence between two players # BID4(p1,p2,p3,p4,bid,bid,bid,...) # draws a bidding sequence among all four players # SUIT(north,south) # draws a simple single-dummy suit combination # FOOTNOTE(text) # draws footnote markers # PANELIST(text) # draws panelist headers in small caps # OBIDDING,OBID4 # similar to old macros; doesn't consider # its arguments to be bids; you have to say CARD() # of everything. Somewhat helpful when you have # very complex things with stuff inside the bids. # SMILEY # obvious # SPADE, HEART, DIAMOND, CLUB # expand to suit symbols; same as CARD(S), CARD(H), etc. # DASH or VOID # -> — an em dash # MAKETABLE # TABLEROW # ENDTABLE # convenient for vote tabulations # HEADER(title) # FOOTER() # # Known problems: # It doesn't handle nested parentheses right. # Escaping parens helps: \( and \) are # handled fine, although FOO(BAR()) might # not be. The precedence rules are pretty # good about that, though. # It doesn't do the old line-by-line bidding macros. # It doesn't really need them. # # Improvements over m4 version: # No quoting "shift" needed. # No include line needed. # Unlimited number of arguments in bidding macros, # which will mostly make the line-by-line macros obsolete. # Can handle doubles and redoubles in CARD # Way fast. # # Unknown problems: # If I knew them, they would be known problems, silly! #require "ctime.pl"; use Time::localtime; $simg = 'gifs/s.gif'; $himg = 'gifs/h.gif'; $dimg = 'gifs/d.gif'; $cimg = 'gifs/c.gif'; $swidth = 13; # width of pips (hardcoded) $sheight = 11; # height of pips (hardcoded) $smiley = 'gifs/smiley.gif'; $tmarker = 'gifs/table11.gif'; @_ = <>; $_ = join("",@_); # remove escaped parens &hide_parens($_); &bridge_trans($_); &bridge_trans($_); &bridge_trans($_); &bridge_trans($_); &bridge_trans($_); &replace_parens($_); print $_; # Bridge formatting stuff # hide_parens converts \( and \) into QUOTED_PAREN and QUOTED_THESIS sub hide_parens { $_[0] =~ s/\\\(/_QUOTED_PAREN_/g; $_[0] =~ s/\\\)/_QUOTED_THESIS_/g; } # replace_parens reverses above and removes escapes sub replace_parens { $_[0] =~ s/_QUOTED_PAREN_/(/g; $_[0] =~ s/_QUOTED_THESIS_/)/g; } # this takes a string and does replacement sub bridge_trans { $_[0] =~ s/{}FOOTNOTE\(([^)]*)\)/&footnote($1)/ge; $_[0] =~ s/{}CARD\(([^)]*)\)/&card($1)/ge; $_[0] =~ s/{}HHAND\(([^)]*)\)/&hhand($1)/ge; $_[0] =~ s/{}HHAND14\(([^)]*)\)/&hhand14($1)/ge; $_[0] =~ s/{}OBIDDING\(([^)]*)\)/&old_bidding($1)/ge; $_[0] =~ s/{}OBID4\(([^)]*)\)/&old_bid4($1)/ge; $_[0] =~ s/{}BIDDING\(([^)]*)\)/&bidding($1)/ge; $_[0] =~ s/{}BID4\(([^)]*)\)/&bid4($1)/ge; $_[0] =~ s/{}VHAND\(([^)]*)\)/&vhand($1)/ge; $_[0] =~ s/{}HAND\(([^)]*)\)/&hand($1)/ge; $_[0] =~ s/{}SUIT\(([^)]*)\)/&suit($1)/ge; $_[0] =~ s/{}PANELIST\(([^)]*)\)/&panelist($1)/ge; $_[0] =~ s/{}SMILEY/\\":)\"/g; $_[0] =~ s/{}SPADE/\\"S:\"\/g; $_[0] =~ s/{}HEART/\\"H:\"\/g; $_[0] =~ s/{}DIAMOND/\\"D:\"\/g; $_[0] =~ s/{}CLUB/\\"C:\"\/g; $_[0] =~ s/{}DASH/\—/g; $_[0] =~ s/{}VOID/\—/g; $_[0] =~ s/{}MAKETABLE\(([^)]*)\)/&maketable($1)/ge; $_[0] =~ s/{}TABLEROW\(([^)]*)\)/&tablerow($1)/ge; $_[0] =~ s/{}ENDTABLE\(([^)]*)\)/&endtable($1)/ge; $_[0] =~ s/{}HEADER\(([^)]*)\)/&header($1)/ge; $_[0] =~ s/{}FOOTER\(([^)]*)\)/&footer($1)/ge; $_[0] =~ s/FOOTNOTE\(([^)]*)\)/&footnote($1)/ge; $_[0] =~ s/CARD\(([^)]*)\)/&card($1)/ge; $_[0] =~ s/HHAND\(([^)]*)\)/&hhand($1)/ge; $_[0] =~ s/HHAND14\(([^)]*)\)/&hhand14($1)/ge; $_[0] =~ s/OBIDDING\(([^)]*)\)/&old_bidding($1)/ge; $_[0] =~ s/OBID4\(([^)]*)\)/&old_bid4($1)/ge; $_[0] =~ s/BIDDING\(([^)]*)\)/&bidding($1)/ge; $_[0] =~ s/BID4\(([^)]*)\)/&bid4($1)/ge; $_[0] =~ s/VHAND\(([^)]*)\)/&vhand($1)/ge; $_[0] =~ s/HAND\(([^)]*)\)/&hand($1)/ge; $_[0] =~ s/SUIT\(([^)]*)\)/&suit($1)/ge; $_[0] =~ s/PANELIST\(([^)]*)\)/&panelist($1)/ge; $_[0] =~ s/SMILEY/\/g; $_[0] =~ s/SPADE/\\"S:\"\/g; $_[0] =~ s/HEART/\\"H:\"\/g; $_[0] =~ s/DIAMOND/\\"D:\"\/g; $_[0] =~ s/CLUB/\\"C:\"\/g; $_[0] =~ s/DASH/\—/g; $_[0] =~ s/VOID/\—/g; $_[0] =~ s/MAKETABLE\(([^)]*)\)/&maketable($1)/ge; $_[0] =~ s/TABLEROW\(([^)]*)\)/&tablerow($1)/ge; $_[0] =~ s/ENDTABLE\(([^)]*)\)/&endtable($1)/ge; $_[0] =~ s/HEADER\(([^)]*)\)/&header($1)/ge; $_[0] =~ s/FOOTER\(([^)]*)\)/&footer($1)/ge; $_[0] =~ s/\\'/'/g; # get rid of nasty smart quotes $_[0] =~ s/‘/‘/g; $_[0] =~ s/’/’/g; $_[0] =~ s/“/“/g; $_[0] =~ s/”/”/g; } # this replaces 1H, 2S, CA, etc. with appropriate stuff sub card { $_ = $_[0]; if (/Dbl/i || /Pass/i) { $_; } elsif (/^S/i) { s/^.//; 'S:'.$_.''; } elsif (/^H/i) { s/^.//; 'H:'.$_.''; } elsif (/^D/i) { s/^.//; 'D:'.$_.''; } elsif (/^C/i) { s/^.//; 'C:'.$_.''; } elsif (/S$/i) { s/.$//; ''.$_.'S:'; } elsif (/H$/i) { s/.$//; ''.$_.'H:'; } elsif (/D$/i) { s/.$//; ''.$_.'D:'; } elsif (/C$/i) { s/.$//; ''.$_.'C:'; } elsif (/Sx$/i) { s/..$//; ''.$_.'S:x'; } elsif (/Hx$/i) { s/..$//; ''.$_.'H:x'; } elsif (/Dx$/i) { s/..$//; ''.$_.'D:x'; } elsif (/Cx$/i) { s/..$//; ''.$_.'C:x'; } elsif (/Sxx$/i) { s/...$//; ''.$_.'S:xx'; } elsif (/Hxx$/i) { s/...$//; ''.$_.'H:xx'; } elsif (/Dxx$/i) { s/...$//; ''.$_.'D:xx'; } elsif (/Cxx$/i) { s/...$//; ''.$_.'C:xx'; } else { $_; } } # hhand draws a horizontal hand # it takes four arguments: spades, hearts, diamonds, clubs # the reason to start with a space is that some versions of # netscape have a bug vertically aligning images and text # on the same line when the image comes first. sub hhand { $ncards = &handsize($_[0]); @args = split(/,/,$_[0]); $result = ' S:'.$args[0]. ' H:'.$args[1]. ' D:'.$args[2]. ' C:'.$args[3].''; if (!($ncards == 13)) {$result .= " <-- hand has $ncards cards. ";} $result; } # this version allows non-13-card hands sub hhand14 { $ncards = &handsize($_[0]); @args = split(/,/,$_[0]); $result = ' S:'.$args[0]. ' H:'.$args[1]. ' D:'.$args[2]. ' C:'.$args[3].''; $result; } # vhand draws a vertical hand # it takes four arguments: spades, hearts, diamonds, clubs sub vhand { @args = split(/,/,$_[0]); 'S: '.$args[0].'
'. 'H: '.$args[1].'
'. 'D: '.$args[2].'
'. 'C: '.$args[3]; } # hand draws a full hand diagram # it takes four arguments, North, West, East, South sub hand { @args = split(/,/,$_[0]); "\n". "\n". "\n". "\n". "
".$args[0]."
".$args[1]."".$args[2]."
".$args[3]."
\n"; } # Two player bidding sub bidding { @args = split(/,/,$_[0]); $output = "\n"; # first two args are players $output .= '\n"; # remaining args are bids for ($i=2;$i<$#args+1;$i+=2) { $output .= '\n"; } $output .= "
'; $output .= $args[0]; $output .= ''; $output .= $args[1]; $output .= "
'.&card(@args[$i]). ''.&card(@args[$i+1])."
\n"; $output; } sub old_bidding { @args = split(/,/,$_[0]); $output = "\n"; # first two args are players $output .= '\n"; # remaining args are bids for ($i=2;$i<$#args+1;$i+=2) { $output .= '\n"; } $output .= "
'; $output .= $args[0]; $output .= ''; $output .= $args[1]; $output .= "
'.@args[$i]. ''.@args[$i+1]."
\n"; $output; } # four player bidding sub bid4 { local($td1,$td2,$td3); @args = split(/,/,$_[0]); $output = "\n"; # first four args are players $output .= '\n"; $td1 = "td"; $td2 = "td"; $td3 = "td"; # remaining args are bids for ($i=4;$i<$#args+3;$i+=4) { # handle "All pass" well. if ($args[$i ] =~ /All\s*Pass/i) {$td1 = "td colspan = 4";} if ($args[$i+1] =~ /All\s*Pass/i) {$td2 = "td colspan = 3";} if ($args[$i+2] =~ /All\s*Pass/i) {$td3 = "td colspan = 2";} $output .= "<$td1>". &card($args[$i]). "<$td2>".&card($args[$i+1]). "<$td3>".&card($args[$i+2]). "\n"; } $output .= "
'; $output .= $args[0]; $output .= ''; $output .= $args[1]; $output .= ''; $output .= $args[2]; $output .= ''; $output .= $args[3]; $output .= "
".&card($args[$i+3]). "
\n"; $output; } sub old_bid4 { @args = split(/,/,$_[0]); $output = "\n"; # first four args are players $output .= '\n"; # remaining args are bids for ($i=4;$i<$#args+3;$i+=4) { $output .= '\n"; } $output .= "
'; $output .= $args[0]; $output .= ''; $output .= $args[1]; $output .= ''; $output .= $args[2]; $output .= ''; $output .= $args[3]; $output .= "
'. @args[$i]. ''.@args[$i+1]. ''.@args[$i+2]. ''.@args[$i+3]. "
\n"; $output; } # suit draws a two-player suit combination sub suit { @args = split(/,/,$_[0]); "\n". "\n". "\n". "\n". "\n". "\n". "
Dummy
".$args[0]."
Declarer
".$args[1]."
\n"; } # Footnote draws superscripts sub footnote { "".$_[0].""; } # Panelist is strange: all caps, but different size fonts sub panelist { $param = $_[0]; $param =~ tr/a-z/A-Z/; # upcase # upcase anything after a space or carat (and remove the carat) @chars = split(//,$param); $result = "
"; $currently = 1; # big for ($i=0;$i<$#chars+1;$i++) { $big = 0; # is it big or little? if ($i == 0) {$big = 1;} elsif ($chars[$i] eq '^') {$i++; $big = 1;} elsif ($chars[$i] eq ' ') {$i++; $result .= ' '; $big = 1;} if ($currently != $big) { if ($big) {$result .= "";} else {$result .= "";} } $result .= $chars[$i]; $currently = $big; } # if it is small, add an end font if (!$currently) {$result .= "";} $result .= "
"; $result; } # Start table and do header row sub maketable { $result = "\n"; $result .= "
\n"; $result .= "\n"; $result .= "\n"; @params = split(/,/,$_[0]); for ($i=0;$i<=$#params;$i++) {$result .= ""; } $result .= "\n"; $result; } # add a row to a table sub tablerow { $result = ""; @params = split(/,/,$_[0]); for ($i=0;$i<=$#params;$i++) { if ($params[$i] =~ /^\*/) {$result .= "";} elsif ($params[$i] =~ /^\=/) # push right {$result .= "";} else {$result .= "";} } $result .= "\n"; $result; } # end table sub endtable { $result = "
".$params[$i]."
".substr($params[$i],1)."".substr($params[$i],1)."".$params[$i]."
"; $result .= "
"; $result; } # How many cards in the hand? It knows about 10 and T and VOID sub handsize { $cards=$_[0]; $cards =~ s/,//g; $cards =~ s/ //g; $cards =~ s/VOID//g; $cards =~ s/10/T/g; length($cards); } # header sub header { $result = "".$_[0]."". "

".$_[0]."

"; $result; } # footer sub footer { $result = "
"; if ($_[0] ne "") {$result .= "Answers
";} $result .= "
Jeff Goldsmith, "; $result .= &okDate; $result .= "
"; $result; } # reasonable date sub okDate { $date = &ctime(time); ($weekday,$month,$day,$time,$year) = split(/\s+/,$date); $month." ".$day.", ".$year; }