authorgravatar for 11367844+lino-levan@users.noreply.github.comLino Le Van <11367844+lino-levan@users.noreply.github.com> 2024-01-18 15:42:08-08:00
committergravatar for 11367844+lino-levan@users.noreply.github.comLino Le Van <11367844+lino-levan@users.noreply.github.com> 2024-01-18 15:42:08-08:00
log8133856c0042befc85ff2725f34dfb71d50b1095
treec335dda758bfdc1f4d64d3e35917f7f33cb79d8c
parent9153cb96ee98310426d7dab102d4564af52589bd

feat: idk make blocks work I guess


19 files changed, 1049 insertions(+), 175 deletions(-)

Cargo.lock+89-1
...@@ -75,6 +75,12 @@ dependencies = [...@@ -75,6 +75,12 @@ dependencies = [
75 "util",75 "util",
76]76]
7777
78[[package]]
79name = "autocfg"
80version = "1.1.0"
81source = "registry+https://github.com/rust-lang/crates.io-index"
82checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
83
78[[package]]84[[package]]
79name = "backtrace"85name = "backtrace"
80version = "0.3.69"86version = "0.3.69"
...@@ -166,6 +172,7 @@ version = "0.1.0"...@@ -166,6 +172,7 @@ version = "0.1.0"
166dependencies = [172dependencies = [
167 "ast",173 "ast",
168 "clap",174 "clap",
175 "js_ast",
169 "js_printer",176 "js_printer",
170 "miette",177 "miette",
171 "thiserror",178 "thiserror",
...@@ -227,17 +234,22 @@ checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"...@@ -227,17 +234,22 @@ checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
227name = "js_ast"234name = "js_ast"
228version = "0.1.0"235version = "0.1.0"
229dependencies = [236dependencies = [
237 "ast",
230 "miette",238 "miette",
239 "num",
231 "thiserror",240 "thiserror",
241 "util",
232]242]
233243
234[[package]]244[[package]]
235name = "js_printer"245name = "js_printer"
236version = "0.1.0"246version = "0.1.0"
237dependencies = [247dependencies = [
238 "ast",248 "js_ast",
239 "miette",249 "miette",
250 "num",
240 "thiserror",251 "thiserror",
252 "util",
241]253]
242254
243[[package]]255[[package]]
...@@ -299,6 +311,82 @@ dependencies = [...@@ -299,6 +311,82 @@ dependencies = [
299 "adler",311 "adler",
300]312]
301313
314[[package]]
315name = "num"
316version = "0.4.1"
317source = "registry+https://github.com/rust-lang/crates.io-index"
318checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
319dependencies = [
320 "num-bigint",
321 "num-complex",
322 "num-integer",
323 "num-iter",
324 "num-rational",
325 "num-traits",
326]
327
328[[package]]
329name = "num-bigint"
330version = "0.4.4"
331source = "registry+https://github.com/rust-lang/crates.io-index"
332checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
333dependencies = [
334 "autocfg",
335 "num-integer",
336 "num-traits",
337]
338
339[[package]]
340name = "num-complex"
341version = "0.4.4"
342source = "registry+https://github.com/rust-lang/crates.io-index"
343checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
344dependencies = [
345 "num-traits",
346]
347
348[[package]]
349name = "num-integer"
350version = "0.1.45"
351source = "registry+https://github.com/rust-lang/crates.io-index"
352checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
353dependencies = [
354 "autocfg",
355 "num-traits",
356]
357
358[[package]]
359name = "num-iter"
360version = "0.1.43"
361source = "registry+https://github.com/rust-lang/crates.io-index"
362checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
363dependencies = [
364 "autocfg",
365 "num-integer",
366 "num-traits",
367]
368
369[[package]]
370name = "num-rational"
371version = "0.4.1"
372source = "registry+https://github.com/rust-lang/crates.io-index"
373checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
374dependencies = [
375 "autocfg",
376 "num-bigint",
377 "num-integer",
378 "num-traits",
379]
380
381[[package]]
382name = "num-traits"
383version = "0.2.17"
384source = "registry+https://github.com/rust-lang/crates.io-index"
385checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
386dependencies = [
387 "autocfg",
388]
389
302[[package]]390[[package]]
303name = "object"391name = "object"
304version = "0.32.2"392version = "0.32.2"
Cargo.toml+1
...@@ -14,3 +14,4 @@ members = ["crates/cli", "crates/util", "crates/tokenizer", "crates/ast", "crate...@@ -14,3 +14,4 @@ members = ["crates/cli", "crates/util", "crates/tokenizer", "crates/ast", "crate
14clap = { version = "4.4.12", features = ["derive"] }14clap = { version = "4.4.12", features = ["derive"] }
15miette = { version = "5.10.0", features = ["fancy"] }15miette = { version = "5.10.0", features = ["fancy"] }
16thiserror = "1.0.53"16thiserror = "1.0.53"
17num = "0.4.1"
crates/ast/src/parse_expression.rs+11-7
...@@ -4,12 +4,12 @@ use tokenizer::Token;...@@ -4,12 +4,12 @@ use tokenizer::Token;
44
5use super::error::AstError;5use super::error::AstError;
6use super::parse_block::parse_block;6use super::parse_block::parse_block;
7use super::types::{Ast, ParserContext};7use super::types::{Ast, Block, ParserContext};
88
9pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {9pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {
10 let left = match tokens.next() {10 let left = match tokens.next() {
11 Some(token) => match token {11 Some(token) => match token {
12 Token::Number(number) => Ast::Number(number.clone()),12 Token::Number(number) => Ast::Number(number),
13 Token::String(string) => Ast::String(string.clone()),13 Token::String(string) => Ast::String(string.clone()),
14 Token::True => Ast::Bool(true),14 Token::True => Ast::Bool(true),
15 Token::False => Ast::Bool(false),15 Token::False => Ast::Bool(false),
...@@ -24,17 +24,21 @@ pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {...@@ -24,17 +24,21 @@ pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {
2424
25 let value = parse_block(tokens)?;25 let value = parse_block(tokens)?;
2626
27 let mut else_value = None;27 let mut else_value: Option<Block> = None;
2828
29 match tokens.peek() {29 match tokens.peek() {
30 Some(Token::Else) => {30 Some(Token::Else) => {
31 tokens.next();31 tokens.next();
32 match tokens.peek() {32 match tokens.peek() {
33 Option::Some(Token::If) => {33 Some(Token::If) => {
34 else_value = Some(Box::new(parse_expression(tokens)?));34 let else_expression = parse_expression(tokens)?;
35 else_value = Some(Block {
36 value: vec![else_expression],
37 implicit_return: true,
38 });
35 }39 }
36 Option::Some(Token::OpenBracket) => {40 Some(Token::OpenBracket) => {
37 else_value = Some(Box::new(Ast::Block(parse_block(tokens)?)));41 else_value = Some(parse_block(tokens)?);
38 }42 }
39 _ => {43 _ => {
40 return Err(AstError::new(44 return Err(AstError::new(
crates/ast/src/parse_statement.rs+17-12
...@@ -6,12 +6,17 @@ use super::error::AstError;...@@ -6,12 +6,17 @@ use super::error::AstError;
6use super::parse_block::parse_block;6use super::parse_block::parse_block;
7use super::parse_expression::parse_expression;7use super::parse_expression::parse_expression;
8use super::parse_type::parse_type;8use super::parse_type::parse_type;
9use super::types::{Argument, Ast, ParserContext};9use super::types::{Argument, Ast, Block, ParserContext};
1010
11/// Precondition: There is at least one token in the iterator11/// Precondition: There is at least one token in the iterator
12pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {12pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
13 let token = tokens.next().unwrap();13 let token = tokens.next().unwrap();
14 match token {14 match token {
15 Token::OpenBracket => {
16 // FRICK, we have to backtrack
17 tokens.back();
18 Ok(Ast::Block(parse_block(tokens)?))
19 }
15 Token::Pub => {20 Token::Pub => {
16 match tokens.peek() {21 match tokens.peek() {
17 Some(Token::Let) | Some(Token::Var) | Some(Token::Function) => (),22 Some(Token::Let) | Some(Token::Var) | Some(Token::Function) => (),
...@@ -20,9 +25,7 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -20,9 +25,7 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
2025
21 let inner = parse_statement(tokens)?;26 let inner = parse_statement(tokens)?;
2227
23 return Ok(Ast::Public {28 return Ok(Ast::Public(Box::new(inner)));
24 value: Box::new(inner),
25 });
26 }29 }
27 Token::Let => {30 Token::Let => {
28 let name = match tokens.next() {31 let name = match tokens.next() {
...@@ -86,9 +89,7 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -86,9 +89,7 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
86 _ => return Err(AstError::new("Expected semicolon after return statement").into()),89 _ => return Err(AstError::new("Expected semicolon after return statement").into()),
87 };90 };
8891
89 Ok(Ast::Return {92 Ok(Ast::Return(Box::new(expression)))
90 return_value: Box::new(expression),
91 })
92 }93 }
93 Token::Function => {94 Token::Function => {
94 let name = match tokens.next() {95 let name = match tokens.next() {
...@@ -179,17 +180,21 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -179,17 +180,21 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
179180
180 let value = parse_block(tokens)?;181 let value = parse_block(tokens)?;
181182
182 let mut else_value = None;183 let mut else_value: Option<Block> = None;
183184
184 match tokens.peek() {185 match tokens.peek() {
185 Some(Token::Else) => {186 Some(Token::Else) => {
186 tokens.next();187 tokens.next();
187 match tokens.peek() {188 match tokens.peek() {
188 Option::Some(Token::If) => {189 Some(Token::If) => {
189 else_value = Some(Box::new(parse_expression(tokens)?));190 let else_if = parse_statement(tokens)?;
191 else_value = Some(Block {
192 value: vec![else_if],
193 implicit_return: false,
194 });
190 }195 }
191 Option::Some(Token::OpenBracket) => {196 Some(Token::OpenBracket) => {
192 else_value = Some(Box::new(Ast::Block(parse_block(tokens)?)));197 else_value = Some(parse_block(tokens)?);
193 }198 }
194 _ => {199 _ => {
195 return Err(AstError::new(200 return Err(AstError::new(
crates/ast/src/types.rs+4-8
...@@ -64,9 +64,6 @@ pub struct Argument {...@@ -64,9 +64,6 @@ pub struct Argument {
6464
65#[derive(Debug)]65#[derive(Debug)]
66pub enum Ast {66pub enum Ast {
67 Public {
68 value: Box<Ast>,
69 },
70 ConstantDeclaration {67 ConstantDeclaration {
71 name: String,68 name: String,
72 return_type: Type,69 return_type: Type,
...@@ -82,9 +79,6 @@ pub enum Ast {...@@ -82,9 +79,6 @@ pub enum Ast {
82 left: Box<Ast>,79 left: Box<Ast>,
83 right: Box<Ast>,80 right: Box<Ast>,
84 },81 },
85 Return {
86 return_value: Box<Ast>,
87 },
88 Function {82 Function {
89 name: String,83 name: String,
90 arguments: Vec<Argument>,84 arguments: Vec<Argument>,
...@@ -98,10 +92,12 @@ pub enum Ast {...@@ -98,10 +92,12 @@ pub enum Ast {
98 If {92 If {
99 condition: Box<Ast>,93 condition: Box<Ast>,
100 value: Block,94 value: Block,
101 else_value: Option<Box<Ast>>,95 else_value: Option<Block>,
102 },96 },
97 Return(Box<Ast>),
98 Public(Box<Ast>),
103 Block(Block),99 Block(Block),
104 Number(String),100 Number(f64),
105 Bool(bool),101 Bool(bool),
106 String(String),102 String(String),
107 Identifier(String),103 Identifier(String),
crates/cli/Cargo.toml+1
...@@ -14,4 +14,5 @@ thiserror.workspace = true...@@ -14,4 +14,5 @@ thiserror.workspace = true
1414
15tokenizer = { path = "../tokenizer" }15tokenizer = { path = "../tokenizer" }
16ast = { path = "../ast" }16ast = { path = "../ast" }
17js_ast = { path = "../js_ast" }
17js_printer = { path = "../js_printer" }18js_printer = { path = "../js_printer" }
crates/cli/src/main.rs+3-5
...@@ -3,9 +3,9 @@ use std::path::PathBuf;...@@ -3,9 +3,9 @@ use std::path::PathBuf;
3use clap::{Parser, Subcommand};3use clap::{Parser, Subcommand};
4use miette::Result;4use miette::Result;
55
6use tokenizer;
7use ast;6use ast;
8use js_printer;7use js_printer;
8use tokenizer;
99
10#[derive(Parser)]10#[derive(Parser)]
11#[command(author, version, about, long_about = None)]11#[command(author, version, about, long_about = None)]
...@@ -42,7 +42,6 @@ fn get_runtime(runtime: &String) -> String {...@@ -42,7 +42,6 @@ fn get_runtime(runtime: &String) -> String {
42 }42 }
43}43}
4444
45
46pub fn transpile(content: String) -> Result<String> {45pub fn transpile(content: String) -> Result<String> {
47 // 1. Tokenize the input46 // 1. Tokenize the input
48 let tokens = tokenizer::tokenize(content)?;47 let tokens = tokenizer::tokenize(content)?;
...@@ -53,14 +52,13 @@ pub fn transpile(content: String) -> Result<String> {...@@ -53,14 +52,13 @@ pub fn transpile(content: String) -> Result<String> {
53 // 4. Optimize the AST52 // 4. Optimize the AST
54 // optimize(tree.clone())?;53 // optimize(tree.clone())?;
55 // 5. Translate the AST into javascript54 // 5. Translate the AST into javascript
56 // translate(tree.clone())?;55 let js_ast = js_ast::translate(blight_ast)?;
57 // 6. Print the JS AST56 // 6. Print the JS AST
58 let javascript = js_printer::transpile(blight_ast)?;57 let javascript = js_printer::print(js_ast);
59 // 7. Return the javascript58 // 7. Return the javascript
60 return Ok(javascript);59 return Ok(javascript);
61}60}
6261
63
64fn main() -> Result<()> {62fn main() -> Result<()> {
65 let cli = Cli::parse();63 let cli = Cli::parse();
6664
crates/js_ast/Cargo.toml+4
...@@ -8,3 +8,7 @@ edition = "2021"...@@ -8,3 +8,7 @@ edition = "2021"
8[dependencies]8[dependencies]
9miette.workspace = true9miette.workspace = true
10thiserror.workspace = true10thiserror.workspace = true
11num.workspace = true
12
13ast = { path = "../ast" }
14util = { path = "../util" }
crates/js_ast/src/lib.rs+406
...@@ -0,0 +1,406 @@...@@ -0,0 +1,406 @@
1use std::collections::HashMap;
2
3use miette::Result;
4
5use ast::Ast;
6
7mod types;
8pub use types::{JsExpression, JsStatement};
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: JsExpression) -> JsExpression {
54 self.context
55 .last_mut()
56 .unwrap()
57 .statements
58 .push(JsStatement::VariableDeclaration {
59 name: format!("${}", self.internal_counter),
60 value,
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: &Ast, ctx: &mut TranslateContext) -> JsExpression {
102 match ast {
103 Ast::Number(value) => JsExpression::Number(*value),
104 Ast::String(value) => JsExpression::String(value.clone()),
105 Ast::Identifier(value) => JsExpression::Identifier(ctx.get_identifier(value.clone())),
106 Ast::Bool(value) => JsExpression::Bool(*value),
107 Ast::FunctionCall { name, arguments } => {
108 let mut handles = Vec::new();
109 for argument in arguments {
110 handles.push(get_expression_handle(argument, ctx));
111 }
112 ctx.push_internal_variable(JsExpression::FunctionCall {
113 name: ctx.get_identifier(name.clone()),
114 arguments: handles,
115 })
116 }
117 Ast::Operation {
118 operator,
119 left,
120 right,
121 } => {
122 let left_handle = get_expression_handle(left, ctx);
123 let right_handle = get_expression_handle(right, ctx);
124 JsExpression::Operation {
125 operator: operator.clone(),
126 left: Box::new(left_handle),
127 right: Box::new(right_handle),
128 }
129 }
130 Ast::Block(block) => {
131 if !block.implicit_return {
132 panic!("Expression blocks must have an implicit return");
133 }
134 ctx.push_context();
135 for (i, statement) in block.value.iter().enumerate() {
136 if i == block.value.len() - 1 {
137 break;
138 } else {
139 translate_statement(statement, ctx);
140 }
141 }
142 let result = get_expression_handle(block.value.last().unwrap(), ctx);
143 let value = ctx.pop_context();
144 value.iter().for_each(|x| ctx.push(x.clone()));
145 result
146 }
147 _ => {
148 panic!("unimplemented: {:?}", ast);
149 }
150 }
151}
152
153fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) {
154 match ast {
155 Ast::ConstantDeclaration {
156 name,
157 return_type: _,
158 value,
159 } => {
160 let handle = get_expression_handle(value, ctx);
161 let rename = ctx.declare_variable(name.clone());
162 ctx.push(JsStatement::ConstantDeclaration {
163 name: rename,
164 value: handle,
165 });
166 }
167 Ast::VariableDeclaration {
168 name,
169 return_type: _,
170 value,
171 } => {
172 let handle = get_expression_handle(value, ctx);
173 let rename = ctx.declare_variable(name.clone());
174 ctx.push(JsStatement::VariableDeclaration {
175 name: rename,
176 value: handle,
177 });
178 }
179 Ast::Function {
180 name,
181 arguments,
182 return_type: _,
183 block,
184 } => {
185 let rename = ctx.declare_variable(name.clone());
186 let arguments = arguments
187 .iter()
188 .map(|x| ctx.declare_variable(x.name.clone()))
189 .collect();
190
191 ctx.push_context();
192 for (i, statement) in block.value.iter().enumerate() {
193 if block.implicit_return && i == block.value.len() - 1 {
194 let handle = get_expression_handle(statement, ctx);
195 ctx.push(JsStatement::Return(handle));
196 } else {
197 translate_statement(statement, ctx);
198 }
199 }
200 let value = ctx.pop_context();
201
202 ctx.push(JsStatement::Function {
203 name: rename,
204 arguments,
205 value,
206 });
207 }
208 Ast::Public(value) => {
209 translate_statement(value, ctx);
210 // I'm sorry, I just don't know how to get the original name properly... my bad
211 match ctx.peek() {
212 JsStatement::Function { name, .. }
213 | JsStatement::VariableDeclaration { name, .. }
214 | JsStatement::ConstantDeclaration { name, .. } => {
215 let original_name = name.split("$").next().unwrap().to_string();
216 ctx.push(JsStatement::Export(Box::new(
217 JsStatement::ConstantDeclaration {
218 name: original_name,
219 value: JsExpression::Identifier(name.clone()),
220 },
221 )));
222 }
223 _ => {
224 panic!("Cannot make non-function public");
225 }
226 }
227 }
228 Ast::Return(value) => {
229 let handle = get_expression_handle(value, ctx);
230 ctx.push(JsStatement::Return(handle));
231 }
232 Ast::Block(block) => {
233 ctx.push_context();
234 for (i, statement) in block.value.iter().enumerate() {
235 if block.implicit_return && i == block.value.len() - 1 {
236 let handle = get_expression_handle(statement, ctx);
237 ctx.push(JsStatement::Return(handle));
238 } else {
239 translate_statement(statement, ctx);
240 }
241 }
242 let value = ctx.pop_context();
243 value.iter().for_each(|x| ctx.push(x.clone()));
244 }
245 Ast::If {
246 condition,
247 value: block,
248 else_value,
249 } => {
250 let handle = get_expression_handle(condition, ctx);
251
252 ctx.push_context();
253 for (i, statement) in block.value.iter().enumerate() {
254 if block.implicit_return && i == block.value.len() - 1 {
255 let handle = get_expression_handle(statement, ctx);
256 ctx.push(JsStatement::Return(handle));
257 } else {
258 translate_statement(statement, ctx);
259 }
260 }
261 let value = ctx.pop_context();
262 let else_value = match else_value {
263 Some(block) => {
264 ctx.push_context();
265 for (i, statement) in block.value.iter().enumerate() {
266 if block.implicit_return && i == block.value.len() - 1 {
267 let handle = get_expression_handle(statement, ctx);
268 ctx.push(JsStatement::Return(handle));
269 } else {
270 translate_statement(statement, ctx);
271 }
272 }
273 Some(ctx.pop_context())
274 }
275 None => None,
276 };
277
278 ctx.push(JsStatement::If {
279 condition: handle,
280 value,
281 else_value,
282 });
283 }
284 Ast::FunctionCall { name, arguments } => {
285 let mut handles = Vec::new();
286 for argument in arguments {
287 handles.push(get_expression_handle(argument, ctx));
288 }
289 ctx.push(JsStatement::Expression(JsExpression::FunctionCall {
290 name: match name.as_str() {
291 "print" => "console.log".to_string(),
292 _ => name.clone(),
293 },
294 arguments: handles,
295 }));
296 }
297 Ast::Comment(_) | Ast::DocComment(_) => {}
298 _ => {
299 panic!("unimplemented: {:?}", ast);
300 }
301 }
302}
303
304pub fn translate(_ast: Vec<Ast>) -> Result<Vec<JsStatement>> {
305 let mut ctx = TranslateContext::new();
306
307 for ast in _ast {
308 translate_statement(&ast, &mut ctx);
309 }
310
311 Ok(ctx.pop_context())
312}
313
314#[cfg(test)]
315mod tests {
316 use crate::translate;
317 use crate::{JsExpression, JsStatement};
318 use ast::{Ast, Type};
319 use util::Operator;
320
321 #[test]
322 fn test_translate() {
323 let ast = Ast::ConstantDeclaration {
324 name: "hi".to_string(),
325 return_type: Type::Int,
326 value: Box::new(Ast::Operation {
327 operator: Operator::Add,
328 left: Box::new(Ast::Operation {
329 operator: Operator::Add,
330 left: Box::new(Ast::Number(1.0)),
331 right: Box::new(Ast::Operation {
332 operator: Operator::Add,
333 left: Box::new(Ast::Number(2.0)),
334 right: Box::new(Ast::Number(3.0)),
335 }),
336 }),
337 right: Box::new(Ast::Operation {
338 operator: Operator::Add,
339 left: Box::new(Ast::Number(4.0)),
340 right: Box::new(Ast::Number(5.0)),
341 }),
342 }),
343 };
344 assert_eq!(
345 translate(vec![ast]).unwrap(),
346 vec![
347 JsStatement::ConstantDeclaration {
348 name: "$0".to_string(),
349 value: JsExpression::Number(1.0)
350 },
351 JsStatement::ConstantDeclaration {
352 name: "$1".to_string(),
353 value: JsExpression::Number(2.0)
354 },
355 JsStatement::ConstantDeclaration {
356 name: "$2".to_string(),
357 value: JsExpression::Number(3.0)
358 },
359 JsStatement::ConstantDeclaration {
360 name: "$3".to_string(),
361 value: JsExpression::Operation {
362 operator: Operator::Add,
363 left: Box::new(JsExpression::Identifier("$1".to_string())),
364 right: Box::new(JsExpression::Identifier("$2".to_string()))
365 }
366 },
367 JsStatement::ConstantDeclaration {
368 name: "$4".to_string(),
369 value: JsExpression::Operation {
370 operator: Operator::Add,
371 left: Box::new(JsExpression::Identifier("$0".to_string())),
372 right: Box::new(JsExpression::Identifier("$3".to_string()))
373 }
374 },
375 JsStatement::ConstantDeclaration {
376 name: "$5".to_string(),
377 value: JsExpression::Number(4.0)
378 },
379 JsStatement::ConstantDeclaration {
380 name: "$6".to_string(),
381 value: JsExpression::Number(5.0)
382 },
383 JsStatement::ConstantDeclaration {
384 name: "$7".to_string(),
385 value: JsExpression::Operation {
386 operator: Operator::Add,
387 left: Box::new(JsExpression::Identifier("$5".to_string())),
388 right: Box::new(JsExpression::Identifier("$6".to_string()))
389 }
390 },
391 JsStatement::ConstantDeclaration {
392 name: "$8".to_string(),
393 value: JsExpression::Operation {
394 operator: Operator::Add,
395 left: Box::new(JsExpression::Identifier("$4".to_string())),
396 right: Box::new(JsExpression::Identifier("$7".to_string()))
397 }
398 },
399 JsStatement::ConstantDeclaration {
400 name: "hi".to_string(),
401 value: JsExpression::Identifier("$8".to_string())
402 }
403 ]
404 )
405 }
406}
crates/js_ast/src/types.rs created+53
...@@ -0,0 +1,53 @@
1use num::bigint::BigInt;
2use std::collections::HashMap;
3
4use util::Operator;
5
6#[derive(Debug, PartialEq, Clone)]
7pub enum JsExpression {
8 Number(f64),
9 BigInt(BigInt),
10 String(String),
11 Bool(bool),
12 Identifier(String),
13 Array(Vec<JsExpression>),
14 Object(HashMap<String, JsExpression>),
15 Operation {
16 operator: Operator,
17 left: Box<JsExpression>,
18 right: Box<JsExpression>,
19 },
20 Function {
21 arguments: Vec<String>,
22 value: Vec<JsStatement>,
23 },
24 FunctionCall {
25 name: String,
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 VariableDeclaration {
36 name: String,
37 value: JsExpression,
38 },
39 ConstantDeclaration {
40 name: String,
41 value: JsExpression,
42 },
43 Function {
44 name: String,
45 arguments: Vec<String>,
46 value: Vec<JsStatement>,
47 },
48 If {
49 condition: JsExpression,
50 value: Vec<JsStatement>,
51 else_value: Option<Vec<JsStatement>>,
52 },
53}
crates/js_printer/Cargo.toml+3-1
...@@ -8,5 +8,7 @@ edition = "2021"...@@ -8,5 +8,7 @@ edition = "2021"
8[dependencies]8[dependencies]
9miette.workspace = true9miette.workspace = true
10thiserror.workspace = true10thiserror.workspace = true
11num.workspace = true
1112
12ast = { path = "../ast" }13js_ast = { path = "../js_ast" }
14util = { path = "../util" }
crates/js_printer/src/lib.rs+7-130
...@@ -1,139 +1,16 @@...@@ -1,139 +1,16 @@
1use miette::Result;1mod print_expression;
2mod print_statement;
23
3use ast::{Ast, Type};4use print_statement::print_statement;
45
5fn transpile_helper(tree: &Ast) -> Result<String> {6use js_ast::JsStatement;
6 Ok(match tree {
7 Ast::Number(number) => format!("{}", number),
8 Ast::Identifier(name) => format!("{}", name),
9 Ast::Bool(value) => format!("{}", value),
10 Ast::String(value) => format!("\"{}\"", value),
11 Ast::Comment(comment) => format!("// {}\n", comment),
12 Ast::DocComment(comment) => format!("/** {} */\n", comment),
13 Ast::Operation {
14 operator,
15 left,
16 right,
17 } => format!(
18 "{} {} {}",
19 transpile_helper(left)?,
20 operator.clone().to_string(),
21 transpile_helper(right)?
22 ),
23 Ast::Public { value } => format!("export {}", transpile_helper(value)?),
24 Ast::VariableDeclaration {
25 name,
26 return_type,
27 value,
28 } => match return_type {
29 Type::Bigint => format!("let {} = BigInt({});\n", name, transpile_helper(value)?),
30 _ => format!("let {} = {};\n", name, transpile_helper(value)?),
31 },
32 Ast::ConstantDeclaration {
33 name,
34 return_type,
35 value,
36 } => match return_type {
37 Type::Bigint => format!("const {} = BigInt({});\n", name, transpile_helper(value)?),
38 _ => format!("const {} = {};\n", name, transpile_helper(value)?),
39 },
40 Ast::Return { return_value } => format!("return {};\n", transpile_helper(return_value)?),
41 Ast::Function {
42 name,
43 arguments,
44 return_type: _,
45 block,
46 } => {
47 let mut result = format!("function {}(", name);
487
49 for (i, argument) in arguments.iter().enumerate() {8pub fn print(forest: Vec<JsStatement>) -> String {
50 if i == 0 {
51 result += argument.name.as_str();
52 } else {
53 result += format!(", {}", argument.name).as_str();
54 }
55 }
56
57 result += ") {\n";
58
59 for (i, statement) in block.value.iter().enumerate() {
60 if block.implicit_return && i == block.value.len() - 1 {
61 result += format!("return {};\n", transpile_helper(statement)?).as_str();
62 break;
63 }
64 result += transpile_helper(statement)?.as_str();
65 }
66
67 result += "}\n";
68
69 result
70 }
71 Ast::If {
72 condition,
73 value,
74 else_value,
75 } => {
76 let mut result = format!("if ({}) {{\n", transpile_helper(condition)?,);
77
78 for (i, statement) in value.value.iter().enumerate() {
79 if value.implicit_return && i == value.value.len() - 1 {
80 result += format!("return {};\n", transpile_helper(statement)?).as_str();
81 break;
82 }
83 result += transpile_helper(statement)?.as_str();
84 }
85
86 result += "}";
87
88 if let Some(val) = else_value {
89 result += format!(" else {{\n{}}}", transpile_helper(val)?).as_str();
90 } else {
91 result += "\n";
92 }
93
94 result
95 }
96 Ast::FunctionCall { name, arguments } => {
97 let mut result = match name.as_str() {
98 "print" => String::from("console.log("),
99 _ => format!("{}(", name),
100 };
101
102 for (i, argument) in arguments.iter().enumerate() {
103 if i == 0 {
104 result += transpile_helper(argument)?.as_str();
105 } else {
106 result += format!(", {}", transpile_helper(argument)?).as_str();
107 }
108 }
109
110 result += ")";
111
112 result
113 }
114 Ast::Block(block) => {
115 let mut result = String::from("(()=>{\n");
116 for (i, statement) in block.value.iter().enumerate() {
117 if block.implicit_return && i == block.value.len() - 1 {
118 result += format!("return {};\n", transpile_helper(statement)?).as_str();
119 break;
120 }
121 result += transpile_helper(statement)?.as_str();
122 }
123
124 result += "})()\n";
125
126 result
127 }
128 })
129}
130
131pub fn transpile(forest: Vec<Ast>) -> Result<String> {
132 let mut result = String::new();9 let mut result = String::new();
13310
134 for tree in forest {11 for tree in forest {
135 result.push_str(&transpile_helper(&tree)?);12 result.push_str(&print_statement(&tree));
136 }13 }
13714
138 Ok(result)15 result
139}16}
crates/js_printer/src/print_expression.rs created+261
...@@ -0,0 +1,261 @@
1use js_ast::JsExpression;
2
3use crate::print_statement::print_statement;
4
5pub fn print_expression(statement: &JsExpression) -> String {
6 match statement {
7 JsExpression::Number(value) => format!("{}", value),
8 JsExpression::BigInt(value) => format!("{}n", value),
9 JsExpression::String(value) => {
10 let mut result = String::new();
11 // TODO: This is an INCREDIBLY naive implementation of escaping.
12 // Also it's probably not even correct.
13 for char in value.chars() {
14 match char {
15 '\n' => result.push_str("\\n"),
16 '\r' => result.push_str("\\r"),
17 '\t' => result.push_str("\\t"),
18 '"' => result.push_str("\\\""),
19 '\\' => result.push_str("\\\\"),
20 _ => result.push(char),
21 }
22 }
23 format!("\"{}\"", result)
24 }
25 JsExpression::Bool(value) => format!("{}", value),
26 JsExpression::Identifier(value) => format!("{}", value),
27 JsExpression::Array(value) => format!(
28 "[{}]",
29 value
30 .iter()
31 .map(|x| print_expression(x))
32 .collect::<Vec<String>>()
33 .join(",")
34 ),
35 JsExpression::Object(value) => format!(
36 "{{{}}}",
37 value
38 .iter()
39 .map(|(k, v)| format!("\"{}\":{}", k, print_expression(v)))
40 .collect::<Vec<String>>()
41 .join(",")
42 ),
43 JsExpression::Operation {
44 operator,
45 left,
46 right,
47 } => format!(
48 "{}{}{}",
49 print_expression(left),
50 operator.clone().to_string(),
51 print_expression(right)
52 ),
53 JsExpression::Function { arguments, value } => format!(
54 "({})=>{{{}}}",
55 arguments.join(","),
56 value
57 .iter()
58 .map(|x| format!("{}", print_statement(x)))
59 .collect::<Vec<String>>()
60 .join("")
61 ),
62 JsExpression::FunctionCall { name, arguments } => format!(
63 "{}({})",
64 name,
65 arguments
66 .iter()
67 .map(|x| print_expression(x))
68 .collect::<Vec<String>>()
69 .join(",")
70 ),
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use crate::print_expression::print_expression;
77 use js_ast::JsExpression;
78 use js_ast::JsStatement;
79 use num::{bigint::Sign, BigInt};
80 use std::collections::HashMap;
81 use util::Operator;
82
83 #[test]
84 fn number() {
85 assert_eq!(print_expression(&JsExpression::Number(1.0)), "1");
86 assert_eq!(print_expression(&JsExpression::Number(1.5)), "1.5");
87 assert_eq!(
88 print_expression(&JsExpression::Number(1.5e10)),
89 "15000000000"
90 );
91 assert_eq!(
92 print_expression(&JsExpression::Number(1.5e-10)),
93 "0.00000000015"
94 );
95 }
96
97 #[test]
98 fn bigint() {
99 assert_eq!(
100 print_expression(&JsExpression::BigInt(BigInt::new(Sign::Plus, vec![1]))),
101 "1n"
102 );
103 assert_eq!(
104 print_expression(&JsExpression::BigInt(BigInt::new(
105 Sign::Plus,
106 vec![10000000]
107 ))),
108 "10000000n"
109 );
110 assert_eq!(
111 print_expression(&JsExpression::BigInt(BigInt::new(
112 Sign::Minus,
113 vec![10000000]
114 ))),
115 "-10000000n"
116 );
117 }
118
119 #[test]
120 fn string() {
121 assert_eq!(
122 print_expression(&JsExpression::String("".to_string())),
123 "\"\""
124 );
125 assert_eq!(
126 print_expression(&JsExpression::String("hello".to_string())),
127 "\"hello\""
128 );
129 assert_eq!(
130 print_expression(&JsExpression::String("hello\nworld".to_string())),
131 "\"hello\\nworld\""
132 );
133 }
134
135 #[test]
136 fn bool() {
137 assert_eq!(print_expression(&JsExpression::Bool(true)), "true");
138 assert_eq!(print_expression(&JsExpression::Bool(false)), "false");
139 }
140
141 #[test]
142 fn identifier() {
143 assert_eq!(
144 print_expression(&JsExpression::Identifier("hello".to_string())),
145 "hello"
146 );
147 }
148
149 #[test]
150 fn array() {
151 assert_eq!(print_expression(&JsExpression::Array(vec![])), "[]");
152 assert_eq!(
153 print_expression(&JsExpression::Array(vec![JsExpression::Number(1.0)])),
154 "[1]"
155 );
156 assert_eq!(
157 print_expression(&JsExpression::Array(vec![
158 JsExpression::Number(1.0),
159 JsExpression::Number(2.0)
160 ])),
161 "[1,2]"
162 );
163 }
164
165 #[test]
166 fn object() {
167 assert_eq!(
168 print_expression(&JsExpression::Object(HashMap::new())),
169 "{}"
170 );
171 let mut map = HashMap::new();
172 map.insert("hello".to_string(), JsExpression::Number(1.0));
173 assert_eq!(
174 print_expression(&JsExpression::Object(map)),
175 "{\"hello\":1}"
176 );
177 }
178
179 #[test]
180 fn operation() {
181 assert_eq!(
182 print_expression(&JsExpression::Operation {
183 operator: Operator::Add,
184 left: Box::new(JsExpression::Number(1.0)),
185 right: Box::new(JsExpression::Number(2.0)),
186 }),
187 "1+2"
188 );
189 assert_eq!(
190 print_expression(&JsExpression::Operation {
191 operator: Operator::Subtract,
192 left: Box::new(JsExpression::Number(1.0)),
193 right: Box::new(JsExpression::Number(2.0)),
194 }),
195 "1-2"
196 );
197 }
198
199 #[test]
200 fn function() {
201 use crate::print_expression::print_expression;
202 use js_ast::JsExpression;
203
204 assert_eq!(
205 print_expression(&JsExpression::Function {
206 arguments: vec![],
207 value: vec![],
208 }),
209 "()=>{}"
210 );
211 assert_eq!(
212 print_expression(&JsExpression::Function {
213 arguments: vec!["a".to_string()],
214 value: vec![],
215 }),
216 "(a)=>{}"
217 );
218 assert_eq!(
219 print_expression(&JsExpression::Function {
220 arguments: vec!["a".to_string(), "b".to_string()],
221 value: vec![],
222 }),
223 "(a,b)=>{}"
224 );
225 assert_eq!(
226 print_expression(&JsExpression::Function {
227 arguments: vec!["a".to_string(), "b".to_string()],
228 value: vec![JsStatement::VariableDeclaration {
229 name: "a".to_string(),
230 value: JsExpression::Number(1.0)
231 }],
232 }),
233 "(a,b)=>{let a=1;}"
234 );
235 }
236
237 #[test]
238 fn function_call() {
239 assert_eq!(
240 print_expression(&JsExpression::FunctionCall {
241 name: "hello".to_string(),
242 arguments: vec![],
243 }),
244 "hello()"
245 );
246 assert_eq!(
247 print_expression(&JsExpression::FunctionCall {
248 name: "hello".to_string(),
249 arguments: vec![JsExpression::Number(1.0)],
250 }),
251 "hello(1)"
252 );
253 assert_eq!(
254 print_expression(&JsExpression::FunctionCall {
255 name: "hello".to_string(),
256 arguments: vec![JsExpression::Number(1.0), JsExpression::Number(2.0)],
257 }),
258 "hello(1,2)"
259 );
260 }
261}
crates/js_printer/src/print_statement.rs created+145
...@@ -0,0 +1,145 @@
1use js_ast::JsStatement;
2
3use crate::print_expression::print_expression;
4
5pub 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::VariableDeclaration { name, value } => {
11 format!("let {}={};", name, print_expression(value))
12 }
13 JsStatement::ConstantDeclaration { name, value } => {
14 format!("const {}={};", name, print_expression(value))
15 }
16 JsStatement::Function {
17 name,
18 arguments,
19 value,
20 } => format!(
21 "function {}({}){{{}}}",
22 name,
23 arguments.join(","),
24 value
25 .iter()
26 .map(|x| format!("{}", print_statement(x)))
27 .collect::<Vec<String>>()
28 .join("")
29 ),
30 JsStatement::If {
31 condition,
32 value,
33 else_value,
34 } => {
35 let else_value = match else_value {
36 Some(value) => format!(
37 "else{{{}}}",
38 value
39 .iter()
40 .map(|x| format!("{}", print_statement(x)))
41 .collect::<Vec<String>>()
42 .join("")
43 ),
44 None => "".to_string(),
45 };
46 format!(
47 "if({}){{{}}}{}",
48 print_expression(condition),
49 value
50 .iter()
51 .map(|x| format!("{}", print_statement(x)))
52 .collect::<Vec<String>>()
53 .join(""),
54 else_value
55 )
56 }
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use crate::print_statement::print_statement;
63 use js_ast::JsExpression;
64 use js_ast::JsStatement;
65
66 #[test]
67 fn variable_declaration() {
68 assert_eq!(
69 print_statement(&JsStatement::VariableDeclaration {
70 name: "x".to_string(),
71 value: JsExpression::Number(1.0),
72 }),
73 "let x=1;"
74 );
75 }
76
77 #[test]
78 fn constant_declaration() {
79 assert_eq!(
80 print_statement(&JsStatement::ConstantDeclaration {
81 name: "x".to_string(),
82 value: JsExpression::Number(1.0),
83 }),
84 "const x=1;"
85 );
86 }
87
88 #[test]
89 fn function_declaration() {
90 assert_eq!(
91 print_statement(&JsStatement::Function {
92 name: "x".to_string(),
93 arguments: vec!["y".to_string()],
94 value: vec![JsStatement::Expression(JsExpression::Number(1.0))],
95 }),
96 "function x(y){1;}"
97 );
98 }
99
100 #[test]
101 fn expression() {
102 assert_eq!(
103 print_statement(&JsStatement::Expression(JsExpression::Number(1.0))),
104 "1;"
105 );
106 }
107
108 #[test]
109 fn export() {
110 assert_eq!(
111 print_statement(&JsStatement::Export(Box::new(JsStatement::Expression(
112 JsExpression::Number(1.0)
113 )))),
114 "export 1;"
115 );
116 }
117
118 #[test]
119 fn return_statement() {
120 assert_eq!(
121 print_statement(&JsStatement::Return(JsExpression::Number(1.0))),
122 "return 1;"
123 );
124 }
125
126 #[test]
127 fn if_statement() {
128 assert_eq!(
129 print_statement(&JsStatement::If {
130 condition: JsExpression::Number(1.0),
131 value: vec![JsStatement::Expression(JsExpression::Number(2.0))],
132 else_value: None,
133 }),
134 "if(1){2;}"
135 );
136 assert_eq!(
137 print_statement(&JsStatement::If {
138 condition: JsExpression::Number(1.0),
139 value: vec![JsStatement::Expression(JsExpression::Number(2.0))],
140 else_value: Some(vec![JsStatement::Expression(JsExpression::Number(3.0))]),
141 }),
142 "if(1){2;}else{3;}"
143 );
144 }
145}
crates/tokenizer/src/lib.rs+11-3
...@@ -14,7 +14,7 @@ struct UnknownCharacter {...@@ -14,7 +14,7 @@ struct UnknownCharacter {
14pub enum Token {14pub enum Token {
15 // Literals15 // Literals
16 Identifier(String),16 Identifier(String),
17 Number(String),17 Number(f64),
18 String(String),18 String(String),
19 // Comments19 // Comments
20 Comment(String),20 Comment(String),
...@@ -112,7 +112,15 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {...@@ -112,7 +112,15 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
112 _ => break,112 _ => break,
113 }113 }
114 }114 }
115 tokens.push(Token::Number(current_token));115 match current_token.parse::<f64>() {
116 Ok(v) => tokens.push(Token::Number(v)),
117 Err(_) => {
118 return Err(UnknownCharacter {
119 error: format!("Unknown character: {}", current_token),
120 }
121 .into())
122 }
123 }
116 }124 }
117 ':' => tokens.push(Token::Colon),125 ':' => tokens.push(Token::Colon),
118 '=' => match chars.peek() {126 '=' => match chars.peek() {
...@@ -130,7 +138,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {...@@ -130,7 +138,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
130 chars.next();138 chars.next();
131 tokens.push(Token::RightArrow);139 tokens.push(Token::RightArrow);
132 }140 }
133 _ => tokens.push(Token::Operator(Operator::Substract)),141 _ => tokens.push(Token::Operator(Operator::Subtract)),
134 },142 },
135 '*' => match chars.peek() {143 '*' => match chars.peek() {
136 Some((_, '*')) => {144 Some((_, '*')) => {
crates/util/src/lib.rs+3-3
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1#[derive(Debug, Clone)]1#[derive(Debug, Clone, PartialEq)]
2pub enum Operator {2pub enum Operator {
3 Add,3 Add,
4 Substract,4 Subtract,
5 Multiply,5 Multiply,
6 Divide,6 Divide,
7 Power,7 Power,
...@@ -13,7 +13,7 @@ impl Operator {...@@ -13,7 +13,7 @@ impl Operator {
13 pub fn to_string(self) -> String {13 pub fn to_string(self) -> String {
14 String::from(match self {14 String::from(match self {
15 Self::Add => "+",15 Self::Add => "+",
16 Self::Substract => "-",16 Self::Subtract => "-",
17 Self::Divide => "/",17 Self::Divide => "/",
18 Self::Multiply => "*",18 Self::Multiply => "*",
19 Self::Power => "**",19 Self::Power => "**",
examples/basics/fib.blight+5-5
...@@ -1,11 +1,11 @@...@@ -1,11 +1,11 @@
1pub fn fib(n: int) -> int {1pub fn fib(n: int) -> int {
2 if n == 0 {2 if n == 0 {
3 return 0;3 0
4 } else if n == 1 {
5 1
6 } else {
7 fib(n - 1) + fib(n - 2)
4 }8 }
5 if n == 1 {
6 return 1;
7 }
8 fib(n - 1) + fib(n - 2)
9}9}
1010
11print(fib(10));11print(fib(10));
examples/basics/shadowing.blight created+12
...@@ -0,0 +1,12 @@
1let x: int = 0;
2let x: int = 1;
3print(x); // 1
4
5let y: int = 0;
6{
7 let y: int = 1;
8 {
9 let y: int = 2;
10 }
11}
12print(y); // 0
examples/basics/weird.blight created+13
...@@ -0,0 +1,13 @@
1// Just trying to break the blight compiler
2fn bingus(y: bool) -> int {
3 let x: int = 5 + {
4 if y {
5 return 100000;
6 }
7 6
8 };
9 x
10}
11
12print(bingus(true));
13print(bingus(false));