valhallac

Compiler for set-theoretic programming language.
git clone git://git.knutsen.co/valhallac
Log | Files | Refs | README | LICENSE

commit fa3240ba36c4052b39316684348aef1bbd98f1bd
parent bec75cc31cee86eb7fece599abca75450f6869df
Author: Demonstrandum <moi@knutsen.co>
Date:   Tue, 16 Jul 2019 15:28:05 +0100

Seperated lib and bin.

Diffstat:
MCargo.toml | 8++++++++
Asrc/bin.rs | 6++++++
Asrc/lib.rs | 15+++++++++++++++
Dsrc/main.rs | 15---------------
Msrc/syntax/ast.rs | 8++++++++
Msrc/syntax/mod.rs | 5++++-
Asrc/syntax/operators.rs | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/syntax/parser.rs | 55+++++++++++++++++++++++++++++++++++++++++--------------
Mtest.vh | 5++---
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