authorgravatar for 11367844+lino-levan@users.noreply.github.comLino Le Van <11367844+lino-levan@users.noreply.github.com> 2024-01-10 08:31:10-08:00
committergravatar for 11367844+lino-levan@users.noreply.github.comLino Le Van <11367844+lino-levan@users.noreply.github.com> 2024-01-10 08:31:10-08:00
log266f616b552eb86530ed664e44aea026cb5820db
treeac537bd975a86f79730ca87c12b09cf957b062dc
parentbf7dedc48580545fcbcbb877d6f84087b96b4c62

feat: add function calling and print built-in


6 files changed, 219 insertions(+), 79 deletions(-)

cli/src/main.rs+52-3
...@@ -20,8 +20,16 @@ struct Cli {...@@ -20,8 +20,16 @@ struct Cli {
20enum Commands {20enum Commands {
21 /// Compiles a file into javascript21 /// Compiles a file into javascript
22 Compile {22 Compile {
23 /// lists test values
24 file: PathBuf,23 file: PathBuf,
24 // Whether to output to stdout instead of a file
25 #[arg(long, default_value_t = false)]
26 stdout: bool,
27 },
28 Run {
29 file: PathBuf,
30 /// What javascript runtime to use
31 #[arg(short, long, default_value = "deno")]
32 runtime: String,
25 },33 },
26}34}
2735
...@@ -29,10 +37,51 @@ fn main() -> Result<()> {...@@ -29,10 +37,51 @@ fn main() -> Result<()> {
29 let cli = Cli::parse();37 let cli = Cli::parse();
3038
31 match cli.command {39 match cli.command {
32 Commands::Compile { file } => {40 Commands::Compile { file, stdout } => {
41 let contents = std::fs::read_to_string(file.clone()).unwrap();
42 let result = transpile(contents.clone())?;
43
44 if stdout {
45 println!("{}", result);
46 } else {
47 std::fs::write(file.with_extension("js"), result).unwrap();
48 }
49 }
50 Commands::Run { file, runtime } => {
33 let contents = std::fs::read_to_string(file.clone()).unwrap();51 let contents = std::fs::read_to_string(file.clone()).unwrap();
34 let result = transpile(contents.clone())?;52 let result = transpile(contents.clone())?;
35 std::fs::write(file.with_extension("js"), result).unwrap();53 let mut temp_file = std::env::temp_dir();
54 temp_file.push("temp.js");
55 std::fs::write(temp_file.clone(), result).unwrap();
56 match runtime.as_str() {
57 "deno" => {
58 let output = std::process::Command::new("deno")
59 .arg("run")
60 .arg("-A")
61 .arg(temp_file)
62 .output()
63 .expect("Failed to execute process");
64 println!("{}", String::from_utf8_lossy(&output.stdout));
65 }
66 "node" => {
67 let output = std::process::Command::new("node")
68 .arg(temp_file)
69 .output()
70 .expect("Failed to execute process");
71 println!("{}", String::from_utf8_lossy(&output.stdout));
72 }
73 "bun" => {
74 let output = std::process::Command::new("bun")
75 .arg("run")
76 .arg(temp_file)
77 .output()
78 .expect("Failed to execute process");
79 println!("{}", String::from_utf8_lossy(&output.stdout));
80 }
81 _ => {
82 println!("Unknown runtime: {}", runtime);
83 }
84 }
36 }85 }
37 }86 }
3887
compiler/src/ast/mod.rs+121-45
...@@ -6,42 +6,50 @@ mod types;...@@ -6,42 +6,50 @@ mod types;
6use super::tokenizer::Token;6use super::tokenizer::Token;
77
8use error::AstError;8use error::AstError;
9use types::{ParserContext, Argument};9use types::{Argument, ParserContext};
1010
11pub use types::{Ast, Type};11pub use types::{Ast, Type};
1212
13fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {13fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {
14
15 let left = match tokens.next() {14 let left = match tokens.next() {
16 Some(token) => match token {15 Some(token) => match token {
17 Token::Number(number) => Ast::Number(number.clone()),16 Token::Number(number) => Ast::Number(number.clone()),
18 Token::Identifier(name) => {17 Token::Identifier(name) => match tokens.peek() {
19 match tokens.peek() {18 Some(Token::OpenParanthesis) => {
20 Some(Token::OpenParanthesis) => {19 tokens.next();
21 tokens.next();
2220
23 let mut arguments = Vec::new();21 let mut arguments = Vec::new();
22
23 loop {
24 match tokens.peek() {
25 Some(Token::ClosedParanthesis) => {
26 tokens.next();
27 break;
28 }
29 _ => (),
30 }
31 arguments.push(parse_expression(tokens)?);
2432
25 loop {33 match tokens.peek() {
26 match tokens.peek() {34 Some(Token::Comma) => {
27 Some(Token::ClosedParanthesis) => break,35 tokens.next();
28 _ => ()
29 }36 }
30 arguments.push(parse_expression(tokens)?);37 Some(Token::ClosedParanthesis) => (),
3138 _ => {
32 match tokens.peek() {39 return Err(AstError::new(
33 Some(Token::Comma) => {40 "Expected ) or , but found something else instead.",
34 tokens.next();41 )
35 }42 .into())
36 Some(Token::ClosedParanthesis) => (),
37 _ => return Err(AstError::new("Expected ) or , but found something else instead.").into()),
38 }43 }
39 }44 }
45 }
4046
41 Ast::FunctionCall { name: name.clone(), arguments }47 Ast::FunctionCall {
48 name: name.clone(),
49 arguments,
42 }50 }
43 _ => Ast::Identifier(name.clone())
44 }51 }
52 _ => Ast::Identifier(name.clone()),
45 },53 },
46 _ => return Err(AstError::new("Expected expression, but did not get it").into()),54 _ => return Err(AstError::new("Expected expression, but did not get it").into()),
47 },55 },
...@@ -90,7 +98,9 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -90,7 +98,9 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
9098
91 let inner = parse_statement(tokens)?;99 let inner = parse_statement(tokens)?;
92100
93 return Ok(Ast::Public { value: Box::new(inner) })101 return Ok(Ast::Public {
102 value: Box::new(inner),
103 });
94 }104 }
95 Token::Let => {105 Token::Let => {
96 let name = match tokens.next() {106 let name = match tokens.next() {
...@@ -151,14 +161,12 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -151,14 +161,12 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
151161
152 match tokens.next() {162 match tokens.next() {
153 Some(Token::Semicolon) => (),163 Some(Token::Semicolon) => (),
154 _ => {164 _ => return Err(AstError::new("Expected semicolon after return statement").into()),
155 return Err(
156 AstError::new("Expected semicolon after return statement").into(),
157 )
158 }
159 };165 };
160166
161 Ok(Ast::Return { return_value: Box::new(expression) })167 Ok(Ast::Return {
168 return_value: Box::new(expression),
169 })
162 }170 }
163 Token::Function => {171 Token::Function => {
164 let name = match tokens.next() {172 let name = match tokens.next() {
...@@ -168,7 +176,12 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -168,7 +176,12 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
168176
169 match tokens.next() {177 match tokens.next() {
170 Some(Token::OpenParanthesis) => (),178 Some(Token::OpenParanthesis) => (),
171 _ => return Err(AstError::new("Expected open paranthesis after identifier in function").into()),179 _ => {
180 return Err(AstError::new(
181 "Expected open paranthesis after identifier in function",
182 )
183 .into())
184 }
172 };185 };
173186
174 let mut arguments = Vec::new();187 let mut arguments = Vec::new();
...@@ -178,13 +191,18 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -178,13 +191,18 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
178 Some(Token::ClosedParanthesis) => {191 Some(Token::ClosedParanthesis) => {
179 tokens.next();192 tokens.next();
180 break;193 break;
181 },194 }
182 _ => ()195 _ => (),
183 }196 }
184197
185 let argument_name = match tokens.next() {198 let argument_name = match tokens.next() {
186 Some(Token::Identifier(name)) => name,199 Some(Token::Identifier(name)) => name,
187 _ => return Err(AstError::new("Expected identifier for argument name in function").into()),200 _ => {
201 return Err(AstError::new(
202 "Expected identifier for argument name in function",
203 )
204 .into())
205 }
188 };206 };
189207
190 let return_type = match tokens.next() {208 let return_type = match tokens.next() {
...@@ -195,21 +213,34 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -195,21 +213,34 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
195 match tokens.peek() {213 match tokens.peek() {
196 Some(Token::Comma) => {214 Some(Token::Comma) => {
197 tokens.next();215 tokens.next();
198 },216 }
199 _ => ()217 _ => (),
200 }218 }
201219
202 arguments.push(Argument { name: argument_name.clone(), return_type})220 arguments.push(Argument {
221 name: argument_name.clone(),
222 return_type,
223 })
203 }224 }
204225
205 let return_type = match tokens.next() {226 let return_type = match tokens.next() {
206 Some(Token::RightArrow) => parse_type(tokens)?,227 Some(Token::RightArrow) => parse_type(tokens)?,
207 _ => return Err(AstError::new("Expected right arrow after argument list in function").into()),228 _ => {
229 return Err(AstError::new(
230 "Expected right arrow after argument list in function",
231 )
232 .into())
233 }
208 };234 };
209235
210 match tokens.next() {236 match tokens.next() {
211 Some(Token::OpenBracket) => (),237 Some(Token::OpenBracket) => (),
212 _ => return Err(AstError::new("Expected open bracket after type in function declaration").into()),238 _ => {
239 return Err(AstError::new(
240 "Expected open bracket after type in function declaration",
241 )
242 .into())
243 }
213 };244 };
214245
215 let mut value = Vec::new();246 let mut value = Vec::new();
...@@ -219,15 +250,62 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -219,15 +250,62 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
219 Some(Token::ClosedBracket) => {250 Some(Token::ClosedBracket) => {
220 tokens.next();251 tokens.next();
221 break;252 break;
222 },253 }
223 _ => ()254 _ => (),
224 }255 }
225256
226 value.push(parse_statement(tokens)?);257 value.push(parse_statement(tokens)?);
227 }258 }
228259
229 return Ok(Ast::Function { name, arguments, return_type, value })260 return Ok(Ast::Function {
261 name,
262 arguments,
263 return_type,
264 value,
265 });
230 }266 }
267 Token::Identifier(name) => match tokens.peek() {
268 Some(Token::OpenParanthesis) => {
269 tokens.next();
270
271 let mut arguments = Vec::new();
272
273 loop {
274 match tokens.peek() {
275 Some(Token::ClosedParanthesis) => {
276 tokens.next();
277 break;
278 }
279 _ => (),
280 }
281 arguments.push(parse_expression(tokens)?);
282
283 match tokens.peek() {
284 Some(Token::Comma) => {
285 tokens.next();
286 }
287 Some(Token::ClosedParanthesis) => (),
288 _ => {
289 return Err(AstError::new(
290 "Expected ) or , but found something else instead.",
291 )
292 .into())
293 }
294 }
295 }
296
297 match tokens.next() {
298 Some(Token::Semicolon) => (),
299 _ => return Err(AstError::new("Expected semicolon after function call").into()),
300 };
301
302 Ok(Ast::FunctionCall {
303 name: name.clone(),
304 arguments,
305 })
306 }
307 _ => Err(AstError::new("Unexpected identifier").into()),
308 },
231 Token::Comment(comment) => Ok(Ast::Comment(comment.clone())),309 Token::Comment(comment) => Ok(Ast::Comment(comment.clone())),
232 Token::DocComment(comment) => {310 Token::DocComment(comment) => {
233 let mut concatted_comments = comment.clone();311 let mut concatted_comments = comment.clone();
...@@ -236,15 +314,13 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -236,15 +314,13 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
236 Some(Token::DocComment(comment)) => {314 Some(Token::DocComment(comment)) => {
237 concatted_comments.push_str(format!("\n{}", comment).as_str());315 concatted_comments.push_str(format!("\n{}", comment).as_str());
238 tokens.next();316 tokens.next();
239 },317 }
240 _ => break,318 _ => break,
241 }319 }
242 }320 }
243 Ok(Ast::DocComment(concatted_comments))321 Ok(Ast::DocComment(concatted_comments))
244 },322 }
245 _ => {323 _ => return Err(AstError::new("Unexpected token").into()),
246 return Err(AstError::new("Unexpected token").into())
247 },
248 }324 }
249}325}
250326
compiler/src/ast/types.rs+7-8
...@@ -1,10 +1,10 @@...@@ -1,10 +1,10 @@
1use crate::util::Operator;
2use crate::tokenizer::Token;1use crate::tokenizer::Token;
2use crate::util::Operator;
33
4#[derive(Debug)]4#[derive(Debug)]
5pub struct ParserContext {5pub struct ParserContext {
6 pub tokens: Vec<Token>,6 pub tokens: Vec<Token>,
7 pub cursor: usize7 pub cursor: usize,
8}8}
99
10impl ParserContext {10impl ParserContext {
...@@ -35,7 +35,6 @@ impl ParserContext {...@@ -35,7 +35,6 @@ impl ParserContext {
35 }35 }
36}36}
3737
38
39#[derive(Debug)]38#[derive(Debug)]
40pub enum Type {39pub enum Type {
41 Int,40 Int,
...@@ -46,13 +45,13 @@ pub enum Type {...@@ -46,13 +45,13 @@ pub enum Type {
46#[derive(Debug)]45#[derive(Debug)]
47pub struct Argument {46pub struct Argument {
48 pub name: String,47 pub name: String,
49 pub return_type: Type48 pub return_type: Type,
50}49}
5150
52#[derive(Debug)]51#[derive(Debug)]
53pub enum Ast {52pub enum Ast {
54 Public {53 Public {
55 value: Box<Ast>54 value: Box<Ast>,
56 },55 },
57 ConstantDeclaration {56 ConstantDeclaration {
58 name: String,57 name: String,
...@@ -70,13 +69,13 @@ pub enum Ast {...@@ -70,13 +69,13 @@ pub enum Ast {
70 right: Box<Ast>,69 right: Box<Ast>,
71 },70 },
72 Return {71 Return {
73 return_value: Box<Ast>72 return_value: Box<Ast>,
74 },73 },
75 Function {74 Function {
76 name: String,75 name: String,
77 arguments: Vec<Argument>,76 arguments: Vec<Argument>,
78 return_type: Type,77 return_type: Type,
79 value: Vec<Ast>78 value: Vec<Ast>,
80 },79 },
81 FunctionCall {80 FunctionCall {
82 name: String,81 name: String,
...@@ -85,5 +84,5 @@ pub enum Ast {...@@ -85,5 +84,5 @@ pub enum Ast {
85 Number(String),84 Number(String),
86 Identifier(String),85 Identifier(String),
87 Comment(String),86 Comment(String),
88 DocComment(String)87 DocComment(String),
89}88}
compiler/src/tokenizer.rs+14-18
...@@ -94,23 +94,19 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {...@@ -94,23 +94,19 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
94 '=' => tokens.push(Token::Equals),94 '=' => tokens.push(Token::Equals),
95 ';' => tokens.push(Token::Semicolon),95 ';' => tokens.push(Token::Semicolon),
96 '+' => tokens.push(Token::Operator(Operator::Add)),96 '+' => tokens.push(Token::Operator(Operator::Add)),
97 '-' => {97 '-' => match chars.peek() {
98 match chars.peek() {98 Some((_, '>')) => {
99 Some((_, '>')) => {99 chars.next();
100 chars.next();100 tokens.push(Token::RightArrow);
101 tokens.push(Token::RightArrow);
102 }
103 _ => tokens.push(Token::Operator(Operator::Substract))
104 }101 }
102 _ => tokens.push(Token::Operator(Operator::Substract)),
105 },103 },
106 '*' => {104 '*' => match chars.peek() {
107 match chars.peek() {105 Some((_, '*')) => {
108 Some((_, '*')) => {106 chars.next();
109 chars.next();107 tokens.push(Token::Operator(Operator::Power));
110 tokens.push(Token::Operator(Operator::Power));
111 }
112 _ => tokens.push(Token::Operator(Operator::Multiply))
113 }108 }
109 _ => tokens.push(Token::Operator(Operator::Multiply)),
114 },110 },
115 '/' => {111 '/' => {
116 match chars.peek() {112 match chars.peek() {
...@@ -121,7 +117,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {...@@ -121,7 +117,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
121 Some((_, '/')) => {117 Some((_, '/')) => {
122 chars.next(); // Consume the third slash118 chars.next(); // Consume the third slash
123 true119 true
124 },120 }
125 _ => false,121 _ => false,
126 };122 };
127123
...@@ -136,7 +132,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {...@@ -136,7 +132,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
136 '\n' => break,132 '\n' => break,
137 _ => {133 _ => {
138 comment.push(c.clone());134 comment.push(c.clone());
139 },135 }
140 }136 }
141 }137 }
142138
...@@ -148,9 +144,9 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {...@@ -148,9 +144,9 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
148 tokens.push(Token::Comment(comment));144 tokens.push(Token::Comment(comment));
149 }145 }
150 }146 }
151 _ => tokens.push(Token::Operator(Operator::Divide))147 _ => tokens.push(Token::Operator(Operator::Divide)),
152 }148 }
153 },149 }
154 ',' => tokens.push(Token::Comma),150 ',' => tokens.push(Token::Comma),
155 '(' => tokens.push(Token::OpenParanthesis),151 '(' => tokens.push(Token::OpenParanthesis),
156 ')' => tokens.push(Token::ClosedParanthesis),152 ')' => tokens.push(Token::ClosedParanthesis),
compiler/src/walker.rs+23-5
...@@ -19,16 +19,29 @@ fn transpile_helper(tree: &Ast) -> Result<String> {...@@ -19,16 +19,29 @@ fn transpile_helper(tree: &Ast) -> Result<String> {
19 transpile_helper(right)?19 transpile_helper(right)?
20 ),20 ),
21 Ast::Public { value } => format!("export {}", transpile_helper(value)?),21 Ast::Public { value } => format!("export {}", transpile_helper(value)?),
22 Ast::VariableDeclaration { name, return_type, value } => match return_type {22 Ast::VariableDeclaration {
23 name,
24 return_type,
25 value,
26 } => match return_type {
23 Type::Bigint => format!("let {} = BigInt({});\n", name, transpile_helper(value)?),27 Type::Bigint => format!("let {} = BigInt({});\n", name, transpile_helper(value)?),
24 _ => format!("let {} = {};\n", name, transpile_helper(value)?),28 _ => format!("let {} = {};\n", name, transpile_helper(value)?),
25 },29 },
26 Ast::ConstantDeclaration { name, return_type, value } => match return_type {30 Ast::ConstantDeclaration {
31 name,
32 return_type,
33 value,
34 } => match return_type {
27 Type::Bigint => format!("const {} = BigInt({});\n", name, transpile_helper(value)?),35 Type::Bigint => format!("const {} = BigInt({});\n", name, transpile_helper(value)?),
28 _ => format!("const {} = {};\n", name, transpile_helper(value)?),36 _ => format!("const {} = {};\n", name, transpile_helper(value)?),
29 },37 },
30 Ast::Return { return_value } => format!("return {};\n", transpile_helper(return_value)?),38 Ast::Return { return_value } => format!("return {};\n", transpile_helper(return_value)?),
31 Ast::Function { name, arguments, return_type: _, value } => {39 Ast::Function {
40 name,
41 arguments,
42 return_type: _,
43 value,
44 } => {
32 let mut result = format!("function {}(", name);45 let mut result = format!("function {}(", name);
3346
34 for (i, argument) in arguments.iter().enumerate() {47 for (i, argument) in arguments.iter().enumerate() {
...@@ -48,9 +61,12 @@ fn transpile_helper(tree: &Ast) -> Result<String> {...@@ -48,9 +61,12 @@ fn transpile_helper(tree: &Ast) -> Result<String> {
48 result += "}\n";61 result += "}\n";
4962
50 result63 result
51 },64 }
52 Ast::FunctionCall { name, arguments } => {65 Ast::FunctionCall { name, arguments } => {
53 let mut result = format!("{}(", name);66 let mut result = match name.as_str() {
67 "print" => String::from("console.log("),
68 _ => format!("{}(", name),
69 };
5470
55 for (i, argument) in arguments.iter().enumerate() {71 for (i, argument) in arguments.iter().enumerate() {
56 if i == 0 {72 if i == 0 {
...@@ -60,6 +76,8 @@ fn transpile_helper(tree: &Ast) -> Result<String> {...@@ -60,6 +76,8 @@ fn transpile_helper(tree: &Ast) -> Result<String> {
60 }76 }
61 }77 }
6278
79 result += ")";
80
63 result81 result
64 }82 }
65 })83 })
examples/basics/functions.blight+2
...@@ -3,3 +3,5 @@ fn bingus(x: int, y: int) -> int {...@@ -3,3 +3,5 @@ fn bingus(x: int, y: int) -> int {
3}3}
44
5let result: int = bingus(10, 25);5let result: int = bingus(10, 25);
6
7print(result); // built-in function