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 = [
240240 "miette",
241241 "num",
242242 "thiserror",
243 "tokenizer",
243244]
244245
245246[[package]]
crates/ast/ast.rs+55-2
......@@ -59,7 +59,7 @@ pub struct DocComment {
5959 pub loc: Loc,
6060}
6161
62#[derive(Debug, Clone)]
62#[derive(Debug, PartialEq, Clone)]
6363pub struct Comment {
6464 pub data: String,
6565}
......@@ -98,7 +98,7 @@ pub struct BlockWithLoc {
9898pub struct If {
9999 pub condition: Box<Expr>,
100100 pub yes: BlockWithLoc,
101 pub no: Option<BlockWithLoc>,
101 pub no: Option<Box<Expr>>,
102102}
103103
104104#[derive(Debug, Clone)]
......@@ -166,6 +166,52 @@ impl BuiltinId {
166166 }
167167}
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
169215impl Statement {
170216 pub fn requires_semicolon(&self) -> bool {
171217 match &self {
......@@ -178,6 +224,13 @@ impl Statement {
178224 }
179225 }
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
181234 pub fn to_top_level(self) -> TopLevelStatement {
182235 match self {
183236 Statement::Expr(expr) => TopLevelStatement::Expr(expr),
crates/js_ast/Cargo.toml+2
......@@ -4,6 +4,7 @@ version = "0.1.0"
44edition = "2021"
55
66[lib]
7path = "./lib.rs"
78
89[dependencies]
910miette.workspace = true
......@@ -11,3 +12,4 @@ thiserror.workspace = true
1112num.workspace = true
1213
1314ast = { 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 @@
1use js_ast::JsExpression;
1use js_ast::{JsExpr, JsExprData};
22
33use crate::print_statement::print_statement;
44
5pub fn print_expression(statement: &JsExpression) -> String {
6 match statement {
7 JsExpression::Raw(value) => format!("{}", value),
8 JsExpression::Number(value) => format!("{}", value),
9 JsExpression::BigInt(value) => format!("{}n", value),
10 JsExpression::String(value) => {
5pub fn print_expression(expr: &JsExpr) -> String {
6 match &expr.data {
7 JsExprData::Raw(value) => format!("{}", value),
8 JsExprData::Number(value) => format!("{}", value),
9 JsExprData::BigInt(value) => format!("{}n", value),
10 JsExprData::String(value) => {
1111 let mut result = String::new();
1212 // TODO: This is an INCREDIBLY naive implementation of escaping.
1313 // Also it's probably not even correct.
......@@ -23,9 +23,9 @@ pub fn print_expression(statement: &JsExpression) -> String {
2323 }
2424 format!("\"{}\"", result)
2525 }
26 JsExpression::Bool(value) => format!("{}", value),
27 JsExpression::Identifier(value) => format!("{}", value),
28 JsExpression::Array(value) => format!(
26 JsExprData::Bool(value) => format!("{}", value),
27 JsExprData::Identifier(value) => format!("{}", value),
28 JsExprData::Array(value) => format!(
2929 "[{}]",
3030 value
3131 .iter()
......@@ -33,67 +33,83 @@ pub fn print_expression(statement: &JsExpression) -> String {
3333 .collect::<Vec<String>>()
3434 .join(",")
3535 ),
36 JsExpression::Object(value) => format!(
36 JsExprData::Object(value) => format!(
3737 "{{{}}}",
3838 value
3939 .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 ))
4145 .collect::<Vec<String>>()
4246 .join(",")
4347 ),
44 JsExpression::Binary {
48 JsExprData::Binary {
4549 operator,
4650 left,
4751 right,
4852 } => format!(
4953 "{}{}{}",
50 print_expression(left),
54 print_expression(&left),
5155 operator.clone().to_string(),
52 print_expression(right)
56 print_expression(&right)
5357 ),
54 JsExpression::Function { arguments, value } => format!(
55 "({})=>{{{}}}",
56 arguments.join(","),
57 value
58 .iter()
59 .map(|x| format!("{}", print_statement(x)))
60 .collect::<Vec<String>>()
61 .join("")
62 ),
63 JsExpression::Call { base, arguments } => format!(
58 // JsExprData::Function {
59 // arguments,
60 // value,
61 // name: _,
62 // params: _,
63 // block,
64 // } => format!(
65 // "({})=>{{{}}}",
66 // arguments.join(","),
67 // value
68 // .iter()
69 // .map(|x| format!("{}", print_statement(x)))
70 // .collect::<Vec<String>>()
71 // .join("")
72 // ),
73 JsExprData::Call { base, arguments } => format!(
6474 "{}({})",
65 match *base.clone() {
66 JsExpression::Identifier(value) => value.clone(),
67 JsExpression::Raw(value) => value.clone(),
68 _ => panic!("Function call name must be an identifier"),
69 },
75 print_expression(&base),
7076 arguments
7177 .iter()
7278 .map(|x| print_expression(x))
7379 .collect::<Vec<String>>()
7480 .join(",")
7581 ),
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!(),
7695 }
7796}
7897
7998#[cfg(test)]
8099mod tests {
81100 use crate::print_expression::print_expression;
82 use js_ast::JsExpression;
101 use js_ast::JsExprData;
83102 use js_ast::JsStatement;
84103 use num::{bigint::Sign, BigInt};
85104 use std::collections::HashMap;
86105
87106 #[test]
88107 fn number() {
89 assert_eq!(print_expression(&JsExpression::Number(1.0)), "1");
90 assert_eq!(print_expression(&JsExpression::Number(1.5)), "1.5");
91 assert_eq!(
92 print_expression(&JsExpression::Number(1.5e10)),
93 "15000000000"
94 );
108 assert_eq!(print_expression(&JsExprData::Number(1.0)), "1");
109 assert_eq!(print_expression(&JsExprData::Number(1.5)), "1.5");
110 assert_eq!(print_expression(&JsExprData::Number(1.5e10)), "15000000000");
95111 assert_eq!(
96 print_expression(&JsExpression::Number(1.5e-10)),
112 print_expression(&JsExprData::Number(1.5e-10)),
97113 "0.00000000015"
98114 );
99115 }
......@@ -101,18 +117,15 @@ mod tests {
101117 #[test]
102118 fn bigint() {
103119 assert_eq!(
104 print_expression(&JsExpression::BigInt(BigInt::new(Sign::Plus, vec![1]))),
120 print_expression(&JsExprData::BigInt(BigInt::new(Sign::Plus, vec![1]))),
105121 "1n"
106122 );
107123 assert_eq!(
108 print_expression(&JsExpression::BigInt(BigInt::new(
109 Sign::Plus,
110 vec![10000000]
111 ))),
124 print_expression(&JsExprData::BigInt(BigInt::new(Sign::Plus, vec![10000000]))),
112125 "10000000n"
113126 );
114127 assert_eq!(
115 print_expression(&JsExpression::BigInt(BigInt::new(
128 print_expression(&JsExprData::BigInt(BigInt::new(
116129 Sign::Minus,
117130 vec![10000000]
118131 ))),
......@@ -123,44 +136,44 @@ mod tests {
123136 #[test]
124137 fn string() {
125138 assert_eq!(
126 print_expression(&JsExpression::String("".to_string())),
139 print_expression(&JsExprData::String("".to_string())),
127140 "\"\""
128141 );
129142 assert_eq!(
130 print_expression(&JsExpression::String("hello".to_string())),
143 print_expression(&JsExprData::String("hello".to_string())),
131144 "\"hello\""
132145 );
133146 assert_eq!(
134 print_expression(&JsExpression::String("hello\nworld".to_string())),
147 print_expression(&JsExprData::String("hello\nworld".to_string())),
135148 "\"hello\\nworld\""
136149 );
137150 }
138151
139152 #[test]
140153 fn bool() {
141 assert_eq!(print_expression(&JsExpression::Bool(true)), "true");
142 assert_eq!(print_expression(&JsExpression::Bool(false)), "false");
154 assert_eq!(print_expression(&JsExprData::Bool(true)), "true");
155 assert_eq!(print_expression(&JsExprData::Bool(false)), "false");
143156 }
144157
145158 #[test]
146159 fn identifier() {
147160 assert_eq!(
148 print_expression(&JsExpression::Identifier("hello".to_string())),
161 print_expression(&JsExprData::Identifier("hello".to_string())),
149162 "hello"
150163 );
151164 }
152165
153166 #[test]
154167 fn array() {
155 assert_eq!(print_expression(&JsExpression::Array(vec![])), "[]");
168 assert_eq!(print_expression(&JsExprData::Array(vec![])), "[]");
156169 assert_eq!(
157 print_expression(&JsExpression::Array(vec![JsExpression::Number(1.0)])),
170 print_expression(&JsExprData::Array(vec![JsExprData::Number(1.0)])),
158171 "[1]"
159172 );
160173 assert_eq!(
161 print_expression(&JsExpression::Array(vec![
162 JsExpression::Number(1.0),
163 JsExpression::Number(2.0)
174 print_expression(&JsExprData::Array(vec![
175 JsExprData::Number(1.0),
176 JsExprData::Number(2.0)
164177 ])),
165178 "[1,2]"
166179 );
......@@ -168,33 +181,27 @@ mod tests {
168181
169182 #[test]
170183 fn object() {
171 assert_eq!(
172 print_expression(&JsExpression::Object(HashMap::new())),
173 "{}"
174 );
184 assert_eq!(print_expression(&JsExprData::Object(HashMap::new())), "{}");
175185 let mut map = HashMap::new();
176 map.insert("hello".to_string(), JsExpression::Number(1.0));
177 assert_eq!(
178 print_expression(&JsExpression::Object(map)),
179 "{\"hello\":1}"
180 );
186 map.insert("hello".to_string(), JsExprData::Number(1.0));
187 assert_eq!(print_expression(&JsExprData::Object(map)), "{\"hello\":1}");
181188 }
182189
183190 #[test]
184191 fn operation() {
185192 assert_eq!(
186 print_expression(&JsExpression::Binary {
193 print_expression(&JsExprData::Binary {
187194 operator: AnyOperator::Add,
188 left: Box::new(JsExpression::Number(1.0)),
189 right: Box::new(JsExpression::Number(2.0)),
195 left: Box::new(JsExprData::Number(1.0)),
196 right: Box::new(JsExprData::Number(2.0)),
190197 }),
191198 "1+2"
192199 );
193200 assert_eq!(
194 print_expression(&JsExpression::Binary {
201 print_expression(&JsExprData::Binary {
195202 operator: AnyOperator::Sub,
196 left: Box::new(JsExpression::Number(1.0)),
197 right: Box::new(JsExpression::Number(2.0)),
203 left: Box::new(JsExprData::Number(1.0)),
204 right: Box::new(JsExprData::Number(2.0)),
198205 }),
199206 "1-2"
200207 );
......@@ -203,35 +210,35 @@ mod tests {
203210 #[test]
204211 fn function() {
205212 use crate::print_expression::print_expression;
206 use js_ast::JsExpression;
213 use js_ast::JsExprData;
207214
208215 assert_eq!(
209 print_expression(&JsExpression::Function {
216 print_expression(&JsExprData::Function {
210217 arguments: vec![],
211218 value: vec![],
212219 }),
213220 "()=>{}"
214221 );
215222 assert_eq!(
216 print_expression(&JsExpression::Function {
223 print_expression(&JsExprData::Function {
217224 arguments: vec!["a".to_string()],
218225 value: vec![],
219226 }),
220227 "(a)=>{}"
221228 );
222229 assert_eq!(
223 print_expression(&JsExpression::Function {
230 print_expression(&JsExprData::Function {
224231 arguments: vec!["a".to_string(), "b".to_string()],
225232 value: vec![],
226233 }),
227234 "(a,b)=>{}"
228235 );
229236 assert_eq!(
230 print_expression(&JsExpression::Function {
237 print_expression(&JsExprData::Function {
231238 arguments: vec!["a".to_string(), "b".to_string()],
232239 value: vec![JsStatement::Declaration {
233240 name: "a".to_string(),
234 value: JsExpression::Number(1.0)
241 value: JsExprData::Number(1.0)
235242 }],
236243 }),
237244 "(a,b)=>{let a=1;}"
......@@ -241,23 +248,23 @@ mod tests {
241248 #[test]
242249 fn function_call() {
243250 assert_eq!(
244 print_expression(&JsExpression::Call {
245 name: Box::new(JsExpression::Identifier("hello".to_string())),
251 print_expression(&JsExprData::Call {
252 name: Box::new(JsExprData::Identifier("hello".to_string())),
246253 arguments: vec![],
247254 }),
248255 "hello()"
249256 );
250257 assert_eq!(
251 print_expression(&JsExpression::Call {
252 name: Box::new(JsExpression::Identifier("hello".to_string())),
253 arguments: vec![JsExpression::Number(1.0)],
258 print_expression(&JsExprData::Call {
259 name: Box::new(JsExprData::Identifier("hello".to_string())),
260 arguments: vec![JsExprData::Number(1.0)],
254261 }),
255262 "hello(1)"
256263 );
257264 assert_eq!(
258 print_expression(&JsExpression::Call {
259 name: Box::new(JsExpression::Identifier("hello".to_string())),
260 arguments: vec![JsExpression::Number(1.0), JsExpression::Number(2.0)],
265 print_expression(&JsExprData::Call {
266 name: Box::new(JsExprData::Identifier("hello".to_string())),
267 arguments: vec![JsExprData::Number(1.0), JsExprData::Number(2.0)],
261268 }),
262269 "hello(1,2)"
263270 );
crates/js_printer/src/print_statement.rs+20-21
......@@ -1,15 +1,16 @@
1use js_ast::JsStatement;
1use js_ast::{JsStatement, JsStatementData};
22
33use crate::print_expression::print_expression;
44
55pub fn print_statement(statement: &JsStatement) -> String {
6 match statement {
7 JsStatement::Expression(value) => format!("{};", print_expression(value)),
8 JsStatement::Export(value) => format!("export {}", print_statement(value)),
9 JsStatement::Return(value) => format!("return {};", print_expression(value)),
10 JsStatement::Declaration {
6 match &statement.data {
7 JsStatementData::Expression(value) => format!("{};", print_expression(&value)),
8 // JsStatementData::Export(value) => format!("export {}", print_statement(value)),
9 JsStatementData::Export(value) => todo!(),
10 JsStatementData::Return(value) => format!("return {};", print_expression(&value)),
11 JsStatementData::Declaration {
1112 name,
12 value,
13 init,
1314 is_constant,
1415 } => {
1516 format!(
......@@ -19,32 +20,30 @@ pub fn print_statement(statement: &JsStatement) -> String {
1920 false => "let",
2021 },
2122 name,
22 print_expression(value)
23 print_expression(&init)
2324 )
2425 }
25 JsStatement::Function {
26 JsStatementData::Function {
2627 name,
27 arguments,
28 value,
28 block,
29 params,
2930 } => format!(
3031 "function {}({}){{{}}}",
3132 name,
32 arguments.join(","),
33 value
33 params.join(","),
34 block
35 .statements
3436 .iter()
3537 .map(|x| format!("{}", print_statement(x)))
3638 .collect::<Vec<String>>()
3739 .join("")
3840 ),
39 JsStatement::If {
40 condition,
41 value,
42 else_value,
43 } => {
44 let else_value = match else_value {
41 JsStatementData::If { condition, yes, no } => {
42 let else_value = match no {
4543 Some(value) => format!(
4644 "else{{{}}}",
4745 value
46 .statements
4847 .iter()
4948 .map(|x| format!("{}", print_statement(x)))
5049 .collect::<Vec<String>>()
......@@ -54,8 +53,8 @@ pub fn print_statement(statement: &JsStatement) -> String {
5453 };
5554 format!(
5655 "if({}){{{}}}{}",
57 print_expression(condition),
58 value
56 print_expression(&condition),
57 yes.statements
5958 .iter()
6059 .map(|x| format!("{}", print_statement(x)))
6160 .collect::<Vec<String>>()
crates/parser/parse_expr_if.rs+15-4
......@@ -1,4 +1,4 @@
1use ast::{BlockWithLoc, If};
1use ast::{BlockWithLoc, Expr, ExprData, If};
22use miette::Result;
33use tokenizer::Token;
44
......@@ -48,10 +48,21 @@ pub fn parse_expr_if(p: &mut Parser) -> Result<If> {
4848 match p.peek() {
4949 Some(Token::OpenBrace) => {
5050 let loc = p.loc();
51 Some(BlockWithLoc {
52 block: parse_block(p)?,
51 let comments = p.take_comments();
52 Some(Box::new(Expr::new(
53 ExprData::Block(parse_block(p)?),
5354 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 )))
5566 }
5667 _ => return Err(p.error("Expected a block after else".to_string()).into()),
5768 }
crates/tokenizer/src/lib.rs+9-1
......@@ -1,9 +1,17 @@
11use miette::{Diagnostic, Result};
22use thiserror::Error;
33
4#[derive(Debug, Clone)]
4#[derive(Debug, PartialEq, Clone)]
55pub struct Loc {}
66
7impl Loc {
8 pub const EMPTY: Loc = Loc {};
9
10 pub fn new() -> Self {
11 Self {}
12 }
13}
14
715#[derive(Error, Diagnostic, Debug)]
816#[error("UnknownCharacter")]
917struct UnknownCharacter {
examples/basics/comments.blight+9-2
......@@ -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;