From 63c606d17095e336ee0deeedb30f94a490ec4059 Mon Sep 17 00:00:00 2001 From: Akshay Date: Fri, 11 Oct 2024 08:25:31 +0530 Subject: add analysis phase --- src/ast.rs | 130 ++++++++++++++++++++++++++++++++++++++++++++------- src/eval/analysis.rs | 108 ++++++++++++++++++++++++++++++++++++++++++ src/eval/builtins.rs | 45 +++++++----------- src/main.rs | 8 ++-- 4 files changed, 242 insertions(+), 49 deletions(-) create mode 100644 src/eval/analysis.rs (limited to 'src') 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 { }, } +impl std::fmt::Display for Pattern { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Begin => write!(f, "BEGIN"), + Self::End=> write!(f, "END"), + Self::Tree { modifier, matcher } => write!(f, "{modifier} {matcher}"), + } + } +} + impl Pattern { pub fn matches(&self, node: tree_sitter::Node) -> bool { match self { @@ -75,12 +85,39 @@ pub enum Modifier { Leave, } +impl std::fmt::Display for Modifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Modifier::Enter => write!(f, "enter"), + Modifier::Leave => write!(f, "leave"), + } + } +} + #[derive(Debug, Eq, PartialEq, Clone)] pub enum TreePattern { Atom(String), List(Vec), } +impl std::fmt::Display for TreePattern { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Atom(a) => write!(f, "{a}"), + Self::List(l) => { + write!( + f, + "({})", + l.iter() + .map(|i| i.to_string()) + .collect::>() + .join(" ") + ) + } + } + } +} + impl TreePattern { pub fn matches(&self, node: tree_sitter::Node) -> bool { match self { @@ -112,17 +149,6 @@ 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, @@ -162,13 +188,6 @@ impl Expr { parameters: params.to_vec(), }) } - - #[cfg(test)] - pub fn list(items: [Expr; N]) -> Expr { - Self::List(List { - items: items.to_vec(), - }) - } } #[derive(Debug, Eq, PartialEq, Clone, Copy)] @@ -176,6 +195,14 @@ pub enum UnaryOp { Not, } +impl std::fmt::Display for UnaryOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Not => write!(f, "!"), + } + } +} + #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum BinOp { Arith(ArithOp), @@ -223,6 +250,17 @@ impl std::str::FromStr for BinOp { } } +impl std::fmt::Display for BinOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Cmp(op) => write!(f, "{op}"), + Self::Arith(op) => write!(f, "{op}"), + Self::Logic(op) => write!(f, "{op}"), + Self::Assign(op) => write!(f, "{op}"), + } + } +} + // + - * / #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum ArithOp { @@ -233,6 +271,18 @@ pub enum ArithOp { Mod, } +impl std::fmt::Display for ArithOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Add => write!(f, "+"), + Self::Sub => write!(f, "-"), + Self::Mul => write!(f, "*"), + Self::Div => write!(f, "/"), + Self::Mod => write!(f, "%"), + } + } +} + // && || #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum LogicOp { @@ -240,6 +290,15 @@ pub enum LogicOp { Or, } +impl std::fmt::Display for LogicOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::And => write!(f, "&&"), + Self::Or => write!(f, "||"), + } + } +} + // == != > < >= <= #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum CmpOp { @@ -251,12 +310,34 @@ pub enum CmpOp { Lte, } +impl std::fmt::Display for CmpOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Eq => write!(f, "=="), + Self::Neq => write!(f, "!="), + Self::Gt => write!(f, ">"), + Self::Lt => write!(f, "<"), + Self::Gte => write!(f, ">="), + Self::Lte => write!(f, "<="), + } + } +} + // =, +=, -=, *=, /= #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct AssignOp { pub op: Option, } +impl std::fmt::Display for AssignOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.op { + None => write!(f, "="), + Some(op) => write!(f, "{op}="), + } + } +} + pub type Identifier = String; #[derive(Debug, Eq, PartialEq, Clone)] @@ -301,6 +382,19 @@ pub enum Type { List, } +impl std::fmt::Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Unit => write!(f, "unit"), + Self::Integer => write!(f, "int"), + Self::String => write!(f, "string"), + Self::Boolean => write!(f, "bool"), + Self::Node => write!(f, "node"), + Self::List => write!(f, "list"), + } + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Declaration { 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 @@ +use crate::{ast, Wrap}; + +#[derive(Debug, PartialEq, Eq)] +pub struct Analysis { + diagnostics: Vec, +} + +impl Analysis { + pub fn print(&self) { + for Diagnostic { level, kind } in &self.diagnostics { + eprintln!("{level} {kind}"); + } + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Diagnostic { + level: Level, + kind: Kind, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Level { + Warning, + Error, +} + +impl std::fmt::Display for Level { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Level::Warning => write!(f, "[warning]"), + Level::Error => write!(f, "[error]"), + } + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Kind { + Pattern { + invalid_node_kind: String, + full_pattern: ast::Pattern, + }, +} + +impl std::fmt::Display for Kind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Pattern { + invalid_node_kind, + full_pattern, + } => { + write!( + f, + "invalid node kind `{invalid_node_kind}` in pattern `{full_pattern}`" + ) + } + } + } +} + +pub fn run(ctx: &super::Context) -> Analysis { + [validate_patterns(ctx)] + .into_iter() + .flatten() + .collect::>() + .wrap(|diagnostics| Analysis { diagnostics }) +} + +fn validate_patterns(ctx: &super::Context) -> Vec { + fn validate_pattern( + pattern: &ast::TreePattern, + language: &tree_sitter::Language, + ) -> Option { + match pattern { + ast::TreePattern::Atom(a) => { + if language.id_for_node_kind(a, true) == 0 { + Some(a.to_owned()) + } else { + None + } + } + ast::TreePattern::List(items) => { + for item in items { + validate_pattern(item, language)?; + } + None + } + } + } + + ctx.program + .stanzas + .iter() + .flat_map(|s| match &s.pattern { + ast::Pattern::Begin | ast::Pattern::End => None, + ast::Pattern::Tree { matcher, .. } => { + validate_pattern(matcher, &ctx.language).map(|invalid_node_kind| Kind::Pattern { + invalid_node_kind, + full_pattern: s.pattern.clone(), + }) + } + }) + .map(|kind| Diagnostic { + level: Level::Error, + kind, + }) + .collect() +} 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! { // string isupper, islower, + toupper, + tolower, substr, // node @@ -78,6 +80,22 @@ fn islower(ctx: &mut Context, args: &[ast::Expr]) -> Result { .into()) } +fn toupper(ctx: &mut Context, args: &[ast::Expr]) -> Result { + Ok(ctx + .eval_expr(&get_args::<1>(args)?[0])? + .as_str()? + .to_uppercase() + .into()) +} + +fn tolower(ctx: &mut Context, args: &[ast::Expr]) -> Result { + Ok(ctx + .eval_expr(&get_args::<1>(args)?[0])? + .as_str()? + .to_lowercase() + .into()) +} + fn substr(ctx: &mut Context, args: &[ast::Expr]) -> Result { if let Ok([string, start, end]) = get_args::<3>(args) { let v = ctx.eval_expr(string)?; @@ -217,30 +235,3 @@ fn get_args(args: &[ast::Expr]) -> std::result::Result<&[ast::Ex got: args.len(), }) } - -#[cfg(test)] -mod test { - use super::*; - use crate::{ast::*, eval::*}; - - #[test] - fn test_ts_builtins() { - let language = tree_sitter_python::language(); - let mut ctx = Context::new(language).with_program(Program::new()); - - 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![5usize.into()].into(), - } - ); - } -} 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 { fn main() { let cli: Cli = argh::from_env(); - tbsp::evaluate(&cli.file(), &cli.program(), cli.language()).unwrap_or_else(|e| { - eprintln!("{e:?}"); - std::process::exit(-1); - }); + if let Err(e) = tbsp::evaluate(&cli.file(), &cli.program(), cli.language()) { + eprintln!("{e}"); + std::process::exit(e.code()); + }; } -- cgit v1.2.3