| author | |
| committer | |
| log | ff04a973b4a12303ecf15c34fbf2bedf8deda9d5 |
| tree | 71273a1743fc538d0015e6e3b465de4227bb6b39 |
| parent | c89bf26922f4448e6f0cb6015f4627792841deab |
| signature | Commit is signed but in an unrecognized format. |
12 files changed, 886 insertions(+), 497 deletions(-)
Cargo.lock+1| ... | @@ -240,6 +240,7 @@ dependencies = [ | ... | @@ -240,6 +240,7 @@ dependencies = [ |
| 240 | "miette", | 240 | "miette", |
| 241 | "num", | 241 | "num", |
| 242 | "thiserror", | 242 | "thiserror", |
| 243 | "tokenizer", | ||
| 243 | ] | 244 | ] |
| 244 | 245 | ||
| 245 | [[package]] | 246 | [[package]] |
crates/ast/ast.rs+55-2| ... | @@ -59,7 +59,7 @@ pub struct DocComment { | ... | @@ -59,7 +59,7 @@ pub struct DocComment { |
| 59 | pub loc: Loc, | 59 | pub loc: Loc, |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | #[derive(Debug, Clone)] | 62 | #[derive(Debug, PartialEq, Clone)] |
| 63 | pub struct Comment { | 63 | pub struct Comment { |
| 64 | pub data: String, | 64 | pub data: String, |
| 65 | } | 65 | } |
| ... | @@ -98,7 +98,7 @@ pub struct BlockWithLoc { | ... | @@ -98,7 +98,7 @@ pub struct BlockWithLoc { |
| 98 | pub struct If { | 98 | pub struct If { |
| 99 | pub condition: Box<Expr>, | 99 | pub condition: Box<Expr>, |
| 100 | pub yes: BlockWithLoc, | 100 | pub yes: BlockWithLoc, |
| 101 | pub no: Option<BlockWithLoc>, | 101 | pub no: Option<Box<Expr>>, |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | #[derive(Debug, Clone)] | 104 | #[derive(Debug, Clone)] |
| ... | @@ -166,6 +166,52 @@ impl BuiltinId { | ... | @@ -166,6 +166,52 @@ impl BuiltinId { |
| 166 | } | 166 | } |
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | impl ExprData { | ||
| 170 | pub fn is_valid_js_expression(&self) -> bool { | ||
| 171 | match self { | ||
| 172 | // A block containing just expressions can be an expression | ||
| 173 | ExprData::Block(block) => block.is_valid_js_expression(), | ||
| 174 | ExprData::If(expr) => expr.is_valid_js_expression(), | ||
| 175 | ExprData::Return(_) => false, | ||
| 176 | ExprData::Builtin { id, .. } => match id { | ||
| 177 | BuiltinId::Raw => true, | ||
| 178 | }, | ||
| 179 | _ => true, | ||
| 180 | } | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | impl Block { | ||
| 185 | pub fn is_valid_js_expression(&self) -> bool { | ||
| 186 | self.statements | ||
| 187 | .iter() | ||
| 188 | .all(|statement| statement.is_valid_js_expression()) | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | impl Expr { | ||
| 193 | pub fn is_valid_js_expression(&self) -> bool { | ||
| 194 | self.data.is_valid_js_expression() | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | impl If { | ||
| 199 | pub fn is_valid_js_expression(&self) -> bool { | ||
| 200 | self.condition.is_valid_js_expression() | ||
| 201 | && self.yes.is_valid_js_expression() | ||
| 202 | && match &self.no { | ||
| 203 | Some(no) => no.is_valid_js_expression(), | ||
| 204 | None => true, | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | impl BlockWithLoc { | ||
| 210 | pub fn is_valid_js_expression(&self) -> bool { | ||
| 211 | self.block.is_valid_js_expression() | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 169 | impl Statement { | 215 | impl Statement { |
| 170 | pub fn requires_semicolon(&self) -> bool { | 216 | pub fn requires_semicolon(&self) -> bool { |
| 171 | match &self { | 217 | match &self { |
| ... | @@ -178,6 +224,13 @@ impl Statement { | ... | @@ -178,6 +224,13 @@ impl Statement { |
| 178 | } | 224 | } |
| 179 | } | 225 | } |
| 180 | 226 | ||
| 227 | pub fn is_valid_js_expression(&self) -> bool { | ||
| 228 | match self { | ||
| 229 | Statement::Expr(expr) => expr.data.is_valid_js_expression(), | ||
| 230 | _ => false, | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 181 | pub fn to_top_level(self) -> TopLevelStatement { | 234 | pub fn to_top_level(self) -> TopLevelStatement { |
| 182 | match self { | 235 | match self { |
| 183 | Statement::Expr(expr) => TopLevelStatement::Expr(expr), | 236 | Statement::Expr(expr) => TopLevelStatement::Expr(expr), |
crates/js_ast/Cargo.toml+2| ... | @@ -4,6 +4,7 @@ version = "0.1.0" | ... | @@ -4,6 +4,7 @@ version = "0.1.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | 5 | ||
| 6 | [lib] | 6 | [lib] |
| 7 | path = "./lib.rs" | ||
| 7 | 8 | ||
| 8 | [dependencies] | 9 | [dependencies] |
| 9 | miette.workspace = true | 10 | miette.workspace = true |
| ... | @@ -11,3 +12,4 @@ thiserror.workspace = true | ... | @@ -11,3 +12,4 @@ thiserror.workspace = true |
| 11 | num.workspace = true | 12 | num.workspace = true |
| 12 | 13 | ||
| 13 | ast = { path = "../ast" } | 14 | ast = { path = "../ast" } |
| 15 | tokenizer = { path = "../tokenizer" } # TODO: move Loc out of the tokenizer |
crates/js_ast/js_ast.rs created+273| ... | @@ -0,0 +1,273 @@ | ||
| 1 | use ast::{BinaryOperator, BlockWithLoc, BuiltinId, Comment, Expr, UnaryOperator}; | ||
| 2 | use num::bigint::BigInt; | ||
| 3 | use tokenizer::Loc; | ||
| 4 | |||
| 5 | #[derive(Debug, PartialEq, Clone)] | ||
| 6 | pub struct JsExpr { | ||
| 7 | pub data: JsExprData, | ||
| 8 | pub loc: Loc, | ||
| 9 | pub comment: Option<Comment>, | ||
| 10 | } | ||
| 11 | |||
| 12 | #[derive(Debug, PartialEq, Clone)] | ||
| 13 | pub struct JsKV { | ||
| 14 | pub key: JsExpr, | ||
| 15 | pub value: JsExpr, | ||
| 16 | } | ||
| 17 | |||
| 18 | #[derive(Debug, PartialEq, Clone)] | ||
| 19 | pub enum JsExprData { | ||
| 20 | /// node emitted by the '@raw' building | ||
| 21 | Raw(String), | ||
| 22 | // Litearls | ||
| 23 | Number(f64), | ||
| 24 | BigInt(BigInt), | ||
| 25 | String(String), | ||
| 26 | Bool(bool), | ||
| 27 | Array(Vec<JsExpr>), | ||
| 28 | Object(Vec<JsKV>), | ||
| 29 | Null, | ||
| 30 | Undefined, | ||
| 31 | |||
| 32 | Identifier(String), | ||
| 33 | Unary { | ||
| 34 | operator: UnaryOperator, | ||
| 35 | value: Box<JsExpr>, | ||
| 36 | }, | ||
| 37 | Binary { | ||
| 38 | operator: BinaryOperator, | ||
| 39 | left: Box<JsExpr>, | ||
| 40 | right: Box<JsExpr>, | ||
| 41 | }, | ||
| 42 | Dot { | ||
| 43 | base: Box<JsExpr>, | ||
| 44 | name: String, | ||
| 45 | }, | ||
| 46 | Index { | ||
| 47 | base: Box<JsExpr>, | ||
| 48 | index: Box<JsExpr>, | ||
| 49 | }, | ||
| 50 | Call { | ||
| 51 | base: Box<JsExpr>, | ||
| 52 | arguments: Vec<JsExpr>, | ||
| 53 | }, | ||
| 54 | Function { | ||
| 55 | name: String, | ||
| 56 | params: Vec<String>, | ||
| 57 | block: Vec<JsStatement>, | ||
| 58 | }, | ||
| 59 | If { | ||
| 60 | condition: Box<JsExpr>, | ||
| 61 | yes: Vec<JsExpr>, | ||
| 62 | no: Option<Box<JsExpr>>, | ||
| 63 | }, | ||
| 64 | ArrowFunction { | ||
| 65 | params: Vec<String>, | ||
| 66 | block: Vec<JsStatement>, | ||
| 67 | }, | ||
| 68 | Comma { | ||
| 69 | items: Vec<JsExpr>, | ||
| 70 | }, | ||
| 71 | } | ||
| 72 | |||
| 73 | #[derive(Debug, PartialEq, Clone)] | ||
| 74 | pub struct JsStatement { | ||
| 75 | pub data: JsStatementData, | ||
| 76 | pub loc: Loc, | ||
| 77 | pub comment: Option<Comment>, | ||
| 78 | } | ||
| 79 | |||
| 80 | #[derive(Debug, PartialEq, Clone)] | ||
| 81 | pub enum JsStatementData { | ||
| 82 | Expression(JsExpr), | ||
| 83 | Return(JsExpr), | ||
| 84 | Export(JsExport), | ||
| 85 | Declaration { | ||
| 86 | name: String, | ||
| 87 | init: JsExpr, | ||
| 88 | is_constant: bool, | ||
| 89 | }, | ||
| 90 | Function { | ||
| 91 | name: String, | ||
| 92 | params: Vec<String>, | ||
| 93 | block: JsBlock, | ||
| 94 | }, | ||
| 95 | If { | ||
| 96 | condition: JsExpr, | ||
| 97 | yes: JsBlock, | ||
| 98 | no: Option<JsBlock>, | ||
| 99 | }, | ||
| 100 | } | ||
| 101 | |||
| 102 | #[derive(Debug, PartialEq, Clone)] | ||
| 103 | pub enum JsExport { | ||
| 104 | Named(Vec<String>), | ||
| 105 | Default(JsExpr), | ||
| 106 | Equals(JsExpr), | ||
| 107 | } | ||
| 108 | |||
| 109 | #[derive(Debug, PartialEq, Clone)] | ||
| 110 | pub struct JsBlock { | ||
| 111 | pub statements: Vec<JsStatement>, | ||
| 112 | pub loc: Loc, | ||
| 113 | } | ||
| 114 | |||
| 115 | impl JsExpr { | ||
| 116 | pub fn from_blight(expr: &Expr) -> Option<JsExpr> { | ||
| 117 | use ast::ExprData::*; | ||
| 118 | |||
| 119 | let loc = &expr.loc; | ||
| 120 | let comment = &expr.comment; | ||
| 121 | |||
| 122 | match &expr.data { | ||
| 123 | Block(block) => Self::comma_expr_from_blight_block(&BlockWithLoc { | ||
| 124 | block: block.clone(), | ||
| 125 | loc: loc.clone(), | ||
| 126 | }), | ||
| 127 | Binary { op, left, right } => { | ||
| 128 | let left = Self::from_blight(&*left)?; | ||
| 129 | let right = Self::from_blight(&*right)?; | ||
| 130 | Some(JsExpr::new( | ||
| 131 | JsExprData::Binary { | ||
| 132 | operator: op.clone(), | ||
| 133 | left: Box::new(left), | ||
| 134 | right: Box::new(right), | ||
| 135 | }, | ||
| 136 | loc.clone(), | ||
| 137 | comment.clone(), | ||
| 138 | )) | ||
| 139 | } | ||
| 140 | Unary { op, value } => { | ||
| 141 | let value = Self::from_blight(&*value)?; | ||
| 142 | Some(JsExpr::new( | ||
| 143 | JsExprData::Unary { | ||
| 144 | operator: op.clone(), | ||
| 145 | value: Box::new(value), | ||
| 146 | }, | ||
| 147 | loc.clone(), | ||
| 148 | comment.clone(), | ||
| 149 | )) | ||
| 150 | } | ||
| 151 | Fn { | ||
| 152 | name, | ||
| 153 | params, | ||
| 154 | return_type, | ||
| 155 | block, | ||
| 156 | } => { | ||
| 157 | let block = Self::comma_expr_from_blight_block(&BlockWithLoc { | ||
| 158 | block: block.clone(), | ||
| 159 | loc: loc.clone(), | ||
| 160 | })?; | ||
| 161 | Some(JsExpr::new( | ||
| 162 | JsExprData::Function { | ||
| 163 | name: name.clone(), | ||
| 164 | params: vec![], | ||
| 165 | block: vec![JsStatement::new( | ||
| 166 | JsStatementData::Expression(block), | ||
| 167 | loc.clone(), | ||
| 168 | comment.clone(), | ||
| 169 | )], | ||
| 170 | }, | ||
| 171 | loc.clone(), | ||
| 172 | comment.clone(), | ||
| 173 | )) | ||
| 174 | } | ||
| 175 | Builtin { id, arguments } => match id { | ||
| 176 | BuiltinId::Raw => { | ||
| 177 | let mut args = arguments.iter(); | ||
| 178 | let arg = args.next().unwrap(); | ||
| 179 | let arg = match &arg.data { | ||
| 180 | Literal(ast::Literal::String(s)) => s, | ||
| 181 | _ => panic!("raw expects a string"), | ||
| 182 | }; | ||
| 183 | Some(JsExpr::new( | ||
| 184 | JsExprData::Raw(arg.clone()), | ||
| 185 | loc.clone(), | ||
| 186 | comment.clone(), | ||
| 187 | )) | ||
| 188 | } | ||
| 189 | }, | ||
| 190 | If(expr) => { | ||
| 191 | let condition = Self::from_blight(&expr.condition)?; | ||
| 192 | let yes = Self::comma_expr_from_blight_block(&expr.yes)?; | ||
| 193 | let no = match &expr.no { | ||
| 194 | Some(no) => Some(Self::from_blight(&no)?), | ||
| 195 | None => None, | ||
| 196 | }; | ||
| 197 | Some(JsExpr::new( | ||
| 198 | JsExprData::If { | ||
| 199 | condition: Box::new(condition), | ||
| 200 | yes: vec![yes], | ||
| 201 | no: no.map(Box::new), | ||
| 202 | }, | ||
| 203 | loc.clone(), | ||
| 204 | comment.clone(), | ||
| 205 | )) | ||
| 206 | } | ||
| 207 | |||
| 208 | Call { base, arguments } => todo!(), | ||
| 209 | Field { base, field } => todo!(), | ||
| 210 | |||
| 211 | Return(_) => None, | ||
| 212 | |||
| 213 | Literal(_) => todo!(), | ||
| 214 | Identifier(_) => todo!(), | ||
| 215 | // Literal(ast::Literal::Number(n)) => { | ||
| 216 | // Some(JsExpr::new(JsExprData::Number(n), loc.clone(), comment)) | ||
| 217 | // } | ||
| 218 | // Literal(ast::Literal::Bool(b)) => Some(JsExpr::new(JsExprData::Bool(b), loc, comment)), | ||
| 219 | // Literal(ast::Literal::String(s)) => { | ||
| 220 | // Some(JsExpr::new(JsExprData::String(s), loc, comment)) | ||
| 221 | // } | ||
| 222 | // Literal(ast::Literal::Void) => Some(JsExpr::new(JsExprData::Null, loc, comment)), | ||
| 223 | // Identifier(id) => Some(JsExpr::new(JsExprData::Identifier(id), loc, comment)), | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | /// Since blocks are not valid expressions in JS, this creates a | ||
| 228 | /// comma expression from a block with more than one statement. | ||
| 229 | /// | ||
| 230 | /// Not possible to create an ast => None | ||
| 231 | pub fn comma_expr_from_blight_block(b: &ast::BlockWithLoc) -> Option<JsExpr> { | ||
| 232 | if !b.block.has_implicit_return { | ||
| 233 | return Some(JsExpr::new(JsExprData::Null, b.loc.clone(), None)); | ||
| 234 | } | ||
| 235 | // a block expression with a single statement is just that statement | ||
| 236 | if b.block.statements.len() == 1 && b.block.has_implicit_return { | ||
| 237 | match b.block.statements.first().unwrap() { | ||
| 238 | ast::Statement::Expr(expr) => Self::from_blight(expr), | ||
| 239 | _ => return None, | ||
| 240 | }; | ||
| 241 | } | ||
| 242 | let mut statements = Vec::new(); | ||
| 243 | for statement in &b.block.statements { | ||
| 244 | match statement { | ||
| 245 | ast::Statement::Expr(expr) => { | ||
| 246 | if let Some(expr) = Self::from_blight(&expr) { | ||
| 247 | statements.push(expr); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | _ => return None, | ||
| 251 | } | ||
| 252 | } | ||
| 253 | // a block without implicit return is to be type void, so we append null | ||
| 254 | if !b.block.has_implicit_return { | ||
| 255 | statements.push(JsExpr::new(JsExprData::Null, b.loc.clone(), None)); | ||
| 256 | } | ||
| 257 | Some(JsExpr::new( | ||
| 258 | JsExprData::Comma { items: statements }, | ||
| 259 | b.loc.clone(), | ||
| 260 | None, | ||
| 261 | )) | ||
| 262 | } | ||
| 263 | |||
| 264 | pub fn new(data: JsExprData, loc: Loc, comment: Option<Comment>) -> Self { | ||
| 265 | Self { data, loc, comment } | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | impl JsStatement { | ||
| 270 | pub fn new(data: JsStatementData, loc: Loc, comment: Option<Comment>) -> Self { | ||
| 271 | Self { data, loc, comment } | ||
| 272 | } | ||
| 273 | } | ||
crates/js_ast/lib.rs created+411| ... | @@ -0,0 +1,411 @@ | ||
| 1 | use std::collections::HashMap; | ||
| 2 | |||
| 3 | use ast::{BlockWithLoc, Expr, ExprData, Literal, Module, Statement}; | ||
| 4 | pub use js_ast::*; | ||
| 5 | use miette::Result; | ||
| 6 | use tokenizer::Loc; | ||
| 7 | |||
| 8 | mod js_ast; | ||
| 9 | |||
| 10 | struct StackContext { | ||
| 11 | statements: Vec<JsStatement>, | ||
| 12 | /// Variables that are declared in this context. | ||
| 13 | /// The number is the number of times the variable has been declared. | ||
| 14 | /// This is used to allow for shadowing | ||
| 15 | variable_map: HashMap<String, u32>, | ||
| 16 | block: u32, | ||
| 17 | } | ||
| 18 | |||
| 19 | impl StackContext { | ||
| 20 | fn new(block: u32) -> Self { | ||
| 21 | StackContext { | ||
| 22 | statements: Vec::new(), | ||
| 23 | variable_map: HashMap::new(), | ||
| 24 | block, | ||
| 25 | } | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | struct TranslateContext { | ||
| 30 | context: Vec<StackContext>, | ||
| 31 | internal_counter: u32, | ||
| 32 | block_counter: u32, | ||
| 33 | } | ||
| 34 | |||
| 35 | impl TranslateContext { | ||
| 36 | fn new() -> Self { | ||
| 37 | TranslateContext { | ||
| 38 | context: vec![StackContext::new(0)], | ||
| 39 | internal_counter: 0, | ||
| 40 | block_counter: 0, | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | fn push_context(self: &mut Self) { | ||
| 45 | self.block_counter += 1; | ||
| 46 | self.context.push(StackContext::new(self.block_counter)); | ||
| 47 | } | ||
| 48 | |||
| 49 | fn pop_context(self: &mut Self) -> Vec<JsStatement> { | ||
| 50 | self.context.pop().unwrap().statements | ||
| 51 | } | ||
| 52 | |||
| 53 | fn push_internal_variable(self: &mut Self, value: JsExpr) -> JsExpr { | ||
| 54 | let name = format!("${}", self.internal_counter); | ||
| 55 | let loc = value.loc.clone(); | ||
| 56 | self.context | ||
| 57 | .last_mut() | ||
| 58 | .unwrap() | ||
| 59 | .statements | ||
| 60 | .push(JsStatement::new( | ||
| 61 | JsStatementData::Declaration { | ||
| 62 | name: name.clone(), | ||
| 63 | init: value, | ||
| 64 | is_constant: false, | ||
| 65 | }, | ||
| 66 | loc, | ||
| 67 | None, | ||
| 68 | )); | ||
| 69 | self.internal_counter += 1; | ||
| 70 | JsExpr::new(JsExprData::Identifier(name), Loc::EMPTY, None) | ||
| 71 | } | ||
| 72 | |||
| 73 | fn push(self: &mut Self, statement: JsStatement) { | ||
| 74 | self.context.last_mut().unwrap().statements.push(statement); | ||
| 75 | } | ||
| 76 | |||
| 77 | // fn peek(self: &Self) -> &JsStatement { | ||
| 78 | // self.context.last().unwrap().statements.last().unwrap() | ||
| 79 | // } | ||
| 80 | |||
| 81 | fn declare_variable(self: &mut Self, name: String) -> String { | ||
| 82 | let context = self.context.last_mut().unwrap(); | ||
| 83 | if let Some(value) = context.variable_map.get_mut(name.as_str()) { | ||
| 84 | *value += 1; | ||
| 85 | format!("{}${}${}", name, value, context.block) | ||
| 86 | } else { | ||
| 87 | context.variable_map.insert(name.to_string(), 0); | ||
| 88 | format!("{}$0${}", name, context.block) | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | fn get_identifier(self: &Self, name: String) -> String { | ||
| 93 | for context in self.context.iter().rev() { | ||
| 94 | if let Some(value) = context.variable_map.get(name.as_str()) { | ||
| 95 | return format!("{}${}${}", name, value, context.block); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | panic!("Variable {} not found", name); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | fn get_block_handle(b: &ast::BlockWithLoc, ctx: &mut TranslateContext) -> JsExpr { | ||
| 103 | // :) | ||
| 104 | if b.is_valid_js_expression() { | ||
| 105 | return JsExpr::comma_expr_from_blight_block(b).unwrap(); | ||
| 106 | } | ||
| 107 | |||
| 108 | ctx.push_context(); | ||
| 109 | for (i, statement) in b.block.statements.iter().enumerate() { | ||
| 110 | if i == b.block.statements.len() - 1 { | ||
| 111 | break; | ||
| 112 | } else { | ||
| 113 | translate_statement(statement, ctx); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | let result = match b.block.statements.last().unwrap() { | ||
| 117 | ast::Statement::Expr(expr) => get_expression_handle(expr, ctx), | ||
| 118 | stmt => { | ||
| 119 | translate_statement(stmt, ctx); | ||
| 120 | JsExpr::new(JsExprData::Null, Loc::EMPTY, None) | ||
| 121 | } | ||
| 122 | }; | ||
| 123 | let value = ctx.pop_context(); | ||
| 124 | value.iter().for_each(|x| ctx.push(x.clone())); | ||
| 125 | result | ||
| 126 | } | ||
| 127 | |||
| 128 | // basically, expressions are really hard to translate | ||
| 129 | // in blight, everything is an expression | ||
| 130 | // in js, very few things are expressions | ||
| 131 | // here, we recursively expand out expressions into statements | ||
| 132 | // we then store the result of all the expressions in a variable | ||
| 133 | // and then return the name of that variable | ||
| 134 | fn get_expression_handle(ast: &Expr, ctx: &mut TranslateContext) -> JsExpr { | ||
| 135 | match ast.data.clone() { | ||
| 136 | ExprData::Literal(Literal::String(value)) => JsExpr::new( | ||
| 137 | JsExprData::String(value), | ||
| 138 | ast.loc.clone(), | ||
| 139 | ast.comment.clone(), | ||
| 140 | ), | ||
| 141 | ExprData::Literal(Literal::Number(value)) => JsExpr::new( | ||
| 142 | JsExprData::Number(value), | ||
| 143 | ast.loc.clone(), | ||
| 144 | ast.comment.clone(), | ||
| 145 | ), | ||
| 146 | ExprData::Literal(Literal::Void) => { | ||
| 147 | JsExpr::new(JsExprData::Null, ast.loc.clone(), ast.comment.clone()) | ||
| 148 | } | ||
| 149 | ExprData::Literal(Literal::Bool(b)) => { | ||
| 150 | JsExpr::new(JsExprData::Bool(b), ast.loc.clone(), ast.comment.clone()) | ||
| 151 | } | ||
| 152 | |||
| 153 | ExprData::Identifier(value) => JsExpr::new( | ||
| 154 | JsExprData::Identifier(ctx.get_identifier(value.clone())), | ||
| 155 | ast.loc.clone(), | ||
| 156 | ast.comment.clone(), | ||
| 157 | ), | ||
| 158 | ExprData::Call { base, arguments } => { | ||
| 159 | let mut handles = Vec::new(); | ||
| 160 | for argument in arguments { | ||
| 161 | handles.push(get_expression_handle(&argument, ctx)); | ||
| 162 | } | ||
| 163 | let base = get_expression_handle(&base, ctx); | ||
| 164 | ctx.push_internal_variable(JsExpr::new( | ||
| 165 | JsExprData::Call { | ||
| 166 | base: Box::new(base), | ||
| 167 | arguments: handles, | ||
| 168 | }, | ||
| 169 | ast.loc.clone(), | ||
| 170 | ast.comment.clone(), | ||
| 171 | )) | ||
| 172 | } | ||
| 173 | ExprData::Builtin { id, arguments } => match id { | ||
| 174 | ast::BuiltinId::Raw => { | ||
| 175 | if arguments.len() != 1 { | ||
| 176 | panic!("@raw expects exactly one argument"); | ||
| 177 | } | ||
| 178 | match arguments.get(0).unwrap() { | ||
| 179 | Expr { | ||
| 180 | data: ExprData::Literal(Literal::String(value)), | ||
| 181 | .. | ||
| 182 | } => JsExpr::new( | ||
| 183 | JsExprData::Raw(value.clone()), | ||
| 184 | ast.loc.clone(), | ||
| 185 | ast.comment.clone(), | ||
| 186 | ), | ||
| 187 | _ => panic!("@raw expects a string literal"), | ||
| 188 | } | ||
| 189 | } | ||
| 190 | }, | ||
| 191 | ExprData::Binary { op, left, right } => { | ||
| 192 | let left_handle = get_expression_handle(&left, ctx); | ||
| 193 | let right_handle = get_expression_handle(&right, ctx); | ||
| 194 | JsExpr::new( | ||
| 195 | JsExprData::Binary { | ||
| 196 | operator: op.clone(), | ||
| 197 | left: Box::new(left_handle), | ||
| 198 | right: Box::new(right_handle), | ||
| 199 | }, | ||
| 200 | ast.loc.clone(), | ||
| 201 | ast.comment.clone(), | ||
| 202 | ) | ||
| 203 | } | ||
| 204 | |||
| 205 | ExprData::Unary { op, value } => { | ||
| 206 | let value_handle = get_expression_handle(&value, ctx); | ||
| 207 | JsExpr::new( | ||
| 208 | JsExprData::Unary { | ||
| 209 | operator: op.clone(), | ||
| 210 | value: Box::new(value_handle), | ||
| 211 | }, | ||
| 212 | ast.loc.clone(), | ||
| 213 | ast.comment.clone(), | ||
| 214 | ) | ||
| 215 | } | ||
| 216 | |||
| 217 | ExprData::Field { base, field } => { | ||
| 218 | let base = Box::new(get_expression_handle(&base, ctx)); | ||
| 219 | |||
| 220 | match field.data { | ||
| 221 | ExprData::Literal(Literal::String(name)) => JsExpr::new( | ||
| 222 | JsExprData::Dot { base, name }, | ||
| 223 | ast.loc.clone(), | ||
| 224 | ast.comment.clone(), | ||
| 225 | ), | ||
| 226 | _ => JsExpr::new( | ||
| 227 | JsExprData::Index { | ||
| 228 | base, | ||
| 229 | index: Box::new(get_expression_handle(&field, ctx)), | ||
| 230 | }, | ||
| 231 | ast.loc.clone(), | ||
| 232 | ast.comment.clone(), | ||
| 233 | ), | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | ExprData::Block(block) => get_block_handle( | ||
| 238 | &BlockWithLoc { | ||
| 239 | block, | ||
| 240 | loc: ast.loc.clone(), | ||
| 241 | }, | ||
| 242 | ctx, | ||
| 243 | ), | ||
| 244 | |||
| 245 | ExprData::If(expr) => { | ||
| 246 | let handle = get_expression_handle(&expr.condition, ctx); | ||
| 247 | |||
| 248 | let yes = get_block_handle(&expr.yes, ctx); | ||
| 249 | let no = match expr.no { | ||
| 250 | Some(block) => Some(get_expression_handle(&block, ctx)), | ||
| 251 | None => None, | ||
| 252 | }; | ||
| 253 | |||
| 254 | // ctx.push(JsStatement::new( | ||
| 255 | // JsStatementData::If { | ||
| 256 | // condition: handle, | ||
| 257 | // yes, | ||
| 258 | // no, | ||
| 259 | // }, | ||
| 260 | // ast.loc, | ||
| 261 | // ast.comment, | ||
| 262 | // )); | ||
| 263 | |||
| 264 | // panic!() | ||
| 265 | handle | ||
| 266 | } | ||
| 267 | ExprData::Fn { | ||
| 268 | name, | ||
| 269 | params, | ||
| 270 | return_type, | ||
| 271 | block, | ||
| 272 | } => todo!(), | ||
| 273 | ExprData::Return(_) => todo!(), | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | fn translate_statement(ast: &Statement, ctx: &mut TranslateContext) { | ||
| 278 | match ast { | ||
| 279 | Statement::Decl(decl) => { | ||
| 280 | let handle = get_expression_handle(&decl.init, ctx); | ||
| 281 | let rename = ctx.declare_variable(decl.name.clone()); | ||
| 282 | ctx.push(JsStatement::new( | ||
| 283 | JsStatementData::Declaration { | ||
| 284 | name: rename, | ||
| 285 | init: handle, | ||
| 286 | is_constant: decl.is_constant.clone(), | ||
| 287 | }, | ||
| 288 | decl.loc.clone(), | ||
| 289 | None, | ||
| 290 | )); | ||
| 291 | } | ||
| 292 | Statement::Expr(expr) => { | ||
| 293 | let h = get_expression_handle(expr, ctx); | ||
| 294 | ctx.push(JsStatement::new( | ||
| 295 | JsStatementData::Expression(h), | ||
| 296 | expr.loc.clone(), | ||
| 297 | None, | ||
| 298 | )) | ||
| 299 | } | ||
| 300 | Statement::Comment(_) => {} // Ast::Function { | ||
| 301 | // name, | ||
| 302 | // arguments, | ||
| 303 | // return_type: _, | ||
| 304 | // block, | ||
| 305 | // } => { | ||
| 306 | // let rename = ctx.declare_variable(name.clone()); | ||
| 307 | // let arguments = arguments | ||
| 308 | // .iter() | ||
| 309 | // .map(|x| ctx.declare_variable(x.name.clone())) | ||
| 310 | // .collect(); | ||
| 311 | |||
| 312 | // ctx.push_context(); | ||
| 313 | // for (i, statement) in block.value.iter().enumerate() { | ||
| 314 | // if block.implicit_return && i == block.value.len() - 1 { | ||
| 315 | // let handle = get_expression_handle(statement, ctx); | ||
| 316 | // ctx.push(JsStatement::Return(handle)); | ||
| 317 | // } else { | ||
| 318 | // translate_statement(statement, ctx); | ||
| 319 | // } | ||
| 320 | // } | ||
| 321 | // let value = ctx.pop_context(); | ||
| 322 | |||
| 323 | // ctx.push(JsStatement::Function { | ||
| 324 | // name: rename, | ||
| 325 | // arguments, | ||
| 326 | // value, | ||
| 327 | // }); | ||
| 328 | // } | ||
| 329 | // Ast::Public(value) => { | ||
| 330 | // translate_statement(value, ctx); | ||
| 331 | // // I'm sorry, I just don't know how to get the original name properly... my bad | ||
| 332 | // match ctx.peek() { | ||
| 333 | // JsStatement::Function { name, .. } | JsStatement::Declaration { name, .. } => { | ||
| 334 | // let original_name = name.split("$").next().unwrap().to_string(); | ||
| 335 | // ctx.push(JsStatement::Export(Box::new(JsStatement::Declaration { | ||
| 336 | // name: original_name, | ||
| 337 | // value: JsExpr::Identifier(name.clone()), | ||
| 338 | // is_constant: true, | ||
| 339 | // }))); | ||
| 340 | // } | ||
| 341 | // _ => { | ||
| 342 | // panic!("Cannot make non-function public"); | ||
| 343 | // } | ||
| 344 | // } | ||
| 345 | // } | ||
| 346 | // Ast::Return(value) => { | ||
| 347 | // let handle = get_expression_handle(value, ctx); | ||
| 348 | // ctx.push(JsStatement::Return(handle)); | ||
| 349 | // } | ||
| 350 | // Ast::Block(block) => { | ||
| 351 | // ctx.push_context(); | ||
| 352 | // for (i, statement) in block.value.iter().enumerate() { | ||
| 353 | // if block.implicit_return && i == block.value.len() - 1 { | ||
| 354 | // let handle = get_expression_handle(statement, ctx); | ||
| 355 | // ctx.push(JsStatement::Return(handle)); | ||
| 356 | // } else { | ||
| 357 | // translate_statement(statement, ctx); | ||
| 358 | // } | ||
| 359 | // } | ||
| 360 | // let value = ctx.pop_context(); | ||
| 361 | // value.iter().for_each(|x| ctx.push(x.clone())); | ||
| 362 | // } | ||
| 363 | |||
| 364 | // Ast::FunctionCall { name, arguments } => { | ||
| 365 | // let mut handles = Vec::new(); | ||
| 366 | // for argument in arguments { | ||
| 367 | // handles.push(get_expression_handle(argument, ctx)); | ||
| 368 | // } | ||
| 369 | // match *name.clone() { | ||
| 370 | // Ast::Identifier(name) => { | ||
| 371 | // ctx.push(JsStatement::Expression(JsExpr::Call { | ||
| 372 | // name: Box::new(JsExpr::Identifier(ctx.get_identifier(name.clone()))), | ||
| 373 | // arguments: handles, | ||
| 374 | // })); | ||
| 375 | // } | ||
| 376 | // Ast::BuiltIn(name) => match name.as_str() { | ||
| 377 | // "@raw" => { | ||
| 378 | // if handles.len() != 1 { | ||
| 379 | // panic!("@raw expects exactly one argument"); | ||
| 380 | // } | ||
| 381 | // match handles.get(0).unwrap() { | ||
| 382 | // JsExpr::String(value) => { | ||
| 383 | // ctx.push(JsStatement::Expression(JsExpr::Call { | ||
| 384 | // name: Box::new(JsExpr::Raw(value.clone())), | ||
| 385 | // arguments: handles, | ||
| 386 | // })); | ||
| 387 | // } | ||
| 388 | // _ => panic!("@raw expects a string argument"), | ||
| 389 | // } | ||
| 390 | // } | ||
| 391 | // _ => panic!("Unknown built-in function {}", name), | ||
| 392 | // }, | ||
| 393 | // _ => panic!("Function name must be an identifier"), | ||
| 394 | // } | ||
| 395 | // } | ||
| 396 | // Ast::Comment(_) | Ast::DocComment(_) => {} | ||
| 397 | // _ => { | ||
| 398 | // panic!("unimplemented: {:?}", ast); | ||
| 399 | // } | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | pub fn translate(module: &Module) -> Result<Vec<JsStatement>> { | ||
| 404 | let mut ctx = TranslateContext::new(); | ||
| 405 | |||
| 406 | for ast in module.statements.iter() { | ||
| 407 | translate_statement(&ast.clone().to_statement(), &mut ctx); | ||
| 408 | } | ||
| 409 | |||
| 410 | Ok(ctx.pop_context()) | ||
| 411 | } | ||
crates/js_ast/src/lib.rs deleted-333| ... | @@ -1,333 +0,0 @@ | ||
| 1 | use std::collections::HashMap; | ||
| 2 | |||
| 3 | use ast::{Expr, ExprData, Literal, Module, Statement}; | ||
| 4 | use miette::Result; | ||
| 5 | |||
| 6 | mod types; | ||
| 7 | pub use types::{JsExpression, JsStatement}; | ||
| 8 | |||
| 9 | struct StackContext { | ||
| 10 | statements: Vec<JsStatement>, | ||
| 11 | /// Variables that are declared in this context. | ||
| 12 | /// The number is the number of times the variable has been declared. | ||
| 13 | /// This is used to allow for shadowing | ||
| 14 | variable_map: HashMap<String, u32>, | ||
| 15 | block: u32, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl StackContext { | ||
| 19 | fn new(block: u32) -> Self { | ||
| 20 | StackContext { | ||
| 21 | statements: Vec::new(), | ||
| 22 | variable_map: HashMap::new(), | ||
| 23 | block, | ||
| 24 | } | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | struct TranslateContext { | ||
| 29 | context: Vec<StackContext>, | ||
| 30 | internal_counter: u32, | ||
| 31 | block_counter: u32, | ||
| 32 | } | ||
| 33 | |||
| 34 | impl TranslateContext { | ||
| 35 | fn new() -> Self { | ||
| 36 | TranslateContext { | ||
| 37 | context: vec![StackContext::new(0)], | ||
| 38 | internal_counter: 0, | ||
| 39 | block_counter: 0, | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | fn push_context(self: &mut Self) { | ||
| 44 | self.block_counter += 1; | ||
| 45 | self.context.push(StackContext::new(self.block_counter)); | ||
| 46 | } | ||
| 47 | |||
| 48 | fn pop_context(self: &mut Self) -> Vec<JsStatement> { | ||
| 49 | self.context.pop().unwrap().statements | ||
| 50 | } | ||
| 51 | |||
| 52 | fn push_internal_variable(self: &mut Self, value: JsExpression) -> JsExpression { | ||
| 53 | self.context | ||
| 54 | .last_mut() | ||
| 55 | .unwrap() | ||
| 56 | .statements | ||
| 57 | .push(JsStatement::Declaration { | ||
| 58 | name: format!("${}", self.internal_counter), | ||
| 59 | value, | ||
| 60 | is_constant: false, | ||
| 61 | }); | ||
| 62 | self.internal_counter += 1; | ||
| 63 | JsExpression::Identifier(format!("${}", self.internal_counter - 1)) | ||
| 64 | } | ||
| 65 | |||
| 66 | fn push(self: &mut Self, statement: JsStatement) { | ||
| 67 | self.context.last_mut().unwrap().statements.push(statement); | ||
| 68 | } | ||
| 69 | |||
| 70 | fn peek(self: &Self) -> &JsStatement { | ||
| 71 | self.context.last().unwrap().statements.last().unwrap() | ||
| 72 | } | ||
| 73 | |||
| 74 | fn declare_variable(self: &mut Self, name: String) -> String { | ||
| 75 | let context = self.context.last_mut().unwrap(); | ||
| 76 | if let Some(value) = context.variable_map.get_mut(name.as_str()) { | ||
| 77 | *value += 1; | ||
| 78 | format!("{}${}${}", name, value, context.block) | ||
| 79 | } else { | ||
| 80 | context.variable_map.insert(name.to_string(), 0); | ||
| 81 | format!("{}$0${}", name, context.block) | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | fn get_identifier(self: &Self, name: String) -> String { | ||
| 86 | for context in self.context.iter().rev() { | ||
| 87 | if let Some(value) = context.variable_map.get(name.as_str()) { | ||
| 88 | return format!("{}${}${}", name, value, context.block); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | panic!("Variable {} not found", name); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | // basically, expressions are really hard to translate | ||
| 96 | // in blight, everything is an expression | ||
| 97 | // in js, very few things are expressions | ||
| 98 | // here, we recursively expand out expressions into statements | ||
| 99 | // we then store the result of all the expressions in a variable | ||
| 100 | // and then return the name of that variable | ||
| 101 | fn get_expression_handle(ast: &Expr, ctx: &mut TranslateContext) -> JsExpression { | ||
| 102 | match ast.data.clone() { | ||
| 103 | ExprData::Literal(Literal::String(value)) => JsExpression::String(value), | ||
| 104 | ExprData::Literal(Literal::Number(value)) => JsExpression::Number(value), | ||
| 105 | ExprData::Literal(Literal::Void) => JsExpression::Identifier("null".to_string()), | ||
| 106 | ExprData::Literal(Literal::Bool(value)) => JsExpression::Identifier(if value { | ||
| 107 | "true".to_string() | ||
| 108 | } else { | ||
| 109 | "false".to_string() | ||
| 110 | }), | ||
| 111 | |||
| 112 | ExprData::Identifier(value) => JsExpression::Identifier(ctx.get_identifier(value.clone())), | ||
| 113 | ExprData::Call { base, arguments } => { | ||
| 114 | let mut handles = Vec::new(); | ||
| 115 | for argument in arguments { | ||
| 116 | handles.push(get_expression_handle(&argument, ctx)); | ||
| 117 | } | ||
| 118 | let base = get_expression_handle(&base, ctx); | ||
| 119 | ctx.push_internal_variable(JsExpression::Call { | ||
| 120 | base: Box::new(base), | ||
| 121 | arguments: handles, | ||
| 122 | }) | ||
| 123 | } | ||
| 124 | ExprData::Builtin { id, arguments } => match id { | ||
| 125 | ast::BuiltinId::Raw => { | ||
| 126 | if arguments.len() != 1 { | ||
| 127 | panic!("@raw expects exactly one argument"); | ||
| 128 | } | ||
| 129 | match arguments.get(0).unwrap() { | ||
| 130 | Expr { | ||
| 131 | data: ExprData::Literal(Literal::String(value)), | ||
| 132 | .. | ||
| 133 | } => JsExpression::Raw(value.clone()), | ||
| 134 | _ => panic!("@raw expects a string literal"), | ||
| 135 | } | ||
| 136 | } | ||
| 137 | }, | ||
| 138 | ExprData::Binary { op, left, right } => { | ||
| 139 | let left_handle = get_expression_handle(&left, ctx); | ||
| 140 | let right_handle = get_expression_handle(&right, ctx); | ||
| 141 | JsExpression::Binary { | ||
| 142 | operator: op.clone(), | ||
| 143 | left: Box::new(left_handle), | ||
| 144 | right: Box::new(right_handle), | ||
| 145 | } | ||
| 146 | } | ||
| 147 | ExprData::Block(block) => { | ||
| 148 | if !block.has_implicit_return { | ||
| 149 | panic!("Expression blocks must have an implicit return"); | ||
| 150 | } | ||
| 151 | ctx.push_context(); | ||
| 152 | for (i, statement) in block.statements.iter().enumerate() { | ||
| 153 | if i == block.statements.len() - 1 { | ||
| 154 | break; | ||
| 155 | } else { | ||
| 156 | translate_statement(statement, ctx); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | todo!() | ||
| 160 | // let result = get_expression_handle(block.statements.last().unwrap(), ctx); | ||
| 161 | // let value = ctx.pop_context(); | ||
| 162 | // value.iter().for_each(|x| ctx.push(x.clone())); | ||
| 163 | // result | ||
| 164 | } | ||
| 165 | _ => { | ||
| 166 | panic!("unimplemented: {:?}", ast); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | fn translate_statement(ast: &Statement, ctx: &mut TranslateContext) { | ||
| 172 | match ast { | ||
| 173 | Statement::Decl(decl) => { | ||
| 174 | let handle = get_expression_handle(&decl.init, ctx); | ||
| 175 | let rename = ctx.declare_variable(decl.name.clone()); | ||
| 176 | ctx.push(JsStatement::Declaration { | ||
| 177 | name: rename, | ||
| 178 | value: handle, | ||
| 179 | is_constant: decl.is_constant.clone(), | ||
| 180 | }); | ||
| 181 | } | ||
| 182 | Statement::Expr(_) => todo!(), | ||
| 183 | Statement::Comment(_) => todo!(), | ||
| 184 | // Ast::Function { | ||
| 185 | // name, | ||
| 186 | // arguments, | ||
| 187 | // return_type: _, | ||
| 188 | // block, | ||
| 189 | // } => { | ||
| 190 | // let rename = ctx.declare_variable(name.clone()); | ||
| 191 | // let arguments = arguments | ||
| 192 | // .iter() | ||
| 193 | // .map(|x| ctx.declare_variable(x.name.clone())) | ||
| 194 | // .collect(); | ||
| 195 | |||
| 196 | // ctx.push_context(); | ||
| 197 | // for (i, statement) in block.value.iter().enumerate() { | ||
| 198 | // if block.implicit_return && i == block.value.len() - 1 { | ||
| 199 | // let handle = get_expression_handle(statement, ctx); | ||
| 200 | // ctx.push(JsStatement::Return(handle)); | ||
| 201 | // } else { | ||
| 202 | // translate_statement(statement, ctx); | ||
| 203 | // } | ||
| 204 | // } | ||
| 205 | // let value = ctx.pop_context(); | ||
| 206 | |||
| 207 | // ctx.push(JsStatement::Function { | ||
| 208 | // name: rename, | ||
| 209 | // arguments, | ||
| 210 | // value, | ||
| 211 | // }); | ||
| 212 | // } | ||
| 213 | // Ast::Public(value) => { | ||
| 214 | // translate_statement(value, ctx); | ||
| 215 | // // I'm sorry, I just don't know how to get the original name properly... my bad | ||
| 216 | // match ctx.peek() { | ||
| 217 | // JsStatement::Function { name, .. } | JsStatement::Declaration { name, .. } => { | ||
| 218 | // let original_name = name.split("$").next().unwrap().to_string(); | ||
| 219 | // ctx.push(JsStatement::Export(Box::new(JsStatement::Declaration { | ||
| 220 | // name: original_name, | ||
| 221 | // value: JsExpression::Identifier(name.clone()), | ||
| 222 | // is_constant: true, | ||
| 223 | // }))); | ||
| 224 | // } | ||
| 225 | // _ => { | ||
| 226 | // panic!("Cannot make non-function public"); | ||
| 227 | // } | ||
| 228 | // } | ||
| 229 | // } | ||
| 230 | // Ast::Return(value) => { | ||
| 231 | // let handle = get_expression_handle(value, ctx); | ||
| 232 | // ctx.push(JsStatement::Return(handle)); | ||
| 233 | // } | ||
| 234 | // Ast::Block(block) => { | ||
| 235 | // ctx.push_context(); | ||
| 236 | // for (i, statement) in block.value.iter().enumerate() { | ||
| 237 | // if block.implicit_return && i == block.value.len() - 1 { | ||
| 238 | // let handle = get_expression_handle(statement, ctx); | ||
| 239 | // ctx.push(JsStatement::Return(handle)); | ||
| 240 | // } else { | ||
| 241 | // translate_statement(statement, ctx); | ||
| 242 | // } | ||
| 243 | // } | ||
| 244 | // let value = ctx.pop_context(); | ||
| 245 | // value.iter().for_each(|x| ctx.push(x.clone())); | ||
| 246 | // } | ||
| 247 | // Ast::If { | ||
| 248 | // condition, | ||
| 249 | // value: block, | ||
| 250 | // else_value, | ||
| 251 | // } => { | ||
| 252 | // let handle = get_expression_handle(condition, ctx); | ||
| 253 | |||
| 254 | // ctx.push_context(); | ||
| 255 | // for (i, statement) in block.value.iter().enumerate() { | ||
| 256 | // if block.implicit_return && i == block.value.len() - 1 { | ||
| 257 | // let handle = get_expression_handle(statement, ctx); | ||
| 258 | // ctx.push(JsStatement::Return(handle)); | ||
| 259 | // } else { | ||
| 260 | // translate_statement(statement, ctx); | ||
| 261 | // } | ||
| 262 | // } | ||
| 263 | // let value = ctx.pop_context(); | ||
| 264 | // let else_value = match else_value { | ||
| 265 | // Some(block) => { | ||
| 266 | // ctx.push_context(); | ||
| 267 | // for (i, statement) in block.value.iter().enumerate() { | ||
| 268 | // if block.implicit_return && i == block.value.len() - 1 { | ||
| 269 | // let handle = get_expression_handle(statement, ctx); | ||
| 270 | // ctx.push(JsStatement::Return(handle)); | ||
| 271 | // } else { | ||
| 272 | // translate_statement(statement, ctx); | ||
| 273 | // } | ||
| 274 | // } | ||
| 275 | // Some(ctx.pop_context()) | ||
| 276 | // } | ||
| 277 | // None => None, | ||
| 278 | // }; | ||
| 279 | |||
| 280 | // ctx.push(JsStatement::If { | ||
| 281 | // condition: handle, | ||
| 282 | // value, | ||
| 283 | // else_value, | ||
| 284 | // }); | ||
| 285 | // } | ||
| 286 | // Ast::FunctionCall { name, arguments } => { | ||
| 287 | // let mut handles = Vec::new(); | ||
| 288 | // for argument in arguments { | ||
| 289 | // handles.push(get_expression_handle(argument, ctx)); | ||
| 290 | // } | ||
| 291 | // match *name.clone() { | ||
| 292 | // Ast::Identifier(name) => { | ||
| 293 | // ctx.push(JsStatement::Expression(JsExpression::Call { | ||
| 294 | // name: Box::new(JsExpression::Identifier(ctx.get_identifier(name.clone()))), | ||
| 295 | // arguments: handles, | ||
| 296 | // })); | ||
| 297 | // } | ||
| 298 | // Ast::BuiltIn(name) => match name.as_str() { | ||
| 299 | // "@raw" => { | ||
| 300 | // if handles.len() != 1 { | ||
| 301 | // panic!("@raw expects exactly one argument"); | ||
| 302 | // } | ||
| 303 | // match handles.get(0).unwrap() { | ||
| 304 | // JsExpression::String(value) => { | ||
| 305 | // ctx.push(JsStatement::Expression(JsExpression::Call { | ||
| 306 | // name: Box::new(JsExpression::Raw(value.clone())), | ||
| 307 | // arguments: handles, | ||
| 308 | // })); | ||
| 309 | // } | ||
| 310 | // _ => panic!("@raw expects a string argument"), | ||
| 311 | // } | ||
| 312 | // } | ||
| 313 | // _ => panic!("Unknown built-in function {}", name), | ||
| 314 | // }, | ||
| 315 | // _ => panic!("Function name must be an identifier"), | ||
| 316 | // } | ||
| 317 | // } | ||
| 318 | // Ast::Comment(_) | Ast::DocComment(_) => {} | ||
| 319 | // _ => { | ||
| 320 | // panic!("unimplemented: {:?}", ast); | ||
| 321 | // } | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | pub fn translate(module: &Module) -> Result<Vec<JsStatement>> { | ||
| 326 | let mut ctx = TranslateContext::new(); | ||
| 327 | |||
| 328 | for ast in module.statements.iter() { | ||
| 329 | translate_statement(&ast.clone().to_statement(), &mut ctx); | ||
| 330 | } | ||
| 331 | |||
| 332 | Ok(ctx.pop_context()) | ||
| 333 | } | ||
crates/js_ast/src/types.rs deleted-50| ... | @@ -1,50 +0,0 @@ | ||
| 1 | use ast::BinaryOperator; | ||
| 2 | use num::bigint::BigInt; | ||
| 3 | use std::collections::HashMap; | ||
| 4 | |||
| 5 | #[derive(Debug, PartialEq, Clone)] | ||
| 6 | pub enum JsExpression { | ||
| 7 | Number(f64), | ||
| 8 | BigInt(BigInt), | ||
| 9 | String(String), | ||
| 10 | Bool(bool), | ||
| 11 | Identifier(String), | ||
| 12 | Raw(String), | ||
| 13 | Array(Vec<JsExpression>), | ||
| 14 | Object(HashMap<String, JsExpression>), | ||
| 15 | Binary { | ||
| 16 | operator: BinaryOperator, | ||
| 17 | left: Box<JsExpression>, | ||
| 18 | right: Box<JsExpression>, | ||
| 19 | }, | ||
| 20 | Function { | ||
| 21 | arguments: Vec<String>, | ||
| 22 | value: Vec<JsStatement>, | ||
| 23 | }, | ||
| 24 | Call { | ||
| 25 | base: Box<JsExpression>, | ||
| 26 | arguments: Vec<JsExpression>, | ||
| 27 | }, | ||
| 28 | } | ||
| 29 | |||
| 30 | #[derive(Debug, PartialEq, Clone)] | ||
| 31 | pub enum JsStatement { | ||
| 32 | Expression(JsExpression), | ||
| 33 | Export(Box<JsStatement>), | ||
| 34 | Return(JsExpression), | ||
| 35 | Declaration { | ||
| 36 | name: String, | ||
| 37 | value: JsExpression, | ||
| 38 | is_constant: bool, | ||
| 39 | }, | ||
| 40 | Function { | ||
| 41 | name: String, | ||
| 42 | arguments: Vec<String>, | ||
| 43 | value: Vec<JsStatement>, | ||
| 44 | }, | ||
| 45 | If { | ||
| 46 | condition: JsExpression, | ||
| 47 | value: Vec<JsStatement>, | ||
| 48 | else_value: Option<Vec<JsStatement>>, | ||
| 49 | }, | ||
| 50 | } | ||
crates/js_printer/src/print_expression.rs+91-84| ... | @@ -1,13 +1,13 @@ | ... | @@ -1,13 +1,13 @@ |
| 1 | use js_ast::JsExpression; | 1 | use js_ast::{JsExpr, JsExprData}; |
| 2 | 2 | ||
| 3 | use crate::print_statement::print_statement; | 3 | use crate::print_statement::print_statement; |
| 4 | 4 | ||
| 5 | pub fn print_expression(statement: &JsExpression) -> String { | 5 | pub fn print_expression(expr: &JsExpr) -> String { |
| 6 | match statement { | 6 | match &expr.data { |
| 7 | JsExpression::Raw(value) => format!("{}", value), | 7 | JsExprData::Raw(value) => format!("{}", value), |
| 8 | JsExpression::Number(value) => format!("{}", value), | 8 | JsExprData::Number(value) => format!("{}", value), |
| 9 | JsExpression::BigInt(value) => format!("{}n", value), | 9 | JsExprData::BigInt(value) => format!("{}n", value), |
| 10 | JsExpression::String(value) => { | 10 | JsExprData::String(value) => { |
| 11 | let mut result = String::new(); | 11 | let mut result = String::new(); |
| 12 | // TODO: This is an INCREDIBLY naive implementation of escaping. | 12 | // TODO: This is an INCREDIBLY naive implementation of escaping. |
| 13 | // Also it's probably not even correct. | 13 | // Also it's probably not even correct. |
| ... | @@ -23,9 +23,9 @@ pub fn print_expression(statement: &JsExpression) -> String { | ... | @@ -23,9 +23,9 @@ pub fn print_expression(statement: &JsExpression) -> String { |
| 23 | } | 23 | } |
| 24 | format!("\"{}\"", result) | 24 | format!("\"{}\"", result) |
| 25 | } | 25 | } |
| 26 | JsExpression::Bool(value) => format!("{}", value), | 26 | JsExprData::Bool(value) => format!("{}", value), |
| 27 | JsExpression::Identifier(value) => format!("{}", value), | 27 | JsExprData::Identifier(value) => format!("{}", value), |
| 28 | JsExpression::Array(value) => format!( | 28 | JsExprData::Array(value) => format!( |
| 29 | "[{}]", | 29 | "[{}]", |
| 30 | value | 30 | value |
| 31 | .iter() | 31 | .iter() |
| ... | @@ -33,67 +33,83 @@ pub fn print_expression(statement: &JsExpression) -> String { | ... | @@ -33,67 +33,83 @@ pub fn print_expression(statement: &JsExpression) -> String { |
| 33 | .collect::<Vec<String>>() | 33 | .collect::<Vec<String>>() |
| 34 | .join(",") | 34 | .join(",") |
| 35 | ), | 35 | ), |
| 36 | JsExpression::Object(value) => format!( | 36 | JsExprData::Object(value) => format!( |
| 37 | "{{{}}}", | 37 | "{{{}}}", |
| 38 | value | 38 | value |
| 39 | .iter() | 39 | .iter() |
| 40 | .map(|(k, v)| format!("\"{}\":{}", k, print_expression(v))) | 40 | .map(|entry| format!( |
| 41 | "{}:{}", | ||
| 42 | print_expression(&entry.key), | ||
| 43 | print_expression(&entry.value) | ||
| 44 | )) | ||
| 41 | .collect::<Vec<String>>() | 45 | .collect::<Vec<String>>() |
| 42 | .join(",") | 46 | .join(",") |
| 43 | ), | 47 | ), |
| 44 | JsExpression::Binary { | 48 | JsExprData::Binary { |
| 45 | operator, | 49 | operator, |
| 46 | left, | 50 | left, |
| 47 | right, | 51 | right, |
| 48 | } => format!( | 52 | } => format!( |
| 49 | "{}{}{}", | 53 | "{}{}{}", |
| 50 | print_expression(left), | 54 | print_expression(&left), |
| 51 | operator.clone().to_string(), | 55 | operator.clone().to_string(), |
| 52 | print_expression(right) | 56 | print_expression(&right) |
| 53 | ), | 57 | ), |
| 54 | JsExpression::Function { arguments, value } => format!( | 58 | // JsExprData::Function { |
| 55 | "({})=>{{{}}}", | 59 | // arguments, |
| 56 | arguments.join(","), | 60 | // value, |
| 57 | value | 61 | // name: _, |
| 58 | .iter() | 62 | // params: _, |
| 59 | .map(|x| format!("{}", print_statement(x))) | 63 | // block, |
| 60 | .collect::<Vec<String>>() | 64 | // } => format!( |
| 61 | .join("") | 65 | // "({})=>{{{}}}", |
| 62 | ), | 66 | // arguments.join(","), |
| 63 | JsExpression::Call { base, arguments } => format!( | 67 | // value |
| 68 | // .iter() | ||
| 69 | // .map(|x| format!("{}", print_statement(x))) | ||
| 70 | // .collect::<Vec<String>>() | ||
| 71 | // .join("") | ||
| 72 | // ), | ||
| 73 | JsExprData::Call { base, arguments } => format!( | ||
| 64 | "{}({})", | 74 | "{}({})", |
| 65 | match *base.clone() { | 75 | print_expression(&base), |
| 66 | JsExpression::Identifier(value) => value.clone(), | ||
| 67 | JsExpression::Raw(value) => value.clone(), | ||
| 68 | _ => panic!("Function call name must be an identifier"), | ||
| 69 | }, | ||
| 70 | arguments | 76 | arguments |
| 71 | .iter() | 77 | .iter() |
| 72 | .map(|x| print_expression(x)) | 78 | .map(|x| print_expression(x)) |
| 73 | .collect::<Vec<String>>() | 79 | .collect::<Vec<String>>() |
| 74 | .join(",") | 80 | .join(",") |
| 75 | ), | 81 | ), |
| 82 | JsExprData::Null => todo!(), | ||
| 83 | JsExprData::Undefined => todo!(), | ||
| 84 | JsExprData::Unary { operator, value } => todo!(), | ||
| 85 | JsExprData::Dot { base, name } => todo!(), | ||
| 86 | JsExprData::Index { base, index } => todo!(), | ||
| 87 | JsExprData::Function { | ||
| 88 | name, | ||
| 89 | params, | ||
| 90 | block, | ||
| 91 | } => todo!(), | ||
| 92 | JsExprData::If { condition, yes, no } => todo!(), | ||
| 93 | JsExprData::ArrowFunction { params, block } => todo!(), | ||
| 94 | JsExprData::Comma { items } => todo!(), | ||
| 76 | } | 95 | } |
| 77 | } | 96 | } |
| 78 | 97 | ||
| 79 | #[cfg(test)] | 98 | #[cfg(test)] |
| 80 | mod tests { | 99 | mod tests { |
| 81 | use crate::print_expression::print_expression; | 100 | use crate::print_expression::print_expression; |
| 82 | use js_ast::JsExpression; | 101 | use js_ast::JsExprData; |
| 83 | use js_ast::JsStatement; | 102 | use js_ast::JsStatement; |
| 84 | use num::{bigint::Sign, BigInt}; | 103 | use num::{bigint::Sign, BigInt}; |
| 85 | use std::collections::HashMap; | 104 | use std::collections::HashMap; |
| 86 | 105 | ||
| 87 | #[test] | 106 | #[test] |
| 88 | fn number() { | 107 | fn number() { |
| 89 | assert_eq!(print_expression(&JsExpression::Number(1.0)), "1"); | 108 | assert_eq!(print_expression(&JsExprData::Number(1.0)), "1"); |
| 90 | assert_eq!(print_expression(&JsExpression::Number(1.5)), "1.5"); | 109 | assert_eq!(print_expression(&JsExprData::Number(1.5)), "1.5"); |
| 91 | assert_eq!( | 110 | assert_eq!(print_expression(&JsExprData::Number(1.5e10)), "15000000000"); |
| 92 | print_expression(&JsExpression::Number(1.5e10)), | ||
| 93 | "15000000000" | ||
| 94 | ); | ||
| 95 | assert_eq!( | 111 | assert_eq!( |
| 96 | print_expression(&JsExpression::Number(1.5e-10)), | 112 | print_expression(&JsExprData::Number(1.5e-10)), |
| 97 | "0.00000000015" | 113 | "0.00000000015" |
| 98 | ); | 114 | ); |
| 99 | } | 115 | } |
| ... | @@ -101,18 +117,15 @@ mod tests { | ... | @@ -101,18 +117,15 @@ mod tests { |
| 101 | #[test] | 117 | #[test] |
| 102 | fn bigint() { | 118 | fn bigint() { |
| 103 | assert_eq!( | 119 | assert_eq!( |
| 104 | print_expression(&JsExpression::BigInt(BigInt::new(Sign::Plus, vec![1]))), | 120 | print_expression(&JsExprData::BigInt(BigInt::new(Sign::Plus, vec![1]))), |
| 105 | "1n" | 121 | "1n" |
| 106 | ); | 122 | ); |
| 107 | assert_eq!( | 123 | assert_eq!( |
| 108 | print_expression(&JsExpression::BigInt(BigInt::new( | 124 | print_expression(&JsExprData::BigInt(BigInt::new(Sign::Plus, vec![10000000]))), |
| 109 | Sign::Plus, | ||
| 110 | vec![10000000] | ||
| 111 | ))), | ||
| 112 | "10000000n" | 125 | "10000000n" |
| 113 | ); | 126 | ); |
| 114 | assert_eq!( | 127 | assert_eq!( |
| 115 | print_expression(&JsExpression::BigInt(BigInt::new( | 128 | print_expression(&JsExprData::BigInt(BigInt::new( |
| 116 | Sign::Minus, | 129 | Sign::Minus, |
| 117 | vec![10000000] | 130 | vec![10000000] |
| 118 | ))), | 131 | ))), |
| ... | @@ -123,44 +136,44 @@ mod tests { | ... | @@ -123,44 +136,44 @@ mod tests { |
| 123 | #[test] | 136 | #[test] |
| 124 | fn string() { | 137 | fn string() { |
| 125 | assert_eq!( | 138 | assert_eq!( |
| 126 | print_expression(&JsExpression::String("".to_string())), | 139 | print_expression(&JsExprData::String("".to_string())), |
| 127 | "\"\"" | 140 | "\"\"" |
| 128 | ); | 141 | ); |
| 129 | assert_eq!( | 142 | assert_eq!( |
| 130 | print_expression(&JsExpression::String("hello".to_string())), | 143 | print_expression(&JsExprData::String("hello".to_string())), |
| 131 | "\"hello\"" | 144 | "\"hello\"" |
| 132 | ); | 145 | ); |
| 133 | assert_eq!( | 146 | assert_eq!( |
| 134 | print_expression(&JsExpression::String("hello\nworld".to_string())), | 147 | print_expression(&JsExprData::String("hello\nworld".to_string())), |
| 135 | "\"hello\\nworld\"" | 148 | "\"hello\\nworld\"" |
| 136 | ); | 149 | ); |
| 137 | } | 150 | } |
| 138 | 151 | ||
| 139 | #[test] | 152 | #[test] |
| 140 | fn bool() { | 153 | fn bool() { |
| 141 | assert_eq!(print_expression(&JsExpression::Bool(true)), "true"); | 154 | assert_eq!(print_expression(&JsExprData::Bool(true)), "true"); |
| 142 | assert_eq!(print_expression(&JsExpression::Bool(false)), "false"); | 155 | assert_eq!(print_expression(&JsExprData::Bool(false)), "false"); |
| 143 | } | 156 | } |
| 144 | 157 | ||
| 145 | #[test] | 158 | #[test] |
| 146 | fn identifier() { | 159 | fn identifier() { |
| 147 | assert_eq!( | 160 | assert_eq!( |
| 148 | print_expression(&JsExpression::Identifier("hello".to_string())), | 161 | print_expression(&JsExprData::Identifier("hello".to_string())), |
| 149 | "hello" | 162 | "hello" |
| 150 | ); | 163 | ); |
| 151 | } | 164 | } |
| 152 | 165 | ||
| 153 | #[test] | 166 | #[test] |
| 154 | fn array() { | 167 | fn array() { |
| 155 | assert_eq!(print_expression(&JsExpression::Array(vec![])), "[]"); | 168 | assert_eq!(print_expression(&JsExprData::Array(vec![])), "[]"); |
| 156 | assert_eq!( | 169 | assert_eq!( |
| 157 | print_expression(&JsExpression::Array(vec![JsExpression::Number(1.0)])), | 170 | print_expression(&JsExprData::Array(vec![JsExprData::Number(1.0)])), |
| 158 | "[1]" | 171 | "[1]" |
| 159 | ); | 172 | ); |
| 160 | assert_eq!( | 173 | assert_eq!( |
| 161 | print_expression(&JsExpression::Array(vec![ | 174 | print_expression(&JsExprData::Array(vec![ |
| 162 | JsExpression::Number(1.0), | 175 | JsExprData::Number(1.0), |
| 163 | JsExpression::Number(2.0) | 176 | JsExprData::Number(2.0) |
| 164 | ])), | 177 | ])), |
| 165 | "[1,2]" | 178 | "[1,2]" |
| 166 | ); | 179 | ); |
| ... | @@ -168,33 +181,27 @@ mod tests { | ... | @@ -168,33 +181,27 @@ mod tests { |
| 168 | 181 | ||
| 169 | #[test] | 182 | #[test] |
| 170 | fn object() { | 183 | fn object() { |
| 171 | assert_eq!( | 184 | assert_eq!(print_expression(&JsExprData::Object(HashMap::new())), "{}"); |
| 172 | print_expression(&JsExpression::Object(HashMap::new())), | ||
| 173 | "{}" | ||
| 174 | ); | ||
| 175 | let mut map = HashMap::new(); | 185 | let mut map = HashMap::new(); |
| 176 | map.insert("hello".to_string(), JsExpression::Number(1.0)); | 186 | map.insert("hello".to_string(), JsExprData::Number(1.0)); |
| 177 | assert_eq!( | 187 | assert_eq!(print_expression(&JsExprData::Object(map)), "{\"hello\":1}"); |
| 178 | print_expression(&JsExpression::Object(map)), | ||
| 179 | "{\"hello\":1}" | ||
| 180 | ); | ||
| 181 | } | 188 | } |
| 182 | 189 | ||
| 183 | #[test] | 190 | #[test] |
| 184 | fn operation() { | 191 | fn operation() { |
| 185 | assert_eq!( | 192 | assert_eq!( |
| 186 | print_expression(&JsExpression::Binary { | 193 | print_expression(&JsExprData::Binary { |
| 187 | operator: AnyOperator::Add, | 194 | operator: AnyOperator::Add, |
| 188 | left: Box::new(JsExpression::Number(1.0)), | 195 | left: Box::new(JsExprData::Number(1.0)), |
| 189 | right: Box::new(JsExpression::Number(2.0)), | 196 | right: Box::new(JsExprData::Number(2.0)), |
| 190 | }), | 197 | }), |
| 191 | "1+2" | 198 | "1+2" |
| 192 | ); | 199 | ); |
| 193 | assert_eq!( | 200 | assert_eq!( |
| 194 | print_expression(&JsExpression::Binary { | 201 | print_expression(&JsExprData::Binary { |
| 195 | operator: AnyOperator::Sub, | 202 | operator: AnyOperator::Sub, |
| 196 | left: Box::new(JsExpression::Number(1.0)), | 203 | left: Box::new(JsExprData::Number(1.0)), |
| 197 | right: Box::new(JsExpression::Number(2.0)), | 204 | right: Box::new(JsExprData::Number(2.0)), |
| 198 | }), | 205 | }), |
| 199 | "1-2" | 206 | "1-2" |
| 200 | ); | 207 | ); |
| ... | @@ -203,35 +210,35 @@ mod tests { | ... | @@ -203,35 +210,35 @@ mod tests { |
| 203 | #[test] | 210 | #[test] |
| 204 | fn function() { | 211 | fn function() { |
| 205 | use crate::print_expression::print_expression; | 212 | use crate::print_expression::print_expression; |
| 206 | use js_ast::JsExpression; | 213 | use js_ast::JsExprData; |
| 207 | 214 | ||
| 208 | assert_eq!( | 215 | assert_eq!( |
| 209 | print_expression(&JsExpression::Function { | 216 | print_expression(&JsExprData::Function { |
| 210 | arguments: vec![], | 217 | arguments: vec![], |
| 211 | value: vec![], | 218 | value: vec![], |
| 212 | }), | 219 | }), |
| 213 | "()=>{}" | 220 | "()=>{}" |
| 214 | ); | 221 | ); |
| 215 | assert_eq!( | 222 | assert_eq!( |
| 216 | print_expression(&JsExpression::Function { | 223 | print_expression(&JsExprData::Function { |
| 217 | arguments: vec!["a".to_string()], | 224 | arguments: vec!["a".to_string()], |
| 218 | value: vec![], | 225 | value: vec![], |
| 219 | }), | 226 | }), |
| 220 | "(a)=>{}" | 227 | "(a)=>{}" |
| 221 | ); | 228 | ); |
| 222 | assert_eq!( | 229 | assert_eq!( |
| 223 | print_expression(&JsExpression::Function { | 230 | print_expression(&JsExprData::Function { |
| 224 | arguments: vec!["a".to_string(), "b".to_string()], | 231 | arguments: vec!["a".to_string(), "b".to_string()], |
| 225 | value: vec![], | 232 | value: vec![], |
| 226 | }), | 233 | }), |
| 227 | "(a,b)=>{}" | 234 | "(a,b)=>{}" |
| 228 | ); | 235 | ); |
| 229 | assert_eq!( | 236 | assert_eq!( |
| 230 | print_expression(&JsExpression::Function { | 237 | print_expression(&JsExprData::Function { |
| 231 | arguments: vec!["a".to_string(), "b".to_string()], | 238 | arguments: vec!["a".to_string(), "b".to_string()], |
| 232 | value: vec![JsStatement::Declaration { | 239 | value: vec![JsStatement::Declaration { |
| 233 | name: "a".to_string(), | 240 | name: "a".to_string(), |
| 234 | value: JsExpression::Number(1.0) | 241 | value: JsExprData::Number(1.0) |
| 235 | }], | 242 | }], |
| 236 | }), | 243 | }), |
| 237 | "(a,b)=>{let a=1;}" | 244 | "(a,b)=>{let a=1;}" |
| ... | @@ -241,23 +248,23 @@ mod tests { | ... | @@ -241,23 +248,23 @@ mod tests { |
| 241 | #[test] | 248 | #[test] |
| 242 | fn function_call() { | 249 | fn function_call() { |
| 243 | assert_eq!( | 250 | assert_eq!( |
| 244 | print_expression(&JsExpression::Call { | 251 | print_expression(&JsExprData::Call { |
| 245 | name: Box::new(JsExpression::Identifier("hello".to_string())), | 252 | name: Box::new(JsExprData::Identifier("hello".to_string())), |
| 246 | arguments: vec![], | 253 | arguments: vec![], |
| 247 | }), | 254 | }), |
| 248 | "hello()" | 255 | "hello()" |
| 249 | ); | 256 | ); |
| 250 | assert_eq!( | 257 | assert_eq!( |
| 251 | print_expression(&JsExpression::Call { | 258 | print_expression(&JsExprData::Call { |
| 252 | name: Box::new(JsExpression::Identifier("hello".to_string())), | 259 | name: Box::new(JsExprData::Identifier("hello".to_string())), |
| 253 | arguments: vec![JsExpression::Number(1.0)], | 260 | arguments: vec![JsExprData::Number(1.0)], |
| 254 | }), | 261 | }), |
| 255 | "hello(1)" | 262 | "hello(1)" |
| 256 | ); | 263 | ); |
| 257 | assert_eq!( | 264 | assert_eq!( |
| 258 | print_expression(&JsExpression::Call { | 265 | print_expression(&JsExprData::Call { |
| 259 | name: Box::new(JsExpression::Identifier("hello".to_string())), | 266 | name: Box::new(JsExprData::Identifier("hello".to_string())), |
| 260 | arguments: vec![JsExpression::Number(1.0), JsExpression::Number(2.0)], | 267 | arguments: vec![JsExprData::Number(1.0), JsExprData::Number(2.0)], |
| 261 | }), | 268 | }), |
| 262 | "hello(1,2)" | 269 | "hello(1,2)" |
| 263 | ); | 270 | ); |
crates/js_printer/src/print_statement.rs+20-21| ... | @@ -1,15 +1,16 @@ | ... | @@ -1,15 +1,16 @@ |
| 1 | use js_ast::JsStatement; | 1 | use js_ast::{JsStatement, JsStatementData}; |
| 2 | 2 | ||
| 3 | use crate::print_expression::print_expression; | 3 | use crate::print_expression::print_expression; |
| 4 | 4 | ||
| 5 | pub fn print_statement(statement: &JsStatement) -> String { | 5 | pub fn print_statement(statement: &JsStatement) -> String { |
| 6 | match statement { | 6 | match &statement.data { |
| 7 | JsStatement::Expression(value) => format!("{};", print_expression(value)), | 7 | JsStatementData::Expression(value) => format!("{};", print_expression(&value)), |
| 8 | JsStatement::Export(value) => format!("export {}", print_statement(value)), | 8 | // JsStatementData::Export(value) => format!("export {}", print_statement(value)), |
| 9 | JsStatement::Return(value) => format!("return {};", print_expression(value)), | 9 | JsStatementData::Export(value) => todo!(), |
| 10 | JsStatement::Declaration { | 10 | JsStatementData::Return(value) => format!("return {};", print_expression(&value)), |
| 11 | JsStatementData::Declaration { | ||
| 11 | name, | 12 | name, |
| 12 | value, | 13 | init, |
| 13 | is_constant, | 14 | is_constant, |
| 14 | } => { | 15 | } => { |
| 15 | format!( | 16 | format!( |
| ... | @@ -19,32 +20,30 @@ pub fn print_statement(statement: &JsStatement) -> String { | ... | @@ -19,32 +20,30 @@ pub fn print_statement(statement: &JsStatement) -> String { |
| 19 | false => "let", | 20 | false => "let", |
| 20 | }, | 21 | }, |
| 21 | name, | 22 | name, |
| 22 | print_expression(value) | 23 | print_expression(&init) |
| 23 | ) | 24 | ) |
| 24 | } | 25 | } |
| 25 | JsStatement::Function { | 26 | JsStatementData::Function { |
| 26 | name, | 27 | name, |
| 27 | arguments, | 28 | block, |
| 28 | value, | 29 | params, |
| 29 | } => format!( | 30 | } => format!( |
| 30 | "function {}({}){{{}}}", | 31 | "function {}({}){{{}}}", |
| 31 | name, | 32 | name, |
| 32 | arguments.join(","), | 33 | params.join(","), |
| 33 | value | 34 | block |
| 35 | .statements | ||
| 34 | .iter() | 36 | .iter() |
| 35 | .map(|x| format!("{}", print_statement(x))) | 37 | .map(|x| format!("{}", print_statement(x))) |
| 36 | .collect::<Vec<String>>() | 38 | .collect::<Vec<String>>() |
| 37 | .join("") | 39 | .join("") |
| 38 | ), | 40 | ), |
| 39 | JsStatement::If { | 41 | JsStatementData::If { condition, yes, no } => { |
| 40 | condition, | 42 | let else_value = match no { |
| 41 | value, | ||
| 42 | else_value, | ||
| 43 | } => { | ||
| 44 | let else_value = match else_value { | ||
| 45 | Some(value) => format!( | 43 | Some(value) => format!( |
| 46 | "else{{{}}}", | 44 | "else{{{}}}", |
| 47 | value | 45 | value |
| 46 | .statements | ||
| 48 | .iter() | 47 | .iter() |
| 49 | .map(|x| format!("{}", print_statement(x))) | 48 | .map(|x| format!("{}", print_statement(x))) |
| 50 | .collect::<Vec<String>>() | 49 | .collect::<Vec<String>>() |
| ... | @@ -54,8 +53,8 @@ pub fn print_statement(statement: &JsStatement) -> String { | ... | @@ -54,8 +53,8 @@ pub fn print_statement(statement: &JsStatement) -> String { |
| 54 | }; | 53 | }; |
| 55 | format!( | 54 | format!( |
| 56 | "if({}){{{}}}{}", | 55 | "if({}){{{}}}{}", |
| 57 | print_expression(condition), | 56 | print_expression(&condition), |
| 58 | value | 57 | yes.statements |
| 59 | .iter() | 58 | .iter() |
| 60 | .map(|x| format!("{}", print_statement(x))) | 59 | .map(|x| format!("{}", print_statement(x))) |
| 61 | .collect::<Vec<String>>() | 60 | .collect::<Vec<String>>() |
crates/parser/parse_expr_if.rs+15-4| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | use ast::{BlockWithLoc, If}; | 1 | use ast::{BlockWithLoc, Expr, ExprData, If}; |
| 2 | use miette::Result; | 2 | use miette::Result; |
| 3 | use tokenizer::Token; | 3 | use tokenizer::Token; |
| 4 | 4 | ||
| ... | @@ -48,10 +48,21 @@ pub fn parse_expr_if(p: &mut Parser) -> Result<If> { | ... | @@ -48,10 +48,21 @@ pub fn parse_expr_if(p: &mut Parser) -> Result<If> { |
| 48 | match p.peek() { | 48 | match p.peek() { |
| 49 | Some(Token::OpenBrace) => { | 49 | Some(Token::OpenBrace) => { |
| 50 | let loc = p.loc(); | 50 | let loc = p.loc(); |
| 51 | Some(BlockWithLoc { | 51 | let comments = p.take_comments(); |
| 52 | block: parse_block(p)?, | 52 | Some(Box::new(Expr::new( |
| 53 | ExprData::Block(parse_block(p)?), | ||
| 53 | loc, | 54 | loc, |
| 54 | }) | 55 | comments, |
| 56 | ))) | ||
| 57 | } | ||
| 58 | Some(Token::If) => { | ||
| 59 | let loc = p.loc(); | ||
| 60 | let comments = p.take_comments(); | ||
| 61 | Some(Box::new(Expr::new( | ||
| 62 | ExprData::If(parse_expr_if(p)?), | ||
| 63 | loc, | ||
| 64 | comments, | ||
| 65 | ))) | ||
| 55 | } | 66 | } |
| 56 | _ => return Err(p.error("Expected a block after else".to_string()).into()), | 67 | _ => return Err(p.error("Expected a block after else".to_string()).into()), |
| 57 | } | 68 | } |
crates/tokenizer/src/lib.rs+9-1| ... | @@ -1,9 +1,17 @@ | ... | @@ -1,9 +1,17 @@ |
| 1 | use miette::{Diagnostic, Result}; | 1 | use miette::{Diagnostic, Result}; |
| 2 | use thiserror::Error; | 2 | use thiserror::Error; |
| 3 | 3 | ||
| 4 | #[derive(Debug, Clone)] | 4 | #[derive(Debug, PartialEq, Clone)] |
| 5 | pub struct Loc {} | 5 | pub struct Loc {} |
| 6 | 6 | ||
| 7 | impl Loc { | ||
| 8 | pub const EMPTY: Loc = Loc {}; | ||
| 9 | |||
| 10 | pub fn new() -> Self { | ||
| 11 | Self {} | ||
| 12 | } | ||
| 13 | } | ||
| 14 | |||
| 7 | #[derive(Error, Diagnostic, Debug)] | 15 | #[derive(Error, Diagnostic, Debug)] |
| 8 | #[error("UnknownCharacter")] | 16 | #[error("UnknownCharacter")] |
| 9 | struct UnknownCharacter { | 17 | struct UnknownCharacter { |
examples/basics/comments.blight+9-2| ... | @@ -1,2 +1,9 @@ | ... | @@ -1,2 +1,9 @@ |
| 1 | 2 // yapping | ||
| 2 | + 2; | ||
| \ No newline at end of file | |||
| 1 | // this is a regular comment | ||
| 2 | let x: int = 0; | ||
| 3 | |||
| 4 | /// this is a doc comment | ||
| 5 | let y: int = 0; | ||
| 6 | |||
| 7 | /// Doc comments can be multiple lines. | ||
| 8 | /// This can be done by simply adding another triple-slash to the next line. | ||
| 9 | let z: int = 0; | ||