authorchloe caruso <git@paperclover.net> 2024-01-20 16:32:51-08:00
committerchloe caruso <git@paperclover.net> 2024-01-20 16:32:51-08:00
logfd797b948e101371e252f5053d00c77f529a48ad
tree40046a669d7d7128449b23739954ef611a241a0a
parent9593137d0c6b96fae79c7366d2c37b6ddcd36d08

rewrite the parser in rust


44 files changed, 1941 insertions(+), 1245 deletions(-)

Cargo.lock+13-9
......@@ -70,9 +70,9 @@ name = "ast"
7070version = "0.1.0"
7171dependencies = [
7272 "miette",
73 "num",
7374 "thiserror",
7475 "tokenizer",
75 "util",
7676]
7777
7878[[package]]
......@@ -134,7 +134,6 @@ dependencies = [
134134 "miette",
135135 "num",
136136 "thiserror",
137 "util",
138137]
139138
140139[[package]]
......@@ -187,6 +186,7 @@ dependencies = [
187186 "js_ast",
188187 "js_printer",
189188 "miette",
189 "parser",
190190 "thiserror",
191191 "tokenizer",
192192]
......@@ -250,7 +250,6 @@ dependencies = [
250250 "miette",
251251 "num",
252252 "thiserror",
253 "util",
254253]
255254
256255[[package]]
......@@ -261,7 +260,6 @@ dependencies = [
261260 "miette",
262261 "num",
263262 "thiserror",
264 "util",
265263]
266264
267265[[package]]
......@@ -420,6 +418,17 @@ version = "3.5.0"
420418source = "registry+https://github.com/rust-lang/crates.io-index"
421419checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
422420
421[[package]]
422name = "parser"
423version = "0.1.0"
424dependencies = [
425 "ast",
426 "miette",
427 "num",
428 "thiserror",
429 "tokenizer",
430]
431
423432[[package]]
424433name = "proc-macro2"
425434version = "1.0.76"
......@@ -555,7 +564,6 @@ version = "0.1.0"
555564dependencies = [
556565 "miette",
557566 "thiserror",
558 "util",
559567]
560568
561569[[package]]
......@@ -582,10 +590,6 @@ version = "0.2.1"
582590source = "registry+https://github.com/rust-lang/crates.io-index"
583591checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
584592
585[[package]]
586name = "util"
587version = "0.1.0"
588
589593[[package]]
590594name = "winapi"
591595version = "0.3.9"
Cargo.toml+10-2
......@@ -3,12 +3,20 @@ version = "0.1.0"
33name = "blight"
44edition = "2023"
55license = "MIT"
6authors = ["Lino Le Van"]
6authors = ["Lino Le Van", "Dave Caruso"]
77repository = "https://github.com/lino-levan/blight"
88
99[workspace]
1010resolver = "2"
11members = ["crates/cli", "crates/util", "crates/tokenizer", "crates/ast", "crates/js_ast", "crates/js_printer"]
11members = [
12 "crates/ast",
13 "crates/checker",
14 "crates/cli",
15 "crates/js_ast",
16 "crates/js_printer",
17 "crates/parser",
18 "crates/tokenizer",
19]
1220
1321[workspace.dependencies]
1422clap = { version = "4.4.12", features = ["derive"] }
crates/ast/Cargo.toml+2-1
......@@ -4,10 +4,11 @@ version = "0.1.0"
44edition = "2021"
55
66[lib]
7path = "./lib.rs"
78
89[dependencies]
910miette.workspace = true
1011thiserror.workspace = true
12num.workspace = true
1113
1214tokenizer = { path = "../tokenizer" }
13util = { path = "../util" }
crates/ast/ast.rs created+174
......@@ -0,0 +1,174 @@
1use tokenizer::Loc;
2
3use crate::{
4 types::{Literal, Type},
5 BinaryOperator, UnaryOperator,
6};
7
8#[derive(Debug, Clone)]
9pub enum BuiltinId {
10 Raw,
11}
12
13#[derive(Debug, Clone)]
14pub struct Module {
15 pub statements: Vec<TopLevelStatement>,
16}
17
18#[derive(Debug, Clone)]
19pub struct Block {
20 pub statements: Vec<Statement>,
21 pub has_implicit_return: bool,
22}
23
24#[derive(Debug, Clone)]
25pub struct Param {
26 pub name: String,
27 pub param_type: Type,
28}
29
30#[derive(Debug, Clone)]
31
32pub struct Decl {
33 pub name: String,
34 pub decl_type: Type,
35 pub init: Expr,
36 pub is_constant: bool,
37 pub is_pub: bool,
38 pub loc: Loc,
39 pub comment: Option<DocComment>,
40}
41
42#[derive(Debug, Clone)]
43pub struct Expr {
44 pub data: ExprData,
45 pub loc: Loc,
46}
47
48impl Expr {
49 pub fn new(data: ExprData, loc: Loc) -> Self {
50 Self { data, loc }
51 }
52}
53
54#[derive(Debug, Clone)]
55pub struct DocComment {
56 pub data: String,
57 pub loc: Loc,
58}
59
60#[derive(Debug, Clone)]
61pub struct BlockWithLoc {
62 pub block: Block,
63 pub loc: Loc,
64}
65
66#[derive(Debug, Clone)]
67pub struct If {
68 pub condition: Box<Expr>,
69 pub yes: BlockWithLoc,
70 pub no: Option<BlockWithLoc>,
71}
72
73#[derive(Debug, Clone)]
74pub enum ExprData {
75 Block(Block),
76 Binary {
77 op: BinaryOperator,
78 left: Box<Expr>,
79 right: Box<Expr>,
80 },
81 Unary {
82 op: UnaryOperator,
83 value: Box<Expr>,
84 },
85 Fn {
86 name: String,
87 params: Vec<Param>,
88 return_type: Type,
89 block: Block,
90 },
91 Call {
92 base: Box<Expr>,
93 arguments: Vec<Expr>,
94 },
95 Field {
96 base: Box<Expr>,
97 field: Box<Expr>,
98 },
99 Builtin {
100 id: BuiltinId,
101 arguments: Vec<Expr>,
102 },
103 If(If),
104 Return(Box<Expr>),
105 Literal(Literal),
106 Identifier(String),
107}
108
109#[derive(Debug, Clone)]
110pub enum Statement {
111 Expr(Expr),
112 Decl(Decl),
113}
114
115#[derive(Debug, Clone)]
116pub enum TopLevelStatement {
117 Expr(Expr),
118 Decl(Decl),
119}
120
121impl BuiltinId {
122 pub fn display_name(&self) -> &'static str {
123 match self {
124 Self::Raw => "@raw",
125 }
126 }
127
128 pub fn from_str(name: &str) -> Option<Self> {
129 match name {
130 "@raw" => Some(Self::Raw),
131 _ => None,
132 }
133 }
134}
135
136impl Statement {
137 pub fn requires_semicolon(&self) -> bool {
138 match &self {
139 Statement::Expr(expr) => match expr.data {
140 ExprData::Block(_) => false,
141 ExprData::If(_) => false,
142 _ => true,
143 },
144 _ => false,
145 }
146 }
147
148 pub fn to_top_level(self) -> TopLevelStatement {
149 match self {
150 Statement::Expr(expr) => TopLevelStatement::Expr(expr),
151 Statement::Decl(decl) => TopLevelStatement::Decl(decl),
152 }
153 }
154}
155
156impl TopLevelStatement {
157 pub fn requires_semicolon(&self) -> bool {
158 match &self {
159 TopLevelStatement::Expr(expr) => match expr.data {
160 ExprData::Block(_) => false,
161 ExprData::If(_) => false,
162 _ => true,
163 },
164 _ => false,
165 }
166 }
167
168 pub fn to_statement(self) -> Statement {
169 match self {
170 TopLevelStatement::Expr(expr) => Statement::Expr(expr),
171 TopLevelStatement::Decl(decl) => Statement::Decl(decl),
172 }
173 }
174}
crates/ast/lib.rs created+7
......@@ -0,0 +1,7 @@
1mod ast;
2mod operators;
3mod types;
4
5pub use ast::*;
6pub use operators::*;
7pub use types::*;
crates/ast/operators.rs created+171
......@@ -0,0 +1,171 @@
1use tokenizer::Token;
2
3use crate::Literal;
4
5#[derive(Debug, Clone, PartialEq)]
6pub enum BinaryOperator {
7 Add,
8 Sub,
9 Mul,
10 Div,
11 Pow,
12 Eq,
13 Gt,
14 Lt,
15 Gte,
16 Lte,
17 Lshift,
18 Rshift,
19 RshiftUnsigned,
20 BitAnd,
21 BitOr,
22 BitXor,
23 Mod,
24 NotEq,
25 And,
26 Or,
27}
28
29#[derive(Debug, Clone, PartialEq)]
30pub enum UnaryOperator {
31 Neg,
32 Not,
33 BitNot,
34}
35
36impl BinaryOperator {
37 pub fn to_string(&self) -> String {
38 String::from(match self {
39 Self::Add => "+",
40 Self::Sub => "-",
41 Self::Div => "/",
42 Self::Mul => "*",
43 Self::Pow => "**",
44 Self::Eq => "==",
45 Self::Gt => ">",
46 Self::Lt => "<",
47 Self::Gte => ">=",
48 Self::Lte => "<=",
49 Self::Lshift => "<<",
50 Self::Rshift => ">>",
51 Self::RshiftUnsigned => ">>>",
52 Self::BitAnd => "&",
53 Self::BitOr => "|",
54 Self::BitXor => "^",
55 Self::Mod => "%",
56 Self::NotEq => "!=",
57 Self::And => "&&",
58 Self::Or => "||",
59 })
60 }
61
62 pub fn from_token(token: &Token) -> Option<BinaryOperator> {
63 match token {
64 Token::And => Some(Self::BitAnd),
65 Token::AndAnd => Some(Self::And),
66 Token::BangEqual => Some(Self::NotEq),
67 Token::Caret => Some(Self::BitXor),
68 Token::EqualEqual => Some(Self::Eq),
69 Token::Gt => Some(Self::Gt),
70 Token::GtEqual => Some(Self::Gte),
71 Token::GtGt => Some(Self::Rshift),
72 Token::GtGtGt => Some(Self::RshiftUnsigned),
73 Token::Lt => Some(Self::Lt),
74 Token::LtEqual => Some(Self::Lte),
75 Token::LtLt => Some(Self::Lshift),
76 Token::Minus => Some(Self::Sub),
77 Token::Or => Some(Self::BitOr),
78 Token::OrOr => Some(Self::Or),
79 Token::Percent => Some(Self::Mod),
80 Token::Plus => Some(Self::Add),
81 Token::Slash => Some(Self::Div),
82 Token::Star => Some(Self::Mul),
83 Token::StarStar => Some(Self::Pow),
84 _ => None,
85 }
86 }
87
88 /// Error case of this means 'types do not allow this operation'
89 pub fn perform(&self, left: Literal, right: Literal) -> Result<Literal, ()> {
90 match self {
91 BinaryOperator::Eq => Ok(Literal::Bool(left == right)),
92 BinaryOperator::NotEq => Ok(Literal::Bool(left != right)),
93
94 BinaryOperator::Or | BinaryOperator::And => match (left, right) {
95 (Literal::Bool(a), Literal::Bool(b)) => Ok(Literal::Bool(match self {
96 BinaryOperator::Or => a || b,
97 BinaryOperator::And => a && b,
98 _ => unreachable!(),
99 })),
100 _ => Err(()),
101 },
102
103 BinaryOperator::Add
104 | BinaryOperator::Sub
105 | BinaryOperator::Mul
106 | BinaryOperator::Div
107 | BinaryOperator::Pow
108 | BinaryOperator::Gt
109 | BinaryOperator::Lt
110 | BinaryOperator::Gte
111 | BinaryOperator::Lte
112 | BinaryOperator::Lshift
113 | BinaryOperator::Rshift
114 | BinaryOperator::RshiftUnsigned
115 | BinaryOperator::BitAnd
116 | BinaryOperator::BitOr
117 | BinaryOperator::BitXor
118 | BinaryOperator::Mod => match (left, right) {
119 (Literal::Number(a), Literal::Number(b)) => Ok(match self {
120 BinaryOperator::Add => Literal::Number(a + b),
121 BinaryOperator::Sub => Literal::Number(a - b),
122 BinaryOperator::Mul => Literal::Number(a * b),
123 BinaryOperator::Div => Literal::Number(a / b),
124 BinaryOperator::Pow => Literal::Number(a.powf(b)),
125 BinaryOperator::Gt => Literal::Bool(a > b),
126 BinaryOperator::Lt => Literal::Bool(a < b),
127 BinaryOperator::Gte => Literal::Bool(a >= b),
128 BinaryOperator::Lte => Literal::Bool(a <= b),
129 BinaryOperator::Lshift => Literal::Number(((a as i32) << (b as i32)).into()),
130 BinaryOperator::Rshift => Literal::Number(((a as i32) >> (b as i32)).into()),
131 BinaryOperator::RshiftUnsigned => {
132 Literal::Number(((a as u32) >> (b as u32)).into())
133 }
134 BinaryOperator::BitAnd => Literal::Number(((a as i32) & (b as i32)).into()),
135 BinaryOperator::BitOr => Literal::Number(((a as i32) | (b as i32)).into()),
136 BinaryOperator::BitXor => Literal::Number(((a as i32) ^ (b as i32)).into()),
137 BinaryOperator::Mod => Literal::Number(a % b),
138 _ => unreachable!(),
139 }),
140 _ => Err(()),
141 },
142 }
143 }
144}
145
146impl UnaryOperator {
147 pub fn to_string(self) -> String {
148 String::from(match self {
149 Self::Neg => "-",
150 Self::Not => "!",
151 Self::BitNot => "~",
152 })
153 }
154
155 pub fn perform(self, value: Literal) -> Result<Literal, ()> {
156 match self {
157 UnaryOperator::Neg => match value {
158 Literal::Number(a) => Ok(Literal::Number(-a)),
159 _ => Err(()),
160 },
161 UnaryOperator::Not => match value {
162 Literal::Bool(a) => Ok(Literal::Bool(!a)),
163 _ => Err(()),
164 },
165 UnaryOperator::BitNot => match value {
166 Literal::Number(a) => Ok(Literal::Number((!(a as i32)).into())),
167 _ => Err(()),
168 },
169 }
170 }
171}
crates/ast/src/error.rs deleted-21
......@@ -1,21 +0,0 @@
1use miette::Diagnostic;
2use thiserror::Error;
3
4#[derive(Error, Diagnostic, Debug)]
5#[error("AstError")]
6pub struct AstError {
7 #[help]
8 error: String,
9}
10
11impl AstError {
12 pub fn new(error: &'static str) -> AstError {
13 AstError {
14 error: String::from(error),
15 }
16 }
17
18 pub fn from_string(error: String) -> AstError {
19 AstError { error }
20 }
21}
crates/ast/src/lib.rs deleted-27
......@@ -1,27 +0,0 @@
1use miette::Result;
2
3mod error;
4mod parse_block;
5mod parse_expression;
6mod parse_function_call;
7mod parse_statement;
8mod parse_type;
9mod types;
10
11use tokenizer::Token;
12
13use parse_statement::parse_statement;
14use types::ParserContext;
15
16pub use types::{Ast, Block, Module, Type};
17
18pub fn parse(tokens: Vec<Token>) -> Result<Module> {
19 let mut ctx = ParserContext::new(tokens);
20 let mut ast: Vec<Ast> = Vec::new();
21
22 while !ctx.empty() {
23 ast.push(parse_statement(&mut ctx)?);
24 }
25
26 Ok(Module { value: ast })
27}
crates/ast/src/parse_block.rs deleted-55
......@@ -1,55 +0,0 @@
1use miette::Result;
2
3use tokenizer::Token;
4
5use super::error::AstError;
6use super::parse_expression::parse_expression;
7use super::parse_statement::parse_statement;
8use super::types::Block;
9use super::types::ParserContext;
10
11pub fn parse_block(tokens: &mut ParserContext) -> Result<Block> {
12 match tokens.next() {
13 Some(Token::OpenBracket) => (),
14 _ => return Err(AstError::new("Expected { when parsing block").into()),
15 }
16
17 let mut value = Vec::new();
18 let mut implicit_return = false;
19
20 loop {
21 match tokens.peek() {
22 Some(Token::ClosedBracket) => {
23 tokens.next();
24 break;
25 }
26 _ => (),
27 }
28
29 let current_cursor = tokens.cursor;
30 let result = parse_statement(tokens);
31
32 match result {
33 Ok(ast) => value.push(ast),
34 Err(_) => {
35 tokens.cursor = current_cursor;
36 value.push(parse_expression(tokens)?);
37 implicit_return = true;
38 match tokens.peek() {
39 Some(Token::ClosedBracket) => (),
40 _ => {
41 return Err(AstError::new(
42 "Expected closed bracket after expression in function body",
43 )
44 .into());
45 }
46 }
47 }
48 }
49 }
50
51 Ok(Block {
52 value,
53 implicit_return,
54 })
55}
crates/ast/src/parse_expression.rs deleted-105
......@@ -1,105 +0,0 @@
1use miette::Result;
2
3use tokenizer::Token;
4
5use super::error::AstError;
6use super::parse_block::parse_block;
7use super::parse_function_call::parse_function_call;
8use super::types::{Ast, Block, ParserContext};
9
10pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> {
11 let left = match tokens.next() {
12 Some(token) => match token {
13 Token::Number(number) => Ast::Number(number),
14 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 }
25 Token::True => Ast::Bool(true),
26 Token::False => Ast::Bool(false),
27 Token::OpenBracket => {
28 // FRICK we consumed the open bracket
29 // we need to put the cursor back
30 tokens.back();
31 Ast::Block(parse_block(tokens)?)
32 }
33 Token::If => {
34 let condition = parse_expression(tokens)?;
35
36 let value = parse_block(tokens)?;
37
38 let mut else_value: Option<Block> = None;
39
40 match tokens.peek() {
41 Some(Token::Else) => {
42 tokens.next();
43 match tokens.peek() {
44 Some(Token::If) => {
45 let else_expression = parse_expression(tokens)?;
46 else_value = Some(Block {
47 value: vec![else_expression],
48 implicit_return: true,
49 });
50 }
51 Some(Token::OpenBracket) => {
52 else_value = Some(parse_block(tokens)?);
53 }
54 _ => {
55 return Err(AstError::new(
56 "Expected if or open bracket after else in if statement",
57 )
58 .into())
59 }
60 }
61 }
62 _ => (),
63 }
64
65 return Ok(Ast::If {
66 condition: Box::new(condition),
67 value,
68 else_value,
69 });
70 }
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)?
77 }
78 _ => Ast::Identifier(name.clone()),
79 }
80 }
81 _ => {
82 println!("token: {:?}", token);
83 return Err(AstError::new("Expected expression, but did not get it").into());
84 }
85 },
86 None => return Err(AstError::new("Expected expression, but got nothing instead").into()),
87 };
88
89 match tokens.peek() {
90 Some(token) => match token {
91 Token::Operator(operator) => {
92 tokens.next();
93 return Ok(Ast::Operation {
94 operator: operator.clone(),
95 left: Box::new(left),
96 right: Box::new(parse_expression(tokens)?),
97 });
98 }
99 _ => (),
100 },
101 _ => (),
102 }
103
104 Ok(left)
105}
crates/ast/src/parse_function_call.rs deleted-56
......@@ -1,56 +0,0 @@
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 deleted-245
......@@ -1,245 +0,0 @@
1use miette::Result;
2
3use tokenizer::Token;
4
5use crate::Type;
6
7use super::error::AstError;
8use super::parse_block::parse_block;
9use super::parse_expression::parse_expression;
10use super::parse_function_call::parse_function_call;
11use super::parse_type::parse_type;
12use super::types::{Argument, Ast, Block, ParserContext};
13
14/// Precondition: There is at least one token in the iterator
15pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
16 let token = tokens.next().unwrap();
17 match token {
18 Token::BuiltIn(name) => {
19 match tokens.peek() {
20 Some(Token::OpenParanthesis) => {
21 // Okay, so we messed up. We shouldn't have consumed that token.
22 tokens.back();
23 parse_function_call(tokens)
24 }
25 _ => Ok(Ast::BuiltIn(name)),
26 }
27 }
28 Token::OpenBracket => {
29 // FRICK, we have to backtrack
30 tokens.back();
31 Ok(Ast::Block(parse_block(tokens)?))
32 }
33 Token::Pub => {
34 match tokens.peek() {
35 Some(Token::Let) | Some(Token::Var) | Some(Token::Function) => (),
36 _ => return Err(AstError::new("Expected let, var, or fn after pub").into()),
37 }
38
39 let inner = parse_statement(tokens)?;
40
41 return Ok(Ast::Public(Box::new(inner)));
42 }
43 Token::Var | Token::Let => {
44 let is_constant = match token {
45 Token::Var => false,
46 Token::Let => true,
47 _ => unreachable!(),
48 };
49 let name = match tokens.next() {
50 Some(Token::Identifier(name)) => name,
51 _ => return Err(AstError::new("Expected identifier after var").into()),
52 };
53 let return_type = match tokens.peek() {
54 Some(Token::Colon) => {
55 tokens.next();
56 parse_type(tokens)?
57 }
58 _ => Type::Infer,
59 };
60 let value = match tokens.next() {
61 Some(Token::Equals) => parse_expression(tokens)?,
62 _ => return Err(AstError::new("Expected equals after identifier").into()),
63 };
64 match tokens.next() {
65 Some(Token::Semicolon) => (),
66 _ => {
67 return Err(
68 AstError::new("Expected semicolon after constant declaration").into(),
69 )
70 }
71 };
72 Ok(Ast::Declaration {
73 name: name.clone(),
74 decl_type: return_type,
75 value: Box::new(value),
76 is_constant,
77 })
78 }
79 Token::Return => {
80 let expression = parse_expression(tokens)?;
81
82 match tokens.next() {
83 Some(Token::Semicolon) => (),
84 _ => return Err(AstError::new("Expected semicolon after return statement").into()),
85 };
86
87 Ok(Ast::Return(Box::new(expression)))
88 }
89 Token::Function => {
90 let name = match tokens.next() {
91 Some(Token::Identifier(name)) => name.clone(),
92 _ => return Err(AstError::new("Expected identifier after fn").into()),
93 };
94
95 match tokens.next() {
96 Some(Token::OpenParanthesis) => (),
97 _ => {
98 return Err(AstError::new(
99 "Expected open paranthesis after identifier in function",
100 )
101 .into())
102 }
103 };
104
105 let mut arguments = Vec::new();
106
107 loop {
108 match tokens.peek() {
109 Some(Token::ClosedParanthesis) => {
110 tokens.next();
111 break;
112 }
113 _ => (),
114 }
115
116 let argument_name = match tokens.next() {
117 Some(Token::Identifier(name)) => name,
118 _ => {
119 return Err(AstError::new(
120 "Expected identifier for argument name in function",
121 )
122 .into())
123 }
124 };
125
126 let return_type = match tokens.next() {
127 Some(Token::Colon) => parse_type(tokens)?,
128 _ => return Err(AstError::new("Expected colon after identifier").into()),
129 };
130
131 match tokens.peek() {
132 Some(Token::Comma) => {
133 tokens.next();
134 }
135 _ => (),
136 }
137
138 arguments.push(Argument {
139 name: argument_name.clone(),
140 return_type,
141 })
142 }
143
144 let return_type = match tokens.next() {
145 Some(Token::RightArrow) => parse_type(tokens)?,
146 _ => {
147 return Err(AstError::new(
148 "Expected right arrow after argument list in function",
149 )
150 .into())
151 }
152 };
153
154 match tokens.peek() {
155 Some(Token::OpenBracket) => (),
156 _ => {
157 return Err(AstError::new(
158 "Expected open bracket after type in function declaration",
159 )
160 .into())
161 }
162 };
163
164 let block = parse_block(tokens)?;
165
166 return Ok(Ast::Function {
167 name,
168 arguments,
169 return_type,
170 block,
171 });
172 }
173 Token::If => {
174 let condition = parse_expression(tokens)?;
175
176 let value = parse_block(tokens)?;
177
178 let mut else_value: Option<Block> = None;
179
180 match tokens.peek() {
181 Some(Token::Else) => {
182 tokens.next();
183 match tokens.peek() {
184 Some(Token::If) => {
185 let else_if = parse_statement(tokens)?;
186 else_value = Some(Block {
187 value: vec![else_if],
188 implicit_return: false,
189 });
190 }
191 Some(Token::OpenBracket) => {
192 else_value = Some(parse_block(tokens)?);
193 }
194 _ => {
195 return Err(AstError::new(
196 "Expected if or open bracket after else in if statement",
197 )
198 .into())
199 }
200 }
201 }
202 _ => (),
203 }
204
205 return Ok(Ast::If {
206 condition: Box::new(condition),
207 value,
208 else_value,
209 });
210 }
211 Token::Identifier(_) => match tokens.peek() {
212 Some(Token::OpenParanthesis) => {
213 // We need to give the identifier back to the parser
214 tokens.back();
215 let result = parse_function_call(tokens);
216 match tokens.next() {
217 Some(Token::Semicolon) => (),
218 _ => {
219 return Err(AstError::new(
220 "Expected semicolon after function call statement",
221 )
222 .into())
223 }
224 };
225 result
226 }
227 _ => Err(AstError::new("Unexpected identifier").into()),
228 },
229 Token::Comment(comment) => Ok(Ast::Comment(comment.clone())),
230 Token::DocComment(comment) => {
231 let mut concatted_comments = comment.clone();
232 loop {
233 match tokens.peek() {
234 Some(Token::DocComment(comment)) => {
235 concatted_comments.push_str(format!("\n{}", comment).as_str());
236 tokens.next();
237 }
238 _ => break,
239 }
240 }
241 Ok(Ast::DocComment(concatted_comments))
242 }
243 _ => return Err(AstError::new("Unexpected token").into()),
244 }
245}
crates/ast/src/parse_type.rs deleted-28
......@@ -1,28 +0,0 @@
1use miette::Result;
2
3use tokenizer::Token;
4
5use super::error::AstError;
6use super::types::{ParserContext, Type};
7
8pub fn parse_type(tokens: &mut ParserContext) -> Result<Type> {
9 match tokens.next() {
10 Some(Token::Identifier(typ)) => match typ.as_str() {
11 "int" => Ok(Type::Int),
12 "float" => Ok(Type::Float),
13 "bigint" => Ok(Type::BigInt),
14 "bool" => Ok(Type::Bool),
15 "string" => Ok(Type::String),
16 _ => {
17 return Err(
18 AstError::from_string(format!(
19 "Invalid type '{}' (TODO: add custom type support)",
20 typ
21 )
22 ).into())
23 }
24 },
25 Some(unexpected) => return Err(AstError::from_string(format!("Expected type, got {}", unexpected)).into()),
26 None => return Err(AstError::from_string("Expected type, got EOF".to_string()).into()),
27 }
28}
crates/ast/src/types.rs deleted-153
......@@ -1,153 +0,0 @@
1// TODO(lino-levan): Split up AST into expressions, statements, etc.
2
3use tokenizer::Token;
4use util::Operator;
5
6#[derive(Debug)]
7pub struct ParserContext {
8 pub tokens: Vec<Token>,
9 pub cursor: usize,
10}
11
12impl ParserContext {
13 pub fn new(tokens: Vec<Token>) -> ParserContext {
14 ParserContext { tokens, cursor: 0 }
15 }
16
17 pub fn empty(&self) -> bool {
18 self.cursor == self.tokens.len()
19 }
20
21 pub fn back(&mut self) {
22 self.cursor -= 1;
23 }
24
25 pub fn next(&mut self) -> Option<Token> {
26 if self.cursor == self.tokens.len() {
27 return None;
28 }
29
30 let token = self.tokens[self.cursor].clone();
31 self.cursor += 1;
32 Some(token)
33 }
34
35 pub fn peek(&self) -> Option<Token> {
36 if self.cursor == self.tokens.len() {
37 return None;
38 }
39
40 Some(self.tokens[self.cursor].clone())
41 }
42}
43
44#[derive(Debug, Clone, PartialEq)]
45pub enum Type {
46 /// Variables without an explicit type are assigned this until
47 /// the type inferencer figures out what this is. There is not a
48 /// keyword for this.
49 Infer,
50 /// The type assigned to numeric literals. This allows integer literals
51 /// to coerce to integer or float depending on the context, but prevents
52 /// implicit casting of runtime values.
53 NumberLiteral(f64),
54 /// Return value of `@raw`. All type checks are disabled.
55 Any,
56 /// noreturn is the type of 'return', aka a value that never exists
57 NoReturn,
58 /// 32 signed integer
59 Int,
60 /// 64 signed JS float
61 Float,
62 /// Boolean
63 Bool,
64 /// JS BigInt
65 BigInt,
66 /// JS String
67 String,
68 /// A union of multiple possible types. Might be removed
69 Union(Vec<Type>),
70}
71
72impl std::fmt::Display for Type {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match self {
75 Type::Infer => write!(f, "infered type"),
76 Type::Any => write!(f, "opaque js value"),
77 Type::NumberLiteral(_) => write!(f, "numeric literal"),
78 Type::Int => write!(f, "int"),
79 Type::Float => write!(f, "float"),
80 Type::BigInt => write!(f, "bigint"),
81 Type::Bool => write!(f, "bool"),
82 Type::String => write!(f, "string"),
83 Type::NoReturn => write!(f, "noreturn"),
84 Type::Union(of) => {
85 for (i, t) in of.iter().enumerate() {
86 if i != 0 {
87 write!(f, " | ")?;
88 }
89 write!(f, "{}", t)?;
90 }
91 Ok(())
92 }
93 }
94 }
95}
96
97#[derive(Debug, Clone)]
98pub struct Module {
99 pub value: Vec<Ast>,
100}
101
102#[derive(Debug, Clone)]
103pub struct Block {
104 pub value: Vec<Ast>,
105 pub implicit_return: bool,
106}
107
108#[derive(Debug, Clone)]
109pub struct Argument {
110 pub name: String,
111 pub return_type: Type,
112}
113
114#[derive(Debug, Clone)]
115pub enum Ast {
116 Declaration {
117 name: String,
118 decl_type: Type,
119 value: Box<Ast>,
120 is_constant: bool,
121 },
122 Operation {
123 operator: Operator,
124 left: Box<Ast>,
125 right: Box<Ast>,
126 },
127 Function {
128 name: String,
129 arguments: Vec<Argument>,
130 return_type: Type,
131 block: Block,
132 },
133 FunctionCall {
134 /// Identifier or BuiltIn
135 name: Box<Ast>,
136 arguments: Vec<Ast>,
137 },
138 If {
139 condition: Box<Ast>,
140 value: Block,
141 else_value: Option<Block>,
142 },
143 Return(Box<Ast>),
144 Public(Box<Ast>),
145 Block(Block),
146 Number(f64),
147 Bool(bool),
148 String(String),
149 Identifier(String),
150 BuiltIn(String),
151 Comment(String),
152 DocComment(String),
153}
crates/ast/types.rs created+208
......@@ -0,0 +1,208 @@
1use miette::{Diagnostic, Result};
2use thiserror::Error;
3
4/// A value that is known at compile-time
5#[derive(Debug, Clone, PartialEq)]
6pub enum Literal {
7 Number(f64),
8 Bool(bool),
9 String(String),
10 Void,
11}
12
13impl std::fmt::Display for Literal {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 match self {
16 Literal::Number(value) => write!(f, "{}", value),
17 Literal::Bool(value) => write!(f, "{}", value),
18 Literal::String(value) => write!(f, "{}", value),
19 Literal::Void => write!(f, "void"),
20 }
21 }
22}
23
24#[derive(Debug, Clone, PartialEq)]
25pub enum Type {
26 /// Variables without an explicit type are assigned this until
27 /// the type inferencer figures out what this is. There is not a
28 /// keyword for this.
29 Infer,
30 /// The type assigned to literals. This allows integer literals
31 /// to coerce to integer or float depending on the context, but
32 /// prevents implicit casting of runtime values.
33 Literal(Literal),
34 /// Return value of `@raw`. All type checks are disabled.
35 Any,
36 /// The type of `void`. This is the return type of functions
37 /// that do not return a value.
38 Void,
39 /// noreturn is the type of 'return', aka a value that never exists.
40 /// unlike void, this includes functions that do not return at all.
41 NoReturn,
42 /// 32 signed integer
43 Int,
44 /// 64 signed JS float
45 Float,
46 /// Boolean
47 Bool,
48 /// JS BigInt
49 BigInt,
50 /// JS String
51 String,
52 /// A union of multiple possible types. Might be removed
53 Union(Vec<Type>),
54}
55
56pub trait TypeCoerceable {
57 fn to_type(&self) -> Option<Type>;
58}
59
60impl TypeCoerceable for Literal {
61 fn to_type(&self) -> Option<Type> {
62 Some(match self {
63 Literal::Number(_) => Type::Float,
64 Literal::Bool(_) => Type::Bool,
65 Literal::String(_) => Type::String,
66 Literal::Void => Type::Void,
67 })
68 }
69}
70
71impl TypeCoerceable for Type {
72 fn to_type(&self) -> Option<Type> {
73 Some(self.clone())
74 }
75}
76
77impl std::fmt::Display for Type {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 match self {
80 Type::Infer => write!(f, "infered type"),
81 Type::Any => write!(f, "opaque js value"),
82 Type::Literal(value) => write!(f, "{}", value),
83 Type::Int => write!(f, "int"),
84 Type::Float => write!(f, "float"),
85 Type::BigInt => write!(f, "bigint"),
86 Type::Bool => write!(f, "bool"),
87 Type::String => write!(f, "string"),
88 Type::NoReturn => write!(f, "noreturn"),
89 Type::Void => write!(f, "void"),
90 Type::Union(of) => {
91 for (i, t) in of.iter().enumerate() {
92 if i != 0 {
93 write!(f, " | ")?;
94 }
95 write!(f, "{}", t)?;
96 }
97 Ok(())
98 }
99 }
100 }
101}
102
103pub fn merge_types(a: Type, b: Type) -> Result<Type> {
104 match a {
105 Type::Infer => return Ok(b),
106 Type::Any => return Ok(Type::Any),
107 _ => {}
108 }
109
110 match b {
111 Type::Infer => return Ok(a),
112 Type::Any => return Ok(Type::Any),
113 _ => {}
114 }
115
116 if a == b {
117 return Ok(a);
118 }
119
120 Err(TypeError::new(format!("TODO: Implement type merging for {} and {}", a, b)).into())
121}
122
123// pub fn coerce_type(target: Type, source: Type) -> Result<Type> {
124// match source {
125// Type::Any => return Ok(target),
126// _ => {}
127// }
128// match target {
129// Type::Infer => Ok(source),
130// Type::Any => Ok(Type::Any),
131
132// Type::Literal(_) => unreachable!(),
133
134// Type::Int => match source {
135// Type::Int => Ok(Type::Int),
136// Type::Float => Ok(Type::Int),
137// Type::Literal(value) => {
138// if value.fract() != 0.0 {
139// Err(
140// TypeError::new(format!("Numeric Literal '{}' is not an integer", value))
141// .into(),
142// )
143// } else if value > i32::MAX.into() || value < i32::MIN.into() {
144// Err(TypeError::new(format!(
145// "Numeric Literal '{}' is out of range for an integer ({}..{})",
146// value,
147// i32::MIN,
148// i32::MAX
149// ))
150// .into())
151// } else {
152// Ok(Type::Int)
153// }
154// }
155// t => Err(TypeError::new(format!("Cannot coerce {} to int", t)).into()),
156// },
157
158// Type::BigInt => match source {
159// Type::Int => Ok(Type::Int),
160// Type::Float => Ok(Type::Int),
161// Type::NumberLiteral(value) => {
162// if value.fract() != 0.0 {
163// Err(
164// TypeError::new(format!("Numeric Literal '{}' is not an integer", value))
165// .into(),
166// )
167// } else {
168// Ok(Type::BigInt)
169// }
170// }
171// t => Err(TypeError::new(format!("Cannot coerce {} to int", t)).into()),
172// },
173
174// Type::Float => match source {
175// Type::Int => Ok(Type::Float),
176// Type::Float => Ok(Type::Float),
177// Type::NumberLiteral(value) => {
178// if value.fract() == 0.0 && value <= f64::MAX.into() && value >= f64::MIN.into() {
179// Ok(Type::Float)
180// } else {
181// Err(TypeError::new(format!("Numeric Literal")).into())
182// }
183// }
184// _ => Err(TypeError::new(format!("Cannot coerce int to float")).into()),
185// },
186
187// other => {
188// if other == source {
189// Ok(other)
190// } else {
191// Err(TypeError::new(format!("Cannot coerce {} to {}", source, other)).into())
192// }
193// }
194// }
195// }
196
197#[derive(Error, Diagnostic, Debug)]
198#[error("TypeError")]
199pub struct TypeError {
200 #[help]
201 error: String,
202}
203
204impl TypeError {
205 pub fn new(error: String) -> Self {
206 Self { error }
207 }
208}
crates/checker/Cargo.toml+1-2
......@@ -10,5 +10,4 @@ miette.workspace = true
1010thiserror.workspace = true
1111num.workspace = true
1212
13ast = { path = "../ast" }
14util = { path = "../util" }
13ast = { path = "../ast" }
\ No newline at end of file
crates/checker/src/ast_type_coersion.rs created+54
......@@ -0,0 +1,54 @@
1// use ast::{BinaryOperator, ExprData, Type};
2
3// impl TypeCoerceable for ExprData {
4// fn to_type(&self) -> Option<Type> {
5// match self {
6// ExprData::Block(block) => {
7// if (block.has_implicit_return) {
8// block.statements.last().unwrap().to_type()
9// } else {
10// Some(Type::Void)
11// }
12// }
13// ExprData::Binary { op, left, right } => {
14// // TODO: this likely has to do further type checking
15// let left_type = left.to_type();
16// match op {
17// BinaryOperator::Add => left_type,
18// BinaryOperator::Sub => left_type,
19// BinaryOperator::Mul => left_type,
20// BinaryOperator::Div => left_type,
21// BinaryOperator::Pow => left_type,
22// BinaryOperator::Eq => Some(Type::Bool),
23// BinaryOperator::Gt => Some(Type::Bool),
24// BinaryOperator::Lt => Some(Type::Bool),
25// BinaryOperator::Gte => Some(Type::Bool),
26// BinaryOperator::Lte => Some(Type::Bool),
27// BinaryOperator::Lshift => Some(Type::Int),
28// BinaryOperator::Rshift => Some(Type::Int),
29// BinaryOperator::BitAnd => Some(Type::Int),
30// BinaryOperator::BitOr => Some(Type::Int),
31// BinaryOperator::BitXor => Some(Type::Int),
32// BinaryOperator::Mod => Some(Type::Int),
33// BinaryOperator::NotEq => Some(Type::Int),
34
35// BinaryOperator::And => Some(Type::Bool),
36// BinaryOperator::Or => Some(Type::Bool),
37// }
38// }
39// ExprData::Unary { op, value } => todo!(),
40// ExprData::Function {
41// name,
42// params,
43// return_type,
44// block,
45// } => Some(return_type),
46// ExprData::Call { base, arguments } => None, // oh shit. yeah this trait isn't implementable
47// ExprData::Builtin { id, arguments } => todo!(),
48// ExprData::If(_) => todo!(),
49// ExprData::Return(_) => todo!(),
50// ExprData::Literal(_) => todo!(),
51// ExprData::Identifier(_) => todo!(),
52// }
53// }
54// }
crates/checker/src/lib.rs+96-203
......@@ -1,203 +1,96 @@
1use ast::{Ast, Module, Type};
2use miette::Result;
3
4use miette::Diagnostic;
5use thiserror::Error;
6
7#[derive(Error, Diagnostic, Debug)]
8#[error("TypeError")]
9pub struct TypeError {
10 #[help]
11 error: String,
12}
13
14impl TypeError {
15 pub fn new(error: String) -> Self {
16 Self { error }
17 }
18}
19
20pub fn check_module(block: &Module) -> Result<()> {
21 for statement in &block.value {
22 check_expression(statement)?;
23 }
24
25 Ok(())
26}
27
28pub fn resolve_type(expr: &Ast) -> Result<Type> {
29 match expr {
30 Ast::Number(value) => Ok(Type::NumberLiteral(value.clone())),
31 Ast::Declaration {
32 name,
33 decl_type,
34 value,
35 is_constant,
36 } => Ok(Type::NoReturn),
37 Ast::Operation {
38 operator,
39 left,
40 right,
41 } => merge_types(resolve_type(left)?, resolve_type(right)?),
42 Ast::Function {
43 name,
44 arguments,
45 return_type,
46 block,
47 } => todo!(),
48 Ast::FunctionCall { name, arguments } => todo!(),
49 Ast::If {
50 condition,
51 value,
52 else_value,
53 } => todo!(),
54 Ast::Return(_) => Ok(Type::NoReturn),
55 Ast::Public(_) => todo!(), // TODO: remove Public
56 Ast::Block(_) => todo!(),
57 Ast::Bool(_) => Ok(Type::Bool),
58 Ast::String(_) => Ok(Type::String),
59 Ast::Identifier(_) => todo!(),
60 Ast::BuiltIn(_) => todo!(),
61
62 Ast::Comment(_) => unreachable!(),
63 Ast::DocComment(_) => unreachable!(),
64 }
65}
66
67pub fn merge_types(a: Type, b: Type) -> Result<Type> {
68 match a {
69 Type::Infer => return Ok(b),
70 Type::Any => return Ok(Type::Any),
71 _ => {}
72 }
73
74 match b {
75 Type::Infer => return Ok(a),
76 Type::Any => return Ok(Type::Any),
77 _ => {}
78 }
79
80 if a == b {
81 return Ok(a);
82 }
83
84 Err(TypeError::new(format!("Cannot merge {} and {}", a, b)).into())
85}
86
87pub fn coerce_type(target: Type, source: Type) -> Result<Type> {
88 match source {
89 Type::Any => return Ok(target),
90 _ => {}
91 }
92 match target {
93 Type::Infer => Ok(source),
94 Type::Any => Ok(Type::Any),
95
96 Type::NumberLiteral(_) => unreachable!(),
97
98 Type::Int => match source {
99 Type::Int => Ok(Type::Int),
100 Type::Float => Ok(Type::Int),
101 Type::NumberLiteral(value) => {
102 if value.fract() != 0.0 {
103 Err(
104 TypeError::new(format!("Numeric Literal '{}' is not an integer", value))
105 .into(),
106 )
107 } else if value > i32::MAX.into() || value < i32::MIN.into() {
108 Err(TypeError::new(format!(
109 "Numeric Literal '{}' is out of range for an integer ({}..{})",
110 value,
111 i32::MIN,
112 i32::MAX
113 ))
114 .into())
115 } else {
116 Ok(Type::Int)
117 }
118 }
119 t => Err(TypeError::new(format!("Cannot coerce {} to int", t)).into()),
120 },
121
122 Type::BigInt => match source {
123 Type::Int => Ok(Type::Int),
124 Type::Float => Ok(Type::Int),
125 Type::NumberLiteral(value) => {
126 if value.fract() != 0.0 {
127 Err(
128 TypeError::new(format!("Numeric Literal '{}' is not an integer", value))
129 .into(),
130 )
131 } else {
132 Ok(Type::BigInt)
133 }
134 }
135 t => Err(TypeError::new(format!("Cannot coerce {} to int", t)).into()),
136 },
137
138 Type::Float => match source {
139 Type::Int => Ok(Type::Float),
140 Type::Float => Ok(Type::Float),
141 Type::NumberLiteral(value) => {
142 if value.fract() == 0.0 && value <= f64::MAX.into() && value >= f64::MIN.into() {
143 Ok(Type::Float)
144 } else {
145 Err(TypeError::new(format!("Numeric Literal")).into())
146 }
147 }
148 _ => Err(TypeError::new(format!("Cannot coerce int to float")).into()),
149 },
150
151 other => {
152 if other == source {
153 Ok(other)
154 } else {
155 Err(TypeError::new(format!("Cannot coerce {} to {}", source, other)).into())
156 }
157 }
158 }
159}
160
161pub fn check_expression(expr: &Ast) -> Result<()> {
162 match expr {
163 Ast::Declaration {
164 name,
165 decl_type: return_type,
166 value,
167 is_constant,
168 } => {
169 check_expression(value)?;
170 coerce_type(return_type.clone(), resolve_type(value)?)?;
171 }
172 _ => {}
173 // Ast::Operation {
174 // operator,
175 // left,
176 // right,
177 // } => Ok(()),
178 // Ast::Function {
179 // name,
180 // arguments,
181 // return_type,
182 // block,
183 // } => Ok(()),
184 // Ast::FunctionCall { name, arguments } => Ok(()),
185 // Ast::If {
186 // condition,
187 // value,
188 // else_value,
189 // } => Ok(()),
190 // Ast::Return(_) => Ok(()),
191 // Ast::Public(_) => Ok(()),
192 // Ast::Block(_) => Ok(()),
193 // Ast::Number(_) => Ok(()),
194 // Ast::Bool(_) => Ok(()),
195 // Ast::String(_) => Ok(()),
196 // Ast::Identifier(_) => Ok(()),
197 // Ast::BuiltIn(_) => Ok(()),
198 // Ast::Comment(_) => Ok(()),
199 // Ast::DocComment(_) => Ok(()),
200 }
201
202 Ok(())
203}
1// use ast::{ Module, Type};
2// use miette::Result;
3
4// mod ast_type_coersion;
5
6// pub fn check_module(block: &Module) -> Result<()> {
7// for statement in &block.statements {
8// check_expression(statement)?;
9// }
10
11// Ok(())
12// }
13
14// pub fn resolve_type(expr: &Ast) -> Result<Type> {
15// match expr {
16// Ast::Number(value) => Ok(Type::NumberLiteral(value.clone())),
17// Ast::Declaration {
18// name,
19// decl_type,
20// value,
21// is_constant,
22// } => Ok(Type::NoReturn),
23// Ast::Binary {
24// operator,
25// left,
26// right,
27// } => merge_types(resolve_type(left)?, resolve_type(right)?),
28// Ast::Function {
29// name,
30// arguments,
31// return_type,
32// block,
33// } => todo!(),
34// Ast::FunctionCall { name, arguments } => todo!(),
35// Ast::If {
36// condition,
37// value,
38// else_value,
39// } => todo!(),
40// Ast::Return(_) => Ok(Type::NoReturn),
41// Ast::Public(_) => todo!(), // TODO: remove Public
42// Ast::Block(_) => todo!(),
43// Ast::Bool(_) => Ok(Type::Bool),
44// Ast::String(_) => Ok(Type::String),
45// Ast::Identifier(_) => todo!(),
46// Ast::BuiltIn(_) => todo!(),
47
48// Ast::Comment(_) => unreachable!(),
49// Ast::DocComment(_) => unreachable!(),
50// }
51// }
52
53// pub fn check_expression(expr: &Ast) -> Result<()> {
54// panic!()
55// match expr {
56// Ast::Declaration {
57// name,
58// decl_type: return_type,
59// value,
60// is_constant,
61// } => {
62// check_expression(value)?;
63// // coerce_type(return_type.clone(), resolve_type(value)?)?;
64// }
65// _ => {}
66// // Ast::Operation {
67// // operator,
68// // left,
69// // right,
70// // } => Ok(()),
71// // Ast::Function {
72// // name,
73// // arguments,
74// // return_type,
75// // block,
76// // } => Ok(()),
77// // Ast::FunctionCall { name, arguments } => Ok(()),
78// // Ast::If {
79// // condition,
80// // value,
81// // else_value,
82// // } => Ok(()),
83// // Ast::Return(_) => Ok(()),
84// // Ast::Public(_) => Ok(()),
85// // Ast::Block(_) => Ok(()),
86// // Ast::Number(_) => Ok(()),
87// // Ast::Bool(_) => Ok(()),
88// // Ast::String(_) => Ok(()),
89// // Ast::Identifier(_) => Ok(()),
90// // Ast::BuiltIn(_) => Ok(()),
91// // Ast::Comment(_) => Ok(()),
92// // Ast::DocComment(_) => Ok(()),
93// }
94
95// Ok(())
96// }
crates/cli/Cargo.toml+1
......@@ -14,6 +14,7 @@ thiserror.workspace = true
1414
1515tokenizer = { path = "../tokenizer" }
1616ast = { path = "../ast" }
17parser = { path = "../parser" }
1718js_ast = { path = "../js_ast" }
1819js_printer = { path = "../js_printer" }
1920checker = { path = "../checker" }
crates/cli/src/main.rs+18-4
......@@ -1,10 +1,8 @@
11use std::{io::Write, path::PathBuf};
22
3use checker;
43use clap::{Parser, Subcommand};
54use miette::Result;
65
7use ast;
86use js_printer;
97use tokenizer;
108
......@@ -40,6 +38,9 @@ enum Commands {
4038 #[arg(short, long, default_value = "deno")]
4139 target: String,
4240 },
41 Astdump {
42 file: PathBuf,
43 },
4344}
4445
4546fn get_runtime(runtime: &String) -> String {
......@@ -53,9 +54,9 @@ pub fn transpile(content: String) -> Result<String> {
5354 // 1. Tokenize the input
5455 let tokens = tokenizer::tokenize(content)?;
5556 // 2. Parse the tokens into an AST
56 let blight_ast = ast::parse(tokens)?;
57 let blight_ast = parser::parse(tokens)?;
5758 // 3. Type check the AST
58 checker::check_module(&blight_ast)?;
59 // checker::check_module(&blight_ast)?;
5960 // 4. Optimize the AST
6061 // optimize(tree.clone())?;
6162 // 5. Translate the AST into javascript
......@@ -66,6 +67,14 @@ pub fn transpile(content: String) -> Result<String> {
6667 return Ok(javascript);
6768}
6869
70pub fn astdump(content: String) -> Result<String> {
71 // 1. Tokenize the input
72 let tokens = tokenizer::tokenize(content)?;
73 // 2. Parse the tokens into an AST
74 let blight_ast = parser::parse(tokens)?;
75 return Ok(format!("{:#?}", blight_ast));
76}
77
6978fn main() -> Result<()> {
7079 let cli = Cli::parse();
7180
......@@ -145,6 +154,11 @@ fn main() -> Result<()> {
145154 .wait()
146155 .unwrap();
147156 }
157 Commands::Astdump { file } => {
158 let contents = std::fs::read_to_string(file.clone()).unwrap();
159 let result = astdump(contents.clone())?;
160 println!("{}", result);
161 }
148162 }
149163
150164 Ok(())
crates/js_ast/Cargo.toml-1
......@@ -11,4 +11,3 @@ thiserror.workspace = true
1111num.workspace = true
1212
1313ast = { path = "../ast" }
14util = { path = "../util" }
crates/js_ast/src/lib.rs+190-194
......@@ -1,9 +1,8 @@
11use std::collections::HashMap;
22
3use ast::{Expr, ExprData, Literal, Module, Statement};
34use miette::Result;
45
5use ast::{Ast, Module};
6
76mod types;
87pub use types::{JsExpression, JsStatement};
98
......@@ -99,68 +98,69 @@ impl TranslateContext {
9998// here, we recursively expand out expressions into statements
10099// we then store the result of all the expressions in a variable
101100// and then return the name of that variable
102fn get_expression_handle(ast: &Ast, ctx: &mut TranslateContext) -> JsExpression {
103 match ast {
104 Ast::Number(value) => JsExpression::Number(*value),
105 Ast::String(value) => JsExpression::String(value.clone()),
106 Ast::Identifier(value) => JsExpression::Identifier(ctx.get_identifier(value.clone())),
107 Ast::Bool(value) => JsExpression::Bool(*value),
108 Ast::FunctionCall { name, arguments } => {
101fn get_expression_handle(ast: &Expr, ctx: &mut TranslateContext) -> JsExpression {
102 match ast.data.clone() {
103 ExprData::Literal(Literal::String(value)) => JsExpression::String(value),
104 ExprData::Literal(Literal::Number(value)) => JsExpression::Number(value),
105 ExprData::Literal(Literal::Void) => JsExpression::Identifier("null".to_string()),
106 ExprData::Literal(Literal::Bool(value)) => JsExpression::Identifier(if value {
107 "true".to_string()
108 } else {
109 "false".to_string()
110 }),
111
112 ExprData::Identifier(value) => JsExpression::Identifier(ctx.get_identifier(value.clone())),
113 ExprData::Call { base, arguments } => {
109114 let mut handles = Vec::new();
110115 for argument in arguments {
111 handles.push(get_expression_handle(argument, ctx));
112 }
113 match *name.clone() {
114 Ast::Identifier(name) => ctx.push_internal_variable(JsExpression::FunctionCall {
115 name: Box::new(JsExpression::Identifier(ctx.get_identifier(name.clone()))),
116 arguments: handles,
117 }),
118 Ast::BuiltIn(name) => match name.as_str() {
119 "@raw" => {
120 if handles.len() != 1 {
121 panic!("@raw expects exactly one argument");
122 }
123 match handles.get(0).unwrap() {
124 JsExpression::String(value) => {
125 return JsExpression::Raw(value.clone());
126 }
127 _ => panic!("@raw expects a string argument"),
128 }
129 }
130 _ => panic!("Unknown built-in function {}", name),
131 },
132 _ => panic!("Function name must be an identifier"),
116 handles.push(get_expression_handle(&argument, ctx));
133117 }
118 let base = get_expression_handle(&base, ctx);
119 ctx.push_internal_variable(JsExpression::Call {
120 base: Box::new(base),
121 arguments: handles,
122 })
134123 }
135 Ast::Operation {
136 operator,
137 left,
138 right,
139 } => {
140 let left_handle = get_expression_handle(left, ctx);
141 let right_handle = get_expression_handle(right, ctx);
142 JsExpression::Operation {
143 operator: operator.clone(),
124 ExprData::Builtin { id, arguments } => match id {
125 ast::BuiltinId::Raw => {
126 if arguments.len() != 1 {
127 panic!("@raw expects exactly one argument");
128 }
129 match arguments.get(0).unwrap() {
130 Expr {
131 data: ExprData::Literal(Literal::String(value)),
132 ..
133 } => JsExpression::Raw(value.clone()),
134 _ => panic!("@raw expects a string literal"),
135 }
136 }
137 },
138 ExprData::Binary { op, left, right } => {
139 let left_handle = get_expression_handle(&left, ctx);
140 let right_handle = get_expression_handle(&right, ctx);
141 JsExpression::Binary {
142 operator: op.clone(),
144143 left: Box::new(left_handle),
145144 right: Box::new(right_handle),
146145 }
147146 }
148 Ast::Block(block) => {
149 if !block.implicit_return {
147 ExprData::Block(block) => {
148 if !block.has_implicit_return {
150149 panic!("Expression blocks must have an implicit return");
151150 }
152151 ctx.push_context();
153 for (i, statement) in block.value.iter().enumerate() {
154 if i == block.value.len() - 1 {
152 for (i, statement) in block.statements.iter().enumerate() {
153 if i == block.statements.len() - 1 {
155154 break;
156155 } else {
157156 translate_statement(statement, ctx);
158157 }
159158 }
160 let result = get_expression_handle(block.value.last().unwrap(), ctx);
161 let value = ctx.pop_context();
162 value.iter().for_each(|x| ctx.push(x.clone()));
163 result
159 todo!()
160 // let result = get_expression_handle(block.statements.last().unwrap(), ctx);
161 // let value = ctx.pop_context();
162 // value.iter().for_each(|x| ctx.push(x.clone()));
163 // result
164164 }
165165 _ => {
166166 panic!("unimplemented: {:?}", ast);
......@@ -168,168 +168,164 @@ fn get_expression_handle(ast: &Ast, ctx: &mut TranslateContext) -> JsExpression
168168 }
169169}
170170
171fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) {
171fn translate_statement(ast: &Statement, ctx: &mut TranslateContext) {
172172 match ast {
173 Ast::Declaration {
174 name,
175 decl_type: _,
176 value,
177 is_constant,
178 } => {
179 let handle = get_expression_handle(value, ctx);
180 let rename = ctx.declare_variable(name.clone());
173 Statement::Decl(decl) => {
174 let handle = get_expression_handle(&decl.init, ctx);
175 let rename = ctx.declare_variable(decl.name.clone());
181176 ctx.push(JsStatement::Declaration {
182177 name: rename,
183178 value: handle,
184 is_constant: is_constant.clone(),
179 is_constant: decl.is_constant.clone(),
185180 });
186181 }
187 Ast::Function {
188 name,
189 arguments,
190 return_type: _,
191 block,
192 } => {
193 let rename = ctx.declare_variable(name.clone());
194 let arguments = arguments
195 .iter()
196 .map(|x| ctx.declare_variable(x.name.clone()))
197 .collect();
182 Statement::Expr(_) => todo!(),
183 // Ast::Function {
184 // name,
185 // arguments,
186 // return_type: _,
187 // block,
188 // } => {
189 // let rename = ctx.declare_variable(name.clone());
190 // let arguments = arguments
191 // .iter()
192 // .map(|x| ctx.declare_variable(x.name.clone()))
193 // .collect();
198194
199 ctx.push_context();
200 for (i, statement) in block.value.iter().enumerate() {
201 if block.implicit_return && i == block.value.len() - 1 {
202 let handle = get_expression_handle(statement, ctx);
203 ctx.push(JsStatement::Return(handle));
204 } else {
205 translate_statement(statement, ctx);
206 }
207 }
208 let value = ctx.pop_context();
195 // ctx.push_context();
196 // for (i, statement) in block.value.iter().enumerate() {
197 // if block.implicit_return && i == block.value.len() - 1 {
198 // let handle = get_expression_handle(statement, ctx);
199 // ctx.push(JsStatement::Return(handle));
200 // } else {
201 // translate_statement(statement, ctx);
202 // }
203 // }
204 // let value = ctx.pop_context();
209205
210 ctx.push(JsStatement::Function {
211 name: rename,
212 arguments,
213 value,
214 });
215 }
216 Ast::Public(value) => {
217 translate_statement(value, ctx);
218 // I'm sorry, I just don't know how to get the original name properly... my bad
219 match ctx.peek() {
220 JsStatement::Function { name, .. } | JsStatement::Declaration { name, .. } => {
221 let original_name = name.split("$").next().unwrap().to_string();
222 ctx.push(JsStatement::Export(Box::new(JsStatement::Declaration {
223 name: original_name,
224 value: JsExpression::Identifier(name.clone()),
225 is_constant: false,
226 })));
227 }
228 _ => {
229 panic!("Cannot make non-function public");
230 }
231 }
232 }
233 Ast::Return(value) => {
234 let handle = get_expression_handle(value, ctx);
235 ctx.push(JsStatement::Return(handle));
236 }
237 Ast::Block(block) => {
238 ctx.push_context();
239 for (i, statement) in block.value.iter().enumerate() {
240 if block.implicit_return && i == block.value.len() - 1 {
241 let handle = get_expression_handle(statement, ctx);
242 ctx.push(JsStatement::Return(handle));
243 } else {
244 translate_statement(statement, ctx);
245 }
246 }
247 let value = ctx.pop_context();
248 value.iter().for_each(|x| ctx.push(x.clone()));
249 }
250 Ast::If {
251 condition,
252 value: block,
253 else_value,
254 } => {
255 let handle = get_expression_handle(condition, ctx);
206 // ctx.push(JsStatement::Function {
207 // name: rename,
208 // arguments,
209 // value,
210 // });
211 // }
212 // Ast::Public(value) => {
213 // translate_statement(value, ctx);
214 // // I'm sorry, I just don't know how to get the original name properly... my bad
215 // match ctx.peek() {
216 // JsStatement::Function { name, .. } | JsStatement::Declaration { name, .. } => {
217 // let original_name = name.split("$").next().unwrap().to_string();
218 // ctx.push(JsStatement::Export(Box::new(JsStatement::Declaration {
219 // name: original_name,
220 // value: JsExpression::Identifier(name.clone()),
221 // is_constant: true,
222 // })));
223 // }
224 // _ => {
225 // panic!("Cannot make non-function public");
226 // }
227 // }
228 // }
229 // Ast::Return(value) => {
230 // let handle = get_expression_handle(value, ctx);
231 // ctx.push(JsStatement::Return(handle));
232 // }
233 // Ast::Block(block) => {
234 // ctx.push_context();
235 // for (i, statement) in block.value.iter().enumerate() {
236 // if block.implicit_return && i == block.value.len() - 1 {
237 // let handle = get_expression_handle(statement, ctx);
238 // ctx.push(JsStatement::Return(handle));
239 // } else {
240 // translate_statement(statement, ctx);
241 // }
242 // }
243 // let value = ctx.pop_context();
244 // value.iter().for_each(|x| ctx.push(x.clone()));
245 // }
246 // Ast::If {
247 // condition,
248 // value: block,
249 // else_value,
250 // } => {
251 // let handle = get_expression_handle(condition, ctx);
256252
257 ctx.push_context();
258 for (i, statement) in block.value.iter().enumerate() {
259 if block.implicit_return && i == block.value.len() - 1 {
260 let handle = get_expression_handle(statement, ctx);
261 ctx.push(JsStatement::Return(handle));
262 } else {
263 translate_statement(statement, ctx);
264 }
265 }
266 let value = ctx.pop_context();
267 let else_value = match else_value {
268 Some(block) => {
269 ctx.push_context();
270 for (i, statement) in block.value.iter().enumerate() {
271 if block.implicit_return && i == block.value.len() - 1 {
272 let handle = get_expression_handle(statement, ctx);
273 ctx.push(JsStatement::Return(handle));
274 } else {
275 translate_statement(statement, ctx);
276 }
277 }
278 Some(ctx.pop_context())
279 }
280 None => None,
281 };
253 // ctx.push_context();
254 // for (i, statement) in block.value.iter().enumerate() {
255 // if block.implicit_return && i == block.value.len() - 1 {
256 // let handle = get_expression_handle(statement, ctx);
257 // ctx.push(JsStatement::Return(handle));
258 // } else {
259 // translate_statement(statement, ctx);
260 // }
261 // }
262 // let value = ctx.pop_context();
263 // let else_value = match else_value {
264 // Some(block) => {
265 // ctx.push_context();
266 // for (i, statement) in block.value.iter().enumerate() {
267 // if block.implicit_return && i == block.value.len() - 1 {
268 // let handle = get_expression_handle(statement, ctx);
269 // ctx.push(JsStatement::Return(handle));
270 // } else {
271 // translate_statement(statement, ctx);
272 // }
273 // }
274 // Some(ctx.pop_context())
275 // }
276 // None => None,
277 // };
282278
283 ctx.push(JsStatement::If {
284 condition: handle,
285 value,
286 else_value,
287 });
288 }
289 Ast::FunctionCall { name, arguments } => {
290 let mut handles = Vec::new();
291 for argument in arguments {
292 handles.push(get_expression_handle(argument, ctx));
293 }
294 match *name.clone() {
295 Ast::Identifier(name) => {
296 ctx.push(JsStatement::Expression(JsExpression::FunctionCall {
297 name: Box::new(JsExpression::Identifier(ctx.get_identifier(name.clone()))),
298 arguments: handles,
299 }));
300 }
301 Ast::BuiltIn(name) => match name.as_str() {
302 "@raw" => {
303 if handles.len() != 1 {
304 panic!("@raw expects exactly one argument");
305 }
306 match handles.get(0).unwrap() {
307 JsExpression::String(value) => {
308 ctx.push(JsStatement::Expression(JsExpression::FunctionCall {
309 name: Box::new(JsExpression::Raw(value.clone())),
310 arguments: handles,
311 }));
312 }
313 _ => panic!("@raw expects a string argument"),
314 }
315 }
316 _ => panic!("Unknown built-in function {}", name),
317 },
318 _ => panic!("Function name must be an identifier"),
319 }
320 }
321 Ast::Comment(_) | Ast::DocComment(_) => {}
322 _ => {
323 panic!("unimplemented: {:?}", ast);
324 }
279 // ctx.push(JsStatement::If {
280 // condition: handle,
281 // value,
282 // else_value,
283 // });
284 // }
285 // Ast::FunctionCall { name, arguments } => {
286 // let mut handles = Vec::new();
287 // for argument in arguments {
288 // handles.push(get_expression_handle(argument, ctx));
289 // }
290 // match *name.clone() {
291 // Ast::Identifier(name) => {
292 // ctx.push(JsStatement::Expression(JsExpression::Call {
293 // name: Box::new(JsExpression::Identifier(ctx.get_identifier(name.clone()))),
294 // arguments: handles,
295 // }));
296 // }
297 // Ast::BuiltIn(name) => match name.as_str() {
298 // "@raw" => {
299 // if handles.len() != 1 {
300 // panic!("@raw expects exactly one argument");
301 // }
302 // match handles.get(0).unwrap() {
303 // JsExpression::String(value) => {
304 // ctx.push(JsStatement::Expression(JsExpression::Call {
305 // name: Box::new(JsExpression::Raw(value.clone())),
306 // arguments: handles,
307 // }));
308 // }
309 // _ => panic!("@raw expects a string argument"),
310 // }
311 // }
312 // _ => panic!("Unknown built-in function {}", name),
313 // },
314 // _ => panic!("Function name must be an identifier"),
315 // }
316 // }
317 // Ast::Comment(_) | Ast::DocComment(_) => {}
318 // _ => {
319 // panic!("unimplemented: {:?}", ast);
320 // }
325321 }
326322}
327323
328324pub fn translate(module: &Module) -> Result<Vec<JsStatement>> {
329325 let mut ctx = TranslateContext::new();
330326
331 for ast in module.value.iter() {
332 translate_statement(&ast, &mut ctx);
327 for ast in module.statements.iter() {
328 translate_statement(&ast.clone().to_statement(), &mut ctx);
333329 }
334330
335331 Ok(ctx.pop_context())
crates/js_ast/src/types.rs+5-6
......@@ -1,8 +1,7 @@
1use ast::BinaryOperator;
12use num::bigint::BigInt;
23use std::collections::HashMap;
34
4use util::Operator;
5
65#[derive(Debug, PartialEq, Clone)]
76pub enum JsExpression {
87 Number(f64),
......@@ -13,8 +12,8 @@ pub enum JsExpression {
1312 Raw(String),
1413 Array(Vec<JsExpression>),
1514 Object(HashMap<String, JsExpression>),
16 Operation {
17 operator: Operator,
15 Binary {
16 operator: BinaryOperator,
1817 left: Box<JsExpression>,
1918 right: Box<JsExpression>,
2019 },
......@@ -22,8 +21,8 @@ pub enum JsExpression {
2221 arguments: Vec<String>,
2322 value: Vec<JsStatement>,
2423 },
25 FunctionCall {
26 name: Box<JsExpression>,
24 Call {
25 base: Box<JsExpression>,
2726 arguments: Vec<JsExpression>,
2827 },
2928}
crates/js_printer/Cargo.toml-1
......@@ -11,4 +11,3 @@ thiserror.workspace = true
1111num.workspace = true
1212
1313js_ast = { path = "../js_ast" }
14util = { path = "../util" }
crates/js_printer/src/print_expression.rs+10-11
......@@ -41,7 +41,7 @@ pub fn print_expression(statement: &JsExpression) -> String {
4141 .collect::<Vec<String>>()
4242 .join(",")
4343 ),
44 JsExpression::Operation {
44 JsExpression::Binary {
4545 operator,
4646 left,
4747 right,
......@@ -60,9 +60,9 @@ pub fn print_expression(statement: &JsExpression) -> String {
6060 .collect::<Vec<String>>()
6161 .join("")
6262 ),
63 JsExpression::FunctionCall { name, arguments } => format!(
63 JsExpression::Call { base, arguments } => format!(
6464 "{}({})",
65 match *name.clone() {
65 match *base.clone() {
6666 JsExpression::Identifier(value) => value.clone(),
6767 JsExpression::Raw(value) => value.clone(),
6868 _ => panic!("Function call name must be an identifier"),
......@@ -83,7 +83,6 @@ mod tests {
8383 use js_ast::JsStatement;
8484 use num::{bigint::Sign, BigInt};
8585 use std::collections::HashMap;
86 use util::Operator;
8786
8887 #[test]
8988 fn number() {
......@@ -184,16 +183,16 @@ mod tests {
184183 #[test]
185184 fn operation() {
186185 assert_eq!(
187 print_expression(&JsExpression::Operation {
188 operator: Operator::Add,
186 print_expression(&JsExpression::Binary {
187 operator: AnyOperator::Add,
189188 left: Box::new(JsExpression::Number(1.0)),
190189 right: Box::new(JsExpression::Number(2.0)),
191190 }),
192191 "1+2"
193192 );
194193 assert_eq!(
195 print_expression(&JsExpression::Operation {
196 operator: Operator::Subtract,
194 print_expression(&JsExpression::Binary {
195 operator: AnyOperator::Sub,
197196 left: Box::new(JsExpression::Number(1.0)),
198197 right: Box::new(JsExpression::Number(2.0)),
199198 }),
......@@ -242,21 +241,21 @@ mod tests {
242241 #[test]
243242 fn function_call() {
244243 assert_eq!(
245 print_expression(&JsExpression::FunctionCall {
244 print_expression(&JsExpression::Call {
246245 name: Box::new(JsExpression::Identifier("hello".to_string())),
247246 arguments: vec![],
248247 }),
249248 "hello()"
250249 );
251250 assert_eq!(
252 print_expression(&JsExpression::FunctionCall {
251 print_expression(&JsExpression::Call {
253252 name: Box::new(JsExpression::Identifier("hello".to_string())),
254253 arguments: vec![JsExpression::Number(1.0)],
255254 }),
256255 "hello(1)"
257256 );
258257 assert_eq!(
259 print_expression(&JsExpression::FunctionCall {
258 print_expression(&JsExpression::Call {
260259 name: Box::new(JsExpression::Identifier("hello".to_string())),
261260 arguments: vec![JsExpression::Number(1.0), JsExpression::Number(2.0)],
262261 }),
crates/parser/Cargo.toml created+15
......@@ -0,0 +1,15 @@
1[package]
2name = "parser"
3version = "0.1.0"
4edition = "2021"
5
6[lib]
7path = "./parser.rs"
8
9[dependencies]
10miette.workspace = true
11thiserror.workspace = true
12num.workspace = true
13
14ast = { path = "../ast" }
15tokenizer = { path = "../tokenizer" }
crates/parser/parse_block.rs created+64
......@@ -0,0 +1,64 @@
1use crate::{parse_statement::parse_statement, Parser};
2use ast::Block;
3use miette::Result;
4use tokenizer::Token;
5
6/// Precondition: The first token exists and is '{'
7pub fn parse_block(p: &mut Parser) -> Result<Block> {
8 match p.next() {
9 Some(Token::OpenBrace) => (),
10 _ => unreachable!(),
11 }
12
13 let mut statements = Vec::new();
14
15 let was_top_level = p.is_top_level;
16 p.is_top_level = false;
17
18 loop {
19 let statement = match parse_statement(p) {
20 Ok(statement) => statement,
21 Err(err) => {
22 p.is_top_level = was_top_level;
23 return Err(err);
24 }
25 };
26 statements.push(statement.clone());
27
28 match p.next() {
29 Some(Token::Semicolon) => loop {
30 match p.peek() {
31 Some(Token::ClosedBrace) => {
32 p.next();
33 p.is_top_level = was_top_level;
34 return Ok(Block {
35 statements,
36 has_implicit_return: false,
37 });
38 }
39 _ => continue,
40 }
41 },
42 Some(Token::ClosedBrace) => {
43 if statement.requires_semicolon() {
44 p.is_top_level = was_top_level;
45 return Err(p
46 .error("Expected semicolon after this statement".to_string())
47 .into());
48 }
49
50 p.is_top_level = was_top_level;
51 return Ok(Block {
52 statements,
53 has_implicit_return: true,
54 });
55 }
56 _ => {
57 p.is_top_level = was_top_level;
58 return Err(p
59 .error("Expected semicolon or '}' after statement".to_string())
60 .into());
61 }
62 };
63 }
64}
crates/parser/parse_call_arguments.rs created+31
......@@ -0,0 +1,31 @@
1use ast::Expr;
2use miette::Result;
3use tokenizer::Token;
4
5use crate::{parse_expr::parse_expr, Parser};
6
7/// Precondition: The '(' was already consumed
8pub fn parse_call_arguments(p: &mut Parser) -> Result<Vec<Expr>> {
9 let mut args = Vec::new();
10
11 loop {
12 let arg = parse_expr(p)?;
13 args.push(arg);
14
15 match p.peek() {
16 Some(Token::Comma) => {
17 _ = p.next();
18 continue;
19 }
20 Some(Token::ClosedParen) => {
21 _ = p.next();
22 return Ok(args);
23 }
24 _ => {
25 return Err(p
26 .error("Expected ',' or ')' after argument".to_string())
27 .into())
28 }
29 }
30 }
31}
crates/parser/parse_declaration.rs created+46
......@@ -0,0 +1,46 @@
1use ast::{Decl, Type};
2use miette::Result;
3use tokenizer::Token;
4
5use crate::parse_expr::parse_expr;
6use crate::parse_type::parse_type;
7use crate::Parser;
8
9/// Precondition: The first token exists and is either 'var' or 'let'
10pub fn parse_declaration(p: &mut Parser, is_pub: bool) -> Result<Decl> {
11 let loc = p.loc();
12 let is_constant = match p.next() {
13 Some(Token::Var) => false,
14 Some(Token::Let) => true,
15 _ => unreachable!(),
16 };
17 let name = match p.next() {
18 Some(Token::Identifier(name)) => name,
19 Some(tok) => return Err(p.error(format!("Unexpected {}", tok)).into()),
20 None => return Err(p.error(format!("Unexpected end of file")).into()),
21 };
22 let return_type = match p.peek() {
23 Some(Token::Colon) => {
24 p.next();
25 parse_type(p)?
26 }
27 _ => Type::Infer,
28 };
29 let init = match p.next() {
30 Some(Token::Equal) => parse_expr(p)?,
31 _ => {
32 return Err(p
33 .error("Variable must have an initializer".to_string())
34 .into())
35 }
36 };
37 Ok(Decl {
38 name: name.clone(),
39 decl_type: return_type,
40 init,
41 is_constant,
42 is_pub,
43 comment: None,
44 loc,
45 })
46}
crates/parser/parse_expr.rs created+161
......@@ -0,0 +1,161 @@
1use ast::{BinaryOperator, Expr, ExprData, Literal};
2use miette::Result;
3use tokenizer::Token;
4
5use crate::{
6 parse_call_arguments::parse_call_arguments, parse_expr_basic::parse_expr_basic, Parser,
7};
8
9/// Derived from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table
10
11#[derive(PartialEq, PartialOrd)]
12enum Level {
13 Prefix, // +, -, !, ~
14 Exponentiation, // **
15 Multiplicative, // *, /, %
16 Additive, // +, -
17 BitwiseShift, // <<, >>, >>>
18 Relational, // <, <=, >, >=
19 Equality, // ==, !=
20 BitwiseAnd, // &
21 BitwiseXor, // ^
22 BitwiseOr, // |
23 LogicalAnd, // &&
24 LogicalOr, // ||, ??
25}
26
27pub fn get_postfix_op_level(token: &Token) -> Option<Level> {
28 match token {
29 Token::StarStar => Some(Level::Exponentiation),
30 Token::Star | Token::Slash | Token::Percent => Some(Level::Multiplicative),
31 Token::Plus | Token::Minus => Some(Level::Additive),
32 Token::LtLt | Token::GtGt | Token::GtGtGt => Some(Level::BitwiseShift),
33 Token::Gt | Token::GtEqual | Token::Lt | Token::LtEqual => Some(Level::Relational),
34 Token::EqualEqual | Token::BangEqual => Some(Level::Equality),
35 Token::And => Some(Level::BitwiseAnd),
36 Token::Caret => Some(Level::BitwiseXor),
37 Token::Or => Some(Level::BitwiseOr),
38 Token::AndAnd => Some(Level::LogicalAnd),
39 Token::OrOr => Some(Level::LogicalOr),
40 _ => None,
41 }
42}
43
44pub fn parse_expr(p: &mut Parser) -> Result<Expr> {
45 let mut expr = parse_expr_basic(p)?;
46
47 // Edge case for return. This has already parsed all the way to the end of the expression
48 if let ExprData::Return(_) = expr.data {
49 return Ok(expr);
50 }
51
52 // Parse function calls and field accesses
53 loop {
54 match p.peek() {
55 Some(Token::Dot) => {
56 let loc = p.loc();
57 _ = p.next();
58 let field = match p.peek() {
59 Some(Token::Identifier(name)) => {
60 let loc = p.loc();
61 p.next();
62 Expr::new(ExprData::Literal(Literal::String(name)), loc)
63 }
64 _ => {
65 return Err(p.error("Expected identifier after dot".to_string()).into());
66 }
67 };
68 expr = Expr::new(
69 ExprData::Field {
70 base: Box::new(expr),
71 field: Box::new(field),
72 },
73 loc,
74 );
75 }
76 Some(Token::OpenParen) => {
77 let loc = p.loc();
78 _ = p.next();
79 let arguments = parse_call_arguments(p)?;
80 expr = Expr::new(
81 ExprData::Call {
82 base: Box::new(expr),
83 arguments,
84 },
85 loc,
86 );
87 }
88 Some(Token::OpenBracket) => {
89 let loc = p.loc();
90 _ = p.next();
91 let index = parse_expr(p)?;
92 if let Some(Token::ClosedBracket) = p.next() {
93 expr = Expr::new(
94 ExprData::Field {
95 base: Box::new(expr),
96 field: Box::new(index),
97 },
98 loc,
99 );
100 } else {
101 return Err(p
102 .error("Expected closing bracket after index".to_string())
103 .into());
104 }
105 }
106 _ => break,
107 }
108 }
109
110 // Parse operators
111 loop {
112 if let Some(token) = p.peek() {
113 if let Some(level) = get_postfix_op_level(&token) {
114 _ = p.next();
115 expr = parse_expr_binary(
116 p,
117 BinaryOperator::from_token(&token).unwrap(),
118 expr.clone(),
119 level,
120 )?;
121 continue;
122 }
123 }
124 break;
125 }
126
127 Ok(expr)
128}
129
130/// Prerequisite: `p.peek()` is an operator
131fn parse_expr_binary(p: &mut Parser, op: BinaryOperator, left: Expr, level: Level) -> Result<Expr> {
132 let loc = p.loc();
133 let mut right = parse_expr_basic(p)?;
134
135 loop {
136 if let Some(token) = p.peek() {
137 if let Some(next_level) = get_postfix_op_level(&token) {
138 if next_level <= level {
139 _ = p.next();
140 right = parse_expr_binary(
141 p,
142 BinaryOperator::from_token(&token).unwrap(),
143 right,
144 next_level,
145 )?;
146 continue;
147 }
148 }
149 }
150 break;
151 }
152
153 Ok(Expr::new(
154 ExprData::Binary {
155 op,
156 left: Box::new(left),
157 right: Box::new(right),
158 },
159 loc,
160 ))
161}
crates/parser/parse_expr_basic.rs created+110
......@@ -0,0 +1,110 @@
1use ast::{BuiltinId, Expr, ExprData, Literal, UnaryOperator};
2use miette::Result;
3use tokenizer::Token;
4
5use crate::{
6 parse_block::parse_block, parse_call_arguments::parse_call_arguments, parse_expr::parse_expr,
7 parse_expr_if::parse_expr_if, Parser,
8};
9
10pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
11 let loc = p.loc();
12 match p.peek() {
13 Some(Token::Number(number)) => {
14 _ = p.next();
15 Ok(Expr::new(
16 ExprData::Literal(Literal::Number(number.clone())),
17 loc,
18 ))
19 }
20 Some(Token::String(string)) => {
21 _ = p.next();
22 Ok(Expr::new(
23 ExprData::Literal(Literal::String(string.clone())),
24 loc,
25 ))
26 }
27 Some(Token::True) => {
28 _ = p.next();
29 Ok(Expr::new(ExprData::Literal(Literal::Bool(true)), loc))
30 }
31 Some(Token::False) => {
32 _ = p.next();
33 Ok(Expr::new(ExprData::Literal(Literal::Bool(false)), loc))
34 }
35 Some(Token::Void) => {
36 _ = p.next();
37 Ok(Expr::new(ExprData::Literal(Literal::Void), loc))
38 }
39 Some(Token::Identifier(name)) => {
40 let loc = p.loc();
41 _ = p.next();
42 if name.starts_with("@") {
43 if let Some(id) = BuiltinId::from_str(name.as_str()) {
44 if let Some(Token::OpenParen) = p.next() {
45 let arguments = parse_call_arguments(p)?;
46 return Ok(Expr::new(ExprData::Builtin { id, arguments }, loc));
47 } else {
48 return Err(p
49 .error_at(format!("Built-in function '{}' must be called", name), loc)
50 .into());
51 }
52 } else {
53 return Err(p
54 .error_at(format!("Non-existant built-in: '{}'", name), loc)
55 .into());
56 }
57 } else {
58 Ok(Expr::new(ExprData::Identifier(name), loc))
59 }
60 }
61
62 Some(Token::Return) => {
63 if p.is_top_level {
64 return Err(p
65 .error("'return' cannot be used at top level".to_string())
66 .into());
67 } else {
68 _ = p.next();
69
70 return Ok(Expr::new(ExprData::Return(Box::new(parse_expr(p)?)), loc));
71 }
72 }
73
74 Some(Token::If) => Ok(Expr::new(ExprData::If(parse_expr_if(p)?), loc)),
75
76 Some(Token::OpenBrace) => Ok(Expr::new(ExprData::Block(parse_block(p)?), loc)),
77
78 Some(Token::OpenParen) => {
79 _ = p.next();
80 let expr = parse_expr_basic(p)?;
81 match p.next() {
82 Some(Token::ClosedParen) => (),
83 Some(token) => {
84 return Err(p.error(format!("Expected ')', but got {:?}", token)).into())
85 }
86 None => return Err(p.error("Unexpected end of file".to_string()).into()),
87 }
88 Ok(expr)
89 }
90
91 Some(t @ Token::Bang) | Some(t @ Token::Minus) | Some(t @ Token::Tilde) => {
92 _ = p.next();
93 Ok(Expr::new(
94 ExprData::Unary {
95 op: match t {
96 Token::Bang => UnaryOperator::Not,
97 Token::Minus => UnaryOperator::Neg,
98 Token::Tilde => UnaryOperator::BitNot,
99 _ => unreachable!(),
100 },
101 value: Box::new(parse_expr_basic(p)?),
102 },
103 loc,
104 ))
105 }
106
107 Some(t) => Err(p.error(format!("Unexpected '{}'", t)).into()),
108 None => Err(p.error("Unexpected end of file".to_string()).into()),
109 }
110}
crates/parser/parse_expr_if.rs created+61
......@@ -0,0 +1,61 @@
1use ast::{BlockWithLoc, If};
2use miette::Result;
3use tokenizer::Token;
4
5use crate::{parse_block::parse_block, parse_expr::parse_expr, Parser};
6
7/// Precondition: the first token exists and is 'if'
8pub fn parse_expr_if(p: &mut Parser) -> Result<If> {
9 match p.next() {
10 Some(Token::If) => (),
11 _ => unreachable!(),
12 }
13
14 match p.peek() {
15 Some(Token::OpenBrace) => {
16 return Err(p
17 .error(
18 "To use a block as the condition for an if statement, wrap it in parentheses"
19 .to_string(),
20 )
21 .into());
22 }
23 _ => (),
24 }
25
26 let condition = Box::new(parse_expr(p)?);
27
28 let yes = match p.peek() {
29 Some(Token::OpenBrace) => {
30 let loc = p.loc();
31 BlockWithLoc {
32 block: parse_block(p)?,
33 loc,
34 }
35 }
36 _ => {
37 return Err(p
38 .error("Expected a block after condition".to_string())
39 .into())
40 }
41 };
42
43 let no = match p.peek() {
44 Some(Token::Else) => {
45 _ = p.next();
46 match p.peek() {
47 Some(Token::OpenBrace) => {
48 let loc = p.loc();
49 Some(BlockWithLoc {
50 block: parse_block(p)?,
51 loc,
52 })
53 }
54 _ => return Err(p.error("Expected a block after else".to_string()).into()),
55 }
56 }
57 _ => None,
58 };
59
60 Ok(If { condition, yes, no })
61}
crates/parser/parse_fn_declaration.rs created+111
......@@ -0,0 +1,111 @@
1use ast::{Decl, Expr, ExprData, Type};
2use miette::Result;
3use tokenizer::Token;
4
5use crate::parse_block::parse_block;
6use crate::parse_param::parse_param;
7use crate::parse_type::parse_type;
8use crate::Parser;
9
10/// Precondition:
11pub fn parse_fn_declaration(p: &mut Parser, is_pub: bool) -> Result<Decl> {
12 let loc = p.loc();
13 match p.next() {
14 Some(Token::Fn) => (),
15 _ => unreachable!(),
16 }
17
18 let name = match p.next() {
19 Some(Token::Identifier(name)) => name,
20 _ => return Err(p.error("Expected identifier after 'fn'".to_string()).into()),
21 };
22
23 match p.peek() {
24 Some(Token::OpenParen) => p.next(),
25 _ => {
26 return Err(p
27 .error("Expected '(' after function name".to_string())
28 .into())
29 }
30 };
31
32 let mut params = Vec::new();
33
34 loop {
35 match p.peek() {
36 Some(Token::ClosedParen) => {
37 _ = p.next();
38 break;
39 }
40 Some(Token::Identifier(_)) => {
41 params.push(parse_param(p)?);
42
43 match p.peek() {
44 Some(Token::Comma) => {
45 p.next();
46 if let Some(Token::ClosedParen) = p.peek() {
47 p.next();
48 break;
49 }
50 }
51 Some(Token::ClosedParen) => break,
52 _ => {
53 return Err(p
54 .error("Expected ',' or ')' after identifier".to_string())
55 .into())
56 }
57 };
58 }
59 _ => {
60 return Err(p
61 .error("Expected ')' or identifier after '('".to_string())
62 .into())
63 }
64 }
65
66 match p.peek() {
67 Some(Token::Comma) => p.next(),
68 Some(Token::ClosedParen) => {
69 _ = p.next();
70 break;
71 }
72 _ => {
73 return Err(p
74 .error("Expected ',' or ')' after identifier".to_string())
75 .into())
76 }
77 };
78 }
79
80 let return_type = match p.peek() {
81 Some(Token::RightArrow) => {
82 _ = p.next();
83 parse_type(p)?
84 }
85 _ => Type::Infer,
86 };
87
88 if let Some(Token::OpenBrace) = p.peek() {
89 let block_loc = p.loc();
90 let block = parse_block(p)?;
91 let function_body = ExprData::Fn {
92 name: name.clone(),
93 params,
94 return_type,
95 block,
96 };
97 Ok(Decl {
98 name,
99 decl_type: Type::Any,
100 init: Expr::new(function_body, block_loc),
101 is_constant: true,
102 is_pub,
103 comment: None,
104 loc: loc,
105 })
106 } else {
107 return Err(p
108 .error("Expected a block to follow function declaration".to_string())
109 .into());
110 }
111}
crates/parser/parse_module.rs created+33
......@@ -0,0 +1,33 @@
1use crate::{parse_top_level_statement::parse_top_level_statement, Parser};
2use ast::Module;
3use miette::Result;
4use tokenizer::Token;
5
6pub fn parse_module(tokens: &mut Parser) -> Result<Module> {
7 let mut statements = Vec::new();
8
9 loop {
10 let statement = parse_top_level_statement(tokens)?;
11
12 match tokens.peek() {
13 Some(Token::Semicolon) => {
14 while let Some(Token::Semicolon) = tokens.peek() {
15 tokens.next();
16 }
17 }
18 _ => {
19 if statement.requires_semicolon() {
20 return Err(tokens
21 .error("Expected semicolon after statement".to_string())
22 .into());
23 }
24 }
25 };
26
27 statements.push(statement);
28
29 if tokens.empty() {
30 return Ok(Module { statements });
31 }
32 }
33}
crates/parser/parse_param.rs created+30
......@@ -0,0 +1,30 @@
1use ast::Param;
2use miette::Result;
3use tokenizer::Token;
4
5use crate::{parse_type::parse_type, Parser};
6
7/// Precondition: A token exists and is an identifier
8pub fn parse_param(p: &mut Parser) -> Result<Param> {
9 let name = match p.next() {
10 Some(Token::Identifier(name)) => name.clone(),
11 _ => unreachable!(),
12 };
13
14 let param_type = match p.peek() {
15 Some(Token::Colon) => {
16 _ = p.next();
17 parse_type(p)?
18 }
19 _ => {
20 return Err(p
21 .error("Function parameters require type annotations".to_string())
22 .into())
23 }
24 };
25
26 Ok(Param {
27 name: name.clone(),
28 param_type,
29 })
30}
crates/parser/parse_statement.rs created+28
......@@ -0,0 +1,28 @@
1use ast::Statement;
2use miette::Result;
3use tokenizer::Token;
4
5use crate::parse_declaration::parse_declaration;
6use crate::parse_expr::parse_expr;
7use crate::parse_fn_declaration::parse_fn_declaration;
8use crate::Parser;
9
10pub fn parse_statement(p: &mut Parser) -> Result<Statement> {
11 match p.peek() {
12 Some(Token::Pub) => {
13 _ = p.next();
14 match p.peek() {
15 Some(Token::Var) | Some(Token::Let) => {
16 Ok(Statement::Decl(parse_declaration(p, true)?))
17 }
18 Some(Token::Fn) => Ok(Statement::Decl(parse_fn_declaration(p, true)?)),
19 Some(_) => Err(p.error("Expected a declaration".to_string()).into()),
20 None => Err(p.error("Unexpected end of file".to_string()).into()),
21 }
22 }
23 Some(Token::Var) | Some(Token::Let) => Ok(Statement::Decl(parse_declaration(p, false)?)),
24 Some(Token::Fn) => Ok(Statement::Decl(parse_fn_declaration(p, false)?)),
25 Some(_) => Ok(Statement::Expr(parse_expr(p)?)),
26 None => Err(p.error("Unexpected end of file".to_string()).into()),
27 }
28}
crates/parser/parse_top_level_statement.rs created+10
......@@ -0,0 +1,10 @@
1use ast::TopLevelStatement;
2use miette::Result;
3
4use crate::{parse_statement::parse_statement, Parser};
5
6/// Precondition: the next token exists
7pub fn parse_top_level_statement(p: &mut Parser) -> Result<TopLevelStatement> {
8 // TODO: we will eventually have real statements that are exclusive to top-level
9 Ok(parse_statement(p)?.to_top_level())
10}
crates/parser/parse_type.rs created+26
......@@ -0,0 +1,26 @@
1use ast::Type;
2use miette::Result;
3
4use tokenizer::Token;
5
6use crate::Parser;
7
8pub fn parse_type(p: &mut Parser) -> Result<Type> {
9 match p.next() {
10 Some(Token::Identifier(id)) => match id.as_str() {
11 "int" => Ok(Type::Int),
12 "float" => Ok(Type::Float),
13 "bigint" => Ok(Type::BigInt),
14 "bool" => Ok(Type::Bool),
15 "string" => Ok(Type::String),
16 invalid_type => Err(p
17 .error(format!(
18 "Invalid type '{}' (TODO: add custom type support)",
19 invalid_type
20 ))
21 .into()),
22 },
23 Some(unexpected) => Err(p.error(format!("Expected type, got {}", unexpected)).into()),
24 None => Err(p.error("Expected type, got EOF".to_string()).into()),
25 }
26}
crates/parser/parser.rs created+104
......@@ -0,0 +1,104 @@
1mod parse_block;
2mod parse_call_arguments;
3mod parse_declaration;
4mod parse_expr;
5mod parse_expr_basic;
6mod parse_expr_if;
7mod parse_fn_declaration;
8mod parse_module;
9mod parse_param;
10mod parse_statement;
11mod parse_top_level_statement;
12mod parse_type;
13
14use tokenizer::{Loc, Token};
15
16use miette::Diagnostic;
17use thiserror::Error;
18
19use ast::Module;
20use miette::Result;
21use parse_module::parse_module;
22use tokenizer::tokenize;
23
24pub fn parse(tokens: Vec<Token>) -> Result<Module> {
25 let mut p = Parser::new(tokens);
26 parse_module(&mut p)
27}
28
29pub fn parse_from_string(string: String) -> Result<Module> {
30 let tokens = tokenize(string)?;
31 parse(tokens)
32}
33
34#[derive(Error, Diagnostic, Debug)]
35#[error("ParseError")]
36pub struct ParseError {
37 #[help]
38 error: String,
39 loc: Option<Loc>,
40}
41
42impl ParseError {
43 pub fn new(error: String, loc: Option<Loc>) -> ParseError {
44 ParseError { error, loc }
45 }
46}
47
48#[derive(Debug)]
49pub struct Parser {
50 pub tokens: Vec<Token>,
51 pub cursor: usize,
52
53 /// Whether or not the parser is at the top level
54 pub is_top_level: bool,
55}
56
57impl Parser {
58 pub fn new(tokens: Vec<Token>) -> Parser {
59 Parser {
60 tokens,
61 cursor: 0,
62 is_top_level: true,
63 }
64 }
65
66 pub fn empty(&self) -> bool {
67 self.cursor == self.tokens.len()
68 }
69
70 pub fn back(&mut self) {
71 self.cursor -= 1;
72 }
73
74 pub fn next(&mut self) -> Option<Token> {
75 if self.cursor == self.tokens.len() {
76 return None;
77 }
78
79 let token = self.tokens[self.cursor].clone();
80 self.cursor += 1;
81 Some(token)
82 }
83
84 pub fn peek(&self) -> Option<Token> {
85 if self.cursor == self.tokens.len() {
86 return None;
87 }
88
89 Some(self.tokens[self.cursor].clone())
90 }
91
92 pub fn error(&self, error: String) -> ParseError {
93 // TODO(@lino-levan): Add location information
94 ParseError::new(error, None)
95 }
96
97 fn loc(&self) -> Loc {
98 return Loc {};
99 }
100
101 fn error_at(&self, error: String, loc: Loc) -> ParseError {
102 ParseError::new(error, Some(loc))
103 }
104}
crates/tokenizer/Cargo.toml-2
......@@ -8,5 +8,3 @@ edition = "2021"
88[dependencies]
99miette.workspace = true
1010thiserror.workspace = true
11
12util = { path = "../util" }
crates/tokenizer/src/lib.rs+150-64
......@@ -1,7 +1,8 @@
11use miette::{Diagnostic, Result};
22use thiserror::Error;
33
4use util::Operator;
4#[derive(Debug, Clone)]
5pub struct Loc {}
56
67#[derive(Error, Diagnostic, Debug)]
78#[error("UnknownCharacter")]
......@@ -16,69 +17,118 @@ pub enum Token {
1617 Identifier(String),
1718 Number(f64),
1819 String(String),
19 BuiltIn(String),
2020 // Comments
2121 Comment(String),
2222 DocComment(String),
2323 // Keywords
24 True,
24 Else,
2525 False,
26 Let,
27 Var,
26 Fn,
2827 If,
29 Else,
30 Return,
28 Let,
3129 Pub,
32 Function,
33 RightArrow,
34 Comma,
35 OpenParanthesis,
36 ClosedParanthesis,
37 OpenBracket,
38 ClosedBracket,
39 Colon,
40 Equals,
41 Semicolon,
42 // Operators
43 Operator(Operator),
30 Return,
31 True,
32 Var,
33 Void,
34 // Symbols
35 And, // &
36 AndAnd, // &&
37 Bang, // !
38 BangEqual, // !=
39 Caret, // ^
40 ClosedBrace, // }
41 ClosedBracket, // ]
42 ClosedParen, // )
43 Colon, // :
44 Comma, // ,
45 Equal, // =
46 EqualEqual, // ==
47 Gt, // >
48 GtEqual, // >=
49 GtGt, // >>
50 GtGtGt, // >>>
51 Lt, // <
52 LtEqual, // <=
53 LtLt, // <<
54 Minus, // -
55 OpenBrace, // {
56 OpenBracket, // [
57 OpenParen, // (
58 Or, // |
59 OrOr, // ||
60 Percent, // %
61 Plus, // +
62 RightArrow, // ->
63 Semicolon, // ;
64 Slash, // /
65 Star, // *
66 StarStar, // **
67 Tilde, // ~
68 Dot, // .
4469}
4570
4671impl std::fmt::Display for Token {
4772 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4873 let token = match self {
4974 Token::Identifier(_) => "an identifier".to_string(),
75
5076 Token::Number(n) => n.to_string(),
5177 Token::String(s) => format!("\"{}\"", s),
52 Token::BuiltIn(_) => "a builtin identifier".to_string(),
5378 Token::Comment(_) => "a comment".to_string(),
5479 Token::DocComment(_) => "a documentation comment".to_string(),
55 Token::True => "true".to_string(),
80
81 Token::Else => "else".to_string(),
5682 Token::False => "false".to_string(),
57 Token::Let => "let".to_string(),
58 Token::Var => "var".to_string(),
83 Token::Fn => "fn".to_string(),
5984 Token::If => "if".to_string(),
60 Token::Else => "else".to_string(),
61 Token::Return => "return".to_string(),
85 Token::Let => "let".to_string(),
6286 Token::Pub => "pub".to_string(),
63 Token::Function => "fn".to_string(),
64 Token::RightArrow => "->".to_string(),
65 Token::Comma => ",".to_string(),
66 Token::OpenParanthesis => "(".to_string(),
67 Token::ClosedParanthesis => ")".to_string(),
68 Token::OpenBracket => "{".to_string(),
69 Token::ClosedBracket => "}".to_string(),
87 Token::Return => "return".to_string(),
88 Token::True => "true".to_string(),
89 Token::Var => "var".to_string(),
90 Token::Void => "void".to_string(),
91
92 Token::And => "&".to_string(),
93 Token::AndAnd => "&&".to_string(),
94 Token::Bang => "!".to_string(),
95 Token::BangEqual => "!=".to_string(),
96 Token::Caret => "^".to_string(),
97 Token::ClosedBrace => "}".to_string(),
98 Token::ClosedBracket => "]".to_string(),
99 Token::ClosedParen => ")".to_string(),
70100 Token::Colon => ":".to_string(),
71 Token::Equals => "=".to_string(),
101 Token::Comma => ",".to_string(),
102 Token::Equal => "=".to_string(),
103 Token::EqualEqual => "==".to_string(),
104 Token::Gt => ">".to_string(),
105 Token::GtEqual => ">=".to_string(),
106 Token::GtGt => ">>".to_string(),
107 Token::Lt => "<".to_string(),
108 Token::LtEqual => "<=".to_string(),
109 Token::LtLt => "<<".to_string(),
110 Token::Minus => "-".to_string(),
111 Token::OpenBrace => "{".to_string(),
112 Token::OpenBracket => "[".to_string(),
113 Token::OpenParen => "(".to_string(),
114 Token::Or => "|".to_string(),
115 Token::OrOr => "||".to_string(),
116 Token::Percent => "%".to_string(),
117 Token::Plus => "+".to_string(),
118 Token::RightArrow => "->".to_string(),
72119 Token::Semicolon => ";".to_string(),
73 Token::Operator(o) => o.clone().to_string(),
120 Token::Slash => "/".to_string(),
121 Token::Star => "*".to_string(),
122 Token::StarStar => "**".to_string(),
123 Token::Tilde => "~".to_string(),
124 Token::Dot => ".".to_string(),
125 Token::GtGtGt => ">>>".to_string(),
74126 };
75127
76128 write!(f, "{}", token)
77129 }
78130}
79131
80
81
82132pub fn tokenize(content: String) -> Result<Vec<Token>> {
83133 let mut tokens: Vec<Token> = Vec::new();
84134 let mut chars = content.chars().enumerate().peekable();
......@@ -105,22 +155,17 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
105155 }
106156
107157 match current_token.as_str() {
108 "var" => tokens.push(Token::Var),
109 "let" => tokens.push(Token::Let),
110 "if" => tokens.push(Token::If),
111158 "else" => tokens.push(Token::Else),
159 "false" => tokens.push(Token::False),
160 "fn" => tokens.push(Token::Fn),
161 "if" => tokens.push(Token::If),
162 "let" => tokens.push(Token::Let),
112163 "pub" => tokens.push(Token::Pub),
113 "fn" => tokens.push(Token::Function),
114164 "return" => tokens.push(Token::Return),
115165 "true" => tokens.push(Token::True),
116 "false" => tokens.push(Token::False),
117 _ => {
118 if current_token.starts_with('@') {
119 tokens.push(Token::BuiltIn(current_token));
120 } else {
121 tokens.push(Token::Identifier(current_token))
122 }
123 }
166 "var" => tokens.push(Token::Var),
167 "void" => tokens.push(Token::Void),
168 _ => tokens.push(Token::Identifier(current_token)),
124169 }
125170 }
126171 '"' => {
......@@ -169,39 +214,53 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
169214 '=' => match chars.peek() {
170215 Some((_, '=')) => {
171216 chars.next();
172 tokens.push(Token::Operator(Operator::Equals));
217 tokens.push(Token::EqualEqual);
173218 }
174 _ => tokens.push(Token::Equals),
219 _ => tokens.push(Token::Equal),
175220 },
176221 ';' => tokens.push(Token::Semicolon),
177222 '>' => match chars.peek() {
178223 Some((_, '=')) => {
179224 chars.next();
180 tokens.push(Token::Operator(Operator::GreaterThanOrEquals));
225 tokens.push(Token::GtEqual);
181226 }
182 _ => tokens.push(Token::Operator(Operator::GreaterThan)),
227 Some((_, '>')) => {
228 chars.next();
229 match chars.peek() {
230 Some((_, '>')) => {
231 chars.next();
232 tokens.push(Token::GtGtGt);
233 }
234 _ => tokens.push(Token::GtGt),
235 }
236 }
237 _ => tokens.push(Token::Gt),
183238 },
184239 '<' => match chars.peek() {
185240 Some((_, '=')) => {
186241 chars.next();
187 tokens.push(Token::Operator(Operator::LessThanOrEquals));
242 tokens.push(Token::LtEqual);
243 }
244 Some((_, '<')) => {
245 chars.next();
246 tokens.push(Token::LtLt);
188247 }
189 _ => tokens.push(Token::Operator(Operator::LessThan)),
248 _ => tokens.push(Token::Lt),
190249 },
191 '+' => tokens.push(Token::Operator(Operator::Add)),
250 '+' => tokens.push(Token::Plus),
192251 '-' => match chars.peek() {
193252 Some((_, '>')) => {
194253 chars.next();
195254 tokens.push(Token::RightArrow);
196255 }
197 _ => tokens.push(Token::Operator(Operator::Subtract)),
256 _ => tokens.push(Token::Minus),
198257 },
199258 '*' => match chars.peek() {
200259 Some((_, '*')) => {
201260 chars.next();
202 tokens.push(Token::Operator(Operator::Power));
261 tokens.push(Token::StarStar);
203262 }
204 _ => tokens.push(Token::Operator(Operator::Multiply)),
263 _ => tokens.push(Token::Star),
205264 },
206265 '/' => {
207266 match chars.peek() {
......@@ -239,18 +298,45 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
239298 tokens.push(Token::Comment(comment));
240299 }
241300 }
242 _ => tokens.push(Token::Operator(Operator::Divide)),
301 _ => tokens.push(Token::Slash),
243302 }
244303 }
245304 ',' => tokens.push(Token::Comma),
246 '(' => tokens.push(Token::OpenParanthesis),
247 ')' => tokens.push(Token::ClosedParanthesis),
248 '{' => tokens.push(Token::OpenBracket),
249 '}' => tokens.push(Token::ClosedBracket),
305 '(' => tokens.push(Token::OpenParen),
306 ')' => tokens.push(Token::ClosedParen),
307 '{' => tokens.push(Token::OpenBrace),
308 '}' => tokens.push(Token::ClosedBrace),
309 '[' => tokens.push(Token::OpenBracket),
310 ']' => tokens.push(Token::ClosedBracket),
311 '&' => match chars.peek() {
312 Some((_, '&')) => {
313 chars.next();
314 tokens.push(Token::AndAnd);
315 }
316 _ => tokens.push(Token::And),
317 },
318 '|' => match chars.peek() {
319 Some((_, '|')) => {
320 chars.next();
321 tokens.push(Token::OrOr);
322 }
323 _ => tokens.push(Token::Or),
324 },
325 '!' => match chars.peek() {
326 Some((_, '=')) => {
327 chars.next();
328 tokens.push(Token::BangEqual);
329 }
330 _ => tokens.push(Token::Bang),
331 },
332 '^' => tokens.push(Token::Caret),
333 '%' => tokens.push(Token::Percent),
334 '~' => tokens.push(Token::Tilde),
335 '.' => tokens.push(Token::Dot),
250336 s if s.is_whitespace() => (),
251337 _ => {
252338 return Err(UnknownCharacter {
253 error: format!("Unsupported character: {}", c),
339 error: format!("Unexpected '{}' while tokenizing", c),
254340 }
255341 .into())
256342 }
crates/util/Cargo.toml deleted-8
......@@ -1,8 +0,0 @@
1[package]
2name = "util"
3version = "0.1.0"
4edition = "2021"
5
6[lib]
7
8[dependencies]
crates/util/src/lib.rs deleted-30
......@@ -1,30 +0,0 @@
1#[derive(Debug, Clone, PartialEq)]
2pub enum Operator {
3 Add,
4 Subtract,
5 Multiply,
6 Divide,
7 Power,
8 Equals,
9 GreaterThan,
10 LessThan,
11 GreaterThanOrEquals,
12 LessThanOrEquals,
13}
14
15impl Operator {
16 pub fn to_string(self) -> String {
17 String::from(match self {
18 Self::Add => "+",
19 Self::Subtract => "-",
20 Self::Divide => "/",
21 Self::Multiply => "*",
22 Self::Power => "**",
23 Self::Equals => "===",
24 Self::GreaterThan => ">",
25 Self::LessThan => "<",
26 Self::GreaterThanOrEquals => ">=",
27 Self::LessThanOrEquals => "<=",
28 })
29 }
30}
examples/basics/control.blight+1-17
......@@ -1,17 +1 @@
1let print = @raw("console.log");
2
3let x: bool = true;
4
5if x {
6 print("x is true!");
7}
8
9let y: int = 3;
10
11if y == 1 {
12 print("y is 1!");
13} else if y == 2 {
14 print("y is 2!");
15} else {
16 print("y is not 1 or 2!");
17}
1let x: bool = 1 * 3 + 2 / 2 == 4 + 2;