mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: Handle comments between statements.
This commit is contained in:
parent
d884ea9385
commit
957d0c6102
@ -756,6 +756,7 @@ impl fmt::Display for Expression {
|
|||||||
/// Encodes a let statement in the UCG AST.
|
/// Encodes a let statement in the UCG AST.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct LetDef {
|
pub struct LetDef {
|
||||||
|
pub pos: Position,
|
||||||
pub name: Token,
|
pub name: Token,
|
||||||
pub value: Expression,
|
pub value: Expression,
|
||||||
}
|
}
|
||||||
@ -770,8 +771,19 @@ pub enum Statement {
|
|||||||
Let(LetDef),
|
Let(LetDef),
|
||||||
|
|
||||||
// Assert statement
|
// Assert statement
|
||||||
Assert(Expression),
|
Assert(Position, Expression),
|
||||||
|
|
||||||
// Identify an Expression for output.
|
// Identify an Expression for output.
|
||||||
Output(Position, Token, Expression),
|
Output(Position, Token, Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Statement {
|
||||||
|
fn pos(&self) -> &Position {
|
||||||
|
match self {
|
||||||
|
Statement::Expression(ref e) => e.pos(),
|
||||||
|
Statement::Let(ref def) => &def.pos,
|
||||||
|
Statement::Assert(ref pos, _) => pos,
|
||||||
|
Statement::Output(ref pos, _, _) => pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -29,6 +29,8 @@ where
|
|||||||
// Indexed by line that the comment was on.
|
// Indexed by line that the comment was on.
|
||||||
// We use this to determine when to print a comment in our AstPrinter
|
// We use this to determine when to print a comment in our AstPrinter
|
||||||
comment_map: Option<&'a CommentMap>,
|
comment_map: Option<&'a CommentMap>,
|
||||||
|
last_line: usize,
|
||||||
|
comment_group_lines: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(jwall): At some point we probably want to be more aware of line length
|
// TODO(jwall): At some point we probably want to be more aware of line length
|
||||||
@ -43,10 +45,14 @@ where
|
|||||||
curr_indent: 0,
|
curr_indent: 0,
|
||||||
comment_map: None,
|
comment_map: None,
|
||||||
w: w,
|
w: w,
|
||||||
|
last_line: 0,
|
||||||
|
comment_group_lines: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_comment_map(mut self, map: &'a CommentMap) -> Self {
|
pub fn with_comment_map(mut self, map: &'a CommentMap) -> Self {
|
||||||
|
self.comment_group_lines = map.keys().cloned().collect();
|
||||||
|
self.comment_group_lines.reverse();
|
||||||
self.comment_map = Some(map);
|
self.comment_map = Some(map);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -77,6 +83,47 @@ where
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_comment_group(&mut self, line: usize) -> std::io::Result<()> {
|
||||||
|
if let Some(ref map) = self.comment_map {
|
||||||
|
let empty: Vec<Token> = Vec::new();
|
||||||
|
//eprintln!("comment line candidate: {}", line);
|
||||||
|
let cg = map.get(&line).unwrap_or(&empty);
|
||||||
|
//eprintln!("comment_group: {:?}", cg);
|
||||||
|
for c in cg.iter() {
|
||||||
|
write!(self.w, "// {}\n", c.fragment.trim())?;
|
||||||
|
}
|
||||||
|
self.comment_group_lines.pop();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_missed_comments(&mut self, line: usize) -> std::io::Result<()> {
|
||||||
|
loop {
|
||||||
|
if let Some(next_comment_line) = self.comment_group_lines.last() {
|
||||||
|
let next_comment_line = *next_comment_line;
|
||||||
|
if next_comment_line < line {
|
||||||
|
self.print_comment_group(next_comment_line)?;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if next_comment_line < line - 1 {
|
||||||
|
write!(self.w, "\n")?;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_comment_if_needed(&mut self, line: usize) -> std::io::Result<()> {
|
||||||
|
if line > self.last_line {
|
||||||
|
self.render_missed_comments(line)?;
|
||||||
|
self.last_line = line;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn render_list_def(&mut self, def: &ListDef) -> std::io::Result<()> {
|
fn render_list_def(&mut self, def: &ListDef) -> std::io::Result<()> {
|
||||||
write!(self.w, "[")?;
|
write!(self.w, "[")?;
|
||||||
self.curr_indent += self.indent_size;
|
self.curr_indent += self.indent_size;
|
||||||
@ -351,6 +398,8 @@ where
|
|||||||
|
|
||||||
pub fn render_stmt(&mut self, stmt: &Statement) -> std::io::Result<()> {
|
pub fn render_stmt(&mut self, stmt: &Statement) -> std::io::Result<()> {
|
||||||
// All statements start at the beginning of a line.
|
// All statements start at the beginning of a line.
|
||||||
|
let line = stmt.pos().line;
|
||||||
|
self.render_comment_if_needed(line)?;
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Let(def) => {
|
Statement::Let(def) => {
|
||||||
write!(&mut self.w, "let {} = ", def.name.fragment)?;
|
write!(&mut self.w, "let {} = ", def.name.fragment)?;
|
||||||
@ -359,7 +408,7 @@ where
|
|||||||
Statement::Expression(_expr) => {
|
Statement::Expression(_expr) => {
|
||||||
self.render_expr(&_expr)?;
|
self.render_expr(&_expr)?;
|
||||||
}
|
}
|
||||||
Statement::Assert(def) => {
|
Statement::Assert(_, def) => {
|
||||||
write!(&mut self.w, "assert ")?;
|
write!(&mut self.w, "assert ")?;
|
||||||
self.render_expr(&def)?;
|
self.render_expr(&def)?;
|
||||||
}
|
}
|
||||||
@ -369,6 +418,7 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
write!(self.w, ";\n")?;
|
write!(self.w, ";\n")?;
|
||||||
|
self.last_line = line;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,6 +426,12 @@ where
|
|||||||
for v in stmts {
|
for v in stmts {
|
||||||
self.render_stmt(v)?;
|
self.render_stmt(v)?;
|
||||||
}
|
}
|
||||||
|
if let Some(last_comment_line) = self.comment_group_lines.first() {
|
||||||
|
eprintln!("last_comment_line is: {}", last_comment_line);
|
||||||
|
eprintln!("comment_map is: {:?}", self.comment_map);
|
||||||
|
eprintln!("coment_group_lines is: {:?}", self.comment_group_lines);
|
||||||
|
self.render_missed_comments(*last_comment_line + 1)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::ast::printer::*;
|
use crate::ast::printer::*;
|
||||||
use crate::iter::OffsetStrIter;
|
use crate::iter::OffsetStrIter;
|
||||||
@ -351,3 +352,53 @@ fn test_format_expr_list_arg_printing() {
|
|||||||
assert!(printer.render(&stmts).is_ok());
|
assert!(printer.render(&stmts).is_ok());
|
||||||
assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input));
|
assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_statement_with_comment_printing() {
|
||||||
|
let mut comment_map = BTreeMap::new();
|
||||||
|
let input = "// add 1 + 1\n1 + 1;";
|
||||||
|
let stmts = assert_parse(input, Some(&mut comment_map));
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
let mut printer = AstPrinter::new(2, &mut buffer).with_comment_map(&comment_map);
|
||||||
|
assert!(printer.render(&stmts).is_ok());
|
||||||
|
assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_statement_with_comment_printing_groups() {
|
||||||
|
let mut comment_map = BTreeMap::new();
|
||||||
|
let input = "// add 1\n// and 1\n1 + 1;";
|
||||||
|
let stmts = assert_parse(input, Some(&mut comment_map));
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
let mut printer = AstPrinter::new(2, &mut buffer).with_comment_map(&comment_map);
|
||||||
|
assert!(printer.render(&stmts).is_ok());
|
||||||
|
assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_statement_with_comment_printing_multiple_groups() {
|
||||||
|
let mut comment_map = BTreeMap::new();
|
||||||
|
let input = "\n// group 1\n// more group 1\n\n// group 2\n// more group 2\n1 + 1;";
|
||||||
|
let stmts = assert_parse(input, Some(&mut comment_map));
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
let mut printer = AstPrinter::new(2, &mut buffer).with_comment_map(&comment_map);
|
||||||
|
assert!(printer.render(&stmts).is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
String::from_utf8(buffer).unwrap(),
|
||||||
|
format!("{}\n", input.trim())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_statement_with_comment_printing_comments_at_end() {
|
||||||
|
let mut comment_map = BTreeMap::new();
|
||||||
|
let input = "// group 1\n1 + 1;\n// group 2\n\n";
|
||||||
|
let stmts = assert_parse(input, Some(&mut comment_map));
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
let mut printer = AstPrinter::new(2, &mut buffer).with_comment_map(&comment_map);
|
||||||
|
assert!(printer.render(&stmts).is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
String::from_utf8(buffer).unwrap(),
|
||||||
|
format!("{}\n", input.trim())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@ pub trait Walker {
|
|||||||
Statement::Expression(ref mut expr) => {
|
Statement::Expression(ref mut expr) => {
|
||||||
self.walk_expression(expr);
|
self.walk_expression(expr);
|
||||||
}
|
}
|
||||||
Statement::Assert(ref mut expr) => {
|
Statement::Assert(_, ref mut expr) => {
|
||||||
self.walk_expression(expr);
|
self.walk_expression(expr);
|
||||||
}
|
}
|
||||||
Statement::Output(_, _, ref mut expr) => {
|
Statement::Output(_, _, ref mut expr) => {
|
||||||
|
@ -511,7 +511,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
fn eval_stmt(&mut self, stmt: &Statement) -> Result<Rc<Val>, Box<dyn Error>> {
|
fn eval_stmt(&mut self, stmt: &Statement) -> Result<Rc<Val>, Box<dyn Error>> {
|
||||||
let child_scope = self.scope.clone();
|
let child_scope = self.scope.clone();
|
||||||
match stmt {
|
match stmt {
|
||||||
&Statement::Assert(ref expr) => self.eval_assert(&expr, &child_scope),
|
&Statement::Assert(_, ref expr) => self.eval_assert(&expr, &child_scope),
|
||||||
&Statement::Let(ref def) => self.eval_let(def),
|
&Statement::Let(ref def) => self.eval_let(def),
|
||||||
&Statement::Expression(ref expr) => self.eval_expr(expr, &child_scope),
|
&Statement::Expression(ref expr) => self.eval_expr(expr, &child_scope),
|
||||||
// Only one output can be used per file. Right now we enforce this by
|
// Only one output can be used per file. Right now we enforce this by
|
||||||
|
@ -793,21 +793,19 @@ make_fn!(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
fn tuple_to_let(tok: Token, expr: Expression) -> Statement {
|
|
||||||
Statement::Let(LetDef {
|
|
||||||
name: tok,
|
|
||||||
value: expr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
make_fn!(
|
make_fn!(
|
||||||
let_stmt_body<SliceIter<Token>, Statement>,
|
let_stmt_body<SliceIter<Token>, Statement>,
|
||||||
do_each!(
|
do_each!(
|
||||||
|
pos => pos,
|
||||||
name => wrap_err!(match_type!(BAREWORD), "Expected name for binding"),
|
name => wrap_err!(match_type!(BAREWORD), "Expected name for binding"),
|
||||||
_ => punct!("="),
|
_ => punct!("="),
|
||||||
val => wrap_err!(trace_parse!(expression), "Expected Expression"),
|
val => wrap_err!(trace_parse!(expression), "Expected Expression"),
|
||||||
_ => punct!(";"),
|
_ => punct!(";"),
|
||||||
(tuple_to_let(name, val))
|
(Statement::Let(LetDef {
|
||||||
|
pos: pos,
|
||||||
|
name: name,
|
||||||
|
value: val,
|
||||||
|
}))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -823,10 +821,11 @@ make_fn!(
|
|||||||
make_fn!(
|
make_fn!(
|
||||||
assert_statement<SliceIter<Token>, Statement>,
|
assert_statement<SliceIter<Token>, Statement>,
|
||||||
do_each!(
|
do_each!(
|
||||||
|
pos => pos,
|
||||||
_ => word!("assert"),
|
_ => word!("assert"),
|
||||||
expr => wrap_err!(must!(expression), "Expected Tuple {ok=<bool>, desc=<str>}"),
|
expr => wrap_err!(must!(expression), "Expected Tuple {ok=<bool>, desc=<str>}"),
|
||||||
_ => must!(punct!(";")),
|
_ => must!(punct!(";")),
|
||||||
(Statement::Assert(expr))
|
(Statement::Assert(pos, expr))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ use crate::error::StackPrinter;
|
|||||||
use crate::iter::OffsetStrIter;
|
use crate::iter::OffsetStrIter;
|
||||||
|
|
||||||
pub type CommentGroup = Vec<Token>;
|
pub type CommentGroup = Vec<Token>;
|
||||||
pub type CommentMap = std::collections::HashMap<usize, CommentGroup>;
|
pub type CommentMap = std::collections::BTreeMap<usize, CommentGroup>;
|
||||||
|
|
||||||
fn is_symbol_char<'a>(i: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, u8> {
|
fn is_symbol_char<'a>(i: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, u8> {
|
||||||
let mut _i = i.clone();
|
let mut _i = i.clone();
|
||||||
@ -353,6 +353,12 @@ fn comment(input: OffsetStrIter) -> Result<OffsetStrIter, Token> {
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Result::Complete(rest, cmt) => {
|
Result::Complete(rest, cmt) => {
|
||||||
|
// Eat the new lines here before continuing
|
||||||
|
let rest =
|
||||||
|
match optional!(rest, either!(text_token!("\r\n"), text_token!("\n"))) {
|
||||||
|
Result::Complete(next_rest, _) => next_rest,
|
||||||
|
_ => rest,
|
||||||
|
};
|
||||||
return Result::Complete(rest, make_tok!(CMT => cmt.to_string(), input));
|
return Result::Complete(rest, make_tok!(CMT => cmt.to_string(), input));
|
||||||
}
|
}
|
||||||
// If we didn't find a new line then we just grab everything.
|
// If we didn't find a new line then we just grab everything.
|
||||||
@ -504,7 +510,9 @@ pub fn tokenize<'a>(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(&mut Some(ref mut map), _) => {
|
(&mut Some(ref mut map), _) => {
|
||||||
out.push(tok);
|
if tok.typ != TokenType::WS {
|
||||||
|
out.push(tok);
|
||||||
|
}
|
||||||
if let Some(tok) = comment_was_last {
|
if let Some(tok) = comment_was_last {
|
||||||
map.insert(tok.pos.line, comment_group);
|
map.insert(tok.pos.line, comment_group);
|
||||||
comment_group = Vec::new();
|
comment_group = Vec::new();
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use abortable_parser::{Offsetable, Result, SliceIter};
|
use abortable_parser::{Offsetable, Result, SliceIter};
|
||||||
@ -167,6 +169,23 @@ fn test_tokenize_one_of_each() {
|
|||||||
assert_eq!(v[38].typ, TokenType::END);
|
assert_eq!(v[38].typ, TokenType::END);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tokenize_one_of_each_comment_map_path() {
|
||||||
|
let input = OffsetStrIter::new(
|
||||||
|
"map out filter assert let import func select as => [ ] { } ; = % / * \
|
||||||
|
+ - . ( ) , 1 . foo \"bar\" // comment\n ; true false == < > <= >= !=",
|
||||||
|
);
|
||||||
|
let mut comment_map = BTreeMap::new();
|
||||||
|
let result = tokenize(input.clone(), Some(&mut comment_map));
|
||||||
|
assert!(result.is_ok(), format!("result {:?} is not ok", result));
|
||||||
|
let v = result.unwrap();
|
||||||
|
for (i, t) in v.iter().enumerate() {
|
||||||
|
println!("{}: {:?}", i, t);
|
||||||
|
}
|
||||||
|
assert_eq!(v.len(), 39);
|
||||||
|
assert_eq!(v[38].typ, TokenType::END);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_has_end() {
|
fn test_parse_has_end() {
|
||||||
let input = OffsetStrIter::new("foo");
|
let input = OffsetStrIter::new("foo");
|
||||||
@ -327,3 +346,24 @@ fn test_match_type() {
|
|||||||
res => assert!(false, format!("Fail: {:?}", res)),
|
res => assert!(false, format!("Fail: {:?}", res)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tokenize_builds_comment_map() {
|
||||||
|
let input = OffsetStrIter::new("// comment 1\n\n//comment 2");
|
||||||
|
let mut comment_map = BTreeMap::new();
|
||||||
|
let result = tokenize(input.clone(), Some(&mut comment_map));
|
||||||
|
assert!(result.is_ok(), format!("result {:?} is not ok", result));
|
||||||
|
|
||||||
|
assert_eq!(comment_map.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tokenize_builds_comment_map_groups() {
|
||||||
|
let input = OffsetStrIter::new("// first part\n// comment 1\n\n//comment 2");
|
||||||
|
let mut comment_map = BTreeMap::new();
|
||||||
|
let result = tokenize(input.clone(), Some(&mut comment_map));
|
||||||
|
assert!(result.is_ok(), format!("result {:?} is not ok", result));
|
||||||
|
|
||||||
|
assert_eq!(comment_map.len(), 2);
|
||||||
|
assert_eq!(comment_map[&2].len(), 2);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user