authorchloe caruso <git@paperclover.net> 2024-01-20 21:15:12-08:00
committerchloe caruso <git@paperclover.net> 2024-01-20 21:15:12-08:00
logb9cad6ad285b99192b686be1cea8b1b8cfcdd6c1
tree71273a1743fc538d0015e6e3b465de4227bb6b39
parentb11fe69cd78904be138106b8b7fc9fdd4a86f01e

it compiles again


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]
244245
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}
6161
62#[derive(Debug, Clone)]62#[derive(Debug, PartialEq, Clone)]
63pub struct Comment {63pub 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 {
98pub struct If {98pub 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}
103103
104#[derive(Debug, Clone)]104#[derive(Debug, Clone)]
...@@ -166,6 +166,52 @@ impl BuiltinId {...@@ -166,6 +166,52 @@ impl BuiltinId {
166 }166 }
167}167}
168168
169impl 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
184impl 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
192impl Expr {
193 pub fn is_valid_js_expression(&self) -> bool {
194 self.data.is_valid_js_expression()
195 }
196}
197
198impl 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
209impl BlockWithLoc {
210 pub fn is_valid_js_expression(&self) -> bool {
211 self.block.is_valid_js_expression()
212 }
213}
214
169impl Statement {215impl 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 }
180226
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"
4edition = "2021"4edition = "2021"
55
6[lib]6[lib]
7path = "./lib.rs"
78
8[dependencies]9[dependencies]
9miette.workspace = true10miette.workspace = true
...@@ -11,3 +12,4 @@ thiserror.workspace = true...@@ -11,3 +12,4 @@ thiserror.workspace = true
11num.workspace = true12num.workspace = true
1213
13ast = { path = "../ast" }14ast = { path = "../ast" }
15tokenizer = { path = "../tokenizer" } # TODO: move Loc out of the tokenizer
crates/js_ast/js_ast.rs created+273
...@@ -0,0 +1,273 @@
1use ast::{BinaryOperator, BlockWithLoc, BuiltinId, Comment, Expr, UnaryOperator};
2use num::bigint::BigInt;
3use tokenizer::Loc;
4
5#[derive(Debug, PartialEq, Clone)]
6pub struct JsExpr {
7 pub data: JsExprData,
8 pub loc: Loc,
9 pub comment: Option<Comment>,
10}
11
12#[derive(Debug, PartialEq, Clone)]
13pub struct JsKV {
14 pub key: JsExpr,
15 pub value: JsExpr,
16}
17
18#[derive(Debug, PartialEq, Clone)]
19pub 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)]
74pub struct JsStatement {
75 pub data: JsStatementData,
76 pub loc: Loc,
77 pub comment: Option<Comment>,
78}
79
80#[derive(Debug, PartialEq, Clone)]
81pub 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)]
103pub enum JsExport {
104 Named(Vec<String>),
105 Default(JsExpr),
106 Equals(JsExpr),
107}
108
109#[derive(Debug, PartialEq, Clone)]
110pub struct JsBlock {
111 pub statements: Vec<JsStatement>,
112 pub loc: Loc,
113}
114
115impl 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
269impl 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 @@
1use std::collections::HashMap;
2
3use ast::{BlockWithLoc, Expr, ExprData, Literal, Module, Statement};
4pub use js_ast::*;
5use miette::Result;
6use tokenizer::Loc;
7
8mod js_ast;
9
10struct 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
19impl StackContext {
20 fn new(block: u32) -> Self {
21 StackContext {
22 statements: Vec::new(),
23 variable_map: HashMap::new(),
24 block,
25 }
26 }
27}
28
29struct TranslateContext {
30 context: Vec<StackContext>,
31 internal_counter: u32,
32 block_counter: u32,
33}
34
35impl 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
102fn 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
134fn 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
277fn 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
403pub 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 @@
1use std::collections::HashMap;
2
3use ast::{Expr, ExprData, Literal, Module, Statement};
4use miette::Result;
5
6mod types;
7pub use types::{JsExpression, JsStatement};
8
9struct 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
18impl StackContext {
19 fn new(block: u32) -> Self {
20 StackContext {
21 statements: Vec::new(),
22 variable_map: HashMap::new(),
23 block,
24 }
25 }
26}
27
28struct TranslateContext {
29 context: Vec<StackContext>,
30 internal_counter: u32,
31 block_counter: u32,
32}
33
34impl 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
101fn 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
171fn 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
325pub 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 @@
1use ast::BinaryOperator;
2use num::bigint::BigInt;
3use std::collections::HashMap;
4
5#[derive(Debug, PartialEq, Clone)]
6pub 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)]
31pub 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 @@
1use js_ast::JsExpression;1use js_ast::{JsExpr, JsExprData};
22
3use crate::print_statement::print_statement;3use crate::print_statement::print_statement;
44
5pub fn print_expression(statement: &JsExpression) -> String {5pub 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 value30 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 value38 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 value61 // 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 arguments76 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}
7897
79#[cfg(test)]98#[cfg(test)]
80mod tests {99mod 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;
86105
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 }
138151
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 }
144157
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 }
152165
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 {
168181
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 }
182189
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;
207214
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 @@
1use js_ast::JsStatement;1use js_ast::{JsStatement, JsStatementData};
22
3use crate::print_expression::print_expression;3use crate::print_expression::print_expression;
44
5pub fn print_statement(statement: &JsStatement) -> String {5pub 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 value34 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 value45 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 value57 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 @@
1use ast::{BlockWithLoc, If};1use ast::{BlockWithLoc, Expr, ExprData, If};
2use miette::Result;2use miette::Result;
3use tokenizer::Token;3use tokenizer::Token;
44
...@@ -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 @@
1use miette::{Diagnostic, Result};1use miette::{Diagnostic, Result};
2use thiserror::Error;2use thiserror::Error;
33
4#[derive(Debug, Clone)]4#[derive(Debug, PartialEq, Clone)]
5pub struct Loc {}5pub struct Loc {}
66
7impl 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")]
9struct UnknownCharacter {17struct UnknownCharacter {
examples/basics/comments.blight+9-2
...@@ -1,2 +1,9 @@...@@ -1,2 +1,9 @@
12 // yapping
2 + 2;
\ No newline at end of file
1// this is a regular comment
2let x: int = 0;
3
4/// this is a doc comment
5let 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.
9let z: int = 0;