From f5fababf0ca1afff9ba0b70c7958aeada4e2ce02 Mon Sep 17 00:00:00 2001 From: mschae23 <46165762+mschae23@users.noreply.github.com> Date: Thu, 21 Jul 2022 21:14:29 +0200 Subject: [PATCH] Templates can now be used as member functions --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/compiler/ast.rs | 29 ++++---- src/compiler/compiler.rs | 120 ++++++++++++++++++++++++++------- src/compiler/lexer.rs | 12 +++- src/compiler/parser.rs | 33 +++++---- test/main.densityfunction | 2 +- test/minecraft.densityfunction | 16 +++++ 8 files changed, 151 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d2ee4c..6c2ba87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,7 +66,7 @@ dependencies = [ [[package]] name = "density_function_lang" -version = "2.0.2" +version = "2.1.0" dependencies = [ "clap", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 39979b1..c56f920 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "density_function_lang" -version = "2.0.2" +version = "2.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/compiler/ast.rs b/src/compiler/ast.rs index d558dbc..56910a9 100644 --- a/src/compiler/ast.rs +++ b/src/compiler/ast.rs @@ -8,6 +8,7 @@ use crate::compiler::lexer::Token; pub enum Stmt { Template { name: Token, + this: Option, args: Vec, expr: Expr, }, @@ -35,10 +36,12 @@ pub enum Stmt { impl Debug for Stmt { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - Stmt::Template { name, args, expr } => - write!(f, "template {}({}) = {:?};", name.source(), args.iter() - .map(|arg| arg.source().to_owned()) - .collect::>().join(", "), expr), + Stmt::Template { name, this, args, expr } => + write!(f, "template {}({}{}) = {:?};", name.source(), + this.as_ref().map(|this| format!("{}, ", this.source())).unwrap_or_else(|| String::new()), + args.iter() + .map(|arg| arg.source().to_owned()) + .collect::>().join(", "), expr), Stmt::Function { name, expr } => write!(f, "function {} = {:?};", name.source(), expr), @@ -64,8 +67,6 @@ pub enum Expr { ConstantString(String), Identifier(Token), - Group(Box), - UnaryOperator { operator: Token, expr: Box, @@ -76,8 +77,8 @@ pub enum Expr { right: Box, }, FunctionCall { - receiver: Option>, - name: Token, + callee: Box, + paren_left: Token, args: Vec, }, Member { @@ -98,17 +99,10 @@ impl Debug for Expr { Expr::ConstantInt(value) => write!(f, "{}", value), Expr::ConstantString(value) => write!(f, "\"{}\"", value), Expr::Identifier(value) => write!(f, "{}", value.source()), - Expr::Group(expr) => write!(f, "{:?}", expr), Expr::UnaryOperator { operator, expr } => write!(f, "({}{:?})", operator.source(), expr), Expr::BinaryOperator { left, operator, right } => write!(f, "({:?} {} {:?})", left, operator.source(), right), - Expr::FunctionCall { receiver, name, args } => { - write!(f, "(")?; - - if let Some(receiver) = receiver { - write!(f, "{:?}.", receiver)?; - } - - write!(f, "{}({}))", name, args.iter() + Expr::FunctionCall { callee, args, .. } => { + write!(f, "({:?}({}))", callee, args.iter() .map(|expr| format!("{:?}", expr)) .collect::>().join(", ")) }, @@ -137,6 +131,7 @@ pub struct OutputFunction { #[derive(Debug)] pub struct Template { pub name: String, // Can be identifiers or operator names (like `+`) + pub receiver: bool, pub args: Vec, pub expr: Expr, pub current_modules: Vec>>, diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index c16e66a..c5c96c2 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -54,7 +54,7 @@ impl Compiler { fn compile_statement(&mut self, stmt: Stmt) { match stmt { - Stmt::Template { name, args, expr } => self.compile_template(name, args, expr), + Stmt::Template { name, this, args, expr } => self.compile_template(name, this, args, expr), Stmt::Function { name, expr } => self.compile_function(name, expr), Stmt::Module { name, statements } => self.compile_module(name, statements), Stmt::Include { path } => self.compile_include(path), @@ -63,11 +63,12 @@ impl Compiler { } } - fn compile_template(&mut self, name: Token, args: Vec, expr: Expr) { + fn compile_template(&mut self, name: Token, this: Option, args: Vec, expr: Expr) { let current_module = self.current_module(); let mut current_module = current_module.borrow_mut(); - if current_module.templates.iter().any(|template| *template.borrow().name == *name.source() && template.borrow().args.len() == args.len()) { + if current_module.templates.iter().any(|template| + *template.borrow().name == *name.source() && template.borrow().args.len() == args.len() && template.borrow().receiver == this.is_some()) { self.error_at(*name.start(), "Tried to define multiple templates with the same name", false); return; } @@ -77,6 +78,7 @@ impl Compiler { current_module.templates.push(Rc::new(RefCell::new(Template { name: name.source().to_owned(), + receiver: this.is_some(), args: args.iter().map(|arg| arg.source().to_owned()).collect(), expr, current_modules: template_current_modules, @@ -265,33 +267,31 @@ impl Compiler { if let Some((_, element)) = self.template_scopes.last().map(|scope| scope.args.iter().rfind(|(arg, _)| *arg == *name.source())).flatten() { element.clone() } else { - self.error_at(*name.start(), &format!("Unresolved reference: '{}'", name.source()), false); - JsonElement::Error + self.evaluate_identifier(name) } } - Expr::Group(expr) => self.compile_expr(*expr), + // Expr::Group(expr) => self.compile_expr(*expr), Expr::UnaryOperator { operator, expr } => { let compiled_expr = self.compile_expr(*expr); - self.evaluate_template(None, operator, vec![compiled_expr]) + let pos = *operator.start(); + self.evaluate_template(Expr::Identifier(operator), pos, vec![compiled_expr]) }, Expr::BinaryOperator { left, operator, right } => { let compiled_left = self.compile_expr(*left); let compiled_right = self.compile_expr(*right); - self.evaluate_template(None, operator, vec![compiled_left, compiled_right]) + let pos = *operator.start(); + self.evaluate_template(Expr::Identifier(operator), pos, vec![compiled_left, compiled_right]) }, - Expr::FunctionCall { receiver, name, args } => { - let compiled_receiver = receiver.map(|receiver| self.compile_expr(*receiver)); + Expr::FunctionCall { callee, paren_left, args } => { let compiled_args = args.into_iter().map(|arg| self.compile_expr(arg)).collect(); - self.evaluate_template(compiled_receiver, name, compiled_args) + self.evaluate_template(*callee, *paren_left.start(), compiled_args) }, Expr::Member { receiver, name } => { - let _ = self.compile_expr(*receiver); - - self.error_at(*name.start(), &format!("Unresolved reference: '{}'", name.source()), false); - JsonElement::Error + let compiled_receiver = self.compile_expr(*receiver); + self.evaluate_member(compiled_receiver, name) }, Expr::Object(fields) => { JsonElement::Object(fields.into_iter().map(|(name, field)| (name.source().to_owned(), self.compile_expr(field))).collect()) @@ -303,21 +303,32 @@ impl Compiler { } } - fn evaluate_template(&mut self, receiver: Option, name: Token, args: Vec) -> JsonElement { - if let Some(_) = receiver { - self.error_at(*name.start(), "Member functions are not implemented yet", false); - } + fn evaluate_template(&mut self, callee: Expr, token_pos: TokenPos, args: Vec) -> JsonElement { + let (receiver, name) = match callee { + Expr::Member { receiver, name} => (Some(self.compile_expr(*receiver)), name), + Expr::Identifier(name) => (None, name), + _ => { + self.error_at(token_pos, "Cannot call non-identifier expression", true); + return JsonElement::Error; + }, + }; - let template = match self.find_template(&name, args.len()) { + let template = match self.find_template(&name, receiver.as_ref(), args.len()) { Some(template) => template, None => { self.error_at(*name.start(), &format!("Unresolved function call: {}", name.source()), false); return JsonElement::Error; - }, + } }; let template_borrow = template.borrow(); let mut scope_args = vec![]; + // eprintln!("name: {}; template receiver: {}; provided receiver: {}; template args: {:?}; provided args: {:?}", + // &template_borrow.name, template_borrow.receiver, receiver.is_some(), &template_borrow.args, &args); + + if let Some(receiver) = receiver { + scope_args.push((String::from("this"), receiver)); + } for i in 0..template_borrow.args.len() { let name = template_borrow.args[i].clone(); @@ -346,7 +357,14 @@ impl Compiler { expr } - fn find_template(&mut self, name: &Token, arg_count: usize) -> Option>> { + fn find_template(&mut self, name: &Token, receiver: Option<&JsonElement>, arg_count: usize) -> Option>> { + if let Some(receiver) = receiver { + match receiver { + JsonElement::Module(module) => return Self::find_template_on(module, name, false, arg_count), + _ => {}, + } + } + let mut module_index: isize = self.current_module.len() as isize - 1; while module_index >= -1 { @@ -354,7 +372,7 @@ impl Compiler { &self.current_module[module_index as usize] } else { &self.top_level_module }); - if let Some(template) = Self::find_template_on(&module, &name, arg_count) { + if let Some(template) = Self::find_template_on(&module, &name, receiver.is_some(), arg_count) { return Some(template); } @@ -364,9 +382,9 @@ impl Compiler { None } - fn find_template_on(module: &Rc>, name: &Token, arg_count: usize) -> Option>> { + fn find_template_on(module: &Rc>, name: &Token, receiver: bool, arg_count: usize) -> Option>> { for template in &module.borrow().templates { - if *template.borrow().name == *name.source() && template.borrow().args.len() == arg_count { + if *template.borrow().name == *name.source() && template.borrow().args.len() == arg_count && (template.borrow().receiver == receiver) { return Some(Rc::clone(template)); } } @@ -374,6 +392,58 @@ impl Compiler { None } + fn evaluate_identifier(&mut self, name: Token) -> JsonElement { + let mut module_index: isize = self.current_module.len() as isize - 1; + + while module_index >= -1 { + let module = Rc::clone(if module_index >= 0 { + &self.current_module[module_index as usize] + } else { &self.top_level_module }); + + for template in &module.borrow().templates { + if *template.borrow().name == *name.source() && !template.borrow().receiver { + return JsonElement::Template(Rc::clone(template)); + } + } + + for sub_module in &module.borrow().sub_modules { + if *sub_module.borrow().name == *name.source() { + return JsonElement::Module(Rc::clone(sub_module)); + } + } + + module_index -= 1; + } + + self.error_at(*name.start(), &format!("Unresolved reference: '{}'", name.source()), false); + JsonElement::Error + } + + fn evaluate_member(&mut self, receiver: JsonElement, name: Token) -> JsonElement { + match receiver { + JsonElement::Module(module) => { + for template in &module.borrow().templates { + if *template.borrow().name == *name.source() { + return JsonElement::Template(Rc::clone(template)); + } + } + + for sub_module in &module.borrow().sub_modules { + if *sub_module.borrow().name == *name.source() { + return JsonElement::Module(Rc::clone(sub_module)); + } + } + + self.error_at(*name.start(), &format!("Unresolved reference: '{}'", name.source()), false); + JsonElement::Error + }, + _ => { + self.error_at(*name.start(), "Tried to get a member of non-module value", true); + JsonElement::Error + }, + } + } + fn current_module(&self) -> Rc> { self.current_module.last().map(|module| Rc::clone(&module)).unwrap_or_else(|| Rc::clone(&self.top_level_module)) } diff --git a/src/compiler/lexer.rs b/src/compiler/lexer.rs index a03323b..6244402 100644 --- a/src/compiler/lexer.rs +++ b/src/compiler/lexer.rs @@ -54,7 +54,7 @@ pub enum TokenType { // Keywords Builtin, Inline, - Template, + Template, This, Function, Module, Include, Import, @@ -343,7 +343,15 @@ impl<'source> Lexer<'source> { } else { TokenType::Identifier } }, 'm' => Lexer::check_keyword(name, 1, "module", TokenType::Module), - 't' => Lexer::check_keyword(name, 1, "template", TokenType::Template), + 't' => { + if let Some(c) = chars.next() { + match c { + 'e' => Lexer::check_keyword(name, 2, "template", TokenType::Template), + 'h' => Lexer::check_keyword(name, 2, "this", TokenType::This), + _ => TokenType::Identifier, + } + } else { TokenType::Identifier } + }, _ => TokenType::Identifier, }; diff --git a/src/compiler/parser.rs b/src/compiler/parser.rs index a6aff1e..52f209e 100644 --- a/src/compiler/parser.rs +++ b/src/compiler/parser.rs @@ -90,12 +90,21 @@ impl<'source> Parser<'source> { } fn parse_template_statement(&mut self) -> Stmt { - self.expect_any(&*ALLOWED_TEMPLATE_NAME_TYPES, "Expected name after 'template'"); let name = self.previous.clone(); self.expect(TokenType::ParenthesisLeft, "Expected '(' after template name"); let mut arguments = vec![]; + let mut this = None; + + if self.matches(TokenType::This) { + let name = self.previous.clone(); + this = Some(name); + + if !self.check(TokenType::ParenthesisRight) { + self.expect(TokenType::Comma, "Expected ',' or ')' after 'this'"); + } + } if !self.check(TokenType::ParenthesisRight) { self.expect(TokenType::Identifier, "Expected template parameter name after '('"); @@ -119,7 +128,7 @@ impl<'source> Parser<'source> { let expr = self.parse_expression(); self.expect_statement_end(); - Stmt::Template { name, args: arguments, expr } + Stmt::Template { name, this, args: arguments, expr } } fn parse_module_statement(&mut self) -> Stmt { @@ -269,12 +278,7 @@ impl<'source> Parser<'source> { } fn finish_call(&mut self, callee: Expr) -> Expr { - match &callee { - Expr::Member { .. } => {}, - Expr::Identifier(_) => {}, - _ => - self.error(&format!("Cannot call non-identifier expression: {:?}", &callee), true), - }; + let paren_left = self.previous.clone(); let mut arguments = vec![]; @@ -291,14 +295,7 @@ impl<'source> Parser<'source> { } self.expect(TokenType::ParenthesisRight, "Expected ')' after function call arguments"); - - match callee { - Expr::Member { receiver, name } => - Expr::FunctionCall { receiver: Some(receiver), name, args: arguments }, - Expr::Identifier(name) => - Expr::FunctionCall { receiver: None, name, args: arguments }, - _ => Expr::Error, - } + Expr::FunctionCall { callee: Box::new(callee), paren_left, args: arguments } } fn parse_primary(&mut self) -> Expr { @@ -324,7 +321,7 @@ impl<'source> Parser<'source> { Expr::Error }, } - } else if self.matches(TokenType::Identifier) { + } else if self.matches(TokenType::Identifier) || self.matches(TokenType::This) { return Expr::Identifier(self.previous.clone()) } else if self.matches(TokenType::String) { return Expr::ConstantString(self.previous.source().to_owned()) @@ -332,7 +329,7 @@ impl<'source> Parser<'source> { let expr = self.parse_expression(); self.expect(TokenType::ParenthesisRight, "Expected ')' after expression"); - return Expr::Group(Box::new(expr)); + return expr; } else if self.matches(TokenType::BracketLeft) { return self.parse_object_expression(); } else if self.matches(TokenType::SquareBracketLeft) { diff --git a/test/main.densityfunction b/test/main.densityfunction index cee719d..b1e654b 100644 --- a/test/main.densityfunction +++ b/test/main.densityfunction @@ -18,7 +18,7 @@ module overworld { // function jaggedness = flat_cache(cache_2d(...)); function ridges = flat_cache(shifted_noise("minecraft:shift_x", 0.0, "minecraft:shift_z", 0.25, 0.0, "minecraft:ridge")); - function ridges_folded = -3.0 * (-0.3333333333333333 + abs(-0.6666666666666666 + abs("minecraft:overworld/ridges"))); + function ridges_folded = -3.0 * (-0.3333333333333333 + (-0.6666666666666666 + "minecraft:overworld/ridges".abs()).abs()); function sloped_cheese = 4.0 * quarter_negative( ("minecraft:overworld/depth" + "minecraft:overworld/jaggedness" * half_negative(noise("minecraft:jagged", 1500.0, 0.0))) diff --git a/test/minecraft.densityfunction b/test/minecraft.densityfunction index 662baf0..9644118 100644 --- a/test/minecraft.densityfunction +++ b/test/minecraft.densityfunction @@ -74,6 +74,22 @@ module df { // TODO template spline(...) = ... + // Member templates + template add(this, other) = add(this, other); + + template abs(this) = abs(this); + template square(this) = square(this); + template cube(this) = cube(this); + template half_negative(this) = half_negative(this); + template quarter_negative(this) = quarter_negative(this); + template squeeze(this) = squeeze(this); + + template interpolated(this) = interpolated(this); + template flat_cache(this) = flat_cache(this); + template cache_2d(this) = cache_2d(this); + template cache_once(this) = cache_once(this); + template cache_all_in_cell(this) = cache_all_in_cell(this); + // + and * operators template +(left, right) = add(left, right); template *(left, right) = mul(left, right);