diff options
author | Akshay <[email protected]> | 2024-10-11 03:55:31 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2024-10-11 03:55:31 +0100 |
commit | 63c606d17095e336ee0deeedb30f94a490ec4059 (patch) | |
tree | 8e64f9058306fd81cde35b95eacd40ac3ab99c97 /src | |
parent | b84a1a3598f68c1575a92bcd2ba68e9d1322256d (diff) |
add analysis phase
Diffstat (limited to 'src')
-rw-r--r-- | src/ast.rs | 130 | ||||
-rw-r--r-- | src/eval/analysis.rs | 108 | ||||
-rw-r--r-- | src/eval/builtins.rs | 45 | ||||
-rw-r--r-- | src/main.rs | 8 |
4 files changed, 242 insertions, 49 deletions
@@ -59,6 +59,16 @@ pub enum Pattern { | |||
59 | }, | 59 | }, |
60 | } | 60 | } |
61 | 61 | ||
62 | impl 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 | |||
62 | impl Pattern { | 72 | impl 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 | ||
88 | impl 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)] |
79 | pub enum TreePattern { | 98 | pub enum TreePattern { |
80 | Atom(String), | 99 | Atom(String), |
81 | List(Vec<TreePattern>), | 100 | List(Vec<TreePattern>), |
82 | } | 101 | } |
83 | 102 | ||
103 | impl 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 | |||
84 | impl TreePattern { | 121 | impl 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 | ||
115 | impl 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)] |
127 | pub enum Expr { | 153 | pub 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 | ||
198 | impl 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)] |
180 | pub enum BinOp { | 207 | pub enum BinOp { |
181 | Arith(ArithOp), | 208 | Arith(ArithOp), |
@@ -223,6 +250,17 @@ impl std::str::FromStr for BinOp { | |||
223 | } | 250 | } |
224 | } | 251 | } |
225 | 252 | ||
253 | impl 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)] |
228 | pub enum ArithOp { | 266 | pub enum ArithOp { |
@@ -233,6 +271,18 @@ pub enum ArithOp { | |||
233 | Mod, | 271 | Mod, |
234 | } | 272 | } |
235 | 273 | ||
274 | impl 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)] |
238 | pub enum LogicOp { | 288 | pub enum LogicOp { |
@@ -240,6 +290,15 @@ pub enum LogicOp { | |||
240 | Or, | 290 | Or, |
241 | } | 291 | } |
242 | 292 | ||
293 | impl 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)] |
245 | pub enum CmpOp { | 304 | pub enum CmpOp { |
@@ -251,12 +310,34 @@ pub enum CmpOp { | |||
251 | Lte, | 310 | Lte, |
252 | } | 311 | } |
253 | 312 | ||
313 | impl 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)] |
256 | pub struct AssignOp { | 328 | pub struct AssignOp { |
257 | pub op: Option<ArithOp>, | 329 | pub op: Option<ArithOp>, |
258 | } | 330 | } |
259 | 331 | ||
332 | impl 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 | |||
260 | pub type Identifier = String; | 341 | pub 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 | ||
385 | impl 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)] |
305 | pub struct Declaration { | 399 | pub 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 @@ | |||
1 | use crate::{ast, Wrap}; | ||
2 | |||
3 | #[derive(Debug, PartialEq, Eq)] | ||
4 | pub struct Analysis { | ||
5 | diagnostics: Vec<Diagnostic>, | ||
6 | } | ||
7 | |||
8 | impl 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)] | ||
17 | pub struct Diagnostic { | ||
18 | level: Level, | ||
19 | kind: Kind, | ||
20 | } | ||
21 | |||
22 | #[derive(Debug, PartialEq, Eq)] | ||
23 | pub enum Level { | ||
24 | Warning, | ||
25 | Error, | ||
26 | } | ||
27 | |||
28 | impl 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)] | ||
38 | pub enum Kind { | ||
39 | Pattern { | ||
40 | invalid_node_kind: String, | ||
41 | full_pattern: ast::Pattern, | ||
42 | }, | ||
43 | } | ||
44 | |||
45 | impl 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 | |||
61 | pub 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 | |||
69 | fn 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 | ||
83 | fn 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 | |||
91 | fn 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 | |||
81 | fn substr(ctx: &mut Context, args: &[ast::Expr]) -> Result { | 99 | fn 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)] | ||
222 | mod 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> { | |||
73 | fn main() { | 73 | fn 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 | } |