Skip to content

Commit

Permalink
Untangle user grammar parsing from resolution. This means it is now a…
Browse files Browse the repository at this point in the history
…gnostic of scopes and the imperative resolver.

Remove keywords from scoping; instead, they are handled by interpreting the grammar. This means keywords can no longer be shadowed (boo, but I suppose that's just sane).
  • Loading branch information
Ivorforce committed Apr 29, 2024
1 parent 7cb67ff commit 9f561d1
Show file tree
Hide file tree
Showing 7 changed files with 584 additions and 522 deletions.
15 changes: 11 additions & 4 deletions src/parser/grammar.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use uuid::Uuid;
use std::rc::Rc;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Display, Error, Formatter};
use linked_hash_map::LinkedHashMap;
use std::hash::{Hash, Hasher};
use strum::{Display, EnumIter};
use std::rc::Rc;

use itertools::Itertools;
use linked_hash_map::LinkedHashMap;
use strum::{Display, EnumIter};
use uuid::Uuid;

use crate::error::{RResult, RuntimeError};

#[derive(Copy, Clone, PartialEq, Eq, Debug, Display, EnumIter)]
Expand Down Expand Up @@ -43,13 +45,15 @@ pub enum PatternPart {
#[derive(Clone, PartialEq, Eq)]
pub struct Grammar<Function: Clone + PartialEq + Eq + Hash + Debug> {
pub patterns: HashSet<Rc<Pattern<Function>>>,
pub keywords: HashSet<String>,
pub groups_and_keywords: LinkedHashMap<Rc<PrecedenceGroup>, HashMap<String, Function>>,
}

impl<Function: Clone + PartialEq + Eq + Hash + Debug> Grammar<Function> where {
pub fn new() -> Self {
Grammar {
patterns: Default::default(),
keywords: Default::default(),
groups_and_keywords: Default::default(),
}
}
Expand All @@ -59,6 +63,7 @@ impl<Function: Clone + PartialEq + Eq + Hash + Debug> Grammar<Function> where {
.map(|p| (p, HashMap::new()))
.collect();
self.patterns = HashSet::new();
self.keywords = HashSet::new();
}

pub fn add_pattern(&mut self, pattern: Rc<Pattern<Function>>) -> RResult<Vec<String>> {
Expand All @@ -74,6 +79,7 @@ impl<Function: Clone + PartialEq + Eq + Hash + Debug> Grammar<Function> where {
] => {
assert_eq!(pattern.precedence_group.associativity, OperatorAssociativity::LeftUnary);
keyword_map.insert(keyword.clone(), pattern.function.clone());
self.keywords.insert(keyword.clone());
vec![keyword.clone()]
},
[
Expand All @@ -89,6 +95,7 @@ impl<Function: Clone + PartialEq + Eq + Hash + Debug> Grammar<Function> where {
] => {
assert_ne!(pattern.precedence_group.associativity, OperatorAssociativity::LeftUnary);
keyword_map.insert(keyword.clone(), pattern.function.clone());
self.keywords.insert(keyword.clone());
vec![keyword.clone()]
}
_ => return Err(RuntimeError::error("This pattern form is not supported; try using unary or binary patterns.").to_array()),
Expand Down
2 changes: 1 addition & 1 deletion src/resolver/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl <'a> GlobalResolver<'a> {
for decoration in pstatement.decorations_as_vec()? {
let pattern = try_parse_pattern(decoration, Rc::clone(&fun), &self.global_variables)?;
self.module.patterns.insert(Rc::clone(&pattern));
self.global_variables.add_pattern(pattern)?;
self.global_variables.grammar.add_pattern(pattern)?;
}
self.schedule_function_body(&fun, syntax.body.as_ref(), pstatement.value.position.clone());
self.add_function_interface(fun, representation)?;
Expand Down
4 changes: 3 additions & 1 deletion src/resolver/grammar.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::fmt::Display;
use std::hash::{Hash, Hasher};

use itertools::Itertools;

use crate::program::expression_tree::ExpressionID;
use crate::program::functions::ParameterKey;

pub mod parse;
pub mod precedence_order;
pub mod expressions;

#[derive(Clone)]
pub struct Struct {
Expand Down
296 changes: 296 additions & 0 deletions src/resolver/grammar/expressions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;

use itertools::Itertools;

use crate::ast;
use crate::error::{RResult, RuntimeError};
use crate::parser::grammar::{Grammar, OperatorAssociativity};
use crate::util::position::Positioned;

pub enum Value<'a, Function> {
Operation(Function, Vec<Box<Positioned<Self>>>),
Identifier(&'a String),
RealLiteral(&'a String),
IntLiteral(&'a String),
StringLiteral(&'a Vec<Box<Positioned<ast::StringPart>>>),
StructLiteral(&'a ast::Struct),
ArrayLiteral(&'a ast::Array),
Block(&'a ast::Block),
MemberAccess(Box<Positioned<Self>>, &'a String),
FunctionCall(Box<Positioned<Self>>, &'a ast::Struct),
Subscript(Box<Positioned<Self>>, &'a ast::Array),
IfThenElse(&'a ast::IfThenElse),
}

pub enum Token<'a, Function> {
Keyword(&'a String),
Value(Box<Positioned<Value<'a, Function>>>),
}

pub fn parse_to_tokens<'a, Function: Clone + PartialEq + Eq + Hash + Debug>(syntax: &'a[Box<Positioned<ast::Term>>], grammar: &'a Grammar<Function>) -> RResult<Vec<Positioned<Token<'a, Function>>>> {
let mut tokens = vec![];

let mut i = 0;
while i < syntax.len() {
let ast_token = &syntax[i];
i += 1;

match &ast_token.value {
ast::Term::Error(err) => Err(err.clone().to_array())?,
ast::Term::Identifier(identifier) => {
if grammar.keywords.contains(identifier) {
tokens.push(ast_token.with_value(Token::Keyword(identifier)));
}
else {
tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::Identifier(identifier))))));
}
}
ast::Term::MacroIdentifier(_) => {
return Err(RuntimeError::error("Macro not supported here.").to_array())
}
ast::Term::Dot => {
let Some(target_token) = tokens.pop() else {
return Err(RuntimeError::error("Dot notation requires a preceding object.").to_array())
};
let Token::Value(target) = target_token.value else {
return Err(RuntimeError::error("Dot notation requires a preceding object.").to_array())
};

let Some(next) = syntax.get(i) else {
return Err(RuntimeError::error("Dot notation requires a following identifier.").to_array())
};
let ast::Term::Identifier(member) = &next.value else {
return Err(RuntimeError::error("Dot notation requires a following identifier.").to_array())
};

i += 1;
tokens.push(Positioned {
position: target_token.position,
value: Token::Value(Box::new(Positioned {
position: next.position.clone(),
value: Value::MemberAccess(target, member)
}))
});
}
ast::Term::IntLiteral(string) => {
tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::IntLiteral(string))))));
}
ast::Term::RealLiteral(string) => {
tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::RealLiteral(string))))));
}
ast::Term::StringLiteral(parts) => {
tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::StringLiteral(parts))))));
}
ast::Term::Struct(s) => {
if let Some(Token::Value(_)) = tokens.last().map(|t| &t.value) {
// Previous token; we've got a direct call!
let Token::Value(previous) = tokens.pop().unwrap().value else { panic!() };

tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::FunctionCall(previous, s))))));
continue;
}

tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::StructLiteral(s))))));
}
ast::Term::Array(array) => {
if let Some(Token::Value(_)) = tokens.last().map(|t| &t.value) {
// Previous token; we've got a direct call!
let Token::Value(previous) = tokens.pop().unwrap().value else { panic!() };

tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::Subscript(previous, array))))));
continue;
}

tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::ArrayLiteral(array))))));
}
ast::Term::Block(block) => {
tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::Block(block))))));
}
ast::Term::IfThenElse(if_then_else) => {
tokens.push(ast_token.with_value(Token::Value(Box::new(ast_token.with_value(Value::IfThenElse(if_then_else))))));
}
}
}

Ok(tokens)
}

pub fn parse_unary<'a, Function: Clone + PartialEq + Eq + Hash + Debug>(mut tokens: Vec<Positioned<Token<'a, Function>>>, functions: Option<&'a HashMap<String, Function>>) -> RResult<(Vec<Box<Positioned<Value<'a, Function>>>>, Vec<&'a str>)> {
let mut values: Vec<Box<Positioned<Value<Function>>>> = vec![];
let mut keywords: Vec<&str> = vec![];

let final_ptoken = tokens.remove(tokens.len() - 1);
if let Token::Value(value) = final_ptoken.value {
values.push(value);
}
else {
return Err(RuntimeError::error("Expected expression.").in_range(final_ptoken.position).to_array())
}

if let Some(functions) = functions {
while let Some(ptoken) = tokens.pop() {
let Token::Keyword(keyword) = ptoken.value else {
return Err(RuntimeError::error("Got two consecutive values; expected an operator in between.").to_array())
};

if let Some(Token::Value(_)) = tokens.last().map(|t| &t.value) {
let Token::Value(value) = tokens.pop().unwrap().value else { panic!() };

// Binary Operator keyword, because left of operator is a value
values.insert(0, value);
keywords.insert(0, keyword);

continue
}

// Unary operator, because left of operator is an operator
let argument = values.remove(0);
values.insert(0, Box::new(ptoken.with_value(Value::Operation(functions[keyword].clone(), vec![argument]))));
}
}

