From 527015a9f87ed6446516fd429af8f378d813c35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Sat, 4 Nov 2023 09:19:56 +0100 Subject: [PATCH 1/3] wip: code-emission abstraction --- src/code/generation.rs | 75 ++++++++++++++++++++++++++++++++++++++++++ src/code/generator.rs | 6 ---- src/code/mod.rs | 4 +-- 3 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 src/code/generation.rs delete mode 100644 src/code/generator.rs diff --git a/src/code/generation.rs b/src/code/generation.rs new file mode 100644 index 0000000..cd4450b --- /dev/null +++ b/src/code/generation.rs @@ -0,0 +1,75 @@ +use crate::parser::ast::*; + +use super::{Builder, VisitError, Visitor}; + +struct ConstructionError; + +pub trait CodeEmitter { + fn build_class(&self, target: &Class) -> Result; +} + +pub struct EmitterVisitor(Box); + +impl Visitor for EmitterVisitor { + fn visit_return(&self, ctx: Builder, stmt: &Return) -> Result {} + + fn visit_class(&self, ctx: Builder, stmt: &Class) -> Result { + // stmt. + } + + fn visit_fn(&self, ctx: Builder, stmt: &Function) -> Result {} + + fn visit_assignment(&self, ctx: Builder, stmt: &Assignment) -> Result {} + + fn visit_declaration(&self, ctx: Builder, stmt: &Let) -> Result {} + + fn visit_expression_statement( + &self, + ctx: Builder, + stmt: &Expression, + ) -> Result { + } + + fn visit_lambda(&self, ctx: Builder, expr: &Lambda) -> Result {} + + fn visit_reference( + &self, + ctx: Builder, + expr: &MemberExpression, + ) -> Result { + } + + fn visit_call(&self, ctx: Builder, expr: &CallExpression) -> Result {} + + fn visit_tuple(&self, ctx: Builder, expr: &Tuple) -> Result {} + + fn visit_number(&self, ctx: Builder, expr: &Number) -> Result {} + + fn visit_string(&self, ctx: Builder, expr: &StringLiteral) -> Result {} + + fn visit_unit(&self, ctx: Builder) -> Result {} + + fn visit_binary(&self, ctx: Builder, expr: &BinaryExpression) -> Result {} + + fn visit_unary(&self, ctx: Builder, expr: &UnaryExpression) -> Result {} + + fn visit_if(&self, ctx: Builder, expr: &If) -> Result {} + + fn visit_table(&self, ctx: Builder, expr: &Table) -> Result {} + + fn visit_vector(&self, ctx: Builder, expr: &Vector) -> Result {} + + fn visit_for(&self, ctx: Builder, expr: &For) -> Result {} + + fn visit_while(&self, ctx: Builder, expr: &While) -> Result {} + + fn visit_loop(&self, ctx: Builder, expr: &Loop) -> Result {} + + fn visit_match(&self, ctx: Builder, expr: &Match) -> Result {} + + fn visit_1tuple(&self, ctx: Builder, expr: &Expression) -> Result {} + + fn visit_identifier(&self, ctx: Builder, expr: &Identifier) -> Result {} + + fn visit_do(&self, ctx: Builder, expr: &Do) -> Result {} +} diff --git a/src/code/generator.rs b/src/code/generator.rs deleted file mode 100644 index 99489af..0000000 --- a/src/code/generator.rs +++ /dev/null @@ -1,6 +0,0 @@ -/// TODO -pub trait Generator { - fn gen() { - todo!("Not ready yet.") - } -} diff --git a/src/code/mod.rs b/src/code/mod.rs index cce9646..f522a22 100644 --- a/src/code/mod.rs +++ b/src/code/mod.rs @@ -1,10 +1,10 @@ mod builder; -mod generator; +mod generation; pub mod macros; mod visitor; pub use builder::Builder; -pub use generator::Generator; +pub use generation::CodeEmitter; pub use visitor::{VisitError, Visitor}; pub trait BuilderVisitor: Visitor {} From 200169ddd0267cc377edb6fb8b7e7f272cdffd1f Mon Sep 17 00:00:00 2001 From: sigmasoldi3r Date: Sat, 4 Nov 2023 16:54:28 +0100 Subject: [PATCH 2/3] wip generation --- src/code/generation.rs | 167 +++- src/code/mod.rs | 12 +- src/code/visitor.rs | 81 -- src/code/walker.rs | 79 ++ src/lua/mod.rs | 1652 ++++++++++++++++++++-------------------- src/main.rs | 1 - src/parser/ast.rs | 14 +- src/parser/grammar.rs | 8 +- 8 files changed, 1049 insertions(+), 965 deletions(-) delete mode 100644 src/code/visitor.rs create mode 100644 src/code/walker.rs diff --git a/src/code/generation.rs b/src/code/generation.rs index cd4450b..84e1deb 100644 --- a/src/code/generation.rs +++ b/src/code/generation.rs @@ -1,75 +1,158 @@ -use crate::parser::ast::*; - -use super::{Builder, VisitError, Visitor}; +use std::error::Error; -struct ConstructionError; +use crate::parser::ast::*; -pub trait CodeEmitter { - fn build_class(&self, target: &Class) -> Result; +use super::{ + builder::Builder, + walker::{AstWalker, Result}, +}; + +#[derive(Debug)] +struct ConstructionError(Box); + +pub trait CodeBuilder { + // Classes + fn build_class(&self, ctx: Builder, target: &Class) -> Result; + fn build_class_method(&self, ctx: Builder, target: &Function) -> Result; + fn build_class_value(&self, ctx: Builder, target: &Let) -> Result; + fn build_class_decorator(&self, ctx: Builder, target: &Decorator) -> Result; + fn build_class_field_decorator(&self, ctx: Builder, target: &Decorator) -> Result; + + // Functions + fn build_function(&self, ctx: Builder, target: &Function) -> Result; + fn build_function_decorator(&self, ctx: Builder, target: &Decorator) -> Result; + fn build_macro_function(&self, ctx: Builder, target: &Function) -> Result; + fn build_native_function(&self, ctx: Builder, target: &Function) -> Result; + + // Lambdas and variables + fn build_lambda(&self, ctx: Builder, target: &Lambda) -> Result; + fn build_let(&self, ctx: Builder, target: &Let) -> Result; + fn build_use_statement(&self, ctx: Builder, target: &Let) -> Result; + fn build_use_expression(&self, ctx: Builder, target: &Let) -> Result; + fn build_return(&self, ctx: Builder, target: &Return) -> Result; + fn build_unit(&self, ctx: Builder, target: ()) -> Result; + + // Conditional and looping blocks + fn build_if(&self, ctx: Builder, target: &If) -> Result; + fn build_for(&self, ctx: Builder, target: &For) -> Result; + fn build_while(&self, ctx: Builder, target: &While) -> Result; + fn build_loop(&self, ctx: Builder, target: &Loop) -> Result; + fn build_while_let(&self, ctx: Builder, target: &While) -> Result; + + // Script blocks + fn build_block(&self, ctx: Builder, target: &Class) -> Result; + fn build_script(&self, ctx: Builder, target: &Class) -> Result; } -pub struct EmitterVisitor(Box); +pub struct CodeEmitter { + pub builder: Box, +} -impl Visitor for EmitterVisitor { - fn visit_return(&self, ctx: Builder, stmt: &Return) -> Result {} +impl AstWalker for CodeEmitter { + fn visit_return(&self, ctx: Builder, stmt: &Return) -> Result { + self.builder.build_return(ctx, stmt) + } - fn visit_class(&self, ctx: Builder, stmt: &Class) -> Result { - // stmt. + fn visit_class(&self, ctx: Builder, stmt: &Class) -> Result { + let ctx = self.builder.build_class(ctx, stmt)?; + let ctx = stmt.fields.iter().fold(Ok(ctx), |ctx, field| match field { + ClassField::Method(field) => self.builder.build_class_method(ctx?, field), + ClassField::Let(field) => self.builder.build_class_value(ctx?, field), + })?; + let ctx = stmt.decorators.iter().fold(Ok(ctx), |ctx, dec| { + self.builder.build_class_decorator(ctx?, dec) + })?; } - fn visit_fn(&self, ctx: Builder, stmt: &Function) -> Result {} + fn visit_fn(&self, ctx: Builder, stmt: &Function) -> Result { + todo!() + } - fn visit_assignment(&self, ctx: Builder, stmt: &Assignment) -> Result {} + fn visit_assignment(&self, ctx: Builder, stmt: &Assignment) -> Result { + todo!() + } - fn visit_declaration(&self, ctx: Builder, stmt: &Let) -> Result {} + fn visit_declaration(&self, ctx: Builder, stmt: &Let) -> Result { + todo!() + } - fn visit_expression_statement( - &self, - ctx: Builder, - stmt: &Expression, - ) -> Result { + fn visit_expression_statement(&self, ctx: Builder, stmt: &Expression) -> Result { + todo!() } - fn visit_lambda(&self, ctx: Builder, expr: &Lambda) -> Result {} + fn visit_lambda(&self, ctx: Builder, expr: &Lambda) -> Result { + todo!() + } - fn visit_reference( - &self, - ctx: Builder, - expr: &MemberExpression, - ) -> Result { + fn visit_reference(&self, ctx: Builder, expr: &MemberExpression) -> Result { + todo!() } - fn visit_call(&self, ctx: Builder, expr: &CallExpression) -> Result {} + fn visit_call(&self, ctx: Builder, expr: &CallExpression) -> Result { + todo!() + } - fn visit_tuple(&self, ctx: Builder, expr: &Tuple) -> Result {} + fn visit_tuple(&self, ctx: Builder, expr: &Tuple) -> Result { + todo!() + } - fn visit_number(&self, ctx: Builder, expr: &Number) -> Result {} + fn visit_number(&self, ctx: Builder, expr: &Number) -> Result { + todo!() + } - fn visit_string(&self, ctx: Builder, expr: &StringLiteral) -> Result {} + fn visit_string(&self, ctx: Builder, expr: &StringLiteral) -> Result { + todo!() + } - fn visit_unit(&self, ctx: Builder) -> Result {} + fn visit_unit(&self, ctx: Builder) -> Result { + todo!() + } - fn visit_binary(&self, ctx: Builder, expr: &BinaryExpression) -> Result {} + fn visit_binary(&self, ctx: Builder, expr: &BinaryExpression) -> Result { + todo!() + } - fn visit_unary(&self, ctx: Builder, expr: &UnaryExpression) -> Result {} + fn visit_unary(&self, ctx: Builder, expr: &UnaryExpression) -> Result { + todo!() + } - fn visit_if(&self, ctx: Builder, expr: &If) -> Result {} + fn visit_if(&self, ctx: Builder, expr: &If) -> Result { + todo!() + } - fn visit_table(&self, ctx: Builder, expr: &Table) -> Result {} + fn visit_table(&self, ctx: Builder, expr: &Table) -> Result { + todo!() + } - fn visit_vector(&self, ctx: Builder, expr: &Vector) -> Result {} + fn visit_vector(&self, ctx: Builder, expr: &Vector) -> Result { + todo!() + } - fn visit_for(&self, ctx: Builder, expr: &For) -> Result {} + fn visit_for(&self, ctx: Builder, expr: &For) -> Result { + todo!() + } - fn visit_while(&self, ctx: Builder, expr: &While) -> Result {} + fn visit_while(&self, ctx: Builder, expr: &While) -> Result { + todo!() + } - fn visit_loop(&self, ctx: Builder, expr: &Loop) -> Result {} + fn visit_loop(&self, ctx: Builder, expr: &Loop) -> Result { + todo!() + } - fn visit_match(&self, ctx: Builder, expr: &Match) -> Result {} + fn visit_match(&self, ctx: Builder, expr: &Match) -> Result { + todo!() + } - fn visit_1tuple(&self, ctx: Builder, expr: &Expression) -> Result {} + fn visit_1tuple(&self, ctx: Builder, expr: &Expression) -> Result { + todo!() + } - fn visit_identifier(&self, ctx: Builder, expr: &Identifier) -> Result {} + fn visit_identifier(&self, ctx: Builder, expr: &Identifier) -> Result { + todo!() + } - fn visit_do(&self, ctx: Builder, expr: &Do) -> Result {} + fn visit_do(&self, ctx: Builder, expr: &Do) -> Result { + todo!() + } } diff --git a/src/code/mod.rs b/src/code/mod.rs index f522a22..b2f3e33 100644 --- a/src/code/mod.rs +++ b/src/code/mod.rs @@ -1,10 +1,4 @@ -mod builder; -mod generation; +pub mod builder; +pub mod generation; pub mod macros; -mod visitor; - -pub use builder::Builder; -pub use generation::CodeEmitter; -pub use visitor::{VisitError, Visitor}; - -pub trait BuilderVisitor: Visitor {} +pub mod walker; diff --git a/src/code/visitor.rs b/src/code/visitor.rs deleted file mode 100644 index 21e3d8b..0000000 --- a/src/code/visitor.rs +++ /dev/null @@ -1,81 +0,0 @@ -use ast::*; - -use crate::parser::{ast, Script}; - -#[derive(Debug)] -pub struct VisitError; - -pub trait Visitor { - // Those need to be implemented explicitly by the user: - fn visit_return(&self, ctx: T, stmt: &Return) -> Result; - fn visit_class(&self, ctx: T, stmt: &Class) -> Result; - fn visit_fn(&self, ctx: T, stmt: &Function) -> Result; - fn visit_assignment(&self, ctx: T, stmt: &Assignment) -> Result; - fn visit_declaration(&self, ctx: T, stmt: &Let) -> Result; - fn visit_expression_statement(&self, ctx: T, stmt: &Expression) -> Result; - fn visit_lambda(&self, ctx: T, expr: &Lambda) -> Result; - fn visit_reference(&self, ctx: T, expr: &MemberExpression) -> Result; - fn visit_call(&self, ctx: T, expr: &CallExpression) -> Result; - fn visit_tuple(&self, ctx: T, expr: &Tuple) -> Result; - fn visit_number(&self, ctx: T, expr: &Number) -> Result; - fn visit_string(&self, ctx: T, expr: &StringLiteral) -> Result; - fn visit_unit(&self, ctx: T) -> Result; - fn visit_binary(&self, ctx: T, expr: &BinaryExpression) -> Result; - fn visit_unary(&self, ctx: T, expr: &UnaryExpression) -> Result; - fn visit_if(&self, ctx: T, expr: &If) -> Result; - fn visit_table(&self, ctx: T, expr: &Table) -> Result; - fn visit_vector(&self, ctx: T, expr: &Vector) -> Result; - fn visit_for(&self, ctx: T, expr: &For) -> Result; - fn visit_while(&self, ctx: T, expr: &While) -> Result; - fn visit_loop(&self, ctx: T, expr: &Loop) -> Result; - 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) - } - fn exit_script(&self, ctx: T, _script: &Script) -> Result { - Ok(ctx) - } - - // Generically implementable matching patterns: - fn visit_expression(&self, ctx: T, expression: &Expression) -> Result { - match expression { - Expression::Lambda(e) => self.visit_lambda(ctx, e), - Expression::Reference(e) => self.visit_reference(ctx, e), - Expression::Call(e) => self.visit_call(ctx, e), - Expression::Tuple(e) => self.visit_tuple(ctx, e), - Expression::Number(e) => self.visit_number(ctx, e), - Expression::String(e) => self.visit_string(ctx, e), - Expression::Unit => self.visit_unit(ctx), - Expression::Binary(e) => self.visit_binary(ctx, e), - Expression::Unary(e) => self.visit_unary(ctx, e), - Expression::Table(e) => self.visit_table(ctx, e), - 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 { - let ctx = self.enter_script(ctx, script)?; - let ctx = script - .statements - .iter() - .fold(Ok(ctx), |ctx, stmt| match stmt { - ast::Statement::If(e) => self.visit_if(ctx?, e), - ast::Statement::For(e) => self.visit_for(ctx?, e), - ast::Statement::Loop(e) => self.visit_loop(ctx?, e), - ast::Statement::While(e) => self.visit_while(ctx?, e), - ast::Statement::Return(e) => self.visit_return(ctx?, e), - ast::Statement::Class(e) => self.visit_class(ctx?, e), - ast::Statement::Function(e) => self.visit_fn(ctx?, e), - ast::Statement::Assignment(e) => self.visit_assignment(ctx?, e), - ast::Statement::Let(e) => self.visit_declaration(ctx?, e), - ast::Statement::Match(e) => self.visit_match(ctx?, e), - ast::Statement::Expression(e) => self.visit_expression_statement(ctx?, e), - })?; - self.exit_script(ctx, script) - } -} diff --git a/src/code/walker.rs b/src/code/walker.rs new file mode 100644 index 0000000..e20a04c --- /dev/null +++ b/src/code/walker.rs @@ -0,0 +1,79 @@ +use std::error::Error; + +use ast::*; + +use crate::parser::{ast, Script}; + +use super::builder::Builder; + +#[derive(Debug)] +pub struct VisitError(Box); + +pub type Result = std::result::Result; + +pub trait AstWalker { + // Those need to be implemented explicitly by the user: + fn visit_return(&self, ctx: Builder, stmt: &Return) -> Result; + fn visit_class(&self, ctx: Builder, stmt: &Class) -> Result; + fn visit_fn(&self, ctx: Builder, stmt: &Function) -> Result; + fn visit_assignment(&self, ctx: Builder, stmt: &Assignment) -> Result; + fn visit_declaration(&self, ctx: Builder, stmt: &Let) -> Result; + fn visit_expression_statement(&self, ctx: Builder, stmt: &Expression) -> Result; + fn visit_lambda(&self, ctx: Builder, expr: &Lambda) -> Result; + fn visit_reference(&self, ctx: Builder, expr: &MemberExpression) -> Result; + fn visit_call(&self, ctx: Builder, expr: &CallExpression) -> Result; + fn visit_tuple(&self, ctx: Builder, expr: &Tuple) -> Result; + fn visit_number(&self, ctx: Builder, expr: &Number) -> Result; + fn visit_string(&self, ctx: Builder, expr: &StringLiteral) -> Result; + fn visit_unit(&self, ctx: Builder) -> Result; + fn visit_binary(&self, ctx: Builder, expr: &BinaryExpression) -> Result; + fn visit_unary(&self, ctx: Builder, expr: &UnaryExpression) -> Result; + fn visit_if(&self, ctx: Builder, expr: &If) -> Result; + fn visit_table(&self, ctx: Builder, expr: &Table) -> Result; + fn visit_vector(&self, ctx: Builder, expr: &Vector) -> Result; + fn visit_for(&self, ctx: Builder, expr: &For) -> Result; + fn visit_while(&self, ctx: Builder, expr: &While) -> Result; + fn visit_loop(&self, ctx: Builder, expr: &Loop) -> Result; + fn visit_match(&self, ctx: Builder, expr: &Match) -> Result; + fn visit_1tuple(&self, ctx: Builder, expr: &Expression) -> Result; + fn visit_identifier(&self, ctx: Builder, expr: &Identifier) -> Result; + fn visit_do(&self, ctx: Builder, expr: &Do) -> Result; + + // Generically implementable matching patterns: + fn visit_expression(&self, ctx: Builder, expression: &Expression) -> Result { + match expression { + Expression::Lambda(e) => self.visit_lambda(ctx, e), + Expression::Reference(e) => self.visit_reference(ctx, e), + Expression::Call(e) => self.visit_call(ctx, e), + Expression::Tuple(e) => self.visit_tuple(ctx, e), + Expression::Number(e) => self.visit_number(ctx, e), + Expression::String(e) => self.visit_string(ctx, e), + Expression::Unit => self.visit_unit(ctx), + Expression::Binary(e) => self.visit_binary(ctx, e), + Expression::Unary(e) => self.visit_unary(ctx, e), + Expression::Table(e) => self.visit_table(ctx, e), + 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: Builder, script: &Script) -> Result { + script + .statements + .iter() + .fold(Ok(ctx), |ctx, stmt| match stmt { + ast::Statement::If(e) => self.visit_if(ctx?, e), + ast::Statement::For(e) => self.visit_for(ctx?, e), + ast::Statement::Loop(e) => self.visit_loop(ctx?, e), + ast::Statement::While(e) => self.visit_while(ctx?, e), + ast::Statement::Return(e) => self.visit_return(ctx?, e), + ast::Statement::Class(e) => self.visit_class(ctx?, e), + ast::Statement::Function(e) => self.visit_fn(ctx?, e), + ast::Statement::Assignment(e) => self.visit_assignment(ctx?, e), + ast::Statement::Let(e) => self.visit_declaration(ctx?, e), + ast::Statement::Match(e) => self.visit_match(ctx?, e), + ast::Statement::Expression(e) => self.visit_expression_statement(ctx?, e), + }) + } +} diff --git a/src/lua/mod.rs b/src/lua/mod.rs index d21e3b9..3dd1a02 100644 --- a/src/lua/mod.rs +++ b/src/lua/mod.rs @@ -1,856 +1,856 @@ -use crate::{ - code::{self, VisitError}, - parser::ast::{self}, -}; +// use crate::{ +// code::{self, VisitError}, +// parser::ast::{self}, +// }; -fn escape_string(str: String) -> String { - return str.replace("\n", "\\n"); -} +// 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 - .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", - b':' => "colon", - _ => panic!( - "Error! Unexpected operator {} to be translated as a function!", - ch - ), - } - .to_owned() - }) - .collect::>() - .join("_"); - ctx.put(name) -} +// 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", +// b':' => "colon", +// _ => panic!( +// "Error! Unexpected operator {} to be translated as a function!", +// ch +// ), +// } +// .to_owned() +// }) +// .collect::>() +// .join("_"); +// ctx.put(name) +// } -pub struct LuaEmitter; +// pub struct LuaEmitter; -impl LuaEmitter { - pub fn escape_reference( - &self, - ctx: code::Builder, - ident: &ast::Identifier, - ) -> Result { - let ctx = match &ident.0 { - a if a == "then" => ctx.put("['then']"), - ident => ctx.put(".").put(ident.clone()), - }; - Ok(ctx) - } - pub fn generate_member_segment( - &self, - s: &S, - ctx: code::Builder, - elem: &ast::MemberSegment, - ) -> Result - where - S: code::Visitor, - { - match elem { - ast::MemberSegment::Computed(c) => { - let ctx = ctx.put("["); - let ctx = s.visit_expression(ctx, &c)?; - Ok(ctx.put("]")) - } - ast::MemberSegment::IdentifierDynamic(i) => self.escape_reference(ctx, i), - ast::MemberSegment::IdentifierStatic(_) => Err(VisitError), - } - } - pub fn generate_destructured_assignment( - &self, - ctx: code::Builder, - e: &ast::Destructuring, - ) -> Result { - let mut i = 0; - match e.1 { - ast::DestructureOrigin::Tuple => e.0.iter().fold(Ok(ctx), |ctx, elem| { - let ctx = ctx? - .line() - .put(elem.0.clone()) - .put(" = __destructure__") - .put(format!("._{}", i)) - .put(";"); - i += 1; - Ok(ctx) - }), - ast::DestructureOrigin::Array => e.0.iter().fold(Ok(ctx), |ctx, elem| { - let ctx = ctx? - .line() - .put(elem.0.clone()) - .put(" = __destructure__") - .put(format!("[{}]", i)) - .put(";"); - i += 1; - Ok(ctx) - }), - ast::DestructureOrigin::Table => e.0.iter().fold(Ok(ctx), |ctx, elem| { - let ctx = ctx? - .line() - .put(elem.0.clone()) - .put(" = __destructure__.") - .put(elem.0.clone()) - .put(";"); - Ok(ctx) - }), - } - } -} +// impl LuaEmitter { +// pub fn escape_reference( +// &self, +// ctx: code::Builder, +// ident: &ast::Identifier, +// ) -> Result { +// let ctx = match &ident.0 { +// a if a == "then" => ctx.put("['then']"), +// ident => ctx.put(".").put(ident.clone()), +// }; +// Ok(ctx) +// } +// pub fn generate_member_segment( +// &self, +// s: &S, +// ctx: code::Builder, +// elem: &ast::MemberSegment, +// ) -> Result +// where +// S: code::Visitor, +// { +// match elem { +// ast::MemberSegment::Computed(c) => { +// let ctx = ctx.put("["); +// let ctx = s.visit_expression(ctx, &c)?; +// Ok(ctx.put("]")) +// } +// ast::MemberSegment::IdentifierDynamic(i) => self.escape_reference(ctx, i), +// ast::MemberSegment::IdentifierStatic(_) => Err(VisitError), +// } +// } +// pub fn generate_destructured_assignment( +// &self, +// ctx: code::Builder, +// e: &ast::Destructuring, +// ) -> Result { +// let mut i = 0; +// match e.1 { +// ast::DestructureOrigin::Tuple => e.0.iter().fold(Ok(ctx), |ctx, elem| { +// let ctx = ctx? +// .line() +// .put(elem.0.clone()) +// .put(" = __destructure__") +// .put(format!("._{}", i)) +// .put(";"); +// i += 1; +// Ok(ctx) +// }), +// ast::DestructureOrigin::Array => e.0.iter().fold(Ok(ctx), |ctx, elem| { +// let ctx = ctx? +// .line() +// .put(elem.0.clone()) +// .put(" = __destructure__") +// .put(format!("[{}]", i)) +// .put(";"); +// i += 1; +// Ok(ctx) +// }), +// ast::DestructureOrigin::Table => e.0.iter().fold(Ok(ctx), |ctx, elem| { +// let ctx = ctx? +// .line() +// .put(elem.0.clone()) +// .put(" = __destructure__.") +// .put(elem.0.clone()) +// .put(";"); +// Ok(ctx) +// }), +// } +// } +// } -impl code::Visitor for LuaEmitter { - fn visit_return( - &self, - ctx: code::Builder, - stmt: &ast::Return, - ) -> Result { - let ctx = ctx.line().put("return "); - let ctx = self.visit_expression(ctx, &stmt.value)?; - Ok(ctx.put(";")) - } +// impl code::Visitor for LuaEmitter { +// fn visit_return( +// &self, +// ctx: code::Builder, +// stmt: &ast::Return, +// ) -> Result { +// let ctx = ctx.line().put("return "); +// let ctx = self.visit_expression(ctx, &stmt.value)?; +// 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_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, - expr: &ast::Expression, - ) -> Result { - let ctx = ctx.put("("); - let ctx = self.visit_expression(ctx, expr)?; - Ok(ctx.put(")")) - } +// fn visit_1tuple( +// &self, +// ctx: code::Builder, +// expr: &ast::Expression, +// ) -> Result { +// let ctx = ctx.put("("); +// let ctx = self.visit_expression(ctx, expr)?; +// Ok(ctx.put(")")) +// } - fn visit_identifier( - &self, - ctx: code::Builder, - expr: &ast::Identifier, - ) -> Result { - Ok(ctx.put(expr.0.clone())) - } +// fn visit_identifier( +// &self, +// ctx: code::Builder, +// expr: &ast::Identifier, +// ) -> Result { +// Ok(ctx.put(expr.0.clone())) +// } - fn visit_class( - &self, - ctx: code::Builder, - stmt: &ast::Class, - ) -> Result { - let ctx = ctx - .line() - .put(format!("local {} = {{}};", stmt.name.0.clone())) - .line() - .put(format!("{}.__meta__ = {{}};", stmt.name.0.clone())) - .line() - .put(format!( - "{}.__meta__.__call = function(self, struct)", - stmt.name.0.clone() - )) - .push() - .line() - .put("return setmetatable(struct, self.prototype.__meta__);") - .pop() - .unwrap() - .line() - .put("end;") - .line() - .put(format!("{}.prototype = {{}};", stmt.name.0.clone())) - .line() - .put(format!( - "{}.prototype.__meta__ = {{}};", - stmt.name.0.clone() - )) - .line() - .put(format!( - "{}.prototype.__meta__.__index = {}.prototype;", - stmt.name.0.clone(), - stmt.name.0.clone() - )) - .line() - .put(format!( - "setmetatable({}, {}.__meta__);", - stmt.name.0.clone(), - stmt.name.0.clone() - )); - let ctx = stmt.fields.iter().fold(Ok(ctx), |ctx, field| { - let ctx = ctx?.line(); - let ctx = match field { - ast::ClassField::Method(f) => { - let is_self = if let Some(first) = f.arguments.first() { - first.name.0 == "self" - } else { - false - }; - let level = if is_self { ".prototype." } else { "." }.to_string(); - let ctx = ctx - .put(stmt.name.0.clone()) - .put(level) - .put(f.name.0.clone()) - .put(" = "); - let ctx = self.visit_lambda( - ctx, - &ast::Lambda { - arguments: f.arguments.clone(), - body: ast::ScriptOrExpression::Script(f.body.clone()), - }, - )?; - 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 { - ast::AssignmentTarget::Destructuring(_) => { - panic!("Can't destructure that!") - } - ast::AssignmentTarget::Identifier(e) => ctx.put(format!( - "{}.prototype.{} = ", - stmt.name.0.clone(), - e.0.clone() - )), - }; - let ctx = if let Some(value) = f.value.as_ref() { - self.visit_expression(ctx, value)? - } else { - ctx.put("nil") - }; - ctx.put(";") - } - }; - 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) - } +// fn visit_class( +// &self, +// ctx: code::Builder, +// stmt: &ast::Class, +// ) -> Result { +// let ctx = ctx +// .line() +// .put(format!("local {} = {{}};", stmt.name.0.clone())) +// .line() +// .put(format!("{}.__meta__ = {{}};", stmt.name.0.clone())) +// .line() +// .put(format!( +// "{}.__meta__.__call = function(self, struct)", +// stmt.name.0.clone() +// )) +// .push() +// .line() +// .put("return setmetatable(struct, self.prototype.__meta__);") +// .pop() +// .unwrap() +// .line() +// .put("end;") +// .line() +// .put(format!("{}.prototype = {{}};", stmt.name.0.clone())) +// .line() +// .put(format!( +// "{}.prototype.__meta__ = {{}};", +// stmt.name.0.clone() +// )) +// .line() +// .put(format!( +// "{}.prototype.__meta__.__index = {}.prototype;", +// stmt.name.0.clone(), +// stmt.name.0.clone() +// )) +// .line() +// .put(format!( +// "setmetatable({}, {}.__meta__);", +// stmt.name.0.clone(), +// stmt.name.0.clone() +// )); +// let ctx = stmt.fields.iter().fold(Ok(ctx), |ctx, field| { +// let ctx = ctx?.line(); +// let ctx = match field { +// ast::ClassField::Method(f) => { +// let is_self = if let Some(first) = f.arguments.first() { +// first.name.0 == "self" +// } else { +// false +// }; +// let level = if is_self { ".prototype." } else { "." }.to_string(); +// let ctx = ctx +// .put(stmt.name.0.clone()) +// .put(level) +// .put(f.name.0.clone()) +// .put(" = "); +// let ctx = self.visit_lambda( +// ctx, +// &ast::Lambda { +// arguments: f.arguments.clone(), +// body: ast::ScriptOrExpression::Script(f.body.clone()), +// }, +// )?; +// 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 { +// ast::AssignmentTarget::Destructuring(_) => { +// panic!("Can't destructure that!") +// } +// ast::AssignmentTarget::Identifier(e) => ctx.put(format!( +// "{}.prototype.{} = ", +// stmt.name.0.clone(), +// e.0.clone() +// )), +// }; +// let ctx = if let Some(value) = f.value.as_ref() { +// self.visit_expression(ctx, value)? +// } else { +// ctx.put("nil") +// }; +// ctx.put(";") +// } +// }; +// 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) +// } - fn visit_fn( - &self, - ctx: code::Builder, - stmt: &ast::Function, - ) -> Result { - let ctx = ctx - .line() - .put("local function ") - .put(stmt.name.0.clone()) - .put("("); - let ctx = if let Some(first) = stmt.arguments.first() { - ctx.put(first.name.0.clone()) - } else { - ctx - }; - let ctx = stmt - .arguments - .iter() - .skip(1) - .fold(ctx, |ctx, ident| ctx.put(", ").put(ident.name.0.clone())); - let ctx = if stmt.arguments.len() > 0 { - ctx.put(", ...") - } else { - ctx.put("...") - }; - let ctx = ctx.put(")").push(); - 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), - }, - None => ctx.put("error('Native function implementation not found')"), - } - .line() - .put("-- NATIVE CODE") - } 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(); - 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) - } +// fn visit_fn( +// &self, +// ctx: code::Builder, +// stmt: &ast::Function, +// ) -> Result { +// let ctx = ctx +// .line() +// .put("local function ") +// .put(stmt.name.0.clone()) +// .put("("); +// let ctx = if let Some(first) = stmt.arguments.first() { +// ctx.put(first.name.0.clone()) +// } else { +// ctx +// }; +// let ctx = stmt +// .arguments +// .iter() +// .skip(1) +// .fold(ctx, |ctx, ident| ctx.put(", ").put(ident.name.0.clone())); +// let ctx = if stmt.arguments.len() > 0 { +// ctx.put(", ...") +// } else { +// ctx.put("...") +// }; +// let ctx = ctx.put(")").push(); +// 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), +// }, +// None => ctx.put("error('Native function implementation not found')"), +// } +// .line() +// .put("-- NATIVE CODE") +// } 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(); +// 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) +// } - fn visit_assignment( - &self, - ctx: code::Builder, - stmt: &ast::Assignment, - ) -> Result { - let segment = self - .visit_reference(ctx.clone_like(), &stmt.target)? - .collect(); - let ctx = ctx.line().put(segment).put(" = "); - let ctx = if let Some(extra) = stmt.extra.as_ref() { - let ast::Assignment { target, value, .. } = stmt.clone(); - self.visit_binary( - ctx, - &ast::BinaryExpression { - left: ast::Expression::Reference(Box::new(target)), - operator: extra.clone(), - right: value, - }, - ) - } else { - self.visit_expression(ctx, &stmt.value) - }?; - let ctx = ctx.put(";"); - Ok(ctx) - } +// fn visit_assignment( +// &self, +// ctx: code::Builder, +// stmt: &ast::Assignment, +// ) -> Result { +// let segment = self +// .visit_reference(ctx.clone_like(), &stmt.target)? +// .collect(); +// let ctx = ctx.line().put(segment).put(" = "); +// let ctx = if let Some(extra) = stmt.extra.as_ref() { +// let ast::Assignment { target, value, .. } = stmt.clone(); +// self.visit_binary( +// ctx, +// &ast::BinaryExpression { +// left: ast::Expression::Reference(Box::new(target)), +// operator: extra.clone(), +// right: value, +// }, +// ) +// } else { +// self.visit_expression(ctx, &stmt.value) +// }?; +// let ctx = ctx.put(";"); +// Ok(ctx) +// } - fn visit_declaration( - &self, - ctx: code::Builder, - stmt: &ast::Let, - ) -> Result { - let ctx = match &stmt.target { - ast::AssignmentTarget::Destructuring(e) => { - let ctx = ctx.line().put("local "); - let ctx = ctx.put(e.0.get(0).unwrap().0.clone()); - let ctx = - e.0.iter() - .skip(1) - .fold(ctx, |ctx, elem| ctx.put(", ").put(elem.0.clone())); - let ctx = ctx - .put(";") - .line() - .put("do") - .push() - .line() - .put("local __destructure__ = "); - let ctx = self - .visit_expression(ctx, stmt.value.as_ref().unwrap())? - .put(";"); - self.generate_destructured_assignment(ctx, &e)? - .pop() - .unwrap() - .line() - .put("end") - } - ast::AssignmentTarget::Identifier(e) => { - let ctx = ctx.line().put("local ").put(e.0.clone()).put(" = "); - let ctx = self.visit_expression(ctx, stmt.value.as_ref().unwrap())?; - ctx.put(";") - } - }; - Ok(ctx) - } +// fn visit_declaration( +// &self, +// ctx: code::Builder, +// stmt: &ast::Let, +// ) -> Result { +// let ctx = match &stmt.target { +// ast::AssignmentTarget::Destructuring(e) => { +// let ctx = ctx.line().put("local "); +// let ctx = ctx.put(e.0.get(0).unwrap().0.clone()); +// let ctx = +// e.0.iter() +// .skip(1) +// .fold(ctx, |ctx, elem| ctx.put(", ").put(elem.0.clone())); +// let ctx = ctx +// .put(";") +// .line() +// .put("do") +// .push() +// .line() +// .put("local __destructure__ = "); +// let ctx = self +// .visit_expression(ctx, stmt.value.as_ref().unwrap())? +// .put(";"); +// self.generate_destructured_assignment(ctx, &e)? +// .pop() +// .unwrap() +// .line() +// .put("end") +// } +// ast::AssignmentTarget::Identifier(e) => { +// let ctx = ctx.line().put("local ").put(e.0.clone()).put(" = "); +// let ctx = self.visit_expression(ctx, stmt.value.as_ref().unwrap())?; +// ctx.put(";") +// } +// }; +// Ok(ctx) +// } - fn visit_expression_statement( - &self, - ctx: code::Builder, - stmt: &ast::Expression, - ) -> Result { - Ok(self.visit_expression(ctx.line(), stmt)?.put(";")) - } +// fn visit_expression_statement( +// &self, +// ctx: code::Builder, +// stmt: &ast::Expression, +// ) -> Result { +// Ok(self.visit_expression(ctx.line(), stmt)?.put(";")) +// } - fn visit_lambda( - &self, - ctx: code::Builder, - expr: &ast::Lambda, - ) -> Result { - let ctx = ctx.put("function("); - let ctx = if let Some(first) = expr.arguments.first() { - ctx.put(first.name.0.clone()) - } else { - ctx - }; - let ctx = expr - .arguments - .iter() - .skip(1) - .fold(ctx, |ctx, ident| ctx.put(", ").put(ident.name.0.clone())); - let ctx = if expr.arguments.len() > 0 { - ctx.put(", ...") - } else { - ctx.put("...") - }; - let ctx = ctx.put(")").push(); - let ctx = match &expr.body { - ast::ScriptOrExpression::Script(e) => self.visit_script(ctx, e)?, - ast::ScriptOrExpression::Expression(e) => self - .visit_expression(ctx.line().put("return "), e) - .map(|b| b.put(";"))?, - }; - Ok(ctx.pop().unwrap().line().put("end")) - } +// fn visit_lambda( +// &self, +// ctx: code::Builder, +// expr: &ast::Lambda, +// ) -> Result { +// let ctx = ctx.put("function("); +// let ctx = if let Some(first) = expr.arguments.first() { +// ctx.put(first.name.0.clone()) +// } else { +// ctx +// }; +// let ctx = expr +// .arguments +// .iter() +// .skip(1) +// .fold(ctx, |ctx, ident| ctx.put(", ").put(ident.name.0.clone())); +// let ctx = if expr.arguments.len() > 0 { +// ctx.put(", ...") +// } else { +// ctx.put("...") +// }; +// let ctx = ctx.put(")").push(); +// let ctx = match &expr.body { +// ast::ScriptOrExpression::Script(e) => self.visit_script(ctx, e)?, +// ast::ScriptOrExpression::Expression(e) => self +// .visit_expression(ctx.line().put("return "), e) +// .map(|b| b.put(";"))?, +// }; +// Ok(ctx.pop().unwrap().line().put("end")) +// } - fn visit_reference( - &self, - ctx: code::Builder, - expr: &ast::MemberExpression, - ) -> Result { - let ctx = self.visit_expression(ctx, &expr.head)?; - let ctx = expr.tail.iter().fold(Ok(ctx), |ctx, elem| { - self.generate_member_segment(self, ctx?, elem) - })?; - Ok(ctx) - } +// fn visit_reference( +// &self, +// ctx: code::Builder, +// expr: &ast::MemberExpression, +// ) -> Result { +// let ctx = self.visit_expression(ctx, &expr.head)?; +// let ctx = expr.tail.iter().fold(Ok(ctx), |ctx, elem| { +// self.generate_member_segment(self, ctx?, elem) +// })?; +// Ok(ctx) +// } - fn enter_script( - &self, - ctx: code::Builder, - _script: &crate::parser::Script, - ) -> Result { - Ok(ctx.line().put("local argv = {...};")) - } +// fn enter_script( +// &self, +// ctx: code::Builder, +// _script: &crate::parser::Script, +// ) -> Result { +// Ok(ctx.line().put("local argv = {...};")) +// } - fn visit_call( - &self, - ctx: code::Builder, - expr: &ast::CallExpression, - ) -> Result { - let ctx = if let Some(callee) = expr.head.callee.clone() { - let ctx = self.visit_expression(ctx, &callee.head)?; - let ctx = callee - .tail - .iter() - .rev() - .skip(1) - .rev() - .fold(Ok(ctx), |ctx, elem| { - let ctx = ctx?; - let ctx = match elem { - ast::MemberSegment::Computed(c) => { - let ctx = ctx.put("["); - let ctx = self.visit_expression(ctx, &c)?; - ctx.put("]") - } - ast::MemberSegment::IdentifierDynamic(c) => ctx.put(".").put(c.0.clone()), - ast::MemberSegment::IdentifierStatic(_) => Err(VisitError)?, - }; - Ok(ctx) - })?; - let ctx = if let Some(last) = callee.tail.last() { - match last { - ast::MemberSegment::Computed(c) => { - let ctx = ctx.put("["); - let ctx = self.visit_expression(ctx, &c)?; - ctx.put("]") - } - ast::MemberSegment::IdentifierDynamic(c) => ctx.put(":").put(c.0.clone()), - ast::MemberSegment::IdentifierStatic(c) => ctx.put(".").put(c.0.clone()), - } - } else { - ctx - }; - ctx - } else { - 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 { - ctx - }; - let ctx = expr - .head - .arguments - .iter() - .skip(1) - .fold(Ok(ctx), |ctx, elem| { - let ctx = ctx?.put(", "); - self.visit_expression(ctx, elem) - })?; - let ctx = ctx.put(")"); - let ctx = expr.tail.iter().fold(Ok(ctx), |ctx, elem| match elem { - ast::CallExpressionVariant::Call(c) => self.visit_call( - ctx?, - &ast::CallExpression { - head: c.clone(), - tail: vec![], - }, - ), - ast::CallExpressionVariant::Member(m) => match m { - ast::MemberSegment::Computed(c) => { - let ctx = ctx?.put("["); - let ctx = self.visit_expression(ctx, &c)?; - Ok(ctx.put("]")) - } - ast::MemberSegment::IdentifierStatic(i) => Ok(ctx?.put(".").put(i.0.clone())), - ast::MemberSegment::IdentifierDynamic(i) => Ok(ctx?.put(":").put(i.0.clone())), - }, - })?; - Ok(ctx) - } +// fn visit_call( +// &self, +// ctx: code::Builder, +// expr: &ast::CallExpression, +// ) -> Result { +// let ctx = if let Some(callee) = expr.head.callee.clone() { +// let ctx = self.visit_expression(ctx, &callee.head)?; +// let ctx = callee +// .tail +// .iter() +// .rev() +// .skip(1) +// .rev() +// .fold(Ok(ctx), |ctx, elem| { +// let ctx = ctx?; +// let ctx = match elem { +// ast::MemberSegment::Computed(c) => { +// let ctx = ctx.put("["); +// let ctx = self.visit_expression(ctx, &c)?; +// ctx.put("]") +// } +// ast::MemberSegment::IdentifierDynamic(c) => ctx.put(".").put(c.0.clone()), +// ast::MemberSegment::IdentifierStatic(_) => Err(VisitError)?, +// }; +// Ok(ctx) +// })?; +// let ctx = if let Some(last) = callee.tail.last() { +// match last { +// ast::MemberSegment::Computed(c) => { +// let ctx = ctx.put("["); +// let ctx = self.visit_expression(ctx, &c)?; +// ctx.put("]") +// } +// ast::MemberSegment::IdentifierDynamic(c) => ctx.put(":").put(c.0.clone()), +// ast::MemberSegment::IdentifierStatic(c) => ctx.put(".").put(c.0.clone()), +// } +// } else { +// ctx +// }; +// ctx +// } else { +// 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 { +// ctx +// }; +// let ctx = expr +// .head +// .arguments +// .iter() +// .skip(1) +// .fold(Ok(ctx), |ctx, elem| { +// let ctx = ctx?.put(", "); +// self.visit_expression(ctx, elem) +// })?; +// let ctx = ctx.put(")"); +// let ctx = expr.tail.iter().fold(Ok(ctx), |ctx, elem| match elem { +// ast::CallExpressionVariant::Call(c) => self.visit_call( +// ctx?, +// &ast::CallExpression { +// head: c.clone(), +// tail: vec![], +// }, +// ), +// ast::CallExpressionVariant::Member(m) => match m { +// ast::MemberSegment::Computed(c) => { +// let ctx = ctx?.put("["); +// let ctx = self.visit_expression(ctx, &c)?; +// Ok(ctx.put("]")) +// } +// ast::MemberSegment::IdentifierStatic(i) => Ok(ctx?.put(".").put(i.0.clone())), +// ast::MemberSegment::IdentifierDynamic(i) => Ok(ctx?.put(":").put(i.0.clone())), +// }, +// })?; +// Ok(ctx) +// } - fn visit_tuple( - &self, - ctx: code::Builder, - expr: &ast::Tuple, - ) -> Result { - let ctx = ctx.put("{"); - let ctx = if let Some(first) = expr.0.first().as_ref() { - let ctx = ctx.put(format!("_0 = ")); - self.visit_expression(ctx, first)? - } else { - ctx - }; - let ctx = expr - .0 - .iter() - .skip(1) - .fold(Ok((ctx, 1_u16)), |ctx, value| { - let (ctx, i) = ctx?; - let ctx = ctx.put(format!(", _{} = ", i)); - let ctx = self.visit_expression(ctx, value)?; - Ok((ctx, i + 1)) - })? - .0; - let ctx = ctx.put("}"); - Ok(ctx) - } +// fn visit_tuple( +// &self, +// ctx: code::Builder, +// expr: &ast::Tuple, +// ) -> Result { +// let ctx = ctx.put("{"); +// let ctx = if let Some(first) = expr.0.first().as_ref() { +// let ctx = ctx.put(format!("_0 = ")); +// self.visit_expression(ctx, first)? +// } else { +// ctx +// }; +// let ctx = expr +// .0 +// .iter() +// .skip(1) +// .fold(Ok((ctx, 1_u16)), |ctx, value| { +// let (ctx, i) = ctx?; +// let ctx = ctx.put(format!(", _{} = ", i)); +// let ctx = self.visit_expression(ctx, value)?; +// Ok((ctx, i + 1)) +// })? +// .0; +// let ctx = ctx.put("}"); +// Ok(ctx) +// } - fn visit_number( - &self, - ctx: code::Builder, - expr: &ast::Number, - ) -> Result { - let repr = match expr { - ast::Number::Float(e) => e.to_string(), - ast::Number::Integer(e) => e.to_string(), - }; - Ok(ctx.put(repr)) - } +// fn visit_number( +// &self, +// ctx: code::Builder, +// expr: &ast::Number, +// ) -> Result { +// let repr = match expr { +// ast::Number::Float(e) => e.to_string(), +// ast::Number::Integer(e) => e.to_string(), +// }; +// Ok(ctx.put(repr)) +// } - fn visit_string( - &self, - ctx: code::Builder, - expr: &ast::StringLiteral, - ) -> Result { - let ctx = match expr { - ast::StringLiteral::Double(s) => ctx.put("\"").put(escape_string(s.clone())).put("\""), - }; - Ok(ctx) - } +// fn visit_string( +// &self, +// ctx: code::Builder, +// expr: &ast::StringLiteral, +// ) -> Result { +// let ctx = match expr { +// ast::StringLiteral::Double(s) => ctx.put("\"").put(escape_string(s.clone())).put("\""), +// }; +// Ok(ctx) +// } - fn visit_unit(&self, ctx: code::Builder) -> Result { - Ok(ctx.put("nil")) - } +// fn visit_unit(&self, ctx: code::Builder) -> Result { +// Ok(ctx.put("nil")) +// } - fn visit_binary( - &self, - ctx: code::Builder, - expr: &ast::BinaryExpression, - ) -> Result { - 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) - } - "++" => { - // 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(", "); - let ctx = self.visit_expression(ctx, &expr.right)?.put(")"); - Ok(ctx) - } - } - } +// fn visit_binary( +// &self, +// ctx: code::Builder, +// expr: &ast::BinaryExpression, +// ) -> Result { +// 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) +// } +// "++" => { +// // 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(", "); +// let ctx = self.visit_expression(ctx, &expr.right)?.put(")"); +// Ok(ctx) +// } +// } +// } - fn visit_unary( - &self, - ctx: code::Builder, - expr: &ast::UnaryExpression, - ) -> Result { - 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)?; - Ok(ctx) - } +// fn visit_unary( +// &self, +// ctx: code::Builder, +// expr: &ast::UnaryExpression, +// ) -> Result { +// 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)?; +// Ok(ctx) +// } - fn visit_if( - &self, - ctx: code::Builder, - expr: &ast::If, - ) -> Result { - let ctx = ctx.line().put("if "); - let ctx = self.visit_expression(ctx, &expr.condition)?; - let ctx = ctx.put(" then").push(); - let ctx = self.visit_script(ctx, &expr.body)?; - let ctx = expr.branches.iter().fold(Ok(ctx), |ctx, (c, s)| { - let ctx = ctx?.pop().unwrap().line().put("elseif "); - let ctx = self.visit_expression(ctx, c)?; - let ctx = ctx.put(" then").push(); - let ctx = self.visit_script(ctx, s)?; - Ok(ctx) - })?; - let ctx = if let Some(eb) = expr.else_branch.as_ref() { - let ctx = ctx.pop().unwrap().line().put("else").push(); - self.visit_script(ctx, eb)? - } else { - ctx - }; - let ctx = ctx.pop().unwrap().line().put("end"); - Ok(ctx) - } +// fn visit_if( +// &self, +// ctx: code::Builder, +// expr: &ast::If, +// ) -> Result { +// let ctx = ctx.line().put("if "); +// let ctx = self.visit_expression(ctx, &expr.condition)?; +// let ctx = ctx.put(" then").push(); +// let ctx = self.visit_script(ctx, &expr.body)?; +// let ctx = expr.branches.iter().fold(Ok(ctx), |ctx, (c, s)| { +// let ctx = ctx?.pop().unwrap().line().put("elseif "); +// let ctx = self.visit_expression(ctx, c)?; +// let ctx = ctx.put(" then").push(); +// let ctx = self.visit_script(ctx, s)?; +// Ok(ctx) +// })?; +// let ctx = if let Some(eb) = expr.else_branch.as_ref() { +// let ctx = ctx.pop().unwrap().line().put("else").push(); +// self.visit_script(ctx, eb)? +// } else { +// ctx +// }; +// let ctx = ctx.pop().unwrap().line().put("end"); +// Ok(ctx) +// } - fn visit_table( - &self, - ctx: code::Builder, - expr: &ast::Table, - ) -> Result { - let ctx = ctx.put("{"); - let ctx = if let Some((k, v)) = expr.key_values.first() { - match k { - ast::TableKeyExpression::Identifier(k) => { - let ctx = ctx.put(k.0.clone()).put(" = "); - self.visit_expression(ctx, &v.clone().unwrap()) - } - ast::TableKeyExpression::Expression(k) => { - let ctx = ctx.put("["); - let ctx = self.visit_expression(ctx, &k)?.put("] = "); - self.visit_expression(ctx, &v.clone().unwrap()) - } - ast::TableKeyExpression::Implicit(k) => { - let ctx = ctx.put(k.0.clone()).put(" = ").put(k.0.clone()); - Ok(ctx) - } - }? - } else { - ctx - }; - let ctx = expr - .key_values - .iter() - .skip(1) - .fold(Ok(ctx), |ctx, (k, v)| { - let ctx = ctx?.put(", "); - match k { - ast::TableKeyExpression::Identifier(k) => { - let ctx = ctx.put(k.0.clone()).put(" = "); - self.visit_expression(ctx, &v.clone().unwrap()) - } - ast::TableKeyExpression::Expression(k) => { - let ctx = ctx.put("["); - let ctx = self.visit_expression(ctx, &k)?.put("] = "); - self.visit_expression(ctx, &v.clone().unwrap()) - } - ast::TableKeyExpression::Implicit(k) => { - let ctx = ctx.put(k.0.clone()).put(" = ").put(k.0.clone()); - Ok(ctx) - } - } - })?; - Ok(ctx.put("}")) - } +// fn visit_table( +// &self, +// ctx: code::Builder, +// expr: &ast::Table, +// ) -> Result { +// let ctx = ctx.put("{"); +// let ctx = if let Some((k, v)) = expr.key_values.first() { +// match k { +// ast::TableKeyExpression::Identifier(k) => { +// let ctx = ctx.put(k.0.clone()).put(" = "); +// self.visit_expression(ctx, &v.clone().unwrap()) +// } +// ast::TableKeyExpression::Expression(k) => { +// let ctx = ctx.put("["); +// let ctx = self.visit_expression(ctx, &k)?.put("] = "); +// self.visit_expression(ctx, &v.clone().unwrap()) +// } +// ast::TableKeyExpression::Implicit(k) => { +// let ctx = ctx.put(k.0.clone()).put(" = ").put(k.0.clone()); +// Ok(ctx) +// } +// }? +// } else { +// ctx +// }; +// let ctx = expr +// .key_values +// .iter() +// .skip(1) +// .fold(Ok(ctx), |ctx, (k, v)| { +// let ctx = ctx?.put(", "); +// match k { +// ast::TableKeyExpression::Identifier(k) => { +// let ctx = ctx.put(k.0.clone()).put(" = "); +// self.visit_expression(ctx, &v.clone().unwrap()) +// } +// ast::TableKeyExpression::Expression(k) => { +// let ctx = ctx.put("["); +// let ctx = self.visit_expression(ctx, &k)?.put("] = "); +// self.visit_expression(ctx, &v.clone().unwrap()) +// } +// ast::TableKeyExpression::Implicit(k) => { +// let ctx = ctx.put(k.0.clone()).put(" = ").put(k.0.clone()); +// Ok(ctx) +// } +// } +// })?; +// Ok(ctx.put("}")) +// } - fn visit_vector( - &self, - ctx: code::Builder, - expr: &ast::Vector, - ) -> Result { - let ctx = ctx.put("{"); - let ctx = if let Some(first) = expr.expressions.first() { - self.visit_expression(ctx, first)? - } else { - ctx - }; - let ctx = expr.expressions.iter().skip(1).fold(Ok(ctx), |ctx, v| { - let ctx = ctx?.put(", "); - let ctx = self.visit_expression(ctx, v)?; - Ok(ctx) - })?; - Ok(ctx.put("}")) - } +// fn visit_vector( +// &self, +// ctx: code::Builder, +// expr: &ast::Vector, +// ) -> Result { +// let ctx = ctx.put("{"); +// let ctx = if let Some(first) = expr.expressions.first() { +// self.visit_expression(ctx, first)? +// } else { +// ctx +// }; +// let ctx = expr.expressions.iter().skip(1).fold(Ok(ctx), |ctx, v| { +// let ctx = ctx?.put(", "); +// let ctx = self.visit_expression(ctx, v)?; +// Ok(ctx) +// })?; +// Ok(ctx.put("}")) +// } - fn visit_for( - &self, - ctx: code::Builder, - expr: &ast::For, - ) -> Result { - let ctx = match &expr.handler { - ast::AssignmentTarget::Destructuring(e) => { - 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)?; - let ctx = self.visit_script(ctx, &expr.body)?; - ctx.pop().unwrap().line().put("end") - } - ast::AssignmentTarget::Identifier(e) => { - let ctx = ctx.line().put(format!("for {} in ", e.0.clone())); - let ctx = self.visit_expression(ctx, &expr.target)?; - let ctx = ctx.put(" do").push(); - let ctx = self.visit_script(ctx, &expr.body)?; - ctx.pop().unwrap().line().put("end") - } - }; - Ok(ctx) - } +// fn visit_for( +// &self, +// ctx: code::Builder, +// expr: &ast::For, +// ) -> Result { +// let ctx = match &expr.handler { +// ast::AssignmentTarget::Destructuring(e) => { +// 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)?; +// let ctx = self.visit_script(ctx, &expr.body)?; +// ctx.pop().unwrap().line().put("end") +// } +// ast::AssignmentTarget::Identifier(e) => { +// let ctx = ctx.line().put(format!("for {} in ", e.0.clone())); +// let ctx = self.visit_expression(ctx, &expr.target)?; +// let ctx = ctx.put(" do").push(); +// let ctx = self.visit_script(ctx, &expr.body)?; +// ctx.pop().unwrap().line().put("end") +// } +// }; +// Ok(ctx) +// } - fn visit_while( - &self, - ctx: code::Builder, - expr: &ast::While, - ) -> Result { - let ctx = match &expr.condition { - ast::ExpressionOrLet::Expression(e) => { - let ctx = ctx.line().put("while "); - let ctx = self.visit_expression(ctx, e)?; - let ctx = ctx.put(" do").push(); - let ctx = self.visit_script(ctx, &expr.body)?; - ctx.pop().unwrap().line().put("end") - } - ast::ExpressionOrLet::Let(e) => { - if let ast::AssignmentTarget::Identifier(id) = &e.target { - let ctx = ctx.line().put("do").push(); - let ctx = self.visit_declaration(ctx, e)?; - let ctx = ctx.line().put(format!("while {} do", id.0.clone())).push(); - let ctx = self.visit_script(ctx, &expr.body)?; - let ctx = self.visit_assignment( - ctx, - &ast::Assignment { - target: ast::MemberExpression { - head: ast::Expression::Identifier(id.clone()), - tail: vec![], - }, - value: e.value.clone().unwrap(), - extra: None, - }, - )?; - ctx.pop() - .unwrap() - .line() - .put("end") - .pop() - .unwrap() - .line() - .put("end") - } else { - panic!("Destructured while-let not supported"); - } - } - }; - Ok(ctx) - } +// fn visit_while( +// &self, +// ctx: code::Builder, +// expr: &ast::While, +// ) -> Result { +// let ctx = match &expr.condition { +// ast::ExpressionOrLet::Expression(e) => { +// let ctx = ctx.line().put("while "); +// let ctx = self.visit_expression(ctx, e)?; +// let ctx = ctx.put(" do").push(); +// let ctx = self.visit_script(ctx, &expr.body)?; +// ctx.pop().unwrap().line().put("end") +// } +// ast::ExpressionOrLet::Let(e) => { +// if let ast::AssignmentTarget::Identifier(id) = &e.target { +// let ctx = ctx.line().put("do").push(); +// let ctx = self.visit_declaration(ctx, e)?; +// let ctx = ctx.line().put(format!("while {} do", id.0.clone())).push(); +// let ctx = self.visit_script(ctx, &expr.body)?; +// let ctx = self.visit_assignment( +// ctx, +// &ast::Assignment { +// target: ast::MemberExpression { +// head: ast::Expression::Identifier(id.clone()), +// tail: vec![], +// }, +// value: e.value.clone().unwrap(), +// extra: None, +// }, +// )?; +// ctx.pop() +// .unwrap() +// .line() +// .put("end") +// .pop() +// .unwrap() +// .line() +// .put("end") +// } else { +// panic!("Destructured while-let not supported"); +// } +// } +// }; +// Ok(ctx) +// } - fn visit_loop( - &self, - ctx: code::Builder, - expr: &ast::Loop, - ) -> Result { - let ctx = ctx.line().put("while true do").push(); - let ctx = self.visit_script(ctx, &expr.body)?; - let ctx = ctx.pop().unwrap().line().put("end"); - Ok(ctx) - } +// fn visit_loop( +// &self, +// ctx: code::Builder, +// expr: &ast::Loop, +// ) -> Result { +// let ctx = ctx.line().put("while true do").push(); +// let ctx = self.visit_script(ctx, &expr.body)?; +// let ctx = ctx.pop().unwrap().line().put("end"); +// Ok(ctx) +// } - fn visit_match( - &self, - _ctx: code::Builder, - _expr: &ast::Match, - ) -> Result { - todo!("Match code generation not implemented yet.") - } -} +// fn visit_match( +// &self, +// _ctx: code::Builder, +// _expr: &ast::Match, +// ) -> Result { +// todo!("Match code generation not implemented yet.") +// } +// } diff --git a/src/main.rs b/src/main.rs index 15b13d5..1420643 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ use std::{fs::File, io::Write, path::Path}; use clap::Parser; -use code::Visitor; use errors::report_error; use runtime::RuntimeError; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 546246f..ce5d967 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -94,9 +94,7 @@ pub struct Class { #[derive(Debug, Clone)] pub struct CallSubExpression { pub callee: Option, - // pub static_target: Option, pub arguments: Vec, - pub is_macro: bool, } impl Into for CallSubExpression { fn into(self) -> CallExpressionVariant { @@ -110,6 +108,18 @@ pub enum CallExpressionVariant { Member(MemberSegment), } +#[derive(Debug, Clone)] +pub enum MacroCallArguments { + FunctionLike(Vec), + BlockLike(Script), +} + +#[derive(Debug, Clone)] +pub struct MacroCallExpression { + pub target: Identifier, + pub arguments: MacroCallArguments, +} + #[derive(Debug, Clone)] pub struct CallExpression { pub head: CallSubExpression, diff --git a/src/parser/grammar.rs b/src/parser/grammar.rs index e396805..c643e22 100644 --- a/src/parser/grammar.rs +++ b/src/parser/grammar.rs @@ -92,16 +92,16 @@ peg::parser! { 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() _ arguments:call_arguments() + { CallSubExpression { callee: Some(callee), arguments }.into() } / callee:member_expression() _ arg:table_expression() - { CallSubExpression { callee: Some(callee), is_macro: false, arguments: vec![arg] } } + { CallSubExpression { callee: Some(callee), 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() } + / _ arguments:call_arguments() { CallSubExpression { callee: None, arguments }.into() } )* { CallExpression { head, tail } } From dc7555a7d237ee50894d2d33d825b993250c78f1 Mon Sep 17 00:00:00 2001 From: sigmasoldi3r Date: Sat, 4 Nov 2023 17:37:01 +0100 Subject: [PATCH 3/3] formatted visitor simplified --- src/code/{walker.rs => ast_visitor.rs} | 54 +- src/code/generation.rs | 158 ----- src/code/mod.rs | 3 +- src/lua/helpers.rs | 0 src/lua/mod.rs | 878 +------------------------ src/lua/visitor.rs | 781 ++++++++++++++++++++++ src/main.rs | 29 +- src/parser/ast.rs | 1 + src/runtime.rs | 25 +- 9 files changed, 847 insertions(+), 1082 deletions(-) rename src/code/{walker.rs => ast_visitor.rs} (81%) delete mode 100644 src/code/generation.rs create mode 100644 src/lua/helpers.rs create mode 100644 src/lua/visitor.rs diff --git a/src/code/walker.rs b/src/code/ast_visitor.rs similarity index 81% rename from src/code/walker.rs rename to src/code/ast_visitor.rs index e20a04c..c004f25 100644 --- a/src/code/walker.rs +++ b/src/code/ast_visitor.rs @@ -7,37 +7,49 @@ use crate::parser::{ast, Script}; use super::builder::Builder; #[derive(Debug)] -pub struct VisitError(Box); +pub struct VisitError(pub Box); pub type Result = std::result::Result; -pub trait AstWalker { - // Those need to be implemented explicitly by the user: - fn visit_return(&self, ctx: Builder, stmt: &Return) -> Result; - fn visit_class(&self, ctx: Builder, stmt: &Class) -> Result; - fn visit_fn(&self, ctx: Builder, stmt: &Function) -> Result; - fn visit_assignment(&self, ctx: Builder, stmt: &Assignment) -> Result; - fn visit_declaration(&self, ctx: Builder, stmt: &Let) -> Result; - fn visit_expression_statement(&self, ctx: Builder, stmt: &Expression) -> Result; - fn visit_lambda(&self, ctx: Builder, expr: &Lambda) -> Result; +pub trait Visitor { + // Expressions fn visit_reference(&self, ctx: Builder, expr: &MemberExpression) -> Result; fn visit_call(&self, ctx: Builder, expr: &CallExpression) -> Result; + fn visit_binary(&self, ctx: Builder, expr: &BinaryExpression) -> Result; + fn visit_unary(&self, ctx: Builder, expr: &UnaryExpression) -> Result; + fn visit_wrapped_expression(&self, ctx: Builder, expr: &Expression) -> Result; + fn visit_identifier(&self, ctx: Builder, expr: &Identifier) -> Result; + fn visit_use_expression(&self, ctx: Builder, expr: &Identifier) -> Result; + + // Literals + fn visit_lambda(&self, ctx: Builder, expr: &Lambda) -> Result; fn visit_tuple(&self, ctx: Builder, expr: &Tuple) -> Result; + fn visit_unit(&self, ctx: Builder) -> Result; fn visit_number(&self, ctx: Builder, expr: &Number) -> Result; fn visit_string(&self, ctx: Builder, expr: &StringLiteral) -> Result; - fn visit_unit(&self, ctx: Builder) -> Result; - fn visit_binary(&self, ctx: Builder, expr: &BinaryExpression) -> Result; - fn visit_unary(&self, ctx: Builder, expr: &UnaryExpression) -> Result; - fn visit_if(&self, ctx: Builder, expr: &If) -> Result; fn visit_table(&self, ctx: Builder, expr: &Table) -> Result; fn visit_vector(&self, ctx: Builder, expr: &Vector) -> Result; + + // Statements + fn visit_return(&self, ctx: Builder, stmt: &Return) -> Result; + fn visit_class(&self, ctx: Builder, stmt: &Class) -> Result; + fn visit_fn(&self, ctx: Builder, stmt: &Function) -> Result; + fn visit_assignment(&self, ctx: Builder, stmt: &Assignment) -> Result; + fn visit_declaration(&self, ctx: Builder, stmt: &Let) -> Result; + fn visit_expression_statement(&self, ctx: Builder, stmt: &Expression) -> Result; + fn visit_use_statement(&self, ctx: Builder, stmt: &Identifier) -> Result; + + // Looping fn visit_for(&self, ctx: Builder, expr: &For) -> Result; fn visit_while(&self, ctx: Builder, expr: &While) -> Result; fn visit_loop(&self, ctx: Builder, expr: &Loop) -> Result; + + // Conditionals + fn visit_if(&self, ctx: Builder, expr: &If) -> Result; fn visit_match(&self, ctx: Builder, expr: &Match) -> Result; - fn visit_1tuple(&self, ctx: Builder, expr: &Expression) -> Result; - fn visit_identifier(&self, ctx: Builder, expr: &Identifier) -> Result; - fn visit_do(&self, ctx: Builder, expr: &Do) -> Result; + + fn visit_block_expression(&self, ctx: Builder, expr: &Do) -> Result; + fn visit_script(&self, ctx: Builder, script: &Script) -> Result; // Generically implementable matching patterns: fn visit_expression(&self, ctx: Builder, expression: &Expression) -> Result { @@ -53,12 +65,13 @@ pub trait AstWalker { Expression::Unary(e) => self.visit_unary(ctx, e), Expression::Table(e) => self.visit_table(ctx, e), Expression::Vector(e) => self.visit_vector(ctx, e), - Expression::Tuple1(e) => self.visit_1tuple(ctx, e), + Expression::Tuple1(e) => self.visit_wrapped_expression(ctx, e), Expression::Identifier(e) => self.visit_identifier(ctx, e), - Expression::Do(e) => self.visit_do(ctx, e), + Expression::Do(e) => self.visit_block_expression(ctx, e), + Expression::Use(e) => self.visit_use_expression(ctx, e), } } - fn visit_script(&self, ctx: Builder, script: &Script) -> Result { + fn visit_block(&self, ctx: Builder, script: &Script) -> Result { script .statements .iter() @@ -74,6 +87,7 @@ pub trait AstWalker { ast::Statement::Let(e) => self.visit_declaration(ctx?, e), ast::Statement::Match(e) => self.visit_match(ctx?, e), ast::Statement::Expression(e) => self.visit_expression_statement(ctx?, e), + ast::Statement::Use(e) => self.visit_use_statement(ctx?, e), }) } } diff --git a/src/code/generation.rs b/src/code/generation.rs deleted file mode 100644 index 84e1deb..0000000 --- a/src/code/generation.rs +++ /dev/null @@ -1,158 +0,0 @@ -use std::error::Error; - -use crate::parser::ast::*; - -use super::{ - builder::Builder, - walker::{AstWalker, Result}, -}; - -#[derive(Debug)] -struct ConstructionError(Box); - -pub trait CodeBuilder { - // Classes - fn build_class(&self, ctx: Builder, target: &Class) -> Result; - fn build_class_method(&self, ctx: Builder, target: &Function) -> Result; - fn build_class_value(&self, ctx: Builder, target: &Let) -> Result; - fn build_class_decorator(&self, ctx: Builder, target: &Decorator) -> Result; - fn build_class_field_decorator(&self, ctx: Builder, target: &Decorator) -> Result; - - // Functions - fn build_function(&self, ctx: Builder, target: &Function) -> Result; - fn build_function_decorator(&self, ctx: Builder, target: &Decorator) -> Result; - fn build_macro_function(&self, ctx: Builder, target: &Function) -> Result; - fn build_native_function(&self, ctx: Builder, target: &Function) -> Result; - - // Lambdas and variables - fn build_lambda(&self, ctx: Builder, target: &Lambda) -> Result; - fn build_let(&self, ctx: Builder, target: &Let) -> Result; - fn build_use_statement(&self, ctx: Builder, target: &Let) -> Result; - fn build_use_expression(&self, ctx: Builder, target: &Let) -> Result; - fn build_return(&self, ctx: Builder, target: &Return) -> Result; - fn build_unit(&self, ctx: Builder, target: ()) -> Result; - - // Conditional and looping blocks - fn build_if(&self, ctx: Builder, target: &If) -> Result; - fn build_for(&self, ctx: Builder, target: &For) -> Result; - fn build_while(&self, ctx: Builder, target: &While) -> Result; - fn build_loop(&self, ctx: Builder, target: &Loop) -> Result; - fn build_while_let(&self, ctx: Builder, target: &While) -> Result; - - // Script blocks - fn build_block(&self, ctx: Builder, target: &Class) -> Result; - fn build_script(&self, ctx: Builder, target: &Class) -> Result; -} - -pub struct CodeEmitter { - pub builder: Box, -} - -impl AstWalker for CodeEmitter { - fn visit_return(&self, ctx: Builder, stmt: &Return) -> Result { - self.builder.build_return(ctx, stmt) - } - - fn visit_class(&self, ctx: Builder, stmt: &Class) -> Result { - let ctx = self.builder.build_class(ctx, stmt)?; - let ctx = stmt.fields.iter().fold(Ok(ctx), |ctx, field| match field { - ClassField::Method(field) => self.builder.build_class_method(ctx?, field), - ClassField::Let(field) => self.builder.build_class_value(ctx?, field), - })?; - let ctx = stmt.decorators.iter().fold(Ok(ctx), |ctx, dec| { - self.builder.build_class_decorator(ctx?, dec) - })?; - } - - fn visit_fn(&self, ctx: Builder, stmt: &Function) -> Result { - todo!() - } - - fn visit_assignment(&self, ctx: Builder, stmt: &Assignment) -> Result { - todo!() - } - - fn visit_declaration(&self, ctx: Builder, stmt: &Let) -> Result { - todo!() - } - - fn visit_expression_statement(&self, ctx: Builder, stmt: &Expression) -> Result { - todo!() - } - - fn visit_lambda(&self, ctx: Builder, expr: &Lambda) -> Result { - todo!() - } - - fn visit_reference(&self, ctx: Builder, expr: &MemberExpression) -> Result { - todo!() - } - - fn visit_call(&self, ctx: Builder, expr: &CallExpression) -> Result { - todo!() - } - - fn visit_tuple(&self, ctx: Builder, expr: &Tuple) -> Result { - todo!() - } - - fn visit_number(&self, ctx: Builder, expr: &Number) -> Result { - todo!() - } - - fn visit_string(&self, ctx: Builder, expr: &StringLiteral) -> Result { - todo!() - } - - fn visit_unit(&self, ctx: Builder) -> Result { - todo!() - } - - fn visit_binary(&self, ctx: Builder, expr: &BinaryExpression) -> Result { - todo!() - } - - fn visit_unary(&self, ctx: Builder, expr: &UnaryExpression) -> Result { - todo!() - } - - fn visit_if(&self, ctx: Builder, expr: &If) -> Result { - todo!() - } - - fn visit_table(&self, ctx: Builder, expr: &Table) -> Result { - todo!() - } - - fn visit_vector(&self, ctx: Builder, expr: &Vector) -> Result { - todo!() - } - - fn visit_for(&self, ctx: Builder, expr: &For) -> Result { - todo!() - } - - fn visit_while(&self, ctx: Builder, expr: &While) -> Result { - todo!() - } - - fn visit_loop(&self, ctx: Builder, expr: &Loop) -> Result { - todo!() - } - - fn visit_match(&self, ctx: Builder, expr: &Match) -> Result { - todo!() - } - - fn visit_1tuple(&self, ctx: Builder, expr: &Expression) -> Result { - todo!() - } - - fn visit_identifier(&self, ctx: Builder, expr: &Identifier) -> Result { - todo!() - } - - fn visit_do(&self, ctx: Builder, expr: &Do) -> Result { - todo!() - } -} diff --git a/src/code/mod.rs b/src/code/mod.rs index b2f3e33..0472280 100644 --- a/src/code/mod.rs +++ b/src/code/mod.rs @@ -1,4 +1,3 @@ +pub mod ast_visitor; pub mod builder; -pub mod generation; pub mod macros; -pub mod walker; diff --git a/src/lua/helpers.rs b/src/lua/helpers.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lua/mod.rs b/src/lua/mod.rs index 9fac48c..8e6e8a0 100644 --- a/src/lua/mod.rs +++ b/src/lua/mod.rs @@ -1,876 +1,2 @@ -use crate::{ - code::{self, VisitError}, - 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 - .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", - b':' => "colon", - _ => panic!( - "Error! Unexpected operator {} to be translated as a function!", - ch - ), - } - .to_owned() - }) - .collect::>() - .join("_"); - ctx.put(name) -} - -pub struct LuaEmitter; - -impl LuaEmitter { - pub fn escape_reference( - &self, - ctx: code::Builder, - ident: &ast::Identifier, - ) -> Result { - let ctx = match &ident.0 { - a if a == "then" => ctx.put("['then']"), - ident => ctx.put(".").put(ident.clone()), - }; - Ok(ctx) - } - pub fn generate_member_segment( - &self, - s: &S, - ctx: code::Builder, - elem: &ast::MemberSegment, - ) -> Result - where - S: code::Visitor, - { - match elem { - ast::MemberSegment::Computed(c) => { - let ctx = ctx.put("["); - let ctx = s.visit_expression(ctx, &c)?; - Ok(ctx.put("]")) - } - ast::MemberSegment::IdentifierDynamic(i) => self.escape_reference(ctx, i), - ast::MemberSegment::IdentifierStatic(_) => Err(VisitError), - } - } - pub fn generate_destructured_assignment( - &self, - ctx: code::Builder, - e: &ast::Destructuring, - ) -> Result { - let mut i = 0; - match e.1 { - ast::DestructureOrigin::Tuple => e.0.iter().fold(Ok(ctx), |ctx, elem| { - let ctx = ctx? - .line() - .put(elem.0.clone()) - .put(" = __destructure__") - .put(format!("._{}", i)) - .put(";"); - i += 1; - Ok(ctx) - }), - ast::DestructureOrigin::Array => e.0.iter().fold(Ok(ctx), |ctx, elem| { - let ctx = ctx? - .line() - .put(elem.0.clone()) - .put(" = __destructure__") - .put(format!("[{}]", i)) - .put(";"); - i += 1; - Ok(ctx) - }), - ast::DestructureOrigin::Table => e.0.iter().fold(Ok(ctx), |ctx, elem| { - let ctx = ctx? - .line() - .put(elem.0.clone()) - .put(" = __destructure__.") - .put(elem.0.clone()) - .put(";"); - Ok(ctx) - }), - } - } -} - -impl code::Visitor for LuaEmitter { - fn visit_return( - &self, - ctx: code::Builder, - stmt: &ast::Return, - ) -> Result { - let ctx = ctx.line().put("return "); - let ctx = self.visit_expression(ctx, &stmt.value)?; - 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, - expr: &ast::Expression, - ) -> Result { - let ctx = ctx.put("("); - let ctx = self.visit_expression(ctx, expr)?; - Ok(ctx.put(")")) - } - - fn visit_identifier( - &self, - ctx: code::Builder, - expr: &ast::Identifier, - ) -> Result { - Ok(ctx.put(expr.0.clone())) - } - - fn visit_class( - &self, - ctx: code::Builder, - stmt: &ast::Class, - ) -> Result { - let ctx = ctx - .line() - .put(format!("local {} = {{}};", stmt.name.0.clone())) - .line() - .put(format!("{}.__meta__ = {{}};", stmt.name.0.clone())) - .line() - .put(format!( - "{}.__meta__.__call = function(self, struct)", - stmt.name.0.clone() - )) - .push() - .line() - .put("return setmetatable(struct, self.prototype.__meta__);") - .pop() - .unwrap() - .line() - .put("end;") - .line() - .put(format!("{}.prototype = {{}};", stmt.name.0.clone())) - .line() - .put(format!( - "{}.prototype.__meta__ = {{}};", - stmt.name.0.clone() - )) - .line() - .put(format!( - "{}.prototype.__meta__.__index = {}.prototype;", - stmt.name.0.clone(), - stmt.name.0.clone() - )) - .line() - .put(format!( - "setmetatable({}, {}.__meta__);", - stmt.name.0.clone(), - stmt.name.0.clone() - )); - let ctx = stmt.fields.iter().fold(Ok(ctx), |ctx, field| { - let ctx = ctx?.line(); - let ctx = match field { - ast::ClassField::Method(f) => { - let is_self = if let Some(first) = f.arguments.first() { - first.name.0 == "self" - } else { - false - }; - let level = if is_self { ".prototype." } else { "." }.to_string(); - let ctx = ctx - .put(stmt.name.0.clone()) - .put(level) - .put(f.name.0.clone()) - .put(" = "); - let ctx = self.visit_lambda( - ctx, - &ast::Lambda { - arguments: f.arguments.clone(), - body: ast::ScriptOrExpression::Script(f.body.clone()), - }, - )?; - 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 { - ast::AssignmentTarget::Destructuring(_) => { - panic!("Can't destructure that!") - } - ast::AssignmentTarget::Identifier(e) => ctx.put(format!( - "{}.prototype.{} = ", - stmt.name.0.clone(), - e.0.clone() - )), - }; - let ctx = if let Some(value) = f.value.as_ref() { - self.visit_expression(ctx, value)? - } else { - ctx.put("nil") - }; - ctx.put(";") - } - }; - 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) - } - - fn visit_fn( - &self, - ctx: code::Builder, - stmt: &ast::Function, - ) -> Result { - let ctx = ctx - .line() - .put("local function ") - .put(stmt.name.0.clone()) - .put("("); - let ctx = if let Some(first) = stmt.arguments.first() { - ctx.put(first.name.0.clone()) - } else { - ctx - }; - let ctx = stmt - .arguments - .iter() - .skip(1) - .fold(ctx, |ctx, ident| ctx.put(", ").put(ident.name.0.clone())); - let ctx = if stmt.arguments.len() > 0 { - ctx.put(", ...") - } else { - ctx.put("...") - }; - let ctx = ctx.put(")").push(); - 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), - }, - None => ctx.put("error('Native function implementation not found')"), - } - .line() - .put("-- NATIVE CODE") - } 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(); - 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) - } - - fn visit_assignment( - &self, - ctx: code::Builder, - stmt: &ast::Assignment, - ) -> Result { - let segment = self - .visit_reference(ctx.clone_like(), &stmt.target)? - .collect(); - let ctx = ctx.line().put(segment).put(" = "); - let ctx = if let Some(extra) = stmt.extra.as_ref() { - let ast::Assignment { target, value, .. } = stmt.clone(); - self.visit_binary( - ctx, - &ast::BinaryExpression { - left: ast::Expression::Reference(Box::new(target)), - operator: extra.clone(), - right: value, - }, - ) - } else { - self.visit_expression(ctx, &stmt.value) - }?; - let ctx = ctx.put(";"); - Ok(ctx) - } - - fn visit_declaration( - &self, - ctx: code::Builder, - stmt: &ast::Let, - ) -> Result { - let ctx = match &stmt.target { - ast::AssignmentTarget::Destructuring(e) => { - let ctx = ctx.line().put("local "); - let ctx = ctx.put(e.0.get(0).unwrap().0.clone()); - let ctx = - e.0.iter() - .skip(1) - .fold(ctx, |ctx, elem| ctx.put(", ").put(elem.0.clone())); - let ctx = ctx - .put(";") - .line() - .put("do") - .push() - .line() - .put("local __destructure__ = "); - let ctx = self - .visit_expression(ctx, stmt.value.as_ref().unwrap())? - .put(";"); - self.generate_destructured_assignment(ctx, &e)? - .pop() - .unwrap() - .line() - .put("end") - } - ast::AssignmentTarget::Identifier(e) => { - let ctx = ctx.line().put("local ").put(e.0.clone()).put(" = "); - let ctx = self.visit_expression(ctx, stmt.value.as_ref().unwrap())?; - ctx.put(";") - } - }; - Ok(ctx) - } - - fn visit_expression_statement( - &self, - ctx: code::Builder, - stmt: &ast::Expression, - ) -> Result { - Ok(self.visit_expression(ctx.line(), stmt)?.put(";")) - } - - fn visit_lambda( - &self, - ctx: code::Builder, - expr: &ast::Lambda, - ) -> Result { - let ctx = ctx.put("function("); - let ctx = if let Some(first) = expr.arguments.first() { - ctx.put(first.name.0.clone()) - } else { - ctx - }; - let ctx = expr - .arguments - .iter() - .skip(1) - .fold(ctx, |ctx, ident| ctx.put(", ").put(ident.name.0.clone())); - let ctx = if expr.arguments.len() > 0 { - ctx.put(", ...") - } else { - ctx.put("...") - }; - let ctx = ctx.put(")").push(); - let ctx = match &expr.body { - ast::ScriptOrExpression::Script(e) => self.visit_script(ctx, e)?, - ast::ScriptOrExpression::Expression(e) => self - .visit_expression(ctx.line().put("return "), e) - .map(|b| b.put(";"))?, - }; - Ok(ctx.pop().unwrap().line().put("end")) - } - - fn visit_reference( - &self, - ctx: code::Builder, - expr: &ast::MemberExpression, - ) -> Result { - let ctx = self.visit_expression(ctx, &expr.head)?; - let ctx = expr.tail.iter().fold(Ok(ctx), |ctx, elem| { - self.generate_member_segment(self, ctx?, elem) - })?; - Ok(ctx) - } - - fn visit_use( - &self, - ctx: code::Builder, - expr: &ast::Identifier, - ) -> Result { - Ok(ctx.put(format!("__modules__.{}", expr.0.clone()))) - } - - fn visit_use_statement( - &self, - ctx: code::Builder, - expr: &ast::Identifier, - ) -> Result { - let target = expr.0.clone(); - let target2 = expr.0.clone(); - Ok(ctx - .line() - .put(format!("local {target} = __modules__.{target2};"))) - } - - fn enter_script( - &self, - ctx: code::Builder, - _script: &crate::parser::Script, - ) -> Result { - Ok(ctx.line().put("local argv = {...};")) - } - - fn visit_call( - &self, - ctx: code::Builder, - expr: &ast::CallExpression, - ) -> Result { - let ctx = if let Some(callee) = expr.head.callee.clone() { - let ctx = self.visit_expression(ctx, &callee.head)?; - let ctx = callee - .tail - .iter() - .rev() - .skip(1) - .rev() - .fold(Ok(ctx), |ctx, elem| { - let ctx = ctx?; - let ctx = match elem { - ast::MemberSegment::Computed(c) => { - let ctx = ctx.put("["); - let ctx = self.visit_expression(ctx, &c)?; - ctx.put("]") - } - ast::MemberSegment::IdentifierDynamic(c) => ctx.put(".").put(c.0.clone()), - ast::MemberSegment::IdentifierStatic(_) => Err(VisitError)?, - }; - Ok(ctx) - })?; - let ctx = if let Some(last) = callee.tail.last() { - match last { - ast::MemberSegment::Computed(c) => { - let ctx = ctx.put("["); - let ctx = self.visit_expression(ctx, &c)?; - ctx.put("]") - } - ast::MemberSegment::IdentifierDynamic(c) => ctx.put(":").put(c.0.clone()), - ast::MemberSegment::IdentifierStatic(c) => ctx.put(".").put(c.0.clone()), - } - } else { - ctx - }; - ctx - } else { - 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 { - ctx - }; - let ctx = expr - .head - .arguments - .iter() - .skip(1) - .fold(Ok(ctx), |ctx, elem| { - let ctx = ctx?.put(", "); - self.visit_expression(ctx, elem) - })?; - let ctx = ctx.put(")"); - let ctx = expr.tail.iter().fold(Ok(ctx), |ctx, elem| match elem { - ast::CallExpressionVariant::Call(c) => self.visit_call( - ctx?, - &ast::CallExpression { - head: c.clone(), - tail: vec![], - }, - ), - ast::CallExpressionVariant::Member(m) => match m { - ast::MemberSegment::Computed(c) => { - let ctx = ctx?.put("["); - let ctx = self.visit_expression(ctx, &c)?; - Ok(ctx.put("]")) - } - ast::MemberSegment::IdentifierStatic(i) => Ok(ctx?.put(".").put(i.0.clone())), - ast::MemberSegment::IdentifierDynamic(i) => Ok(ctx?.put(":").put(i.0.clone())), - }, - })?; - Ok(ctx) - } - - fn visit_tuple( - &self, - ctx: code::Builder, - expr: &ast::Tuple, - ) -> Result { - let ctx = ctx.put("{"); - let ctx = if let Some(first) = expr.0.first().as_ref() { - let ctx = ctx.put(format!("_0 = ")); - self.visit_expression(ctx, first)? - } else { - ctx - }; - let ctx = expr - .0 - .iter() - .skip(1) - .fold(Ok((ctx, 1_u16)), |ctx, value| { - let (ctx, i) = ctx?; - let ctx = ctx.put(format!(", _{} = ", i)); - let ctx = self.visit_expression(ctx, value)?; - Ok((ctx, i + 1)) - })? - .0; - let ctx = ctx.put("}"); - Ok(ctx) - } - - fn visit_number( - &self, - ctx: code::Builder, - expr: &ast::Number, - ) -> Result { - let repr = match expr { - ast::Number::Float(e) => e.to_string(), - ast::Number::Integer(e) => e.to_string(), - }; - Ok(ctx.put(repr)) - } - - fn visit_string( - &self, - ctx: code::Builder, - expr: &ast::StringLiteral, - ) -> Result { - let ctx = match expr { - ast::StringLiteral::Double(s) => ctx.put("\"").put(escape_string(s.clone())).put("\""), - }; - Ok(ctx) - } - - fn visit_unit(&self, ctx: code::Builder) -> Result { - Ok(ctx.put("nil")) - } - - fn visit_binary( - &self, - ctx: code::Builder, - expr: &ast::BinaryExpression, - ) -> Result { - 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) - } - "++" => { - // 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(", "); - let ctx = self.visit_expression(ctx, &expr.right)?.put(")"); - Ok(ctx) - } - } - } - - fn visit_unary( - &self, - ctx: code::Builder, - expr: &ast::UnaryExpression, - ) -> Result { - 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)?; - Ok(ctx) - } - - fn visit_if( - &self, - ctx: code::Builder, - expr: &ast::If, - ) -> Result { - let ctx = ctx.line().put("if "); - let ctx = self.visit_expression(ctx, &expr.condition)?; - let ctx = ctx.put(" then").push(); - let ctx = self.visit_script(ctx, &expr.body)?; - let ctx = expr.branches.iter().fold(Ok(ctx), |ctx, (c, s)| { - let ctx = ctx?.pop().unwrap().line().put("elseif "); - let ctx = self.visit_expression(ctx, c)?; - let ctx = ctx.put(" then").push(); - let ctx = self.visit_script(ctx, s)?; - Ok(ctx) - })?; - let ctx = if let Some(eb) = expr.else_branch.as_ref() { - let ctx = ctx.pop().unwrap().line().put("else").push(); - self.visit_script(ctx, eb)? - } else { - ctx - }; - let ctx = ctx.pop().unwrap().line().put("end"); - Ok(ctx) - } - - fn visit_table( - &self, - ctx: code::Builder, - expr: &ast::Table, - ) -> Result { - let ctx = ctx.put("{"); - let ctx = if let Some((k, v)) = expr.key_values.first() { - match k { - ast::TableKeyExpression::Identifier(k) => { - let ctx = ctx.put(k.0.clone()).put(" = "); - self.visit_expression(ctx, &v.clone().unwrap()) - } - ast::TableKeyExpression::Expression(k) => { - let ctx = ctx.put("["); - let ctx = self.visit_expression(ctx, &k)?.put("] = "); - self.visit_expression(ctx, &v.clone().unwrap()) - } - ast::TableKeyExpression::Implicit(k) => { - let ctx = ctx.put(k.0.clone()).put(" = ").put(k.0.clone()); - Ok(ctx) - } - }? - } else { - ctx - }; - let ctx = expr - .key_values - .iter() - .skip(1) - .fold(Ok(ctx), |ctx, (k, v)| { - let ctx = ctx?.put(", "); - match k { - ast::TableKeyExpression::Identifier(k) => { - let ctx = ctx.put(k.0.clone()).put(" = "); - self.visit_expression(ctx, &v.clone().unwrap()) - } - ast::TableKeyExpression::Expression(k) => { - let ctx = ctx.put("["); - let ctx = self.visit_expression(ctx, &k)?.put("] = "); - self.visit_expression(ctx, &v.clone().unwrap()) - } - ast::TableKeyExpression::Implicit(k) => { - let ctx = ctx.put(k.0.clone()).put(" = ").put(k.0.clone()); - Ok(ctx) - } - } - })?; - Ok(ctx.put("}")) - } - - fn visit_vector( - &self, - ctx: code::Builder, - expr: &ast::Vector, - ) -> Result { - let ctx = ctx.put("{"); - let ctx = if let Some(first) = expr.expressions.first() { - self.visit_expression(ctx, first)? - } else { - ctx - }; - let ctx = expr.expressions.iter().skip(1).fold(Ok(ctx), |ctx, v| { - let ctx = ctx?.put(", "); - let ctx = self.visit_expression(ctx, v)?; - Ok(ctx) - })?; - Ok(ctx.put("}")) - } - - fn visit_for( - &self, - ctx: code::Builder, - expr: &ast::For, - ) -> Result { - let ctx = match &expr.handler { - ast::AssignmentTarget::Destructuring(e) => { - 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)?; - let ctx = self.visit_script(ctx, &expr.body)?; - ctx.pop().unwrap().line().put("end") - } - ast::AssignmentTarget::Identifier(e) => { - let ctx = ctx.line().put(format!("for {} in ", e.0.clone())); - let ctx = self.visit_expression(ctx, &expr.target)?; - let ctx = ctx.put(" do").push(); - let ctx = self.visit_script(ctx, &expr.body)?; - ctx.pop().unwrap().line().put("end") - } - }; - Ok(ctx) - } - - fn visit_while( - &self, - ctx: code::Builder, - expr: &ast::While, - ) -> Result { - let ctx = match &expr.condition { - ast::ExpressionOrLet::Expression(e) => { - let ctx = ctx.line().put("while "); - let ctx = self.visit_expression(ctx, e)?; - let ctx = ctx.put(" do").push(); - let ctx = self.visit_script(ctx, &expr.body)?; - ctx.pop().unwrap().line().put("end") - } - ast::ExpressionOrLet::Let(e) => { - if let ast::AssignmentTarget::Identifier(id) = &e.target { - let ctx = ctx.line().put("do").push(); - let ctx = self.visit_declaration(ctx, e)?; - let ctx = ctx.line().put(format!("while {} do", id.0.clone())).push(); - let ctx = self.visit_script(ctx, &expr.body)?; - let ctx = self.visit_assignment( - ctx, - &ast::Assignment { - target: ast::MemberExpression { - head: ast::Expression::Identifier(id.clone()), - tail: vec![], - }, - value: e.value.clone().unwrap(), - extra: None, - }, - )?; - ctx.pop() - .unwrap() - .line() - .put("end") - .pop() - .unwrap() - .line() - .put("end") - } else { - panic!("Destructured while-let not supported"); - } - } - }; - Ok(ctx) - } - - fn visit_loop( - &self, - ctx: code::Builder, - expr: &ast::Loop, - ) -> Result { - let ctx = ctx.line().put("while true do").push(); - let ctx = self.visit_script(ctx, &expr.body)?; - let ctx = ctx.pop().unwrap().line().put("end"); - Ok(ctx) - } - - fn visit_match( - &self, - _ctx: code::Builder, - _expr: &ast::Match, - ) -> Result { - todo!("Match code generation not implemented yet.") - } -} +pub mod helpers; +pub mod visitor; diff --git a/src/lua/visitor.rs b/src/lua/visitor.rs new file mode 100644 index 0000000..147f738 --- /dev/null +++ b/src/lua/visitor.rs @@ -0,0 +1,781 @@ +use crate::{ + code::{ + ast_visitor::{Result, VisitError, Visitor}, + builder::Builder, + }, + parser::ast::{self}, +}; + +#[derive(Debug)] +struct BadCode; +impl std::fmt::Display for BadCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Bad code") + } +} +impl std::error::Error for BadCode { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +fn escape_string(str: String) -> String { + return str.replace("\n", "\\n"); +} + +fn translate_operator(ctx: Builder, op: String) -> 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", + b':' => "colon", + _ => panic!( + "Error! Unexpected operator {} to be translated as a function!", + ch + ), + } + .to_owned() + }) + .collect::>() + .join("_"); + ctx.put(name) +} + +pub struct LuaEmitter(); + +impl LuaEmitter { + pub fn escape_reference(&self, ctx: Builder, ident: &ast::Identifier) -> Result { + let ctx = match &ident.0 { + a if a == "then" => ctx.put("['then']"), + ident => ctx.put(".").put(ident.clone()), + }; + Ok(ctx) + } + pub fn generate_member_segment( + &self, + s: &S, + ctx: Builder, + elem: &ast::MemberSegment, + ) -> Result + where + S: Visitor, + { + match elem { + ast::MemberSegment::Computed(c) => { + let ctx = ctx.put("["); + let ctx = s.visit_expression(ctx, &c)?; + Ok(ctx.put("]")) + } + ast::MemberSegment::IdentifierDynamic(i) => self.escape_reference(ctx, i), + ast::MemberSegment::IdentifierStatic(_) => Err(VisitError(Box::new(BadCode))), + } + } + pub fn generate_destructured_assignment(&self, ctx: Builder, e: &ast::Destructuring) -> Result { + let mut i = 0; + match e.1 { + ast::DestructureOrigin::Tuple => e.0.iter().fold(Ok(ctx), |ctx, elem| { + let ctx = ctx? + .line() + .put(elem.0.clone()) + .put(" = __destructure__") + .put(format!("._{}", i)) + .put(";"); + i += 1; + Ok(ctx) + }), + ast::DestructureOrigin::Array => e.0.iter().fold(Ok(ctx), |ctx, elem| { + let ctx = ctx? + .line() + .put(elem.0.clone()) + .put(" = __destructure__") + .put(format!("[{}]", i)) + .put(";"); + i += 1; + Ok(ctx) + }), + ast::DestructureOrigin::Table => e.0.iter().fold(Ok(ctx), |ctx, elem| { + let ctx = ctx? + .line() + .put(elem.0.clone()) + .put(" = __destructure__.") + .put(elem.0.clone()) + .put(";"); + Ok(ctx) + }), + } + } +} + +impl Visitor for LuaEmitter { + fn visit_return(&self, ctx: Builder, stmt: &ast::Return) -> Result { + let ctx = ctx.line().put("return "); + let ctx = self.visit_expression(ctx, &stmt.value)?; + Ok(ctx.put(";")) + } + + fn visit_block_expression(&self, ctx: 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_wrapped_expression(&self, ctx: Builder, expr: &ast::Expression) -> Result { + let ctx = ctx.put("("); + let ctx = self.visit_expression(ctx, expr)?; + Ok(ctx.put(")")) + } + + fn visit_identifier(&self, ctx: Builder, expr: &ast::Identifier) -> Result { + Ok(ctx.put(expr.0.clone())) + } + + fn visit_class(&self, ctx: Builder, stmt: &ast::Class) -> Result { + let ctx = ctx + .line() + .put(format!("local {} = {{}};", stmt.name.0.clone())) + .line() + .put(format!("{}.__meta__ = {{}};", stmt.name.0.clone())) + .line() + .put(format!( + "{}.__meta__.__call = function(self, struct)", + stmt.name.0.clone() + )) + .push() + .line() + .put("return setmetatable(struct, self.prototype.__meta__);") + .pop() + .unwrap() + .line() + .put("end;") + .line() + .put(format!("{}.prototype = {{}};", stmt.name.0.clone())) + .line() + .put(format!( + "{}.prototype.__meta__ = {{}};", + stmt.name.0.clone() + )) + .line() + .put(format!( + "{}.prototype.__meta__.__index = {}.prototype;", + stmt.name.0.clone(), + stmt.name.0.clone() + )) + .line() + .put(format!( + "setmetatable({}, {}.__meta__);", + stmt.name.0.clone(), + stmt.name.0.clone() + )); + let ctx = stmt.fields.iter().fold(Ok(ctx), |ctx, field| { + let ctx = ctx?.line(); + let ctx = match field { + ast::ClassField::Method(f) => { + let is_self = if let Some(first) = f.arguments.first() { + first.name.0 == "self" + } else { + false + }; + let level = if is_self { ".prototype." } else { "." }.to_string(); + let ctx = ctx + .put(stmt.name.0.clone()) + .put(level) + .put(f.name.0.clone()) + .put(" = "); + let ctx = self.visit_lambda( + ctx, + &ast::Lambda { + arguments: f.arguments.clone(), + body: ast::ScriptOrExpression::Script(f.body.clone()), + }, + )?; + 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 { + ast::AssignmentTarget::Destructuring(_) => { + panic!("Can't destructure that!") + } + ast::AssignmentTarget::Identifier(e) => ctx.put(format!( + "{}.prototype.{} = ", + stmt.name.0.clone(), + e.0.clone() + )), + }; + let ctx = if let Some(value) = f.value.as_ref() { + self.visit_expression(ctx, value)? + } else { + ctx.put("nil") + }; + ctx.put(";") + } + }; + 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) + } + + fn visit_fn(&self, ctx: Builder, stmt: &ast::Function) -> Result { + let ctx = ctx + .line() + .put("local function ") + .put(stmt.name.0.clone()) + .put("("); + let ctx = if let Some(first) = stmt.arguments.first() { + ctx.put(first.name.0.clone()) + } else { + ctx + }; + let ctx = stmt + .arguments + .iter() + .skip(1) + .fold(ctx, |ctx, ident| ctx.put(", ").put(ident.name.0.clone())); + let ctx = if stmt.arguments.len() > 0 { + ctx.put(", ...") + } else { + ctx.put("...") + }; + let ctx = ctx.put(")").push(); + 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), + }, + None => ctx.put("error('Native function implementation not found')"), + } + .line() + .put("-- NATIVE CODE") + } 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(); + 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) + } + + fn visit_assignment(&self, ctx: Builder, stmt: &ast::Assignment) -> Result { + let segment = self + .visit_reference(ctx.clone_like(), &stmt.target)? + .collect(); + let ctx = ctx.line().put(segment).put(" = "); + let ctx = if let Some(extra) = stmt.extra.as_ref() { + let ast::Assignment { target, value, .. } = stmt.clone(); + self.visit_binary( + ctx, + &ast::BinaryExpression { + left: ast::Expression::Reference(Box::new(target)), + operator: extra.clone(), + right: value, + }, + ) + } else { + self.visit_expression(ctx, &stmt.value) + }?; + let ctx = ctx.put(";"); + Ok(ctx) + } + + fn visit_declaration(&self, ctx: Builder, stmt: &ast::Let) -> Result { + let ctx = match &stmt.target { + ast::AssignmentTarget::Destructuring(e) => { + let ctx = ctx.line().put("local "); + let ctx = ctx.put(e.0.get(0).unwrap().0.clone()); + let ctx = + e.0.iter() + .skip(1) + .fold(ctx, |ctx, elem| ctx.put(", ").put(elem.0.clone())); + let ctx = ctx + .put(";") + .line() + .put("do") + .push() + .line() + .put("local __destructure__ = "); + let ctx = self + .visit_expression(ctx, stmt.value.as_ref().unwrap())? + .put(";"); + self.generate_destructured_assignment(ctx, &e)? + .pop() + .unwrap() + .line() + .put("end") + } + ast::AssignmentTarget::Identifier(e) => { + let ctx = ctx.line().put("local ").put(e.0.clone()).put(" = "); + let ctx = self.visit_expression(ctx, stmt.value.as_ref().unwrap())?; + ctx.put(";") + } + }; + Ok(ctx) + } + + fn visit_expression_statement(&self, ctx: Builder, stmt: &ast::Expression) -> Result { + Ok(self.visit_expression(ctx.line(), stmt)?.put(";")) + } + + fn visit_lambda(&self, ctx: Builder, expr: &ast::Lambda) -> Result { + let ctx = ctx.put("function("); + let ctx = if let Some(first) = expr.arguments.first() { + ctx.put(first.name.0.clone()) + } else { + ctx + }; + let ctx = expr + .arguments + .iter() + .skip(1) + .fold(ctx, |ctx, ident| ctx.put(", ").put(ident.name.0.clone())); + let ctx = if expr.arguments.len() > 0 { + ctx.put(", ...") + } else { + ctx.put("...") + }; + let ctx = ctx.put(")").push(); + let ctx = match &expr.body { + ast::ScriptOrExpression::Script(e) => self.visit_script(ctx, e)?, + ast::ScriptOrExpression::Expression(e) => self + .visit_expression(ctx.line().put("return "), e) + .map(|b| b.put(";"))?, + }; + Ok(ctx.pop().unwrap().line().put("end")) + } + + fn visit_reference(&self, ctx: Builder, expr: &ast::MemberExpression) -> Result { + let ctx = self.visit_expression(ctx, &expr.head)?; + let ctx = expr.tail.iter().fold(Ok(ctx), |ctx, elem| { + self.generate_member_segment(self, ctx?, elem) + })?; + Ok(ctx) + } + + fn visit_use_expression(&self, ctx: Builder, expr: &ast::Identifier) -> Result { + Ok(ctx.put(format!("__modules__.{}", expr.0.clone()))) + } + + fn visit_use_statement(&self, ctx: Builder, expr: &ast::Identifier) -> Result { + let target = expr.0.clone(); + let target2 = expr.0.clone(); + Ok(ctx + .line() + .put(format!("local {target} = __modules__.{target2};"))) + } + + fn visit_call(&self, ctx: Builder, expr: &ast::CallExpression) -> Result { + let ctx = if let Some(callee) = expr.head.callee.clone() { + let ctx = self.visit_expression(ctx, &callee.head)?; + let ctx = callee + .tail + .iter() + .rev() + .skip(1) + .rev() + .fold(Ok(ctx), |ctx, elem| { + let ctx = ctx?; + let ctx = match elem { + ast::MemberSegment::Computed(c) => { + let ctx = ctx.put("["); + let ctx = self.visit_expression(ctx, &c)?; + ctx.put("]") + } + ast::MemberSegment::IdentifierDynamic(c) => ctx.put(".").put(c.0.clone()), + ast::MemberSegment::IdentifierStatic(_) => { + Err(VisitError(Box::new(BadCode)))? + } + }; + Ok(ctx) + })?; + let ctx = if let Some(last) = callee.tail.last() { + match last { + ast::MemberSegment::Computed(c) => { + let ctx = ctx.put("["); + let ctx = self.visit_expression(ctx, &c)?; + ctx.put("]") + } + ast::MemberSegment::IdentifierDynamic(c) => ctx.put(":").put(c.0.clone()), + ast::MemberSegment::IdentifierStatic(c) => ctx.put(".").put(c.0.clone()), + } + } else { + ctx + }; + ctx + } else { + ctx + }; + let ctx = ctx.put("("); + let ctx = if let Some(first) = expr.head.arguments.first() { + self.visit_expression(ctx, first)? + } else { + ctx + }; + let ctx = expr + .head + .arguments + .iter() + .skip(1) + .fold(Ok(ctx), |ctx, elem| { + let ctx = ctx?.put(", "); + self.visit_expression(ctx, elem) + })?; + let ctx = ctx.put(")"); + let ctx = expr.tail.iter().fold(Ok(ctx), |ctx, elem| match elem { + ast::CallExpressionVariant::Call(c) => self.visit_call( + ctx?, + &ast::CallExpression { + head: c.clone(), + tail: vec![], + }, + ), + ast::CallExpressionVariant::Member(m) => match m { + ast::MemberSegment::Computed(c) => { + let ctx = ctx?.put("["); + let ctx = self.visit_expression(ctx, &c)?; + Ok(ctx.put("]")) + } + ast::MemberSegment::IdentifierStatic(i) => Ok(ctx?.put(".").put(i.0.clone())), + ast::MemberSegment::IdentifierDynamic(i) => Ok(ctx?.put(":").put(i.0.clone())), + }, + })?; + Ok(ctx) + } + + fn visit_tuple(&self, ctx: Builder, expr: &ast::Tuple) -> Result { + let ctx = ctx.put("{"); + let ctx = if let Some(first) = expr.0.first().as_ref() { + let ctx = ctx.put(format!("_0 = ")); + self.visit_expression(ctx, first)? + } else { + ctx + }; + let ctx = expr + .0 + .iter() + .skip(1) + .fold(Ok((ctx, 1_u16)), |ctx, value| { + let (ctx, i) = ctx?; + let ctx = ctx.put(format!(", _{} = ", i)); + let ctx = self.visit_expression(ctx, value)?; + Ok((ctx, i + 1)) + })? + .0; + let ctx = ctx.put("}"); + Ok(ctx) + } + + fn visit_number(&self, ctx: Builder, expr: &ast::Number) -> Result { + let numeric_string = match expr { + ast::Number::Float(e) => e.to_string(), + ast::Number::Integer(e) => e.to_string(), + }; + Ok(ctx.put(numeric_string)) + } + + fn visit_string(&self, ctx: Builder, expr: &ast::StringLiteral) -> Result { + let ctx = match expr { + ast::StringLiteral::Double(s) => ctx.put("\"").put(escape_string(s.clone())).put("\""), + }; + Ok(ctx) + } + + fn visit_unit(&self, ctx: Builder) -> Result { + Ok(ctx.put("nil")) + } + + fn visit_binary(&self, ctx: Builder, expr: &ast::BinaryExpression) -> Result { + 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) + } + "++" => { + // 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(", "); + let ctx = self.visit_expression(ctx, &expr.right)?.put(")"); + Ok(ctx) + } + } + } + + fn visit_unary(&self, ctx: Builder, expr: &ast::UnaryExpression) -> Result { + 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)?; + Ok(ctx) + } + + fn visit_if(&self, ctx: Builder, expr: &ast::If) -> Result { + let ctx = ctx.line().put("if "); + let ctx = self.visit_expression(ctx, &expr.condition)?; + let ctx = ctx.put(" then").push(); + let ctx = self.visit_script(ctx, &expr.body)?; + let ctx = expr.branches.iter().fold(Ok(ctx), |ctx, (c, s)| { + let ctx = ctx?.pop().unwrap().line().put("elseif "); + let ctx = self.visit_expression(ctx, c)?; + let ctx = ctx.put(" then").push(); + let ctx = self.visit_script(ctx, s)?; + Ok(ctx) + })?; + let ctx = if let Some(eb) = expr.else_branch.as_ref() { + let ctx = ctx.pop().unwrap().line().put("else").push(); + self.visit_script(ctx, eb)? + } else { + ctx + }; + let ctx = ctx.pop().unwrap().line().put("end"); + Ok(ctx) + } + + fn visit_table(&self, ctx: Builder, expr: &ast::Table) -> Result { + let ctx = ctx.put("{"); + let ctx = if let Some((k, v)) = expr.key_values.first() { + match k { + ast::TableKeyExpression::Identifier(k) => { + let ctx = ctx.put(k.0.clone()).put(" = "); + self.visit_expression(ctx, &v.clone().unwrap()) + } + ast::TableKeyExpression::Expression(k) => { + let ctx = ctx.put("["); + let ctx = self.visit_expression(ctx, &k)?.put("] = "); + self.visit_expression(ctx, &v.clone().unwrap()) + } + ast::TableKeyExpression::Implicit(k) => { + let ctx = ctx.put(k.0.clone()).put(" = ").put(k.0.clone()); + Ok(ctx) + } + }? + } else { + ctx + }; + let ctx = expr + .key_values + .iter() + .skip(1) + .fold(Ok(ctx), |ctx, (k, v)| { + let ctx = ctx?.put(", "); + match k { + ast::TableKeyExpression::Identifier(k) => { + let ctx = ctx.put(k.0.clone()).put(" = "); + self.visit_expression(ctx, &v.clone().unwrap()) + } + ast::TableKeyExpression::Expression(k) => { + let ctx = ctx.put("["); + let ctx = self.visit_expression(ctx, &k)?.put("] = "); + self.visit_expression(ctx, &v.clone().unwrap()) + } + ast::TableKeyExpression::Implicit(k) => { + let ctx = ctx.put(k.0.clone()).put(" = ").put(k.0.clone()); + Ok(ctx) + } + } + })?; + Ok(ctx.put("}")) + } + + fn visit_vector(&self, ctx: Builder, expr: &ast::Vector) -> Result { + let ctx = ctx.put("{"); + let ctx = if let Some(first) = expr.expressions.first() { + self.visit_expression(ctx, first)? + } else { + ctx + }; + let ctx = expr.expressions.iter().skip(1).fold(Ok(ctx), |ctx, v| { + let ctx = ctx?.put(", "); + let ctx = self.visit_expression(ctx, v)?; + Ok(ctx) + })?; + Ok(ctx.put("}")) + } + + fn visit_for(&self, ctx: Builder, expr: &ast::For) -> Result { + let ctx = match &expr.handler { + ast::AssignmentTarget::Destructuring(e) => { + 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)?; + let ctx = self.visit_script(ctx, &expr.body)?; + ctx.pop().unwrap().line().put("end") + } + ast::AssignmentTarget::Identifier(e) => { + let ctx = ctx.line().put(format!("for {} in ", e.0.clone())); + let ctx = self.visit_expression(ctx, &expr.target)?; + let ctx = ctx.put(" do").push(); + let ctx = self.visit_script(ctx, &expr.body)?; + ctx.pop().unwrap().line().put("end") + } + }; + Ok(ctx) + } + + fn visit_while(&self, ctx: Builder, expr: &ast::While) -> Result { + let ctx = match &expr.condition { + ast::ExpressionOrLet::Expression(e) => { + let ctx = ctx.line().put("while "); + let ctx = self.visit_expression(ctx, e)?; + let ctx = ctx.put(" do").push(); + let ctx = self.visit_script(ctx, &expr.body)?; + ctx.pop().unwrap().line().put("end") + } + ast::ExpressionOrLet::Let(e) => { + if let ast::AssignmentTarget::Identifier(id) = &e.target { + let ctx = ctx.line().put("do").push(); + let ctx = self.visit_declaration(ctx, e)?; + let ctx = ctx.line().put(format!("while {} do", id.0.clone())).push(); + let ctx = self.visit_script(ctx, &expr.body)?; + let ctx = self.visit_assignment( + ctx, + &ast::Assignment { + target: ast::MemberExpression { + head: ast::Expression::Identifier(id.clone()), + tail: vec![], + }, + value: e.value.clone().unwrap(), + extra: None, + }, + )?; + ctx.pop() + .unwrap() + .line() + .put("end") + .pop() + .unwrap() + .line() + .put("end") + } else { + panic!("Destructured while-let not supported"); + } + } + }; + Ok(ctx) + } + + fn visit_loop(&self, ctx: Builder, expr: &ast::Loop) -> Result { + let ctx = ctx.line().put("while true do").push(); + let ctx = self.visit_script(ctx, &expr.body)?; + let ctx = ctx.pop().unwrap().line().put("end"); + Ok(ctx) + } + + fn visit_match(&self, _ctx: Builder, _expr: &ast::Match) -> Result { + todo!("Match code generation not implemented yet.") + } + + fn visit_script(&self, ctx: Builder, script: &crate::parser::Script) -> Result { + let ctx = ctx + .put("-- Generated by the Saturnus compiler 1.0") + .line() + .put("-- WARNING! Changes may be discarded at any moment!"); + self.visit_block(ctx, script) + } +} diff --git a/src/main.rs b/src/main.rs index 0d34d5f..16e4bbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ use clap::Parser; use errors::report_error; use runtime::RuntimeError; +use crate::code::{ast_visitor::Visitor, builder::Builder}; + #[cfg(test)] #[macro_use] extern crate spectral; @@ -71,29 +73,24 @@ struct CompilationOptions { } fn try_run(options: CompilationOptions, input: String, indent: String) -> Result<(), RuntimeError> { - let host = runtime::RuntimeHost::new(indent.clone()); - // TODO: Clean std code injection - let input = if options.args.no_std { - format!("let __modules__ = {{ }};\n{input}") + let header = format!("let __modules__ = {{ }};"); + let header = if options.args.no_std { + header } else { let embed = include_str!("assets/std.saturn"); - format!( - "let __modules__ = {{ - std: {{ - {embed} - }} -}}; -{input}" - ) + format!("{header}\n__modules__.std = {{\n{embed}\n}};") }; + let compiler = lua::visitor::LuaEmitter(); + if options.args.dump_saturnus { println!("{input}"); return Ok(()); } - let script = parser::Script::parse(input).map_err(|err| RuntimeError::ParseError(err))?; + let script = parser::Script::parse(format!("{header}\n{input}")) + .map_err(|err| RuntimeError::ParseError(err))?; let CompilationOptions { args, @@ -103,8 +100,8 @@ fn try_run(options: CompilationOptions, input: String, indent: String) -> Result if args.compile { println!("Compiling {:?}...", in_path); - let output = lua::LuaEmitter - .visit_script(code::Builder::new(indent), &script) + let output = compiler + .visit_script(Builder::new(indent), &script) .map_err(|err| RuntimeError::CompilationError(err))? .collect(); if args.print { @@ -115,6 +112,8 @@ fn try_run(options: CompilationOptions, input: String, indent: String) -> Result out_file.write_all(output.as_bytes()).unwrap(); } } else { + let host: runtime::RuntimeHost = + runtime::RuntimeHost::new(indent.clone(), Box::new(compiler)); host.evaluate(&script)?; } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 0dda767..866c891 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -108,6 +108,7 @@ pub enum CallExpressionVariant { Member(MemberSegment), } +// TODO: Implement macros! #[derive(Debug, Clone)] pub enum MacroCallArguments { FunctionLike(Vec), diff --git a/src/runtime.rs b/src/runtime.rs index 920e32c..473f389 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,7 +1,8 @@ use crate::{ - code, - code::{VisitError, Visitor}, - lua, + code::{ + ast_visitor::{VisitError, Visitor}, + builder::Builder, + }, parser::Script, }; @@ -26,13 +27,18 @@ pub struct EvaluationOutput { /// This host will take care of evaluating the incoming Saturnus code. pub struct RuntimeHost { host: rlua::Lua, + compiler: Box, indent: String, } impl RuntimeHost { - pub fn new(indent: String) -> RuntimeHost { + pub fn new(indent: String, compiler: Box) -> RuntimeHost { let host = rlua::Lua::new(); - RuntimeHost { host, indent } + RuntimeHost { + host, + indent, + compiler, + } } pub fn run(&self, code: &String) -> Result { @@ -41,12 +47,9 @@ impl RuntimeHost { } pub fn evaluate(&self, script: &Script) -> Result { - let code = lua::LuaEmitter - .visit_script( - code::Builder::new(self.indent.clone()) - .put("-- Compiled by Saturnus compiler, warning: Changes may be discarded!"), - &script, - ) + let code = self + .compiler + .visit_script(Builder::new(self.indent.clone()), &script) .map_err(|err| RuntimeError::CompilationError(err))? .collect(); self.host