From b8308b51b0f4a95f9e211506a8bc61fd04d2edab Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 8 Sep 2024 21:43:32 +0100 Subject: simplify builtins, more list features --- flake.lock | 69 ++++++++++++++ flake.nix | 63 +++++++++++++ src/ast.rs | 78 +++++++++++++++- src/builtins.rs | 214 ++++++++++++++++++++++++++++++++++++++++++ src/eval.rs | 282 +++++++++++++++++++++----------------------------------- src/lib.rs | 3 +- 6 files changed, 528 insertions(+), 181 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/builtins.rs diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..261d530 --- /dev/null +++ b/flake.lock @@ -0,0 +1,69 @@ +{ + "nodes": { + "gitignore": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1724748588, + "narHash": "sha256-NlpGA4+AIf1dKNq76ps90rxowlFXUsV9x7vK/mN37JM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a6292e34000dc93d43bccf78338770c1c5ec8a99", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "gitignore": "gitignore", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1724811750, + "narHash": "sha256-PvhVgQ1rm3gfhK7ts4emprhh/KMkFwXogmgsQ3srR7g=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "6a1c4915dca7149e7258d8c7f3ac634d8c65f6c6", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..4f69fe8 --- /dev/null +++ b/flake.nix @@ -0,0 +1,63 @@ +{ + inputs = { + + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + gitignore = { + url = "github:hercules-ci/gitignore.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + }; + + outputs = + { self + , nixpkgs + , gitignore + , rust-overlay + }: + let + inherit (gitignore.lib) gitignoreSource; + + supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + nixpkgsFor = forAllSystems (system: + import nixpkgs { + inherit system; + overlays = [rust-overlay.overlays.default]; + }); + + in + { + + devShell = forAllSystems (system: + let + pkgs = nixpkgsFor."${system}"; + in + pkgs.mkShell { + nativeBuildInputs = [ + pkgs.cargo-watch + pkgs.bacon + pkgs.rustfmt + pkgs.cargo + + pkgs.rust-bin.nightly.latest.default + pkgs.rust-bin.nightly.latest.rust-analyzer + pkgs.lld + pkgs.trunk + + + pkgs.mermaid-cli + ]; + RUST_LOG = "info"; + RUST_BACKTRACE = 1; + }); + }; +} + + diff --git a/src/ast.rs b/src/ast.rs index fe986da..6d7d326 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -56,6 +56,17 @@ pub enum Statement { Declaration(Declaration), } +impl Statement { + #[cfg(test)] + pub fn decl(ty: Type, ident: &str, init: Expr) -> Self { + Self::Declaration(Declaration { + ty, + name: ident.to_owned(), + init: Some(init.boxed()), + }) + } +} + #[derive(Debug, Eq, PartialEq, Clone)] pub enum Expr { Node, @@ -77,11 +88,30 @@ impl Expr { Box::new(self) } - pub fn as_ident(self) -> Option { - match self { - Self::Ident(i) => Some(i), - _ => None, - } + #[cfg(test)] + pub fn bin(lhs: Expr, op: &str, rhs: Expr) -> Expr { + use std::str::FromStr; + Self::Bin(lhs.boxed(), BinOp::from_str(op).unwrap(), rhs.boxed()) + } + + #[cfg(test)] + pub fn ident(i: &str) -> Expr { + Self::Ident(i.to_owned()) + } + + #[cfg(test)] + pub fn call(fun: &str, params: [Expr; N]) -> Expr { + Self::Call(Call { + function: fun.to_owned(), + parameters: params.to_vec(), + }) + } + + #[cfg(test)] + pub fn list(items: [Expr; N]) -> Expr { + Self::List(List { + items: items.to_vec() + }) } } @@ -99,6 +129,44 @@ pub enum BinOp { Assign(AssignOp), } +impl std::str::FromStr for BinOp { + type Err = (); + fn from_str(val: &str) -> Result { + match val { + "+" => Ok(Self::Arith(ArithOp::Add)), + "-" => Ok(Self::Arith(ArithOp::Sub)), + "*" => Ok(Self::Arith(ArithOp::Mul)), + "/" => Ok(Self::Arith(ArithOp::Div)), + "%" => Ok(Self::Arith(ArithOp::Mod)), + ">" => Ok(Self::Cmp(CmpOp::Gt)), + "<" => Ok(Self::Cmp(CmpOp::Lt)), + ">=" => Ok(Self::Cmp(CmpOp::Gte)), + "<=" => Ok(Self::Cmp(CmpOp::Lte)), + "==" => Ok(Self::Cmp(CmpOp::Eq)), + "!=" => Ok(Self::Cmp(CmpOp::Neq)), + "&&" => Ok(Self::Logic(LogicOp::And)), + "||" => Ok(Self::Logic(LogicOp::Or)), + "=" => Ok(Self::Assign(AssignOp { op: None })), + "+=" => Ok(Self::Assign(AssignOp { + op: Some(ArithOp::Add), + })), + "-=" => Ok(Self::Assign(AssignOp { + op: Some(ArithOp::Sub), + })), + "*=" => Ok(Self::Assign(AssignOp { + op: Some(ArithOp::Mul), + })), + "/=" => Ok(Self::Assign(AssignOp { + op: Some(ArithOp::Div), + })), + "%=" => Ok(Self::Assign(AssignOp { + op: Some(ArithOp::Mod), + })), + _ => Err(()), + } + } +} + // + - * / #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum ArithOp { diff --git a/src/builtins.rs b/src/builtins.rs new file mode 100644 index 0000000..8f15603 --- /dev/null +++ b/src/builtins.rs @@ -0,0 +1,214 @@ +use std::{collections::HashMap, sync::LazyLock}; + +use crate::{ + ast, + eval::{Context, Error, Result, Value}, + Wrap +}; + +pub static BUILTINS: LazyLock>> = + LazyLock::new(|| { + [ + Print.boxed(), + IsUpper.boxed(), + IsLower.boxed(), + Substr.boxed(), + Text.boxed(), + Parent.boxed(), + Children.boxed(), + Length.boxed(), + Kind.boxed(), + ] + .into_iter() + .map(|b| (b.id(), b)) + .collect() + }); + +pub(crate) trait Builtin +where + Self: 'static, +{ + /// Name of this function + fn id(&self) -> &'static str; + + /// Function description + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result; + + /// Anything that is Builtin can be turned into a trait object + fn boxed(self) -> Box + where + Self: Sized + Sync + Send, + { + Box::new(self) as Box + } +} + +fn get_args(args: &[ast::Expr]) -> std::result::Result<&[ast::Expr; N], Error> { + args.try_into().map_err(|_| Error::IncorrectArgFormat { + wanted: N, + got: args.len(), + }) +} + +struct Print; +impl Builtin for Print { + fn id(&self) -> &'static str { + "print" + } + + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { + for arg in args { + let val = ctx.eval_expr(arg)?; + print!("{val}"); + } + Ok(Value::Unit) + } +} + +struct IsUpper; +impl Builtin for IsUpper { + fn id(&self) -> &'static str { + "isupper" + } + + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { + Ok(ctx + .eval_expr(&get_args::<1>(args)?[0])? + .as_str()? + .chars() + .all(|c| c.is_ascii_uppercase()) + .into()) + } +} + +struct IsLower; +impl Builtin for IsLower { + fn id(&self) -> &'static str { + "islower" + } + + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { + Ok(ctx + .eval_expr(&get_args::<1>(args)?[0])? + .as_str()? + .chars() + .all(|c| c.is_ascii_lowercase()) + .into()) + } +} + +struct Substr; +impl Builtin for Substr { + fn id(&self) -> &'static str { + "substr" + } + + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { + if let Ok([string, start, end]) = get_args::<3>(args) { + let v = ctx.eval_expr(string)?; + let s = v.as_str()?; + let start = ctx.eval_expr(start)?.as_int()?; + let end = ctx.eval_expr(end)?.as_int()?; + if start < 0 || start >= s.len() as i128 || end >= s.len() as i128 || start > end { + return Err(Error::InvalidStringSlice { + length: s.len(), + start, + end, + }); + } + Ok(s[start as usize..end as usize].into()) + } else { + let [string, end] = get_args::<2>(args)?; + let v = ctx.eval_expr(string)?; + let s = v.as_str()?; + let end = ctx.eval_expr(end)?.as_int()?; + if end >= s.len() as i128 { + return Err(Error::InvalidStringSlice { + length: s.len(), + start: 0, + end, + }); + } + Ok(s[..end as usize].into()) + } + } +} + +struct Text; +impl Builtin for Text { + fn id(&self) -> &'static str { + "text" + } + + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let id = v.as_node()?; + let node = ctx.get_node_by_id(id).unwrap(); + let text = node + .utf8_text(ctx.input_src.as_ref().unwrap().as_bytes()) + .unwrap(); + text.to_owned().wrap(Value::String).wrap(Ok) + } +} + +struct Parent; +impl Builtin for Parent { + fn id(&self) -> &'static str { + "parent" + } + + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let id = v.as_node()?; + let node = ctx.get_node_by_id(id).unwrap(); + let parent = node.parent(); + parent + .map(|n| Value::Node(n.id())) + .ok_or(Error::NoParentNode(node)) + } +} + +struct Children; +impl Builtin for Children { + fn id(&self) -> &'static str { + "children" + } + + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let id = v.as_node()?; + let node = ctx.get_node_by_id(id).unwrap(); + let children = node + .children(&mut node.walk()) + .map(|c| Value::Node(c.id())) + .collect::>(); + Ok(Value::List(children)) + } +} + +struct Length; +impl Builtin for Length { + fn id(&self) -> &'static str { + "length" + } + + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let l = v.as_list()?; + (l.len() as i128).wrap(Value::Integer).wrap(Ok) + } +} + +struct Kind; +impl Builtin for Kind { + fn id(&self) -> &'static str { + "kind" + } + + fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let id = v.as_node()?; + let node = ctx.get_node_by_id(id).unwrap(); + node.kind().to_owned().wrap(Value::String).wrap(Ok) + } +} diff --git a/src/eval.rs b/src/eval.rs index 104571b..35cba82 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -93,7 +93,7 @@ impl Value { } } - fn as_str(&self) -> std::result::Result<&str, Error> { + pub(crate) fn as_str(&self) -> std::result::Result<&str, Error> { match self { Self::String(s) => Ok(s.as_str()), v => Err(Error::TypeMismatch { @@ -103,7 +103,7 @@ impl Value { } } - fn as_int(&self) -> std::result::Result { + pub(crate) fn as_int(&self) -> std::result::Result { match self { Self::Integer(i) => Ok(*i), v => Err(Error::TypeMismatch { @@ -113,7 +113,7 @@ impl Value { } } - fn as_node(&self) -> std::result::Result { + pub(crate) fn as_node(&self) -> std::result::Result { match self { Self::Node(id) => Ok(*id), v => Err(Error::TypeMismatch { @@ -123,7 +123,7 @@ impl Value { } } - fn as_list(&self) -> std::result::Result, Error> { + pub(crate) fn as_list(&self) -> std::result::Result, Error> { match self { Self::List(values) => Ok(values.clone()), v => Err(Error::TypeMismatch { @@ -137,6 +137,7 @@ impl Value { match (self, other) { (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s + *o)), (Self::String(s), Self::String(o)) => Ok(Self::String(format!("{s}{o}"))), + (Self::List(l), o) => Ok(Self::List(l.iter().cloned().chain([o.clone()]).collect())), _ => Err(Error::UndefinedBinOp( ast::BinOp::Arith(ast::ArithOp::Add), self.ty(), @@ -297,8 +298,13 @@ impl fmt::Display for Value { Self::Node(id) => write!(f, ""), Self::List(items) => { write!(f, "[")?; - for i in items { - write!(f, "{i}")?; + let mut iterable = items.iter().peekable(); + while let Some(item) = iterable.next() { + if iterable.peek().is_none() { + write!(f, "{item}")?; + } else { + write!(f, "{item}, ")?; + } } write!(f, "]") } @@ -318,6 +324,12 @@ impl From for Value { } } +impl From for Value { + fn from(value: usize) -> Self { + (value as i128).into() + } +} + impl From<&str> for Value { fn from(value: &str) -> Self { Self::String(value.to_owned()) @@ -402,6 +414,10 @@ pub enum Error { MalformedExpr(String), InvalidNodeKind(String), NoParentNode(tree_sitter::Node<'static>), + IncorrectArgFormat { + wanted: usize, + got: usize, + }, InvalidStringSlice { length: usize, start: i128, @@ -409,7 +425,7 @@ pub enum Error { }, ArrayOutOfBounds { idx: i128, - len: usize + len: usize, }, // current node is only set in visitors, not in BEGIN or END blocks CurrentNodeNotPresent, @@ -421,7 +437,7 @@ pub struct Context { variables: HashMap, language: tree_sitter::Language, visitors: Visitors, - input_src: Option, + pub(crate) input_src: Option, cursor: Option>, tree: Option<&'static tree_sitter::Tree>, cache: HashMap>, @@ -511,7 +527,7 @@ impl Context { self } - fn eval_expr(&mut self, expr: &ast::Expr) -> Result { + pub(crate) fn eval_expr(&mut self, expr: &ast::Expr) -> Result { match expr { ast::Expr::Unit => Ok(Value::Unit), ast::Expr::Lit(lit) => self.eval_lit(lit), @@ -680,78 +696,10 @@ impl Context { } fn eval_call(&mut self, call: &ast::Call) -> Result { - match (call.function.as_str(), call.parameters.as_slice()) { - ("print", args) => { - for arg in args { - let val = self.eval_expr(arg)?; - print!("{val}"); - } - Ok(Value::Unit) - } - (predicate @ ("isupper" | "islower"), [arg]) => Ok(self - .eval_expr(arg)? - .as_str()? - .chars() - .all(|c| match predicate { - "isupper" => c.is_ascii_uppercase(), - "islower" => c.is_ascii_lowercase(), - _ => unreachable!(), - }) - .into()), - ("substr", [string, indices @ ..]) => { - let v = self.eval_expr(string)?; - let s = v.as_str()?; - match indices { - [start, end] => { - let start = self.eval_expr(start)?.as_int()?; - let end = self.eval_expr(end)?.as_int()?; - if start < 0 - || start >= s.len() as i128 - || end >= s.len() as i128 - || start > end - { - return Err(Error::InvalidStringSlice { - length: s.len(), - start, - end, - }); - } - Ok(s[start as usize..end as usize].into()) - } - [end] => { - let end = self.eval_expr(end)?.as_int()?; - if end >= s.len() as i128 { - return Err(Error::InvalidStringSlice { - length: s.len(), - start: 0, - end, - }); - } - Ok(s[..end as usize].into()) - } - _ => todo!(), - } - } - ("text", [arg]) => { - let v = self.eval_expr(arg)?; - let id = v.as_node()?; - let node = self.get_node_by_id(id).unwrap(); - let text = node - .utf8_text(self.input_src.as_ref().unwrap().as_bytes()) - .unwrap(); - Ok(Value::String(text.to_owned())) - } - ("parent", [arg]) => { - let v = self.eval_expr(arg)?; - let id = v.as_node()?; - let node = self.get_node_by_id(id).unwrap(); - let parent = node.parent(); - parent - .map(|n| Value::Node(n.id())) - .ok_or(Error::NoParentNode(node)) - } - (s, _) => Err(Error::FailedLookup(s.to_owned())), - } + (&*crate::builtins::BUILTINS) + .get(call.function.as_str()) + .ok_or_else(|| Error::FailedLookup(call.function.to_owned()))? + .eval(self, call.parameters.as_slice()) } fn eval_list(&mut self, list: &ast::List) -> Result { @@ -768,7 +716,7 @@ impl Context { if idx < 0 || idx >= target.len() as i128 { Err(Error::ArrayOutOfBounds { idx, - len: target.len() + len: target.len(), }) } else { Ok(target.remove(idx as usize)) @@ -881,47 +829,29 @@ pub fn evaluate(file: &str, program: &str, language: tree_sitter::Language) -> R #[cfg(test)] mod test { use super::*; + use crate::ast::*; #[test] fn bin() { let language = tree_sitter_python::language(); - let mut ctx = Context::new(language) - .with_program(ast::Program::new()) - .unwrap(); + let mut ctx = Context::new(language).with_program(Program::new()).unwrap(); assert_eq!( - ctx.eval_expr(&ast::Expr::Bin( - ast::Expr::int(5).boxed(), - ast::BinOp::Arith(ast::ArithOp::Add), - ast::Expr::int(10).boxed(), - )), + ctx.eval_expr(&Expr::bin(Expr::int(5), "+", Expr::int(10),)), Ok(Value::Integer(15)) ); assert_eq!( - ctx.eval_expr(&ast::Expr::Bin( - ast::Expr::int(5).boxed(), - ast::BinOp::Cmp(ast::CmpOp::Eq), - ast::Expr::int(10).boxed(), - )), + ctx.eval_expr(&Expr::bin(Expr::int(5), "==", Expr::int(10),)), Ok(Value::Boolean(false)) ); assert_eq!( - ctx.eval_expr(&ast::Expr::Bin( - ast::Expr::int(5).boxed(), - ast::BinOp::Cmp(ast::CmpOp::Lt), - ast::Expr::int(10).boxed(), - )), + ctx.eval_expr(&Expr::bin(Expr::int(5), "<", Expr::int(10),)), Ok(Value::Boolean(true)) ); assert_eq!( - ctx.eval_expr(&ast::Expr::Bin( - ast::Expr::Bin( - ast::Expr::int(5).boxed(), - ast::BinOp::Cmp(ast::CmpOp::Lt), - ast::Expr::int(10).boxed(), - ) - .boxed(), - ast::BinOp::Logic(ast::LogicOp::And), - ast::Expr::false_().boxed() + ctx.eval_expr(&Expr::bin( + Expr::bin(Expr::int(5), "<", Expr::int(10),), + "&&", + Expr::false_(), )), Ok(Value::Boolean(false)) ); @@ -930,24 +860,16 @@ mod test { #[test] fn test_evaluate_blocks() { let language = tree_sitter_python::language(); - let mut ctx = Context::new(language) - .with_program(ast::Program::new()) - .unwrap(); + let mut ctx = Context::new(language).with_program(Program::new()).unwrap(); assert_eq!( - ctx.eval_block(&ast::Block { + ctx.eval_block(&Block { body: vec![ - ast::Statement::Declaration(ast::Declaration { - ty: ast::Type::Integer, + Statement::Declaration(Declaration { + ty: Type::Integer, name: "a".to_owned(), init: None, }), - ast::Statement::Bare(ast::Expr::Bin( - ast::Expr::Ident("a".to_owned()).boxed(), - ast::BinOp::Assign(ast::AssignOp { - op: Some(ast::ArithOp::Add) - }), - ast::Expr::int(5).boxed() - )), + Statement::Bare(Expr::bin(Expr::ident("a"), "+=", Expr::int(5),)), ] }), Ok(Value::Unit) @@ -955,7 +877,7 @@ mod test { assert_eq!( ctx.lookup(&String::from("a")).unwrap().clone(), Variable { - ty: ast::Type::Integer, + ty: Type::Integer, name: "a".to_owned(), value: Value::Integer(5) } @@ -965,35 +887,29 @@ mod test { #[test] fn test_evaluate_if() { let language = tree_sitter_python::language(); - let mut ctx = Context::new(language) - .with_program(ast::Program::new()) - .unwrap(); + let mut ctx = Context::new(language).with_program(Program::new()).unwrap(); assert_eq!( - ctx.eval_block(&ast::Block { + ctx.eval_block(&Block { body: vec![ - ast::Statement::Declaration(ast::Declaration { - ty: ast::Type::Integer, + Statement::Declaration(Declaration { + ty: Type::Integer, name: "a".to_owned(), - init: Some(ast::Expr::int(1).boxed()), + init: Some(Expr::int(1).boxed()), }), - ast::Statement::Bare(ast::Expr::If(ast::IfExpr { - condition: ast::Expr::true_().boxed(), - then: ast::Block { - body: vec![ast::Statement::Bare(ast::Expr::Bin( - ast::Expr::Ident("a".to_owned()).boxed(), - ast::BinOp::Assign(ast::AssignOp { - op: Some(ast::ArithOp::Add) - }), - ast::Expr::int(5).boxed() + Statement::Bare(Expr::If(IfExpr { + condition: Expr::true_().boxed(), + then: Block { + body: vec![Statement::Bare(Expr::bin( + Expr::Ident("a".to_owned()), + "+=", + Expr::int(5), ))] }, - else_: ast::Block { - body: vec![ast::Statement::Bare(ast::Expr::Bin( - ast::Expr::Ident("a".to_owned()).boxed(), - ast::BinOp::Assign(ast::AssignOp { - op: Some(ast::ArithOp::Add) - }), - ast::Expr::int(10).boxed() + else_: Block { + body: vec![Statement::Bare(Expr::bin( + Expr::ident("a"), + "+=", + Expr::int(10), ))] } })) @@ -1004,7 +920,7 @@ mod test { assert_eq!( ctx.lookup(&String::from("a")).unwrap().clone(), Variable { - ty: ast::Type::Integer, + ty: Type::Integer, name: "a".to_owned(), value: Value::Integer(6) } @@ -1014,29 +930,23 @@ mod test { #[test] fn test_substring() { let language = tree_sitter_python::language(); - let mut ctx = Context::new(language) - .with_program(ast::Program::new()) - .unwrap(); + let mut ctx = Context::new(language).with_program(Program::new()).unwrap(); assert_eq!( - ctx.eval_block(&ast::Block { + ctx.eval_block(&Block { body: vec![ - ast::Statement::Declaration(ast::Declaration { - ty: ast::Type::String, + Statement::Declaration(Declaration { + ty: Type::String, name: "a".to_owned(), - init: Some(ast::Expr::str("foobar").boxed()), + init: Some(Expr::str("foobar").boxed()), }), - ast::Statement::Declaration(ast::Declaration { - ty: ast::Type::String, + Statement::Declaration(Declaration { + ty: Type::String, name: "b".to_owned(), init: Some( - ast::Expr::Call(ast::Call { - function: "substr".into(), - parameters: vec![ - ast::Expr::Ident("a".to_owned()), - ast::Expr::int(0), - ast::Expr::int(3), - ] - }) + Expr::call( + "substr", + [Expr::Ident("a".to_owned()), Expr::int(0), Expr::int(3),] + ) .boxed() ), }), @@ -1047,7 +957,7 @@ mod test { assert_eq!( ctx.lookup(&String::from("b")).unwrap().clone(), Variable { - ty: ast::Type::String, + ty: Type::String, name: "b".to_owned(), value: "foo".into() } @@ -1057,17 +967,15 @@ mod test { #[test] fn test_list() { let language = tree_sitter_python::language(); - let mut ctx = Context::new(language) - .with_program(ast::Program::new()) - .unwrap(); + let mut ctx = Context::new(language).with_program(Program::new()).unwrap(); assert_eq!( - ctx.eval_block(&ast::Block { - body: vec![ast::Statement::Declaration(ast::Declaration { - ty: ast::Type::List, + ctx.eval_block(&Block { + body: vec![Statement::Declaration(Declaration { + ty: Type::List, name: "a".to_owned(), init: Some( - ast::Expr::List(ast::List { - items: vec![ast::Expr::int(5)] + Expr::List(List { + items: vec![Expr::int(5)] }) .boxed() ), @@ -1078,9 +986,33 @@ mod test { assert_eq!( ctx.lookup(&String::from("a")).unwrap().clone(), Variable { - ty: ast::Type::List, + ty: Type::List, + name: "a".to_owned(), + value: vec![5usize.into()].into(), + } + ); + } + + #[test] + fn test_ts_builtins() { + let language = tree_sitter_python::language(); + let mut ctx = Context::new(language).with_program(Program::new()).unwrap(); + assert_eq!( + ctx.eval_block(&Block { + body: vec![Statement::decl( + Type::List, + "a", + Expr::list([Expr::int(5)]), + )] + }), + Ok(Value::Unit) + ); + assert_eq!( + ctx.lookup(&String::from("a")).unwrap().clone(), + Variable { + ty: Type::List, name: "a".to_owned(), - value: vec![5.into()].into(), + value: vec![5usize.into()].into(), } ); } diff --git a/src/lib.rs b/src/lib.rs index 39f4605..afce26c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ mod ast; +mod builtins; mod eval; -pub mod parser; +mod parser; mod string; pub use eval::evaluate; -- cgit v1.2.3