plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
plum-core/src/builtin_usage.rs
| 29313cb | 1 | use std::collections::HashSet; |
| 29313cb | 2 | |
| 29313cb | 3 | use crate::ast::{ |
| 29313cb | 4 | Arg, AssignTarget, AttrKind, CasePattern, Expr, FnBody, Item, ParamType, Source, Stmt, |
| 29313cb | 5 | StringPart, Type, |
| 29313cb | 6 | }; |
| 29313cb | 7 | |
| 29313cb | 8 | /// The builtin type names this walk tracks: primitive value types with no |
| 29313cb | 9 | /// enum-variant/name-resolution step that naturally fails when unimported |
| 29313cb | 10 | /// (unlike `Bool`, whose `True`/`False` variants simply don't resolve without |
| 29313cb | 11 | /// `import std/Bool`) — so nothing else catches a missing `import std/Int` / |
| 29313cb | 12 | /// `import std/Float` / `import std/Str`. |
| 29313cb | 13 | const TRACKED_TYPES: [&str; 3] = ["Int", "Float", "Str"]; |
| 29313cb | 14 | |
| 29313cb | 15 | fn isTracked(name: &str) -> bool { |
| 29313cb | 16 | TRACKED_TYPES.contains(&name) |
| 29313cb | 17 | } |
| 29313cb | 18 | |
| 29313cb | 19 | /// Every builtin type name (`Int`, `Float`, `Str` — literal or type-annotation) |
| 29313cb | 20 | /// mentioned anywhere in `source` — used by `loader::loadAndMerge` to enforce |
| 29313cb | 21 | /// that a file using one of them explicitly `import`s it, exactly like every |
| 29313cb | 22 | /// other stdlib type (`Bool`, ...) already must be. |
| 29313cb | 23 | pub fn usedBuiltinTypeNames(source: &Source) -> HashSet<String> { |
| 29313cb | 24 | let mut names = HashSet::new(); |
| 29313cb | 25 | for item in &source.items { |
| 29313cb | 26 | match item { |
| 29313cb | 27 | Item::Trait(t) => { |
| 29313cb | 28 | for m in &t.methods { |
| 29313cb | 29 | for p in &m.params { |
| 29313cb | 30 | walkParamType(&p.ty, &mut names); |
| 29313cb | 31 | } |
| 29313cb | 32 | if let Some(r) = &m.returns { |
| 29313cb | 33 | walkType(r, &mut names); |
| 29313cb | 34 | } |
| 29313cb | 35 | } |
| 29313cb | 36 | } |
| 29313cb | 37 | Item::Enum(e) => { |
| 29313cb | 38 | for p in &e.params { |
| 29313cb | 39 | walkType(&p.ty, &mut names); |
| 29313cb | 40 | } |
| 29313cb | 41 | for v in &e.variants { |
| 29313cb | 42 | for f in &v.fields { |
| 29313cb | 43 | walkType(f, &mut names); |
| 29313cb | 44 | } |
| 29313cb | 45 | for val in &v.values { |
| 29313cb | 46 | walkExpr(val, &mut names); |
| 29313cb | 47 | } |
| 29313cb | 48 | } |
| 29313cb | 49 | } |
| 29313cb | 50 | Item::Fn(f) => { |
| 29313cb | 51 | for p in &f.params { |
| 29313cb | 52 | walkParamType(&p.ty, &mut names); |
| 29313cb | 53 | if let Some(d) = &p.default { |
| 29313cb | 54 | walkExpr(d, &mut names); |
| 29313cb | 55 | } |
| 29313cb | 56 | } |
| 29313cb | 57 | if let Some(r) = &f.returns { |
| 29313cb | 58 | walkType(r, &mut names); |
| 29313cb | 59 | } |
| 29313cb | 60 | match &f.body { |
| 29313cb | 61 | FnBody::Expr(e) => walkExpr(e, &mut names), |
| 29313cb | 62 | FnBody::Block(b) => walkStmts(&b.stmts, &mut names), |
| 29313cb | 63 | FnBody::Extern => {} |
| 29313cb | 64 | } |
| 29313cb | 65 | } |
| 29313cb | 66 | Item::Const(c) => walkExpr(&c.value, &mut names), |
| 29313cb | 67 | Item::Test(t) => walkStmts(&t.body.stmts, &mut names), |
| 29313cb | 68 | } |
| 29313cb | 69 | } |
| 29313cb | 70 | names |
| 29313cb | 71 | } |
| 29313cb | 72 | |
| 29313cb | 73 | fn walkType(ty: &Type, names: &mut HashSet<String>) { |
| 29313cb | 74 | if isTracked(&ty.name) { |
| 29313cb | 75 | names.insert(ty.name.clone()); |
| 29313cb | 76 | } |
| 29313cb | 77 | for g in &ty.generics { |
| 29313cb | 78 | walkType(g, names); |
| 29313cb | 79 | } |
| 29313cb | 80 | } |
| 29313cb | 81 | |
| 29313cb | 82 | fn walkParamType(pt: &ParamType, names: &mut HashSet<String>) { |
| 29313cb | 83 | match pt { |
| 29313cb | 84 | ParamType::Type(t) => walkType(t, names), |
| 29313cb | 85 | ParamType::Variadic(t) => walkType(t, names), |
| 29313cb | 86 | ParamType::Fn(params, ret) => { |
| 29313cb | 87 | for p in params { |
| 29313cb | 88 | walkType(p, names); |
| 29313cb | 89 | } |
| 29313cb | 90 | if let Some(r) = ret { |
| 29313cb | 91 | walkType(r, names); |
| 29313cb | 92 | } |
| 29313cb | 93 | } |
| 29313cb | 94 | } |
| 29313cb | 95 | } |
| 29313cb | 96 | |
| 29313cb | 97 | fn walkStmts(stmts: &[Stmt], names: &mut HashSet<String>) { |
| 29313cb | 98 | for s in stmts { |
| 29313cb | 99 | walkStmt(s, names); |
| 29313cb | 100 | } |
| 29313cb | 101 | } |
| 29313cb | 102 | |
| 29313cb | 103 | fn walkStmt(stmt: &Stmt, names: &mut HashSet<String>) { |
| 29313cb | 104 | match stmt { |
| 29313cb | 105 | Stmt::Assign(a) => { |
| 29313cb | 106 | for t in &a.targets { |
| 29313cb | 107 | if let AssignTarget::Field(obj, _) = t { |
| 29313cb | 108 | walkExpr(obj, names); |
| 29313cb | 109 | } |
| 29313cb | 110 | } |
| 29313cb | 111 | for v in &a.values { |
| 29313cb | 112 | walkExpr(v, names); |
| 29313cb | 113 | } |
| 29313cb | 114 | } |
| 29313cb | 115 | Stmt::Break | Stmt::Continue | Stmt::Todo => {} |
| 29313cb | 116 | Stmt::Assert(check) => walkExpr(&check.cond, names), |
| 29313cb | 117 | Stmt::For(f) => { |
| 29313cb | 118 | walkExpr(&f.iter, names); |
| 29313cb | 119 | walkStmts(&f.body.stmts, names); |
| 29313cb | 120 | } |
| 29313cb | 121 | Stmt::While(w) => { |
| 29313cb | 122 | walkExpr(&w.condition, names); |
| 29313cb | 123 | walkStmts(&w.body.stmts, names); |
| 29313cb | 124 | } |
| 29313cb | 125 | Stmt::If(i) => { |
| 29313cb | 126 | walkExpr(&i.condition, names); |
| 29313cb | 127 | walkStmts(&i.body.stmts, names); |
| 29313cb | 128 | for ei in &i.else_ifs { |
| 29313cb | 129 | walkExpr(&ei.condition, names); |
| 29313cb | 130 | walkStmts(&ei.body.stmts, names); |
| 29313cb | 131 | } |
| 29313cb | 132 | if let Some(e) = &i.else_ { |
| 29313cb | 133 | walkStmts(&e.stmts, names); |
| 29313cb | 134 | } |
| 29313cb | 135 | } |
| 29313cb | 136 | Stmt::Match(m) => { |
| 29313cb | 137 | for s in &m.subjects { |
| 29313cb | 138 | walkExpr(s, names); |
| 29313cb | 139 | } |
| 29313cb | 140 | for c in &m.cases { |
| 29313cb | 141 | for p in &c.patterns { |
| 29313cb | 142 | walkCasePattern(p, names); |
| 29313cb | 143 | } |
| 29313cb | 144 | if let Some(g) = &c.guard { |
| 29313cb | 145 | walkExpr(g, names); |
| 29313cb | 146 | } |
| 29313cb | 147 | walkStmts(&c.body.stmts, names); |
| 29313cb | 148 | } |
| 29313cb | 149 | } |
| 29313cb | 150 | Stmt::Return(e) => { |
| 29313cb | 151 | if let Some(e) = e { |
| 29313cb | 152 | walkExpr(e, names); |
| 29313cb | 153 | } |
| 29313cb | 154 | } |
| 29313cb | 155 | Stmt::Expr(e) => walkExpr(e, names), |
| 29313cb | 156 | } |
| 29313cb | 157 | } |
| 29313cb | 158 | |
| 29313cb | 159 | fn walkCasePattern(pat: &CasePattern, names: &mut HashSet<String>) { |
| 29313cb | 160 | match pat { |
| 29313cb | 161 | CasePattern::Class { fields, .. } => { |
| 29313cb | 162 | for f in fields { |
| 29313cb | 163 | walkCasePattern(f, names); |
| 29313cb | 164 | } |
| 29313cb | 165 | } |
| 29313cb | 166 | CasePattern::Int(_) => { |
| 29313cb | 167 | names.insert("Int".to_string()); |
| 29313cb | 168 | } |
| 29313cb | 169 | CasePattern::Float(_) => { |
| 29313cb | 170 | names.insert("Float".to_string()); |
| 29313cb | 171 | } |
| 29313cb | 172 | CasePattern::String(_) => { |
| 29313cb | 173 | names.insert("Str".to_string()); |
| 29313cb | 174 | } |
| 29313cb | 175 | CasePattern::Name(_) | CasePattern::Wildcard => {} |
| 29313cb | 176 | } |
| 29313cb | 177 | } |
| 29313cb | 178 | |
| 29313cb | 179 | fn walkExpr(expr: &Expr, names: &mut HashSet<String>) { |
| 29313cb | 180 | match expr { |
| 29313cb | 181 | Expr::Binary(b) => { |
| 29313cb | 182 | walkExpr(&b.left, names); |
| 29313cb | 183 | walkExpr(&b.right, names); |
| 29313cb | 184 | } |
| 29313cb | 185 | Expr::Unary(u) => walkExpr(&u.operand, names), |
| 29313cb | 186 | Expr::Bool(b) => { |
| 29313cb | 187 | walkExpr(&b.left, names); |
| 29313cb | 188 | walkExpr(&b.right, names); |
| 29313cb | 189 | } |
| 29313cb | 190 | Expr::Not(e) => walkExpr(e, names), |
| af1776b | 191 | Expr::Try(e) => walkExpr(e, names), |
| 29313cb | 192 | Expr::Compare(c) => { |
| 29313cb | 193 | walkExpr(&c.left, names); |
| 29313cb | 194 | walkExpr(&c.right, names); |
| 29313cb | 195 | } |
| 29313cb | 196 | Expr::Ternary(t) => { |
| 29313cb | 197 | walkExpr(&t.condition, names); |
| 29313cb | 198 | walkExpr(&t.then, names); |
| 29313cb | 199 | walkExpr(&t.else_, names); |
| 29313cb | 200 | } |
| ec5336f | 201 | Expr::Elvis(e) => { |
| ec5336f | 202 | walkExpr(&e.left, names); |
| ec5336f | 203 | walkExpr(&e.right, names); |
| ec5336f | 204 | } |
| 29313cb | 205 | Expr::FnCall(call) => { |
| 29313cb | 206 | if isTracked(&call.name) { |
| 29313cb | 207 | names.insert(call.name.clone()); |
| 29313cb | 208 | } |
| 29313cb | 209 | for a in &call.args { |
| 29313cb | 210 | walkArg(a, names); |
| 29313cb | 211 | } |
| 29313cb | 212 | } |
| 29313cb | 213 | Expr::ClassCall(c) => { |
| 29313cb | 214 | if isTracked(&c.type_name) { |
| 29313cb | 215 | names.insert(c.type_name.clone()); |
| 29313cb | 216 | } |
| 29313cb | 217 | for g in &c.generics { |
| 29313cb | 218 | walkType(g, names); |
| 29313cb | 219 | } |
| 29313cb | 220 | for f in &c.fields { |
| 29313cb | 221 | walkExpr(&f.value, names); |
| 29313cb | 222 | } |
| 29313cb | 223 | } |
| 29313cb | 224 | Expr::Attribute(a) => { |
| 29313cb | 225 | walkExpr(&a.object, names); |
| 29313cb | 226 | if let AttrKind::Method(call) = &a.attr { |
| 29313cb | 227 | for arg in &call.args { |
| 29313cb | 228 | walkArg(arg, names); |
| 29313cb | 229 | } |
| 29313cb | 230 | } |
| 29313cb | 231 | } |
| 29313cb | 232 | Expr::Paren(e) => walkExpr(e, names), |
| 29313cb | 233 | Expr::String(s) => { |
| 29313cb | 234 | names.insert("Str".to_string()); |
| 29313cb | 235 | for part in &s.parts { |
| 29313cb | 236 | if let StringPart::Interp(e) = part { |
| 29313cb | 237 | walkExpr(e, names); |
| 29313cb | 238 | } |
| 29313cb | 239 | } |
| 29313cb | 240 | } |
| 29313cb | 241 | Expr::Int(_) => { |
| 29313cb | 242 | names.insert("Int".to_string()); |
| 29313cb | 243 | } |
| 29313cb | 244 | Expr::Float(_) => { |
| 29313cb | 245 | names.insert("Float".to_string()); |
| 29313cb | 246 | } |
| 29313cb | 247 | Expr::Self_ | Expr::Var(_) => {} |
| 29313cb | 248 | Expr::TypeName(name) => { |
| 29313cb | 249 | if isTracked(name) { |
| 29313cb | 250 | names.insert(name.clone()); |
| 29313cb | 251 | } |
| 29313cb | 252 | } |
| 29313cb | 253 | Expr::Closure(c) => walkStmts(&c.body.stmts, names), |
| 29313cb | 254 | } |
| 29313cb | 255 | } |
| 29313cb | 256 | |
| 29313cb | 257 | fn walkArg(arg: &Arg, names: &mut HashSet<String>) { |
| 29313cb | 258 | match arg { |
| 29313cb | 259 | Arg::Positional(e) => walkExpr(e, names), |
| 29313cb | 260 | Arg::Keyword { value, .. } => walkExpr(value, names), |
| 29313cb | 261 | Arg::Pair { value, .. } => walkExpr(value, names), |
| 29313cb | 262 | } |
| 29313cb | 263 | } |