aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2024-09-08 21:43:32 +0100
committerAkshay <[email protected]>2024-09-08 21:43:32 +0100
commitb8308b51b0f4a95f9e211506a8bc61fd04d2edab (patch)
treead13a5543c388d945b45202cdc74173ce1a96116
parent1702f955a4546828cd535be6cecad57b90128de8 (diff)
simplify builtins, more list features
-rw-r--r--flake.lock69
-rw-r--r--flake.nix63
-rw-r--r--src/ast.rs78
-rw-r--r--src/builtins.rs214
-rw-r--r--src/eval.rs282
-rw-r--r--src/lib.rs3
6 files changed, 528 insertions, 181 deletions
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..261d530
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,69 @@
1{
2 "nodes": {
3 "gitignore": {
4 "inputs": {
5 "nixpkgs": [
6 "nixpkgs"
7 ]
8 },
9 "locked": {
10 "lastModified": 1709087332,
11 "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
12 "owner": "hercules-ci",
13 "repo": "gitignore.nix",
14 "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
15 "type": "github"
16 },
17 "original": {
18 "owner": "hercules-ci",
19 "repo": "gitignore.nix",
20 "type": "github"
21 }
22 },
23 "nixpkgs": {
24 "locked": {
25 "lastModified": 1724748588,
26 "narHash": "sha256-NlpGA4+AIf1dKNq76ps90rxowlFXUsV9x7vK/mN37JM=",
27 "owner": "nixos",
28 "repo": "nixpkgs",
29 "rev": "a6292e34000dc93d43bccf78338770c1c5ec8a99",
30 "type": "github"
31 },
32 "original": {
33 "owner": "nixos",
34 "ref": "nixpkgs-unstable",
35 "repo": "nixpkgs",
36 "type": "github"
37 }
38 },
39 "root": {
40 "inputs": {
41 "gitignore": "gitignore",
42 "nixpkgs": "nixpkgs",
43 "rust-overlay": "rust-overlay"
44 }
45 },
46 "rust-overlay": {
47 "inputs": {
48 "nixpkgs": [
49 "nixpkgs"
50 ]
51 },
52 "locked": {
53 "lastModified": 1724811750,
54 "narHash": "sha256-PvhVgQ1rm3gfhK7ts4emprhh/KMkFwXogmgsQ3srR7g=",
55 "owner": "oxalica",
56 "repo": "rust-overlay",
57 "rev": "6a1c4915dca7149e7258d8c7f3ac634d8c65f6c6",
58 "type": "github"
59 },
60 "original": {
61 "owner": "oxalica",
62 "repo": "rust-overlay",
63 "type": "github"
64 }
65 }
66 },
67 "root": "root",
68 "version": 7
69}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..4f69fe8
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,63 @@
1{
2 inputs = {
3
4 nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
5
6 gitignore = {
7 url = "github:hercules-ci/gitignore.nix";
8 inputs.nixpkgs.follows = "nixpkgs";
9 };
10
11 rust-overlay = {
12 url = "github:oxalica/rust-overlay";
13 inputs.nixpkgs.follows = "nixpkgs";
14 };
15
16 };
17
18 outputs =
19 { self
20 , nixpkgs
21 , gitignore
22 , rust-overlay
23 }:
24 let
25 inherit (gitignore.lib) gitignoreSource;
26
27 supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
28 forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
29 nixpkgsFor = forAllSystems (system:
30 import nixpkgs {
31 inherit system;
32 overlays = [rust-overlay.overlays.default];
33 });
34
35 in
36 {
37
38 devShell = forAllSystems (system:
39 let
40 pkgs = nixpkgsFor."${system}";
41 in
42 pkgs.mkShell {
43 nativeBuildInputs = [
44 pkgs.cargo-watch
45 pkgs.bacon
46 pkgs.rustfmt
47 pkgs.cargo
48
49 pkgs.rust-bin.nightly.latest.default
50 pkgs.rust-bin.nightly.latest.rust-analyzer
51 pkgs.lld
52 pkgs.trunk
53
54
55 pkgs.mermaid-cli
56 ];
57 RUST_LOG = "info";
58 RUST_BACKTRACE = 1;
59 });
60 };
61}
62
63
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 {
56 Declaration(Declaration), 56 Declaration(Declaration),
57} 57}
58 58
59impl Statement {
60 #[cfg(test)]
61 pub fn decl(ty: Type, ident: &str, init: Expr) -> Self {
62 Self::Declaration(Declaration {
63 ty,
64 name: ident.to_owned(),
65 init: Some(init.boxed()),
66 })
67 }
68}
69
59#[derive(Debug, Eq, PartialEq, Clone)] 70#[derive(Debug, Eq, PartialEq, Clone)]
60pub enum Expr { 71pub enum Expr {
61 Node, 72 Node,
@@ -77,11 +88,30 @@ impl Expr {
77 Box::new(self) 88 Box::new(self)
78 } 89 }
79 90
80 pub fn as_ident(self) -> Option<Identifier> { 91 #[cfg(test)]
81 match self { 92 pub fn bin(lhs: Expr, op: &str, rhs: Expr) -> Expr {
82 Self::Ident(i) => Some(i), 93 use std::str::FromStr;
83 _ => None, 94 Self::Bin(lhs.boxed(), BinOp::from_str(op).unwrap(), rhs.boxed())
84 } 95 }
96
97 #[cfg(test)]
98 pub fn ident(i: &str) -> Expr {
99 Self::Ident(i.to_owned())
100 }
101
102 #[cfg(test)]
103 pub fn call<const N: usize>(fun: &str, params: [Expr; N]) -> Expr {
104 Self::Call(Call {
105 function: fun.to_owned(),
106 parameters: params.to_vec(),
107 })
108 }
109
110 #[cfg(test)]
111 pub fn list<const N: usize>(items: [Expr; N]) -> Expr {
112 Self::List(List {
113 items: items.to_vec()
114 })
85 } 115 }
86} 116}
87 117
@@ -99,6 +129,44 @@ pub enum BinOp {
99 Assign(AssignOp), 129 Assign(AssignOp),
100} 130}
101 131
132impl std::str::FromStr for BinOp {
133 type Err = ();
134 fn from_str(val: &str) -> Result<Self, Self::Err> {
135 match val {
136 "+" => Ok(Self::Arith(ArithOp::Add)),
137 "-" => Ok(Self::Arith(ArithOp::Sub)),
138 "*" => Ok(Self::Arith(ArithOp::Mul)),
139 "/" => Ok(Self::Arith(ArithOp::Div)),
140 "%" => Ok(Self::Arith(ArithOp::Mod)),
141 ">" => Ok(Self::Cmp(CmpOp::Gt)),
142 "<" => Ok(Self::Cmp(CmpOp::Lt)),
143 ">=" => Ok(Self::Cmp(CmpOp::Gte)),
144 "<=" => Ok(Self::Cmp(CmpOp::Lte)),
145 "==" => Ok(Self::Cmp(CmpOp::Eq)),
146 "!=" => Ok(Self::Cmp(CmpOp::Neq)),
147 "&&" => Ok(Self::Logic(LogicOp::And)),
148 "||" => Ok(Self::Logic(LogicOp::Or)),
149 "=" => Ok(Self::Assign(AssignOp { op: None })),
150 "+=" => Ok(Self::Assign(AssignOp {
151 op: Some(ArithOp::Add),
152 })),
153 "-=" => Ok(Self::Assign(AssignOp {
154 op: Some(ArithOp::Sub),
155 })),
156 "*=" => Ok(Self::Assign(AssignOp {
157 op: Some(ArithOp::Mul),
158 })),
159 "/=" => Ok(Self::Assign(AssignOp {
160 op: Some(ArithOp::Div),
161 })),
162 "%=" => Ok(Self::Assign(AssignOp {
163 op: Some(ArithOp::Mod),
164 })),
165 _ => Err(()),
166 }
167 }
168}
169
102// + - * / 170// + - * /
103#[derive(Debug, Eq, PartialEq, Clone, Copy)] 171#[derive(Debug, Eq, PartialEq, Clone, Copy)]
104pub enum ArithOp { 172pub 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 @@
1use std::{collections::HashMap, sync::LazyLock};
2
3use crate::{
4 ast,
5 eval::{Context, Error, Result, Value},
6 Wrap
7};
8
9pub static BUILTINS: LazyLock<HashMap<&'static str, Box<dyn Builtin + Sync + Send>>> =
10 LazyLock::new(|| {
11 [
12 Print.boxed(),
13 IsUpper.boxed(),
14 IsLower.boxed(),
15 Substr.boxed(),
16 Text.boxed(),
17 Parent.boxed(),
18 Children.boxed(),
19 Length.boxed(),
20 Kind.boxed(),
21 ]
22 .into_iter()
23 .map(|b| (b.id(), b))
24 .collect()
25 });
26
27pub(crate) trait Builtin
28where
29 Self: 'static,
30{
31 /// Name of this function
32 fn id(&self) -> &'static str;
33
34 /// Function description
35 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result;
36
37 /// Anything that is Builtin can be turned into a trait object
38 fn boxed(self) -> Box<dyn Builtin + Sync + Send>
39 where
40 Self: Sized + Sync + Send,
41 {
42 Box::new(self) as Box<dyn Builtin + Sync + Send>
43 }
44}
45
46fn get_args<const N: usize>(args: &[ast::Expr]) -> std::result::Result<&[ast::Expr; N], Error> {
47 args.try_into().map_err(|_| Error::IncorrectArgFormat {
48 wanted: N,
49 got: args.len(),
50 })
51}
52
53struct Print;
54impl Builtin for Print {
55 fn id(&self) -> &'static str {
56 "print"
57 }
58
59 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
60 for arg in args {
61 let val = ctx.eval_expr(arg)?;
62 print!("{val}");
63 }
64 Ok(Value::Unit)
65 }
66}
67
68struct IsUpper;
69impl Builtin for IsUpper {
70 fn id(&self) -> &'static str {
71 "isupper"
72 }
73
74 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
75 Ok(ctx
76 .eval_expr(&get_args::<1>(args)?[0])?
77 .as_str()?
78 .chars()
79 .all(|c| c.is_ascii_uppercase())
80 .into())
81 }
82}
83
84struct IsLower;
85impl Builtin for IsLower {
86 fn id(&self) -> &'static str {
87 "islower"
88 }
89
90 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
91 Ok(ctx
92 .eval_expr(&get_args::<1>(args)?[0])?
93 .as_str()?
94 .chars()
95 .all(|c| c.is_ascii_lowercase())
96 .into())
97 }
98}
99
100struct Substr;
101impl Builtin for Substr {
102 fn id(&self) -> &'static str {
103 "substr"
104 }
105
106 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
107 if let Ok([string, start, end]) = get_args::<3>(args) {
108 let v = ctx.eval_expr(string)?;
109 let s = v.as_str()?;
110 let start = ctx.eval_expr(start)?.as_int()?;
111 let end = ctx.eval_expr(end)?.as_int()?;
112 if start < 0 || start >= s.len() as i128 || end >= s.len() as i128 || start > end {
113 return Err(Error::InvalidStringSlice {
114 length: s.len(),
115 start,
116 end,
117 });
118 }
119 Ok(s[start as usize..end as usize].into())
120 } else {
121 let [string, end] = get_args::<2>(args)?;
122 let v = ctx.eval_expr(string)?;
123 let s = v.as_str()?;
124 let end = ctx.eval_expr(end)?.as_int()?;
125 if end >= s.len() as i128 {
126 return Err(Error::InvalidStringSlice {
127 length: s.len(),
128 start: 0,
129 end,
130 });
131 }
132 Ok(s[..end as usize].into())
133 }
134 }
135}
136
137struct Text;
138impl Builtin for Text {
139 fn id(&self) -> &'static str {
140 "text"
141 }
142
143 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
144 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
145 let id = v.as_node()?;
146 let node = ctx.get_node_by_id(id).unwrap();
147 let text = node
148 .utf8_text(ctx.input_src.as_ref().unwrap().as_bytes())
149 .unwrap();
150 text.to_owned().wrap(Value::String).wrap(Ok)
151 }
152}
153
154struct Parent;
155impl Builtin for Parent {
156 fn id(&self) -> &'static str {
157 "parent"
158 }
159
160 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
161 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
162 let id = v.as_node()?;
163 let node = ctx.get_node_by_id(id).unwrap();
164 let parent = node.parent();
165 parent
166 .map(|n| Value::Node(n.id()))
167 .ok_or(Error::NoParentNode(node))
168 }
169}
170
171struct Children;
172impl Builtin for Children {
173 fn id(&self) -> &'static str {
174 "children"
175 }
176
177 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
178 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
179 let id = v.as_node()?;
180 let node = ctx.get_node_by_id(id).unwrap();
181 let children = node
182 .children(&mut node.walk())
183 .map(|c| Value::Node(c.id()))
184 .collect::<Vec<_>>();
185 Ok(Value::List(children))
186 }
187}
188
189struct Length;
190impl Builtin for Length {
191 fn id(&self) -> &'static str {
192 "length"
193 }
194
195 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
196 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
197 let l = v.as_list()?;
198 (l.len() as i128).wrap(Value::Integer).wrap(Ok)
199 }
200}
201
202struct Kind;
203impl Builtin for Kind {
204 fn id(&self) -> &'static str {
205 "kind"
206 }
207
208 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
209 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
210 let id = v.as_node()?;
211 let node = ctx.get_node_by_id(id).unwrap();
212 node.kind().to_owned().wrap(Value::String).wrap(Ok)
213 }
214}
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 {
93 } 93 }
94 } 94 }
95 95
96 fn as_str(&self) -> std::result::Result<&str, Error> { 96 pub(crate) fn as_str(&self) -> std::result::Result<&str, Error> {
97 match self { 97 match self {
98 Self::String(s) => Ok(s.as_str()), 98 Self::String(s) => Ok(s.as_str()),
99 v => Err(Error::TypeMismatch { 99 v => Err(Error::TypeMismatch {
@@ -103,7 +103,7 @@ impl Value {
103 } 103 }
104 } 104 }
105 105
106 fn as_int(&self) -> std::result::Result<i128, Error> { 106 pub(crate) fn as_int(&self) -> std::result::Result<i128, Error> {
107 match self { 107 match self {
108 Self::Integer(i) => Ok(*i), 108 Self::Integer(i) => Ok(*i),
109 v => Err(Error::TypeMismatch { 109 v => Err(Error::TypeMismatch {
@@ -113,7 +113,7 @@ impl Value {
113 } 113 }
114 } 114 }
115 115
116 fn as_node(&self) -> std::result::Result<NodeId, Error> { 116 pub(crate) fn as_node(&self) -> std::result::Result<NodeId, Error> {
117 match self { 117 match self {
118 Self::Node(id) => Ok(*id), 118 Self::Node(id) => Ok(*id),
119 v => Err(Error::TypeMismatch { 119 v => Err(Error::TypeMismatch {
@@ -123,7 +123,7 @@ impl Value {
123 } 123 }
124 } 124 }
125 125
126 fn as_list(&self) -> std::result::Result<Vec<Value>, Error> { 126 pub(crate) fn as_list(&self) -> std::result::Result<Vec<Value>, Error> {
127 match self { 127 match self {
128 Self::List(values) => Ok(values.clone()), 128 Self::List(values) => Ok(values.clone()),
129 v => Err(Error::TypeMismatch { 129 v => Err(Error::TypeMismatch {
@@ -137,6 +137,7 @@ impl Value {
137 match (self, other) { 137 match (self, other) {
138 (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s + *o)), 138 (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s + *o)),
139 (Self::String(s), Self::String(o)) => Ok(Self::String(format!("{s}{o}"))), 139 (Self::String(s), Self::String(o)) => Ok(Self::String(format!("{s}{o}"))),
140 (Self::List(l), o) => Ok(Self::List(l.iter().cloned().chain([o.clone()]).collect())),
140 _ => Err(Error::UndefinedBinOp( 141 _ => Err(Error::UndefinedBinOp(
141 ast::BinOp::Arith(ast::ArithOp::Add), 142 ast::BinOp::Arith(ast::ArithOp::Add),
142 self.ty(), 143 self.ty(),
@@ -297,8 +298,13 @@ impl fmt::Display for Value {
297 Self::Node(id) => write!(f, "<node #{id}>"), 298 Self::Node(id) => write!(f, "<node #{id}>"),
298 Self::List(items) => { 299 Self::List(items) => {
299 write!(f, "[")?; 300 write!(f, "[")?;
300 for i in items { 301 let mut iterable = items.iter().peekable();
301 write!(f, "{i}")?; 302 while let Some(item) = iterable.next() {
303 if iterable.peek().is_none() {
304 write!(f, "{item}")?;
305 } else {
306 write!(f, "{item}, ")?;
307 }
302 } 308 }
303 write!(f, "]") 309 write!(f, "]")
304 } 310 }
@@ -318,6 +324,12 @@ impl From<i128> for Value {
318 } 324 }
319} 325}
320 326
327impl From<usize> for Value {
328 fn from(value: usize) -> Self {
329 (value as i128).into()
330 }
331}
332
321impl From<&str> for Value { 333impl From<&str> for Value {
322 fn from(value: &str) -> Self { 334 fn from(value: &str) -> Self {
323 Self::String(value.to_owned()) 335 Self::String(value.to_owned())
@@ -402,6 +414,10 @@ pub enum Error {
402 MalformedExpr(String), 414 MalformedExpr(String),
403 InvalidNodeKind(String), 415 InvalidNodeKind(String),
404 NoParentNode(tree_sitter::Node<'static>), 416 NoParentNode(tree_sitter::Node<'static>),
417 IncorrectArgFormat {
418 wanted: usize,
419 got: usize,
420 },
405 InvalidStringSlice { 421 InvalidStringSlice {
406 length: usize, 422 length: usize,
407 start: i128, 423 start: i128,
@@ -409,7 +425,7 @@ pub enum Error {
409 }, 425 },
410 ArrayOutOfBounds { 426 ArrayOutOfBounds {
411 idx: i128, 427 idx: i128,
412 len: usize 428 len: usize,
413 }, 429 },
414 // current node is only set in visitors, not in BEGIN or END blocks 430 // current node is only set in visitors, not in BEGIN or END blocks
415 CurrentNodeNotPresent, 431 CurrentNodeNotPresent,
@@ -421,7 +437,7 @@ pub struct Context {
421 variables: HashMap<ast::Identifier, Variable>, 437 variables: HashMap<ast::Identifier, Variable>,
422 language: tree_sitter::Language, 438 language: tree_sitter::Language,
423 visitors: Visitors, 439 visitors: Visitors,
424 input_src: Option<String>, 440 pub(crate) input_src: Option<String>,
425 cursor: Option<tree_sitter::TreeCursor<'static>>, 441 cursor: Option<tree_sitter::TreeCursor<'static>>,
426 tree: Option<&'static tree_sitter::Tree>, 442 tree: Option<&'static tree_sitter::Tree>,
427 cache: HashMap<NodeId, tree_sitter::Node<'static>>, 443 cache: HashMap<NodeId, tree_sitter::Node<'static>>,
@@ -511,7 +527,7 @@ impl Context {
511 self 527 self
512 } 528 }
513 529
514 fn eval_expr(&mut self, expr: &ast::Expr) -> Result { 530 pub(crate) fn eval_expr(&mut self, expr: &ast::Expr) -> Result {
515 match expr { 531 match expr {
516 ast::Expr::Unit => Ok(Value::Unit), 532 ast::Expr::Unit => Ok(Value::Unit),
517 ast::Expr::Lit(lit) => self.eval_lit(lit), 533 ast::Expr::Lit(lit) => self.eval_lit(lit),
@@ -680,78 +696,10 @@ impl Context {
680 } 696 }
681 697
682 fn eval_call(&mut self, call: &ast::Call) -> Result { 698 fn eval_call(&mut self, call: &ast::Call) -> Result {
683 match (call.function.as_str(), call.parameters.as_slice()) { 699 (&*crate::builtins::BUILTINS)
684 ("print", args) => { 700 .get(call.function.as_str())
685 for arg in args { 701 .ok_or_else(|| Error::FailedLookup(call.function.to_owned()))?
686 let val = self.eval_expr(arg)?; 702 .eval(self, call.parameters.as_slice())
687 print!("{val}");
688 }
689 Ok(Value::Unit)
690 }
691 (predicate @ ("isupper" | "islower"), [arg]) => Ok(self
692 .eval_expr(arg)?
693 .as_str()?
694 .chars()
695 .all(|c| match predicate {
696 "isupper" => c.is_ascii_uppercase(),
697 "islower" => c.is_ascii_lowercase(),
698 _ => unreachable!(),
699 })
700 .into()),
701 ("substr", [string, indices @ ..]) => {
702 let v = self.eval_expr(string)?;
703 let s = v.as_str()?;
704 match indices {
705 [start, end] => {
706 let start = self.eval_expr(start)?.as_int()?;
707 let end = self.eval_expr(end)?.as_int()?;
708 if start < 0
709 || start >= s.len() as i128
710 || end >= s.len() as i128
711 || start > end
712 {
713 return Err(Error::InvalidStringSlice {
714 length: s.len(),
715 start,
716 end,
717 });
718 }
719 Ok(s[start as usize..end as usize].into())
720 }
721 [end] => {
722 let end = self.eval_expr(end)?.as_int()?;
723 if end >= s.len() as i128 {
724 return Err(Error::InvalidStringSlice {
725 length: s.len(),
726 start: 0,
727 end,
728 });
729 }
730 Ok(s[..end as usize].into())
731 }
732 _ => todo!(),
733 }
734 }
735 ("text", [arg]) => {
736 let v = self.eval_expr(arg)?;
737 let id = v.as_node()?;
738 let node = self.get_node_by_id(id).unwrap();
739 let text = node
740 .utf8_text(self.input_src.as_ref().unwrap().as_bytes())
741 .unwrap();
742 Ok(Value::String(text.to_owned()))
743 }
744 ("parent", [arg]) => {
745 let v = self.eval_expr(arg)?;
746 let id = v.as_node()?;
747 let node = self.get_node_by_id(id).unwrap();
748 let parent = node.parent();
749 parent
750 .map(|n| Value::Node(n.id()))
751 .ok_or(Error::NoParentNode(node))
752 }
753 (s, _) => Err(Error::FailedLookup(s.to_owned())),
754 }
755 } 703 }
756 704
757 fn eval_list(&mut self, list: &ast::List) -> Result { 705 fn eval_list(&mut self, list: &ast::List) -> Result {
@@ -768,7 +716,7 @@ impl Context {
768 if idx < 0 || idx >= target.len() as i128 { 716 if idx < 0 || idx >= target.len() as i128 {
769 Err(Error::ArrayOutOfBounds { 717 Err(Error::ArrayOutOfBounds {
770 idx, 718 idx,
771 len: target.len() 719 len: target.len(),
772 }) 720 })
773 } else { 721 } else {
774 Ok(target.remove(idx as usize)) 722 Ok(target.remove(idx as usize))
@@ -881,47 +829,29 @@ pub fn evaluate(file: &str, program: &str, language: tree_sitter::Language) -> R
881#[cfg(test)] 829#[cfg(test)]
882mod test { 830mod test {
883 use super::*; 831 use super::*;
832 use crate::ast::*;
884 833
885 #[test] 834 #[test]
886 fn bin() { 835 fn bin() {
887 let language = tree_sitter_python::language(); 836 let language = tree_sitter_python::language();
888 let mut ctx = Context::new(language) 837 let mut ctx = Context::new(language).with_program(Program::new()).unwrap();
889 .with_program(ast::Program::new())
890 .unwrap();
891 assert_eq!( 838 assert_eq!(
892 ctx.eval_expr(&ast::Expr::Bin( 839 ctx.eval_expr(&Expr::bin(Expr::int(5), "+", Expr::int(10),)),
893 ast::Expr::int(5).boxed(),
894 ast::BinOp::Arith(ast::ArithOp::Add),
895 ast::Expr::int(10).boxed(),
896 )),
897 Ok(Value::Integer(15)) 840 Ok(Value::Integer(15))
898 ); 841 );
899 assert_eq!( 842 assert_eq!(
900 ctx.eval_expr(&ast::Expr::Bin( 843 ctx.eval_expr(&Expr::bin(Expr::int(5), "==", Expr::int(10),)),
901 ast::Expr::int(5).boxed(),
902 ast::BinOp::Cmp(ast::CmpOp::Eq),
903 ast::Expr::int(10).boxed(),
904 )),
905 Ok(Value::Boolean(false)) 844 Ok(Value::Boolean(false))
906 ); 845 );
907 assert_eq!( 846 assert_eq!(
908 ctx.eval_expr(&ast::Expr::Bin( 847 ctx.eval_expr(&Expr::bin(Expr::int(5), "<", Expr::int(10),)),
909 ast::Expr::int(5).boxed(),
910 ast::BinOp::Cmp(ast::CmpOp::Lt),
911 ast::Expr::int(10).boxed(),
912 )),
913 Ok(Value::Boolean(true)) 848 Ok(Value::Boolean(true))
914 ); 849 );
915 assert_eq!( 850 assert_eq!(
916 ctx.eval_expr(&ast::Expr::Bin( 851 ctx.eval_expr(&Expr::bin(
917 ast::Expr::Bin( 852 Expr::bin(Expr::int(5), "<", Expr::int(10),),
918 ast::Expr::int(5).boxed(), 853 "&&",
919 ast::BinOp::Cmp(ast::CmpOp::Lt), 854 Expr::false_(),
920 ast::Expr::int(10).boxed(),
921 )
922 .boxed(),
923 ast::BinOp::Logic(ast::LogicOp::And),
924 ast::Expr::false_().boxed()
925 )), 855 )),
926 Ok(Value::Boolean(false)) 856 Ok(Value::Boolean(false))
927 ); 857 );
@@ -930,24 +860,16 @@ mod test {
930 #[test] 860 #[test]
931 fn test_evaluate_blocks() { 861 fn test_evaluate_blocks() {
932 let language = tree_sitter_python::language(); 862 let language = tree_sitter_python::language();
933 let mut ctx = Context::new(language) 863 let mut ctx = Context::new(language).with_program(Program::new()).unwrap();
934 .with_program(ast::Program::new())
935 .unwrap();
936 assert_eq!( 864 assert_eq!(
937 ctx.eval_block(&ast::Block { 865 ctx.eval_block(&Block {
938 body: vec![ 866 body: vec![
939 ast::Statement::Declaration(ast::Declaration { 867 Statement::Declaration(Declaration {
940 ty: ast::Type::Integer, 868 ty: Type::Integer,
941 name: "a".to_owned(), 869 name: "a".to_owned(),
942 init: None, 870 init: None,
943 }), 871 }),
944 ast::Statement::Bare(ast::Expr::Bin( 872 Statement::Bare(Expr::bin(Expr::ident("a"), "+=", Expr::int(5),)),
945 ast::Expr::Ident("a".to_owned()).boxed(),
946 ast::BinOp::Assign(ast::AssignOp {
947 op: Some(ast::ArithOp::Add)
948 }),
949 ast::Expr::int(5).boxed()
950 )),
951 ] 873 ]
952 }), 874 }),
953 Ok(Value::Unit) 875 Ok(Value::Unit)
@@ -955,7 +877,7 @@ mod test {
955 assert_eq!( 877 assert_eq!(
956 ctx.lookup(&String::from("a")).unwrap().clone(), 878 ctx.lookup(&String::from("a")).unwrap().clone(),
957 Variable { 879 Variable {
958 ty: ast::Type::Integer, 880 ty: Type::Integer,
959 name: "a".to_owned(), 881 name: "a".to_owned(),
960 value: Value::Integer(5) 882 value: Value::Integer(5)
961 } 883 }
@@ -965,35 +887,29 @@ mod test {
965 #[test] 887 #[test]
966 fn test_evaluate_if() { 888 fn test_evaluate_if() {
967 let language = tree_sitter_python::language(); 889 let language = tree_sitter_python::language();
968 let mut ctx = Context::new(language) 890 let mut ctx = Context::new(language).with_program(Program::new()).unwrap();
969 .with_program(ast::Program::new())
970 .unwrap();
971 assert_eq!( 891 assert_eq!(
972 ctx.eval_block(&ast::Block { 892 ctx.eval_block(&Block {
973 body: vec![ 893 body: vec![
974 ast::Statement::Declaration(ast::Declaration { 894 Statement::Declaration(Declaration {
975 ty: ast::Type::Integer, 895 ty: Type::Integer,
976 name: "a".to_owned(), 896 name: "a".to_owned(),
977 init: Some(ast::Expr::int(1).boxed()), 897 init: Some(Expr::int(1).boxed()),
978 }), 898 }),
979 ast::Statement::Bare(ast::Expr::If(ast::IfExpr { 899 Statement::Bare(Expr::If(IfExpr {
980 condition: ast::Expr::true_().boxed(), 900 condition: Expr::true_().boxed(),
981 then: ast::Block { 901 then: Block {
982 body: vec![ast::Statement::Bare(ast::Expr::Bin( 902 body: vec![Statement::Bare(Expr::bin(
983 ast::Expr::Ident("a".to_owned()).boxed(), 903 Expr::Ident("a".to_owned()),
984 ast::BinOp::Assign(ast::AssignOp { 904 "+=",
985 op: Some(ast::ArithOp::Add) 905 Expr::int(5),
986 }),
987 ast::Expr::int(5).boxed()
988 ))] 906 ))]
989 }, 907 },
990 else_: ast::Block { 908 else_: Block {
991 body: vec![ast::Statement::Bare(ast::Expr::Bin( 909 body: vec![Statement::Bare(Expr::bin(
992 ast::Expr::Ident("a".to_owned()).boxed(), 910 Expr::ident("a"),
993 ast::BinOp::Assign(ast::AssignOp { 911 "+=",
994 op: Some(ast::ArithOp::Add) 912 Expr::int(10),
995 }),
996 ast::Expr::int(10).boxed()
997 ))] 913 ))]
998 } 914 }
999 })) 915 }))
@@ -1004,7 +920,7 @@ mod test {
1004 assert_eq!( 920 assert_eq!(
1005 ctx.lookup(&String::from("a")).unwrap().clone(), 921 ctx.lookup(&String::from("a")).unwrap().clone(),
1006 Variable { 922 Variable {
1007 ty: ast::Type::Integer, 923 ty: Type::Integer,
1008 name: "a".to_owned(), 924 name: "a".to_owned(),
1009 value: Value::Integer(6) 925 value: Value::Integer(6)
1010 } 926 }
@@ -1014,29 +930,23 @@ mod test {
1014 #[test] 930 #[test]
1015 fn test_substring() { 931 fn test_substring() {
1016 let language = tree_sitter_python::language(); 932 let language = tree_sitter_python::language();
1017 let mut ctx = Context::new(language) 933 let mut ctx = Context::new(language).with_program(Program::new()).unwrap();
1018 .with_program(ast::Program::new())
1019 .unwrap();
1020 assert_eq!( 934 assert_eq!(
1021 ctx.eval_block(&ast::Block { 935 ctx.eval_block(&Block {
1022 body: vec![ 936 body: vec![
1023 ast::Statement::Declaration(ast::Declaration { 937 Statement::Declaration(Declaration {
1024 ty: ast::Type::String, 938 ty: Type::String,
1025 name: "a".to_owned(), 939 name: "a".to_owned(),
1026 init: Some(ast::Expr::str("foobar").boxed()), 940 init: Some(Expr::str("foobar").boxed()),
1027 }), 941 }),
1028 ast::Statement::Declaration(ast::Declaration { 942 Statement::Declaration(Declaration {
1029 ty: ast::Type::String, 943 ty: Type::String,
1030 name: "b".to_owned(), 944 name: "b".to_owned(),
1031 init: Some( 945 init: Some(
1032 ast::Expr::Call(ast::Call { 946 Expr::call(
1033 function: "substr".into(), 947 "substr",
1034 parameters: vec![ 948 [Expr::Ident("a".to_owned()), Expr::int(0), Expr::int(3),]
1035 ast::Expr::Ident("a".to_owned()), 949 )
1036 ast::Expr::int(0),
1037 ast::Expr::int(3),
1038 ]
1039 })
1040 .boxed() 950 .boxed()
1041 ), 951 ),
1042 }), 952 }),
@@ -1047,7 +957,7 @@ mod test {
1047 assert_eq!( 957 assert_eq!(
1048 ctx.lookup(&String::from("b")).unwrap().clone(), 958 ctx.lookup(&String::from("b")).unwrap().clone(),
1049 Variable { 959 Variable {
1050 ty: ast::Type::String, 960 ty: Type::String,
1051 name: "b".to_owned(), 961 name: "b".to_owned(),
1052 value: "foo".into() 962 value: "foo".into()
1053 } 963 }
@@ -1057,17 +967,15 @@ mod test {
1057 #[test] 967 #[test]
1058 fn test_list() { 968 fn test_list() {
1059 let language = tree_sitter_python::language(); 969 let language = tree_sitter_python::language();
1060 let mut ctx = Context::new(language) 970 let mut ctx = Context::new(language).with_program(Program::new()).unwrap();
1061 .with_program(ast::Program::new())
1062 .unwrap();
1063 assert_eq!( 971 assert_eq!(
1064 ctx.eval_block(&ast::Block { 972 ctx.eval_block(&Block {
1065 body: vec![ast::Statement::Declaration(ast::Declaration { 973 body: vec![Statement::Declaration(Declaration {
1066 ty: ast::Type::List, 974 ty: Type::List,
1067 name: "a".to_owned(), 975 name: "a".to_owned(),
1068 init: Some( 976 init: Some(
1069 ast::Expr::List(ast::List { 977 Expr::List(List {
1070 items: vec![ast::Expr::int(5)] 978 items: vec![Expr::int(5)]
1071 }) 979 })
1072 .boxed() 980 .boxed()
1073 ), 981 ),
@@ -1078,9 +986,33 @@ mod test {
1078 assert_eq!( 986 assert_eq!(
1079 ctx.lookup(&String::from("a")).unwrap().clone(), 987 ctx.lookup(&String::from("a")).unwrap().clone(),
1080 Variable { 988 Variable {
1081 ty: ast::Type::List, 989 ty: Type::List,
990 name: "a".to_owned(),
991 value: vec![5usize.into()].into(),
992 }
993 );
994 }
995
996 #[test]
997 fn test_ts_builtins() {
998 let language = tree_sitter_python::language();
999 let mut ctx = Context::new(language).with_program(Program::new()).unwrap();
1000 assert_eq!(
1001 ctx.eval_block(&Block {
1002 body: vec![Statement::decl(
1003 Type::List,
1004 "a",
1005 Expr::list([Expr::int(5)]),
1006 )]
1007 }),
1008 Ok(Value::Unit)
1009 );
1010 assert_eq!(
1011 ctx.lookup(&String::from("a")).unwrap().clone(),
1012 Variable {
1013 ty: Type::List,
1082 name: "a".to_owned(), 1014 name: "a".to_owned(),
1083 value: vec![5.into()].into(), 1015 value: vec![5usize.into()].into(),
1084 } 1016 }
1085 ); 1017 );
1086 } 1018 }
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 @@
1mod ast; 1mod ast;
2mod builtins;
2mod eval; 3mod eval;
3pub mod parser; 4mod parser;
4mod string; 5mod string;
5 6
6pub use eval::evaluate; 7pub use eval::evaluate;