authorgravatar for 11367844+lino-levan@users.noreply.github.comLino Le Van <11367844+lino-levan@users.noreply.github.com> 2024-01-18 19:07:38-08:00
committergravatar for 11367844+lino-levan@users.noreply.github.comLino Le Van <11367844+lino-levan@users.noreply.github.com> 2024-01-18 19:07:38-08:00
log2900f23394e35089aca94dbd1de195c7666365df
tree63fc7d53b2754c3f33a86f8813c086511d9bfa2a
parent8133856c0042befc85ff2725f34dfb71d50b1095

feat: add raw


21 files changed, 192 insertions(+), 189 deletions(-)

.github/logo.png created
Binary files /dev/null and b/.github/logo.png differ
README.md+2
......@@ -1,3 +1,5 @@
1![Blight Logo](/.github/logo.png)
2
13# Blight
24
35Blight sure is a programming language.
crates/ast/src/lib.rs+1
......@@ -3,6 +3,7 @@ use miette::Result;
33mod error;
44mod parse_block;
55mod parse_expression;
6mod parse_function_call;
67mod parse_statement;
78mod parse_type;
89mod types;
crates/ast/src/parse_expression.rs+19-35
......@@ -4,6 +4,7 @@ use tokenizer::Token;
44
55use super::error::AstError;
66use super::parse_block::parse_block;
7use super::parse_function_call::parse_function_call;
78use super::types::{Ast, Block, ParserContext};
89
910pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {
......@@ -11,6 +12,16 @@ pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {
1112 Some(token) => match token {
1213 Token::Number(number) => Ast::Number(number),
1314 Token::String(string) => Ast::String(string.clone()),
15 Token::BuiltIn(name) => {
16 match tokens.peek() {
17 Some(Token::OpenParanthesis) => {
18 // FRICK we consumed the open paranthesis
19 tokens.back();
20 parse_function_call(tokens)?
21 }
22 _ => Ast::BuiltIn(name.clone()),
23 }
24 }
1425 Token::True => Ast::Bool(true),
1526 Token::False => Ast::Bool(false),
1627 Token::OpenBracket => {
......@@ -57,43 +68,16 @@ pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {
5768 else_value,
5869 });
5970 }
60 Token::Identifier(name) => match tokens.peek() {
61 Some(Token::OpenParanthesis) => {
62 tokens.next();
63
64 let mut arguments = Vec::new();
65
66 loop {
67 match tokens.peek() {
68 Some(Token::ClosedParanthesis) => {
69 tokens.next();
70 break;
71 }
72 _ => (),
73 }
74 arguments.push(parse_expression(tokens)?);
75
76 match tokens.peek() {
77 Some(Token::Comma) => {
78 tokens.next();
79 }
80 Some(Token::ClosedParanthesis) => (),
81 _ => {
82 return Err(AstError::new(
83 "Expected ) or , but found something else instead.",
84 )
85 .into())
86 }
87 }
88 }
89
90 Ast::FunctionCall {
91 name: name.clone(),
92 arguments,
71 Token::Identifier(name) => {
72 match tokens.peek() {
73 Some(Token::OpenParanthesis) => {
74 // FRICK we consumed the open paranthesis
75 tokens.back();
76 parse_function_call(tokens)?
9377 }
78 _ => Ast::Identifier(name.clone()),
9479 }
95 _ => Ast::Identifier(name.clone()),
96 },
80 }
9781 _ => {
9882 println!("token: {:?}", token);
9983 return Err(AstError::new("Expected expression, but did not get it").into());
crates/ast/src/parse_function_call.rs created+56
......@@ -0,0 +1,56 @@
1use miette::Result;
2
3use tokenizer::Token;
4
5use super::error::AstError;
6use super::parse_expression::parse_expression;
7use super::types::{Ast, ParserContext};
8
9/// Parses a function call AS AN EXPRESSION
10/// This does not consume the semicolon at the end of the function call
11/// if it is a statement.
12pub fn parse_function_call(tokens: &mut ParserContext) -> Result<Ast> {
13 let identifier = match tokens.next() {
14 Some(Token::Identifier(identifier)) => Ast::Identifier(identifier),
15 Some(Token::BuiltIn(identifier)) => Ast::BuiltIn(identifier),
16 _ => return Err(AstError::new("Expected identifier").into()),
17 };
18
19 match tokens.peek() {
20 Some(Token::OpenParanthesis) => {
21 tokens.next();
22
23 let mut arguments = Vec::new();
24
25 loop {
26 match tokens.peek() {
27 Some(Token::ClosedParanthesis) => {
28 tokens.next();
29 break;
30 }
31 _ => (),
32 }
33 arguments.push(parse_expression(tokens)?);
34
35 match tokens.peek() {
36 Some(Token::Comma) => {
37 tokens.next();
38 }
39 Some(Token::ClosedParanthesis) => (),
40 _ => {
41 return Err(AstError::new(
42 "Expected ) or , but found something else instead.",
43 )
44 .into())
45 }
46 }
47 }
48
49 Ok(Ast::FunctionCall {
50 name: Box::new(identifier),
51 arguments,
52 })
53 }
54 _ => Err(AstError::new("Unexpected identifier").into()),
55 }
56}
crates/ast/src/parse_statement.rs+23-36
......@@ -5,6 +5,7 @@ use tokenizer::Token;
55use super::error::AstError;
66use super::parse_block::parse_block;
77use super::parse_expression::parse_expression;
8use super::parse_function_call::parse_function_call;
89use super::parse_type::parse_type;
910use super::types::{Argument, Ast, Block, ParserContext};
1011
......@@ -12,6 +13,16 @@ use super::types::{Argument, Ast, Block, ParserContext};
1213pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
1314 let token = tokens.next().unwrap();
1415 match token {
16 Token::BuiltIn(name) => {
17 match tokens.peek() {
18 Some(Token::OpenParanthesis) => {
19 // Okay, so we messed up. We shouldn't have consumed that token.
20 tokens.back();
21 parse_function_call(tokens)
22 }
23 _ => Ok(Ast::BuiltIn(name)),
24 }
25 }
1526 Token::OpenBracket => {
1627 // FRICK, we have to backtrack
1728 tokens.back();
......@@ -44,7 +55,7 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
4455 Some(Token::Semicolon) => (),
4556 _ => {
4657 return Err(
47 AstError::new("Expected semicolon after variable declaration").into(),
58 AstError::new("Expected semicolon after constant declaration").into(),
4859 )
4960 }
5061 };
......@@ -213,45 +224,21 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
213224 else_value,
214225 });
215226 }
216 Token::Identifier(name) => match tokens.peek() {
227 Token::Identifier(_) => match tokens.peek() {
217228 Some(Token::OpenParanthesis) => {
218 tokens.next();
219
220 let mut arguments = Vec::new();
221
222 loop {
223 match tokens.peek() {
224 Some(Token::ClosedParanthesis) => {
225 tokens.next();
226 break;
227 }
228 _ => (),
229 }
230 arguments.push(parse_expression(tokens)?);
231
232 match tokens.peek() {
233 Some(Token::Comma) => {
234 tokens.next();
235 }
236 Some(Token::ClosedParanthesis) => (),
237 _ => {
238 return Err(AstError::new(
239 "Expected ) or , but found something else instead.",
240 )
241 .into())
242 }
243 }
244 }
245
229 // We need to give the identifier back to the parser
230 tokens.back();
231 let result = parse_function_call(tokens);
246232 match tokens.next() {
247233 Some(Token::Semicolon) => (),
248 _ => return Err(AstError::new("Expected semicolon after function call").into()),
234 _ => {
235 return Err(AstError::new(
236 "Expected semicolon after function call statement",
237 )
238 .into())
239 }
249240 };
250
251 Ok(Ast::FunctionCall {
252 name: name.clone(),
253 arguments,
254 })
241 result
255242 }
256243 _ => Err(AstError::new("Unexpected identifier").into()),
257244 },
crates/ast/src/parse_type.rs+1
......@@ -13,6 +13,7 @@ pub fn parse_type(tokens: &mut ParserContext) -> Result<Type> {
1313 "bigint" => Ok(Type::Bigint),
1414 "bool" => Ok(Type::Bool),
1515 "string" => Ok(Type::String),
16 "any" => Ok(Type::Any),
1617 _ => return Err(AstError::new("Expected type").into()),
1718 },
1819 _ => return Err(AstError::new("Expected type").into()),
crates/ast/src/types.rs+8-5
......@@ -41,28 +41,29 @@ impl ParserContext {
4141 }
4242}
4343
44#[derive(Debug)]
44#[derive(Debug, Clone)]
4545pub enum Type {
4646 Int,
4747 Float,
4848 Bigint,
4949 Bool,
5050 String,
51 Any,
5152}
5253
53#[derive(Debug)]
54#[derive(Debug, Clone)]
5455pub struct Block {
5556 pub value: Vec<Ast>,
5657 pub implicit_return: bool,
5758}
5859
59#[derive(Debug)]
60#[derive(Debug, Clone)]
6061pub struct Argument {
6162 pub name: String,
6263 pub return_type: Type,
6364}
6465
65#[derive(Debug)]
66#[derive(Debug, Clone)]
6667pub enum Ast {
6768 ConstantDeclaration {
6869 name: String,
......@@ -86,7 +87,8 @@ pub enum Ast {
8687 block: Block,
8788 },
8889 FunctionCall {
89 name: String,
90 /// Identifier or BuiltIn
91 name: Box<Ast>,
9092 arguments: Vec<Ast>,
9193 },
9294 If {
......@@ -101,6 +103,7 @@ pub enum Ast {
101103 Bool(bool),
102104 String(String),
103105 Identifier(String),
106 BuiltIn(String),
104107 Comment(String),
105108 DocComment(String),
106109}
crates/js_ast/src/lib.rs+46-104
......@@ -109,10 +109,27 @@ fn get_expression_handle(ast: &Ast, ctx: &mut TranslateContext) -> JsExpression
109109 for argument in arguments {
110110 handles.push(get_expression_handle(argument, ctx));
111111 }
112 ctx.push_internal_variable(JsExpression::FunctionCall {
113 name: ctx.get_identifier(name.clone()),
114 arguments: handles,
115 })
112 match *name.clone() {
113 Ast::Identifier(name) => ctx.push_internal_variable(JsExpression::FunctionCall {
114 name: Box::new(JsExpression::Identifier(ctx.get_identifier(name.clone()))),
115 arguments: handles,
116 }),
117 Ast::BuiltIn(name) => match name.as_str() {
118 "@raw" => {
119 if handles.len() != 1 {
120 panic!("@raw expects exactly one argument");
121 }
122 match handles.get(0).unwrap() {
123 JsExpression::String(value) => {
124 return JsExpression::Raw(value.clone());
125 }
126 _ => panic!("@raw expects a string argument"),
127 }
128 }
129 _ => panic!("Unknown built-in function {}", name),
130 },
131 _ => panic!("Function name must be an identifier"),
132 }
116133 }
117134 Ast::Operation {
118135 operator,
......@@ -286,13 +303,32 @@ fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) {
286303 for argument in arguments {
287304 handles.push(get_expression_handle(argument, ctx));
288305 }
289 ctx.push(JsStatement::Expression(JsExpression::FunctionCall {
290 name: match name.as_str() {
291 "print" => "console.log".to_string(),
292 _ => name.clone(),
306 match *name.clone() {
307 Ast::Identifier(name) => {
308 ctx.push(JsStatement::Expression(JsExpression::FunctionCall {
309 name: Box::new(JsExpression::Identifier(ctx.get_identifier(name.clone()))),
310 arguments: handles,
311 }));
312 }
313 Ast::BuiltIn(name) => match name.as_str() {
314 "@raw" => {
315 if handles.len() != 1 {
316 panic!("@raw expects exactly one argument");
317 }
318 match handles.get(0).unwrap() {
319 JsExpression::String(value) => {
320 ctx.push(JsStatement::Expression(JsExpression::FunctionCall {
321 name: Box::new(JsExpression::Raw(value.clone())),
322 arguments: handles,
323 }));
324 }
325 _ => panic!("@raw expects a string argument"),
326 }
327 }
328 _ => panic!("Unknown built-in function {}", name),
293329 },
294 arguments: handles,
295 }));
330 _ => panic!("Function name must be an identifier"),
331 }
296332 }
297333 Ast::Comment(_) | Ast::DocComment(_) => {}
298334 _ => {
......@@ -310,97 +346,3 @@ pub fn translate(_ast: Vec<Ast>) -> Result<Vec<JsStatement>> {
310346
311347 Ok(ctx.pop_context())
312348}
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+2-1
......@@ -10,6 +10,7 @@ pub enum JsExpression {
1010 String(String),
1111 Bool(bool),
1212 Identifier(String),
13 Raw(String),
1314 Array(Vec<JsExpression>),
1415 Object(HashMap<String, JsExpression>),
1516 Operation {
......@@ -22,7 +23,7 @@ pub enum JsExpression {
2223 value: Vec<JsStatement>,
2324 },
2425 FunctionCall {
25 name: String,
26 name: Box<JsExpression>,
2627 arguments: Vec<JsExpression>,
2728 },
2829}
crates/js_printer/src/print_expression.rs+9-4
......@@ -4,6 +4,7 @@ use crate::print_statement::print_statement;
44
55pub fn print_expression(statement: &JsExpression) -> String {
66 match statement {
7 JsExpression::Raw(value) => format!("{}", value),
78 JsExpression::Number(value) => format!("{}", value),
89 JsExpression::BigInt(value) => format!("{}n", value),
910 JsExpression::String(value) => {
......@@ -61,7 +62,11 @@ pub fn print_expression(statement: &JsExpression) -> String {
6162 ),
6263 JsExpression::FunctionCall { name, arguments } => format!(
6364 "{}({})",
64 name,
65 match *name.clone() {
66 JsExpression::Identifier(value) => value.clone(),
67 JsExpression::Raw(value) => value.clone(),
68 _ => panic!("Function call name must be an identifier"),
69 },
6570 arguments
6671 .iter()
6772 .map(|x| print_expression(x))
......@@ -238,21 +243,21 @@ mod tests {
238243 fn function_call() {
239244 assert_eq!(
240245 print_expression(&JsExpression::FunctionCall {
241 name: "hello".to_string(),
246 name: Box::new(JsExpression::Identifier("hello".to_string())),
242247 arguments: vec![],
243248 }),
244249 "hello()"
245250 );
246251 assert_eq!(
247252 print_expression(&JsExpression::FunctionCall {
248 name: "hello".to_string(),
253 name: Box::new(JsExpression::Identifier("hello".to_string())),
249254 arguments: vec![JsExpression::Number(1.0)],
250255 }),
251256 "hello(1)"
252257 );
253258 assert_eq!(
254259 print_expression(&JsExpression::FunctionCall {
255 name: "hello".to_string(),
260 name: Box::new(JsExpression::Identifier("hello".to_string())),
256261 arguments: vec![JsExpression::Number(1.0), JsExpression::Number(2.0)],
257262 }),
258263 "hello(1,2)"
crates/tokenizer/src/lib.rs+9-2
......@@ -16,6 +16,7 @@ pub enum Token {
1616 Identifier(String),
1717 Number(f64),
1818 String(String),
19 BuiltIn(String),
1920 // Comments
2021 Comment(String),
2122 DocComment(String),
......@@ -49,7 +50,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
4950 loop {
5051 match chars.next() {
5152 Some((_, c)) => match c {
52 'a'..='z' | 'A'..='Z' | '_' => {
53 'a'..='z' | 'A'..='Z' | '_' | '@' => {
5354 let mut current_token = String::new();
5455 current_token.push(c);
5556 loop {
......@@ -77,7 +78,13 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
7778 "return" => tokens.push(Token::Return),
7879 "true" => tokens.push(Token::True),
7980 "false" => tokens.push(Token::False),
80 _ => tokens.push(Token::Identifier(current_token)),
81 _ => {
82 if current_token.starts_with('@') {
83 tokens.push(Token::BuiltIn(current_token));
84 } else {
85 tokens.push(Token::Identifier(current_token))
86 }
87 }
8188 }
8289 }
8390 '"' => {
examples/basics/control.blight+2
......@@ -1,3 +1,5 @@
1let print: any = @raw("console.log");
2
13let x: bool = true;
24
35if x {
examples/basics/expressions.blight+1
......@@ -7,4 +7,5 @@ let x: int = 5 + {
77 y
88};
99
10let print: any = @raw("console.log");
1011print(x); // 10
examples/basics/fib.blight+1
......@@ -8,4 +8,5 @@ pub fn fib(n: int) -> int {
88 }
99}
1010
11let print: any = @raw("console.log");
1112print(fib(10));
examples/basics/functions.blight+2-1
......@@ -4,4 +4,5 @@ fn bingus(x: int, y: int) -> int {
44
55let result: int = bingus(10, 25);
66
7print(result); // built-in function
7let print: any = @raw("console.log");
8print(result);
examples/basics/operations.blight+3
......@@ -1 +1,4 @@
11let a: int = 10 - 2 / 4 * 15;
2
3let print: any = @raw("console.log");
4print(a);
examples/basics/shadowing.blight+2
......@@ -1,3 +1,5 @@
1let print: any = @raw("console.log");
2
13let x: int = 0;
24let x: int = 1;
35print(x); // 1
examples/basics/variables.blight+3
......@@ -5,3 +5,6 @@ var z: bigint = 0;
55let a: int = 0;
66let b: float = 0;
77let c: bigint = 0;
8
9let print: any = @raw("console.log");
10print(x);
examples/basics/weird.blight+1
......@@ -9,5 +9,6 @@ fn bingus(y: bool) -> int {
99 x
1010}
1111
12let print: any = @raw("console.log");
1213print(bingus(true));
1314print(bingus(false));
examples/interop/blight_in_js/main.js+1-1
......@@ -1,4 +1,4 @@
11import { add, TOP_LEVEL } from "./add.js";
22
33console.log(add(1, 2));
4console.log(TOP_LEVEL)
4console.log(TOP_LEVEL);