Skip to content

Commit a34268f

Browse files
committed
Add MathML Core operator dictionary
1 parent d6a4834 commit a34268f

File tree

9 files changed

+1558
-346
lines changed

9 files changed

+1558
-346
lines changed

Cargo.lock

Lines changed: 106 additions & 246 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ license = "Apache-2.0"
1515

1616
[features]
1717
fonts = []
18+
19+
[dependencies]
20+
bitflags = "2"

NOTICE

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,10 @@ The W3C Document license applies to
827827
https://www.w3.org/TR/referrer-policy/
828828
Copyright © 2017 World Wide Web Consortium.
829829

830+
* The parts of the data found in `files/mathml/data.rs` that were derived from
831+
https://www.w3.org/TR/mathml-core/
832+
Copyright © 2025 World Wide Web Consortium.
833+
830834
This work is being provided by the copyright holders under the following license.
831835

832836
# License
@@ -888,4 +892,4 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
888892
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
889893
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
890894

891-
================================================================================
895+
================================================================================

codegen/src/html.rs

Lines changed: 4 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
//! The spec dir will automatically be populated with the necessary
44
//! specifications if one is missing.
55
6-
use std::borrow::Cow;
7-
use std::fmt::{Display, Write};
8-
use std::path::PathBuf;
6+
use std::fmt::Write;
97

8+
use crate::*;
109
use regex::Regex;
11-
use scraper::{ElementRef, Html, Selector};
10+
use scraper::{ElementRef, Selector};
1211

1312
// When referencing more specs, make sure to update the NOTICE file.
1413
#[rustfmt::skip]
@@ -22,7 +21,7 @@ mod spec {
2221

2322
pub fn main() {
2423
// Directory where specs will be read from / downloaded into.
25-
let dir = std::env::args_os().nth(2).map(PathBuf::from);
24+
let dir = std::env::args_os().nth(2).map(std::path::PathBuf::from);
2625
let mut ctx = Context {
2726
html: load_spec(&dir, spec::HTML),
2827
aria: load_spec(&dir, spec::ARIA),
@@ -52,28 +51,6 @@ pub fn main() {
5251
eprintln!("Success!");
5352
}
5453

55-
/// Reads a spec from the directory or, if it does not exist, fetches and stores it.
56-
fn load_spec(spec_dir: &Option<PathBuf>, url: &str) -> ElementRef<'static> {
57-
let text = if let Some(dir) = spec_dir {
58-
// Extract the last part of the URL as the filename.
59-
let name = url.rsplit_terminator("/").next().unwrap();
60-
let path = dir.join(name).with_extension("html");
61-
if path.exists() {
62-
eprintln!("Reading from {}", path.display());
63-
std::fs::read_to_string(&path).unwrap()
64-
} else {
65-
let text = crate::fetch(url);
66-
eprintln!("Writing to {}", path.display());
67-
std::fs::create_dir_all(dir).unwrap();
68-
std::fs::write(&path, &text).unwrap();
69-
text
70-
}
71-
} else {
72-
crate::fetch(url)
73-
};
74-
Box::leak(Box::new(Html::parse_document(&text))).root_element()
75-
}
76-
7754
struct Context<'a> {
7855
html: ElementRef<'a>,
7956
fetch: ElementRef<'a>,
@@ -252,28 +229,6 @@ impl Applicable {
252229
}
253230
}
254231

255-
/// Creates a lazily initialized static value.
256-
macro_rules! lazy {
257-
($ty:ty = $init:expr) => {{
258-
static VAL: ::std::sync::LazyLock<$ty> = ::std::sync::LazyLock::new(|| $init);
259-
&*VAL
260-
}};
261-
}
262-
263-
/// Creates a static CSS selector.
264-
macro_rules! s {
265-
($s:literal) => {
266-
lazy!(Selector = Selector::parse($s).unwrap())
267-
};
268-
}
269-
270-
/// Creates a lazily initialized regular expression.
271-
macro_rules! re {
272-
($s:expr) => {
273-
lazy!(Regex = Regex::new($s).unwrap())
274-
};
275-
}
276-
277232
/// Collects all attributes with documentation and descriptions.
278233
fn collect_attributes(ctx: &mut Context) -> Vec<AttrInfo> {
279234
let mut infos = vec![];
@@ -785,53 +740,3 @@ fn docs(text: &str) -> String {
785740
.to_owned()
786741
+ "."
787742
}
788-
789-
/// Helpers methods on [`ElementRef`].
790-
trait ElementRefExt<'a> {
791-
fn inner_text(&self) -> String;
792-
fn select_text(&self, selector: &Selector) -> String;
793-
fn select_first(&self, selector: &Selector) -> ElementRef<'a>;
794-
}
795-
796-
impl<'a> ElementRefExt<'a> for ElementRef<'a> {
797-
fn inner_text(&self) -> String {
798-
self.text().collect()
799-
}
800-
801-
fn select_text(&self, selector: &Selector) -> String {
802-
self.select(selector).flat_map(|elem| elem.text()).collect()
803-
}
804-
805-
#[track_caller]
806-
fn select_first(&self, selector: &Selector) -> ElementRef<'a> {
807-
self.select(selector)
808-
.next()
809-
.expect("found no matching element")
810-
}
811-
}
812-
813-
trait Join {
814-
fn join(self, separator: &str) -> String;
815-
}
816-
817-
impl<I, T> Join for I
818-
where
819-
I: Iterator<Item = T>,
820-
T: Display,
821-
{
822-
fn join(self, separator: &str) -> String {
823-
self.map(|v| v.to_string())
824-
.collect::<Vec<_>>()
825-
.join(separator)
826-
}
827-
}
828-
829-
trait StrExt {
830-
fn replace_regex(&self, re: &Regex, replacement: &str) -> Cow<'_, str>;
831-
}
832-
833-
impl StrExt for str {
834-
fn replace_regex(&self, re: &Regex, replacement: &str) -> Cow<'_, str> {
835-
re.replace_all(self, replacement)
836-
}
837-
}

