commit fa3240ba36c4052b39316684348aef1bbd98f1bd
parent bec75cc31cee86eb7fece599abca75450f6869df
Author: Demonstrandum <moi@knutsen.co>
Date: Tue, 16 Jul 2019 15:28:05 +0100
Seperated lib and bin.
Diffstat:
9 files changed, 197 insertions(+), 33 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -13,6 +13,14 @@ version = "0.1.0"
authors = ["Demonstrandum <moi@knutsen.co>"]
edition = "2018"
+[lib]
+name = "valhalla"
+path = "src/lib.rs"
+
+[[bin]]
+name = "valhalla"
+path = "src/bin.rs"
+
[dependencies]
lazy_static = "1.3.0"
regex = "1"
diff --git a/src/bin.rs b/src/bin.rs
@@ -0,0 +1,5 @@
+use valhalla;
+
+pub fn main() {
+ valhalla::parse();
+}+
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
@@ -0,0 +1,15 @@
+//! Crate responsible for parsing and compiling
+//! the generated AST to Brokkr-bytecode for the
+//! Valhalla set theoretic programming language.
+
+/// Syntax submodule, responsible for lexical analysis,
+/// parsing and static analysis.
+mod syntax;
+
+
+pub fn parse() {
+ println!("\nTill Valhalla!\n");
+
+ syntax::parse_file("./test.vh");
+}
+
diff --git a/src/main.rs b/src/main.rs
@@ -1,15 +0,0 @@
-//! Crate responsible for parsing and compiling
-//! the generated AST to Brokkr-bytecode for the
-//! Valhalla set theoretic programming language.
-
-/// Syntax submodule, responsible for lexical analysis,
-/// parsing and static analysis.
-mod syntax;
-
-
-fn main() {
- println!("\nTill Valhalla!\n");
-
- syntax::parse_file("./test.vh");
-}
-
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
@@ -214,6 +214,14 @@ impl SymNode {
pub fn new(value : &str) -> Nodes { Nodes::Sym(SymNode { value: value.to_string() }) }
}
+impl CallNode {
+ pub fn new(callee : Nodes, operands : Vec<Nodes>) -> Nodes {
+ Nodes::Call(CallNode {
+ callee: Box::new(callee),
+ operands: operands,
+ })
+ }
+}
/// Root branch of the AST.
pub struct Root {
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
@@ -8,6 +8,9 @@ mod token;
/// Abstract Syntax Tree nodes and methods.
mod ast;
+/// Dealing with associativity and precedence.
+mod operators;
+
/// Lexer splits code up into a token-stream
/// of relevant lexical tokens, making the
/// parsing step a lot easier.
@@ -26,7 +29,7 @@ pub fn parse_file(filename : &str) {
.expect("Could not open file for reading.");
println!("Code:\n{}\n", code);
- let stream = lexer::lex(&code);
+ let mut stream = lexer::lex(&code);
println!("Stream:\n{}\n", stream.to_string());
let tree = parser::parse(stream);
diff --git a/src/syntax/operators.rs b/src/syntax/operators.rs
@@ -0,0 +1,112 @@
+
+/// Side of associativity.
+#[derive(PartialEq)]
+pub enum Side {
+ Left, Right, Neither
+}
+
+/// Operator information, including:
+/// - The string, representing what the operator looks like.
+/// - Its precedence (as an i32), the higher the int, the higher the precedence.
+/// - Associativity, which can either be left, right, or no associativity.
+/// - The number of arguments it takes / its arity. Either one, or two.
+pub struct Operator {
+ pub name : String,
+ pub precedence : i32,
+ pub associativity : Side,
+ pub arity : i32,
+}
+
+impl Operator {
+ pub fn new(name : &str, precedence : i32, associativity : Side, arity : i32) -> Self {
+ Operator {
+ name: name.to_string(),
+ precedence,
+ associativity,
+ arity,
+ }
+ }
+
+ pub fn is_left(&self) -> bool {
+ if self.associativity == Side::Left {
+ return true;
+ }
+ false
+ }
+
+ pub fn is_right(&self) -> bool {
+ if self.associativity == Side::Right {
+ return true;
+ }
+ false
+ }
+}
+
+/// Wrapper for table of known operators.
+pub struct PrecedenceTable {
+ pub table : Vec<Operator>
+}
+
+#[macro_export]
+macro_rules! push_op {
+ ($table:expr, $op:expr, $prec:expr, $assoc:path, $arity:expr) => {
+ $table.table.push(Operator::new($op, $prec as i32, $assoc, $arity as i32))
+ };
+}
+
+impl PrecedenceTable {
+ pub fn new() -> Self {
+ let op = Operator::new;
+ let mut table = PrecedenceTable { table: vec![
+ op( "::",21, Side::Left, 2),
+ op( "<>",20, Side::Right, 2),
+ // Function calls have precedence 19, i.e. very high.
+ // e.g. `f x + 3` bracketed is the same as `(f (x) (+) (3))`.
+ op("not",18, Side::Right, 1),
+ op( "-",17, Side::Right, 1),
+ op( "^",16, Side::Right, 2),
+ op( "*",15, Side::Left, 2),
+ op( "/",15, Side::Left, 2),
+ op("mod",15, Side::Left, 2),
+ op( "&",14, Side::Left, 2),
+ op( "|",13, Side::Left, 2),
+ op( "+",12, Side::Left, 2),
+ op( "-",12, Side::Left, 2),
+ op( "\\",12, Side::Left, 2),
+ op( "->",11, Side::Right, 2),
+ op( ">>",10, Side::Right, 2),
+ op( "<<",10, Side::Left, 2),
+ op( "==", 9, Side::Neither, 2),
+ op( "is",9, Side::Neither, 2),
+ op( "/=", 9, Side::Neither, 2),
+ op("isn't",9,Side::Neither, 2),
+ op( "<", 9, Side::Neither, 2),
+ op( "<=", 9, Side::Neither, 2),
+ op( ">", 9, Side::Neither, 2),
+ op( ">=", 9, Side::Neither, 2),
+ op( "<-", 8, Side::Neither, 2),
+ op( "&&", 7, Side::Right, 2),
+ op("and", 7, Side::Right, 2),
+ op( "||", 6, Side::Right, 2),
+ op( "or", 6, Side::Right, 2),
+ op( "..", 5, Side::Neither, 2),
+ op( ":", 4, Side::Neither, 2),
+ op( "|>", 4, Side::Right, 2),
+ op( "=", 3, Side::Right, 2),
+ op( "if", 2, Side::Neither, 2),
+ op("unless", 2, Side::Neither, 2),
+ op( ",", 1, Side::Right, 2),
+ op( "=>", 0, Side::Neither, 2),
+ ]};
+
+ table
+ }
+
+ pub fn lookup(&self, name : &str, arity : i32) -> Option<&Operator> {
+ self.table.iter().filter(|o| o.name == name && o.arity == arity).next()
+ }
+
+ pub fn exists(&self, name : &str) -> bool {
+ self.table.iter().filter(|o| o.name == name).next().is_some()
+ }
+}+
\ No newline at end of file
diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs
@@ -1,28 +1,55 @@
use super::token;
use super::ast;
+use super::operators;
use token::{Token, TokenType};
use ast::{Numerics, Nodes};
pub fn parse(stream : Vec<Token>) -> ast::Root {
- let mut tree = ast::Root::new();
+ let mut environment = ParseEnvironment::new(stream);
- for token in stream {
- if token.is_atomic() {
- tree.branches.push(atom(&token));
- }
- }
+ environment.start();
- tree
+ environment.root
}
-fn atom(token : &Token) -> Nodes {
- match token.class {
- TokenType::Ident => ast::IdentNode::new(&token.string),
- TokenType::Op => ast::IdentNode::new(&token.string),
- TokenType::Num => ast::NumNode::new(&*token.string),
- TokenType::Str => ast::StrNode::new(&token.string),
- _ => panic!("Passed non-atomic token to `atom` parser.")
+struct ParseEnvironment {
+ pub root : ast::Root,
+ pub stream : Vec<Token>,
+ pub optable : operators::PrecedenceTable
+}
+
+impl ParseEnvironment {
+ pub fn new(stream : Vec<Token>) -> Self {
+ ParseEnvironment {
+ root: ast::Root::new(),
+ stream: stream,
+ optable: operators::PrecedenceTable::new()
+ }
+ }
+
+ pub fn start(&mut self) {
+ while !self.stream.is_empty() {
+ let token = self.stream.remove(0);
+ match token.class {
+ TokenType::Op => {
+ if !self.optable.exists(&token.string) { panic!("Use of undefined operator."); }
+ // ...
+ }
+ _ => panic!("Unexpected token.")
+ };
+ }
+ self.root.branches.push(ast::IdentNode::new("hello"));
+ }
+
+ fn atom(&self, token : &Token) -> Nodes {
+ match token.class {
+ TokenType::Ident => ast::IdentNode::new(&token.string),
+ TokenType::Op => ast::IdentNode::new(&token.string),
+ TokenType::Num => ast::NumNode::new(&*token.string),
+ TokenType::Str => ast::StrNode::new(&token.string),
+ _ => panic!("Passed non-atomic token to `atom` parser.")
+ }
}
}
diff --git a/test.vh b/test.vh
@@ -1,2 +1 @@
-漢字 = "hello漢字漢字 world"
- 漢字漢字 漢字v-
\ No newline at end of file
+3 + 8 * 9+
\ No newline at end of file