use tree_sitter;
use tree_sitter_applesoft;
use crate::lang;
use crate::lang::Visit;
use std::collections::HashMap;
use log::{trace,error};
use crate::{STDRESULT,DYNERR};
struct Primaries {
line: String,
nums: Vec<usize>,
}
impl Visit for Primaries {
fn visit(&mut self,curs: &tree_sitter::TreeCursor) -> lang::WalkerChoice {
if let Some(parent) = curs.node().parent() {
let grab_it = match (parent.kind(),curs.node().kind()) {
("line","linenum") => true,
_ => false
};
if grab_it {
self.push_linenum(curs).expect("unexpected parsing error");
return lang::WalkerChoice::GotoParentSibling;
}
}
return lang::WalkerChoice::GotoChild;
}
}
impl Primaries {
fn new() -> Self {
Self {
line: String::new(),
nums: Vec::new()
}
}
fn push_linenum(&mut self,curs: &tree_sitter::TreeCursor) -> STDRESULT {
let txt = lang::node_text(curs.node(), &self.line).replace(" ","");
trace!("found primary line number {}",txt);
if let Ok(num) = usize::from_str_radix(&txt, 10) {
self.nums.push(num);
return Ok(());
}
Err(Box::new(lang::Error::Syntax))
}
fn get_primary_nums(&mut self,source: &str, parser: &mut tree_sitter::Parser) -> Vec<usize> {
self.nums = Vec::new();
for line in source.clone().lines() {
if line.len()==0 {
continue;
}
self.line = line.to_string() + "\n";
let tree = parser.parse(&self.line,None).expect("error parsing");
self.walk(&tree);
}
self.nums.clone()
}
}
pub struct Renumberer {
line: String,
renumbered: String,
last_end_point: Option<tree_sitter::Point>,
map: HashMap<usize,usize>
}
impl Visit for Renumberer {
fn visit(&mut self,curs: &tree_sitter::TreeCursor) -> lang::WalkerChoice {
match curs.node().kind() {
"linenum" => {
let txt = lang::node_text(curs.node(),&self.line);
trace!("line number node {}",txt);
if let Ok(num) = usize::from_str_radix(&txt.replace(" ",""),10) {
if let Some(new_num) = self.map.get(&num) {
let leading = txt.len() - txt.trim_start().len();
let trailing = txt.len() - txt.trim_end().len();
let mut fmt_num = String::from(" ").repeat(leading);
fmt_num += &new_num.to_string();
fmt_num += &String::from(" ").repeat(trailing);
self.concat(&fmt_num,&curs.node());
} else {
self.concat(&txt,&curs.node());
}
}
},
_ => if curs.node().named_child_count()==0 {
let txt = lang::node_text(curs.node(),&self.line);
self.concat(&txt,&curs.node());
return lang::WalkerChoice::GotoSibling;
}
};
lang::WalkerChoice::GotoChild
}
}
impl Renumberer {
pub fn new() -> Self {
Self {
line: String::new(),
renumbered: String::new(),
last_end_point: None,
map: HashMap::new()
}
}
fn concat(&mut self,item: &str,node: &tree_sitter::Node) {
if let Some(end) = self.last_end_point {
if node.start_position().column>end.column {
self.renumbered += &" ".repeat(node.start_position().column-end.column);
}
}
self.renumbered += item;
self.last_end_point = Some(node.end_position());
}
pub fn renumber(&mut self,source: &str, beg: usize, end: usize, first: usize, step: usize)
-> Result<String,DYNERR> {
self.renumbered = String::new();
self.map = HashMap::new();
let mut parser = tree_sitter::Parser::new();
parser.set_language(tree_sitter_applesoft::language()).expect("error loading applesoft grammar");
let mut primary_finder = Primaries::new();
let primaries = primary_finder.get_primary_nums(source, &mut parser);
let mut curr = first;
for i in 0..primaries.len() {
if primaries[i]>=beg && primaries[i]<end {
if curr > 63999 {
error!("attempt to create line {}, max is 63999",curr);
return Err(Box::new(lang::Error::LineNumber));
}
if i > 0 && primaries[i-1] < beg && curr <= primaries[i-1] {
error!("mapping {} to {} violates lower bound",primaries[i],curr);
return Err(Box::new(lang::Error::LineNumber));
}
if i < primaries.len()-1 && primaries[i+1] >= end && curr >= primaries[i+1] {
error!("mapping {} to {} violates upper bound",primaries[i],curr);
return Err(Box::new(lang::Error::LineNumber));
}
trace!("mapping {} to {}",primaries[i],curr);
self.map.insert(primaries[i],curr);
curr += step;
}
}
for line in source.lines() {
if line.len()==0 {
self.renumbered += "\n";
continue;
}
self.line = String::from(line) + "\n";
let tree = parser.parse(&self.line,None).expect("error parsing");
self.walk(&tree);
self.renumbered += "\n";
}
Ok(self.renumbered.clone())
}
}