Skip to content

Commit

Permalink
Merge pull request #1125 from googlefonts/get-parse-tree
Browse files Browse the repository at this point in the history
[fea-rs] Make parse_string return ParseTree
  • Loading branch information
cmyr authored Nov 15, 2024
2 parents d8fc6b9 + f5956f7 commit 619e495
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 22 deletions.
10 changes: 7 additions & 3 deletions fea-lsp/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,13 @@ fn compute_offsets(text: &str) -> Vec<usize> {
}

fn parse(text: &str) -> (Vec<(Kind, Range<usize>)>, Vec<fea_rs::Diagnostic>) {
let (root, errors) = fea_rs::parse::parse_string(text);
let result = root.iter_tokens().map(|t| (t.kind, t.range())).collect();
(result, errors)
let (ast, errors) = fea_rs::parse::parse_string(text);
let result = ast
.root()
.iter_tokens()
.map(|t| (t.kind, t.range()))
.collect();
(result, errors.diagnostics().to_vec())
}

pub static STYLES: &[SemanticTokenType] = &[
Expand Down
2 changes: 1 addition & 1 deletion fea-rs/benches/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const DEVA: &str = include_str!("../test-data/real-files/plex_devanagari.fea");
const LATN: &str = include_str!("../test-data/real-files/roboto-regular.fea");
const ARAB: &str = include_str!("../test-data/real-files/tajawal-regular.fea");

fn parse_source(source: Arc<str>) -> fea_rs::Node {
fn parse_source(source: Arc<str>) -> fea_rs::ParseTree {
fea_rs::parse::parse_string(source).0
}

Expand Down
4 changes: 2 additions & 2 deletions fea-rs/src/bin/highlight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ use std::{env, ffi::OsStr, path::PathBuf};
fn main() {
let args = Args::get_from_env_or_exit();
let raw_fea = std::fs::read_to_string(args.path).unwrap();
let (node, _errors) = fea_rs::parse::parse_string(raw_fea);
let (ast, _errors) = fea_rs::parse::parse_string(raw_fea);
let mut current_style = Style::new().fg(Colour::White);
let mut needs_paint = String::new();

for token in node.iter_tokens() {
for token in ast.root().iter_tokens() {
let style = fea_rs::util::style_for_kind(token.kind);
// if the style has changed, draw the previous range.
if style != current_style {
Expand Down
5 changes: 5 additions & 0 deletions fea-rs/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ impl DiagnosticSet {
})
}

/// Return the underlying diagnostics, as a slice
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.messages
}

/// Returns an opaque type that can pretty-print the diagnostics
pub fn display(&self) -> impl std::fmt::Display + '_ {
DiagnosticDisplayer(self)
Expand Down
38 changes: 30 additions & 8 deletions fea-rs/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ mod parser;
mod source;
mod tree;

use std::{ffi::OsString, path::PathBuf, sync::Arc};
use std::{
ffi::{OsStr, OsString},
path::PathBuf,
sync::Arc,
};

pub use lexer::TokenSet;
pub use source::{FileSystemResolver, SourceLoadError, SourceResolver};
Expand All @@ -20,7 +24,7 @@ pub(crate) use context::{IncludeStatement, ParseContext};
pub(crate) use parser::Parser;
pub(crate) use source::{FileId, Source, SourceList, SourceMap};

use crate::{Diagnostic, DiagnosticSet, GlyphMap, Node};
use crate::{DiagnosticSet, GlyphMap};

/// Attempt to parse a feature file from disk, including its imports.
///
Expand Down Expand Up @@ -76,21 +80,39 @@ pub fn parse_root(
/// Convenience method to parse a block of FEA from memory.
///
/// This is useful for things like testing or syntax highlighting of a single file,
/// but it cannot handle imports, or handle ambiguous glyph names.
/// but it cannot handle includes, or handle ambiguous glyph names.
///
/// The input text can be any of `&str`, `String`, or `Arc<str>`.
pub fn parse_string(text: impl Into<Arc<str>>) -> (Node, Vec<Diagnostic>) {
let source = source::Source::new("<parse::parse_string>", text.into());
let (node, errs, _) = context::parse_src(&source, None);
(node, errs)
///
/// # Panics
///
/// Panics if the input contains any include statements.
pub fn parse_string(text: impl Into<Arc<str>>) -> (ParseTree, DiagnosticSet) {
const SRC_NAME: &str = "parse::parse_string";
let text = text.into();
parse_root(
SRC_NAME.into(),
None,
Box::new(move |s: &OsStr| {
if s == SRC_NAME {
Ok(text.clone())
} else {
Err(SourceLoadError::new(
s.to_os_string(),
"parse_string cannot handle imports",
))
}
}),
)
.unwrap()
}

/// Parse an arbitrary block of FEA text with a specific parsing function.
///
/// This can be used to parse any part of the grammar, including elements that
/// are not valid at the top level.
#[cfg(test)]
pub(crate) fn parse_node(text: &str, parser_fn: impl FnOnce(&mut Parser)) -> Node {
pub(crate) fn parse_node(text: &str, parser_fn: impl FnOnce(&mut Parser)) -> crate::Node {
let mut sink = crate::token_tree::AstSink::new(text, FileId::CURRENT_FILE, None);
let mut parser = Parser::new(text, &mut sink);
parser_fn(&mut parser);
Expand Down
8 changes: 6 additions & 2 deletions fea-rs/src/token_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,8 +631,12 @@ mod tests {

#[test]
fn token_iter() {
let (root, _errs) = crate::parse::parse_string(SAMPLE_FEA);
let reconstruct = root.iter_tokens().map(Token::as_str).collect::<String>();
let (ast, _errs) = crate::parse::parse_string(SAMPLE_FEA);
let reconstruct = ast
.root()
.iter_tokens()
.map(Token::as_str)
.collect::<String>();
crate::assert_eq_str!(SAMPLE_FEA, reconstruct);
}
}
12 changes: 6 additions & 6 deletions fea-rs/src/token_tree/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,10 @@ mod tests {

#[test]
fn abs_positions() {
let (root, errs) = crate::parse::parse_string(SAMPLE_FEA);
let (ast, errs) = crate::parse::parse_string(SAMPLE_FEA);
assert!(errs.is_empty());
let mut last_end = 0;
for token in root.iter_tokens() {
for token in ast.root().iter_tokens() {
assert_eq!(
token.range().start,
last_end,
Expand All @@ -214,8 +214,8 @@ mod tests {

#[test]
fn ascend_jump() {
let (root, _errs) = crate::parse::parse_string(SAMPLE_FEA);
let mut cursor = root.cursor();
let (ast, _errs) = crate::parse::parse_string(SAMPLE_FEA);
let mut cursor = ast.root().cursor();
cursor.advance();
cursor.advance();
cursor.advance();
Expand Down Expand Up @@ -250,9 +250,9 @@ mod tests {

#[test]
fn advance() {
let (root, errs) = crate::parse::parse_string("feature kern { pos a b -20; }kern;");
let (ast, errs) = crate::parse::parse_string("feature kern { pos a b -20; }kern;");
assert!(errs.is_empty());
let mut cursor = root.cursor();
let mut cursor = ast.root().cursor();
assert!(
at_node(&cursor, Kind::FeatureNode),
"{:?}",
Expand Down

0 comments on commit 619e495

Please sign in to comment.