aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--src/eval.rs154
3 files changed, 150 insertions, 12 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b853519..afa07fe 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -49,6 +49,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
49checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2" 49checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2"
50 50
51[[package]] 51[[package]]
52name = "la-arena"
53version = "0.3.1"
54source = "registry+https://github.com/rust-lang/crates.io-index"
55checksum = "3752f229dcc5a481d60f385fa479ff46818033d881d2d801aa27dffcfb5e8306"
56
57[[package]]
52name = "memchr" 58name = "memchr"
53version = "2.7.4" 59version = "2.7.4"
54source = "registry+https://github.com/rust-lang/crates.io-index" 60source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -153,6 +159,7 @@ name = "tbsp"
153version = "0.1.0" 159version = "0.1.0"
154dependencies = [ 160dependencies = [
155 "argh", 161 "argh",
162 "la-arena",
156 "nom", 163 "nom",
157 "regex", 164 "regex",
158 "serde", 165 "serde",
diff --git a/Cargo.toml b/Cargo.toml
index 429338f..5d43ae0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ tree-sitter-javascript = {version = "0.21", optional = true}
18tree-sitter-python = {version = "0.21", optional = true} 18tree-sitter-python = {version = "0.21", optional = true}
19tree-sitter-rust = {version = "0.21", optional = true} 19tree-sitter-rust = {version = "0.21", optional = true}
20argh = "0.1.12" 20argh = "0.1.12"
21la-arena = "0.3.1"
21 22
22[features] 23[features]
23default = ["md", "typescript", "javascript", "rust", "python"] 24default = ["md", "typescript", "javascript", "rust", "python"]
diff --git a/src/eval.rs b/src/eval.rs
index 029cffe..1a3f8a8 100644
--- a/src/eval.rs
+++ b/src/eval.rs
@@ -76,10 +76,33 @@ impl Value {
76 Self::String(String::default()) 76 Self::String(String::default())
77 } 77 }
78 78
79 fn as_boolean(&self) -> Option<bool> { 79 fn as_boolean(&self) -> std::result::Result<bool, Error> {
80 match self { 80 match self {
81 Self::Boolean(b) => Some(*b), 81 Self::Boolean(b) => Ok(*b),
82 _ => None, 82 v => Err(Error::TypeMismatch {
83 expected: ast::Type::Boolean,
84 got: v.ty(),
85 }),
86 }
87 }
88
89 fn as_str(&self) -> std::result::Result<&str, Error> {
90 match self {
91 Self::String(s) => Ok(s.as_str()),
92 v => Err(Error::TypeMismatch {
93 expected: ast::Type::String,
94 got: v.ty(),
95 }),
96 }
97 }
98
99 fn as_int(&self) -> std::result::Result<i128, Error> {
100 match self {
101 Self::Integer(i) => Ok(*i),
102 v => Err(Error::TypeMismatch {
103 expected: ast::Type::Integer,
104 got: v.ty(),
105 }),
83 } 106 }
84 } 107 }
85 108
@@ -250,6 +273,24 @@ impl fmt::Display for Value {
250 } 273 }
251} 274}
252 275
276impl From<bool> for Value {
277 fn from(value: bool) -> Self {
278 Self::Boolean(value)
279 }
280}
281
282impl From<i128> for Value {
283 fn from(value: i128) -> Self {
284 Self::Integer(value)
285 }
286}
287
288impl From<&str> for Value {
289 fn from(value: &str) -> Self {
290 Self::String(value.to_owned())
291 }
292}
293
253type NodeKind = u16; 294type NodeKind = u16;
254 295
255#[derive(Debug, Default)] 296#[derive(Debug, Default)]
@@ -312,12 +353,20 @@ impl Visitors {
312#[derive(Debug, PartialEq, Eq)] 353#[derive(Debug, PartialEq, Eq)]
313pub enum Error { 354pub enum Error {
314 FailedLookup(ast::Identifier), 355 FailedLookup(ast::Identifier),
315 TypeMismatch { expected: ast::Type, got: ast::Type }, 356 TypeMismatch {
357 expected: ast::Type,
358 got: ast::Type,
359 },
316 UndefinedBinOp(ast::BinOp, ast::Type, ast::Type), 360 UndefinedBinOp(ast::BinOp, ast::Type, ast::Type),
317 UndefinedUnaryOp(ast::UnaryOp, ast::Type), 361 UndefinedUnaryOp(ast::UnaryOp, ast::Type),
318 AlreadyBound(ast::Identifier), 362 AlreadyBound(ast::Identifier),
319 MalformedExpr(String), 363 MalformedExpr(String),
320 InvalidNodeKind(String), 364 InvalidNodeKind(String),
365 InvalidStringSlice {
366 length: usize,
367 start: i128,
368 end: i128,
369 },
321 // current node is only set in visitors, not in BEGIN or END blocks 370 // current node is only set in visitors, not in BEGIN or END blocks
322 CurrentNodeNotPresent, 371 CurrentNodeNotPresent,
323} 372}
@@ -496,10 +545,7 @@ impl<'a> Context<'a> {
496 let l = self.eval_expr(lhs)?; 545 let l = self.eval_expr(lhs)?;
497 546
498 // short-circuit 547 // short-circuit
499 let l_value = l.as_boolean().ok_or_else(|| Error::TypeMismatch { 548 let l_value = l.as_boolean()?;
500 expected: ast::Type::Boolean,
501 got: l.ty(),
502 })?;
503 549
504 match op { 550 match op {
505 ast::LogicOp::Or => { 551 ast::LogicOp::Or => {
@@ -531,10 +577,7 @@ impl<'a> Context<'a> {
531 fn eval_if(&mut self, if_expr: &ast::If) -> Result { 577 fn eval_if(&mut self, if_expr: &ast::If) -> Result {
532 let cond = self.eval_expr(&if_expr.condition)?; 578 let cond = self.eval_expr(&if_expr.condition)?;
533 579
534 if cond.as_boolean().ok_or_else(|| Error::TypeMismatch { 580 if cond.as_boolean()? {
535 expected: ast::Type::Boolean,
536 got: cond.ty(),
537 })? {
538 self.eval_block(&if_expr.then) 581 self.eval_block(&if_expr.then)
539 } else { 582 } else {
540 self.eval_block(&if_expr.else_) 583 self.eval_block(&if_expr.else_)
@@ -550,6 +593,50 @@ impl<'a> Context<'a> {
550 } 593 }
551 Ok(Value::Unit) 594 Ok(Value::Unit)
552 } 595 }
596 (predicate @ ("isupper" | "islower"), [arg]) => Ok(self
597 .eval_expr(arg)?
598 .as_str()?
599 .chars()
600 .all(|c| match predicate {
601 "isupper" => c.is_ascii_uppercase(),
602 "islower" => c.is_ascii_lowercase(),
603 _ => unreachable!(),
604 })
605 .into()),
606 ("substr", [string, indices @ ..]) => {
607 let v = self.eval_expr(string)?;
608 let s = v.as_str()?;
609 match indices {
610 [start, end] => {
611 let start = self.eval_expr(start)?.as_int()?;
612 let end = self.eval_expr(end)?.as_int()?;
613 if start < 0
614 || start >= s.len() as i128
615 || end >= s.len() as i128
616 || start > end
617 {
618 return Err(Error::InvalidStringSlice {
619 length: s.len(),
620 start,
621 end,
622 });
623 }
624 Ok(s[start as usize..end as usize].into())
625 }
626 [end] => {
627 let end = self.eval_expr(end)?.as_int()?;
628 if end >= s.len() as i128 {
629 return Err(Error::InvalidStringSlice {
630 length: s.len(),
631 start: 0,
632 end,
633 });
634 }
635 Ok(s[..end as usize].into())
636 }
637 _ => todo!(),
638 }
639 }
553 ("text", [arg]) => { 640 ("text", [arg]) => {
554 let node = match self.eval_expr(arg)? { 641 let node = match self.eval_expr(arg)? {
555 Value::Node => self 642 Value::Node => self
@@ -804,4 +891,47 @@ mod test {
804 } 891 }
805 ); 892 );
806 } 893 }
894
895 #[test]
896 fn test_substring() {
897 let language = tree_sitter_python::language();
898 let mut ctx = Context::new(language)
899 .with_program(ast::Program::new())
900 .unwrap();
901 assert_eq!(
902 ctx.eval_block(&ast::Block {
903 body: vec![
904 ast::Statement::Declaration(ast::Declaration {
905 ty: ast::Type::String,
906 name: "a".to_owned(),
907 init: Some(ast::Expr::str("foobar").boxed()),
908 }),
909 ast::Statement::Declaration(ast::Declaration {
910 ty: ast::Type::String,
911 name: "b".to_owned(),
912 init: Some(
913 ast::Expr::Call(ast::Call {
914 function: "substr".into(),
915 parameters: vec![
916 ast::Expr::Ident("a".to_owned()),
917 ast::Expr::int(0),
918 ast::Expr::int(3),
919 ]
920 })
921 .boxed()
922 ),
923 }),
924 ]
925 }),
926 Ok(Value::Unit)
927 );
928 assert_eq!(
929 ctx.lookup(&String::from("b")).unwrap().clone(),
930 Variable {
931 ty: ast::Type::String,
932 name: "b".to_owned(),
933 value: "foo".into()
934 }
935 );
936 }
807} 937}