fork of https://github.com/tree-sitter/tree-sitter-graph
at main 9.5 kB view raw
1// -*- coding: utf-8 -*- 2// ------------------------------------------------------------------------------------------------ 3// Copyright © 2021, tree-sitter authors. 4// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. 5// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. 6// ------------------------------------------------------------------------------------------------ 7 8use std::path::Path; 9use thiserror::Error; 10 11use crate::ast::Stanza; 12use crate::ast::Statement; 13use crate::execution::CancellationError; 14use crate::parse_error::Excerpt; 15use crate::Location; 16 17/// An error that can occur while executing a graph DSL file 18#[derive(Debug, Error)] 19pub enum ExecutionError { 20 #[error(transparent)] 21 Cancelled(#[from] CancellationError), 22 #[error("Cannot assign immutable variable {0}")] 23 CannotAssignImmutableVariable(String), 24 #[error("Cannot assign scoped variable {0}")] 25 CannotAssignScopedVariable(String), 26 #[error("Cannot define mutable scoped variable {0}")] 27 CannotDefineMutableScopedVariable(String), 28 #[error("Duplicate attribute {0}")] 29 DuplicateAttribute(String), 30 #[error("Duplicate edge {0}")] 31 DuplicateEdge(String), 32 #[error("Duplicate variable {0}")] 33 DuplicateVariable(String), 34 #[error("Expected a graph node reference {0}")] 35 ExpectedGraphNode(String), 36 #[error("Expected a list {0}")] 37 ExpectedList(String), 38 #[error("Expected a boolean {0}")] 39 ExpectedBoolean(String), 40 #[error("Expected an integer {0}")] 41 ExpectedInteger(String), 42 #[error("Expected a string {0}")] 43 ExpectedString(String), 44 #[error("Expected a syntax node {0}")] 45 ExpectedSyntaxNode(String), 46 #[error("Invalid parameters {0}")] 47 InvalidParameters(String), 48 #[error("Scoped variables can only be attached to syntax nodes {0}")] 49 InvalidVariableScope(String), 50 #[error("Missing global variable {0}")] 51 MissingGlobalVariable(String), 52 #[error("Recursively defined scoped variable {0}")] 53 RecursivelyDefinedScopedVariable(String), 54 #[error("Recursively defined variable {0}")] 55 RecursivelyDefinedVariable(String), 56 #[error("Undefined capture {0}")] 57 UndefinedCapture(String), 58 #[error("Undefined function {0}")] 59 UndefinedFunction(String), 60 #[error("Undefined regex capture {0}")] 61 UndefinedRegexCapture(String), 62 #[error("Undefined scoped variable {0}")] 63 UndefinedScopedVariable(String), 64 #[error("Empty regex capture {0}")] 65 EmptyRegexCapture(String), 66 #[error("Undefined edge {0}")] 67 UndefinedEdge(String), 68 #[error("Undefined variable {0}")] 69 UndefinedVariable(String), 70 #[error("Cannot add scoped variable after being forced {0}")] 71 VariableScopesAlreadyForced(String), 72 #[error("Function {0} failed: {1}")] 73 FunctionFailed(String, String), 74 #[error("{0}. Caused by: {1}")] 75 InContext(Context, Box<ExecutionError>), 76} 77 78#[derive(Clone, Debug)] 79pub enum Context { 80 Statement(Vec<StatementContext>), 81 Other(String), 82} 83 84#[derive(Clone, Debug)] 85pub struct StatementContext { 86 pub statement: String, 87 pub statement_location: Location, 88 pub stanza_location: Location, 89 pub source_location: Location, 90 pub node_kind: String, 91} 92 93impl StatementContext { 94 pub(crate) fn new(stmt: &Statement, stanza: &Stanza, source_node: &tree_sitter::Node) -> Self { 95 Self { 96 statement: format!("{}", stmt), 97 statement_location: stmt.location(), 98 stanza_location: stanza.range.start, 99 source_location: Location::from(source_node.range().start_point), 100 node_kind: source_node.kind().to_string(), 101 } 102 } 103 104 pub(crate) fn update_statement(&mut self, stmt: &Statement) { 105 self.statement = format!("{}", stmt); 106 self.statement_location = stmt.location(); 107 } 108} 109 110impl From<StatementContext> for Context { 111 fn from(value: StatementContext) -> Self { 112 Self::Statement(vec![value]) 113 } 114} 115 116impl From<(StatementContext, StatementContext)> for Context { 117 fn from((left, right): (StatementContext, StatementContext)) -> Self { 118 Self::Statement(vec![left, right]) 119 } 120} 121 122impl From<String> for Context { 123 fn from(value: String) -> Self { 124 Self::Other(value) 125 } 126} 127 128impl std::fmt::Display for Context { 129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 130 match self { 131 Self::Statement(stmts) => { 132 let mut first = true; 133 for stmt in stmts { 134 stmt.fmt(f, first)?; 135 first = false; 136 } 137 } 138 Self::Other(msg) => write!(f, "{}", msg)?, 139 } 140 Ok(()) 141 } 142} 143 144impl StatementContext { 145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, first: bool) -> std::fmt::Result { 146 if first { 147 write!(f, "Error executing",)?; 148 } else { 149 write!(f, " and executing",)?; 150 } 151 write!( 152 f, 153 " {} in stanza at {} matching ({}) node at {}", 154 self.statement, self.stanza_location, self.node_kind, self.source_location 155 )?; 156 Ok(()) 157 } 158} 159 160pub(super) trait ResultWithExecutionError<R> { 161 fn with_context<F>(self, with_context: F) -> Result<R, ExecutionError> 162 where 163 F: FnOnce() -> Context; 164} 165 166impl<R> ResultWithExecutionError<R> for Result<R, ExecutionError> { 167 fn with_context<F>(self, with_context: F) -> Result<R, ExecutionError> 168 where 169 F: FnOnce() -> Context, 170 { 171 self.map_err(|e| match e { 172 cancelled @ ExecutionError::Cancelled(_) => cancelled, 173 in_other_context @ ExecutionError::InContext(Context::Other(_), _) => { 174 ExecutionError::InContext(with_context(), Box::new(in_other_context)) 175 } 176 in_stmt_context @ ExecutionError::InContext(_, _) => in_stmt_context, 177 _ => ExecutionError::InContext(with_context(), Box::new(e)), 178 }) 179 } 180} 181 182impl ExecutionError { 183 pub fn display_pretty<'a>( 184 &'a self, 185 source_path: &'a Path, 186 source: &'a str, 187 tsg_path: &'a Path, 188 tsg: &'a str, 189 ) -> impl std::fmt::Display + 'a { 190 DisplayExecutionErrorPretty { 191 error: self, 192 source_path, 193 source, 194 tsg_path, 195 tsg, 196 } 197 } 198} 199 200struct DisplayExecutionErrorPretty<'a> { 201 error: &'a ExecutionError, 202 source_path: &'a Path, 203 source: &'a str, 204 tsg_path: &'a Path, 205 tsg: &'a str, 206} 207 208impl std::fmt::Display for DisplayExecutionErrorPretty<'_> { 209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 210 self.fmt_entry(f, 0, self.error) 211 } 212} 213 214impl DisplayExecutionErrorPretty<'_> { 215 fn fmt_entry( 216 &self, 217 f: &mut std::fmt::Formatter<'_>, 218 index: usize, 219 error: &ExecutionError, 220 ) -> std::fmt::Result { 221 match error { 222 ExecutionError::InContext(context, cause) => { 223 match context { 224 Context::Statement(stmts) => { 225 let mut first = true; 226 for stmt in stmts { 227 stmt.fmt_pretty( 228 f, 229 self.source_path, 230 self.source, 231 self.tsg_path, 232 self.tsg, 233 index, 234 first, 235 )?; 236 first = false; 237 } 238 } 239 Context::Other(msg) => writeln!(f, "{:>5}: {}", index, msg)?, 240 }; 241 self.fmt_entry(f, index + 1, cause)?; 242 Ok(()) 243 } 244 other => writeln!(f, "{:>5}: {}", index, other), 245 } 246 } 247} 248 249impl StatementContext { 250 fn fmt_pretty( 251 &self, 252 f: &mut std::fmt::Formatter<'_>, 253 source_path: &Path, 254 source: &str, 255 tsg_path: &Path, 256 tsg: &str, 257 index: usize, 258 first: bool, 259 ) -> std::fmt::Result { 260 if first { 261 writeln!( 262 f, 263 "{:>5}: Error executing statement {}", 264 index, self.statement 265 )?; 266 } else { 267 writeln!(f, " > and executing statement {}", self.statement)?; 268 } 269 write!( 270 f, 271 "{}", 272 Excerpt::from_source( 273 tsg_path, 274 tsg, 275 self.statement_location.row, 276 self.statement_location.to_column_range(), 277 7 278 ) 279 )?; 280 writeln!(f, "{}in stanza", " ".repeat(7))?; 281 write!( 282 f, 283 "{}", 284 Excerpt::from_source( 285 tsg_path, 286 tsg, 287 self.stanza_location.row, 288 self.stanza_location.to_column_range(), 289 7 290 ) 291 )?; 292 writeln!(f, "{}matching ({}) node", " ".repeat(7), self.node_kind)?; 293 write!( 294 f, 295 "{}", 296 Excerpt::from_source( 297 source_path, 298 source, 299 self.source_location.row, 300 self.source_location.to_column_range(), 301 7 302 ) 303 )?; 304 Ok(()) 305 } 306}