diff --git a/fir/src/checks.rs b/fir/src/checks.rs index 61fc31a8..17378afd 100644 --- a/fir/src/checks.rs +++ b/fir/src/checks.rs @@ -98,7 +98,7 @@ impl<T: Debug> Fir<T> { } => { check!(to => Kind::Function { .. }, node); check!(@generics => Kind::TypeReference { .. }, node); - check!(@args => Kind::TypedValue { .. } | Kind::Constant(_), node); + check!(@args => Kind::TypedValue { .. } | Kind::Constant(_) | Kind::Call { .. }, node); } Kind::Type { generics, diff --git a/fire/src/instance.rs b/fire/src/instance.rs new file mode 100644 index 00000000..2a58afb1 --- /dev/null +++ b/fire/src/instance.rs @@ -0,0 +1,60 @@ +// FIXME: This is invalid +// what's a type? at runtime? +// just the hash of the type? -> that's good enough +// what's the hash of a type? +// for a string -> the hash of this string +// for a char/int/bool -> the actual value (an i64) +// for a float -> eeeeeeeh? typecheck error? +// introduce a safe-float type in the stdlib? +// for other types -> needs to be a unique hash -> based on source location and FirId? +type Type = &'static str; + +// an instance needs to be unique +// needs to be hashable +// we need the type of the value - it needs to be known at all times +// FIXME: We can probably improve this type by specializing it more - turning it into a sum type differentiating between +// FIXME: We need to be very careful about what a "Clone" means here +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Instance { + ty: Type, + data: Vec<u8>, +} + +impl Instance { + pub fn empty() -> Instance { + Instance { + ty: "empty type", + data: Vec::new(), + } + } + + pub fn data(&self) -> &[u8] { + &self.data + } +} + +impl From<i64> for Instance { + fn from(value: i64) -> Instance { + Instance { + ty: "int", + // origin: node.origin, + data: value.to_le_bytes().to_vec(), + } + } +} + +impl From<&str> for Instance { + fn from(value: &str) -> Instance { + Instance { + ty: "string", + // orgin: node.origin, + data: value.as_bytes().to_owned(), + } + } +} + +impl From<&String> for Instance { + fn from(value: &String) -> Instance { + Instance::from(value.as_str()) + } +} diff --git a/fire/src/lib.rs b/fire/src/lib.rs index e11c5f82..330fd754 100644 --- a/fire/src/lib.rs +++ b/fire/src/lib.rs @@ -1,11 +1,10 @@ +pub mod instance; + use std::collections::HashMap; +use instance::Instance; + // FIXME: Missing doc -// FIXME: Execution is very tree-like, isn't it -// FIXME: How do we get the last value of a Statements? -// FIXME: How do Returns work in this system? -// FIXME: How do we use the value returned by a function call for example? -// where x = id(15); use fir::{Fir, Kind, Node, OriginIdx, RefIdx}; use flatten::FlattenData; @@ -25,28 +24,6 @@ impl Interpret for Fir<FlattenData<'_>> { } } -// FIXME: This is invalid -// what's a type? at runtime? -// just the hash of the type? -> that's good enough -// what's the hash of a type? -// for a string -> the hash of this string -// for a char/int/bool -> the actual value (an i64) -// for a float -> eeeeeeeh? typecheck error? -// introduce a safe-float type in the stdlib? -// for other types -> needs to be a unique hash -> based on source location and FirId? -type Type = &'static str; - -// an instance needs to be unique -// needs to be hashable -// we need the type of the value - it needs to be known at all times -// FIXME: We can probably improve this type by specializing it more - turning it into a sum type differentiating between -// FIXME: We need to be very careful about what a "Clone" means here -#[derive(PartialEq, Eq, Debug, Clone)] -pub struct Instance { - pub(crate) ty: Type, - pub(crate) data: Vec<u8>, -} - // FIXME: How do we deal with the fact that we're acting on a Flat representation? // FIXME: Is the [`Fir`] stable? Can we just access the last node as the entry point to the [`Fir`]? // then it makes everything super easy actually @@ -100,7 +77,7 @@ impl Fire { let value = self.values.get(&arg.unwrap()).unwrap(); // FIXME: Ugly as SIN println!("{}", unsafe { - String::from_utf8_unchecked(value.data.clone()) + String::from_utf8_unchecked(value.data().into()) }); }) } @@ -110,11 +87,10 @@ impl Fire { // None } - // FIXME: Sholud this return a Result<Option<Instance>, Error>? fn fire_block( &mut self, fir: &Fir<FlattenData<'_>>, - _node: &Node<FlattenData<'_>>, + node: &Node<FlattenData<'_>>, stmts: &[RefIdx], ) { // How do we deal with returns in this system? @@ -123,33 +99,20 @@ impl Fire { self.fire_node(fir, node); }); - // FIXME: This is invalid, isn't it? - // FIXME: or should we just return () here? - // FIXME: If the last value is a return, we need to have this node's ID refer to the value as well + if let Some(last_stmt) = stmts.last() { + if let Kind::Return(_) = &fir.nodes[&last_stmt.unwrap()].kind { + self.transfer(last_stmt, node.origin) + } + } } - // FIXME: This can return an Instance, right? - // FIXME: Does this need "self"? - // FIXME: Does this need "fir"? - fn fire_constant( - &mut self, - _fir: &Fir<FlattenData<'_>>, - node: &Node<FlattenData<'_>>, - _c: &RefIdx, - ) { + // FIXME: Does this need "_c"? + fn fire_constant(&mut self, node: &Node<FlattenData<'_>>, _c: &RefIdx) { let ast = node.data.ast.node(); let instance = match &ast.node { - ast::Node::Constant(ast::Value::Integer(value)) => Instance { - ty: "int", - // origin: node.origin, - data: value.to_le_bytes().to_vec(), - }, - ast::Node::Constant(ast::Value::Str(s)) => Instance { - ty: "string", - // orgin: node.origin, - data: s.clone().into_bytes(), - }, + ast::Node::Constant(ast::Value::Integer(value)) => Instance::from(*value), + ast::Node::Constant(ast::Value::Str(s)) => Instance::from(s), _ => unreachable!(), }; @@ -212,10 +175,7 @@ impl Fire { // TODO: can we just check if value == ty? let instance = if fields.is_empty() { - Instance { - ty: "empty type", - data: Vec::new(), - } + Instance::empty() } else { unreachable!() }; @@ -225,21 +185,28 @@ impl Fire { self.allocate(node.origin, instance); } + fn fire_return( + &mut self, + fir: &Fir<FlattenData<'_>>, + node: &Node<FlattenData<'_>>, + expr: &Option<RefIdx>, + ) { + if let Some(returned) = expr { + self.fire_node_ref(fir, returned); + + self.transfer(returned, node.origin); + } // FIXME: Allocate None otherwise? + } + fn fire_node_ref(&mut self, fir: &Fir<FlattenData<'_>>, node_ref: &RefIdx) { let node = &fir.nodes[&node_ref.unwrap()]; self.fire_node(fir, node) } - // FIXME: Do we actually need to return an `Instance` here? We should just be able to lookup the rvalue's id or w/ever in the ctx - // `where x = call()` - // -> we perform the call, allocate data, and then look it up when accessing `x`? Is `x` a reference to call()? a move? that ties in the move semantics right? - // where we would "move" the value from the call's OriginId to x's OriginId fn fire_node(&mut self, fir: &Fir<FlattenData<'_>>, node: &Node<FlattenData<'_>>) { - dbg!(&node.data.ast); - match &node.kind { - Kind::Constant(c) => self.fire_constant(fir, node, c), + Kind::Constant(c) => self.fire_constant(node, c), Kind::Statements(stmts) => self.fire_block(fir, node, stmts), Kind::Call { to, @@ -248,6 +215,7 @@ impl Fire { } => self.fire_call(fir, node, to, args), Kind::Binding { to } => self.fire_binding(fir, node, to), Kind::TypedValue { value, ty } => self.fire_typed_value(fir, node, value, ty), + Kind::Return(expr) => self.fire_return(fir, node, expr), // Kind::TypeReference(r) => self.traverse_type_reference(fir, node, r), // Kind::Generic { default } => self.traverse_generic(fir, node, default), // Kind::Type { generics, fields } => self.traverse_type(fir, node, generics, fields), @@ -272,7 +240,6 @@ impl Fire { // false_block, // } => self.traverse_condition(fir, node, condition, true_block, false_block), // Kind::Loop { condition, block } => self.traverse_loop(fir, node, condition, block), - // Kind::Return(expr) => self.traverse_return(fir, node, expr), _ => {}, } } @@ -322,6 +289,16 @@ mod tests { }; } + #[test] + fn last_value() { + let ast = ast! { + "jinko" + }; + + let result = fir!(ast).interpret(); + assert_eq!(result, Some(Instance::from("jinko"))) + } + #[test] fn call() { let ast = ast! { @@ -331,5 +308,18 @@ mod tests { }; let result = fir!(ast).interpret(); + assert_eq!(result, Some(Instance::from(15))) + } + + #[test] + fn nested_call() { + let ast = ast! { + func id(x: string) -> string { x } + + id(id(id(id("jinko")))) + }; + + let result = fir!(ast).interpret(); + assert_eq!(result, Some(Instance::from("jinko"))) } }