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