1use crate::ast::*;
3use crate::parser::{Token, parse, tokenize};
4use serde::{Deserialize, Serialize};
5use std::collections::{BTreeMap, BTreeSet};
6
7pub(crate) const MAIN: &str = "main";
8pub(crate) const BUILTIN: &str = "builtin";
9
10#[derive(Clone, Debug, Serialize, Deserialize)]
12pub struct Symbol {
13 pub name: String,
15 pub qualified_name: String,
17 pub kind: String,
19 pub span: Span,
21 pub signature: String,
23 pub exported: bool,
25}
26#[derive(Clone, Debug, Serialize, Deserialize)]
28pub struct MethodSymbol {
29 pub class: String,
31 pub name: String,
33 pub parameter_count: usize,
35 pub span: Span,
37}
38#[derive(Clone, Debug, Serialize, Deserialize)]
40pub struct ProgramMetadata {
41 pub methods: Vec<MethodSymbol>,
43 pub symbols: Vec<Symbol>,
45 pub tokens: Vec<Token>,
47 pub modules: Vec<String>,
49 pub references: Vec<ExpressionReference>,
51}
52#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
54#[derive(Clone, Debug, Serialize, Deserialize)]
55pub struct ExpressionReference {
56 pub span: Span,
58 pub ty: Type,
60 pub kind: String,
62 pub identity: Option<String>,
64 pub inspectable: bool,
66 pub target: Option<crate::inspection::InspectionTarget>,
68}
69#[derive(Clone, Debug, Default)]
70pub(crate) struct Module {
71 pub classes: BTreeMap<String, Class>,
72 pub enums: BTreeMap<String, Enum>,
73 pub functions: BTreeMap<String, Function>,
74 pub traits: BTreeMap<String, Trait>,
75 pub imports: BTreeSet<String>,
76 pub exports: BTreeSet<String>,
77 pub statements: Vec<Stmt>,
78}
79#[derive(Clone, Debug)]
81pub struct Program {
82 pub(crate) modules: BTreeMap<String, Module>,
83 pub(crate) methods: BTreeMap<String, BTreeMap<String, Function>>,
84 pub(crate) implementations: BTreeSet<(String, String)>,
85 pub(crate) systems: Vec<(String, System)>,
86 pub(crate) order: Vec<String>,
87 pub(crate) sources: BTreeMap<String, String>,
88 pub(crate) native_signatures: BTreeMap<String, (Vec<Type>, Type)>,
89 pub(crate) binding_types: BTreeMap<(String, usize, usize), Type>,
90 pub(crate) mutating_methods: BTreeSet<(String, String)>,
91 pub(crate) expression_types: BTreeMap<(String, usize, usize), Type>,
92 pub(crate) method_receivers: BTreeMap<(String, usize, usize), Type>,
93 pub(crate) metadata: ProgramMetadata,
94}
95impl Program {
96 pub fn metadata(&self) -> &ProgramMetadata {
98 &self.metadata
99 }
100 pub fn sources(&self) -> &BTreeMap<String, String> {
102 &self.sources
103 }
104 pub(crate) fn key(module: &str, name: &str) -> String {
105 if module == MAIN || module == BUILTIN {
106 name.to_owned()
107 } else {
108 format!("{module}.{name}")
109 }
110 }
111 pub(crate) fn resolve(
112 &self,
113 module: &str,
114 name: &str,
115 span: &Span,
116 ) -> Result<(String, String), Diagnostic> {
117 if let Some((prefix, local)) = name.rsplit_once('.') {
118 let current = &self.modules[module];
119 if prefix != module && prefix != BUILTIN && !current.imports.contains(prefix) {
120 return Err(Diagnostic::new(
121 "unknown_module",
122 format!("module `{prefix}` is not imported"),
123 span.clone(),
124 ));
125 }
126 let target = self.modules.get(prefix).ok_or_else(|| {
127 Diagnostic::new(
128 "unknown_module",
129 format!("unknown module `{prefix}`"),
130 span.clone(),
131 )
132 })?;
133 if prefix != module && prefix != BUILTIN && !target.exports.contains(local) {
134 return Err(Diagnostic::new(
135 "private_name",
136 format!("`{local}` is not exported by `{prefix}`"),
137 span.clone(),
138 ));
139 }
140 return Ok((prefix.to_owned(), local.to_owned()));
141 }
142 let m = &self.modules[module];
143 if m.classes.contains_key(name)
144 || m.enums.contains_key(name)
145 || m.functions.contains_key(name)
146 || m.traits.contains_key(name)
147 {
148 return Ok((module.to_owned(), name.to_owned()));
149 }
150 let b = &self.modules[BUILTIN];
151 if b.classes.contains_key(name)
152 || b.enums.contains_key(name)
153 || b.functions.contains_key(name)
154 || b.traits.contains_key(name)
155 {
156 return Ok((BUILTIN.to_owned(), name.to_owned()));
157 }
158 Ok((module.to_owned(), name.to_owned()))
159 }
160 pub(crate) fn class(
161 &self,
162 module: &str,
163 name: &str,
164 span: &Span,
165 ) -> Result<(String, &Class), Diagnostic> {
166 let (m, n) = self.resolve(module, name, span)?;
167 let class = self.modules[&m].classes.get(&n).ok_or_else(|| {
168 Diagnostic::new(
169 "unknown_class",
170 format!("unknown class `{name}`"),
171 span.clone(),
172 )
173 })?;
174 Ok((m, class))
175 }
176 pub(crate) fn class_by_key(&self, key: &str) -> Option<(&str, &Class)> {
177 self.modules.iter().find_map(|(m, module)| {
178 module
179 .classes
180 .values()
181 .find(|c| Self::key(m, &c.name) == key)
182 .map(|c| (m.as_str(), c))
183 })
184 }
185 pub(crate) fn enum_by_key(&self, key: &str) -> Option<(&str, &Enum)> {
186 self.modules.iter().find_map(|(module, declarations)| {
187 declarations
188 .enums
189 .values()
190 .find(|value| Self::key(module, &value.name) == key)
191 .map(|value| (module.as_str(), value))
192 })
193 }
194 pub(crate) fn variant(
195 &self,
196 module: &str,
197 name: &str,
198 span: &Span,
199 ) -> Result<Option<(Type, String, Vec<Type>)>, Diagnostic> {
200 let Some((owner, case)) = name.rsplit_once('.') else {
201 return Ok(None);
202 };
203 let (target, local) = self.resolve(module, owner, span)?;
204 let Some(declaration) = self.modules[&target].enums.get(&local) else {
205 return Ok(None);
206 };
207 let variant = declaration
208 .cases
209 .iter()
210 .find(|variant| variant.name == case)
211 .ok_or_else(|| {
212 Diagnostic::new(
213 "unknown_case",
214 format!("{owner} has no case {case}"),
215 span.clone(),
216 )
217 })?;
218 let payload = variant
219 .payload
220 .iter()
221 .map(|ty| self.canonical_type(&target, ty, span))
222 .collect::<Result<_, _>>()?;
223 Ok(Some((
224 Type::named(Self::key(&target, &local)),
225 case.into(),
226 payload,
227 )))
228 }
229 pub(crate) fn canonical_type(
230 &self,
231 module: &str,
232 ty: &Type,
233 span: &Span,
234 ) -> Result<Type, Diagnostic> {
235 match ty {
236 Type::Applied { name, arguments } => Ok(Type::Applied {
237 name: name.clone(),
238 arguments: arguments
239 .iter()
240 .map(|ty| self.canonical_type(module, ty, span))
241 .collect::<Result<_, _>>()?,
242 }),
243 Type::Function {
244 parameters,
245 returns,
246 } => Ok(Type::function(
247 parameters
248 .iter()
249 .map(|ty| self.canonical_type(module, ty, span))
250 .collect::<Result<_, _>>()?,
251 self.canonical_type(module, returns, span)?,
252 )),
253 Type::ComponentReference(inner) => Ok(Type::ComponentReference(Box::new(
254 self.canonical_type(module, inner, span)?,
255 ))),
256 Type::Tensor(inner) => {
257 let inner = self.canonical_type(module, inner, span)?;
258 if crate::data::dtype(&inner).is_none() {
259 return Err(Diagnostic::new(
260 "invalid_dtype",
261 "Tensor[T] requires a numeric dtype",
262 span.clone(),
263 ));
264 }
265 Ok(Type::Tensor(Box::new(inner)))
266 }
267 Type::List(inner) => Ok(Type::List(Box::new(
268 self.canonical_type(module, inner, span)?,
269 ))),
270 Type::Named(name)
271 if crate::data::is_primitive(name) || matches!(name.as_str(), "Any" | "List") =>
272 {
273 Ok(Type::named(name))
274 }
275 Type::Named(name) => {
276 if name.contains('.')
279 && (self.class_by_key(name).is_some()
280 || self.enum_by_key(name).is_some()
281 || self.modules.iter().any(|(m, declarations)| {
282 declarations
283 .traits
284 .contains_key(name.strip_prefix(&format!("{m}.")).unwrap_or(""))
285 }))
286 {
287 return Ok(ty.clone());
288 }
289 let (m, n) = self.resolve(module, name, span)?;
290 Ok(Type::named(Self::key(&m, &n)))
291 }
292 }
293 }
294 pub(crate) fn typed_builtin_signature(
295 &self,
296 name: &str,
297 ty: &Type,
298 span: &Span,
299 ) -> Result<Type, Diagnostic> {
300 if name == "tensor" {
301 if crate::data::dtype(ty).is_none() {
302 return Err(Diagnostic::new(
303 "invalid_dtype",
304 "tensor[T] requires a numeric dtype",
305 span.clone(),
306 ));
307 }
308 return Ok(Type::function(
309 vec![Type::List(Box::new(ty.clone())), crate::data::shape_type()],
310 crate::data::result_type(crate::data::tensor_type(ty.clone())),
311 ));
312 }
313 let Type::Named(key) = ty else {
314 return Err(Diagnostic::new(
315 "unknown_query_type",
316 "ECS type argument must be a class or trait",
317 span.clone(),
318 ));
319 };
320 let concrete = self.class_by_key(key).is_some();
321 let contract = self.modules.iter().any(|(module, declarations)| {
322 declarations
323 .traits
324 .keys()
325 .any(|local| Self::key(module, local) == *key)
326 });
327 if !matches!(name, "get" | "has" | "query" | "bind") {
328 return Err(Diagnostic::new(
329 "invalid_type_argument",
330 "only get, has, query, and bind accept a type argument",
331 span.clone(),
332 ));
333 }
334 if !concrete && (name != "query" || !contract) {
335 return Err(Diagnostic::new(
336 "unknown_query_type",
337 format!(
338 "{name}<T> requires {}",
339 if name == "query" {
340 "a class or trait"
341 } else {
342 "a concrete class"
343 }
344 ),
345 span.clone(),
346 ));
347 }
348 let parameters = if name == "query" {
349 vec![]
350 } else {
351 vec![Type::named("Entity")]
352 };
353 let returns = match name {
354 "get" => ty.clone(),
355 "has" => Type::named("Bool"),
356 "bind" => Type::ComponentReference(Box::new(ty.clone())),
357 _ => Type::List(Box::new(Type::named("Entity"))),
358 };
359 Ok(Type::function(parameters, returns))
360 }
361 fn same_signature(&self, actual: &Function, required: &Function) -> bool {
362 actual.parameters.len() == required.parameters.len()
363 && actual
364 .parameters
365 .iter()
366 .zip(&required.parameters)
367 .all(|(a, b)| {
368 self.canonical_type(&actual.span.module, &a.ty, &a.span)
369 .ok()
370 == self
371 .canonical_type(&required.span.module, &b.ty, &b.span)
372 .ok()
373 })
374 && self
375 .canonical_type(&actual.span.module, &actual.returns, &actual.span)
376 .ok()
377 == self
378 .canonical_type(&required.span.module, &required.returns, &required.span)
379 .ok()
380 }
381 pub(crate) fn query_classes(
382 &self,
383 module: &str,
384 name: &str,
385 span: &Span,
386 ) -> Result<Vec<String>, Diagnostic> {
387 let (m, n) = self.resolve(module, name, span)?;
388 let key = Self::key(&m, &n);
389 if self.modules[&m].classes.contains_key(&n) {
390 return Ok(vec![key]);
391 }
392 if self.modules[&m].traits.contains_key(&n) {
393 return Ok(self
394 .implementations
395 .iter()
396 .filter_map(|(class, t)| (t == &key).then_some(class.clone()))
397 .collect());
398 }
399 Err(Diagnostic::new(
400 "unknown_query_type",
401 format!("`{name}` is not a class or trait"),
402 span.clone(),
403 ))
404 }
405 pub(crate) fn has_type(&self, module: &str, ty: &Type, span: &Span) -> Result<(), Diagnostic> {
406 match ty {
407 Type::Applied { name, arguments } => {
408 let arity = match name.as_str() {
409 "Res" => 2,
410 "Opt" => 1,
411 _ => {
412 return Err(Diagnostic::new(
413 "unknown_type",
414 format!("unknown generic type {name}"),
415 span.clone(),
416 ));
417 }
418 };
419 if arguments.len() != arity {
420 return Err(Diagnostic::new(
421 "type_arity",
422 format!("{name} requires {arity} type arguments"),
423 span.clone(),
424 ));
425 }
426 for argument in arguments {
427 self.has_type(module, argument, span)?;
428 }
429 Ok(())
430 }
431 Type::Tensor(inner) => {
432 if crate::data::dtype(inner).is_none() {
433 return Err(Diagnostic::new(
434 "invalid_dtype",
435 "Tensor[T] requires a numeric dtype",
436 span.clone(),
437 ));
438 }
439 Ok(())
440 }
441 Type::List(inner) => self.has_type(module, inner, span),
442 Type::ComponentReference(inner) => {
443 let Type::Named(name) = inner.as_ref() else {
444 return Err(Diagnostic::new(
445 "unknown_type",
446 "ComponentReference[T] requires a concrete class",
447 span.clone(),
448 ));
449 };
450 self.class(module, name, span).map(|_| ())
451 }
452 Type::Function {
453 parameters,
454 returns,
455 } => {
456 for ty in parameters.iter().chain(std::iter::once(returns.as_ref())) {
457 self.has_type(module, ty, span)?;
458 }
459 Ok(())
460 }
461 Type::Named(name)
462 if crate::data::is_primitive(name) || matches!(name.as_str(), "Any" | "List") =>
463 {
464 Ok(())
465 }
466 Type::Named(name) => {
467 let (m, n) = self.resolve(module, name, span)?;
468 if self.modules[&m].classes.contains_key(&n)
469 || self.modules[&m].enums.contains_key(&n)
470 || self.modules[&m].traits.contains_key(&n)
471 {
472 Ok(())
473 } else {
474 Err(Diagnostic::new(
475 "unknown_type",
476 format!("unknown type `{name}`"),
477 span.clone(),
478 ))
479 }
480 }
481 }
482 }
483}
484pub(crate) fn type_text(ty: &Type) -> String {
485 match ty {
486 Type::Applied { name, arguments } => format!(
487 "{name}[{}]",
488 arguments
489 .iter()
490 .map(type_text)
491 .collect::<Vec<_>>()
492 .join(", ")
493 ),
494 Type::Named(n) => n.clone(),
495 Type::Tensor(t) => format!("Tensor[{}]", type_text(t)),
496 Type::List(t) => format!("List[{}]", type_text(t)),
497 Type::ComponentReference(t) => format!("ComponentReference[{}]", type_text(t)),
498 Type::Function {
499 parameters,
500 returns,
501 } => format!(
502 "func({}) -> {}",
503 parameters
504 .iter()
505 .map(type_text)
506 .collect::<Vec<_>>()
507 .join(", "),
508 type_text(returns)
509 ),
510 }
511}
512pub(crate) fn function_text(f: &Function) -> String {
513 format!(
514 "func {}({}) -> {}",
515 f.name,
516 f.parameters
517 .iter()
518 .map(|p| format!("{}: {}", p.name, type_text(&p.ty)))
519 .collect::<Vec<_>>()
520 .join(", "),
521 type_text(&f.returns)
522 )
523}
524fn symbol(
525 module: &str,
526 name: &str,
527 kind: &str,
528 span: &Span,
529 signature: String,
530 exports: &BTreeSet<String>,
531) -> Symbol {
532 Symbol {
533 name: name.into(),
534 qualified_name: format!("{module}.{name}"),
535 kind: kind.into(),
536 span: span.clone(),
537 signature,
538 exported: module == MAIN || module == BUILTIN || exports.contains(name),
539 }
540}
541
542fn pure_default(expr: &Expr) -> bool {
543 match &expr.kind {
544 ExprKind::Assign { .. } => false,
545 ExprKind::TensorLiteral { values, .. } => values.iter().all(pure_default),
546 ExprKind::Match { value, arms } => {
547 pure_default(value) && arms.iter().all(|arm| pure_default(&arm.value))
548 }
549 ExprKind::Propagate(value) => pure_default(value),
550 ExprKind::TypedMethod { object, .. } => pure_default(object),
551 ExprKind::TypedLiteral { value, .. } => pure_default(value),
552 ExprKind::Number(_) | ExprKind::Bool(_) | ExprKind::Text(_) | ExprKind::Unit => true,
553 ExprKind::Name(_) => true,
554 ExprKind::List(values) => values.iter().all(pure_default),
555 ExprKind::Record { fields, .. } => fields.values().all(pure_default),
556 ExprKind::Unary { value, .. } => pure_default(value),
557 ExprKind::Binary { left, right, .. } => pure_default(left) && pure_default(right),
558 ExprKind::Field { object, .. } => pure_default(object),
559 ExprKind::Index { object, indices } => {
560 pure_default(object)
561 && indices.iter().all(|index| match index {
562 IndexExpr::Index(e) => pure_default(e),
563 IndexExpr::Slice { start, stop, step } => [start, stop, step]
564 .into_iter()
565 .flatten()
566 .all(|expr| pure_default(expr)),
567 IndexExpr::NewAxis | IndexExpr::Ellipsis => true,
568 })
569 }
570 ExprKind::TypedFunction { .. } => true,
571 ExprKind::TypedCall {
572 name, arguments, ..
573 } => {
574 matches!(
575 name.as_str(),
576 "tensor" | "convert" | "builtin.tensor" | "builtin.convert"
577 ) && arguments.iter().all(pure_default)
578 }
579 ExprKind::Call { callee, arguments } => {
580 let name = match &callee.kind {
581 ExprKind::Name(n) => Some(n.as_str()),
582 ExprKind::Field { object, field } if matches!(&object.kind,ExprKind::Name(n) if n=="builtin") => {
583 Some(field.as_str())
584 }
585 _ => None,
586 };
587 name.is_some_and(|n| {
588 matches!(
589 n,
590 "sin"
591 | "cos"
592 | "sqrt"
593 | "abs"
594 | "floor"
595 | "ceil"
596 | "min"
597 | "max"
598 | "clamp"
599 | "pow"
600 | "len"
601 | "append"
602 | "range"
603 )
604 }) && arguments.iter().all(pure_default)
605 }
606 }
607}
608
609pub fn compile(
617 source: &str,
618 supplied: &BTreeMap<String, String>,
619) -> Result<Program, Vec<Diagnostic>> {
620 compile_with_registry(source, supplied, &crate::NativeRegistry::default())
621}
622
623pub fn compile_with_registry(
628 source: &str,
629 supplied: &BTreeMap<String, String>,
630 registry: &crate::NativeRegistry,
631) -> Result<Program, Vec<Diagnostic>> {
632 let mut sources = BTreeMap::from([
633 (MAIN.to_owned(), source.to_owned()),
634 (BUILTIN.to_owned(), crate::catalog::PRELUDE.to_owned()),
635 ]);
636 let mut parsed = BTreeMap::<String, Vec<Item>>::new();
637 let mut order = Vec::new();
638 let mut visiting = BTreeSet::new();
639 let mut errors = Vec::new();
640 fn load(
641 name: &str,
642 supplied: &BTreeMap<String, String>,
643 sources: &mut BTreeMap<String, String>,
644 parsed: &mut BTreeMap<String, Vec<Item>>,
645 order: &mut Vec<String>,
646 visiting: &mut BTreeSet<String>,
647 errors: &mut Vec<Diagnostic>,
648 ) {
649 if parsed.contains_key(name) {
650 return;
651 }
652 if order.len() + visiting.len() >= 64 {
653 errors.push(Diagnostic::new(
654 "module_limit",
655 "program exceeds 64 modules",
656 Span::new(name, 0, 0),
657 ));
658 return;
659 }
660 if !visiting.insert(name.to_owned()) {
661 errors.push(Diagnostic::new(
662 "cyclic_import",
663 format!("cyclic import of `{name}`"),
664 Span::new(name, 0, 0),
665 ));
666 return;
667 }
668 let Some(source) = sources
669 .get(name)
670 .cloned()
671 .or_else(|| supplied.get(name).cloned())
672 else {
673 errors.push(Diagnostic::new(
674 "missing_module",
675 format!("module `{name}` was not supplied"),
676 Span::new(name, 0, 0),
677 ));
678 visiting.remove(name);
679 return;
680 };
681 sources.insert(name.to_owned(), source.clone());
682 match parse(&source, name) {
683 Ok(items) => {
684 for item in &items {
685 if let Item::Import { module, span } = item {
686 if module == MAIN {
687 errors.push(Diagnostic::new(
688 "reserved_module",
689 "cannot import reserved entry module `main`",
690 span.clone(),
691 ));
692 continue;
693 }
694 load(module, supplied, sources, parsed, order, visiting, errors);
695 }
696 }
697 parsed.insert(name.to_owned(), items);
698 order.push(name.to_owned());
699 }
700 Err(mut diagnostics) => errors.append(&mut diagnostics),
701 }
702 visiting.remove(name);
703 }
704 load(
705 BUILTIN,
706 supplied,
707 &mut sources,
708 &mut parsed,
709 &mut order,
710 &mut visiting,
711 &mut errors,
712 );
713 load(
714 MAIN,
715 supplied,
716 &mut sources,
717 &mut parsed,
718 &mut order,
719 &mut visiting,
720 &mut errors,
721 );
722 if !errors.is_empty() {
723 return Err(errors);
724 }
725 let mut program = Program {
726 modules: BTreeMap::new(),
727 methods: BTreeMap::new(),
728 implementations: BTreeSet::new(),
729 systems: Vec::new(),
730 order,
731 sources,
732 native_signatures: registry.signatures(),
733 binding_types: BTreeMap::new(),
734 mutating_methods: BTreeSet::new(),
735 expression_types: BTreeMap::new(),
736 method_receivers: BTreeMap::new(),
737 metadata: ProgramMetadata {
738 methods: Vec::new(),
739 symbols: Vec::new(),
740 tokens: Vec::new(),
741 modules: Vec::new(),
742 references: Vec::new(),
743 },
744 };
745 let mut implementations = Vec::new();
746 for module_name in &program.order {
747 let mut module = Module::default();
748 if module_name == BUILTIN {
749 module.enums.insert(
750 "DataError".into(),
751 Enum {
752 name: "DataError".into(),
753 cases: konjure_sdk::data::DataError::VARIANTS
754 .iter()
755 .map(|case| EnumCase {
756 name: case.name.into(),
757 payload: vec![],
758 span: Span::new(BUILTIN, 0, 0),
759 })
760 .collect(),
761 span: Span::new(BUILTIN, 0, 0),
762 },
763 );
764 }
765 let mut names = BTreeSet::new();
766 for item in &parsed[module_name] {
767 let named = match item {
768 Item::Class(c) => Some((&c.name, &c.span)),
769 Item::Enum(e) => Some((&e.name, &e.span)),
770 Item::Function(f) => Some((&f.name, &f.span)),
771 Item::Trait(t) => Some((&t.name, &t.span)),
772 Item::System(s) => Some((&s.name, &s.span)),
773 _ => None,
774 };
775 if let Some((name, span)) = named
776 && !names.insert(name.clone())
777 {
778 errors.push(Diagnostic::new(
779 "duplicate_name",
780 format!("duplicate declaration `{name}`"),
781 span.clone(),
782 ));
783 }
784 match item {
785 Item::Enum(e) => {
786 let mut cases = BTreeSet::new();
787 for case in &e.cases {
788 if !cases.insert(&case.name) {
789 errors.push(Diagnostic::new(
790 "duplicate_case",
791 format!("duplicate enum case {}", case.name),
792 case.span.clone(),
793 ));
794 }
795 }
796 module.enums.insert(e.name.clone(), e.clone());
797 }
798 Item::Class(c) => {
799 let mut fields = BTreeSet::new();
800 for f in &c.fields {
801 if !fields.insert(&f.name) {
802 errors.push(Diagnostic::new(
803 "duplicate_field",
804 format!("duplicate field `{}`", f.name),
805 f.span.clone(),
806 ));
807 }
808 }
809 implementations.push((
810 module_name.clone(),
811 Implementation {
812 trait_name: None,
813 class: c.name.clone(),
814 methods: c.methods.clone(),
815 span: c.span.clone(),
816 },
817 ));
818 module.classes.insert(c.name.clone(), c.clone());
819 }
820 Item::Function(f) => {
821 module.functions.insert(f.name.clone(), f.clone());
822 }
823 Item::Trait(t) => {
824 module.traits.insert(t.name.clone(), t.clone());
825 }
826 Item::System(s) => program.systems.push((module_name.clone(), s.clone())),
827 Item::Implementation(i) => implementations.push((module_name.clone(), i.clone())),
828 Item::Import { module: m, .. } => {
829 module.imports.insert(m.clone());
830 }
831 Item::Export { name, .. } => {
832 module.exports.insert(name.clone());
833 }
834 Item::Statement(s) => module.statements.push(s.clone()),
835 }
836 }
837 for item in &parsed[module_name] {
838 if let Item::Export { name, span } = item
839 && !names.contains(name)
840 && !module
841 .statements
842 .iter()
843 .any(|s| matches!(&s.kind,StmtKind::Let{name:n,..} if n==name))
844 {
845 errors.push(Diagnostic::new(
846 "unknown_export",
847 format!("cannot export undefined name `{name}`"),
848 span.clone(),
849 ));
850 }
851 }
852 program.modules.insert(module_name.clone(), module);
853 }
854 for (m, module) in &program.modules {
855 if m != BUILTIN {
856 for item in &parsed[m] {
857 let named = match item {
858 Item::Class(c) => Some((&c.name, &c.span)),
859 Item::Enum(e) => Some((&e.name, &e.span)),
860 Item::Function(f) => Some((&f.name, &f.span)),
861 Item::Trait(t) => Some((&t.name, &t.span)),
862 _ => None,
863 };
864 if let Some((name, span)) = named {
865 let builtin = &program.modules[BUILTIN];
866 if builtin.classes.contains_key(name)
867 || builtin.enums.contains_key(name)
868 || builtin.traits.contains_key(name)
869 || crate::native::is_builtin(name)
870 || crate::data::is_primitive(name)
871 || matches!(
872 name.as_str(),
873 "Any"
874 | "List"
875 | "Tensor"
876 | "ComponentReference"
877 | "Res"
878 | "Opt"
879 | "Never"
880 | "Ok"
881 | "Err"
882 | "Some"
883 | "None"
884 )
885 {
886 errors.push(Diagnostic::new(
887 "reserved_name",
888 format!("`{name}` is provided by the builtin namespace"),
889 span.clone(),
890 ));
891 }
892 }
893 }
894 }
895 let validate_function = |f: &Function, errors: &mut Vec<Diagnostic>| {
896 let mut parameters = BTreeSet::new();
897 for p in &f.parameters {
898 if !parameters.insert(&p.name) {
899 errors.push(Diagnostic::new(
900 "duplicate_parameter",
901 format!("duplicate parameter `{}`", p.name),
902 p.span.clone(),
903 ));
904 }
905 if let Err(e) = program.has_type(m, &p.ty, &p.span) {
906 errors.push(e);
907 }
908 }
909 if let Err(e) = program.has_type(m, &f.returns, &f.span) {
910 errors.push(e);
911 }
912 };
913 for f in module.functions.values() {
914 validate_function(f, &mut errors);
915 }
916 for declaration in module.enums.values() {
917 for case in &declaration.cases {
918 for ty in &case.payload {
919 if let Err(error) = program.has_type(m, ty, &case.span) {
920 errors.push(error);
921 }
922 }
923 }
924 }
925 for t in module.traits.values() {
926 let mut methods = BTreeSet::new();
927 for f in &t.methods {
928 validate_function(f, &mut errors);
929 if !methods.insert(&f.name) {
930 errors.push(Diagnostic::new(
931 "duplicate_method",
932 format!("duplicate trait method `{}`", f.name),
933 f.span.clone(),
934 ));
935 }
936 }
937 }
938 for c in module.classes.values() {
939 for field in &c.fields {
940 if let Some(default) = &field.default
941 && !pure_default(default)
942 {
943 errors.push(Diagnostic::new("impure_default","field defaults allow only constant values, constructors, and pure builtin expressions",default.span.clone()));
944 }
945 if let Err(e) = program.has_type(m, &field.ty, &field.span) {
946 errors.push(e);
947 }
948 }
949 }
950 }
951 for (module, i) in implementations {
952 let (class_module, class) = match program.class(&module, &i.class, &i.span) {
953 Ok(c) => c,
954 Err(e) => {
955 errors.push(e);
956 continue;
957 }
958 };
959 let class_key = Program::key(&class_module, &class.name);
960 if let Some(trait_name) = &i.trait_name {
961 match program.resolve(&module, trait_name, &i.span) {
962 Ok((tm, tn)) => {
963 if let Some(t) = program.modules[&tm].traits.get(&tn) {
964 for required in &t.methods {
965 match i.methods.iter().find(|f| f.name == required.name) {
966 Some(actual) if program.same_signature(actual, required) => {}
967 _ => errors.push(Diagnostic::new(
968 "trait_contract",
969 format!(
970 "implementation of `{trait_name}` needs `{}`",
971 function_text(required)
972 ),
973 i.span.clone(),
974 )),
975 }
976 }
977 for method in &i.methods {
978 if !t.methods.iter().any(|r| r.name == method.name) {
979 errors.push(Diagnostic::new(
980 "trait_contract",
981 format!(
982 "`{}` is not a member of trait `{trait_name}`",
983 method.name
984 ),
985 method.span.clone(),
986 ));
987 }
988 }
989 program
990 .implementations
991 .insert((class_key.clone(), Program::key(&tm, &tn)));
992 } else {
993 errors.push(Diagnostic::new(
994 "unknown_trait",
995 format!("unknown trait `{trait_name}`"),
996 i.span.clone(),
997 ));
998 }
999 }
1000 Err(e) => errors.push(e),
1001 }
1002 }
1003 for method in &i.methods {
1004 let mut parameters = std::collections::BTreeSet::new();
1005 for parameter in &method.parameters {
1006 if parameter.name == "self" || !parameters.insert(¶meter.name) {
1007 errors.push(Diagnostic::new(
1008 "duplicate_parameter",
1009 "method parameters must be unique and cannot redeclare implicit self",
1010 parameter.span.clone(),
1011 ));
1012 }
1013 }
1014 if let Err(e) = program.has_type(&module, &method.returns, &method.span) {
1015 errors.push(e);
1016 }
1017 for param in &method.parameters {
1018 if let Err(e) = program.has_type(&module, ¶m.ty, ¶m.span) {
1019 errors.push(e);
1020 }
1021 }
1022 }
1023 let methods = program.methods.entry(class_key).or_default();
1024 for method in i.methods {
1025 if methods
1026 .insert(method.name.clone(), method.clone())
1027 .is_some()
1028 {
1029 errors.push(Diagnostic::new(
1030 "duplicate_method",
1031 format!("duplicate method `{}`", method.name),
1032 method.span,
1033 ));
1034 }
1035 }
1036 }
1037 for (m, system) in &program.systems {
1038 if system.bindings.is_empty() {
1039 errors.push(Diagnostic::new(
1040 "system_query",
1041 "system needs at least one component binding",
1042 system.span.clone(),
1043 ));
1044 }
1045 let mut bindings = BTreeSet::new();
1046 let mut classes = BTreeSet::new();
1047 for binding in &system.bindings {
1048 if !bindings.insert(binding.name.clone())
1049 || matches!(binding.name.as_str(), "entity" | "time" | "tick" | "dt")
1050 {
1051 errors.push(Diagnostic::new(
1052 "system_binding",
1053 "component bindings must be unique and cannot use entity, time, tick, or dt",
1054 binding.span.clone(),
1055 ));
1056 }
1057 let Type::Named(name) = &binding.ty else {
1058 errors.push(Diagnostic::new(
1059 "system_query",
1060 "component selector must name a class or trait",
1061 binding.span.clone(),
1062 ));
1063 continue;
1064 };
1065 if let Err(e) = program.query_classes(m, name, &binding.span) {
1066 errors.push(e);
1067 }
1068 if system.bindings.len() > 1 {
1069 match program.class(m, name, &binding.span) {
1070 Ok((module, class)) => {
1071 if !classes.insert(Program::key(&module, &class.name)) {
1072 errors.push(Diagnostic::new(
1073 "system_alias",
1074 "a join cannot bind the same component class twice",
1075 binding.span.clone(),
1076 ));
1077 }
1078 }
1079 Err(e) => errors.push(e),
1080 }
1081 }
1082 }
1083 let mut callbacks = BTreeSet::new();
1084 for callback in &system.callbacks {
1085 if !callbacks.insert(callback.name.clone()) {
1086 errors.push(Diagnostic::new(
1087 "duplicate_callback",
1088 format!("duplicate system callback `{}`", callback.name),
1089 callback.span.clone(),
1090 ));
1091 }
1092 let valid = (callback.returns == Type::named("Unit")
1093 || callback.returns == crate::data::result_type(Type::named("Unit")))
1094 && match callback.name.as_str() {
1095 "init" | "done" => callback.parameters.is_empty(),
1096 "frame" => {
1097 callback.parameters.len() == 1
1098 && callback.parameters[0].ty == Type::named("f64")
1099 }
1100 _ => false,
1101 };
1102 if !valid {
1103 errors.push(Diagnostic::new("lifecycle_signature", "system callbacks are init() -> Unit, frame(dt: Number) -> Unit, and done() -> Unit", callback.span.clone()));
1104 }
1105 for parameter in &callback.parameters {
1106 if bindings.contains(¶meter.name)
1107 || matches!(parameter.name.as_str(), "self" | "entity" | "time" | "tick")
1108 {
1109 errors.push(Diagnostic::new(
1110 "system_binding",
1111 "callback parameter conflicts with a system binding",
1112 parameter.span.clone(),
1113 ));
1114 }
1115 }
1116 }
1117 if !callbacks.contains("frame") {
1118 errors.push(Diagnostic::new(
1119 "missing_frame",
1120 "system requires func frame(dt: Number) -> Unit",
1121 system.span.clone(),
1122 ));
1123 }
1124 }
1125 if !errors.is_empty() {
1126 return Err(errors);
1127 }
1128 for (m, module) in &program.modules {
1129 for c in module.classes.values() {
1130 program.metadata.symbols.push(symbol(
1131 m,
1132 &c.name,
1133 "type",
1134 &c.span,
1135 format!("type {}", c.name),
1136 &module.exports,
1137 ));
1138 for field in &c.fields {
1139 program.metadata.symbols.push(symbol(
1140 m,
1141 &format!("{}.{}", c.name, field.name),
1142 "field",
1143 &field.span,
1144 format!("{}: {}", field.name, type_text(&field.ty)),
1145 &module.exports,
1146 ));
1147 }
1148 }
1149 for declaration in module.enums.values() {
1150 program.metadata.symbols.push(symbol(
1151 m,
1152 &declaration.name,
1153 "enum",
1154 &declaration.span,
1155 format!("enum {}", declaration.name),
1156 &module.exports,
1157 ));
1158 for case in &declaration.cases {
1159 program.metadata.symbols.push(symbol(
1160 m,
1161 &format!("{}.{}", declaration.name, case.name),
1162 "case",
1163 &case.span,
1164 format!(
1165 "{}({})",
1166 case.name,
1167 case.payload
1168 .iter()
1169 .map(type_text)
1170 .collect::<Vec<_>>()
1171 .join(", ")
1172 ),
1173 &module.exports,
1174 ));
1175 }
1176 }
1177 for f in module.functions.values() {
1178 program.metadata.symbols.push(symbol(
1179 m,
1180 &f.name,
1181 "function",
1182 &f.span,
1183 function_text(f),
1184 &module.exports,
1185 ));
1186 }
1187 for t in module.traits.values() {
1188 program.metadata.symbols.push(symbol(
1189 m,
1190 &t.name,
1191 "trait",
1192 &t.span,
1193 format!("trait {}", t.name),
1194 &module.exports,
1195 ));
1196 }
1197 }
1198 for (m, s) in &program.systems {
1199 program.metadata.symbols.push(symbol(
1200 m,
1201 &s.name,
1202 "system",
1203 &s.span,
1204 format!(
1205 "system {} of ({})",
1206 s.name,
1207 s.bindings
1208 .iter()
1209 .map(|b| format!("{}: {}", b.name, type_text(&b.ty)))
1210 .collect::<Vec<_>>()
1211 .join(", ")
1212 ),
1213 &program.modules[m].exports,
1214 ));
1215 }
1216 for (m, source) in &program.sources {
1217 if let Ok(mut tokens) = tokenize(source, m) {
1218 program.metadata.tokens.append(&mut tokens);
1219 }
1220 }
1221 for (class, methods) in &program.methods {
1222 for method in methods.values() {
1223 program.metadata.methods.push(MethodSymbol {
1224 class: class.clone(),
1225 name: method.name.clone(),
1226 parameter_count: method.parameters.len(),
1227 span: method.span.clone(),
1228 });
1229 }
1230 }
1231 program.metadata.modules = program.order.clone();
1232 crate::typecheck::check(&mut program, registry)?;
1233 Ok(program)
1234}