diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index febb35e6beb593735b1132422976207e52411e04..f5eb4a6882d2da039978d7f1d7e6a49f598c825b 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -8,6 +8,8 @@ pub struct Module {
 #[derive(Clone)]
 pub struct Function {
     pub name: String,
+    pub param_types: Vec<TypeID>,
+    pub return_type: TypeID,
     pub nodes: Vec<Node>,
 }
 
@@ -44,11 +46,11 @@ pub enum Node {
     },
     Fork {
         control: NodeID,
-        factor: u64,
+        factor: usize,
     },
     Join {
         control: NodeID,
-        factor: u64,
+        factor: usize,
     },
     Phi {
         control: NodeID,
@@ -59,7 +61,7 @@ pub enum Node {
         value: NodeID,
     },
     Parameter {
-        index: u64,
+        index: usize,
     },
     Constant {
         id: ConstantID,
@@ -93,6 +95,10 @@ pub enum Node {
 pub struct FunctionID(u32);
 
 impl FunctionID {
+    pub fn new(x: usize) -> Self {
+        FunctionID(x as u32)
+    }
+
     pub fn idx(&self) -> usize {
         self.0 as usize
     }
@@ -102,6 +108,10 @@ impl FunctionID {
 pub struct NodeID(u32);
 
 impl NodeID {
+    pub fn new(x: usize) -> Self {
+        NodeID(x as u32)
+    }
+
     pub fn idx(&self) -> usize {
         self.0 as usize
     }
@@ -111,6 +121,10 @@ impl NodeID {
 pub struct ConstantID(u32);
 
 impl ConstantID {
+    pub fn new(x: usize) -> Self {
+        ConstantID(x as u32)
+    }
+
     pub fn idx(&self) -> usize {
         self.0 as usize
     }
@@ -120,6 +134,10 @@ impl ConstantID {
 pub struct TypeID(u32);
 
 impl TypeID {
+    pub fn new(x: usize) -> Self {
+        TypeID(x as u32)
+    }
+
     pub fn idx(&self) -> usize {
         self.0 as usize
     }
diff --git a/hercules_ir/src/parse.rs b/hercules_ir/src/parse.rs
index 0f27a411b810c7432bb0fd6bfbb3fe70fe77ef45..d6245d8e27fb4024392c45298f415f7e348dc50d 100644
--- a/hercules_ir/src/parse.rs
+++ b/hercules_ir/src/parse.rs
@@ -17,8 +17,10 @@ struct Context<'a> {
 }
 
 fn parse_module<'a>(ir_text: &'a str, mut context: Context<'a>) -> nom::IResult<&'a str, Module> {
-    let (rest, functions) = nom::multi::many0(|x| parse_function(x, &mut context))(ir_text)?;
-    nom::combinator::eof(rest)?;
+    let (rest, functions) =
+        nom::combinator::all_consuming(nom::multi::many0(|x| parse_function(x, &mut context)))(
+            ir_text,
+        )?;
     let mut types = vec![Type::Control(0); context.interned_types.len()];
     for (ty, id) in context.interned_types {
         types[id.idx()] = ty;
@@ -41,6 +43,65 @@ fn parse_function<'a>(
     ir_text: &'a str,
     context: &mut Context<'a>,
 ) -> nom::IResult<&'a str, Function> {
+    context.node_ids.clear();
+    let ir_text = nom::character::complete::multispace0(ir_text)?.0;
+    let ir_text = nom::bytes::complete::tag("fn")(ir_text)?.0;
+    let ir_text = nom::character::complete::multispace0(ir_text)?.0;
+    let (ir_text, function_name) = nom::character::complete::alphanumeric0(ir_text)?;
+    let ir_text = nom::character::complete::char('(')(ir_text)?.0;
+    let (ir_text, params) = nom::multi::separated_list0(
+        nom::character::complete::char(','),
+        nom::sequence::tuple((
+            nom::character::complete::alphanumeric1,
+            nom::character::complete::multispace0,
+            nom::character::complete::char(':'),
+            nom::character::complete::multispace0,
+            |x| parse_type(x, context),
+        )),
+    )(ir_text)?;
+    for param in params.iter() {
+        context
+            .node_ids
+            .insert(param.0, NodeID::new(context.node_ids.len()));
+    }
+    let ir_text = nom::character::complete::char(')')(ir_text)?.0;
+    let ir_text = nom::character::complete::multispace0(ir_text)?.0;
+    let ir_text = nom::bytes::complete::tag("->")(ir_text)?.0;
+    let (ir_text, return_type) = parse_type(ir_text, context)?;
+    let (ir_text, nodes) = nom::multi::many1(|x| parse_node(x, context))(ir_text)?;
+    let mut fixed_nodes = vec![Node::Start; context.node_ids.len()];
+    for (name, node) in nodes {
+        fixed_nodes[context.node_ids.remove(name).unwrap().idx()] = node;
+    }
+    for (_, id) in context.node_ids.iter() {
+        fixed_nodes[id.idx()] = Node::Parameter { index: id.idx() }
+    }
+    return Ok((
+        ir_text,
+        Function {
+            name: String::from(function_name),
+            param_types: params.into_iter().map(|x| x.4).collect(),
+            return_type,
+            nodes: fixed_nodes,
+        },
+    ));
+}
+
+fn parse_node<'a>(
+    ir_text: &'a str,
+    context: &mut Context<'a>,
+) -> nom::IResult<&'a str, (&'a str, Node)> {
+    todo!()
+}
+
+fn parse_type<'a>(ir_text: &'a str, context: &mut Context<'a>) -> nom::IResult<&'a str, TypeID> {
+    todo!()
+}
+
+fn parse_constant<'a>(
+    ir_text: &'a str,
+    context: &mut Context<'a>,
+) -> nom::IResult<&'a str, ConstantID> {
     todo!()
 }