codegen/src/main.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,38 @@
44
55
use std::io::Read;
66

7+
use regex::Regex;
8+
use scraper::{ElementRef, Html, Selector};
9+
10+
/// Creates a lazily initialized static value.
11+
macro_rules! lazy {
12+
($ty:ty = $init:expr) => {{
13+
static VAL: ::std::sync::LazyLock<$ty> = ::std::sync::LazyLock::new(|| $init);
14+
&*VAL
15+
}};
16+
}
17+
18+
/// Creates a static CSS selector.
19+
macro_rules! s {
20+
($s:literal) => {
21+
lazy!(Selector = Selector::parse($s).unwrap())
22+
};
23+
}
24+
25+
/// Creates a lazily initialized regular expression.
26+
macro_rules! re {
27+
($s:expr) => {
28+
lazy!(Regex = Regex::new($s).unwrap())
29+
};
30+
}
31+
732
mod html;
33+
mod mathml;
834

935
fn main() {
1036
match std::env::args().nth(1).as_deref() {
1137
Some("html") => html::main(),
38+
Some("mathml") => mathml::main(),
1239
Some(job) => panic!("unknown codegen job: {job}"),
1340
None => panic!("no codegen job provided"),
1441
}
@@ -27,3 +54,75 @@ fn fetch(url: &str) -> String {
2754
.unwrap();
2855
buf
2956
}
57+
58+
/// Reads a spec from the directory or, if it does not exist, fetches and stores it.
59+
fn load_spec(spec_dir: &Option<std::path::PathBuf>, url: &str) -> ElementRef<'static> {
60+
let text = if let Some(dir) = spec_dir {
61+
// Extract the last part of the URL as the filename.
62+
let name = url.rsplit_terminator("/").next().unwrap();
63+
let path = dir.join(name).with_extension("html");
64+
if path.exists() {
65+
eprintln!("Reading from {}", path.display());
66+
std::fs::read_to_string(&path).unwrap()
67+
} else {
68+
let text = fetch(url);
69+
eprintln!("Writing to {}", path.display());
70+
std::fs::create_dir_all(dir).unwrap();
71+
std::fs::write(&path, &text).unwrap();
72+
text
73+
}
74+
} else {
75+
crate::fetch(url)
76+
};
77+
Box::leak(Box::new(Html::parse_document(&text))).root_element()
78+
}
79+
80+
/// Helpers methods on [`ElementRef`].
81+
trait ElementRefExt<'a> {
82+
fn inner_text(&self) -> String;
83+
fn select_text(&self, selector: &Selector) -> String;
84+
fn select_first(&self, selector: &Selector) -> ElementRef<'a>;
85+
}
86+
87+
impl<'a> ElementRefExt<'a> for ElementRef<'a> {
88+
fn inner_text(&self) -> String {
89+
self.text().collect()
90+
}
91+
92+
fn select_text(&self, selector: &Selector) -> String {
93+
self.select(selector).flat_map(|elem| elem.text()).collect()
94+
}
95+
96+
#[track_caller]
97+
fn select_first(&self, selector: &Selector) -> ElementRef<'a> {
98+
self.select(selector)
99+
.next()
100+
.expect("found no matching element")
101+
}
102+
}
103+
104+
trait Join {
105+
fn join(self, separator: &str) -> String;
106+
}
107+
108+
impl<I, T> Join for I
109+
where
110+
I: Iterator<Item = T>,
111+
T: std::fmt::Display,
112+
{
113+
fn join(self, separator: &str) -> String {
114+
self.map(|v| v.to_string())
115+
.collect::<Vec<_>>()
116+
.join(separator)
117+
}
118+
}
119+
120+
trait StrExt {
121+
fn replace_regex(&self, re: &Regex, replacement: &str) -> std::borrow::Cow<'_, str>;
122+
}
123+
124+
impl StrExt for str {
125+
fn replace_regex(&self, re: &Regex, replacement: &str) -> std::borrow::Cow<'_, str> {
126+
re.replace_all(self, replacement)
127+
}
128+
}

0 commit comments

Comments
 (0)