From 5ad0e9e8e766a584f5774e28a2044e5b2fcb21f6 Mon Sep 17 00:00:00 2001
From: Janis <janis@nirgendwo.xyz>
Date: Mon, 16 Sep 2024 00:15:39 +0200
Subject: [PATCH] calculate which nodes are comptime evaluable

---
 src/ast2/mod.rs | 303 ++++++++++++++++++++++++++++++++++++------------
 1 file changed, 227 insertions(+), 76 deletions(-)

diff --git a/src/ast2/mod.rs b/src/ast2/mod.rs
index 7f4533b..f63d17d 100644
--- a/src/ast2/mod.rs
+++ b/src/ast2/mod.rs
@@ -1938,6 +1938,37 @@ impl Display for Children {
 }
 
 type TypeCache = BTreeMap<Index, intern::Index>;
+// type ComptimeCache = BTreeMap<Index, bool>;
+
+#[derive(Debug, Default)]
+struct ComptimeCache {
+    inner: BTreeMap<Index, bool>,
+    // this is (a,b) where b dominates a
+    // meaning:
+    //  when a is marked as runtime, b can be updated to be runtime as well.
+    dependencies: BTreeMap<Index, Index>,
+}
+
+impl ComptimeCache {
+    fn get(&self, key: &Index) -> Option<bool> {
+        self.inner.get(key).cloned()
+    }
+    fn insert(&mut self, key: Index, value: bool) {
+        self.inner.insert(key, value);
+        if !value {
+            self.set_runtime(key);
+        }
+    }
+    fn mark_as_dominated_by(&mut self, a: Index, b: Index) {
+        self.dependencies.insert(a, b);
+    }
+    fn set_runtime(&mut self, key: Index) {
+        self.inner.insert(key, false);
+        if let Some(&dom) = self.dependencies.get(&key) {
+            self.set_runtime(dom);
+        }
+    }
+}
 
 impl Ast {
     fn get_type_of_node(
@@ -2241,83 +2272,185 @@ impl Ast {
         }
     }
 
-    fn comptime_value_of_node(
-        &self,
-        ip: &InternPool,
-        pointer_bits: u16,
-        cache: &mut TypeCache,
-        index: Index,
-    ) -> crate::comptime::ComptimeNumber {
-        let tag = self.tags[index.index()];
-        let data = self.datas[index.index()];
+    fn is_node_comptime_evaluable(&self, cache: &mut ComptimeCache, index: Index) -> bool {
+        if let Some(a) = cache.get(&index) {
+            a
+        } else {
+            let tag = self.tags[index.index()];
+            let data = self.datas[index.index()];
 
-        match tag {
-            Tag::Root => todo!(),
-            Tag::File => todo!(),
-            Tag::FunctionProto => todo!(),
-            Tag::FunctionDecl => todo!(),
-            Tag::ParameterList => todo!(),
-            Tag::Parameter => todo!(),
-            Tag::Block => todo!(),
-            Tag::BlockTrailingExpr => todo!(),
-            Tag::Constant => {
-                let (ty, value) = data.as_index_intern();
-                let ty = self.get_type_of_node(ip, cache, ty);
-                interned_type_and_value_to_comptime_number(ip, pointer_bits, ty, value)
-            }
-            Tag::ExprStmt => todo!(),
-            Tag::ReturnStmt => todo!(),
-            Tag::ReturnExprStmt => todo!(),
-            Tag::VarDecl => todo!(),
-            Tag::MutVarDecl => todo!(),
-            Tag::VarDeclAssignment => todo!(),
-            Tag::MutVarDeclAssignment => todo!(),
-            Tag::GlobalDecl => todo!(),
-            Tag::StructDecl => todo!(),
-            Tag::FieldDecl => todo!(),
-            Tag::DeclRef => todo!(),
-            Tag::DeclRefUnresolved => todo!(),
-            Tag::InternedType => todo!(),
-            Tag::TypeDeclRef => todo!(),
-            Tag::TypeDeclRefUnresolved => todo!(),
-            Tag::PointerType => todo!(),
-            Tag::ArrayType => todo!(),
-            Tag::CallExpr => todo!(),
-            Tag::FieldAccess => todo!(),
-            Tag::ArgumentList => todo!(),
-            Tag::Argument => todo!(),
-            Tag::NamedArgument => todo!(),
-            Tag::ExplicitCast => todo!(),
-            Tag::Deref => todo!(),
-            Tag::AddressOf => todo!(),
-            Tag::Not => todo!(),
-            Tag::Negate => todo!(),
-            Tag::Or => todo!(),
-            Tag::And => todo!(),
-            Tag::BitOr => todo!(),
-            Tag::BitXOr => todo!(),
-            Tag::BitAnd => todo!(),
-            Tag::Eq => todo!(),
-            Tag::NEq => todo!(),
-            Tag::Lt => todo!(),
-            Tag::Gt => todo!(),
-            Tag::Le => todo!(),
-            Tag::Ge => todo!(),
-            Tag::Shl => todo!(),
-            Tag::Shr => todo!(),
-            Tag::Add => todo!(),
-            Tag::Sub => todo!(),
-            Tag::Mul => todo!(),
-            Tag::Div => todo!(),
-            Tag::Rem => todo!(),
-            Tag::Assign => todo!(),
-            Tag::SubscriptExpr => todo!(),
-            Tag::IfExpr => todo!(),
-            Tag::IfElseExpr => todo!(),
-            Tag::Error => todo!(),
-            Tag::Undefined => todo!(),
+            let children = self.get_node_children(index);
+
+            let are_children_comptime = |this: &Self, cache: &mut ComptimeCache| {
+                children
+                    .iter()
+                    .all(|&i| this.is_node_comptime_evaluable(cache, i))
+            };
+
+            let is_comptime = match tag {
+                Tag::Parameter => false,
+                Tag::Block | Tag::BlockTrailingExpr => are_children_comptime(self, cache),
+                Tag::Constant => true,
+                Tag::ReturnStmt => true,
+                Tag::ReturnExprStmt => are_children_comptime(self, cache),
+                Tag::VarDecl | Tag::MutVarDecl => true,
+                Tag::VarDeclAssignment | Tag::MutVarDeclAssignment => {
+                    are_children_comptime(self, cache)
+                }
+                Tag::GlobalDecl => true,
+                Tag::StructDecl => true,
+                Tag::FieldDecl => true,
+                Tag::DeclRef => self.tags[data.as_index().index()] == Tag::GlobalDecl,
+                Tag::InternedType | Tag::PointerType | Tag::ArrayType | Tag::TypeDeclRef => true,
+                Tag::CallExpr => false,
+                Tag::FieldAccess => {
+                    let parent = data.as_index_intern().0;
+                    cache.mark_as_dominated_by(index, parent);
+                    self.is_node_comptime_evaluable(cache, parent)
+                }
+                Tag::ArgumentList => are_children_comptime(self, cache),
+                Tag::Argument => self.is_node_comptime_evaluable(cache, data.as_index()),
+                Tag::NamedArgument => {
+                    self.is_node_comptime_evaluable(cache, data.as_index_intern().0)
+                }
+                Tag::ExplicitCast => {
+                    self.is_node_comptime_evaluable(cache, data.as_two_indices().0)
+                }
+                Tag::Deref | Tag::AddressOf => false,
+                Tag::Not
+                | Tag::Negate
+                | Tag::Or
+                | Tag::And
+                | Tag::BitOr
+                | Tag::BitXOr
+                | Tag::BitAnd
+                | Tag::Eq
+                | Tag::NEq
+                | Tag::Lt
+                | Tag::Gt
+                | Tag::Le
+                | Tag::Ge
+                | Tag::Shl
+                | Tag::Shr
+                | Tag::Add
+                | Tag::Sub
+                | Tag::Mul
+                | Tag::Div
+                | Tag::SubscriptExpr
+                | Tag::Rem => are_children_comptime(self, cache),
+                Tag::Assign => {
+                    let (left, _) = data.as_two_indices();
+                    cache.mark_as_dominated_by(index, left);
+                    are_children_comptime(self, cache)
+                }
+                Tag::IfExpr | Tag::IfElseExpr => are_children_comptime(self, cache),
+                Tag::Root
+                | Tag::File
+                | Tag::FunctionProto
+                | Tag::FunctionDecl
+                | Tag::ParameterList
+                | Tag::ExprStmt
+                | Tag::DeclRefUnresolved
+                | Tag::TypeDeclRefUnresolved
+                | Tag::Error
+                | Tag::Undefined => false,
+            };
+
+            cache.insert(index, is_comptime);
+
+            is_comptime
         }
     }
+
+    // fn comptime_value_of_node(
+    //     &self,
+    //     ip: &InternPool,
+    //     pointer_bits: u16,
+    //     cache: &mut TypeCache,
+    //     index: Index,
+    // ) -> crate::comptime::ComptimeNumber {
+    //     let tag = self.tags[index.index()];
+    //     let data = self.datas[index.index()];
+
+    //     match tag {
+    //         Tag::Root => todo!(),
+    //         Tag::File => todo!(),
+    //         Tag::FunctionProto => todo!(),
+    //         Tag::FunctionDecl => todo!(),
+    //         Tag::ParameterList => todo!(),
+    //         Tag::Parameter => todo!(),
+    //         Tag::Block => todo!(),
+    //         Tag::BlockTrailingExpr => {
+    //             let (a, b) = data.as_extra_range();
+    //             if a != b {
+    //                 self.comptime_value_of_node(
+    //                     ip,
+    //                     pointer_bits,
+    //                     cache,
+    //                     Index::new(self.extra[b - 1]),
+    //                 )
+    //             } else {
+    //                 None
+    //             }
+    //         }
+    //         Tag::Constant => {
+    //             let (ty, value) = data.as_index_intern();
+    //             let ty = self.get_type_of_node(ip, cache, ty);
+    //             interned_type_and_value_to_comptime_number(ip, pointer_bits, ty, value)
+    //         }
+    //         Tag::ExprStmt => todo!(),
+    //         Tag::ReturnStmt => todo!(),
+    //         Tag::ReturnExprStmt => todo!(),
+    //         Tag::VarDecl => todo!(),
+    //         Tag::MutVarDecl => todo!(),
+    //         Tag::VarDeclAssignment => todo!(),
+    //         Tag::MutVarDeclAssignment => todo!(),
+    //         Tag::GlobalDecl => todo!(),
+    //         Tag::StructDecl => todo!(),
+    //         Tag::FieldDecl => todo!(),
+    //         Tag::DeclRef => todo!(),
+    //         Tag::DeclRefUnresolved => todo!(),
+    //         Tag::InternedType => todo!(),
+    //         Tag::TypeDeclRef => todo!(),
+    //         Tag::TypeDeclRefUnresolved => todo!(),
+    //         Tag::PointerType => todo!(),
+    //         Tag::ArrayType => todo!(),
+    //         Tag::CallExpr => todo!(),
+    //         Tag::FieldAccess => todo!(),
+    //         Tag::ArgumentList => todo!(),
+    //         Tag::Argument => todo!(),
+    //         Tag::NamedArgument => todo!(),
+    //         Tag::ExplicitCast => todo!(),
+    //         Tag::Deref => todo!(),
+    //         Tag::AddressOf => todo!(),
+    //         Tag::Not => todo!(),
+    //         Tag::Negate => todo!(),
+    //         Tag::Or => todo!(),
+    //         Tag::And => todo!(),
+    //         Tag::BitOr => todo!(),
+    //         Tag::BitXOr => todo!(),
+    //         Tag::BitAnd => todo!(),
+    //         Tag::Eq => todo!(),
+    //         Tag::NEq => todo!(),
+    //         Tag::Lt => todo!(),
+    //         Tag::Gt => todo!(),
+    //         Tag::Le => todo!(),
+    //         Tag::Ge => todo!(),
+    //         Tag::Shl => todo!(),
+    //         Tag::Shr => todo!(),
+    //         Tag::Add => todo!(),
+    //         Tag::Sub => todo!(),
+    //         Tag::Mul => todo!(),
+    //         Tag::Div => todo!(),
+    //         Tag::Rem => todo!(),
+    //         Tag::Assign => todo!(),
+    //         Tag::SubscriptExpr => todo!(),
+    //         Tag::IfExpr => todo!(),
+    //         Tag::IfElseExpr => todo!(),
+    //         Tag::Error => todo!(),
+    //         Tag::Undefined => todo!(),
+    //     }
+    // }
 }
 
 fn interned_type_and_value_to_comptime_number(
@@ -2464,6 +2597,7 @@ pub struct AstRenderer<'a> {
     ip: &'a InternPool,
     scopes: Vec<Index>,
     cache: TypeCache,
+    comptime_cache: ComptimeCache,
 }
 
 impl<'a> AstRenderer<'a> {
@@ -2478,6 +2612,7 @@ impl<'a> AstRenderer<'a> {
             ip,
             scopes: Vec::new(),
             cache: TypeCache::new(),
+            comptime_cache: ComptimeCache::default(),
         }
     }
 
@@ -2498,7 +2633,15 @@ impl<'a> AstRenderer<'a> {
 
         let children = Children(self.ast.get_node_children(node));
         let ty = self.ast.get_type_of_node(self.ip, &mut self.cache, node);
-        writeln_indented!(indent, w, "{node} ({ty}) = ({loc}) {tag:?} {}", children)?;
+        let is_comptime = self
+            .ast
+            .is_node_comptime_evaluable(&mut self.comptime_cache, node);
+        writeln_indented!(
+            indent * 2,
+            w,
+            "{node} {}({ty}) = ({loc}) {tag:?} {children}",
+            if is_comptime { "CONST " } else { "" }
+        )?;
 
         for child in children.0 {
             self.render_node(w, indent + 1, child)?;
@@ -2577,7 +2720,14 @@ pub mod ast_gen {
             AstRenderer::new(&self.ast, &self.intern, &self.syms)
         }
 
-        pub fn fold_and_typecheck(&mut self) {}
+        pub fn fold_and_typecheck(&mut self) {
+            enum Asdf {
+                Pre(Index),
+                Post(Index),
+            }
+            let mut nodes = self.ast.get_root_file_indices().collect::<Vec<_>>();
+            let mut cache = TypeCache::new();
+        }
 
         pub fn intern_types(&mut self) {
             let mut nodes = self
@@ -2611,6 +2761,7 @@ pub mod ast_gen {
                                 let pointee = self.ast.datas[pointee.index()].as_intern();
                                 variant!( self.intern.get_key(pointee) => intern::Key::PointerType { pointee, flags });
 
+                                // get interened value from constant node
                                 let length = {
                                     let value = self.ast.datas[length.index()].as_index_intern().1;