From 9ae1a63719b2151a3f7ae6e38928cddbdbbd7e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Tue, 24 Oct 2023 07:50:05 +0200 Subject: [PATCH 01/17] feat: custom operators --- examples/extra_op.saturn | 14 ++- src/lua.rs | 205 ++++++++++++++++++++------------------- src/parser/ast.rs | 62 +----------- src/parser/mod.rs | 192 ++++++++++++++++-------------------- 4 files changed, 202 insertions(+), 271 deletions(-) diff --git a/examples/extra_op.saturn b/examples/extra_op.saturn index 49764fe..b77908a 100644 --- a/examples/extra_op.saturn +++ b/examples/extra_op.saturn @@ -1,12 +1,18 @@ -// Example usage of extra operators: - -fn __pipe_right(left, right) { +// Define your operators +fn __pipe_greater(left, right) { return right(left); } +fn __less_pipe(left, right) { + return left(right); +} + +// Example usage of extra operators: let r = 101 |> (a => a + 10) |> (a => a * 2) |> tostring; -print("Result = " ++ r ++ " (" ++ type(r) ++ ")"); +let kind = type <| r |> a => "(" ++ a ++ ")"; + +print("Result = " ++ r ++ " " ++ kind); diff --git a/src/lua.rs b/src/lua.rs index 7c7069d..12668c2 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -3,6 +3,42 @@ use crate::{ parser::ast::{self}, }; +fn translate_operator(ctx: code::Builder, op: String) -> code::Builder { + let ctx = ctx.put("__"); + let name = op + .into_bytes() + .iter() + .map(|ch| { + match ch { + b'+' => "plus", + b'-' => "minus", + b'*' => "times", + b'/' => "slash", + b'.' => "dot", + b'|' => "pipe", + b'>' => "greater", + b'<' => "less", + b'=' => "equals", + b'?' => "interrogation", + b'!' => "exclamation", + b'~' => "tilde", + b'%' => "percent", + b'&' => "ampersand", + b'#' => "bang", + b'$' => "dollar", + b'^' => "power", + _ => panic!( + "Error! Unexpected operator {} to be translated as a function!", + ch + ), + } + .to_owned() + }) + .collect::>() + .join("_"); + ctx.put(name) +} + pub struct LuaEmitter; impl LuaEmitter { @@ -193,18 +229,18 @@ impl code::Visitor for LuaEmitter { ctx.put(";") } ast::ClassField::Operator(f) => { - let target = match f.operator { - ast::Operator::Plus => "__add", - ast::Operator::Minus => "__sub", - ast::Operator::Product => "__mul", - ast::Operator::Quotient => "__div", - ast::Operator::Remainder => "__mod", - ast::Operator::Power => "__pow", - ast::Operator::Equal => "__eq", - ast::Operator::Less => "__lt", - ast::Operator::LessEqual => "__le", - ast::Operator::Concat => "__concat", - ast::Operator::Count => "__len", + let target = match f.operator.0.as_str() { + "+" => "__add", + "-" => "__sub", + "*" => "__mul", + "/" => "__div", + "%" => "__mod", + "**" => "__pow", + "==" => "__eq", + "<" => "__lt", + "<=" => "__le", + "++" => "__concat", + "#?" => "__len", _ => todo!( "Operator overload for {:?} operator not supported", f.operator.clone() @@ -545,93 +581,62 @@ impl code::Visitor for LuaEmitter { ctx: code::Builder, expr: &ast::BinaryExpression, ) -> Result { - // Match direct function translation first - let ctx = match expr.operator.clone() { - ast::Operator::Elastic - | ast::Operator::ElasticLeft - | ast::Operator::ElasticRight - | ast::Operator::PinguBoth - | ast::Operator::PinguLeft - | ast::Operator::PinguRight - | ast::Operator::ArrowStandBoth - | ast::Operator::ArrowStandLeft - | ast::Operator::ArrowStandRight - | ast::Operator::ArrowLeft - | ast::Operator::ArrowRight - | ast::Operator::Disjoin - | ast::Operator::PipeLeft - | ast::Operator::PipeRight - | ast::Operator::AskRight - | ast::Operator::AskLeft => { - let ctx = match expr.operator.clone() { - ast::Operator::Elastic => ctx.put("__elastic("), - ast::Operator::ElasticLeft => ctx.put("__elastic_left("), - ast::Operator::ElasticRight => ctx.put("__elastic_right("), - ast::Operator::PinguBoth => ctx.put("__pingu_both("), - ast::Operator::PinguLeft => ctx.put("__pingu_left("), - ast::Operator::PinguRight => ctx.put("__pingu_right("), - ast::Operator::ArrowStandBoth => ctx.put("__arrow_stand_both("), - ast::Operator::ArrowStandLeft => ctx.put("__arrow_stand_left("), - ast::Operator::ArrowStandRight => ctx.put("__arrow_stand_right("), - ast::Operator::BothWays => ctx.put("__both_ways("), - ast::Operator::ArrowLeft => ctx.put("__arrow_left("), - ast::Operator::ArrowRight => ctx.put("__arrow_right("), - ast::Operator::Disjoin => ctx.put("__disjoin("), - ast::Operator::PipeLeft => ctx.put("__pipe_left("), - ast::Operator::PipeRight => ctx.put("__pipe_right("), - ast::Operator::AskRight => ctx.put("__ask_right("), - ast::Operator::AskLeft => ctx.put("__ask_left("), - _ => panic!(), - }; - let ctx = self.visit_expression(ctx, &expr.left)?.put(", "); - self.visit_expression(ctx, &expr.right)?.put(")") + let op = expr.operator.0.as_str(); + // Extra logic (Indirect operation expansion) + if op == "??" { + let ctx = self.visit_expression(ctx, &expr.left)?.put(" == nil and "); + let ctx = self.visit_expression(ctx, &expr.right)?.put(" or "); + return self.visit_expression(ctx, &expr.left); + } else if op == "?:" { + let ctx = self.visit_expression(ctx, &expr.left)?.put(" or "); + return self.visit_expression(ctx, &expr.right); + } + // Translate native operators. + match op { + // Basic math + | "+" + | "-" + | "*" + | "/" + | "%" + | "**" + // Comparison + | ">" + | ">=" + | "<" + | "<=" + | "==" + | "~=" + // Logic + | "not" + | "and" + | "or" + // Logic + | "&" + | "|" + | "<<" + | "<<<" + | ">>" + | ">>>" + => { + let ctx = self.visit_expression(ctx, &expr.left)?.put(" "); + let ctx = ctx.put(op.to_owned()); + self.visit_expression(ctx.put(" "), &expr.right) } - _ => { - // Then if not, match extra logic (Indirect operation expansion) - if let ast::Operator::Coalesce = expr.operator.clone() { - let ctx = self.visit_expression(ctx, &expr.left)?.put(" == nil and "); - let ctx = self.visit_expression(ctx, &expr.right)?.put(" or "); - return self.visit_expression(ctx, &expr.left); - } else if let ast::Operator::Elvis = expr.operator.clone() { - let ctx = self.visit_expression(ctx, &expr.left)?.put(" or "); - return self.visit_expression(ctx, &expr.right); - } - // If all fails, translate native operators or panic. + "++"=> { + // Native-to-native operator translation let ctx = self.visit_expression(ctx, &expr.left)?.put(" "); - let ctx = match expr.operator.clone() { - // Basic math - ast::Operator::Plus => ctx.put("+"), - ast::Operator::Minus => ctx.put("-"), - ast::Operator::Product => ctx.put("*"), - ast::Operator::Quotient => ctx.put("/"), - ast::Operator::Remainder => ctx.put("%"), - ast::Operator::Power => ctx.put("**"), - ast::Operator::Concat => ctx.put(".."), - // Comparison - ast::Operator::Greater => ctx.put(">"), - ast::Operator::GreaterEqual => ctx.put(">="), - ast::Operator::Less => ctx.put("<"), - ast::Operator::LessEqual => ctx.put("<="), - ast::Operator::Equal => ctx.put("=="), - ast::Operator::NotEqual => ctx.put("~="), - // Logic - ast::Operator::LogicNot => ctx.put("not"), - ast::Operator::LogicAnd => ctx.put("and"), - ast::Operator::LogicOr => ctx.put("or"), - // Logic - ast::Operator::BWiseAnd => ctx.put("&"), - ast::Operator::BWiseOr => ctx.put("|"), - ast::Operator::BWiseLShift => ctx.put("<<"), - ast::Operator::BWiseLShiftRoundtrip => ctx.put("<<<"), - ast::Operator::BWiseRShift => ctx.put(">>"), - ast::Operator::BWiseRShiftRoundtrip => ctx.put(">>>"), - op => todo!("Binary operator {:?} not supported!", op), - }; - let ctx = self.visit_expression(ctx.put(" "), &expr.right)?; - return Ok(ctx); + let ctx = ctx.put("..".to_owned()); + self.visit_expression(ctx.put(" "), &expr.right) } - }; - Ok(ctx) + _=> { + // Direct function translation + let ctx = translate_operator(ctx, op.to_owned()).put("("); + let ctx = self.visit_expression(ctx, &expr.left)?.put(", "); + let ctx = self.visit_expression(ctx, &expr.right)?.put(")"); + Ok(ctx) + } + } } fn visit_unary( @@ -639,10 +644,10 @@ impl code::Visitor for LuaEmitter { ctx: code::Builder, expr: &ast::UnaryExpression, ) -> Result { - let ctx = match expr.operator.clone() { - ast::Operator::Minus => ctx.put("-"), - ast::Operator::LogicNot => ctx.put("not "), - ast::Operator::Count => ctx.put("#"), + let ctx = match expr.operator.clone().0.as_str() { + "-" => ctx.put("-"), + "not" => ctx.put("not "), + "#?" => ctx.put("#"), op => todo!("Unary operator {:?} not supported!", op), }; let ctx = self.visit_expression(ctx, &expr.expression)?; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index faf09c6..36426c3 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -195,67 +195,7 @@ pub enum ClassField { } #[derive(Debug, Clone)] -pub enum Operator { - // Arithmetic - Plus, - Minus, - Quotient, - Product, - Power, - Remainder, - Concat, - Range, - // Comparison - Equal, - Less, - LessEqual, - Greater, - GreaterEqual, - NotEqual, - Starship, - Funnel, - // Logic - LogicOr, - LogicAnd, - LogicNor, - LogicNand, - LogicXOr, - LogicNot, - // Binary - BWiseAnd, - BWiseOr, - BWiseNot, - BWiseLShift, - BWiseRShift, - BWiseLShiftRoundtrip, - BWiseRShiftRoundtrip, - // Special operators (No native equivalent for these) - Count, // Except this, in Lua. - ArrowRight, - ArrowLeft, - BothWays, - ArrowStandRight, - ArrowStandLeft, - ArrowStandBoth, - // Exclamation, - Tilde, - Disjoin, - Elastic, - ElasticRight, - ElasticLeft, - Elvis, - Coalesce, - PinguRight, - PinguLeft, - PinguBoth, - PipeRight, - PipeLeft, - AskRight, - AskLeft, - // Bolted, - Dollar, - ExclamationQuestion, -} +pub struct Operator(pub String); #[derive(Debug, Clone)] pub struct BinaryExpression { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c7b6664..99b2ccc 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -121,76 +121,56 @@ peg::parser! { { args } rule binary_expression() -> Expression = precedence! { - "-" _ expression:@ { UnaryExpression { expression, operator: Operator::Minus }.into() } - "+" _ expression:@ { UnaryExpression { expression, operator: Operator::Plus }.into() } - "#?" _ expression:@ { UnaryExpression { expression, operator: Operator::Count }.into() } - "not" _ expression:@ { UnaryExpression { expression, operator: Operator::LogicNot }.into() } - "~^" _ expression:@ { UnaryExpression { expression, operator: Operator::BWiseNot }.into() } - // "!" _ expression:@ { UnaryExpression { expression, operator: Operator::Exclamation }.into() } - "~" _ expression:@ { UnaryExpression { expression, operator: Operator::Tilde }.into() } - // "¬" _ expression:@ { UnaryExpression { expression, operator: Operator::Bolted }.into() } - "$" _ expression:@ { UnaryExpression { expression, operator: Operator::Dollar }.into() } - "!?" _ expression:@ { UnaryExpression { expression, operator: Operator::ExclamationQuestion }.into() } + value:$("-") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("+") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("#?") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("not") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("~^") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + // value:$("!" _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("~") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + // value:$("¬" _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("$") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("!?") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } -- - left:(@) _ "++" _ right:@ { BinaryExpression { left, right, operator: Operator::Concat }.into() } - left:(@) _ ".." _ right:@ { BinaryExpression { left, right, operator: Operator::Range }.into() } + left:(@) _ value:$("++") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("..") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ "+" _ right:@ { BinaryExpression { left, right, operator: Operator::Plus }.into() } - left:(@) _ "-" _ right:@ { BinaryExpression { left, right, operator: Operator::Minus }.into() } + left:(@) _ value:$("+") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("-") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } -- - left:(@) _ "*" _ right:@ { BinaryExpression { left, right, operator: Operator::Product }.into() } - left:(@) _ "/" _ right:@ { BinaryExpression { left, right, operator: Operator::Quotient }.into() } + left:(@) _ value:$("*") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("/") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } -- - left:@ _ "**" _ right:(@) { BinaryExpression { left, right, operator: Operator::Power }.into() } + left:@ _ value:$("**") _ right:(@) { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } -- - left:(@) _ "%" _ right:@ { BinaryExpression { left, right, operator: Operator::Remainder }.into() } + left:(@) _ value:$("%") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } -- - left:(@) _ ">=<" _ right:@ { BinaryExpression { left, right, operator: Operator::Funnel }.into() } - left:(@) _ ">=" _ right:@ { BinaryExpression { left, right, operator: Operator::GreaterEqual }.into() } - left:(@) _ ">" _ right:@ { BinaryExpression { left, right, operator: Operator::Greater }.into() } - left:(@) _ "<=>" _ right:@ { BinaryExpression { left, right, operator: Operator::Starship }.into() } - left:(@) _ "<=" _ right:@ { BinaryExpression { left, right, operator: Operator::LessEqual }.into() } - left:(@) _ "<>" _ right:@ { BinaryExpression { left, right, operator: Operator::NotEqual }.into() } - left:(@) _ "<" _ right:@ { BinaryExpression { left, right, operator: Operator::Less }.into() } - left:(@) _ "==" _ right:@ { BinaryExpression { left, right, operator: Operator::Equal }.into() } + left:(@) _ value:$(">=<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$(">=") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$(">") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<=>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<=") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("==") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } -- - left:(@) _ "and" _ right:@ { BinaryExpression { left, right, operator: Operator::LogicAnd }.into() } - left:(@) _ "or" _ right:@ { BinaryExpression { left, right, operator: Operator::LogicOr }.into() } - left:(@) _ "xor" _ right:@ { BinaryExpression { left, right, operator: Operator::LogicXOr }.into() } - left:(@) _ "nand" _ right:@ { BinaryExpression { left, right, operator: Operator::LogicNand }.into() } - left:(@) _ "nor" _ right:@ { BinaryExpression { left, right, operator: Operator::LogicNor }.into() } + left:(@) _ value:$("and") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("or") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("xor") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("nand") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("nor") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } -- - left:(@) _ "&" _ right:@ { BinaryExpression { left, right, operator: Operator::BWiseAnd }.into() } - left:(@) _ "|" _ right:@ { BinaryExpression { left, right, operator: Operator::BWiseOr }.into() } - left:(@) _ "<<<" _ right:@ { BinaryExpression { left, right, operator: Operator::BWiseLShiftRoundtrip }.into() } - left:(@) _ "<<" _ right:@ { BinaryExpression { left, right, operator: Operator::BWiseLShift }.into() } - left:(@) _ ">>>" _ right:@ { BinaryExpression { left, right, operator: Operator::BWiseRShiftRoundtrip }.into() } - left:(@) _ ">>" _ right:@ { BinaryExpression { left, right, operator: Operator::BWiseRShift }.into() } - // Extra logic: - left:(@) _ "^" _ right:@ { BinaryExpression { left, right, operator: Operator::LogicXOr }.into() } - left:(@) _ "¬&" _ right:@ { BinaryExpression { left, right, operator: Operator::LogicNand }.into() } - left:(@) _ "¬|" _ right:@ { BinaryExpression { left, right, operator: Operator::LogicNor }.into() } - -- - left:(@) _ "<~>" _ right:@ { BinaryExpression { left, right, operator: Operator::Elastic }.into() } - left:(@) _ "<~" _ right:@ { BinaryExpression { left, right, operator: Operator::ElasticLeft }.into() } - left:(@) _ "~>" _ right:@ { BinaryExpression { left, right, operator: Operator::ElasticRight }.into() } - left:(@) _ "<:>" _ right:@ { BinaryExpression { left, right, operator: Operator::PinguBoth }.into() } - left:(@) _ "<:" _ right:@ { BinaryExpression { left, right, operator: Operator::PinguLeft }.into() } - left:(@) _ ":>" _ right:@ { BinaryExpression { left, right, operator: Operator::PinguRight }.into() } - left:(@) _ "<-|->" _ right:@ { BinaryExpression { left, right, operator: Operator::ArrowStandBoth }.into() } - left:(@) _ "<-|" _ right:@ { BinaryExpression { left, right, operator: Operator::ArrowStandLeft }.into() } - left:(@) _ "|->" _ right:@ { BinaryExpression { left, right, operator: Operator::ArrowStandRight }.into() } - left:(@) _ "<->" _ right:@ { BinaryExpression { left, right, operator: Operator::BothWays }.into() } - left:(@) _ "<-" _ right:@ { BinaryExpression { left, right, operator: Operator::ArrowLeft }.into() } - left:(@) _ "->" _ right:@ { BinaryExpression { left, right, operator: Operator::ArrowRight }.into() } - left:(@) _ "<|>" _ right:@ { BinaryExpression { left, right, operator: Operator::Disjoin }.into() } - left:(@) _ "<|" _ right:@ { BinaryExpression { left, right, operator: Operator::PipeLeft }.into() } - left:(@) _ "|>" _ right:@ { BinaryExpression { left, right, operator: Operator::PipeRight }.into() } - left:(@) _ "" _ right:@ { BinaryExpression { left, right, operator: Operator::AskLeft }.into() } + left:(@) _ value:$("&") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("|") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<<<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$(">>>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$(">>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } -- - left:(@) _ "?:" _ right:@ { BinaryExpression { left, right, operator: Operator::Elvis }.into() } - left:(@) _ "??" _ right:@ { BinaryExpression { left, right, operator: Operator::Coalesce }.into() } + // Extra logic: + left:(@) _ value:$(['^'|'+'|'-'|'*'|'/'|'.'|'|'|'>'|'<'|'='|'?'|'!'|'~'|'%'|'&'|'#'|'$']+) _ right:@ { + BinaryExpression { left, right, operator: Operator(value.into()) }.into() + } -- e:atom() { e } } @@ -280,11 +260,11 @@ peg::parser! { { ClassField::Operator(OperatorOverload { operator, arguments, body }) } rule assign_extra() -> Operator - = "+" { Operator::Plus } - / "-" { Operator::Minus } - / "*" { Operator::Product } - / "/" { Operator::Quotient } - / "++" { Operator::Concat } + = value:$("+") { Operator(value.into()) } + / value:$("-") { Operator(value.into()) } + / value:$("*") { Operator(value.into()) } + / value:$("/") { Operator(value.into()) } + / value:$("++") { Operator(value.into()) } rule argument_list() -> Vec = "(" _ args:argument() ** (_ "," _) _ ")" { args } @@ -350,46 +330,46 @@ peg::parser! { // Special matching rule: Any Binary Operator rule any_operator() -> Operator - = "++" { Operator::Concat } - / ".." { Operator::Range } - / "+" { Operator::Plus } - / "-" { Operator::Minus } - / "*" { Operator::Product } - / "/" { Operator::Quotient } - / "**" { Operator::Power } - / "%" { Operator::Remainder } - / ">=<" { Operator::Funnel } - / ">=" { Operator::GreaterEqual } - / "<=>" { Operator::Starship } - / "<=" { Operator::LessEqual } - / "<>" { Operator::NotEqual } - / "==" { Operator::Equal } - / "and" { Operator::LogicAnd } - / "or" { Operator::LogicOr } - / "xor" { Operator::LogicXOr } - / "nand" { Operator::LogicNand } - / "nor" { Operator::LogicNor } - / "<~>" { Operator::Elastic } - / "<~" { Operator::ElasticLeft } - / "~>" { Operator::ElasticRight } - / "<:>" { Operator::PinguBoth } - / "<:" { Operator::PinguLeft } - / ":>" { Operator::PinguRight } - / "<-|->" { Operator::ArrowStandBoth } - / "<-|" { Operator::ArrowStandLeft } - / "|->" { Operator::ArrowStandRight } - / "<->" { Operator::BothWays } - / "<-" { Operator::ArrowLeft } - / "->" { Operator::ArrowRight } - / "<|>" { Operator::Disjoin } - / "<|" { Operator::PipeLeft } - / "|>" { Operator::PipeRight } - / "?>" { Operator::AskRight } - / "" { Operator::Greater } - / "<" { Operator::Less } + = value:$("++") { Operator(value.into()) } + / value:$("..") { Operator(value.into()) } + / value:$("+") { Operator(value.into()) } + / value:$("-") { Operator(value.into()) } + / value:$("*") { Operator(value.into()) } + / value:$("/") { Operator(value.into()) } + / value:$("**") { Operator(value.into()) } + / value:$("%") { Operator(value.into()) } + / value:$(">=<") { Operator(value.into()) } + / value:$(">=") { Operator(value.into()) } + / value:$("<=>") { Operator(value.into()) } + / value:$("<=") { Operator(value.into()) } + / value:$("<>") { Operator(value.into()) } + / value:$("==") { Operator(value.into()) } + / value:$("and") { Operator(value.into()) } + / value:$("or") { Operator(value.into()) } + / value:$("xor") { Operator(value.into()) } + / value:$("nand") { Operator(value.into()) } + / value:$("nor") { Operator(value.into()) } + / value:$("<~>") { Operator(value.into()) } + / value:$("<~") { Operator(value.into()) } + / value:$("~>") { Operator(value.into()) } + / value:$("<:>") { Operator(value.into()) } + / value:$("<:") { Operator(value.into()) } + / value:$(":>") { Operator(value.into()) } + / value:$("<-|->") { Operator(value.into()) } + / value:$("<-|") { Operator(value.into()) } + / value:$("|->") { Operator(value.into()) } + / value:$("<->") { Operator(value.into()) } + / value:$("<-") { Operator(value.into()) } + / value:$("->") { Operator(value.into()) } + / value:$("<|>") { Operator(value.into()) } + / value:$("<|") { Operator(value.into()) } + / value:$("|>") { Operator(value.into()) } + / value:$("?>") { Operator(value.into()) } + / value:$("") { Operator(value.into()) } + / value:$("<") { Operator(value.into()) } } } From 41d3aabf2d33a7c5413525f415a95f5b1e94f0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Tue, 24 Oct 2023 10:31:10 +0200 Subject: [PATCH 02/17] feat: added colon to operators --- src/lua.rs | 1 + src/parser/mod.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lua.rs b/src/lua.rs index 12668c2..71bdda8 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -27,6 +27,7 @@ fn translate_operator(ctx: code::Builder, op: String) -> code::Builder { b'#' => "bang", b'$' => "dollar", b'^' => "power", + b':' => "colon", _ => panic!( "Error! Unexpected operator {} to be translated as a function!", ch diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 99b2ccc..8457b1d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -168,7 +168,7 @@ peg::parser! { left:(@) _ value:$(">>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } -- // Extra logic: - left:(@) _ value:$(['^'|'+'|'-'|'*'|'/'|'.'|'|'|'>'|'<'|'='|'?'|'!'|'~'|'%'|'&'|'#'|'$']+) _ right:@ { + left:(@) _ value:$(['^'|'+'|'-'|'*'|'/'|'.'|'|'|'>'|'<'|'='|'?'|'!'|'~'|'%'|'&'|'#'|'$'|':']+) _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } -- From 40d6ba7b4f5b13752a5f0d8353f25dfdac61c8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Fri, 27 Oct 2023 14:36:50 +0200 Subject: [PATCH 03/17] feature: Operators now are customizable Added pure custom operator code generation. This means that you can now have any kind of operator, as long as it does not collide with anything existing Eg: Native operators such as ++ or * --- examples/extra_op.saturn | 11 +++-- examples/loops.saturn | 16 +++++++ src/assets/std.saturn | 94 ++++++++++++++++++++++++++++++++++++++++ src/lua.rs | 25 +++++++++-- src/parser/ast.rs | 1 + src/parser/mod.rs | 37 +++++----------- 6 files changed, 151 insertions(+), 33 deletions(-) create mode 100644 examples/loops.saturn create mode 100644 src/assets/std.saturn diff --git a/examples/extra_op.saturn b/examples/extra_op.saturn index b77908a..26a9534 100644 --- a/examples/extra_op.saturn +++ b/examples/extra_op.saturn @@ -1,13 +1,16 @@ -// Define your operators -fn __pipe_greater(left, right) { +let { operator, entries } = require('../src/assets/std'); + +@operator("|>") +fn pipe_right(left, right) { return right(left); } -fn __less_pipe(left, right) { + +@operator("<|") +fn pipe_left(left, right) { return left(right); } // Example usage of extra operators: - let r = 101 |> (a => a + 10) |> (a => a * 2) diff --git a/examples/loops.saturn b/examples/loops.saturn new file mode 100644 index 0000000..842214b --- /dev/null +++ b/examples/loops.saturn @@ -0,0 +1,16 @@ +let { entries } = require('../src/assets/std'); + +let tbl = { + a: 1, + b: true, + c: "x" +}; + +for (k, v) in entries(tbl) { + print(k, v); +} + + +for i in 1..4 { + print(i); +} diff --git a/src/assets/std.saturn b/src/assets/std.saturn new file mode 100644 index 0000000..96fb593 --- /dev/null +++ b/src/assets/std.saturn @@ -0,0 +1,94 @@ +let expand = table.unpack ?? unpack; + +fn @native entries(tbl) { + @Lua" + local iter = pairs(tbl); + return function(_, next) + local k, v = iter(tbl, next and next._0); + if k and v then + return { _0 = k, _1 = v }, v; + end + end; + "; +} + +let symbols = { + "+": "plus", + "-": "minus", + "*": "times", + "/": "slash", + ".": "dot", + "|": "pipe", + ">": "greater", + "<": "less", + "=": "equals", + "?": "interrogation", + "!": "exclamation", + "~": "tilde", + "%": "percent", + "&": "ampersand", + "#": "bang", + "$": "dollar", + "^": "power", + ":": "colon" +}; + +fn @native operator(symbol) { + @Lua" + local name = '_'; + for i=1, symbol:len() do + local s = symbol:sub(i, i); + local key = symbols[s]; + if key == nil then + error('You cannot declare an operator that contains the symbol ' .. tostring(s) .. '!'); + end + name = name .. '_' .. key; + end + return function(target) + _G[name] = target; + end; + "; +} + +// STD Custom operators + +@operator("|>") +fn pipe_right(left, right) { + return right(left); +} + +@operator("<|") +fn pipe_left(left, right) { + return left(right); +} + +@operator("..") +fn range(start, _end) { + let i = start; + if start < _end { + return () => { + if i > _end { + return (); + } + let c = i; + i += 1; + return c; + }; + } else { + return () => { + if i < _end { + return (); + } + let c = i; + i -= 1; + return c; + }; + } +} + +return { + expand, + entries, + operator, + range +}; diff --git a/src/lua.rs b/src/lua.rs index 71bdda8..1361555 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -3,6 +3,10 @@ use crate::{ parser::ast::{self}, }; +fn escape_string(str: String) -> String { + return str.replace("\n", "\\n"); +} + fn translate_operator(ctx: code::Builder, op: String) -> code::Builder { let ctx = ctx.put("__"); let name = op @@ -303,7 +307,19 @@ impl code::Visitor for LuaEmitter { ctx.put("...") }; let ctx = ctx.put(")").push(); - let ctx = self.visit_script(ctx, &stmt.body)?; + let ctx = if let Some(native) = stmt.native.clone() { + let ctx = ctx.line().put("-- NATIVE CODE"); + match native.iter().find(|(ident, _)| ident.0 == "Lua") { + Some((_, src)) => match src { + ast::StringLiteral::Double(src) => ctx.put(src), + ast::StringLiteral::Single(src) => ctx.put(src), + ast::StringLiteral::Special(_) => panic!("Not implemented"), + }, + None => ctx.put("error('Native function implementation not found')"), + } + } else { + self.visit_script(ctx, &stmt.body)? + }; let ctx = ctx.pop().unwrap().line().put("end"); let ctx = stmt.decorators.iter().fold(Ok(ctx), |ctx, dec| { let ctx = ctx?.line(); @@ -566,7 +582,7 @@ impl code::Visitor for LuaEmitter { expr: &ast::StringLiteral, ) -> Result { let ctx = match expr { - ast::StringLiteral::Double(s) => ctx.put("\"").put(s.clone()).put("\""), + ast::StringLiteral::Double(s) => ctx.put("\"").put(escape_string(s.clone())).put("\""), ast::StringLiteral::Single(s) => ctx.put("'").put(s.clone()).put("'"), ast::StringLiteral::Special(_) => todo!(), }; @@ -694,7 +710,8 @@ impl code::Visitor for LuaEmitter { self.visit_expression(ctx, &v.clone().unwrap()) } ast::TableKeyExpression::Expression(k) => { - let ctx = self.visit_expression(ctx, &k)?.put(" = "); + let ctx = ctx.put("["); + let ctx = self.visit_expression(ctx, &k)?.put("] = "); self.visit_expression(ctx, &v.clone().unwrap()) } ast::TableKeyExpression::Implicit(k) => { @@ -756,7 +773,7 @@ impl code::Visitor for LuaEmitter { ) -> Result { let ctx = match &expr.handler { ast::AssignmentTarget::Destructuring(e) => { - let ctx = ctx.line().put("for __destructuring__ in "); + let ctx = ctx.line().put("for __destructure__ in "); let ctx = self.visit_expression(ctx, &expr.target)?; let ctx = ctx.put(" do").push(); let ctx = self.generate_destructured_assignment(ctx, &e)?; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 36426c3..9ef47ba 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -17,6 +17,7 @@ pub struct Function { pub arguments: Vec, pub decorators: Vec, pub body: Script, + pub native: Option>, } #[derive(Debug, Clone)] diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 8457b1d..915b865 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -48,10 +48,14 @@ peg::parser! { / expected!("Loop") rule func() -> Function - = decorators:decorator_list() FN() __ name:identifier() _ arguments:argument_list() _ "{" body:script() "}" - { Function { name, decorators, body, arguments } } + = decorators:decorator_list() FN() __ NATIVE() __ name:identifier() _ arguments:argument_list() + _ "{" native:(__ "@" name:identifier() _ source:string_literal() _ EOS() { (name, source) })+ __ "}" + { Function { name, decorators, body: Script { statements: vec![] }, arguments, native: Some(native) } } + / + decorators:decorator_list() FN() __ name:identifier() _ arguments:argument_list() _ "{" body:script() "}" + { Function { name, decorators, body, arguments, native: None } } / decorators:decorator_list() FN() __ name:identifier() _ arguments:argument_list() _ "{" _ "}" - { Function { name, decorators, body: Script { statements: vec![] }, arguments } } + { Function { name, decorators, body: Script { statements: vec![] }, arguments, native: None } } / expected!("Function declaration") rule class() -> Class @@ -299,6 +303,8 @@ peg::parser! { rule table_kv_pair() -> (TableKeyExpression, Option) = k:identifier() _ ":" _ v:expression() { (TableKeyExpression::Identifier(k), Some(v)) } + / k:string_expression() _ ":" _ v:expression() + { (TableKeyExpression::Expression(k), Some(v)) } / "[" _ k:expression() _ "]" _ ":" _ v:expression() { (TableKeyExpression::Expression(k), Some(v)) } / k:identifier() @@ -316,13 +322,14 @@ peg::parser! { rule CLASS() = "class" rule END() = "end" rule FN() = "fn" + rule NATIVE() = "@native" rule ANY() = quiet!{ [_] } / expected!("Any character") rule BLANK() = ['\t'|' '] / expected!("White space") rule WS() = BLANK() / LINE_COMMENT() / BLOCK_COMMENT() / EOL() rule LINE_COMMENT() = quiet!{ "//" (!EOL() ANY())* EOL() } / expected!("Line comment") rule BLOCK_COMMENT() = quiet!{ "/*" (!"*/" ANY())* "*/" } / expected!("Block comment") rule EOL() = quiet!{ ['\r'|'\n'] } / expected!("End of line") - rule EOS() = quiet!{ EOL() / ";" } / expected!("End of statement") + rule EOS() = quiet!{ ";" } / expected!("End of statement") //quiet!{ EOL() / ";" } / expected!("End of statement") rule ALPHA() = quiet!{ ['A'..='Z'|'a'..='z'|'_'] } / expected!("Alphanumeric") rule DIGIT() = quiet!{ ['0'..='9'] } / expected!("Digit") rule _ = WS()* @@ -349,27 +356,7 @@ peg::parser! { / value:$("xor") { Operator(value.into()) } / value:$("nand") { Operator(value.into()) } / value:$("nor") { Operator(value.into()) } - / value:$("<~>") { Operator(value.into()) } - / value:$("<~") { Operator(value.into()) } - / value:$("~>") { Operator(value.into()) } - / value:$("<:>") { Operator(value.into()) } - / value:$("<:") { Operator(value.into()) } - / value:$(":>") { Operator(value.into()) } - / value:$("<-|->") { Operator(value.into()) } - / value:$("<-|") { Operator(value.into()) } - / value:$("|->") { Operator(value.into()) } - / value:$("<->") { Operator(value.into()) } - / value:$("<-") { Operator(value.into()) } - / value:$("->") { Operator(value.into()) } - / value:$("<|>") { Operator(value.into()) } - / value:$("<|") { Operator(value.into()) } - / value:$("|>") { Operator(value.into()) } - / value:$("?>") { Operator(value.into()) } - / value:$("") { Operator(value.into()) } - / value:$("<") { Operator(value.into()) } + / value:$(['^'|'+'|'-'|'*'|'/'|'.'|'|'|'>'|'<'|'='|'?'|'!'|'~'|'%'|'&'|'#'|'$'|':']+) { Operator(value.into()) } } } From 5ffd57a1a49b201bbe7bb416de6bb5c8d92b02bf Mon Sep 17 00:00:00 2001 From: sigmasoldi3r Date: Mon, 30 Oct 2023 19:00:05 +0100 Subject: [PATCH 04/17] fix: operator precedence & not equal code generation --- examples/loops.saturn | 3 ++- src/lua.rs | 11 ++++++++--- src/parser/mod.rs | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/loops.saturn b/examples/loops.saturn index 842214b..e46e262 100644 --- a/examples/loops.saturn +++ b/examples/loops.saturn @@ -6,11 +6,12 @@ let tbl = { c: "x" }; +// For-each for (k, v) in entries(tbl) { print(k, v); } - +// Range based for for i in 1..4 { print(i); } diff --git a/src/lua.rs b/src/lua.rs index 1361555..089019b 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -623,7 +623,6 @@ impl code::Visitor for LuaEmitter { | "<" | "<=" | "==" - | "~=" // Logic | "not" | "and" @@ -640,13 +639,19 @@ impl code::Visitor for LuaEmitter { let ctx = ctx.put(op.to_owned()); self.visit_expression(ctx.put(" "), &expr.right) } - "++"=> { + "++" => { // Native-to-native operator translation let ctx = self.visit_expression(ctx, &expr.left)?.put(" "); let ctx = ctx.put("..".to_owned()); self.visit_expression(ctx.put(" "), &expr.right) } - _=> { + "<>" => { + // Native-to-native operator translation + let ctx = self.visit_expression(ctx, &expr.left)?.put(" "); + let ctx = ctx.put("~=".to_owned()); + self.visit_expression(ctx.put(" "), &expr.right) + } + _ => { // Direct function translation let ctx = translate_operator(ctx, op.to_owned()).put("("); let ctx = self.visit_expression(ctx, &expr.left)?.put(", "); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 915b865..3bd5aad 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -264,11 +264,11 @@ peg::parser! { { ClassField::Operator(OperatorOverload { operator, arguments, body }) } rule assign_extra() -> Operator - = value:$("+") { Operator(value.into()) } + = value:$("++") { Operator(value.into()) } + / value:$("+") { Operator(value.into()) } / value:$("-") { Operator(value.into()) } / value:$("*") { Operator(value.into()) } / value:$("/") { Operator(value.into()) } - / value:$("++") { Operator(value.into()) } rule argument_list() -> Vec = "(" _ args:argument() ** (_ "," _) _ ")" { args } From 4923c2d49abba170b1c7a0f06df30704f581a060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Mon, 30 Oct 2023 19:03:56 +0100 Subject: [PATCH 05/17] feat: added oop example --- examples/oop.saturn | 19 ++++++++++++++++ src/assets/std.saturn | 52 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 examples/oop.saturn diff --git a/examples/oop.saturn b/examples/oop.saturn new file mode 100644 index 0000000..0c3a784 --- /dev/null +++ b/examples/oop.saturn @@ -0,0 +1,19 @@ +// Example of OOP possible uses with std +let { abstract, entries } = require("../src/assets/std"); + +class Simple {} + +class TraitLike { + @abstract() fn foo(self) {} +} + +let a = TraitLike {}; +a.foo(); + +let x = [1, 2, 3]; +let y = [4, 5, 6]; +let z = x ::: y; + +for (k, v) in entries(z) { + print(k ++ " => " ++ v); +} diff --git a/src/assets/std.saturn b/src/assets/std.saturn index 96fb593..9a12855 100644 --- a/src/assets/std.saturn +++ b/src/assets/std.saturn @@ -1,5 +1,7 @@ let expand = table.unpack ?? unpack; +// Collections and iterators + fn @native entries(tbl) { @Lua" local iter = pairs(tbl); @@ -12,6 +14,16 @@ fn @native entries(tbl) { "; } +// OOP and inheritance + +fn abstract() { + return (target, name, host) => { + print("Abstract!", target, name); + }; +} + +// Operators & meta + let symbols = { "+": "plus", "-": "minus", @@ -62,6 +74,43 @@ fn pipe_left(left, right) { return left(right); } +@operator(":::") +fn @native merge(left, right) { + @Lua" + local u = table.unpack or unpack; + local tbl = { u(left) }; + for _, v in pairs(right) do + tbl[#tbl + 1] = v; + end + return tbl; + "; +} + +@operator("+?") +fn maybe_sum(left, right) { + return left and (left + right) or right; +} + +@operator("++?") +fn maybe_concat(left, right) { + return left and (left ++ right) or right; +} + +@operator("-?") +fn maybe_subtract(left, right) { + return left and (left - right) or right; +} + +@operator("*?") +fn maybe_times(left, right) { + return left and (left * right) or right; +} + +@operator("/?") +fn maybe_divide(left, right) { + return left and (left / right) or right; +} + @operator("..") fn range(start, _end) { let i = start; @@ -90,5 +139,6 @@ return { expand, entries, operator, - range + range, + abstract }; From 4d92e37534a8df110b9e6ee06be94b96b63097e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Tue, 31 Oct 2023 18:10:49 +0100 Subject: [PATCH 06/17] feat: added code generation for class decorator --- examples/oop.saturn | 24 +++++++++++++++++++++--- src/assets/std.saturn | 27 ++++++++++++++++++++++++--- src/lua.rs | 37 +++++++++++++++++++++++++++---------- 3 files changed, 72 insertions(+), 16 deletions(-) diff --git a/examples/oop.saturn b/examples/oop.saturn index 0c3a784..a5e4651 100644 --- a/examples/oop.saturn +++ b/examples/oop.saturn @@ -1,14 +1,32 @@ // Example of OOP possible uses with std -let { abstract, entries } = require("../src/assets/std"); +let { abstract, entries, inherit, trait } = require("../src/assets/std"); class Simple {} +@trait() class TraitLike { @abstract() fn foo(self) {} } -let a = TraitLike {}; -a.foo(); +@impl(TraitLike) +class Implementor { + fn me(self) { + return "Trait-like's implementation"; + } + fn foo(self) { + print("Foo like " ++ self.me()); + } +} + +@inherit(Implementor) +class Child { + fn me(self) { + return "The child"; + } +} + +let child = Child {}; +let imp = Implementor {}; let x = [1, 2, 3]; let y = [4, 5, 6]; diff --git a/src/assets/std.saturn b/src/assets/std.saturn index 9a12855..b9de4af 100644 --- a/src/assets/std.saturn +++ b/src/assets/std.saturn @@ -14,11 +14,31 @@ fn @native entries(tbl) { "; } +// Runtime + +fn @native panic(message) { + @Lua" + error(message); + "; +} + // OOP and inheritance +fn trait() {} + +fn impl(trait) {} + +fn inherit(parent) {} + +let makePure = (name) => () => panic("Attempting to call abstract method " ++ name ++ "!"); + fn abstract() { - return (target, name, host) => { - print("Abstract!", target, name); + return (_, name, host, label, meta) => { + if meta.is_static { + host[name] = makePure(label ++ "::" ++ name); + } else { + host.prototype[name] = makePure(label ++ "." ++ name); + } }; } @@ -140,5 +160,6 @@ return { entries, operator, range, - abstract + abstract, + panic }; diff --git a/src/lua.rs b/src/lua.rs index 089019b..e679ae6 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -191,16 +191,12 @@ impl code::Visitor for LuaEmitter { let ctx = ctx?.line(); let ctx = match field { ast::ClassField::Method(f) => { - let level = if let Some(first) = f.arguments.first() { - if first.name.0 == "self" { - ".prototype." - } else { - "." - } + let is_self = if let Some(first) = f.arguments.first() { + first.name.0 == "self" } else { - "." - } - .to_string(); + false + }; + let level = if is_self { ".prototype." } else { "." }.to_string(); let ctx = ctx .put(stmt.name.0.clone()) .put(level) @@ -213,7 +209,26 @@ impl code::Visitor for LuaEmitter { body: ast::ScriptOrExpression::Script(f.body.clone()), }, )?; - ctx.put(";") + let ctx = ctx.put(";"); + let ctx = f.decorators.iter().fold(Ok(ctx), |ctx, dec| { + let ctx = ctx?.line(); + let ctx = self.visit_call(ctx, &dec.target)?; + let fn_ref = if is_self { + format!("{}.prototype.{}", stmt.name.0.clone(), f.name.0.clone()) + } else { + format!("{}.{}", stmt.name.0.clone(), f.name.0.clone()) + }; + let ctx = ctx.put(format!( + "({}, \"{}\", {}, \"{}\", {{ is_static = {} }});", + fn_ref, + f.name.0.clone(), + stmt.name.0.clone(), + stmt.name.0.clone(), + !is_self + )); + Ok(ctx) + })?; + ctx } ast::ClassField::Let(f) => { let ctx = match &f.target { @@ -317,6 +332,8 @@ impl code::Visitor for LuaEmitter { }, None => ctx.put("error('Native function implementation not found')"), } + .line() + .put("-- NATIVE CODE") } else { self.visit_script(ctx, &stmt.body)? }; From 021db61f3998abbd6d8cc9b969c96012681e31c3 Mon Sep 17 00:00:00 2001 From: sigmasoldi3r Date: Tue, 31 Oct 2023 19:32:50 +0100 Subject: [PATCH 07/17] fixed decorators on class --- .gitignore | 1 + examples/oop.saturn | 6 ++++-- src/assets/std.saturn | 38 +++++++++++++++++++++++++++++++++++--- src/lua.rs | 20 ++++++++++---------- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index b83d222..f471b72 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target/ +/src/assets/*.lua diff --git a/examples/oop.saturn b/examples/oop.saturn index a5e4651..f7489f4 100644 --- a/examples/oop.saturn +++ b/examples/oop.saturn @@ -1,5 +1,5 @@ // Example of OOP possible uses with std -let { abstract, entries, inherit, trait } = require("../src/assets/std"); +let { abstract, entries, mixin, trait, impl } = require("../src/assets/std"); class Simple {} @@ -18,7 +18,7 @@ class Implementor { } } -@inherit(Implementor) +@mixin(Implementor) class Child { fn me(self) { return "The child"; @@ -27,6 +27,8 @@ class Child { let child = Child {}; let imp = Implementor {}; +imp.foo(); +child.foo(); let x = [1, 2, 3]; let y = [4, 5, 6]; diff --git a/src/assets/std.saturn b/src/assets/std.saturn index b9de4af..0901e09 100644 --- a/src/assets/std.saturn +++ b/src/assets/std.saturn @@ -24,11 +24,40 @@ fn @native panic(message) { // OOP and inheritance -fn trait() {} +let makeTrait = (name) => () => panic("Cannot construct trait " ++ name ++ "!"); -fn impl(trait) {} +fn trait() { + return (target, name) => { + target.__meta__.__call = makeTrait(name); + }; +} + +fn impl(trait) { + return (target, name) => { + let map = {}; + for (k, v) in entries(target.prototype) { + print("Foo",k,v); + map[k] = v; + } + for (k, v) in entries(trait.prototype) { + if map[k] == () { + panic(name ++ " must implement trait method " ++ k ++ "!"); + } + } + }; +} -fn inherit(parent) {} +fn mixin(parent) { + let ctor = parent.prototype.__meta__.__index; + if type(ctor) == "table" { + ctor = (key) => parent.prototype[key]; + } + return (target, name) => { + target.prototype.__meta__.__index = (key) => { + return target.prototype or ctor(key); + }; + }; +} let makePure = (name) => () => panic("Attempting to call abstract method " ++ name ++ "!"); @@ -161,5 +190,8 @@ return { operator, range, abstract, + trait, + impl, + mixin, panic }; diff --git a/src/lua.rs b/src/lua.rs index e679ae6..88a2352 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -281,16 +281,16 @@ impl code::Visitor for LuaEmitter { ctx.put(";") } }; - let ctx = stmt.decorators.iter().fold(Ok(ctx), |ctx, dec| { - let ctx = ctx?.line(); - let ctx = self.visit_call(ctx, &dec.target)?; - let ctx = ctx.put(format!( - "({}, \"{}\");", - stmt.name.0.clone(), - stmt.name.0.clone() - )); - Ok(ctx) - })?; + Ok(ctx) + })?; + let ctx = stmt.decorators.iter().fold(Ok(ctx), |ctx, dec| { + let ctx = ctx?.line(); + let ctx = self.visit_call(ctx, &dec.target)?; + let ctx = ctx.put(format!( + "({}, \"{}\");", + stmt.name.0.clone(), + stmt.name.0.clone() + )); Ok(ctx) })?; Ok(ctx) From 5ada60e37d59cc39e0f982296ce1b68a5d398b6e Mon Sep 17 00:00:00 2001 From: sigmasoldi3r Date: Tue, 31 Oct 2023 22:04:55 +0100 Subject: [PATCH 08/17] wip: macro code generation --- src/lua.rs | 6 ++++++ src/parser/ast.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/lua.rs b/src/lua.rs index 88a2352..18db84a 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -518,6 +518,12 @@ impl code::Visitor for LuaEmitter { ctx }; let ctx = ctx.put("("); + if expr.head.is_macro { + + let ctx = ctx.put(")"); + return Ok(ctx); + } + // If not macro let ctx = if let Some(first) = expr.head.arguments.first() { self.visit_expression(ctx, first)? } else { diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 9ef47ba..6e9b53e 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -26,6 +26,11 @@ pub struct Lambda { pub body: ScriptOrExpression, } +#[derive(Debug, Clone)] +pub struct Do { + pub body: Script +} + #[derive(Debug, Clone)] pub struct Tuple(pub Vec); @@ -254,6 +259,7 @@ pub enum Expression { Tuple(Tuple), Tuple1(Box), Table(Table), + Do(Do), Vector(Vector), Number(Number), String(StringLiteral), From ca411f1aa1fd8d005cf431522590fe620a1627d4 Mon Sep 17 00:00:00 2001 From: sigmasoldi3r Date: Tue, 31 Oct 2023 22:05:21 +0100 Subject: [PATCH 09/17] wip: do expressions --- src/parser/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 3bd5aad..a993322 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -114,6 +114,7 @@ peg::parser! { / vector_expression() / tuple_expression() / lambda_expression() + // / do_expression() / enclosed_expression() rule call_arguments() -> Vec @@ -195,6 +196,7 @@ peg::parser! { rule string_expression() -> Expression = e:string_literal() { Expression::String(e) } rule number_expression() -> Expression = e:number_literal() { Expression::Number(e) } rule lambda_expression() -> Expression = e:lambda_literal() { Expression::Lambda(Box::new(e)) } + // rule do_expression() -> Expression = e:do_literal() { Expression::Do(Box::new(e)) } rule vector_expression() -> Expression = e:vector_literal() { Expression::Vector(e) } rule table_expression() -> Expression = e:table_literal() { Expression::Table(e) } rule tuple_expression() -> Expression = e:tuple_literal() { Expression::Tuple(e) } From e52332cbfa634d28ae1ecaeb7aeeb3b153d5492f Mon Sep 17 00:00:00 2001 From: sigmasoldi3r Date: Wed, 1 Nov 2023 20:54:05 +0100 Subject: [PATCH 10/17] added do-block syntax --- examples/basic.saturn | 44 +++++++++++++++++++++++++++++++++++++ examples/collections.saturn | 9 ++++++++ examples/oop.saturn | 8 ------- src/code.rs | 2 ++ src/lua.rs | 8 ++++++- src/parser/mod.rs | 15 +++++++++---- 6 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 examples/basic.saturn create mode 100644 examples/collections.saturn diff --git a/examples/basic.saturn b/examples/basic.saturn new file mode 100644 index 0000000..e9ba149 --- /dev/null +++ b/examples/basic.saturn @@ -0,0 +1,44 @@ +// Basic examples + +// This is a comment +/* + this also is. +*/ + +// Declare variables +let some_var = "foo"; + +// Mutate them +some_var = 5; +some_var += 1; +print(some_var); + +// Declare functions +fn the_foo() { + return "bar"; +} + +// Use them +let answer = the_foo(); +print(answer); + +// Conditions +if true { + print("Of course"); +} else { + print("Never"); +} + +// Do-block +{ + let this_is = "isolated from the rest"; + print("of_the_code"); +} + +// Can be used as an expression too! +let my_var = "Foo"; +let result = { + let my_var = "another one"; + return my_var ++ "."; +}; +print(result, my_var); diff --git a/examples/collections.saturn b/examples/collections.saturn new file mode 100644 index 0000000..1ba7276 --- /dev/null +++ b/examples/collections.saturn @@ -0,0 +1,9 @@ +require("../src/assets/std"); + +let x = [1, 2, 3]; +let y = [4, 5, 6]; +let z = x ::: y; + +for (k, v) in entries(z) { + print(k ++ " => " ++ v); +} diff --git a/examples/oop.saturn b/examples/oop.saturn index f7489f4..a0b0a5f 100644 --- a/examples/oop.saturn +++ b/examples/oop.saturn @@ -29,11 +29,3 @@ let child = Child {}; let imp = Implementor {}; imp.foo(); child.foo(); - -let x = [1, 2, 3]; -let y = [4, 5, 6]; -let z = x ::: y; - -for (k, v) in entries(z) { - print(k ++ " => " ++ v); -} diff --git a/src/code.rs b/src/code.rs index 0a2de27..8c95757 100644 --- a/src/code.rs +++ b/src/code.rs @@ -31,6 +31,7 @@ pub trait Visitor { fn visit_match(&self, ctx: T, expr: &Match) -> Result; fn visit_1tuple(&self, ctx: T, expr: &Expression) -> Result; fn visit_identifier(&self, ctx: T, expr: &Identifier) -> Result; + fn visit_do(&self, ctx: T, expr: &Do) -> Result; fn enter_script(&self, ctx: T, _script: &Script) -> Result { Ok(ctx) } @@ -54,6 +55,7 @@ pub trait Visitor { Expression::Vector(e) => self.visit_vector(ctx, e), Expression::Tuple1(e) => self.visit_1tuple(ctx, e), Expression::Identifier(e) => self.visit_identifier(ctx, e), + Expression::Do(e) => self.visit_do(ctx, e), } } fn visit_script(&self, ctx: T, script: &Script) -> Result { diff --git a/src/lua.rs b/src/lua.rs index 18db84a..cac9891 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -128,6 +128,13 @@ impl code::Visitor for LuaEmitter { Ok(ctx.put(";")) } + fn visit_do(&self, ctx: code::Builder, expr: &ast::Do) -> Result { + let ctx = ctx.put("(function(...)").push(); + let ctx = self.visit_script(ctx, &expr.body)?; + let ctx = ctx.pop().unwrap().line().put("end)(...)"); + Ok(ctx) + } + fn visit_1tuple( &self, ctx: code::Builder, @@ -519,7 +526,6 @@ impl code::Visitor for LuaEmitter { }; let ctx = ctx.put("("); if expr.head.is_macro { - let ctx = ctx.put(")"); return Ok(ctx); } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a993322..63d860d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -19,6 +19,7 @@ peg::parser! { / e:declare_var() { Statement::Let(e) } / e:assignment() { Statement::Assignment(e) } / e:return_stmt() { Statement::Return(e) } + / e:do_expression() { Statement::Expression(e) } / e:expression() _ EOS() { Statement::Expression(e) } rule if_stmt() -> If @@ -114,7 +115,7 @@ peg::parser! { / vector_expression() / tuple_expression() / lambda_expression() - // / do_expression() + / do_expression() / enclosed_expression() rule call_arguments() -> Vec @@ -188,6 +189,7 @@ peg::parser! { / vector_expression() / table_expression() / tuple_expression() + / do_expression() / e:member_expression() { Expression::Reference(Box::new(e)) } / unit() { Expression::Unit } / enclosed_expression() @@ -196,10 +198,10 @@ peg::parser! { rule string_expression() -> Expression = e:string_literal() { Expression::String(e) } rule number_expression() -> Expression = e:number_literal() { Expression::Number(e) } rule lambda_expression() -> Expression = e:lambda_literal() { Expression::Lambda(Box::new(e)) } - // rule do_expression() -> Expression = e:do_literal() { Expression::Do(Box::new(e)) } rule vector_expression() -> Expression = e:vector_literal() { Expression::Vector(e) } rule table_expression() -> Expression = e:table_literal() { Expression::Table(e) } rule tuple_expression() -> Expression = e:tuple_literal() { Expression::Tuple(e) } + rule do_expression() -> Expression = e:do_literal() { Expression::Do(e) } rule enclosed_expression() -> Expression = "(" _ e:expression() _ ")" { Expression::Tuple1(Box::new(e)) } @@ -242,6 +244,11 @@ peg::parser! { { Tuple(e) } / expected!("Tuple literal") + rule do_literal() -> Do + = "{" body:script() "}" + { Do { body } } + / expected!("Do block") + // Auxiliaries and sub-expressions rule let_expression() -> Let = "let" __ target:assignment_target() value:(_ "=" _ e:expression(){e})? @@ -296,10 +303,10 @@ peg::parser! { = "(" _ e:comma_expr() _ ")" { e } rule comma_expr() -> Vec - = e:expression() ** (_ "," _) { e } + = e:expression() ** (_ "," _) (_ "," _)? { e } rule table_kvs() -> Vec<(TableKeyExpression, Option)> - = kv:table_kv_pair() ** (_ "," _) + = kv:table_kv_pair() ** (_ "," _) (_ "," _)? { kv } rule table_kv_pair() -> (TableKeyExpression, Option) From e5c00f945bf42206c1e63d3b068825e4dccb5007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Thu, 2 Nov 2023 09:17:52 +0100 Subject: [PATCH 11/17] fixed mixin decorator --- examples/oop.saturn | 20 ++++++++++++++++++-- src/assets/std.saturn | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/examples/oop.saturn b/examples/oop.saturn index a0b0a5f..2caf9de 100644 --- a/examples/oop.saturn +++ b/examples/oop.saturn @@ -11,10 +11,10 @@ class TraitLike { @impl(TraitLike) class Implementor { fn me(self) { - return "Trait-like's implementation"; + return "Implementor"; } fn foo(self) { - print("Foo like " ++ self.me()); + print("The foo be like: " ++ self.me()); } } @@ -25,7 +25,23 @@ class Child { } } +class BeMe { + fn bar(self) { + return "Amogus"; + } +} + +@mixin(BeMe) +@mixin(Child) +class MonsterJack { + fn me(self) { + return "Sus town? " ++ self.bar(); + } +} + let child = Child {}; let imp = Implementor {}; +let monster = MonsterJack {}; imp.foo(); child.foo(); +monster.foo(); diff --git a/src/assets/std.saturn b/src/assets/std.saturn index 0901e09..4b4f5c1 100644 --- a/src/assets/std.saturn +++ b/src/assets/std.saturn @@ -24,7 +24,7 @@ fn @native panic(message) { // OOP and inheritance -let makeTrait = (name) => () => panic("Cannot construct trait " ++ name ++ "!"); +let makeTrait = (name) => () => panic("Trait " ++ name ++ " is not constructible!"); fn trait() { return (target, name) => { @@ -36,7 +36,6 @@ fn impl(trait) { return (target, name) => { let map = {}; for (k, v) in entries(target.prototype) { - print("Foo",k,v); map[k] = v; } for (k, v) in entries(trait.prototype) { @@ -48,13 +47,18 @@ fn impl(trait) { } fn mixin(parent) { - let ctor = parent.prototype.__meta__.__index; - if type(ctor) == "table" { - ctor = (key) => parent.prototype[key]; + let index = parent.prototype.__meta__.__index; + if type(index) == "table" { + index = (self, key) => parent.prototype.__meta__.__index[key]; } return (target, name) => { - target.prototype.__meta__.__index = (key) => { - return target.prototype or ctor(key); + let prev = target.prototype.__meta__.__index; + if type(prev) == "table" { + let prev_tbl = prev; + prev = (self, key) => prev_tbl[key]; + } + target.prototype.__meta__.__index = (self, key) => { + return prev(self, key) or index(self, key); }; }; } @@ -137,26 +141,46 @@ fn @native merge(left, right) { @operator("+?") fn maybe_sum(left, right) { + return left and (left + right); +} +@operator("+??") +fn maybe_sum_or(left, right) { return left and (left + right) or right; } @operator("++?") fn maybe_concat(left, right) { + return left and (left ++ right); +} +@operator("++??") +fn maybe_concat_or(left, right) { return left and (left ++ right) or right; } @operator("-?") fn maybe_subtract(left, right) { + return left and (left - right); +} +@operator("-??") +fn maybe_subtract_or(left, right) { return left and (left - right) or right; } @operator("*?") fn maybe_times(left, right) { + return left and (left * right); +} +@operator("*??") +fn maybe_times_or(left, right) { return left and (left * right) or right; } @operator("/?") fn maybe_divide(left, right) { + return left and (left / right); +} +@operator("/??") +fn maybe_divide_or(left, right) { return left and (left / right) or right; } From 034400e30cc6d1c7bd47d31dd1b260541d142f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Thu, 2 Nov 2023 09:21:39 +0100 Subject: [PATCH 12/17] added more examples --- examples/oop.saturn | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/examples/oop.saturn b/examples/oop.saturn index 2caf9de..2dcd390 100644 --- a/examples/oop.saturn +++ b/examples/oop.saturn @@ -1,11 +1,31 @@ // Example of OOP possible uses with std let { abstract, entries, mixin, trait, impl } = require("../src/assets/std"); -class Simple {} +// Simple abstract class example. +class SimpleAbstract { + @abstract() fn example(self) {} + fn inherited(self) { + print("This was inherited " ++ self.name); + } +} + +@mixin(SimpleAbstract) +class Simple { + fn example(self) { + print("Won't blow."); + } + fn run() { + let example = Simple { name: "Simple example" }; + example.example(); + example.inherited(); + } +} +Simple::run(); +// Trait example @trait() class TraitLike { - @abstract() fn foo(self) {} + fn foo(self) {} } @impl(TraitLike) @@ -18,6 +38,7 @@ class Implementor { } } +// Mixin examples @mixin(Implementor) class Child { fn me(self) { From 7d2736ad13a24c86739ab761a08d661469ffe36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Thu, 2 Nov 2023 11:43:11 +0100 Subject: [PATCH 13/17] featuring sub packages --- src/code/builder.rs | 102 ++++++++++++++++++++++++++++++ src/code/mod.rs | 7 +++ src/{code.rs => code/visitor.rs} | 103 ------------------------------- src/{lua.rs => lua/mod.rs} | 0 4 files changed, 109 insertions(+), 103 deletions(-) create mode 100644 src/code/builder.rs create mode 100644 src/code/mod.rs rename src/{code.rs => code/visitor.rs} (63%) rename src/{lua.rs => lua/mod.rs} (100%) diff --git a/src/code/builder.rs b/src/code/builder.rs new file mode 100644 index 0000000..da5a735 --- /dev/null +++ b/src/code/builder.rs @@ -0,0 +1,102 @@ +pub struct UnevenIndentationError; +impl std::fmt::Debug for UnevenIndentationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Uneven Indentation: Attempting to pop furhter than 0!") + .finish() + } +} + +/// # String code builder +/// +/// This simple immutable builder accounts for raw strings (So it is agnostic +/// from the targetted output), but retains indentation aspects. +/// +/// This means that you have an indentation stack, with it's state retained +/// between calls, without having to store it in your code emitter. +/// +/// Each call, consumes the builder and returns an extended version of it. +/// If you want to preserve the state, clone the structure by calling `.clone()` +/// explicitly. +/// +/// Example: +/// ```rs +/// let out = Builder::new(" ") +/// .put("hello") +/// .push().line() +/// .put("my") +/// .pop().unwrap().line() +/// .put("world!") +/// .collect() +/// ``` +/// Yields: +/// ``` +/// hello +/// my +/// world +/// ``` +#[derive(Clone)] +pub struct Builder { + level: u16, + indent: String, + buffer: String, +} +impl Builder { + pub fn new(indent: T) -> Self + where + T: Into, + { + Builder { + level: 0, + indent: indent.into(), + buffer: Default::default(), + } + } + pub fn collect(self) -> String { + self.buffer + } + pub fn push(self) -> Self { + Builder { + level: self.level + 1, + ..self + } + } + pub fn pop(self) -> Result { + if self.level == 0 { + Err(UnevenIndentationError) + } else { + Ok(Builder { + level: self.level - 1, + ..self + }) + } + } + pub fn put(self, fragment: T) -> Self + where + T: Into, + { + Builder { + buffer: format!("{}{}", self.buffer, fragment.into()), + ..self + } + } + pub fn and(self, other: Self) -> Self { + Builder { + buffer: format!("{}{}", self.buffer, other.buffer), + ..self + } + } + pub fn line(self) -> Self { + Builder { + buffer: format!("{}\n{}", self.buffer, self.indent.repeat(self.level.into())), + ..self + } + } + /// Creates a new copy of this builder, but with empty buffer. + pub fn clone_like(&self) -> Self { + Builder { + level: self.level, + indent: self.indent.clone(), + buffer: "".to_string(), + } + } +} diff --git a/src/code/mod.rs b/src/code/mod.rs new file mode 100644 index 0000000..e881520 --- /dev/null +++ b/src/code/mod.rs @@ -0,0 +1,7 @@ +mod builder; +mod visitor; + +pub use builder::Builder; +pub use visitor::{VisitError, Visitor}; + +pub trait BuilderVisitor: Visitor {} diff --git a/src/code.rs b/src/code/visitor.rs similarity index 63% rename from src/code.rs rename to src/code/visitor.rs index 8c95757..21e3d8b 100644 --- a/src/code.rs +++ b/src/code/visitor.rs @@ -79,106 +79,3 @@ pub trait Visitor { self.exit_script(ctx, script) } } - -pub struct UnevenIndentationError; -impl std::fmt::Debug for UnevenIndentationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Uneven Indentation: Attempting to pop furhter than 0!") - .finish() - } -} - -/// # String code builder -/// -/// This simple immutable builder accounts for raw strings (So it is agnostic -/// from the targetted output), but retains indentation aspects. -/// -/// This means that you have an indentation stack, with it's state retained -/// between calls, without having to store it in your code emitter. -/// -/// Each call, consumes the builder and returns an extended version of it. -/// If you want to preserve the state, clone the structure by calling `.clone()` -/// explicitly. -/// -/// Example: -/// ```rs -/// let out = Builder::new(" ") -/// .put("hello") -/// .push().line() -/// .put("my") -/// .pop().unwrap().line() -/// .put("world!") -/// .collect() -/// ``` -/// Yields: -/// ``` -/// hello -/// my -/// world -/// ``` -#[derive(Clone)] -pub struct Builder { - level: u16, - indent: String, - buffer: String, -} -impl Builder { - pub fn new(indent: T) -> Self - where - T: Into, - { - Builder { - level: 0, - indent: indent.into(), - buffer: Default::default(), - } - } - pub fn collect(self) -> String { - self.buffer - } - pub fn push(self) -> Self { - Builder { - level: self.level + 1, - ..self - } - } - pub fn pop(self) -> Result { - if self.level == 0 { - Err(UnevenIndentationError) - } else { - Ok(Builder { - level: self.level - 1, - ..self - }) - } - } - pub fn put(self, fragment: T) -> Self - where - T: Into, - { - Builder { - buffer: format!("{}{}", self.buffer, fragment.into()), - ..self - } - } - pub fn and(self, other: Self) -> Self { - Builder { - buffer: format!("{}{}", self.buffer, other.buffer), - ..self - } - } - pub fn line(self) -> Self { - Builder { - buffer: format!("{}\n{}", self.buffer, self.indent.repeat(self.level.into())), - ..self - } - } - /// Creates a new copy of this builder, but with empty buffer. - pub fn clone_like(&self) -> Self { - Builder { - level: self.level, - indent: self.indent.clone(), - buffer: "".to_string(), - } - } -} diff --git a/src/lua.rs b/src/lua/mod.rs similarity index 100% rename from src/lua.rs rename to src/lua/mod.rs From 36db99927e2b016675a0ded2b497dbafbd40bb34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Thu, 2 Nov 2023 12:04:02 +0100 Subject: [PATCH 14/17] added char expansion --- examples/basic.saturn | 3 +++ examples/extra_op.saturn | 2 +- examples/loops.saturn | 2 +- src/code/generator.rs | 6 ++++++ src/code/mod.rs | 2 ++ src/lua/mod.rs | 36 ------------------------------------ src/parser/ast.rs | 12 +----------- src/parser/mod.rs | 4 +--- 8 files changed, 15 insertions(+), 52 deletions(-) create mode 100644 src/code/generator.rs diff --git a/examples/basic.saturn b/examples/basic.saturn index e9ba149..11274af 100644 --- a/examples/basic.saturn +++ b/examples/basic.saturn @@ -7,6 +7,9 @@ // Declare variables let some_var = "foo"; +let some_char = 'a'; // This is a number actually. +some_char += 1; +print(some_var, some_char); // Mutate them some_var = 5; diff --git a/examples/extra_op.saturn b/examples/extra_op.saturn index 26a9534..448d1cb 100644 --- a/examples/extra_op.saturn +++ b/examples/extra_op.saturn @@ -1,4 +1,4 @@ -let { operator, entries } = require('../src/assets/std'); +let { operator, entries } = require("../src/assets/std"); @operator("|>") fn pipe_right(left, right) { diff --git a/examples/loops.saturn b/examples/loops.saturn index e46e262..bd1f12e 100644 --- a/examples/loops.saturn +++ b/examples/loops.saturn @@ -1,4 +1,4 @@ -let { entries } = require('../src/assets/std'); +let { entries } = require("../src/assets/std"); let tbl = { a: 1, diff --git a/src/code/generator.rs b/src/code/generator.rs new file mode 100644 index 0000000..99489af --- /dev/null +++ b/src/code/generator.rs @@ -0,0 +1,6 @@ +/// TODO +pub trait Generator { + fn gen() { + todo!("Not ready yet.") + } +} diff --git a/src/code/mod.rs b/src/code/mod.rs index e881520..18f0ed2 100644 --- a/src/code/mod.rs +++ b/src/code/mod.rs @@ -1,7 +1,9 @@ mod builder; +mod generator; mod visitor; pub use builder::Builder; +pub use generator::Generator; pub use visitor::{VisitError, Visitor}; pub trait BuilderVisitor: Visitor {} diff --git a/src/lua/mod.rs b/src/lua/mod.rs index cac9891..d21e3b9 100644 --- a/src/lua/mod.rs +++ b/src/lua/mod.rs @@ -255,38 +255,6 @@ impl code::Visitor for LuaEmitter { }; ctx.put(";") } - ast::ClassField::Operator(f) => { - let target = match f.operator.0.as_str() { - "+" => "__add", - "-" => "__sub", - "*" => "__mul", - "/" => "__div", - "%" => "__mod", - "**" => "__pow", - "==" => "__eq", - "<" => "__lt", - "<=" => "__le", - "++" => "__concat", - "#?" => "__len", - _ => todo!( - "Operator overload for {:?} operator not supported", - f.operator.clone() - ), - }; - let ctx = ctx.put(format!( - "{}.prototype.__meta__.{} = ", - stmt.name.0.clone(), - target - )); - let ctx = self.visit_lambda( - ctx, - &ast::Lambda { - arguments: f.arguments.clone(), - body: ast::ScriptOrExpression::Script(f.body.clone()), - }, - )?; - ctx.put(";") - } }; Ok(ctx) })?; @@ -334,8 +302,6 @@ impl code::Visitor for LuaEmitter { match native.iter().find(|(ident, _)| ident.0 == "Lua") { Some((_, src)) => match src { ast::StringLiteral::Double(src) => ctx.put(src), - ast::StringLiteral::Single(src) => ctx.put(src), - ast::StringLiteral::Special(_) => panic!("Not implemented"), }, None => ctx.put("error('Native function implementation not found')"), } @@ -612,8 +578,6 @@ impl code::Visitor for LuaEmitter { ) -> Result { let ctx = match expr { ast::StringLiteral::Double(s) => ctx.put("\"").put(escape_string(s.clone())).put("\""), - ast::StringLiteral::Single(s) => ctx.put("'").put(s.clone()).put("'"), - ast::StringLiteral::Special(_) => todo!(), }; Ok(ctx) } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 6e9b53e..a5aca31 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -28,7 +28,7 @@ pub struct Lambda { #[derive(Debug, Clone)] pub struct Do { - pub body: Script + pub body: Script, } #[derive(Debug, Clone)] @@ -186,18 +186,10 @@ pub enum Statement { Expression(Expression), } -#[derive(Debug, Clone)] -pub struct OperatorOverload { - pub operator: Operator, - pub arguments: Vec, - pub body: Script, -} - #[derive(Debug, Clone)] pub enum ClassField { Method(Function), Let(Let), - Operator(OperatorOverload), } #[derive(Debug, Clone)] @@ -246,8 +238,6 @@ pub enum TableKeyExpression { #[derive(Debug, Clone)] pub enum StringLiteral { Double(String), - Single(String), - Special(String), } #[derive(Debug, Clone)] diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 63d860d..25ce4a3 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -222,11 +222,11 @@ peg::parser! { rule number_literal() -> Number = value:$(DIGIT()+ "." DIGIT()+) { Number::Float(value.parse().unwrap()) } / value:$(DIGIT()+) { Number::Integer(value.parse().unwrap()) } + / "'" value:$(!"'" ANY()) "'" { Number::Integer(value.chars().nth(0).unwrap() as i64) } / expected!("Number literal") rule string_literal() -> StringLiteral = "\"" value:$((!"\"" ANY())*) "\"" { StringLiteral::Double(value.into()) } - / "'" value:$((!"'" ANY())*) "'" { StringLiteral::Single(value.into()) } / expected!("String literal") rule vector_literal() -> Vector @@ -269,8 +269,6 @@ peg::parser! { rule class_fields() -> ClassField = e:declare_var() { ClassField::Let(e) } / e:func() { ClassField::Method(e) } - / "operator" _ operator:any_operator() _ arguments:argument_list() _ "{" body:script() "}" - { ClassField::Operator(OperatorOverload { operator, arguments, body }) } rule assign_extra() -> Operator = value:$("++") { Operator(value.into()) } From 58e962b8549e460805bee227362cac344c9bbd2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Thu, 2 Nov 2023 12:14:39 +0100 Subject: [PATCH 15/17] updated decorator syntax --- examples/function_decorators.saturn | 13 ++++++++++-- readme.md | 32 ++++++++++++++++++++++------- src/parser/mod.rs | 2 +- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/examples/function_decorators.saturn b/examples/function_decorators.saturn index 9524284..5a28928 100644 --- a/examples/function_decorators.saturn +++ b/examples/function_decorators.saturn @@ -1,12 +1,21 @@ -fn Test() { +fn test() { return (target, name) => { print("Adding " ++ name ++ " to the test suite!"); }; } -@Test() +@test() fn my_func() { print("Some func"); } + +fn foo() { + return (_, n) => print("Adding foo to", n); +} +fn bar() { + return (_, n) => print("Adding bar to", n); +} + +@foo() @bar() fn testing() {} diff --git a/readme.md b/readme.md index 45eccf0..682b07a 100644 --- a/readme.md +++ b/readme.md @@ -314,6 +314,22 @@ print(consumer.consume(foo)); print(consumer.consume(bar)); ``` +Also you can decorate the code: +```rs +@bar() +class Foo { + @foo() + fn func(self) { + return "me"; + } +} + +@test() +fn suite() { + print("Yay!"); +} +``` + ## Why replace Lua? I like many aspects of Lua, specially how fast and lightweight the VM is. But @@ -323,11 +339,13 @@ make the scripts less verbose and more easy to write. Aside of the [Language Basics](#language-basics) section, there are other key aspects of the language: -- Decorators! -- A built-in prelude library for runtime type checks. -- ~~Nice string interpolation.~~ (Maybe not?) -- Terser loops. -- Built-in operator overloading. -- Custom operators. +- Decorators +- Classes +- A built-in prelude library for runtime type checks and other things. +- Char expansion +- Macros +- Cooler lambdas. +- Terser loops +- Custom operators - Some [RTTI](https://en.wikipedia.org/wiki/Run-time_type_information) (Which - enables reflection). + enables reflection) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 25ce4a3..41101b8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -285,7 +285,7 @@ peg::parser! { { Argument { name, decorators } } rule decorator_list() -> Vec - = e:decorator() ++ __ __ { e } + = e:decorator() ++ _ _ { e } / { vec![] } rule decorator() -> Decorator From 262d0fcfbb35bf76aaa42a38c2739c2373e3bdcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Thu, 2 Nov 2023 16:52:29 +0100 Subject: [PATCH 16/17] wip: evaluator host --- src/code/macros.rs | 7 +++++++ src/code/mod.rs | 1 + src/main.rs | 8 ++------ src/runtime.rs | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 src/code/macros.rs create mode 100644 src/runtime.rs diff --git a/src/code/macros.rs b/src/code/macros.rs new file mode 100644 index 0000000..7264f9d --- /dev/null +++ b/src/code/macros.rs @@ -0,0 +1,7 @@ +use crate::parser::ast; + +struct MacroExpander; + +impl MacroExpander { + fn expand(call: &ast::CallExpression) -> ast::ScriptOrExpression {} +} diff --git a/src/code/mod.rs b/src/code/mod.rs index 18f0ed2..cce9646 100644 --- a/src/code/mod.rs +++ b/src/code/mod.rs @@ -1,5 +1,6 @@ mod builder; mod generator; +pub mod macros; mod visitor; pub use builder::Builder; diff --git a/src/main.rs b/src/main.rs index 6cc121e..3ab1f90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ mod code; mod errors; mod lua; mod parser; +pub mod runtime; #[cfg(test)] mod tests; @@ -91,12 +92,7 @@ fn main() { out_file.write_all(output.as_bytes()).unwrap(); } } else { - let rt = rlua::Lua::new(); - rt.context(move |ctx| -> rlua::Result<()> { - ctx.load(&output).eval()?; - Ok(()) - }) - .unwrap(); + runtime::RuntimeHost.evaluate(ast).unwrap(); } } Err(err) => { diff --git a/src/runtime.rs b/src/runtime.rs new file mode 100644 index 0000000..df30878 --- /dev/null +++ b/src/runtime.rs @@ -0,0 +1,15 @@ +use std::error::Error; + +use crate::parser::Script; + +pub struct RuntimeHost; + +impl RuntimeHost { + pub fn evaluate(self, ast: Script) -> Result<(), dyn Error> { + let rt = rlua::Lua::new(); + rt.context(move |ctx| -> rlua::Result<()> { + ctx.load(&output).eval()?; + Ok(()) + }) + } +} From 2fa3400c015d82f7ab2a6eb9595aa13eabb6bfcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Fri, 3 Nov 2023 13:26:14 +0100 Subject: [PATCH 17/17] fixed compiler --- src/code/macros.rs | 4 +- src/main.rs | 3 +- src/parser/grammar.rs | 394 +++++++++++++++++++++++++++++++++++++++++ src/parser/mod.rs | 396 +----------------------------------------- src/runtime.rs | 12 +- 5 files changed, 407 insertions(+), 402 deletions(-) create mode 100644 src/parser/grammar.rs diff --git a/src/code/macros.rs b/src/code/macros.rs index 7264f9d..ee46e20 100644 --- a/src/code/macros.rs +++ b/src/code/macros.rs @@ -3,5 +3,7 @@ use crate::parser::ast; struct MacroExpander; impl MacroExpander { - fn expand(call: &ast::CallExpression) -> ast::ScriptOrExpression {} + fn expand(call: &ast::CallExpression) -> ast::ScriptOrExpression { + todo!() + } } diff --git a/src/main.rs b/src/main.rs index 3ab1f90..2b286ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,6 +73,7 @@ fn main() { // Handle parsing errors match parser::Script::parse(input.clone()) { Ok(result) => { + // TODO: ABSTRACT! let output = lua::LuaEmitter .visit_script( code::Builder::new(indent).put( @@ -92,7 +93,7 @@ fn main() { out_file.write_all(output.as_bytes()).unwrap(); } } else { - runtime::RuntimeHost.evaluate(ast).unwrap(); + runtime::RuntimeHost.evaluate(&output).unwrap(); } } Err(err) => { diff --git a/src/parser/grammar.rs b/src/parser/grammar.rs new file mode 100644 index 0000000..db89273 --- /dev/null +++ b/src/parser/grammar.rs @@ -0,0 +1,394 @@ +use super::ast::*; + +peg::parser! { + grammar saturnus_script() for str { + pub rule script() -> Script + = _ statements:statement() ** __ _ + { Script { statements } } + + // Statements + rule statement() -> Statement + = e:class() { Statement::Class(e) } + / e:func() { Statement::Function(e) } + / e:for_each() { Statement::For(e) } + / e:while_loop() { Statement::While(e) } + / e:loop_loop() { Statement::Loop(e) } + / e:if_stmt() { Statement::If(e) } + / e:declare_var() { Statement::Let(e) } + / e:assignment() { Statement::Assignment(e) } + / e:return_stmt() { Statement::Return(e) } + / e:do_expression() { Statement::Expression(e) } + / e:expression() _ EOS() { Statement::Expression(e) } + + rule if_stmt() -> If + = "if" __ condition:expression() __ "{" body:script() + branches:("}" _ "else" __ "if" __ c:expression() __ "{" s:script() { (c, s) })* + else_branch:("}" _ "else" _ "{" e:script() {e})? + "}" + { If { condition, body, branches, else_branch } } + / expected!("If statement") + + rule for_each() -> For + = "for" __ handler:assignment_target() __ "in" __ target:expression() _ "{" + body:script() "}" + { For { handler, target, body } } + / expected!("For loop") + + rule while_loop() -> While + = "while" __ c:expression() _ "{" body:script() "}" + { While { condition: ExpressionOrLet::Expression(c), body } } + / "while" __ c:let_expression() _ "{" body:script() "}" + { While { condition: ExpressionOrLet::Let(c), body } } + / expected!("While loop") + + rule loop_loop() -> Loop + = "loop" _ "{" body:script() "}" + { Loop { body } } + / expected!("Loop") + + rule func() -> Function + = decorators:decorator_list() FN() __ NATIVE() __ name:identifier() _ arguments:argument_list() + _ "{" native:(__ "@" name:identifier() _ source:string_literal() _ EOS() { (name, source) })+ __ "}" + { Function { name, decorators, body: Script { statements: vec![] }, arguments, native: Some(native) } } + / + decorators:decorator_list() FN() __ name:identifier() _ arguments:argument_list() _ "{" body:script() "}" + { Function { name, decorators, body, arguments, native: None } } + / decorators:decorator_list() FN() __ name:identifier() _ arguments:argument_list() _ "{" _ "}" + { Function { name, decorators, body: Script { statements: vec![] }, arguments, native: None } } + / expected!("Function declaration") + + rule class() -> Class + = decorators:decorator_list() CLASS() + __ name:identifier() _ "{" + fields:(_ f:class_fields() _ {f})* + _ "}" + { Class { name, fields, decorators } } + / expected!("Class declaration") + + rule declare_var() -> Let + = e:let_expression() _ EOS() { e } + / expected!("Variable declaration") + + rule assignment() -> Assignment + = target:member_expression() _ extra:assign_extra()? "=" _ value:expression() _ EOS() + { Assignment { target, value, extra } } + + rule return_stmt() -> Return + = "return" __ value:expression() _ EOS() + { Return { value } } + + // Expressions + pub rule expression() -> Expression + = binary_expression() + + rule member_expression() -> MemberExpression + = head:primary() + tail:( + _ "[" _ e:expression() _ "]" { MemberSegment::Computed(e) } + / _ "." _ i:identifier() { MemberSegment::IdentifierDynamic(i) } + / _ "::" _ i:identifier() { MemberSegment::IdentifierStatic(i) } + )* + { MemberExpression { head, tail } } + + rule call_expression() -> CallExpression + = head:( + callee:member_expression() m:"!"? _ arguments:call_arguments() + { CallSubExpression { callee: Some(callee), is_macro: m.is_some(), arguments }.into() } + / callee:member_expression() _ arg:table_expression() + { CallSubExpression { callee: Some(callee), is_macro: false, arguments: vec![arg] } } + ) + tail:( + _ "[" _ prop:expression() _ "]" { MemberSegment::Computed(prop).into() } + / _ "." _ prop:identifier() { MemberSegment::IdentifierDynamic(prop).into() } + / _ "::" _ prop:identifier() { MemberSegment::IdentifierStatic(prop).into() } + / _ arguments:call_arguments() { CallSubExpression { callee: None, is_macro: false, arguments }.into() } + )* + { CallExpression { head, tail } } + + rule primary() -> Expression + = i:identifier() { Expression::Identifier(i) } + / string_expression() + / number_expression() + / table_expression() + / vector_expression() + / tuple_expression() + / lambda_expression() + / do_expression() + / enclosed_expression() + + rule call_arguments() -> Vec + = "(" _ args:(e:call_argument_list() _ { e })? ")" + { args.unwrap_or(vec![]) } + + rule call_argument_list() -> Vec + = args:expression() ** (_ "," _) + { args } + + rule binary_expression() -> Expression = precedence! { + value:$("-") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("+") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("#?") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("not") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("~^") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + // value:$("!" _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("~") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + // value:$("¬" _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("$") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + value:$("!?") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } + -- + left:(@) _ value:$("++") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("..") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + + left:(@) _ value:$("+") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("-") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + -- + left:(@) _ value:$("*") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("/") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + -- + left:@ _ value:$("**") _ right:(@) { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + -- + left:(@) _ value:$("%") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + -- + left:(@) _ value:$(">=<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$(">=") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$(">") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<=>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<=") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("==") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + -- + left:(@) _ value:$("and") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("or") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("xor") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("nand") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("nor") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + -- + left:(@) _ value:$("&") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("|") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<<<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$("<<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$(">>>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + left:(@) _ value:$(">>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } + -- + // Extra logic: + left:(@) _ value:$(['^'|'+'|'-'|'*'|'/'|'.'|'|'|'>'|'<'|'='|'?'|'!'|'~'|'%'|'&'|'#'|'$'|':']+) _ right:@ { + BinaryExpression { left, right, operator: Operator(value.into()) }.into() + } + -- + e:atom() { e } + } + + rule atom() -> Expression + = e:call_expression() { Expression::Call(Box::new(e)) } + / lambda_expression() + / string_expression() + / number_expression() + / vector_expression() + / table_expression() + / tuple_expression() + / do_expression() + / e:member_expression() { Expression::Reference(Box::new(e)) } + / unit() { Expression::Unit } + / enclosed_expression() + + // Literal-to-expression + rule string_expression() -> Expression = e:string_literal() { Expression::String(e) } + rule number_expression() -> Expression = e:number_literal() { Expression::Number(e) } + rule lambda_expression() -> Expression = e:lambda_literal() { Expression::Lambda(Box::new(e)) } + rule vector_expression() -> Expression = e:vector_literal() { Expression::Vector(e) } + rule table_expression() -> Expression = e:table_literal() { Expression::Table(e) } + rule tuple_expression() -> Expression = e:tuple_literal() { Expression::Tuple(e) } + rule do_expression() -> Expression = e:do_literal() { Expression::Do(e) } + + rule enclosed_expression() -> Expression + = "(" _ e:expression() _ ")" { Expression::Tuple1(Box::new(e)) } + + rule lambda_literal() -> Lambda + = name:identifier() _ "=>" _ "{" body:script() "}" + { Lambda { arguments: vec![Argument { name, decorators: vec![] }], body: ScriptOrExpression::Script(body) } } + / name:identifier() _ "=>" _ body:expression() + { Lambda { arguments: vec![Argument { name, decorators: vec![] }], body: ScriptOrExpression::Expression(body) } } + / arguments:argument_list() _ "=>" _ "{" body:script() "}" + { Lambda { arguments, body: ScriptOrExpression::Script(body) } } + / arguments:argument_list() _ "=>" _ expr:expression() + { Lambda { arguments, body: ScriptOrExpression::Expression(expr) } } + / arguments:argument_list() _ "=>" _ "{" _ "}" + { Lambda { arguments, body: ScriptOrExpression::Script(Script { statements: vec![] }) } } + + // Literals + rule number_literal() -> Number + = value:$(DIGIT()+ "." DIGIT()+) { Number::Float(value.parse().unwrap()) } + / value:$(DIGIT()+) { Number::Integer(value.parse().unwrap()) } + / "'" value:$(!"'" ANY()) "'" { Number::Integer(value.chars().nth(0).unwrap() as i64) } + / expected!("Number literal") + + rule string_literal() -> StringLiteral + = "\"" value:$((!"\"" ANY())*) "\"" { StringLiteral::Double(value.into()) } + / expected!("String literal") + + rule vector_literal() -> Vector + = "[" _ expressions:comma_expr() _ "]" + { Vector { expressions } } + / expected!("Vector literal") + + rule table_literal() -> Table + = "{" _ key_values:table_kvs() _ "}" + { Table { key_values } } + / expected!("Table literal") + + rule tuple_literal() -> Tuple + = "(" _ e:expression() **<2,> (_ "," _) _ ")" + { Tuple(e) } + / expected!("Tuple literal") + + rule do_literal() -> Do + = "{" body:script() "}" + { Do { body } } + / expected!("Do block") + + // Auxiliaries and sub-expressions + rule let_expression() -> Let + = "let" __ target:assignment_target() value:(_ "=" _ e:expression(){e})? + { Let { target, value } } + + rule assignment_target() -> AssignmentTarget + = e:identifier() { AssignmentTarget::Identifier(e) } + / e:destructure_expression() { AssignmentTarget::Destructuring(e) } + + rule destructure_expression() -> Destructuring + = "{" _ targets:(name:identifier() ** (_ "," _) { name }) _ "}" + { Destructuring(targets, DestructureOrigin::Table) } + / "(" _ targets:(name:identifier() ** (_ "," _) { name }) _ ")" + { Destructuring(targets, DestructureOrigin::Tuple) } + / "[" _ targets:(name:identifier() ** (_ "," _) { name }) _ "]" + { Destructuring(targets, DestructureOrigin::Array) } + + rule class_fields() -> ClassField + = e:declare_var() { ClassField::Let(e) } + / e:func() { ClassField::Method(e) } + + rule assign_extra() -> Operator + = value:$("++") { Operator(value.into()) } + / value:$("+") { Operator(value.into()) } + / value:$("-") { Operator(value.into()) } + / value:$("*") { Operator(value.into()) } + / value:$("/") { Operator(value.into()) } + + rule argument_list() -> Vec + = "(" _ args:argument() ** (_ "," _) _ ")" { args } + + rule argument() -> Argument + = decorators:decorator_list() name:identifier() + { Argument { name, decorators } } + + rule decorator_list() -> Vec + = e:decorator() ++ _ _ { e } + / { vec![] } + + rule decorator() -> Decorator + = "@" _ target:call_expression() { Decorator { target } } + / expected!("Decorator") + + rule identifier() -> Identifier + = value:$(IDENT()) + { Identifier(value.into()) } + / expected!("Identifier") + + rule wrapped_comma_expr() -> Vec + = "(" _ e:comma_expr() _ ")" { e } + + rule comma_expr() -> Vec + = e:expression() ** (_ "," _) (_ "," _)? { e } + + rule table_kvs() -> Vec<(TableKeyExpression, Option)> + = kv:table_kv_pair() ** (_ "," _) (_ "," _)? + { kv } + + rule table_kv_pair() -> (TableKeyExpression, Option) + = k:identifier() _ ":" _ v:expression() + { (TableKeyExpression::Identifier(k), Some(v)) } + / k:string_expression() _ ":" _ v:expression() + { (TableKeyExpression::Expression(k), Some(v)) } + / "[" _ k:expression() _ "]" _ ":" _ v:expression() + { (TableKeyExpression::Expression(k), Some(v)) } + / k:identifier() + { ( + TableKeyExpression::Implicit(k.clone()), + None + ) } + + rule unit() -> Expression = "()" { Expression::Unit } + + // Tokens + rule IDENT() = ALPHA() (ALPHA() / DIGIT())* + rule LET() = "let" + rule MUT() = "mut" + rule CLASS() = "class" + rule END() = "end" + rule FN() = "fn" + rule NATIVE() = "@native" + rule ANY() = quiet!{ [_] } / expected!("Any character") + rule BLANK() = ['\t'|' '] / expected!("White space") + rule WS() = BLANK() / LINE_COMMENT() / BLOCK_COMMENT() / EOL() + rule LINE_COMMENT() = quiet!{ "//" (!EOL() ANY())* EOL() } / expected!("Line comment") + rule BLOCK_COMMENT() = quiet!{ "/*" (!"*/" ANY())* "*/" } / expected!("Block comment") + rule EOL() = quiet!{ ['\r'|'\n'] } / expected!("End of line") + rule EOS() = quiet!{ ";" } / expected!("End of statement") //quiet!{ EOL() / ";" } / expected!("End of statement") + rule ALPHA() = quiet!{ ['A'..='Z'|'a'..='z'|'_'] } / expected!("Alphanumeric") + rule DIGIT() = quiet!{ ['0'..='9'] } / expected!("Digit") + rule _ = WS()* + rule __ = WS()+ + + // Special matching rule: Any Binary Operator + rule any_operator() -> Operator + = value:$("++") { Operator(value.into()) } + / value:$("..") { Operator(value.into()) } + / value:$("+") { Operator(value.into()) } + / value:$("-") { Operator(value.into()) } + / value:$("*") { Operator(value.into()) } + / value:$("/") { Operator(value.into()) } + / value:$("**") { Operator(value.into()) } + / value:$("%") { Operator(value.into()) } + / value:$(">=<") { Operator(value.into()) } + / value:$(">=") { Operator(value.into()) } + / value:$("<=>") { Operator(value.into()) } + / value:$("<=") { Operator(value.into()) } + / value:$("<>") { Operator(value.into()) } + / value:$("==") { Operator(value.into()) } + / value:$("and") { Operator(value.into()) } + / value:$("or") { Operator(value.into()) } + / value:$("xor") { Operator(value.into()) } + / value:$("nand") { Operator(value.into()) } + / value:$("nor") { Operator(value.into()) } + / value:$(['^'|'+'|'-'|'*'|'/'|'.'|'|'|'>'|'<'|'='|'?'|'!'|'~'|'%'|'&'|'#'|'$'|':']+) { Operator(value.into()) } + } +} + +pub type ParseResult = Result>; + +#[derive(Debug, Clone)] +pub struct Script { + pub statements: Vec, +} +impl Script { + pub fn parse(input: I) -> ParseResult + where + I: Into, + { + let fragment: String = input.into(); + saturnus_script::script(&fragment) + } + + // This function is used only in tests for now. + #[cfg(test)] + pub fn parse_expression( + input: I, + ) -> Result> + where + I: Into, + { + let fragment: String = input.into(); + saturnus_script::expression(&fragment) + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 41101b8..9c9ea43 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,396 +1,4 @@ pub mod ast; +mod grammar; -use ast::*; - -peg::parser! { - grammar saturnus_script() for str { - pub rule script() -> Script - = _ statements:statement() ** __ _ - { Script { statements } } - - // Statements - rule statement() -> Statement - = e:class() { Statement::Class(e) } - / e:func() { Statement::Function(e) } - / e:for_each() { Statement::For(e) } - / e:while_loop() { Statement::While(e) } - / e:loop_loop() { Statement::Loop(e) } - / e:if_stmt() { Statement::If(e) } - / e:declare_var() { Statement::Let(e) } - / e:assignment() { Statement::Assignment(e) } - / e:return_stmt() { Statement::Return(e) } - / e:do_expression() { Statement::Expression(e) } - / e:expression() _ EOS() { Statement::Expression(e) } - - rule if_stmt() -> If - = "if" __ condition:expression() __ "{" body:script() - branches:("}" _ "else" __ "if" __ c:expression() __ "{" s:script() { (c, s) })* - else_branch:("}" _ "else" _ "{" e:script() {e})? - "}" - { If { condition, body, branches, else_branch } } - / expected!("If statement") - - rule for_each() -> For - = "for" __ handler:assignment_target() __ "in" __ target:expression() _ "{" - body:script() "}" - { For { handler, target, body } } - / expected!("For loop") - - rule while_loop() -> While - = "while" __ c:expression() _ "{" body:script() "}" - { While { condition: ExpressionOrLet::Expression(c), body } } - / "while" __ c:let_expression() _ "{" body:script() "}" - { While { condition: ExpressionOrLet::Let(c), body } } - / expected!("While loop") - - rule loop_loop() -> Loop - = "loop" _ "{" body:script() "}" - { Loop { body } } - / expected!("Loop") - - rule func() -> Function - = decorators:decorator_list() FN() __ NATIVE() __ name:identifier() _ arguments:argument_list() - _ "{" native:(__ "@" name:identifier() _ source:string_literal() _ EOS() { (name, source) })+ __ "}" - { Function { name, decorators, body: Script { statements: vec![] }, arguments, native: Some(native) } } - / - decorators:decorator_list() FN() __ name:identifier() _ arguments:argument_list() _ "{" body:script() "}" - { Function { name, decorators, body, arguments, native: None } } - / decorators:decorator_list() FN() __ name:identifier() _ arguments:argument_list() _ "{" _ "}" - { Function { name, decorators, body: Script { statements: vec![] }, arguments, native: None } } - / expected!("Function declaration") - - rule class() -> Class - = decorators:decorator_list() CLASS() - __ name:identifier() _ "{" - fields:(_ f:class_fields() _ {f})* - _ "}" - { Class { name, fields, decorators } } - / expected!("Class declaration") - - rule declare_var() -> Let - = e:let_expression() _ EOS() { e } - / expected!("Variable declaration") - - rule assignment() -> Assignment - = target:member_expression() _ extra:assign_extra()? "=" _ value:expression() _ EOS() - { Assignment { target, value, extra } } - - rule return_stmt() -> Return - = "return" __ value:expression() _ EOS() - { Return { value } } - - // Expressions - pub rule expression() -> Expression - = binary_expression() - - rule member_expression() -> MemberExpression - = head:primary() - tail:( - _ "[" _ e:expression() _ "]" { MemberSegment::Computed(e) } - / _ "." _ i:identifier() { MemberSegment::IdentifierDynamic(i) } - / _ "::" _ i:identifier() { MemberSegment::IdentifierStatic(i) } - )* - { MemberExpression { head, tail } } - - rule call_expression() -> CallExpression - = head:( - callee:member_expression() m:"!"? _ arguments:call_arguments() - { CallSubExpression { callee: Some(callee), is_macro: m.is_some(), arguments }.into() } - / callee:member_expression() _ arg:table_expression() - { CallSubExpression { callee: Some(callee), is_macro: false, arguments: vec![arg] } } - ) - tail:( - _ "[" _ prop:expression() _ "]" { MemberSegment::Computed(prop).into() } - / _ "." _ prop:identifier() { MemberSegment::IdentifierDynamic(prop).into() } - / _ "::" _ prop:identifier() { MemberSegment::IdentifierStatic(prop).into() } - / _ arguments:call_arguments() { CallSubExpression { callee: None, is_macro: false, arguments }.into() } - )* - { CallExpression { head, tail } } - - rule primary() -> Expression - = i:identifier() { Expression::Identifier(i) } - / string_expression() - / number_expression() - / table_expression() - / vector_expression() - / tuple_expression() - / lambda_expression() - / do_expression() - / enclosed_expression() - - rule call_arguments() -> Vec - = "(" _ args:(e:call_argument_list() _ { e })? ")" - { args.unwrap_or(vec![]) } - - rule call_argument_list() -> Vec - = args:expression() ** (_ "," _) - { args } - - rule binary_expression() -> Expression = precedence! { - value:$("-") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - value:$("+") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - value:$("#?") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - value:$("not") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - value:$("~^") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - // value:$("!" _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - value:$("~") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - // value:$("¬" _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - value:$("$") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - value:$("!?") _ expression:@ { UnaryExpression { expression, operator: Operator(value.into()) }.into() } - -- - left:(@) _ value:$("++") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("..") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - - left:(@) _ value:$("+") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("-") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - -- - left:(@) _ value:$("*") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("/") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - -- - left:@ _ value:$("**") _ right:(@) { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - -- - left:(@) _ value:$("%") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - -- - left:(@) _ value:$(">=<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$(">=") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$(">") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("<=>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("<=") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("<>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("==") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - -- - left:(@) _ value:$("and") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("or") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("xor") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("nand") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("nor") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - -- - left:(@) _ value:$("&") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("|") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("<<<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$("<<") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$(">>>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - left:(@) _ value:$(">>") _ right:@ { BinaryExpression { left, right, operator: Operator(value.into()) }.into() } - -- - // Extra logic: - left:(@) _ value:$(['^'|'+'|'-'|'*'|'/'|'.'|'|'|'>'|'<'|'='|'?'|'!'|'~'|'%'|'&'|'#'|'$'|':']+) _ right:@ { - BinaryExpression { left, right, operator: Operator(value.into()) }.into() - } - -- - e:atom() { e } - } - - rule atom() -> Expression - = e:call_expression() { Expression::Call(Box::new(e)) } - / lambda_expression() - / string_expression() - / number_expression() - / vector_expression() - / table_expression() - / tuple_expression() - / do_expression() - / e:member_expression() { Expression::Reference(Box::new(e)) } - / unit() { Expression::Unit } - / enclosed_expression() - - // Literal-to-expression - rule string_expression() -> Expression = e:string_literal() { Expression::String(e) } - rule number_expression() -> Expression = e:number_literal() { Expression::Number(e) } - rule lambda_expression() -> Expression = e:lambda_literal() { Expression::Lambda(Box::new(e)) } - rule vector_expression() -> Expression = e:vector_literal() { Expression::Vector(e) } - rule table_expression() -> Expression = e:table_literal() { Expression::Table(e) } - rule tuple_expression() -> Expression = e:tuple_literal() { Expression::Tuple(e) } - rule do_expression() -> Expression = e:do_literal() { Expression::Do(e) } - - rule enclosed_expression() -> Expression - = "(" _ e:expression() _ ")" { Expression::Tuple1(Box::new(e)) } - - rule lambda_literal() -> Lambda - = name:identifier() _ "=>" _ "{" body:script() "}" - { Lambda { arguments: vec![Argument { name, decorators: vec![] }], body: ScriptOrExpression::Script(body) } } - / name:identifier() _ "=>" _ body:expression() - { Lambda { arguments: vec![Argument { name, decorators: vec![] }], body: ScriptOrExpression::Expression(body) } } - / arguments:argument_list() _ "=>" _ "{" body:script() "}" - { Lambda { arguments, body: ScriptOrExpression::Script(body) } } - / arguments:argument_list() _ "=>" _ expr:expression() - { Lambda { arguments, body: ScriptOrExpression::Expression(expr) } } - / arguments:argument_list() _ "=>" _ "{" _ "}" - { Lambda { arguments, body: ScriptOrExpression::Script(Script { statements: vec![] }) } } - - // Literals - rule number_literal() -> Number - = value:$(DIGIT()+ "." DIGIT()+) { Number::Float(value.parse().unwrap()) } - / value:$(DIGIT()+) { Number::Integer(value.parse().unwrap()) } - / "'" value:$(!"'" ANY()) "'" { Number::Integer(value.chars().nth(0).unwrap() as i64) } - / expected!("Number literal") - - rule string_literal() -> StringLiteral - = "\"" value:$((!"\"" ANY())*) "\"" { StringLiteral::Double(value.into()) } - / expected!("String literal") - - rule vector_literal() -> Vector - = "[" _ expressions:comma_expr() _ "]" - { Vector { expressions } } - / expected!("Vector literal") - - rule table_literal() -> Table - = "{" _ key_values:table_kvs() _ "}" - { Table { key_values } } - / expected!("Table literal") - - rule tuple_literal() -> Tuple - = "(" _ e:expression() **<2,> (_ "," _) _ ")" - { Tuple(e) } - / expected!("Tuple literal") - - rule do_literal() -> Do - = "{" body:script() "}" - { Do { body } } - / expected!("Do block") - - // Auxiliaries and sub-expressions - rule let_expression() -> Let - = "let" __ target:assignment_target() value:(_ "=" _ e:expression(){e})? - { Let { target, value } } - - rule assignment_target() -> AssignmentTarget - = e:identifier() { AssignmentTarget::Identifier(e) } - / e:destructure_expression() { AssignmentTarget::Destructuring(e) } - - rule destructure_expression() -> Destructuring - = "{" _ targets:(name:identifier() ** (_ "," _) { name }) _ "}" - { Destructuring(targets, DestructureOrigin::Table) } - / "(" _ targets:(name:identifier() ** (_ "," _) { name }) _ ")" - { Destructuring(targets, DestructureOrigin::Tuple) } - / "[" _ targets:(name:identifier() ** (_ "," _) { name }) _ "]" - { Destructuring(targets, DestructureOrigin::Array) } - - rule class_fields() -> ClassField - = e:declare_var() { ClassField::Let(e) } - / e:func() { ClassField::Method(e) } - - rule assign_extra() -> Operator - = value:$("++") { Operator(value.into()) } - / value:$("+") { Operator(value.into()) } - / value:$("-") { Operator(value.into()) } - / value:$("*") { Operator(value.into()) } - / value:$("/") { Operator(value.into()) } - - rule argument_list() -> Vec - = "(" _ args:argument() ** (_ "," _) _ ")" { args } - - rule argument() -> Argument - = decorators:decorator_list() name:identifier() - { Argument { name, decorators } } - - rule decorator_list() -> Vec - = e:decorator() ++ _ _ { e } - / { vec![] } - - rule decorator() -> Decorator - = "@" _ target:call_expression() { Decorator { target } } - / expected!("Decorator") - - rule identifier() -> Identifier - = value:$(IDENT()) - { Identifier(value.into()) } - / expected!("Identifier") - - rule wrapped_comma_expr() -> Vec - = "(" _ e:comma_expr() _ ")" { e } - - rule comma_expr() -> Vec - = e:expression() ** (_ "," _) (_ "," _)? { e } - - rule table_kvs() -> Vec<(TableKeyExpression, Option)> - = kv:table_kv_pair() ** (_ "," _) (_ "," _)? - { kv } - - rule table_kv_pair() -> (TableKeyExpression, Option) - = k:identifier() _ ":" _ v:expression() - { (TableKeyExpression::Identifier(k), Some(v)) } - / k:string_expression() _ ":" _ v:expression() - { (TableKeyExpression::Expression(k), Some(v)) } - / "[" _ k:expression() _ "]" _ ":" _ v:expression() - { (TableKeyExpression::Expression(k), Some(v)) } - / k:identifier() - { ( - TableKeyExpression::Implicit(k.clone()), - None - ) } - - rule unit() -> Expression = "()" { Expression::Unit } - - // Tokens - rule IDENT() = ALPHA() (ALPHA() / DIGIT())* - rule LET() = "let" - rule MUT() = "mut" - rule CLASS() = "class" - rule END() = "end" - rule FN() = "fn" - rule NATIVE() = "@native" - rule ANY() = quiet!{ [_] } / expected!("Any character") - rule BLANK() = ['\t'|' '] / expected!("White space") - rule WS() = BLANK() / LINE_COMMENT() / BLOCK_COMMENT() / EOL() - rule LINE_COMMENT() = quiet!{ "//" (!EOL() ANY())* EOL() } / expected!("Line comment") - rule BLOCK_COMMENT() = quiet!{ "/*" (!"*/" ANY())* "*/" } / expected!("Block comment") - rule EOL() = quiet!{ ['\r'|'\n'] } / expected!("End of line") - rule EOS() = quiet!{ ";" } / expected!("End of statement") //quiet!{ EOL() / ";" } / expected!("End of statement") - rule ALPHA() = quiet!{ ['A'..='Z'|'a'..='z'|'_'] } / expected!("Alphanumeric") - rule DIGIT() = quiet!{ ['0'..='9'] } / expected!("Digit") - rule _ = WS()* - rule __ = WS()+ - - // Special matching rule: Any Binary Operator - rule any_operator() -> Operator - = value:$("++") { Operator(value.into()) } - / value:$("..") { Operator(value.into()) } - / value:$("+") { Operator(value.into()) } - / value:$("-") { Operator(value.into()) } - / value:$("*") { Operator(value.into()) } - / value:$("/") { Operator(value.into()) } - / value:$("**") { Operator(value.into()) } - / value:$("%") { Operator(value.into()) } - / value:$(">=<") { Operator(value.into()) } - / value:$(">=") { Operator(value.into()) } - / value:$("<=>") { Operator(value.into()) } - / value:$("<=") { Operator(value.into()) } - / value:$("<>") { Operator(value.into()) } - / value:$("==") { Operator(value.into()) } - / value:$("and") { Operator(value.into()) } - / value:$("or") { Operator(value.into()) } - / value:$("xor") { Operator(value.into()) } - / value:$("nand") { Operator(value.into()) } - / value:$("nor") { Operator(value.into()) } - / value:$(['^'|'+'|'-'|'*'|'/'|'.'|'|'|'>'|'<'|'='|'?'|'!'|'~'|'%'|'&'|'#'|'$'|':']+) { Operator(value.into()) } - } -} - -pub type ParseResult = Result>; - -#[derive(Debug, Clone)] -pub struct Script { - pub statements: Vec, -} -impl Script { - pub fn parse(input: I) -> ParseResult - where - I: Into, - { - let fragment: String = input.into(); - saturnus_script::script(&fragment) - } - - // This function is used only in tests for now. - #[cfg(test)] - pub fn parse_expression( - input: I, - ) -> Result> - where - I: Into, - { - let fragment: String = input.into(); - saturnus_script::expression(&fragment) - } -} +pub use grammar::{ParseResult, Script}; diff --git a/src/runtime.rs b/src/runtime.rs index df30878..d5f641f 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,15 +1,15 @@ -use std::error::Error; - -use crate::parser::Script; - pub struct RuntimeHost; +#[derive(Debug, Clone)] +pub struct RuntimeError(rlua::Error); + impl RuntimeHost { - pub fn evaluate(self, ast: Script) -> Result<(), dyn Error> { + pub fn evaluate(self, code: &String) -> Result<(), RuntimeError> { let rt = rlua::Lua::new(); rt.context(move |ctx| -> rlua::Result<()> { - ctx.load(&output).eval()?; + ctx.load(&code).eval()?; Ok(()) }) + .map_err(|err| RuntimeError(err)) } }