From 36cf5c88cfe5a3141eaa784905f3a3bcc0f7f70c Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 15 Jun 2019 19:15:48 +0100 Subject: Proof of concept extraction state machine --- fable-cat-poc/src/main.rs | 152 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 fable-cat-poc/src/main.rs (limited to 'fable-cat-poc/src') diff --git a/fable-cat-poc/src/main.rs b/fable-cat-poc/src/main.rs new file mode 100644 index 0000000..fb82b8a --- /dev/null +++ b/fable-cat-poc/src/main.rs @@ -0,0 +1,152 @@ +use std::io::{self, Read}; +use std::ops::{Range, RangeBounds}; + +use pulldown_cmark::{Event, Options, Parser, Tag}; + +#[derive(Default)] +struct Span { + first_line: usize, + first_column: usize, + last_line: usize, + last_column: usize, +} + +fn location_to_span(input: &str, loc: &Range) -> Span { + let mut ret = Span::default(); + + let mut line = 1; + let mut col = 1; + for (pos, ch) in input.chars().enumerate() { + if ch == '\n' { + line += 1; + col = 1; + } else { + col += 1; + } + if pos == loc.start { + ret.first_line = line; + ret.first_column = col; + } + if pos == loc.end { + ret.last_line = line; + ret.last_column = col; + } + } + + ret +} + +fn print_thing(input: &str, loc: &Range) { + let content = &input[loc.clone()]; + let span = location_to_span(input, loc); + println!( + "> From line {} column {} to line {} column {}", + span.first_line, span.first_column, span.last_line, span.last_column + ); + for l in content.lines() { + println!("> `{}`", l); + } + if span.first_line == span.last_line { + print!("> `"); + for _ in 0..span.first_column { + print!(" "); + } + for _ in span.first_column..=span.last_column { + print!("^"); + } + println!("`"); + } +} + +fn main() -> io::Result<()> { + let mut input = String::new(); + let inputlen = io::stdin().read_to_string(&mut input)?; + eprintln!("Read {} bytes of input", inputlen); + let options = Options::all(); + let parser = Parser::new_ext(&input, options); + + let mut depth = 0; + let mut in_scenario = false; + let mut in_scenario_block = false; + let mut in_header = false; + let mut last_header_depth = 1000; + let mut last_header_content: Option = None; + for (event, span) in parser.into_offset_iter() { + eprintln!("{:?}", event); + match event { + Event::Start(tag) => { + depth += 1; + eprintln!("Depth now: {}", depth); + if depth == 1 { + match tag { + Tag::Header(level) => { + // Two ways to handle a header, + // If we're in a scenario, we care only if this header + // is at the same or a higher level, otherwise we + // always read the header in. + if in_scenario { + if level <= last_header_depth { + println!("```"); + in_scenario = false; + in_header = true; + last_header_depth = level; + } + } else { + in_header = true; + last_header_depth = level; + } + } + Tag::CodeBlock(name) => { + if name.as_ref() == "fable" { + // Fable code block, only valid if we were + // after a header + if last_header_content.is_some() { + in_scenario_block = true; + } + } + } + _ => {} + } + } + } + Event::End(tag) => { + depth -= 1; + eprintln!("Depth now: {}", depth); + if depth == 0 { + match tag { + Tag::Header(_) => { + // We're ending a header now + in_header = false; + } + Tag::CodeBlock(_) => { + in_scenario_block = false; + } + _ => {} + } + } + } + Event::Text(s) => { + if in_header { + // We're parsing a header + last_header_content = Some(s.to_string()); + in_header = false; + if cfg!(feature = "print_headers") { + print_thing(&input, &span); + } + } else if in_scenario_block { + if !in_scenario { + for _ in 0..last_header_depth { + print!("#"); + } + println!(" {}", last_header_content.as_ref().unwrap()); + in_scenario = true; + println!("```fable"); + } + print!("{}", s); + } + } + _ => {} + } + } + Ok(()) +} -- cgit v1.2.1