Skip to content
Snippets Groups Projects
Commit 2db9d21c authored by Per Lindgren's avatar Per Lindgren
Browse files

borrow check, wip0

parent f62d8f7b
Branches
No related tags found
No related merge requests found
...@@ -175,9 +175,9 @@ dependencies = [ ...@@ -175,9 +175,9 @@ dependencies = [
[[package]] [[package]]
name = "either" name = "either"
version = "1.6.0" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]] [[package]]
name = "ena" name = "ena"
...@@ -235,9 +235,9 @@ dependencies = [ ...@@ -235,9 +235,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.1.14" version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
...@@ -252,9 +252,9 @@ checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" ...@@ -252,9 +252,9 @@ checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.15" version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
dependencies = [ dependencies = [
"libc", "libc",
] ]
...@@ -329,9 +329,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" ...@@ -329,9 +329,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.76" version = "0.2.77"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
[[package]] [[package]]
name = "log" name = "log"
...@@ -387,9 +387,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" ...@@ -387,9 +387,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.20" version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29" checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
...@@ -458,18 +458,18 @@ dependencies = [ ...@@ -458,18 +458,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.115" version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.115" version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
...@@ -515,9 +515,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" ...@@ -515,9 +515,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.40" version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
......
// borrow check environment
use std::iter::FromIterator;
// use std::iter::{FromIterator, Map};
use crate::ast::*;
// use crate::grammar::*;
use std::collections::{HashMap, VecDeque};
#[derive(Debug)]
pub enum Error {
NotFound(String),
Shared(String),
Unique(String),
}
#[derive(Debug)]
pub enum Bc {
Free,
Shared,
Unique,
Ref(Box<Bc>),
}
type Scope = i32;
pub type IdBc = HashMap<Id, Bc>;
#[derive(Debug)]
pub struct BcEnv(VecDeque<IdBc>);
impl BcEnv {
pub fn new() -> Self {
BcEnv(VecDeque::new())
}
pub fn get(&self, id: String) -> Option<(Scope, &Bc)> {
self.0
.iter()
.enumerate()
.find_map(|(i, hm)| hm.get(id.as_str()).map(|bc| (i as i32, bc)))
}
pub fn get_mut(&mut self, id: String) -> Option<(Scope, &mut Bc)> {
self.0
.iter_mut()
.enumerate()
.find_map(|(i, hm)| hm.get_mut(id.as_str()).map(|bc| (i as i32, bc)))
}
pub fn new_id(&mut self, id: String, bc: Bc) {
let hm = self.0.front_mut().unwrap();
println!("insert id {:?}, in scope {:?}", id, hm);
hm.insert(id, bc);
}
pub fn update(&mut self, id: String, bc: Bc) -> Result<(), ()> {
println!("env: update");
match self.get_mut(id.clone()) {
Some(b) => match b {
(_i, Bc::Free) => {
*b.1 = bc;
Ok(())
}
(_i, Bc::Shared) => match bc {
Bc::Shared => Ok(()),
Bc::Unique => Err(()),
_ => panic!("ICE update to Free"),
},
_ => Err(()),
},
None => Err(()),
}
}
pub fn push_empty_scope(&mut self) {
self.0.push_front(HashMap::new());
}
pub fn push_param_scope(&mut self, args: IdBc) {
self.0.push_front(args)
}
pub fn pop_scope(&mut self) {
self.0.pop_front();
}
}
#[test]
fn test_bc_env() {
let mut bc_env = BcEnv::new();
println!("{:?}", bc_env);
bc_env.push_empty_scope();
bc_env.new_id("a".to_string(), Bc::Free);
println!("{:?}", bc_env.get("a".to_string()));
bc_env.push_empty_scope();
bc_env.new_id("b".to_string(), Bc::Free);
println!("{:?}", bc_env.get("a".to_string()));
println!("{:?}", bc_env.get("b".to_string()));
bc_env.update("a".to_string(), Bc::Shared).unwrap();
println!("{:?}", bc_env.get("a".to_string()));
bc_env.update("a".to_string(), Bc::Shared).unwrap();
println!("{:?}", bc_env.get("a".to_string()));
// it should be an error to assign a shared ref to a unique
bc_env.update("a".to_string(), Bc::Unique).unwrap_err();
bc_env.pop_scope();
println!("{:?}", bc_env.get("b".to_string()));
println!("{:?}", bc_env.get("a".to_string()));
}
use std::io::Write;
use env_logger::Builder;
use log::{trace, LevelFilter};
use std::convert::From;
use crate::ast::*;
use crate::bc_env::{Bc, BcEnv, Error, IdBc};
use crate::env::{new_fn_env, new_type_env, FnEnv, TypeEnv};
// type check
fn bc_expr(
e: &Expr,
fn_env: &FnEnv,
type_env: &TypeEnv,
bc_env: &mut BcEnv,
) -> Result<Type, Error> {
use Expr::*;
println!("expr_type {}", e);
println!("var_env {:?}", bc_env);
match e {
// Num(_) => Ok(Type::I32),
// Bool(_) => Ok(Type::Bool),
// Infix(l, op, r) => {
// let lt = expr_type(l, fn_env, type_env, var_env)?;
// let rt = expr_type(r, fn_env, type_env, var_env)?;
// let lt = strip_mut(lt);
// let rt = strip_mut(rt);
// use Op::*;
// match op {
// // Arithmetic and Boolen
// Add | Mul | Div | Sub | And | Or => {
// // check if op and args are compliant
// let opt = op.get_type();
// if lt == opt && rt == opt {
// Ok(opt)
// } else {
// Err(format!(
// "Expected type {}, found, left {}: {}, {:?} right {}: {}, {:?}",
// opt, l, lt, lt, r, rt, rt
// ))
// }
// }
// // Equality
// Eq | Neq => {
// // check if args are of same type
// if lt == rt {
// Ok(Type::Bool)
// } else {
// Err(format!(
// "Comparison requires operands of same type, left {}, right {}",
// lt, rt
// ))
// }
// }
// // Comparison
// Less | Greater | LessEq | GreaterEq => {
// if lt == Type::I32 && rt == Type::I32 {
// // check if args are of i32
// Ok(Type::Bool)
// } else {
// Err(format!(
// "Comparison requires operands of same type, left {}, right {}",
// lt, rt
// ))
// }
// }
// _ => panic!("ICE on {}", e),
// }
// }
// Prefix(op, r) => {
// let rt = expr_type(r, fn_env, type_env, var_env)?;
// let opt = op.get_type();
// // check if both of same type
// if rt == opt {
// Ok(opt)
// } else {
// Err(format!("op {} rt {}", opt, rt))
// }
// }
// Call(s, args) => {
// trace!("call {} with {}", s, args);
// let arg_t: Vec<Type> = args
// .clone()
// .0
// .into_iter()
// .map(|e| expr_type(&*e, fn_env, type_env, var_env))
// .collect::<Result<_, _>>()?;
// trace!("arg types {:?}", arg_t);
// let f = match fn_env.get(s.as_str()) {
// Some(f) => f,
// None => Err(format!("{} not found", s))?,
// };
// let par_t: Vec<Type> = (f.params.0.clone())
// .into_iter()
// .map(|p| From::from(&p))
// .collect();
// trace!(
// "fn to call {} with params {} and types {:?}",
// f,
// f.params,
// par_t
// );
// if arg_t == par_t {
// Ok(f.result.clone())
// } else {
// Err(format!(
// "arguments types {:?} does not match parameter types {:?}",
// arg_t, par_t
// ))
// }
// }
// Id(id) => match var_env.get(id.to_string()) {
// Some(t) => Ok(t.clone()),
// None => Err(format!("variable not found {}", id)),
// },
// As(_e, _t) => unimplemented!("here we implement explicit type cast"),
// // Convert Expr::Ref to Type::Ref
// Ref(ref_e) => {
// let t = expr_type(ref_e, fn_env, type_env, var_env)?;
// trace!("ref_e {}, t {}", ref_e, t);
// let t = match t {
// Type::Mut(t) => t.clone(),
// t => Box::new(t),
// };
// Ok(Type::Ref(t))
// }
// // Convert Expr::Mut to Type::Mut
// RefMut(ref_mut_e) => {
// let t = expr_type(ref_mut_e, fn_env, type_env, var_env)?;
// match t {
// Type::Mut(_) => Ok(Type::Ref(Box::new(t))),
// _ => Err(format!("{} is not mutable in {}", t, ref_mut_e)),
// }
// }
// DeRef(deref_e) => {
// let t = expr_type(deref_e, fn_env, type_env, var_env)?;
// trace!("deref_t {}", &t);
// let t = strip_mut(t);
// trace!("strip deref_t {}", &t);
// match t {
// Type::Ref(dt) => Ok(*dt),
// _ => Err(format!("cannot deref {} of type {}", e, t)),
// }
// }
// Block(b) => check_stmts(b, fn_env, type_env, var_env),
// Stmt(s) => check_stmt(s, fn_env, type_env, var_env),
_ => unimplemented!(),
}
}
// fn lexpr_type<'a>(
// e: &Expr,
// fn_env: &FnEnv,
// type_env: &TypeEnv,
// var_env: &'a mut VarEnv,
// ) -> Result<&'a mut Type, Error> {
// use Expr::*;
// trace!("expr_type {}", e);
// trace!("var_env {:?}", var_env);
// match e {
// Id(id) => match var_env.get_mut(id.to_string()) {
// Some(t) => Ok(t),
// None => Err(format!("variable not found {}", id)),
// },
// DeRef(deref_e) => {
// let t = lexpr_type(deref_e, fn_env, type_env, var_env)?;
// trace!("deref_t {}", &t);
// // strip mut
// let t = match t {
// Type::Mut(t) => t,
// _ => t,
// };
// trace!("strip deref_t {}", t);
// match t {
// Type::Ref(dt) => Ok(dt),
// _ => Err(format!("cannot deref {} of type {}", e, t)),
// }
// }
// _ => Err(format!("Illegal left hand expression {}", e)),
// }
// }
// strip a "mut T" to a T
fn strip_mut(t: Type) -> Type {
match t {
Type::Mut(t) => *t,
_ => t,
}
}
// determine if a type is declared recursively
fn is_known(t: &Type) -> bool {
use Type::*;
match t {
Type::Unknown => false,
Unit | Bool | I32 => true,
Mut(t) | Ref(t) => is_known(t),
_ => panic!("ICE is_known"),
}
}
pub fn bc_stmt(
s: &Stmt,
fn_env: &FnEnv,
type_env: &TypeEnv,
bc_env: &mut BcEnv,
) -> Result<(), Error> {
use Stmt::*;
// trace!("stmt: {}", s);
// match s {
// Stmt::Block(b) => check_stmts(b, fn_env, type_env, var_env),
// Expr(e) => expr_type(&*e, fn_env, type_env, var_env),
// Let(var_id, is_mut, ot, oe) => {
// let t: Type = match (ot, oe) {
// (Some(t), Some(e)) => {
// let e_type = expr_type(&*e, fn_env, type_env, var_env)?;
// let e_type = strip_mut(e_type);
// match strip_mut(t.clone()) == e_type {
// true => t.clone(),
// false => {
// trace!("e {}", e);
// Err(format!("incompatible types, {} <> {}", t, e_type))?
// }
// }
// }
// (None, Some(e)) => {
// let e_type = strip_mut(expr_type(&*e, fn_env, type_env, var_env)?);
// match is_known(&e_type) {
// true => e_type,
// _ => Err("reference to unknown type".to_string())?,
// }
// }
// (Some(t), None) => t.clone(),
// _ => Type::Unknown,
// };
// let t = match is_mut {
// true => Type::Mut(Box::new(t)),
// false => t,
// };
// var_env.new_id(var_id.clone(), t);
// trace!("var_env {:?}", var_env);
// Ok(Type::Unit)
// }
// Assign(lh, e) => {
// trace!("assign");
// let e_type = expr_type(&*e, fn_env, type_env, var_env)?;
// trace!("e_type = {}", &e_type);
// let e_type = strip_mut(e_type);
// trace!("e_type stripped = {}", &e_type);
// trace!("lh expr = {:?}", lh);
// let lh_type = lexpr_type(lh, fn_env, type_env, var_env)?;
// trace!("lh_type {}", lh_type);
// if match lh_type {
// Type::Unknown => {
// trace!("assign to unknown");
// *lh_type = e_type.clone();
// true
// }
// Type::Mut(t) => match **t {
// Type::Unknown => {
// trace!("assign to `mut Unknown`");
// *t = Box::new(e_type.clone());
// true
// }
// _ => **t == e_type,
// },
// _ => Err(format!("assignment to immutable"))?,
// } {
// Ok(Type::Unit)
// } else {
// Err(format!("cannot assign {} = {}", &lh_type, e_type))
// }
// }
// While(e, block) => match expr_type(&*e, fn_env, type_env, var_env) {
// Ok(Type::Bool) => {
// let _ = check_stmts(&block, fn_env, type_env, var_env)?;
// Ok(Type::Unit) // a while statement is of unit type;
// }
// _ => Err("Condition not Boolean".to_string()),
// },
// If(e, then, o_else) => match expr_type(&*e, fn_env, type_env, var_env)? {
// Type::Bool => {
// // The condition is of Bool type
// let then_type = check_stmts(&then, fn_env, type_env, var_env)?;
// trace!("then type {}", then_type);
// match o_else {
// None => Ok(then_type), // type of the arm
// Some(else_stmts) => {
// let else_type = check_stmts(&else_stmts, fn_env, type_env, var_env)?;
// trace!("else type {}", else_type);
// match then_type == else_type {
// true => Ok(then_type), // same type of both arms
// false => {
// trace!("error-----");
// Err(format!(
// "'then' arm :{} does not match 'else' arm :{}",
// then_type, else_type
// ))
// }
// }
// }
// }
// }
// _ => Err("Condition not Boolean".to_string()),
// },
// Semi => Ok(Type::Unit),
// }
Ok(())
}
pub fn bc_stmts(
stmts: &Stmts,
fn_env: &FnEnv,
type_env: &TypeEnv,
bc_env: &mut BcEnv,
) -> Result<(), Error> {
bc_env.push_empty_scope();
let t = stmts
.stmts
.iter()
.try_fold((), |_, s| bc_stmt(s, fn_env, type_env, bc_env))?;
bc_env.pop_scope();
// if stmts.trailing_semi {
// Ok(())
// } else {
// Ok(t.clone())
// }
Ok(())
}
pub fn build_env(p: &Program) -> (FnEnv, TypeEnv) {
let fn_env = new_fn_env(&p.fn_decls);
let type_env = new_type_env(&p.type_decls);
(fn_env, type_env)
}
// pub fn dump_env(fn_env: &FnEnv, type_env: &TypeEnv) {
// trace!("fn_env {:?}", fn_env);
// trace!("type_env {:?}", type_env);
// }
// check a whole Program
pub fn bc_prog(p: &Program) -> Result<(), Error> {
let (fn_env, type_env) = build_env(&p);
println!("Input program \n{}", &p);
for fd in p.fn_decls.iter() {
println!("check function\n{}", fd);
println!("ast\n{:?}", fd);
let mut bc_env = BcEnv::new();
// build a scope for the arguments
let mut arg_ty = IdBc::new();
for Param { is_mut, id, ty } in fd.params.0.iter() {
let ty = match *is_mut {
true => Bc::Unique,
_ => Bc::Shared,
};
arg_ty.insert(id.to_owned(), ty);
}
// var_env.push_param_scope(arg_ty);
// let stmt_type = check_stmts(&fd.body, &fn_env, &type_env, &mut var_env);
// trace!("result {}: {} {:?}", &fd.id, &fd.result, &stmt_type);
// let stmt_type = strip_mut(stmt_type?);
// if stmt_type != fd.result {
// Err(format!(
// "return type {} does not match statements type {}",
// fd.result, stmt_type
// ))?;
// }
}
Ok(())
}
// unit test
// #[test]
// fn test_stmts() {
// use crate::grammar::*;
// use crate::*;
// // use Type::*;
// // setup environment
// let p = ProgramParser::new().parse(prog1!()).unwrap();
// let fn_env = new_fn_env(&p.fn_decls);
// let type_env = new_type_env(&p.type_decls);
// let mut var_env = VarEnv::new();
// trace!("{}", &p);
// let b = fn_env.get(&"b").unwrap();
// let body = &b.body;
// trace!("{}", &body);
// trace!("{:?}", check_stmts(body, &fn_env, &type_env, &mut var_env));
// }
// #[test]
// fn test_expr_type() {
// use crate::grammar::*;
// use crate::*;
// use Type::*;
// // setup environment
// let i = prog1!();
// println!("{}", i);
// let p = ProgramParser::new().parse(prog1!()).unwrap();
// let fn_env = new_fn_env(&p.fn_decls);
// let type_env = new_type_env(&p.type_decls);
// let mut var_env = VarEnv::new();
// var_env.push_empty_scope();
// // // some test variables in scope
// var_env.new_id("i".to_string(), I32); // let i : i32 ...
// var_env.new_id("j".to_string(), Unknown); // let i ...
// println!("p {}", p);
// // type of number
// assert_eq!(
// expr_type(&Expr::Num(1), &fn_env, &type_env, &mut var_env),
// Ok(I32)
// );
// // type of variables
// // not found
// assert!(expr_type(&Expr::Id("a".to_string()), &fn_env, &type_env, &mut var_env).is_err());
// // let i: i32 ...
// assert_eq!(
// expr_type(&Expr::Id("i".to_string()), &fn_env, &type_env, &mut var_env),
// Ok(I32)
// );
// // let j ... (has no type yet)
// assert_eq!(
// expr_type(&Expr::Id("j".to_string()), &fn_env, &type_env, &mut var_env),
// Ok(Unknown)
// );
// // // let n: A ...
// // assert_eq!(
// // expr_type(&Expr::Id("n".to_string()), &fn_env, &type_env, &var_env),
// // Ok(Named("A".to_string()))
// // );
// // type of arithmetic operation (for now just i32)
// assert_eq!(
// expr_type(
// &*ExprParser::new().parse("1 + 2 - 5").unwrap(),
// &fn_env,
// &type_env,
// &mut var_env
// ),
// Ok(I32)
// );
// // type of arithmetic unary operation (for now just i32)
// assert_eq!(
// expr_type(
// &*ExprParser::new().parse("- 5").unwrap(),
// &fn_env,
// &type_env,
// &mut var_env
// ),
// Ok(I32)
// );
// // call, with check, ok
// assert_eq!(
// expr_type(
// &*ExprParser::new().parse("b(1)").unwrap(),
// &fn_env,
// &type_env,
// &mut var_env
// ),
// Ok(I32)
// );
// // call, with check, ok (i: i32)
// assert_eq!(
// expr_type(
// &*ExprParser::new().parse("b(i)").unwrap(),
// &fn_env,
// &type_env,
// &mut var_env
// ),
// Ok(I32)
// );
// // call, with check, error wrong number args
// assert!(expr_type(
// &*ExprParser::new().parse("b(1, 2)").unwrap(),
// &fn_env,
// &type_env,
// &mut var_env
// )
// .is_err());
// // call, with check, error type of arg
// assert!(expr_type(
// &*ExprParser::new().parse("b(true)").unwrap(),
// &fn_env,
// &type_env,
// &mut var_env
// )
// .is_err());
// // call, with check, ok (i: i32)
// assert_eq!(
// expr_type(
// &*ExprParser::new().parse("c(n)").unwrap(),
// &fn_env,
// &type_env,
// &var_env
// ),
// Ok(Unit)
// );
// TODO, ref/ref mut/deref
// }
...@@ -11,6 +11,8 @@ pub mod check; ...@@ -11,6 +11,8 @@ pub mod check;
#[macro_use] #[macro_use]
pub mod input; pub mod input;
pub mod bc_env;
pub mod borrow;
pub mod env; pub mod env;
pub mod read_file; pub mod read_file;
pub mod vm; pub mod vm;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment