commit 5f94308ea77cdb81118b9eb1be4cf577a8fb33f2
parent b73354ec6147294a693bf08e5388741a8a16a9d7
Author: Demonstrandum <moi@knutsen.co>
Date: Sun, 21 Jul 2019 17:49:24 +0100
Currying OPs and comments.
Diffstat:
6 files changed, 104 insertions(+), 49 deletions(-)
diff --git a/README.md b/README.md
@@ -9,18 +9,18 @@ bytecode compilation) which understands the syntax and
semantics, as well as doing static type analysis and code
optimisation. The generated AST is then compiled to
Brokkr bytecode.
-The execution of the subsequential bytecode
-is handled by the langauge's VM (virtual machine) called
-Brokkr, which exists seperately.
+The execution of the subsequent bytecode
+is handled by the language's VM (virtual machine) called
+Brokkr, which exists separately.
Valhalla is a set theoretic programming language.
-That's to say, it's based on priciples from set theory,
+That's to say, it's based on principles from set theory,
in a way that all types are just sets, and hence everything
is just an element of a set. The language is meant to give a
new way to think about types, and provides an intuitive way to
think about types. It may also be used to verify proofs and such
about set theory.
-The language is a general purpose, but intead of being all OOP,
+The language is a general purpose, but instead of being all OOP,
or functional, etc., it's just set theory based. From what I've
-gathered, it's not a very popular paradigme.
+gathered, it's not a very popular paradigm.
diff --git a/samples/currying_infix.vh b/samples/currying_infix.vh
@@ -0,0 +1,3 @@
+(2 + 3) -- Eq. of: ((2 +) 3), can write: ((+ 3) 2)
+(2 +)
+(+ 3) -- Partial application+
\ No newline at end of file
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
@@ -139,6 +139,8 @@ pub struct BlockNode {
pub statements : Vec<Nodes>
}
+pub struct EmptyNode;
+
/// All node types.
pub enum Nodes {
Ident(IdentNode),
@@ -146,7 +148,8 @@ pub enum Nodes {
Str(StrNode),
Sym(SymNode),
Call(CallNode),
- Block(BlockNode)
+ Block(BlockNode),
+ Empty(EmptyNode),
}
@@ -161,6 +164,7 @@ impl fmt::Display for Nodes {
"%call{{\n :callee ({})\n :operands [|\n {}\n |]\n}}", node.callee,
node.operands.iter().map(Nodes::to_string).collect::<Vec<String>>().join("\n ")),
Nodes::Block(node) => format!("%block{{ ... }}"),
+ Nodes::Empty(_) => String::from("()"),
};
write!(f, "{}", printable)
}
@@ -190,8 +194,8 @@ impl Nodes {
Nodes::Num(_) => true,
Nodes::Str(_) => true,
Nodes::Sym(_) => true,
- Nodes::Call(_) => false,
- Nodes::Block(_) => false,
+ Nodes::Empty(_) => true,
+ _ => false
}
}
}
@@ -224,6 +228,10 @@ impl CallNode {
}
}
+impl EmptyNode {
+ pub fn new() -> Nodes { Nodes::Empty(EmptyNode { }) }
+}
+
/// Root branch of the AST.
pub struct Root {
pub branches : Vec<Nodes>
@@ -240,17 +248,17 @@ const TAB : &str = " ";
pub fn pretty_print(node : &Nodes, depth : usize) -> String {
let tab = TAB.repeat(depth);
let printable = match node {
- Nodes::Ident(_) => format!("{}{}", tab, node),
- Nodes::Num(_) => format!("{}{}", tab, node),
- Nodes::Str(_) => format!("{}{}", tab, node),
- Nodes::Sym(_) => format!("{}{}", tab, node),
- Nodes::Call(n) => format!(
- "{tab}%call{{\n{tab}{T}:callee (\n{calling}\n{tab}{T})\n{tab}{T}:operands [|\n{ops}\n{tab}{T}|]\n{tab}}}",
+ Nodes::Call(n) => format!(
+ "{tab}%call{{\n{tab}{T}:callee (\n{calling}\n{tab}{T})\n{tab}{T}:operand [|{op}|]\n{tab}}}",
tab=tab, T=TAB,
calling=pretty_print(&*n.callee, depth + 2),
- ops=n.operands.iter().map(|e| pretty_print(e, depth + 2)).collect::<Vec<String>>().join("\n")
+ op=(if n.operands.is_empty() { String::from(" ") } else { format!(
+ "\n{ops}\n{tab}{T}",
+ ops=pretty_print(&n.operands[0], depth + 2),
+ tab=tab, T=TAB) })
),
- Nodes::Block(n) => format!("%block{{ ... }}"),
+ Nodes::Block(n) => format!("%block{{ ... }}"),
+ _ => format!("{}{}", tab, node)
};
printable
}
diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs
@@ -49,13 +49,13 @@ lazy_static! {
macro_rules! try_match {
($stream:expr, $partial:expr,
$reg:expr, $token_type:expr,
- $current_char:expr, $line:expr, $col:expr) => {
+ $current_char_ptr:expr, $line:expr, $col:expr) => {
if let Some(matched) = $reg.first_match($partial) {
let span = matched.width() as u32;
$stream.push(Token::new(
$token_type, &matched,
location::new($line, $col, span)));
- $current_char += matched.len();
+ $current_char_ptr += matched.len();
$col += span;
continue;
}
@@ -67,42 +67,64 @@ macro_rules! try_match {
pub fn lex(string : &str) -> Vec<Token> {
let mut token_stream : Vec<Token> = Vec::new();
- let mut current_char = 0;
+ let mut current_char_ptr = 0;
let string_size = string.bytes().count();
let mut partial : &str;
let mut line = 1;
let mut col = 1;
- while current_char < string_size {
- if let Some(slice) = &string.get(current_char..) {
+ // Step through
+ while current_char_ptr < string_size {
+ // Align to character boundary.
+ if let Some(slice) = &string.get(current_char_ptr..) {
partial = slice;
} else { // Not on boundary yet.
- current_char += 1;
+ current_char_ptr += 1;
continue;
}
- let maybe_vec = &partial.get(0..2).unwrap_or("");
- let vec_brack = match maybe_vec {
- &"[|" => Some(TokenType::LVec),
- &"|]" => Some(TokenType::RVec),
+
+ let two_chars = partial.get(0..2).unwrap_or("\0\0");
+
+ // Consume EON comment:
+ if two_chars.chars().nth(0).unwrap() == '#' || two_chars == "--" {
+ let old_char_ptr = current_char_ptr;
+ current_char_ptr += if two_chars == "--" { 2 } else { 1 };
+ loop {
+ let current_char = string.bytes().nth(current_char_ptr).unwrap_or(b'\0');
+ if current_char == b'\n' || current_char == b'\0' {
+ break;
+ }
+ current_char_ptr += 1;
+ }
+ col += string.get(old_char_ptr..current_char_ptr)
+ .expect("Comment ended or started not on char boundary.")
+ .width() as u32;
+
+ continue;
+ }
+
+ let vec_brack = match two_chars {
+ "[|" => Some(TokenType::LVec),
+ "|]" => Some(TokenType::RVec),
_ => None
};
if let Some(tt) = vec_brack {
token_stream.push(Token::new(
- tt, maybe_vec,
+ tt, two_chars,
location::new(line, col, 2)));
col += 2;
- current_char += 2;
+ current_char_ptr += 2;
continue;
}
- if *maybe_vec == ": " {
+ if two_chars == ": " {
token_stream.push(Token::new(
TokenType::Op, ":",
location::new(line, col, 1)));
col += 2;
- current_char += 2;
+ current_char_ptr += 2;
continue;
}
@@ -130,7 +152,7 @@ pub fn lex(string : &str) -> Vec<Token> {
} else {
col += 1;
}
- current_char += 1;
+ current_char_ptr += 1;
continue;
}
@@ -143,7 +165,7 @@ pub fn lex(string : &str) -> Vec<Token> {
while !eos { // Spaghet
if let Some(character) = partial.chars().nth(i) {
if character == '"' {
- current_char += 1;
+ current_char_ptr += 1;
col += 1;
eos = true;
} else if character == '\\' {
@@ -156,10 +178,10 @@ pub fn lex(string : &str) -> Vec<Token> {
'b' => String::from("\x08"),
'0' => String::from("\0"),
'x' => {
- if let Some(code) = partial.get((current_char + 2)..(current_char + 4)) {
+ if let Some(code) = partial.get((current_char_ptr + 2)..(current_char_ptr + 4)) {
i += 2;
col += 2;
- current_char += 2;
+ current_char_ptr += 2;
(u8::from_str_radix(code, 16).expect("Malformed hex.") as char).to_string()
} else { String::new() }
}
@@ -167,7 +189,7 @@ pub fn lex(string : &str) -> Vec<Token> {
};
i += 1;
col += 1;
- current_char += 1;
+ current_char_ptr += 1;
contents.push_str(&escaped);
continue;
} else {
@@ -178,7 +200,7 @@ pub fn lex(string : &str) -> Vec<Token> {
contents.push(character);
i += 1;
col += character.width().unwrap_or(2) as u32;
- current_char += character.len_utf8();
+ current_char_ptr += character.len_utf8();
continue;
}
} else {
@@ -186,7 +208,7 @@ pub fn lex(string : &str) -> Vec<Token> {
// Error: Unexpected EOS!
}
i += 1;
- current_char += 1;
+ current_char_ptr += 1;
col += 1;
}
token_stream.push(Token::new(
@@ -197,21 +219,21 @@ pub fn lex(string : &str) -> Vec<Token> {
try_match!(token_stream, partial,
NUM, TokenType::Num,
- current_char, line, col);
+ current_char_ptr, line, col);
try_match!(token_stream, partial,
OP, TokenType::Op,
- current_char, line, col);
+ current_char_ptr, line, col);
try_match!(token_stream, partial,
IDENT, TokenType::Ident,
- current_char, line, col);
+ current_char_ptr, line, col);
try_match!(token_stream, partial,
SYM, TokenType::Sym,
- current_char, line, col);
+ current_char_ptr, line, col);
- current_char += 1;
+ current_char_ptr += 1;
if partial.is_char_boundary(0) { col += 1 }
}
diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs
@@ -49,10 +49,19 @@ impl ParseEnvironment {
fn null_den(&mut self, token : &Token) -> Nodes {
match token.class {
TokenType::Ident => ast::IdentNode::new(&token.string),
- TokenType::Op => { // Prefix Op.
+ TokenType::Op => {
let is_op = self.optable.exists(&token.string);
if is_op {
- return ast::CallNode::new(ast::IdentNode::new(&token.string), vec![self.expr(300)]);
+ return match self.stream[0].class {
+ TokenType::RParen => {
+ ast::CallNode::new(ast::IdentNode::new(&token.string), vec![])
+ },
+ _ => ast::CallNode::new(
+ ast::CallNode::new(
+ ast::IdentNode::new(&token.string),
+ vec![]),
+ vec![self.expr(500)])
+ };
}
issue!(err::Types::ParseError, self.file, token,
"`{}` is not an operator.", token.string);
@@ -60,6 +69,13 @@ impl ParseEnvironment {
TokenType::Num => ast::NumNode::new(&*token.string),
TokenType::Str => ast::StrNode::new(&token.string),
TokenType::LParen => {
+ let current = self.stream.get(0);
+ if current.is_none() || current.unwrap().class == TokenType::EOF {
+ self.expect(TokenType::RParen, current)
+ } else if current.unwrap().class == TokenType::RParen {
+ self.stream.remove(0);
+ return ast::EmptyNode::new();
+ }
let expr = self.expr(0);
self.expect(TokenType::RParen, self.stream.get(0));
self.stream.remove(0);
@@ -93,8 +109,12 @@ impl ParseEnvironment {
}
fn left_den(&mut self, left : Nodes, op : operators::Operator) -> Nodes {
+ let first_appl = ast::CallNode::new(ast::IdentNode::new(op.name), vec![left]);
+ if self.stream[0].class == TokenType::RParen {
+ return first_appl;
+ }
let right = self.expr(op.precedence - (if op.is_right() { 1 } else { 0 }));
- ast::CallNode::new(ast::IdentNode::new(op.name), vec![left, right])
+ ast::CallNode::new(first_appl, vec![right])
}
fn expect(&self, tt : TokenType, maybe_t : Option<&Token>) {
diff --git a/test.vh b/test.vh
@@ -1,2 +1,3 @@
-f : A -> B -> C
-a = n + 3-
\ No newline at end of file
+(2 + 3) -- Eq. of: ((2 +) 3), can write: ((+ 3) 2)
+(2 +)
+(+ 3) -- Partial application+
\ No newline at end of file