aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ast.rs130
-rw-r--r--src/eval/analysis.rs108
-rw-r--r--src/eval/builtins.rs45
-rw-r--r--src/main.rs8
4 files changed, 242 insertions, 49 deletions
diff --git a/src/ast.rs b/src/ast.rs
index ec5f953..7fc71a6 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -59,6 +59,16 @@ pub enum Pattern {
59 }, 59 },
60} 60}
61 61
62impl std::fmt::Display for Pattern {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 match self {
65 Self::Begin => write!(f, "BEGIN"),
66 Self::End=> write!(f, "END"),
67 Self::Tree { modifier, matcher } => write!(f, "{modifier} {matcher}"),
68 }
69 }
70}
71
62impl Pattern { 72impl Pattern {
63 pub fn matches(&self, node: tree_sitter::Node) -> bool { 73 pub fn matches(&self, node: tree_sitter::Node) -> bool {
64 match self { 74 match self {
@@ -75,12 +85,39 @@ pub enum Modifier {
75 Leave, 85 Leave,
76} 86}
77 87
88impl std::fmt::Display for Modifier {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 match self {
91 Modifier::Enter => write!(f, "enter"),
92 Modifier::Leave => write!(f, "leave"),
93 }
94 }
95}
96
78#[derive(Debug, Eq, PartialEq, Clone)] 97#[derive(Debug, Eq, PartialEq, Clone)]
79pub enum TreePattern { 98pub enum TreePattern {
80 Atom(String), 99 Atom(String),
81 List(Vec<TreePattern>), 100 List(Vec<TreePattern>),
82} 101}
83 102
103impl std::fmt::Display for TreePattern {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 match self {
106 Self::Atom(a) => write!(f, "{a}"),
107 Self::List(l) => {
108 write!(
109 f,
110 "({})",
111 l.iter()
112 .map(|i| i.to_string())
113 .collect::<Vec<_>>()
114 .join(" ")
115 )
116 }
117 }
118 }
119}
120
84impl TreePattern { 121impl TreePattern {
85 pub fn matches(&self, node: tree_sitter::Node) -> bool { 122 pub fn matches(&self, node: tree_sitter::Node) -> bool {
86 match self { 123 match self {
@@ -112,17 +149,6 @@ pub enum Statement {
112 Declaration(Declaration), 149 Declaration(Declaration),
113} 150}
114 151
115impl Statement {
116 #[cfg(test)]
117 pub fn decl(ty: Type, ident: &str, init: Expr) -> Self {
118 Self::Declaration(Declaration {
119 ty,
120 name: ident.to_owned(),
121 init: Some(init.boxed()),
122 })
123 }
124}
125
126#[derive(Debug, Eq, PartialEq, Clone)] 152#[derive(Debug, Eq, PartialEq, Clone)]
127pub enum Expr { 153pub enum Expr {
128 Node, 154 Node,
@@ -162,13 +188,6 @@ impl Expr {
162 parameters: params.to_vec(), 188 parameters: params.to_vec(),
163 }) 189 })
164 } 190 }
165
166 #[cfg(test)]
167 pub fn list<const N: usize>(items: [Expr; N]) -> Expr {
168 Self::List(List {
169 items: items.to_vec(),
170 })
171 }
172} 191}
173 192
174#[derive(Debug, Eq, PartialEq, Clone, Copy)] 193#[derive(Debug, Eq, PartialEq, Clone, Copy)]
@@ -176,6 +195,14 @@ pub enum UnaryOp {
176 Not, 195 Not,
177} 196}
178 197
198impl std::fmt::Display for UnaryOp {
199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200 match self {
201 Self::Not => write!(f, "!"),
202 }
203 }
204}
205
179#[derive(Debug, Eq, PartialEq, Clone, Copy)] 206#[derive(Debug, Eq, PartialEq, Clone, Copy)]
180pub enum BinOp { 207pub enum BinOp {
181 Arith(ArithOp), 208 Arith(ArithOp),
@@ -223,6 +250,17 @@ impl std::str::FromStr for BinOp {
223 } 250 }
224} 251}
225 252
253impl std::fmt::Display for BinOp {
254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255 match self {
256 Self::Cmp(op) => write!(f, "{op}"),
257 Self::Arith(op) => write!(f, "{op}"),
258 Self::Logic(op) => write!(f, "{op}"),
259 Self::Assign(op) => write!(f, "{op}"),
260 }
261 }
262}
263
226// + - * / 264// + - * /
227#[derive(Debug, Eq, PartialEq, Clone, Copy)] 265#[derive(Debug, Eq, PartialEq, Clone, Copy)]
228pub enum ArithOp { 266pub enum ArithOp {
@@ -233,6 +271,18 @@ pub enum ArithOp {
233 Mod, 271 Mod,
234} 272}
235 273
274impl std::fmt::Display for ArithOp {
275 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276 match self {
277 Self::Add => write!(f, "+"),
278 Self::Sub => write!(f, "-"),
279 Self::Mul => write!(f, "*"),
280 Self::Div => write!(f, "/"),
281 Self::Mod => write!(f, "%"),
282 }
283 }
284}
285
236// && || 286// && ||
237#[derive(Debug, Eq, PartialEq, Clone, Copy)] 287#[derive(Debug, Eq, PartialEq, Clone, Copy)]
238pub enum LogicOp { 288pub enum LogicOp {
@@ -240,6 +290,15 @@ pub enum LogicOp {
240 Or, 290 Or,
241} 291}
242 292
293impl std::fmt::Display for LogicOp {
294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
295 match self {
296 Self::And => write!(f, "&&"),
297 Self::Or => write!(f, "||"),
298 }
299 }
300}
301
243// == != > < >= <= 302// == != > < >= <=
244#[derive(Debug, Eq, PartialEq, Clone, Copy)] 303#[derive(Debug, Eq, PartialEq, Clone, Copy)]
245pub enum CmpOp { 304pub enum CmpOp {
@@ -251,12 +310,34 @@ pub enum CmpOp {
251 Lte, 310 Lte,
252} 311}
253 312
313impl std::fmt::Display for CmpOp {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 match self {
316 Self::Eq => write!(f, "=="),
317 Self::Neq => write!(f, "!="),
318 Self::Gt => write!(f, ">"),
319 Self::Lt => write!(f, "<"),
320 Self::Gte => write!(f, ">="),
321 Self::Lte => write!(f, "<="),
322 }
323 }
324}
325
254// =, +=, -=, *=, /= 326// =, +=, -=, *=, /=
255#[derive(Debug, Eq, PartialEq, Clone, Copy)] 327#[derive(Debug, Eq, PartialEq, Clone, Copy)]
256pub struct AssignOp { 328pub struct AssignOp {
257 pub op: Option<ArithOp>, 329 pub op: Option<ArithOp>,
258} 330}
259 331
332impl std::fmt::Display for AssignOp {
333 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
334 match self.op {
335 None => write!(f, "="),
336 Some(op) => write!(f, "{op}="),
337 }
338 }
339}
340
260pub type Identifier = String; 341pub type Identifier = String;
261 342
262#[derive(Debug, Eq, PartialEq, Clone)] 343#[derive(Debug, Eq, PartialEq, Clone)]
@@ -301,6 +382,19 @@ pub enum Type {
301 List, 382 List,
302} 383}
303 384
385impl std::fmt::Display for Type {
386 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
387 match self {
388 Self::Unit => write!(f, "unit"),
389 Self::Integer => write!(f, "int"),
390 Self::String => write!(f, "string"),
391 Self::Boolean => write!(f, "bool"),
392 Self::Node => write!(f, "node"),
393 Self::List => write!(f, "list"),
394 }
395 }
396}
397
304#[derive(Debug, PartialEq, Eq, Clone)] 398#[derive(Debug, PartialEq, Eq, Clone)]
305pub struct Declaration { 399pub struct Declaration {
306 pub ty: Type, 400 pub ty: Type,
diff --git a/src/eval/analysis.rs b/src/eval/analysis.rs
new file mode 100644
index 0000000..b3a0083
--- /dev/null
+++ b/src/eval/analysis.rs
@@ -0,0 +1,108 @@
1use crate::{ast, Wrap};
2
3#[derive(Debug, PartialEq, Eq)]
4pub struct Analysis {
5 diagnostics: Vec<Diagnostic>,
6}
7
8impl Analysis {
9 pub fn print(&self) {
10 for Diagnostic { level, kind } in &self.diagnostics {
11 eprintln!("{level} {kind}");
12 }
13 }
14}
15
16#[derive(Debug, PartialEq, Eq)]
17pub struct Diagnostic {
18 level: Level,
19 kind: Kind,
20}
21
22#[derive(Debug, PartialEq, Eq)]
23pub enum Level {
24 Warning,
25 Error,
26}
27
28impl std::fmt::Display for Level {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 match self {
31 Level::Warning => write!(f, "[warning]"),
32 Level::Error => write!(f, "[error]"),
33 }
34 }
35}
36
37#[derive(Debug, PartialEq, Eq)]
38pub enum Kind {
39 Pattern {
40 invalid_node_kind: String,
41 full_pattern: ast::Pattern,
42 },
43}
44
45impl std::fmt::Display for Kind {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 match self {
48 Self::Pattern {
49 invalid_node_kind,
50 full_pattern,
51 } => {
52 write!(
53 f,
54 "invalid node kind `{invalid_node_kind}` in pattern `{full_pattern}`"
55 )
56 }
57 }
58 }
59}
60
61pub fn run(ctx: &super::Context) -> Analysis {
62 [validate_patterns(ctx)]
63 .into_iter()
64 .flatten()
65 .collect::<Vec<_>>()
66 .wrap(|diagnostics| Analysis { diagnostics })
67}
68
69fn validate_patterns(ctx: &super::Context) -> Vec<Diagnostic> {
70 fn validate_pattern(
71 pattern: &ast::TreePattern,
72 language: &tree_sitter::Language,
73 ) -> Option<String> {
74 match pattern {
75 ast::TreePattern::Atom(a) => {
76 if language.id_for_node_kind(a, true) == 0 {
77 Some(a.to_owned())
78 } else {
79 None
80 }
81 }
82 ast::TreePattern::List(items) => {
83 for item in items {
84 validate_pattern(item, language)?;
85 }
86 None
87 }
88 }
89 }
90
91 ctx.program
92 .stanzas
93 .iter()
94 .flat_map(|s| match &s.pattern {
95 ast::Pattern::Begin | ast::Pattern::End => None,
96 ast::Pattern::Tree { matcher, .. } => {
97 validate_pattern(matcher, &ctx.language).map(|invalid_node_kind| Kind::Pattern {
98 invalid_node_kind,
99 full_pattern: s.pattern.clone(),
100 })
101 }
102 })
103 .map(|kind| Diagnostic {
104 level: Level::Error,
105 kind,
106 })
107 .collect()
108}
diff --git a/src/eval/builtins.rs b/src/eval/builtins.rs
index 7d10a86..c91f7ba 100644
--- a/src/eval/builtins.rs
+++ b/src/eval/builtins.rs
@@ -29,6 +29,8 @@ builtins! {
29 // string 29 // string
30 isupper, 30 isupper,
31 islower, 31 islower,
32 toupper,
33 tolower,
32 substr, 34 substr,
33 35
34 // node 36 // node
@@ -78,6 +80,22 @@ fn islower(ctx: &mut Context, args: &[ast::Expr]) -> Result {
78 .into()) 80 .into())
79} 81}
80 82
83fn toupper(ctx: &mut Context, args: &[ast::Expr]) -> Result {
84 Ok(ctx
85 .eval_expr(&get_args::<1>(args)?[0])?
86 .as_str()?
87 .to_uppercase()
88 .into())
89}
90
91fn tolower(ctx: &mut Context, args: &[ast::Expr]) -> Result {
92 Ok(ctx
93 .eval_expr(&get_args::<1>(args)?[0])?
94 .as_str()?
95 .to_lowercase()
96 .into())
97}
98
81fn substr(ctx: &mut Context, args: &[ast::Expr]) -> Result { 99fn substr(ctx: &mut Context, args: &[ast::Expr]) -> Result {
82 if let Ok([string, start, end]) = get_args::<3>(args) { 100 if let Ok([string, start, end]) = get_args::<3>(args) {
83 let v = ctx.eval_expr(string)?; 101 let v = ctx.eval_expr(string)?;
@@ -217,30 +235,3 @@ fn get_args<const N: usize>(args: &[ast::Expr]) -> std::result::Result<&[ast::Ex
217 got: args.len(), 235 got: args.len(),
218 }) 236 })
219} 237}
220
221#[cfg(test)]
222mod test {
223 use super::*;
224 use crate::{ast::*, eval::*};
225
226 #[test]
227 fn test_ts_builtins() {
228 let language = tree_sitter_python::language();
229 let mut ctx = Context::new(language).with_program(Program::new());
230
231 assert_eq!(
232 ctx.eval_block(&Block {
233 body: vec![Statement::decl(Type::List, "a", Expr::list([Expr::int(5)]),)]
234 }),
235 Ok(Value::Unit)
236 );
237 assert_eq!(
238 ctx.lookup(&String::from("a")).unwrap().clone(),
239 Variable {
240 ty: Type::List,
241 name: "a".to_owned(),
242 value: vec![5usize.into()].into(),
243 }
244 );
245 }
246}
diff --git a/src/main.rs b/src/main.rs
index 8f63ca2..193d725 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -73,8 +73,8 @@ fn try_consume_stdin() -> std::io::Result<String> {
73fn main() { 73fn main() {
74 let cli: Cli = argh::from_env(); 74 let cli: Cli = argh::from_env();
75 75
76 tbsp::evaluate(&cli.file(), &cli.program(), cli.language()).unwrap_or_else(|e| { 76 if let Err(e) = tbsp::evaluate(&cli.file(), &cli.program(), cli.language()) {
77 eprintln!("{e:?}"); 77 eprintln!("{e}");
78 std::process::exit(-1); 78 std::process::exit(e.code());
79 }); 79 };
80} 80}