authorgravatar for 11367844+lino-levan@users.noreply.github.comLino Le Van <11367844+lino-levan@users.noreply.github.com> 2024-01-03 18:10:07+01:00
committergravatar for 11367844+lino-levan@users.noreply.github.comLino Le Van <11367844+lino-levan@users.noreply.github.com> 2024-01-03 18:10:07+01:00
log6c14072860c381a177f886b7c3f00001fafd9abc
treece847a8d38b134e2a3e349ddc141dc98eb7c2618
parentde824b0e32bfb81f03869727ac3ff2006a3492e9

feat: add operators


9 files changed, 177 insertions(+), 135 deletions(-)

examples/easy.blight deleted-7
...@@ -1,7 +0,0 @@
1var x: int = 0;
2var y: float = 0;
3var z: bigint = 0;
4
5let a: int = 0;
6let b: float = 0;
7let c: bigint = 0;
examples/operations.blight created+1
...@@ -0,0 +1 @@
1let a: int = 10 - 2 / 4 * 15;
examples/variables.blight created+7
...@@ -0,0 +1,7 @@
1var x: int = 0;
2var y: float = 0;
3var z: bigint = 0;
4
5let a: int = 0;
6let b: float = 0;
7let c: bigint = 0;
src/main.rs+2-2
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1use std::path::PathBuf;1use std::path::PathBuf;
22
3use miette::{Result, NamedSource};
4use clap::{Parser, Subcommand};3use clap::{Parser, Subcommand};
4use miette::Result;
55
6mod transpile;6mod transpile;
77
...@@ -21,7 +21,7 @@ enum Commands {...@@ -21,7 +21,7 @@ enum Commands {
21 /// Compiles a file into javascript21 /// Compiles a file into javascript
22 Compile {22 Compile {
23 /// lists test values23 /// lists test values
24 file: PathBuf24 file: PathBuf,
25 },25 },
26}26}
2727
src/transpile/ast.rs+71-50
...@@ -1,19 +1,21 @@...@@ -1,19 +1,21 @@
1use miette::{Result, Diagnostic};1use miette::{Diagnostic, Result};
2use thiserror::Error;2use thiserror::Error;
33
4use super::tokenizer::Token;4use super::tokenizer::Token;
5use super::util::PeekableIterator;5use super::util::{Operator, PeekableIterator};
66
7#[derive(Error, Diagnostic, Debug)]7#[derive(Error, Diagnostic, Debug)]
8#[error("AstError")]8#[error("AstError")]
9struct AstError {9struct AstError {
10 #[help]10 #[help]
11 error: String11 error: String,
12}12}
1313
14impl AstError {14impl AstError {
15 fn new(error: &'static str) -> AstError {15 fn new(error: &'static str) -> AstError {
16 AstError { error: String::from(error) }16 AstError {
17 error: String::from(error),
18 }
17 }19 }
18}20}
1921
...@@ -21,7 +23,7 @@ impl AstError {...@@ -21,7 +23,7 @@ impl AstError {
21pub enum Type {23pub enum Type {
22 Int,24 Int,
23 Float,25 Float,
24 Bigint26 Bigint,
25}27}
2628
27#[derive(Debug)]29#[derive(Debug)]
...@@ -29,107 +31,126 @@ pub enum Ast {...@@ -29,107 +31,126 @@ pub enum Ast {
29 ConstantDeclaration {31 ConstantDeclaration {
30 name: String,32 name: String,
31 typ: Type,33 typ: Type,
32 value: Box<Ast>34 value: Box<Ast>,
33 },35 },
34 VariableDeclaration {36 VariableDeclaration {
35 name: String,37 name: String,
36 typ: Type,38 typ: Type,
37 value: Box<Ast>39 value: Box<Ast>,
40 },
41 Operation {
42 operator: Operator,
43 left: Box<Ast>,
44 right: Box<Ast>,
38 },45 },
39 Number(String),46 Number(String),
40}47}
4148
42fn parse_expression(tokens: &mut dyn PeekableIterator<Item=&Token>) -> Result<Ast> {49fn parse_expression(tokens: &mut dyn PeekableIterator<Item = &Token>) -> Result<Ast> {
43 match tokens.next() {50 let left = match tokens.next() {
44 Some(token) => match token {51 Some(token) => match token {
45 Token::Number(number) => Token::Number(number.clone()),52 Token::Number(number) => Ast::Number(number.clone()),
46 _ => return Err(AstError::new("Expected expression, but did not get it").into())53 _ => return Err(AstError::new("Expected expression, but did not get it").into()),
47 },54 },
48 None => return Err(AstError::new("Expected expression, but got nothing instead").into())55 None => return Err(AstError::new("Expected expression, but got nothing instead").into()),
49
50 };56 };
57
58 match tokens.peek() {
59 Some(token) => match token {
60 Token::Operator(operator) => {
61 tokens.next();
62 return Ok(Ast::Operation {
63 operator: operator.clone(),
64 left: Box::new(left),
65 right: Box::new(parse_expression(tokens)?),
66 });
67 }
68 _ => (),
69 },
70 _ => (),
71 }
72
51 Ok(Ast::Number(String::from("10")))73 Ok(Ast::Number(String::from("10")))
52}74}
5375
54/// Precondition: There is at least one token in the iterator76/// Precondition: There is at least one token in the iterator
55fn parse_statement(tokens: &mut dyn PeekableIterator<Item=&Token>) -> Result<Ast> {77fn parse_statement(tokens: &mut dyn PeekableIterator<Item = &Token>) -> Result<Ast> {
56 match tokens.next().unwrap() {78 match tokens.next().unwrap() {
57 Token::Let => {79 Token::Let => {
58 let name = match tokens.next() {80 let name = match tokens.next() {
59 Some(Token::Identifier(name)) => name,81 Some(Token::Identifier(name)) => name,
60 _ => return Err(AstError::new("Expected identifier after var").into())82 _ => return Err(AstError::new("Expected identifier after var").into()),
61 };83 };
62 let typ = match tokens.next() {84 let typ = match tokens.next() {
63 Some(Token::Colon) => {85 Some(Token::Colon) => match tokens.next() {
64 match tokens.next() {86 Some(Token::Identifier(typ)) => match typ.as_str() {
65 Some(Token::Identifier(typ)) => {87 "int" => Type::Int,
66 match typ.as_str() {88 "float" => Type::Float,
67 "int" => Type::Int,89 "bigint" => Type::Bigint,
68 "float" => Type::Float,90 _ => return Err(AstError::new("Expected type after colon").into()),
69 "bigint" => Type::Bigint,91 },
70 _ => return Err(AstError::new("Expected type after colon").into())92 _ => return Err(AstError::new("Expected type after colon").into()),
71 }
72 },
73 _ => return Err(AstError::new("Expected type after colon").into())
74 }
75 },93 },
76 _ => return Err(AstError::new("Expected colon after identifier").into())94 _ => return Err(AstError::new("Expected colon after identifier").into()),
77 };95 };
78 let value = match tokens.next() {96 let value = match tokens.next() {
79 Some(Token::Equals) => parse_expression(tokens)?,97 Some(Token::Equals) => parse_expression(tokens)?,
80 _ => return Err(AstError::new("Expected equals after identifier").into())98 _ => return Err(AstError::new("Expected equals after identifier").into()),
81 };99 };
82 match tokens.next() {100 match tokens.next() {
83 Some(Token::Semicolon) => (),101 Some(Token::Semicolon) => (),
84 _ => return Err(AstError::new("Expected semicolon after variable declaration").into())102 _ => {
103 return Err(
104 AstError::new("Expected semicolon after variable declaration").into(),
105 )
106 }
85 };107 };
86 Ok(Ast::ConstantDeclaration {108 Ok(Ast::ConstantDeclaration {
87 name: name.clone(),109 name: name.clone(),
88 typ,110 typ,
89 value: Box::new(value),111 value: Box::new(value),
90 })112 })
91 },113 }
92 Token::Var => {114 Token::Var => {
93 let name = match tokens.next() {115 let name = match tokens.next() {
94 Some(Token::Identifier(name)) => name,116 Some(Token::Identifier(name)) => name,
95 _ => return Err(AstError::new("Expected identifier after var").into())117 _ => return Err(AstError::new("Expected identifier after var").into()),
96 };118 };
97 let typ = match tokens.next() {119 let typ = match tokens.next() {
98 Some(Token::Colon) => {120 Some(Token::Colon) => match tokens.next() {
99 match tokens.next() {121 Some(Token::Identifier(typ)) => match typ.as_str() {
100 Some(Token::Identifier(typ)) => {122 "int" => Type::Int,
101 match typ.as_str() {123 "float" => Type::Float,
102 "int" => Type::Int,124 "bigint" => Type::Bigint,
103 "float" => Type::Float,125 _ => return Err(AstError::new("Expected type after colon").into()),
104 "bigint" => Type::Bigint,126 },
105 _ => return Err(AstError::new("Expected type after colon").into())127 _ => return Err(AstError::new("Expected type after colon").into()),
106 }
107 },
108 _ => return Err(AstError::new("Expected type after colon").into())
109 }
110 },128 },
111 _ => return Err(AstError::new("Expected colon after identifier").into())129 _ => return Err(AstError::new("Expected colon after identifier").into()),
112 };130 };
113 let value = match tokens.next() {131 let value = match tokens.next() {
114 Some(Token::Equals) => parse_expression(tokens)?,132 Some(Token::Equals) => parse_expression(tokens)?,
115 _ => return Err(AstError::new("Expected equals after identifier").into())133 _ => return Err(AstError::new("Expected equals after identifier").into()),
116 };134 };
117 match tokens.next() {135 match tokens.next() {
118 Some(Token::Semicolon) => (),136 Some(Token::Semicolon) => (),
119 _ => return Err(AstError::new("Expected semicolon after variable declaration").into())137 _ => {
138 return Err(
139 AstError::new("Expected semicolon after variable declaration").into(),
140 )
141 }
120 };142 };
121 Ok(Ast::VariableDeclaration {143 Ok(Ast::VariableDeclaration {
122 name: name.clone(),144 name: name.clone(),
123 typ,145 typ,
124 value: Box::new(value),146 value: Box::new(value),
125 })147 })
126 },148 }
127 Token::Number(number) => Ok(Ast::Number(number.clone())),149 Token::Number(number) => Ok(Ast::Number(number.clone())),
128 _ => return Err(AstError::new("Unexpected token").into())150 _ => return Err(AstError::new("Unexpected token").into()),
129 }151 }
130}152}
131153
132
133pub fn parse(tokens: Vec<Token>) -> Result<Vec<Ast>> {154pub fn parse(tokens: Vec<Token>) -> Result<Vec<Ast>> {
134 let mut iter = tokens.iter().peekable();155 let mut iter = tokens.iter().peekable();
135 let mut ast: Vec<Ast> = Vec::new();156 let mut ast: Vec<Ast> = Vec::new();
src/transpile/mod.rs+3-3
...@@ -1,12 +1,12 @@...@@ -1,12 +1,12 @@
1use miette::{Result};1use miette::Result;
22
3mod tokenizer;
4mod ast;3mod ast;
4mod tokenizer;
5mod walker;5mod walker;
66
7mod util;7mod util;
88
9pub fn transpile(content: String) -> Result<String> {9pub fn transpile(content: String) -> Result<String> {
10 // 1. Tokenize the input10 // 1. Tokenize the input
11 let tokens = tokenizer::tokenize(content)?;11 let tokens = tokenizer::tokenize(content)?;
12 // 2. Parse the tokens into an AST12 // 2. Parse the tokens into an AST
src/transpile/tokenizer.rs+56-61
...@@ -1,19 +1,13 @@...@@ -1,19 +1,13 @@
1use miette::{Result, Diagnostic};1use miette::{Diagnostic, Result};
2use thiserror::Error;2use thiserror::Error;
33
4use super::util::Operator;
5
4#[derive(Error, Diagnostic, Debug)]6#[derive(Error, Diagnostic, Debug)]
5#[error("UnknownCharacter")]7#[error("UnknownCharacter")]
6struct UnknownCharacter {8struct UnknownCharacter {
7 #[help]9 #[help]
8 error: String10 error: String,
9}
10
11#[derive(Debug, Clone)]
12pub enum Operator {
13 Add,
14 Substract,
15 Multiply,
16 Divide,
17}11}
1812
19#[derive(Debug, Clone)]13#[derive(Debug, Clone)]
...@@ -28,7 +22,7 @@ pub enum Token {...@@ -28,7 +22,7 @@ pub enum Token {
28 Equals,22 Equals,
29 Semicolon,23 Semicolon,
30 // Operators24 // Operators
31 Operator(Operator)25 Operator(Operator),
32}26}
3327
34pub fn tokenize(content: String) -> Result<Vec<Token>> {28pub fn tokenize(content: String) -> Result<Vec<Token>> {
...@@ -37,64 +31,65 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {...@@ -37,64 +31,65 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
3731
38 loop {32 loop {
39 match chars.next() {33 match chars.next() {
40 Some((i, c)) => {34 Some((_, c)) => match c {
41 match c {35 'a'..='z' | 'A'..='Z' | '_' => {
42 'a'..='z' | 'A'..='Z' | '_' => {36 let mut current_token = String::new();
43 let mut current_token = String::new();37 current_token.push(c);
44 current_token.push(c);38 loop {
45 loop {39 let c = chars.peek();
46 let c = chars.peek();40 if c.is_none() {
47 if c.is_none() {41 break;
48 break;42 }
49 }43 let (_, c) = c.unwrap();
50 let (_, c) = c.unwrap();44 match c {
51 match c {45 'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => {
52 'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => {46 current_token.push(*c);
53 current_token.push(*c);47 chars.next();
54 chars.next();
55 },
56 _ => break,
57 }48 }
49 _ => break,
58 }50 }
51 }
5952
60 match current_token.as_str() {53 match current_token.as_str() {
61 "var" => tokens.push(Token::Var),54 "var" => tokens.push(Token::Var),
62 "let" => tokens.push(Token::Let),55 "let" => tokens.push(Token::Let),
63 _ => tokens.push(Token::Identifier(current_token)),56 _ => tokens.push(Token::Identifier(current_token)),
57 }
58 }
59 '0'..='9' => {
60 let mut current_token = String::new();
61 current_token.push(c);
62 loop {
63 let c = chars.peek();
64 if c.is_none() {
65 break;
64 }66 }
65 },67 let (_, c) = c.unwrap();
66 '0'..='9' => {68 match c {
67 let mut current_token = String::new();69 '0'..='9' => {
68 current_token.push(c);70 current_token.push(*c);
69 loop {71 chars.next();
70 let c = chars.peek();
71 if c.is_none() {
72 break;
73 }
74 let (_, c) = c.unwrap();
75 match c {
76 '0'..='9' => {
77 current_token.push(*c);
78 chars.next();
79 },
80 _ => break,
81 }72 }
73 _ => break,
82 }74 }
83 tokens.push(Token::Number(current_token));
84 },
85 ':' => tokens.push(Token::Colon),
86 '=' => tokens.push(Token::Equals),
87 ';' => tokens.push(Token::Semicolon),
88 '+' => tokens.push(Token::Operator(Operator::Add)),
89 '-' => tokens.push(Token::Operator(Operator::Substract)),
90 '*' => tokens.push(Token::Operator(Operator::Multiply)),
91 '/' => tokens.push(Token::Operator(Operator::Divide)),
92 s if s.is_whitespace() => (),
93 _ => {
94 return Err(UnknownCharacter { error: format!("Unsupported character: {}", c)}.into())
95 }75 }
76 tokens.push(Token::Number(current_token));
77 }
78 ':' => tokens.push(Token::Colon),
79 '=' => tokens.push(Token::Equals),
80 ';' => tokens.push(Token::Semicolon),
81 '+' => tokens.push(Token::Operator(Operator::Add)),
82 '-' => tokens.push(Token::Operator(Operator::Substract)),
83 '*' => tokens.push(Token::Operator(Operator::Multiply)),
84 '/' => tokens.push(Token::Operator(Operator::Divide)),
85 s if s.is_whitespace() => (),
86 _ => {
87 return Err(UnknownCharacter {
88 error: format!("Unsupported character: {}", c),
89 }
90 .into())
96 }91 }
97 }92 },
98 None => break,93 None => break,
99 }94 }
100 }95 }
src/transpile/util.rs+20-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1// Rust why do you not have a peekable iterator in the standard library?1// Rust why do you not have a peekable iterator in the standard library?
2// Stolen from https://users.rust-lang.org/t/peekable-argument-there-has-to-be-a-simpler-way/69592// Stolen from https://users.rust-lang.org/t/peekable-argument-there-has-to-be-a-simpler-way/6959
3pub trait PeekableIterator : std::iter::Iterator {3pub trait PeekableIterator: std::iter::Iterator {
4 fn peek(&mut self) -> Option<&Self::Item>;4 fn peek(&mut self) -> Option<&Self::Item>;
5}5}
66
...@@ -9,3 +9,22 @@ impl<I: std::iter::Iterator> PeekableIterator for std::iter::Peekable<I> {...@@ -9,3 +9,22 @@ impl<I: std::iter::Iterator> PeekableIterator for std::iter::Peekable<I> {
9 std::iter::Peekable::peek(self)9 std::iter::Peekable::peek(self)
10 }10 }
11}11}
12
13#[derive(Debug, Clone)]
14pub enum Operator {
15 Add,
16 Substract,
17 Multiply,
18 Divide,
19}
20
21impl Operator {
22 pub fn to_string(self) -> String {
23 String::from(match self {
24 Self::Add => "+",
25 Self::Substract => "-",
26 Self::Divide => "/",
27 Self::Multiply => "*",
28 })
29 }
30}
src/transpile/walker.rs+17-11
...@@ -3,18 +3,24 @@ use super::ast::{Ast, Type};...@@ -3,18 +3,24 @@ use super::ast::{Ast, Type};
3fn transpile_helper(tree: Ast) -> String {3fn transpile_helper(tree: Ast) -> String {
4 match tree {4 match tree {
5 Ast::Number(number) => format!("{}", number),5 Ast::Number(number) => format!("{}", number),
6 Ast::VariableDeclaration { name, typ, value } => {6 Ast::Operation {
7 match typ {7 operator,
8 Type::Bigint => format!("let {} = BigInt({});\n", name, transpile_helper(*value)),8 left,
9 _ => format!("let {} = {};\n", name, transpile_helper(*value))9 right,
10 }10 } => format!(
11 "{} {} {}",
12 transpile_helper(*left),
13 operator.to_string(),
14 transpile_helper(*right)
15 ),
16 Ast::VariableDeclaration { name, typ, value } => match typ {
17 Type::Bigint => format!("let {} = BigInt({});\n", name, transpile_helper(*value)),
18 _ => format!("let {} = {};\n", name, transpile_helper(*value)),
19 },
20 Ast::ConstantDeclaration { name, typ, value } => match typ {
21 Type::Bigint => format!("const {} = BigInt({});\n", name, transpile_helper(*value)),
22 _ => format!("const {} = {};\n", name, transpile_helper(*value)),
11 },23 },
12 Ast::ConstantDeclaration { name, typ, value } => {
13 match typ {
14 Type::Bigint => format!("const {} = BigInt({});\n", name, transpile_helper(*value)),
15 _ => format!("const {} = {};\n", name, transpile_helper(*value))
16 }
17 }
18 }24 }
19}25}
2026