return Ok((values, keywords))
}

pub fn resolve_expression<'a, Function: Clone + PartialEq + Eq + Hash + Debug>(syntax: &'a[Box<Positioned<ast::Term>>], grammar: &'a Grammar<Function>) -> RResult<Box<Positioned<Value<'a, Function>>>> {
// Here's what this function does:
// We go left to right through all the terms.
// Many terms can just be evaluated to a token, like int literals or local references.
// Some terms do something to the previous term. For example, () will either create an empty
// struct, or it will call the previous object as function (if any).
// Some terms also do something to the next term. Mostly, a . will interpret the next term as a
// member reference rather than a global.
let mut tokens = parse_to_tokens(syntax, grammar)?;

let left_unary_operators = grammar.groups_and_keywords.iter().next().map(|(group, ops)| {
if let Some((group, left_unary_operators)) = &grammar.groups_and_keywords.iter().next() {
if group.associativity != OperatorAssociativity::LeftUnary {
todo!("Left Unary operators must be first for now.");
}
}

ops
});

let (mut values, mut keywords) = parse_unary(tokens, left_unary_operators)?;

if values.len() == 1 {
// Just one argument, we can shortcut!
return Ok(values.remove(0))
}

// Resolve binary operators. At this point, we have only expressions interspersed with operators.
let join_binary_at = |arguments: &mut Vec<Box<Positioned<Value<Function>>>>, function: &Function, i: usize| -> RResult<()> {
let lhs = arguments.remove(i);
let rhs = arguments.remove(i);

let range = lhs.position.start..rhs.position.end;

Ok(arguments.insert(
i,
Box::new(Positioned {
position: range.clone(),
value: Value::Operation(function.clone(), vec![lhs, rhs]),
})
))
};

for (group, group_operators) in grammar.groups_and_keywords.iter() {
match group.associativity {
OperatorAssociativity::Left => {
// Iterate left to right
let mut i = 0;
while i < keywords.len() {
if let Some(function_head) = group_operators.get(keywords[i]) {
keywords.remove(i);
join_binary_at(&mut values, function_head, i)?;
}
else {
i += 1; // Skip
}
}
}
OperatorAssociativity::Right => {
// Iterate right to left
let mut i = keywords.len();
while i > 0 {
i -= 1;
if let Some(alias) = group_operators.get(keywords[i]) {
keywords.remove(i);
join_binary_at(&mut values, alias, i)?;
}
}
}
OperatorAssociativity::None => {
// Iteration direction doesn't matter here.
let mut i = 0;
while i < keywords.len() {
if let Some(alias) = group_operators.get(keywords[i]) {
if i + 1 < group_operators.len() && group_operators.contains_key(keywords[i + 1]) {
panic!("Cannot parse two neighboring {} operators because no associativity is defined.", keywords[i]);
}

keywords.remove(i);
join_binary_at(&mut values, alias, i)?;
}

i += 1;
}
}
OperatorAssociativity::LeftConjunctivePairs => {
// Iteration direction doesn't matter here.
let mut i = 0;
while i < keywords.len() {
if !group_operators.contains_key(keywords[i]) {
// Skip
i += 1;
continue;
}

if i + 1 >= keywords.len() || !group_operators.contains_key(keywords[i + 1]) {
// Just one operation; let's use a binary operator.
join_binary_at(&mut values, &group_operators[keywords.remove(i)], i)?;
continue;
}

// More than one operation; Let's build a pairwise operation!
// We can start with the first two operators and 3 arguments of which we
// know they belong to the operation.
let mut group_arguments = vec![
values.remove(i), values.remove(i), values.remove(i)
];
let mut group_operators = vec![
keywords.remove(i), keywords.remove(i)
];

while i < keywords.len() && group_operators.contains(&keywords[i]) {
// Found one more! Yay!
group_arguments.push(values.remove(i));
group_operators.push(keywords.remove(i));
}

// Let's wrap this up.
values.insert(i, todo!("Resolve group_operators to functions"));
}
}
// Unary operators are already resolved at this stage.
OperatorAssociativity::LeftUnary => {},
OperatorAssociativity::RightUnary => todo!(),
}

if keywords.len() == 0 {
// We can return early
assert_eq!(values.len(), 1);
return Ok(values.pop().unwrap())
}
}

if keywords.len() > 0 {
panic!("Unrecognized binary operator pattern(s); did you forget an import? Offending Operators: {:?}", &keywords);
}

assert_eq!(values.len(), 1);
return Ok(values.pop().unwrap())
}
Loading

0 comments on commit 9f561d1

Please sign in to comment.