commit bb647cb407c25a32430fdcaea94bab89a32904ce
parent d912dc4659fd5744e26810e444d5c635d3c34f39
Author: Demonstrandum <moi@knutsen.co>
Date:   Sun, 28 Jul 2019 23:46:31 +0100
Bytecode compiler simple arithmetic and type coercion.
Diffstat:
19 files changed, 445 insertions(+), 124 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -9,6 +9,8 @@ Cargo.lock
 # These are backup files generated by rustfmt
 **/*.rs.bk
 
+# VS Code Settings
+.vscode/
 
 # OS files
 .DS_Store
diff --git a/Cargo.toml b/Cargo.toml
@@ -26,3 +26,5 @@ lazy_static = "1.3.0"
 regex = "1"
 snailquote = "0.2.0"
 unicode-width = "0.1.5"
+enum-primitive-derive = "^0.1"
+num-traits = "^0.1"+
\ No newline at end of file
diff --git a/samples/casting.vh b/samples/casting.vh
@@ -0,0 +1,3 @@
+2 + 3.2  -- 2 natural gets cast to 2.0 real.
+-3 + 4   -- 4 natural gets cased to 4 integer.
+4 + 5    -- 4 and 5 stay natural.
diff --git a/src/compiler/block.rs b/src/compiler/block.rs
@@ -1,13 +1,23 @@
+use std::fmt;
+
 use super::super::syntax;
 use syntax::ast;
 
 use super::element;
 use super::instructions;
 
-use element::Element;
+use element::{Element, Symbol};
 use instructions::{Instr, Operators};
 
-#[derive(Debug)]
+use super::internal_functions;
+use super::casts;
+
+fn append_unique<'a>(v : &mut Vec<Element<'a>>, e : Element<'a>) -> usize {
+    let index = v.iter().position(|c| c == &e);
+    if index.is_none() { v.push(e); }
+    index.unwrap_or(v.len() - 1)
+}
+
 pub struct LocalBlock<'a> {
     constants : Vec<Element<'a>>,
     locals : Vec<Element<'a>>,
@@ -23,25 +33,73 @@ impl<'a> LocalBlock<'a> {
         }
     }
 
-    pub fn generate(&mut self, node : ast::Nodes) {
+    fn push_const_instr(&mut self, e : Element<'a>) {
+        let index = append_unique(&mut self.constants, e);
+        self.instructions.push(Instr::Operator(Operators::PUSH_CONST as u8));
+        self.instructions.push(Instr::Operand(index as u16));
+    }
+
+    fn emit(&mut self, node : &'a ast::Nodes) {
         match node {
             ast::Nodes::Num(num_node) => {
-                let elem = match num_node.value {
-                    ast::Numerics::Natural(n) => Element::ENatural(n),
-                    ast::Numerics::Integer(n) => Element::EInteger(n),
-                    ast::Numerics::Real(n)    => Element::EReal(n)
-                };
-                let index = self.append_const(elem);
-                self.instructions.push(Instr::Operator(Operators::PUSH_CONST as u8));
-                self.instructions.push(Instr::Operand(index as u16))
+                let elem = casts::numerics_to_element(&num_node.value);
+                self.push_const_instr(elem);
+            },
+            ast::Nodes::Str(str_node) => self.push_const_instr(Element::EString(str_node.value.to_owned())),
+            ast::Nodes::Sym(sym_node) => self.push_const_instr(Element::ESymbol(Symbol::new(&sym_node.value))),
+            ast::Nodes::Call(call_node) => {
+                if call_node.is_binary() {
+                    let ident = call_node.callee.call().unwrap().callee.ident().unwrap();
+                    let args = vec![
+                        &call_node.callee.call().unwrap().operands[0],
+                        &call_node.operands[0]
+                    ];
+
+                    // each_and_every_one
+                    if args.iter().all(|n| n.is_numeric()) {
+                        let nums = args.iter().map(|node| {
+                            casts::numerics_to_element(&node.num().unwrap().value)
+                        }).collect::<Vec<Element>>();
+                        let casted_args = casts::try_cast(nums);
+                        if let Some(cast_succ) = casted_args {
+                            let inop = internal_functions::get_internal_op(&ident.value, Some(&cast_succ));
+                            if let Some(op) = inop {
+                                self.push_const_instr(cast_succ[0].clone());
+                                self.push_const_instr(cast_succ[1].clone());
+                                self.instructions.push(op);
+                            }
+                        }
+                    } else {
+                        let inop = internal_functions::get_internal_op(&ident.value, None);
+                        if let Some(op) = inop {
+                            self.emit(args[0]);
+                            self.emit(args[1]);
+                            self.instructions.push(op)
+                        }
+                    }
+                }
             },
             _ => ()
         };
     }
 
-    fn append_const(&mut self, e : Element<'a>) -> usize {
-        let index = self.constants.iter().position(|c| c == &e);
-        if index.is_none() { self.constants.push(e); }
-        index.unwrap_or(self.constants.len() - 1)
+    pub fn generate(&mut self, nodes : &'a Vec<ast::Nodes>) {
+        for node in nodes {
+            self.emit(node);
+        }
     }
 }
+
+impl<'a> fmt::Display for LocalBlock<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "===Constants===============\n")?;
+        for (i, c) in self.constants.iter().enumerate() {
+            write!(f, "{: >3} |  {} |\n", i, c)?;
+        }
+        write!(f, "===Bytecodes===============\n")?;
+        for inst in &self.instructions {
+            write!(f, "{}", inst)?;
+        }
+        write!(f, "")
+    }
+}+
\ No newline at end of file
diff --git a/src/compiler/casts.rs b/src/compiler/casts.rs
@@ -0,0 +1,58 @@
+use super::element;
+use element::Element;
+
+use super::super::syntax;
+use syntax::ast;
+
+pub fn numerics_to_element<'a>(num : &ast::Numerics) -> Element<'a> {
+    match num {
+        ast::Numerics::Natural(n) => Element::ENatural(*n),
+        ast::Numerics::Integer(n) => Element::EInteger(*n),
+        ast::Numerics::Real(n)    => Element::EReal(*n)
+    }
+}
+
+pub enum Casts {  // In order of cast strength.
+    REAL,
+    INT,
+    NAT
+}
+
+macro_rules! conversion {
+    ($arg:expr, $to:path, $base:ident) => {
+        match $arg {
+            Element::ENatural(n) => $to(*n as $base),
+            Element::EInteger(n) => $to(*n as $base),
+            Element::EReal(n)    => $to(*n as $base),
+            _ => panic!("Internal error, tried to cast non-numeric to numeric.")
+        };
+    };
+}
+
+pub fn cast_to<'a> (cast : Casts, args : &Vec<Element>) -> Vec<Element<'a>> {
+    let mut new_args : Vec<Element> = vec![];
+    for arg in args {
+        let new = match cast {
+            Casts::REAL => conversion!(arg, Element::EReal,    f64),
+            Casts::INT  => conversion!(arg, Element::EInteger, isize),
+            Casts::NAT  => conversion!(arg, Element::ENatural, usize),
+        };
+        new_args.push(new);
+    }
+    new_args
+}
+
+pub fn try_cast<'a>(args : Vec<Element<'a>>) -> Option<Vec<Element<'a>>> {
+    if args.iter().all(Element::is_numeric) {
+        for arg in &args {
+            let converted = match arg {
+                Element::EReal(_)    => Some(cast_to(Casts::REAL, &args)),
+                Element::EInteger(_) => Some(cast_to(Casts::INT,  &args)),
+                _ => None
+            };
+            if let Some(v) = converted { return Some(v); }
+        }
+        return Some(cast_to(Casts::NAT, &args));
+    }
+    None
+}+
\ No newline at end of file
diff --git a/src/compiler/element.rs b/src/compiler/element.rs
@@ -1,11 +1,31 @@
 use std::fmt;
+use std::collections::hash_map::DefaultHasher;
+use std::hash::{Hash, Hasher};
 
+use snailquote::escape;
+
+#[derive(Clone, Copy)]
 pub struct Symbol<'a> {
-    hash : u32,
+    hash : u64,
     string : &'a str
 }
 
-impl<'a> fmt::Debug for Symbol<'a> {
+fn hash_symbol(string : &str) -> u64 {
+    let mut s = DefaultHasher::new();
+    string.hash(&mut s);
+    s.finish()
+}
+
+impl<'a> Symbol<'a> {
+    pub fn new(string : &'a str) -> Self {
+        Symbol {
+            hash: hash_symbol(string),
+            string
+        }
+    }
+}
+
+impl<'a> fmt::Display for Symbol<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, ":{}", self.string)
     }
@@ -17,11 +37,36 @@ impl<'a> PartialEq for Symbol<'a> {
     }
 }
 
-#[derive(PartialEq, Debug)]
+#[derive(Clone, PartialEq)]
 pub enum Element<'a> {
     ENatural(usize),
     EInteger(isize),
     EReal(f64),
     EString(String),
     ESymbol(Symbol<'a>),
-}-
\ No newline at end of file
+}
+
+impl<'a> Element<'a> {
+    pub fn is_numeric(&self) -> bool {
+        match *self {
+            Element::ENatural(_)
+            | Element::EInteger(_)
+            | Element::EReal(_) => true,
+            _ => false
+        }
+    }
+}
+
+
+impl<'a> fmt::Display for Element<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let s = match self {
+            Element::ENatural(t) => format!("{: <5}  => (Nat)   ", t),
+            Element::EInteger(t) => format!("{: <5}  => (Int)   ", t),
+            Element::EReal(t)    => format!("{: <5}  => (Real)  ", if t.fract() == 0f64 { format!("{:.1}", t) } else { f64::to_string(t) }),
+            Element::EString(t)  => format!("{: <5}  => (String)", format!("\"{}\"", escape(t))),
+            Element::ESymbol(t)  => format!("{: <5}  => (Sym)   ", t.to_string()),
+        };
+        write!(f, "{}", s)
+    }
+}
diff --git a/src/compiler/instructions.rs b/src/compiler/instructions.rs
@@ -1,10 +1,28 @@
+use std::fmt;
+
+use num_traits::{FromPrimitive, ToPrimitive};
+
 #[derive(Debug)]
 pub enum Instr {
     Operator(u8),
     Operand(u16)
 }
 
+impl fmt::Display for Instr {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let s = match &self {
+            Instr::Operand(n) => format!("{: >5}\n", n),
+            Instr::Operator(n) => {
+                format!("({:08b}):{}", n, Operators::from_u8(*n).unwrap())
+            }
+        };
+        write!(f, "{}", s)
+    }
+}
+
 #[repr(u8)]
+#[allow(non_camel_case_types)]
+#[derive(Primitive)]
 pub enum Operators {
     HALT        = 0,
     PUSH_CONST  = 1,
@@ -15,4 +33,63 @@ pub enum Operators {
     DUP         = 6,
     DUP_N       = 7,
     SWAP        = 8,
-}-
\ No newline at end of file
+
+    N_ADD       = 40,
+    I_ADD       = 41,
+    R_ADD       = 42,
+    U_ADD       = 43,
+    CONCAT      = 44,
+    N_SUB       = 45,
+    I_SUB       = 46,
+    R_SUB       = 47,
+    U_SUB       = 48,
+    N_MUL       = 49,
+    I_MUL       = 50,
+    R_MUL       = 51,
+    U_MUL       = 52,
+    N_DIV       = 53,
+    I_DIV       = 54,
+    R_DIV       = 55,
+    U_DIV       = 56,
+}
+
+
+impl fmt::Display for Operators {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let s = match &self {
+            Operators::HALT        => "HALT",
+            Operators::PUSH_CONST  => "PUSH_CONST",
+            Operators::PUSH_LOCAL  => "PUSH_LOCAL",
+            Operators::PUSH_SUPER  => "PUSH_SUPER",
+            Operators::POP         => "POP\n",
+            Operators::STORE_LOCAL => "STORE_LOCAL",
+            Operators::DUP         => "DUP\n",
+            Operators::DUP_N       => "DUP_N",
+            Operators::SWAP        => "SWAP\n",
+
+            Operators::N_ADD       => "N_ADD\n",
+            Operators::I_ADD       => "I_ADD\n",
+            Operators::R_ADD       => "R_ADD\n",
+            Operators::U_ADD       => "U_ADD\n",
+            Operators::CONCAT      => "CONCAT\n",
+
+            Operators::N_SUB       => "N_SUB\n",
+            Operators::I_SUB       => "I_SUB\n",
+            Operators::R_SUB       => "R_SUB\n",
+            Operators::U_SUB       => "U_SUB\n",
+
+            Operators::N_MUL       => "N_MUL\n",
+            Operators::I_MUL       => "I_MUL\n",
+            Operators::R_MUL       => "R_MUL\n",
+            Operators::U_MUL       => "U_MUL\n",
+
+            Operators::N_DIV       => "N_DIV\n",
+            Operators::I_DIV       => "I_DIV\n",
+            Operators::R_DIV       => "R_DIV\n",
+            Operators::U_DIV       => "U_DIV\n",
+
+            _ => "INVALID_OPCODE\n"
+        };
+        write!(f, "{}", s)
+    }
+}
diff --git a/src/compiler/internal_functions.rs b/src/compiler/internal_functions.rs
@@ -0,0 +1,51 @@
+use super::element;
+use element::Element;
+
+use super::instructions;
+use instructions::{Instr, Operators};
+
+use super::casts;
+
+/// Gets the appropriate operator for the internal functions.
+/// Assumes all args have equal type.
+pub fn get_internal_op(ident : &str, args : Option<&Vec<Element>>) -> Option<Instr> {
+    match ident {
+        "+" => {
+            if args.is_none() { return Some(Instr::Operator(Operators::U_ADD as u8)); }
+            Some(Instr::Operator(match args.unwrap()[0] {
+                Element::ENatural(_) => Operators::N_ADD,
+                Element::EInteger(_) => Operators::I_ADD,
+                Element::EReal(_)    => Operators::R_ADD,
+                _                    => Operators::U_ADD
+            } as u8))
+        },
+        "-" => {
+            if args.is_none() { return Some(Instr::Operator(Operators::U_SUB as u8)); }
+            Some(Instr::Operator(match args.unwrap()[0] {
+                Element::ENatural(_) => Operators::N_SUB,
+                Element::EInteger(_) => Operators::I_SUB,
+                Element::EReal(_)    => Operators::R_SUB,
+                _                    => Operators::U_SUB
+            } as u8))
+        },
+        "*" => {
+            if args.is_none() { return Some(Instr::Operator(Operators::U_MUL as u8)); }
+            Some(Instr::Operator(match args.unwrap()[0] {
+                Element::ENatural(_) => Operators::N_MUL,
+                Element::EInteger(_) => Operators::I_MUL,
+                Element::EReal(_)    => Operators::R_MUL,
+                _                    => Operators::U_MUL
+            } as u8))
+        },
+        "/" => {
+            if args.is_none() { return Some(Instr::Operator(Operators::U_DIV as u8)); }
+            Some(Instr::Operator(match args.unwrap()[0] {
+                Element::ENatural(_) => Operators::N_DIV,
+                Element::EInteger(_) => Operators::I_DIV,
+                Element::EReal(_)    => Operators::R_DIV,
+                _                    => Operators::U_DIV
+            } as u8))
+        }
+        _ => None
+    }
+}+
\ No newline at end of file
diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs
@@ -1,5 +1,7 @@
 //! Compilation of the syntax tree.
 mod element;
 mod instructions;
+mod casts;
+mod internal_functions;
 pub mod block;
 mod marshal; 
\ No newline at end of file
diff --git a/src/err.rs b/src/err.rs
@@ -0,0 +1,55 @@
+use crate::syntax::token;
+
+use std::fs;
+use std::fmt;
+use std::io::{BufRead, BufReader};
+
+pub enum Types {
+    LexError,
+    ParseError,
+    TypeError,
+    CompError,
+}
+
+impl fmt::Display for Types {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let printable = match *self {
+            Types::LexError   => "Lexicographical Error",
+            Types::ParseError =>         "Grammar Error",
+            Types::TypeError  =>          "Typing Error",
+            Types::CompError  =>     "Compilation Error",
+        };
+        write!(f, "{}", printable)
+    }
+}
+
+pub fn issue(class : Types, filename : &str, token : &token::Token,  message : &str) {
+    let file = fs::File::open(filename).expect("Invalid filename for error message.");
+    let line = BufReader::new(file).lines().nth((token.location.line - 1) as usize).unwrap().unwrap();
+
+    eprintln!("{}", message);
+    eprintln!(" ==> {class} in  (`{file}`:{line}:{col}):\n{space}|\n{line_str}| {stuff}",
+        class=class, file=filename, line=token.location.line,
+        col=token.location.col, space=" ".repeat(5),
+        line_str=format!("{: >4} ", token.location.line), stuff=line);
+    eprintln!("{space}|{: >offset$}",
+        "^".repeat(token.location.span as usize), space=" ".repeat(5),
+        offset=((token.location.col + token.location.span) as usize));
+}
+
+#[macro_export]
+macro_rules! issue {
+    ($type:path, $file:expr, $token:expr, $message:expr) => {
+        {
+            err::issue($type, $file, $token, $message);
+            std::process::exit(1)
+        }
+    };
+    ($type:path, $file:expr, $token:expr, $message:expr, $($form:expr),*) => {
+        {
+            err::issue($type, $file, $token, &format!($message, $($form),*));
+            std::process::exit(1)
+        }
+    };
+}
+
diff --git a/src/lib.rs b/src/lib.rs
@@ -2,17 +2,28 @@
 //! the generated AST to Brokkr-bytecode for the
 //! Valhalla set theoretic programming language.
 
+#[macro_use]
+extern crate enum_primitive_derive;
+extern crate num_traits;
+
+/// Error messages.
+#[macro_use]
+mod err;
+
 /// Syntax submodule, responsible for lexical analysis,
 /// parsing and static analysis.
 mod syntax;
+
+/// Compiler, transforms AST into stack-based bytecode
+/// instructions for the Brokkr VM, and marshals the instructions.
 mod compiler;
 
-pub fn parse(filename : &str) {    
-    syntax::parse_file(filename);
+pub fn parse(filename : &str) {
+    let root = syntax::parse_file(filename);
     let mut code_block = compiler::block::LocalBlock::new();
-    code_block.generate(syntax::ast::NumNode::new(3.14));
-    code_block.generate(syntax::ast::NumNode::new(34));
-    code_block.generate(syntax::ast::NumNode::new(3.14));
-    println!("Code Block:\n{:#?}", code_block)
+
+
+    code_block.generate(&root.branches);
+    println!("Code Block:\n{}", code_block)
 }
 
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
@@ -126,7 +126,7 @@ pub struct SymNode {
 /// Call Node has a pointer to the callee node
 /// and a list of operand nodes.
 pub struct CallNode {
-    /// Pointer to heap allocated calling node. 
+    /// Pointer to heap allocated calling node.
     pub callee : Box<Nodes>,
     /// Pointer to list of operand nodes.
     pub operands : Vec<Nodes>
@@ -159,7 +159,7 @@ impl fmt::Display for Nodes {
             Nodes::Ident(node)  => format!("%ident{{ :value \"{}\" }}", node.value),
             Nodes::Num(node)    => format!("%num{{ :value {} }}", node.value),
             Nodes::Str(node)    => format!("%str{{ :value \"{}\" }}", node.value),
-            Nodes::Sym(node)    => format!("%sym{{ :value \"{}\" }}", node.value),
+            Nodes::Sym(node)    => format!("%sym{{ :value \":{}\" }}", node.value),
             Nodes::Call(node)   => format!(
                 "%call{{\n  :callee ({})\n  :operands [|\n    {}\n  |]\n}}", node.callee,
                 node.operands.iter().map(Nodes::to_string).collect::<Vec<String>>().join("\n    ")),
@@ -190,11 +190,18 @@ impl Nodes {
 
     pub fn is_atomic(&self) -> bool {
         match self {
-            Nodes::Ident(_)  => true,
-            Nodes::Num(_)    => true,
-            Nodes::Str(_)    => true,
-            Nodes::Sym(_)    => true,
-            Nodes::Empty(_)  => true,
+            Nodes::Ident(_)
+            | Nodes::Num(_)
+            | Nodes::Str(_)
+            | Nodes::Sym(_)
+            | Nodes::Empty(_)  => true,
+            _ => false
+        }
+    }
+
+    pub fn is_numeric(&self) -> bool {
+        match self {
+            Nodes::Num(_)=> true,
             _ => false
         }
     }
@@ -216,7 +223,7 @@ impl StrNode {
 }
 
 impl SymNode {
-    pub fn new(value : &str) -> Nodes { Nodes::Sym(SymNode { value: value.to_string() }) }
+    pub fn new(value : &str) -> Nodes { Nodes::Sym(SymNode { value: value[1..].to_string() }) }
 }
 
 impl CallNode {
@@ -226,6 +233,15 @@ impl CallNode {
             operands: operands,
         })
     }
+
+    pub fn is_unary(&self) -> bool {
+        self.callee.ident().is_some() && !self.operands.is_empty()
+    }
+
+    pub fn is_binary(&self) -> bool {
+        let sub_call = self.callee.call();
+        sub_call.is_some() && !self.operands.is_empty() && sub_call.unwrap().is_unary()
+    }
 }
 
 impl EmptyNode {
@@ -243,7 +259,7 @@ impl Root {
     }
 }
 
-const TAB : &str = "  "; 
+const TAB : &str = "  ";
 
 pub fn pretty_print(node : &Nodes, depth : usize) -> String {
     let tab = TAB.repeat(depth);
diff --git a/src/syntax/err.rs b/src/syntax/err.rs
@@ -1,53 +0,0 @@
-use super::token;
-
-use std::fs;
-use std::fmt;
-use std::io::{BufRead, BufReader};
-
-pub enum Types {
-    LexError,
-    ParseError,
-    TypeError,
-}
-
-impl fmt::Display for Types {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let printable = match *self {
-            Types::LexError => "Lexicographical Error",
-            Types::ParseError => "Grammar Error",
-            Types::TypeError => "Typing Error"
-        };
-        write!(f, "{}", printable)
-    }
-}
-
-pub fn issue(class : Types, filename : &str, token : &token::Token,  message : &str) {
-    let file = fs::File::open(filename).expect("Invalid filename for error message.");
-    let line = BufReader::new(file).lines().nth((token.location.line - 1) as usize).unwrap().unwrap();
-
-    eprintln!("{}", message);
-    eprintln!(" ==> {class} in  (`{file}`:{line}:{col}):\n{space}|\n{line_str}| {stuff}",
-        class=class, file=filename, line=token.location.line,
-        col=token.location.col, space=" ".repeat(5),
-        line_str=format!("{: >4} ", token.location.line), stuff=line);
-    eprintln!("{space}|{: >offset$}",
-        "^".repeat(token.location.span as usize), space=" ".repeat(5),
-        offset=((token.location.col + token.location.span) as usize));
-}
-
-#[macro_export]
-macro_rules! issue {
-    ($type:path, $file:expr, $token:expr, $message:expr) => {
-        {
-            super::err::issue($type, $file, $token, $message);
-            std::process::exit(1)
-        }
-    };
-    ($type:path, $file:expr, $token:expr, $message:expr, $($form:expr),*) => {
-        {
-            super::err::issue($type, $file, $token, &format!($message, $($form),*));
-            std::process::exit(1)
-        }
-    };
-}
-
diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs
@@ -66,7 +66,7 @@ macro_rules! try_match {
 /// the generated token-stream (as a Vec<Token>).
 pub fn lex(string : &str) -> Vec<Token> {
     let mut token_stream : Vec<Token> = Vec::new();
-    
+
     let mut current_char_ptr = 0;
     let string_size = string.bytes().count();
 
@@ -74,7 +74,7 @@ pub fn lex(string : &str) -> Vec<Token> {
     let mut line = 1;
     let mut col  = 1;
 
-    // Step through 
+    // Step through
     while current_char_ptr < string_size {
         // Align to character boundary.
         if let Some(slice) = &string.get(current_char_ptr..) {
@@ -101,7 +101,7 @@ pub fn lex(string : &str) -> Vec<Token> {
             col += string.get(old_char_ptr..current_char_ptr)
                 .expect("Comment ended or started not on char boundary.")
                 .width() as u32;
-            
+
             continue;
         }
 
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
@@ -2,10 +2,10 @@
 
 /// location manages line and column location of
 /// lexical tokens as well as their span.
-mod location;
+pub mod location;
 
 /// Provides token classes and methods
-mod token;
+pub mod token;
 
 /// Abstract Syntax Tree nodes and methods.
 pub mod ast;
@@ -13,10 +13,6 @@ pub mod ast;
 /// Dealing with associativity and precedence.
 mod operators;
 
-/// Error messages.
-#[macro_use]
-pub mod err;
-
 /// Lexer splits code up into a token-stream
 /// of relevant lexical tokens, making the
 /// parsing step a lot easier.
@@ -30,7 +26,7 @@ use token::ShowStream;
 
 /// Parses a given file, calling various methods from
 /// the `syntax` sub-module.
-pub fn parse_file(filename : &str) {
+pub fn parse_file(filename : &str) -> ast::Root {
     let code = fs::read_to_string(filename)
         .expect("Could not open file for reading.");
     println!("Code:\n{}\n", code);
@@ -40,4 +36,5 @@ pub fn parse_file(filename : &str) {
 
     let tree = parser::parse(stream, filename);
     println!("AST:\n{}\n", tree);
+    tree
 } 
\ No newline at end of file
diff --git a/src/syntax/operators.rs b/src/syntax/operators.rs
@@ -37,12 +37,6 @@ impl<'a> Operator<'a> {
     pub fn is_unary(&self) -> bool { self.has_arity(1)  }
 
     pub fn is_binary(&self) -> bool { self.has_arity(2) }
-
-    pub fn is_variable(&self) -> bool { self.has_arity(0) }
-
-    pub fn is_func(&self) -> bool {
-        self.precedence == 190 && self.arity >= 1
-    }
 }
 
 /// Wrapper for table of known operators.
@@ -98,12 +92,12 @@ impl<'a> PrecedenceTable<'a> {
             op(  "=", 30, Side::Right,   2),
             op( "if", 20, Side::Neither, 2),
             op("unless", 20, Side::Neither, 2),
-            op(  ",", 10, Side::Right,   2),        
+            op(  ",", 10, Side::Right,   2),
             op( "=>",  1, Side::Neither, 2),
             op(  "(",  0, Side::Neither, 1),
             op(  ")",  0, Side::Neither, 1),
         ]};
-        
+
         table
     }
 
diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs
@@ -1,7 +1,9 @@
 use super::token;
 use super::ast;
 use super::operators;
-use super::err;
+
+#[macro_use]
+use super::super::err;
 
 use token::{Token, TokenType};
 use ast::{Nodes, Numerics};
@@ -20,7 +22,7 @@ struct ParseEnvironment<'a> {
     pub stream : Vec<Token>,
     pub optable : operators::PrecedenceTable<'a>,
     pub file : &'a str,
-    
+
     ignore_newline : bool
 }
 
@@ -35,7 +37,7 @@ impl<'a> ParseEnvironment<'a> {
             ignore_newline: false
         }
     }
-    
+
     pub fn start(&mut self) {
         let mut current = self.stream.first();
         while current.is_some() && current.unwrap().class != TokenType::EOF {
@@ -93,7 +95,7 @@ impl<'a> ParseEnvironment<'a> {
                     return ast::EmptyNode::new();
                 }
 
-                
+
                 self.ignore_newline = true;
                 self.skip_newlines();
                 let expr = self.expr(0);
@@ -126,7 +128,7 @@ impl<'a> ParseEnvironment<'a> {
             let next = &(&self.stream[0].string).clone();
 
             if self.ignore_newline && next == "\n" {
-                self.stream.remove(0);    
+                self.stream.remove(0);
                 continue;
             }
             if next == "\0" || next == "\n" || next == ")" { break; }
@@ -194,7 +196,7 @@ mod test {
         assert_eq!(ast::NumNode::new(-2).num().unwrap().value, Numerics::Integer(-2isize));
         assert_eq!(ast::NumNode::new(-2i32).num().unwrap().value, Numerics::Integer(-2isize));
         assert_eq!(ast::NumNode::new(-2isize).num().unwrap().value, Numerics::Integer(-2isize));
-        
+
         assert_eq!(ast::NumNode::new(-2.62).num().unwrap().value, Numerics::Real(-2.62f64));
         assert_eq!(ast::NumNode::new(2.62).num().unwrap().value, Numerics::Real(2.62f64));
 
diff --git a/src/syntax/token.rs b/src/syntax/token.rs
@@ -85,11 +85,11 @@ impl Token {
     /// Checks if the token represents an atomic datum.
     pub fn is_atomic(&self) -> bool {
         match self.class {
-            TokenType::Ident => true,
-            TokenType::Num   => true,
-            TokenType::Op    => true,
-            TokenType::Sym   => true,
-            TokenType::Str   => true,
+            TokenType::Ident
+            | TokenType::Num
+            | TokenType::Op
+            | TokenType::Sym
+            | TokenType::Str => true,
             _ => false,
         }
     }
diff --git a/test.vh b/test.vh
@@ -1,4 +1,4 @@
-2
-34
-2.3
--2-
\ No newline at end of file
+4 + 3
+5 - 4
+3 * 4.0
+3 + :a