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