scribble-mathjax/mathjax-dev/operator-dictionary/makeOpTable
2010-08-02 17:46:41 +00:00

529 lines
19 KiB
Perl
Executable File

#! /usr/bin/perl
#
# Creates the optable.js data (for inclusion on jax/element/mml/jax.js)
# and the optable directory of auxiliary operator data.
#
# Usage: ./makeOpTable
open(OPHTML,"Operator Dictionary.html") || die "Can't load operator dictionary";
$lines = join('',<OPHTML>);
$lines =~ s/.*<table class.*<tbody>\s*//is;
$lines =~ s!</tbody>.*!!is;
$lines =~ s/^\s*//gm;
$lines =~ s!(<tr>|</t[dh]>)\n!\1!g;
$lines =~ s!</?tr>!!g;
$lines =~ s!^<th[^>]*>!!gm;
$lines =~ s!</td>$!!gm;
$lines =~ s/&amp;/&/g; $lines =~ s/&amp;/&/g;
$lines =~ s/&lt;/</g; $lines =~ s/&gt;/>/g;
foreach $line (split(/\n/,$lines)) {
($id,$char,$name,$form,$prec,$lspace,$rspace,$min,$attributes) = split(/<\/t[dh]><t[dh][^>]*>/,$line);
next if $id eq "min";
$op{$form}{$id} = [$lspace,$rspace,$attributes,$name];
}
# hack to add in missing prefix and postfix |
$op{prefix}{"|"} = [0,0,'fence stretchy symmetric','vertical line'];
$op{postfix}{"|"} = [0,0,'fence stretchy symmetric','vertical line'];
@RANGES = (
["BasicLatin",0x20,0x7F,'REL'],
["Latin1Supplement",0xA0,0xFF,'ORD'],
["LatinExtendedA",0x100,0x17F,'ORD'],
["LatinExtendedB",0x180,0x24F,'ORD'],
["SpacingModLetters",0x2B0,0x2FF,'ORD'],
["CombDiacritMarks",0x300,0x36F,'ORD'],
["GreekAndCoptic",0x370,0x3FF,'ORD'],
# ["Cyrillic",0x400,0x4FF,'ORD'],
# ["PhoneticExtensions",0x1D00,0x1DBF,'ORD'],
["LatinExtendedAdditional",0x1E00,0x1EFF,'ORD'],
["GeneralPunctuation",0x2000,0x206F,'PUNCT'],
["SuperAndSubscripts",0x2070,0x209F,'ORD'],
["CurrencySymbols",0x20A0,0x20CF,'ORD'],
["CombDiactForSymbols",0x20D0,0x20FF,'ORD'],
["LetterlikeSymbols",0x2100,0x214F,'ORD'],
["NumberForms",0x2150,0x218F,'ORD'],
["Arrows",0x2190,0x21FF,'REL'],
["MathOperators",0x2200,0x22FF,'BIN'],
["MiscTechnical",0x2300,0x23FF,'ORD'],
# ["ControlPictures",0x2400,0x243F,'ORD'],
["EnclosedAlphanum",0x2460,0x24FF,'ORD'],
# ["BoxDrawing",0x2500,0x257F,'ORD'],
# ["BlockElements",0x2580,0x259F,'ORD'],
["GeometricShapes",0x25A0,0x25FF,'ORD'],
# ["MiscSymbols",0x2600,0x26FF,'ORD'],
["Dingbats",0x2700,0x27BF,'ORD'],
["MiscMathSymbolsA",0x27C0,0x27EF,'ORD'],
["SupplementalArrowsA",0x27F0,0x27FF,'REL'],
["SupplementalArrowsB",0x2900,0x297F,'REL'],
["MiscMathSymbolsB",0x2980,0x29FF,'ORD'],
["SuppMathOperators",0x2A00,0x2AFF,'BIN'],
["MiscSymbolsAndArrows",0x2B00,0x2BFF,'ORD'],
# ["CJK",0x3000,0x303F,'ORD'],
# ["Hiragana",0x3040,0x309F,'ORD'],
# ["PUA",0xE000,0xFB00,'ORD'],
# ["AlphaPresentForms",0xFB00,0xFB4F,'ORD'],
# ["Specials",0xFFF0,0xFFFF,'ORD'],
["MathAlphabets",0x1D400,0x1D7FF,'ORD'],
);
sub GetRange {
my $n = shift;
if ($n =~ m/^[0-9A-Z]{4}$/) {$n = hex($n)}
elsif ($n =~ m/^&#x([0-9A-Z]+);/) {$n = hex($1)}
else {$n = ord(substr($n,0,1))}
foreach $range (@RANGES) {
my ($name,$l,$u,$type) = @$range;
return $name if $l <= $n && $n <= $u;
}
return "none";
}
%TEX = (
'!' => ['CLOSE', '!'],
'(' => ['OPEN', '('],
')' => ['CLOSE', ')'],
'*' => ['BIN', '*'],
'2217' => ['BIN', '*'],
'+' => ['BIN', '+'],
',' => ['PUNCT', ','],
'-' => ['BIN', '-'],
'2212' => ['BIN', 'minus'],
'.' => ['ORD', '.'],
'/' => ['ORD', '/'],
'2215' => ['BIN','/'],
':' => ['REL', ':'],
';' => ['PUNCT', ';'],
'<' => ['REL', '<'],
'=' => ['REL', '='],
'>' => ['REL', '>'],
'?' => ['CLOSE', '?'],
'[' => ['OPEN', '['],
']' => ['CLOSE', ']'],
'{' => ['OPEN', '{'],
'}' => ['CLOSE', '}'],
'|' => [{prefix => 'OPEN', infix => 'ORD', postfix => 'CLOSE'}, '|'],
'_' => ['ORD', '_'],
'#' => ['ORD', '#'],
'$' => ['ORD', '$'],
'%' => ['ORD', '%'],
'&' => ['ORD', '&'],
'^' => ['ORD', '^'], # accent for MathML
'~' => ['ORD', '~'], # accent for MathML
'`' => ['ORD', '`'], # accent for MathML
# Ord symbols
# '00A7' => ['ORD', '\\S'], # should not be an <mo>
# '2135' => ['ORD', '\\aleph'], # should not be an <mo>
# '210F' => ['ORD', '\\hbar'], # should not be an <mo>
# '0131' => ['ORD', '\\imath'], # should not be an <mo>
# '0237' => ['ORD', '\\jmath'], # should not be an <mo>
'2113' => ['ORD', '\\ell'],
'2118' => ['ORD', '\\wp'],
'211C' => ['ORD', '\\Re'],
'2111' => ['ORD', '\\Im'],
'2202' => ['ORD', '\\partial'],
'221E' => ['ORD', '\\infty'],
'2032' => ['ORD', '\\prime'],
'2205' => ['ORD', '\\emptyset'],
'2207' => ['ORD', '\\nabla'],
'221A' => ['ORD', '\\surd'],
'22A4' => ['ORD', '\\top'],
'22A5' => ['ORD', '\\bot'],
'2220' => ['ORD', '\\angle'],
'25B3' => ['ORD', '\\triangle'],
'\\' => ['ORD', '\\backslash'],
'2216' => ['ORD', '\\backslash'],
'2200' => ['ORD', '\\forall'],
'2203' => ['ORD', '\\exists'],
'00AC' => ['ORD', '\\neg'],
'266D' => ['ORD', '\\flat'],
'266E' => ['ORD', '\\natural'],
'266F' => ['ORD', '\\sharp'],
'2663' => ['ORD', '\\clubsuit'],
'2662' => ['ORD', '\\diamondsuit'],
'2661' => ['ORD', '\\heartsuit'],
'2660' => ['ORD', '\\spadesuit'],
# Big ops
'2210' => ['OP', '\\coprod'],
'22C1' => ['OP', '\\bigvee'],
'22C0' => ['OP', '\\bigwedge'],
'2A04' => ['OP', '\\biguplus'],
'22C2' => ['OP', '\\bigcap'],
'22C3' => ['OP', '\\bigcup'],
'222B' => ['OP', '\\int'],
'220F' => ['OP', '\\prod'],
'2211' => ['OP', '\\sum'],
'2A02' => ['OP', '\\bigotimes'],
'2A01' => ['OP', '\\bigoplus'],
'2A00' => ['OP', '\\bigodot'],
'222E' => ['OP', '\\oint'],
'2A06' => ['OP', '\\bigsqcup'],
'222B' => ['OP', '\\smallint'],
# Binary operations
'25C3' => ['BIN', '\\triangleleft'],
'25B9' => ['BIN', '\\triangleright'],
'25B3' => ['BIN', '\\bigtriangleup'],
'25BD' => ['BIN', '\\bigtriangledown'],
'2227' => ['BIN', '\\wedge'],
'2228' => ['BIN', '\\vee'],
'2229' => ['BIN', '\\cap'],
'222A' => ['BIN', '\\cup'],
'2021' => ['BIN', '\\ddagger'],
'2020' => ['BIN', '\\dagger'],
'2293' => ['BIN', '\\sqcap'],
'2294' => ['BIN', '\\sqcup'],
'228E' => ['BIN', '\\uplus'],
'2A3F' => ['BIN', '\\amalg'],
'22C4' => ['BIN', '\\diamond'],
'2219' => ['BIN', '\\bullet'],
'2240' => ['BIN', '\\wr'],
'00F7' => ['BIN', '\\div'],
'2299' => ['BIN', '\\odot'],
'2298' => ['BIN', '\\oslash'],
'2297' => ['BIN', '\\otimes'],
'2296' => ['BIN', '\\ominus'],
'2295' => ['BIN', '\\oplus'],
'2213' => ['BIN', '\\mp'],
'00B1' => ['BIN', '\\pm'],
'2218' => ['BIN', '\\circ'],
'25EF' => ['BIN', '\\bigcirc'],
'2216' => ['BIN', '\\setminus'],
'22C5' => ['BIN', '\\cdot'],
'2217' => ['BIN', '\\ast'],
'00D7' => ['BIN', '\\times'],
'22C6' => ['BIN', '\\star'],
# Relations
'221D' => ['REL', '\\propto'],
'2291' => ['REL', '\\sqsubseteq'],
'2292' => ['REL', '\\sqsupseteq'],
'2225' => ['REL', '\\parallel'],
'2223' => ['REL', '\\mid'],
'22A3' => ['REL', '\\dashv'],
'22A2' => ['REL', '\\vdash'],
'2264' => ['REL', '\\leq'],
'2265' => ['REL', '\\geq'],
'227B' => ['REL', '\\succ'],
'227A' => ['REL', '\\prec'],
'2248' => ['REL', '\\approx'],
# '227D' => ['REL', '\\succeq'],
# '227C' => ['REL', '\\preceq'],
'2AB0' => ['REL', '\\succeq'],
'2AAF' => ['REL', '\\preceq'],
'2283' => ['REL', '\\supset'],
'2282' => ['REL', '\\subset'],
'2287' => ['REL', '\\supseteq'],
'2286' => ['REL', '\\subseteq'],
'2208' => ['REL', '\\in'],
'220B' => ['REL', '\\ni'],
'2209' => ['REL', '\\notin'],
'220B' => ['REL', '\\owns'],
'226B' => ['REL', '\\gg'],
'226A' => ['REL', '\\ll'],
'223C' => ['REL', '\\sim'],
'2243' => ['REL', '\\simeq'],
'22A5' => ['REL', '\\perp'],
'2261' => ['REL', '\\equiv'],
'224D' => ['REL', '\\asymp'],
'2323' => ['REL', '\\smile'],
'2322' => ['REL', '\\frown'],
'2260' => ['REL', '\\ne'],
'2245' => ['REL', '\\cong'],
'2250' => ['REL', '\\doteq'],
'22C8' => ['REL', '\\bowtie'],
'22A8' => ['REL', '\\models'],
'0338' => ['REL', '\\not'],
# Arrows
'21D4' => ['REL', '\\Leftrightarrow'],
'21D0' => ['REL', '\\Leftarrow'],
'21D2' => ['REL', '\\Rightarrow'],
'2194' => ['REL', '\\leftrightarrow'],
'2190' => ['REL', '\\leftarrow'],
'2192' => ['REL', '\\rightarrow'],
'21A6' => ['REL', '\\mapsto'],
'21BC' => ['REL', '\\leftharpoonup'],
'21BD' => ['REL', '\\leftharpoondown'],
'21C0' => ['REL', '\\rightharpoonup'],
'21C1' => ['REL', '\\rightharpoondown'],
'2197' => ['REL', '\\nearrow'],
'2198' => ['REL', '\\searrow'],
'2196' => ['REL', '\\nwarrow'],
'2199' => ['REL', '\\swarrow'],
'21CC' => ['REL', '\\rightleftharpoons'],
'21AA' => ['REL', '\\hookrightarrow'],
'21A9' => ['REL', '\\hookleftarrow'],
'27F5' => ['REL', '\\longleftarrow'],
'27F8' => ['REL', '\\Longleftarrow'],
'27F6' => ['REL', '\\longrightarrow'],
'27F9' => ['REL', '\\Longrightarrow'],
'27FA' => ['REL', '\\Longleftrightarrow'],
'27F7' => ['REL', '\\longleftrightarrow'],
'27FC' => ['REL', '\\longmapsto'],
# Dots
'2026' => ['INNER', '\\ldots'],
'22EF' => ['INNER', '\\cdots'],
'22EE' => ['ORD', '\\vdots'],
'22F1' => ['INNER', '\\ddots'],
'002E' => ['PUNCT', '\\ldotp','separator'],
# '22C5' => ['PUNCT', '\\cdotp'], # already given above
# '003A' => ['PUNCT', '\\colon'], # already given in dictionary
# Over-under
'203E' => ['ORD', '\\overline','stretchy'],
'23AF' => ['ORD', '\\underline','stretchy'],
'23DE' => ['ORD', '\\overbrace','stretchy'],
'23DF' => ['ORD', '\\underbrace','stretchy'],
# Accents
'0301' => ['ORD', '\\acute','accent'],
'0300' => ['ORD', '\\grave','accent'],
'0308' => ['ORD', '\\ddot','accent'],
'0303' => ['ORD', '\\tilde','accent stretchy'],
'0304' => ['ORD', '\\bar','accent'],
'0306' => ['ORD', '\\breve','accent'],
'030C' => ['ORD', '\\check','accent'],
'0302' => ['ORD', '\\hat','accent stretchy'],
'20D7' => ['ORD', '\\vec','accent'],
'0307' => ['ORD', '\\dot','accent'],
'02CA' => ['ORD', '\\acute','accent'],
'02CB' => ['ORD', '\\grave','accent'],
'00A8' => ['ORD', '\\ddot','accent'],
'02DC' => ['ORD', '\\tilde','accent stretchy'],
'02C9' => ['ORD', '\\bar','accent'],
'02D8' => ['ORD', '\\breve','accent'],
'02C7' => ['ORD', '\\check','accent'],
'02C6' => ['ORD', '\\hat','accent stretchy'],
'02D9' => ['ORD', '\\dot','accent'],
# Delimiters
'2191' => ['REL', '\\uparrow'],
'2193' => ['REL', '\\downarrow'],
'2195' => ['REL', '\\updownarrow'],
'21D1' => ['REL', '\\Uparrow'],
'21D3' => ['REL', '\\Downarrow'],
'21D5' => ['REL', '\\Updownarrow'],
'2308' => ['OPEN', '\\lceil'],
'2309' => ['CLOSE', '\\rceil'],
'230A' => ['OPEN', '\\lfloor'],
'230B' => ['CLOSE', '\\rfloor'],
'23AA' => ['ORD', '\\bracevert'],
'23B0' => ['OPEN', '\\lmoustache','fence stretchy symmetric'],
'23B1' => ['CLOSE', '\\rmoustache','fence stretchy symmetric'],
'27E8' => ['OPEN', '\\langle'],
'27E9' => ['CLOSE', '\\rangle'],
'27EE' => ['OPEN', '\\lgroup','fence stretchy symmetric'],
'27EF' => ['CLOSE', '\\rgroup','fence stretchy symmetric'],
# invisibles
'2061' => ['ORD','function application'],
'2062' => ['ORD','invisible times'],
'2063' => ['ORD','invisible separator','separator'],
'2064' => ['ORD','invisible plus'],
# aliases
'00AF' => ['ORD','horizontal line','accent stretchy'],
'2015' => ['ORD','horizontal line','stretchy'],
'2017' => ['ORD','horizontal line','stretchy'],
'0332' => ['ORD','horizontal line','accent stretchy'],
'2329' => ['OPEN','langle','fence stretchy symmetric'],
'232A' => ['CLOSE','rangle','fence stretchy symmetric'],
'2500' => ['ORD','horizontal line'],
'2758' => ['REL','vertical separator','stretchy symmetric'],
'3008' => ['OPEN','langle','fence stretchy symmetric'],
'3009' => ['CLOSE','rangle','fence stretchy symmetric'],
'FE37' => ['ORD','horizontal brace down','accent stretchy'],
'FE38' => ['ORD','horizontal brace up','accent stretchy'],
'00B7' => ['BIN','center dot'],
'02B9' => ['ORD','prime'],
'2022' => ['BIN','bullet'],
'2044' => ['BIN','fraction slash'],
'2305' => ['BIN','barwedge'],
'2306' => ['BIN','doublebarwedge'],
'25B5' => ['BIN','triangle'],
'25BF' => ['BIN','triangledown'],
'2A2F' => ['BIN','cross product'],
);
%LSPACE = (ORD => 0, OP => 0, BIN => 3, REL => 4, OPEN => 0, CLOSE => 0, PUNCT => 0);
%RSPACE = (ORD => 0, OP => 0, BIN => 3, REL => 4, OPEN => 0, CLOSE => 0, PUNCT => 3);
@CLASS = (
'[0,0,TEXCLASS.ORD]' => 'ORD',
'[1,1,TEXCLASS.ORD]' => 'ORD11',
'[2,1,TEXCLASS.ORD]' => 'ORD21',
'[0,2,TEXCLASS.ORD]' => 'ORD02',
'[5,5,TEXCLASS.ORD]' => 'ORD55',
'[1,2,TEXCLASS.OP,{largeop: true, movablelimits: true, symmetric: true}]' => 'OP',
'[1,2,TEXCLASS.OP,{largeop: true, movablelimits: true}]' => 'OPFIXED',
'[0,1,TEXCLASS.OP,{largeop: true, symmetric: true}]' => 'INTEGRAL',
'[1,2,TEXCLASS.OP,{largeop: true, symmetric: true}]' => 'INTEGRAL2',
'[3,3,TEXCLASS.BIN]' => 'BIN3',
'[4,4,TEXCLASS.BIN]' => 'BIN4',
'[0,1,TEXCLASS.BIN]' => 'BIN01',
'[4,4,TEXCLASS.BIN,{stretchy: true}]' => 'TALLBIN',
'[4,4,TEXCLASS.BIN,{largeop: true, movablelimits: true}]' => 'BINOP',
'[5,5,TEXCLASS.REL]' => 'REL',
'[1,1,TEXCLASS.REL,{stretchy: true}]' => 'REL1',
'[4,4,TEXCLASS.REL]' => 'REL4',
'[5,5,TEXCLASS.REL,{stretchy: true}]' => 'WIDEREL',
'[5,5,TEXCLASS.REL,{accent: true, stretchy: true}]' => 'RELACCENT',
'[0,0,TEXCLASS.OPEN,{fence: true, stretchy: true, symmetric: true}]' => 'OPEN',
'[0,0,TEXCLASS.CLOSE,{fence: true, stretchy: true, symmetric: true}]' => 'CLOSE',
'[0,0,TEXCLASS.INNER]' => 'INNER',
'[0,3,TEXCLASS.PUNCT]' => 'PUNCT',
'[0,0,TEXCLASS.ORD,{accent: true}]' => 'ACCENT',
'[0,0,TEXCLASS.ORD,{accent: true, stretchy: true}]' => 'WIDEACCENT',
);
while (@CLASS) {
my $def = shift(@CLASS), $name = shift(@CLASS);
$CLASS{$def} = $name;
$CLASSDEF{$name} = $def;
push(@CLASSES,$name);
}
foreach $form (reverse(sort(keys %op))) {
foreach $id (sort(keys %{$op{$form}})) {
$ID = $id; $UID = $id; $UID =~ s/([\\'])/\\\1/g;
if ($ID =~ m/^&#x([0-9A-F]+);$/i) {$ID = $1; while (length($ID) < 4) {$ID = "0$ID"}; $UID = "\\u$ID"}
$UID =~ s/&#x([0-9A-F]{4});/\\u\1/g; $UID =~ s/&#x([0-9A-F]{3});/\\u0\1/g;
($lspace,$rspace,$attributes,$name) = @{$op{$form}{$id}};
$class = ($TEX{$ID} ? $TEX{$ID}[0] : undef);
$class = $class->{$form} if ref($class) eq 'HASH';
if (!($hasClass = defined($class))) {
$class = "ORD";
$class = "REL" if $lspace == 5;
$class = "BIN" if $lspace == 4 || $lspace == 3;
$class = "ORD" if $lspace == 0 && $rspace == 0;
$class = "OPEN" if $attributes =~ m/fence/ && $name =~ m/open|left/i;
$class = "CLOSE" if $attributes =~ m/fence/ && $name =~ m/close|right/i;
$class = "OP" if $attributes =~ m/largeop/;
$class = "BIN" if length($ID) == 2;
}
@DATA = ($lspace,$rspace,"TEXCLASS.$class");
push(@DATA,"{".join(': true, ',sort(split(/ /,$attributes))).": true}") if $attributes;
$DATA = "[".join(",",@DATA)."]";
$DATA = "MO.$CLASS{$DATA}" if defined $CLASS{$DATA};
$DATA = sprintf("%-30s"," '$UID': $DATA,")." // $name\n";
$USED{$ID} = 1;
if ($hasClass) {$unicode = "Main"} else {$unicode = GetRange($ID)}
$range{$unicode} = {} unless defined $range{$unicode};
$range{$unicode}{$form} = [] unless defined $range{$unicode}{$form};
push(@{$range{$unicode}{$form}},$DATA);
}
if ($form eq 'infix') {
foreach $ID (sort(keys %TEX)) {
unless ($USED{$ID}) {
next if $onlyTeX && !$TEX{$ID};
@DATA = ($LSPACE{$TEX{$ID}[0]},$RSPACE{$TEX{$ID}[0]},"TEXCLASS.$TEX{$ID}[0]");
push(@DATA,"{".join(': true, ',sort(split(/ /,$TEX{$ID}[2]))).": true}") if $TEX{$ID}[2];
$DATA = "[".join(",",@DATA)."]";
$DATA = "MO.$CLASS{$DATA}" if defined $CLASS{$DATA};
$UID = $ID; $UID = "\\u$ID" if length($UID) == 4;
$DATA = sprintf("%-30s"," '$UID': $DATA,")." // $TEX{$ID}[1]\n";
$range{Main} = {} unless defined $range{Main};
$range{Main}{infix} = [] unless defined $range{Main}{infix};
push(@{$range{Main}{infix}},$DATA);
}
}
}
}
mkdir "optable" unless -e "optable";
@RANGEDEF = ();
foreach $def (@RANGES) {
my ($name,$l,$u,$class) = @$def;
if (defined $range{$name}) {
push(@RANGEDEF,"[".sprintf("0x%X,0x%X",$l,$u).",TEXCLASS.$class,\"$name\"]");
} elsif ($class ne 'REL') {
push(@RANGEDEF,"[".sprintf("0x%X,0x%X",$l,$u).",TEXCLASS.$class]");
}
}
foreach $unicode (sort(keys %range)) {
print "$unicode...";
@forms = ();
if ($unicode eq "Main") {
foreach $form (reverse(sort(keys %{$range{$unicode}}))) {
$lines = $range{$unicode}{$form};
$lines->[scalar(@$lines)-1] =~ s!\, ( *)//! \1//!;
push(@forms," $form: {\n".join("",@$lines)." }");
}
open(JSFILE,">optable.js") || die "Can't write optable.js";
print JSFILE " var MO = {\n";
print JSFILE " ",join(",\n ",map {sprintf("%-11s",$_.":")." ".$CLASSDEF{$_}} @CLASSES),"\n";
print JSFILE " };\n\n";
print JSFILE " MML.mo.Augment({\n";
print JSFILE " SPACE: [\n";
print JSFILE " '0em',\n";
print JSFILE " '0.1111em',\n";
print JSFILE " '0.1667em',\n";
print JSFILE " '0.2222em',\n";
print JSFILE " '0.2667em',\n";
print JSFILE " '0.3333em'\n";
print JSFILE " ],\n";
print JSFILE " RANGES: [\n";
print JSFILE " ",join(",\n ",@RANGEDEF),"\n";
print JSFILE " ],\n";
print JSFILE " OPTABLE: {\n";
print JSFILE join(",\n",@forms),"\n";
print JSFILE " }\n";
print JSFILE " },{\n";
print JSFILE " OPTYPES: MO\n";
print JSFILE " });\n";
close(JSFILE);
} else {
foreach $form (reverse(sort(keys %{$range{$unicode}}))) {
$lines = $range{$unicode}{$form};
$lines->[scalar(@$lines)-1] =~ s!, ( *)//! \1//!;
push(@forms," $form: {\n".join("",@$lines)." }");
}
open(JSFILE,">optable/$unicode.js") || die "Can't write optable/$unicode.js";
print JSFILE "/*************************************************************\n";
print JSFILE " *\n";
print JSFILE " * MathJax/jax/output/HTML-CSS/optable/$unicode.js\n";
print JSFILE " *\n";
print JSFILE " * Copyright (c) 2010 Design Science, Inc.\n";
print JSFILE " *\n";
print JSFILE " * Licensed under the Apache License, Version 2.0 (the \"License\");\n";
print JSFILE " * you may not use this file except in compliance with the License.\n";
print JSFILE " * You may obtain a copy of the License at\n";
print JSFILE " *\n";
print JSFILE " * http://www.apache.org/licenses/LICENSE-2.0\n";
print JSFILE " *\n";
print JSFILE " * Unless required by applicable law or agreed to in writing, software\n";
print JSFILE " * distributed under the License is distributed on an \"AS IS\" BASIS,\n";
print JSFILE " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n";
print JSFILE " * See the License for the specific language governing permissions and\n";
print JSFILE " * limitations under the License.\n";
print JSFILE " *\n";
print JSFILE " */\n\n";
print JSFILE "(function (MML) {\n";
print JSFILE " var MO = MML.mo.OPTYPES;\n";
print JSFILE " var TEXCLASS = MML.TEXCLASS;\n\n";
print JSFILE " MathJax.Hub.Insert(MML.mo.prototype,{\n";
print JSFILE " OPTABLE: {\n";
print JSFILE join(",\n",@forms),"\n";
print JSFILE " }\n";
print JSFILE " });\n\n";
print JSFILE " MathJax.Ajax.loadComplete(MML.optableDir+\"/$unicode.js\");\n\n";
print JSFILE "})(MathJax.ElementJax.mml);\n";
close(JSFILE);
}
print "\n";
}