authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-01-20 19:08:18-08:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-01-20 19:08:18-08:00
logc89bf26922f4448e6f0cb6015f4627792841deab
treede76df5e71a1116de15edc93e1fe53156978c38b
parent17c6ef0255b85120d2bdcae9f48ecd8c88c3c247
signature Commit is signed but in an unrecognized format.

parser changes


24 files changed, 549 insertions(+), 363 deletions(-)

Cargo.lock+11-11
......@@ -126,16 +126,6 @@ version = "1.0.0"
126126source = "registry+https://github.com/rust-lang/crates.io-index"
127127checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
128128
129[[package]]
130name = "checker"
131version = "0.1.0"
132dependencies = [
133 "ast",
134 "miette",
135 "num",
136 "thiserror",
137]
138
139129[[package]]
140130name = "clap"
141131version = "4.4.14"
......@@ -181,12 +171,12 @@ name = "cli"
181171version = "0.1.0"
182172dependencies = [
183173 "ast",
184 "checker",
185174 "clap",
186175 "js_ast",
187176 "js_printer",
188177 "miette",
189178 "parser",
179 "sema",
190180 "thiserror",
191181 "tokenizer",
192182]
......@@ -466,6 +456,16 @@ dependencies = [
466456 "windows-sys",
467457]
468458
459[[package]]
460name = "sema"
461version = "0.1.0"
462dependencies = [
463 "ast",
464 "miette",
465 "num",
466 "thiserror",
467]
468
469469[[package]]
470470name = "smawk"
471471version = "0.3.2"
Cargo.toml+1-1
......@@ -10,7 +10,7 @@ repository = "https://github.com/lino-levan/blight"
1010resolver = "2"
1111members = [
1212 "crates/ast",
13 "crates/checker",
13 "crates/sema",
1414 "crates/cli",
1515 "crates/js_ast",
1616 "crates/js_printer",
crates/ast/ast.rs+38-3
......@@ -12,6 +12,7 @@ pub enum BuiltinId {
1212
1313#[derive(Debug, Clone)]
1414pub struct Module {
15 pub comment: Option<DocComment>,
1516 pub statements: Vec<TopLevelStatement>,
1617}
1718
......@@ -31,7 +32,7 @@ pub struct Param {
3132
3233pub struct Decl {
3334 pub name: String,
34 pub decl_type: Type,
35 pub data_type: Type,
3536 pub init: Expr,
3637 pub is_constant: bool,
3738 pub is_pub: bool,
......@@ -43,11 +44,12 @@ pub struct Decl {
4344pub struct Expr {
4445 pub data: ExprData,
4546 pub loc: Loc,
47 pub comment: Option<Comment>,
4648}
4749
4850impl Expr {
49 pub fn new(data: ExprData, loc: Loc) -> Self {
50 Self { data, loc }
51 pub fn new(data: ExprData, loc: Loc, comment: Option<Comment>) -> Self {
52 Self { data, loc, comment }
5153 }
5254}
5355
......@@ -57,6 +59,35 @@ pub struct DocComment {
5759 pub loc: Loc,
5860}
5961
62#[derive(Debug, Clone)]
63pub struct Comment {
64 pub data: String,
65}
66
67impl Comment {
68 pub fn format(&self) -> String {
69 let mut lines = self.data.lines();
70 let first_line = lines.next().unwrap();
71 let mut result = format!("// {}", first_line);
72 for line in lines {
73 result.push_str(&format!("\n// {}", line));
74 }
75 result
76 }
77}
78
79impl DocComment {
80 pub fn format(&self) -> String {
81 let mut lines = self.data.lines();
82 let first_line = lines.next().unwrap();
83 let mut result = format!("/// {}", first_line);
84 for line in lines {
85 result.push_str(&format!("\n/// {}", line));
86 }
87 result
88 }
89}
90
6091#[derive(Debug, Clone)]
6192pub struct BlockWithLoc {
6293 pub block: Block,
......@@ -110,12 +141,14 @@ pub enum ExprData {
110141pub enum Statement {
111142 Expr(Expr),
112143 Decl(Decl),
144 Comment(Comment),
113145}
114146
115147#[derive(Debug, Clone)]
116148pub enum TopLevelStatement {
117149 Expr(Expr),
118150 Decl(Decl),
151 Comment(Comment),
119152}
120153
121154impl BuiltinId {
......@@ -149,6 +182,7 @@ impl Statement {
149182 match self {
150183 Statement::Expr(expr) => TopLevelStatement::Expr(expr),
151184 Statement::Decl(decl) => TopLevelStatement::Decl(decl),
185 Statement::Comment(comment) => TopLevelStatement::Comment(comment),
152186 }
153187 }
154188}
......@@ -169,6 +203,7 @@ impl TopLevelStatement {
169203 match self {
170204 TopLevelStatement::Expr(expr) => Statement::Expr(expr),
171205 TopLevelStatement::Decl(decl) => Statement::Decl(decl),
206 TopLevelStatement::Comment(comment) => Statement::Comment(comment),
172207 }
173208 }
174209}
crates/ast/operators.rs+34
......@@ -169,3 +169,37 @@ impl UnaryOperator {
169169 }
170170 }
171171}
172
173/// Taken from from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table
174#[derive(PartialEq, PartialOrd)]
175pub enum BinaryOperatorLevel {
176 Exponentiation, // **
177 Multiplicative, // *, /, %
178 Additive, // +, -
179 BitwiseShift, // <<, >>, >>>
180 Relational, // <, <=, >, >=
181 Equality, // ==, !=
182 BitwiseAnd, // &
183 BitwiseXor, // ^
184 BitwiseOr, // |
185 LogicalAnd, // &&
186 LogicalOr, // ||, ??
187}
188
189pub fn get_binary_op_level(token: &Token) -> Option<BinaryOperatorLevel> {
190 use BinaryOperatorLevel::*;
191 match token {
192 Token::StarStar => Some(Exponentiation),
193 Token::Star | Token::Slash | Token::Percent => Some(Multiplicative),
194 Token::Plus | Token::Minus => Some(Additive),
195 Token::LtLt | Token::GtGt | Token::GtGtGt => Some(BitwiseShift),
196 Token::Gt | Token::GtEqual | Token::Lt | Token::LtEqual => Some(Relational),
197 Token::EqualEqual | Token::BangEqual => Some(Equality),
198 Token::And => Some(BitwiseAnd),
199 Token::Caret => Some(BitwiseXor),
200 Token::Or => Some(BitwiseOr),
201 Token::AndAnd => Some(LogicalAnd),
202 Token::OrOr => Some(LogicalOr),
203 _ => None,
204 }
205}
crates/checker/Cargo.toml deleted-13
......@@ -1,13 +0,0 @@
1[package]
2name = "checker"
3version = "0.1.0"
4edition = "2021"
5
6[lib]
7
8[dependencies]
9miette.workspace = true
10thiserror.workspace = true
11num.workspace = true
12
13ast = { path = "../ast" }
\ No newline at end of file
crates/checker/src/ast_type_coersion.rs deleted-54
......@@ -1,54 +0,0 @@
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 deleted-96
......@@ -1,96 +0,0 @@
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-1
......@@ -17,4 +17,4 @@ ast = { path = "../ast" }
1717parser = { path = "../parser" }
1818js_ast = { path = "../js_ast" }
1919js_printer = { path = "../js_printer" }
20checker = { path = "../checker" }
20sema = { path = "../sema" }
crates/cli/src/main.rs+1-1
......@@ -56,7 +56,7 @@ pub fn transpile(content: String) -> Result<String> {
5656 // 2. Parse the tokens into an AST
5757 let blight_ast = parser::parse(tokens)?;
5858 // 3. Type check the AST
59 // checker::check_module(&blight_ast)?;
59 // sema::check_module(&blight_ast)?;
6060 // 4. Optimize the AST
6161 // optimize(tree.clone())?;
6262 // 5. Translate the AST into javascript
crates/js_ast/src/lib.rs+1
......@@ -180,6 +180,7 @@ fn translate_statement(ast: &Statement, ctx: &mut TranslateContext) {
180180 });
181181 }
182182 Statement::Expr(_) => todo!(),
183 Statement::Comment(_) => todo!(),
183184 // Ast::Function {
184185 // name,
185186 // arguments,
crates/parser/parse_declaration.rs+8-4
......@@ -1,4 +1,4 @@
1use ast::{Decl, Type};
1use ast::{Decl, DocComment, Type};
22use miette::Result;
33use tokenizer::Token;
44
......@@ -7,7 +7,11 @@ use crate::parse_type::parse_type;
77use crate::Parser;
88
99/// Precondition: The first token exists and is either 'var' or 'let'
10pub fn parse_declaration(p: &mut Parser, is_pub: bool) -> Result<Decl> {
10pub fn parse_declaration(
11 p: &mut Parser,
12 is_pub: bool,
13 comment: Option<DocComment>,
14) -> Result<Decl> {
1115 let loc = p.loc();
1216 let is_constant = match p.next() {
1317 Some(Token::Var) => false,
......@@ -36,11 +40,11 @@ pub fn parse_declaration(p: &mut Parser, is_pub: bool) -> Result<Decl> {
3640 };
3741 Ok(Decl {
3842 name: name.clone(),
39 decl_type: return_type,
43 data_type: return_type,
4044 init,
4145 is_constant,
4246 is_pub,
43 comment: None,
47 comment,
4448 loc,
4549 })
4650}
crates/parser/parse_doc_comment.rs created+15
......@@ -0,0 +1,15 @@
1use ast::DocComment;
2use tokenizer::Token;
3
4use crate::Parser;
5
6pub fn maybe_read_doc_comment(p: &mut Parser) -> Option<DocComment> {
7 match p.peek() {
8 Some(Token::DocComment(data)) => {
9 let loc = p.loc();
10 p.next();
11 Some(DocComment { data, loc })
12 }
13 _ => None,
14 }
15}
crates/parser/parse_expr.rs+12-101
......@@ -1,45 +1,8 @@
1use ast::{BinaryOperator, Expr, ExprData, Literal};
1use ast::{get_binary_op_level, BinaryOperator, BinaryOperatorLevel, Expr, ExprData};
22use miette::Result;
3use tokenizer::Token;
43
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}
4use crate::parse_expr_basic::parse_expr_basic;
5use crate::Parser;
436
447pub fn parse_expr(p: &mut Parser) -> Result<Expr> {
458 let mut expr = parse_expr_basic(p)?;
......@@ -49,68 +12,10 @@ pub fn parse_expr(p: &mut Parser) -> Result<Expr> {
4912 return Ok(expr);
5013 }
5114
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
11015 // Parse operators
11116 loop {
11217 if let Some(token) = p.peek() {
113 if let Some(level) = get_postfix_op_level(&token) {
18 if let Some(level) = get_binary_op_level(&token) {
11419 _ = p.next();
11520 expr = parse_expr_binary(
11621 p,
......@@ -128,13 +33,18 @@ pub fn parse_expr(p: &mut Parser) -> Result<Expr> {
12833}
12934
13035/// Prerequisite: `p.peek()` is an operator
131fn parse_expr_binary(p: &mut Parser, op: BinaryOperator, left: Expr, level: Level) -> Result<Expr> {
36fn parse_expr_binary(
37 p: &mut Parser,
38 op: BinaryOperator,
39 left: Expr,
40 level: BinaryOperatorLevel,
41) -> Result<Expr> {
13242 let loc = p.loc();
13343 let mut right = parse_expr_basic(p)?;
13444
13545 loop {
13646 if let Some(token) = p.peek() {
137 if let Some(next_level) = get_postfix_op_level(&token) {
47 if let Some(next_level) = get_binary_op_level(&token) {
13848 if next_level <= level {
13949 _ = p.next();
14050 right = parse_expr_binary(
......@@ -157,5 +67,6 @@ fn parse_expr_binary(p: &mut Parser, op: BinaryOperator, left: Expr, level: Leve
15767 right: Box::new(right),
15868 },
15969 loc,
70 None,
16071 ))
16172}
crates/parser/parse_expr_basic.rs+90-20
......@@ -9,32 +9,35 @@ use crate::{
99
1010pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
1111 let loc = p.loc();
12 match p.peek() {
12 let comments = p.take_comments();
13 let mut expr = match p.peek() {
1314 Some(Token::Number(number)) => {
1415 _ = p.next();
15 Ok(Expr::new(
16 Expr::new(
1617 ExprData::Literal(Literal::Number(number.clone())),
1718 loc,
18 ))
19 comments,
20 )
1921 }
2022 Some(Token::String(string)) => {
2123 _ = p.next();
22 Ok(Expr::new(
24 Expr::new(
2325 ExprData::Literal(Literal::String(string.clone())),
2426 loc,
25 ))
27 comments,
28 )
2629 }
2730 Some(Token::True) => {
2831 _ = p.next();
29 Ok(Expr::new(ExprData::Literal(Literal::Bool(true)), loc))
32 Expr::new(ExprData::Literal(Literal::Bool(true)), loc, comments)
3033 }
3134 Some(Token::False) => {
3235 _ = p.next();
33 Ok(Expr::new(ExprData::Literal(Literal::Bool(false)), loc))
36 Expr::new(ExprData::Literal(Literal::Bool(false)), loc, comments)
3437 }
3538 Some(Token::Void) => {
3639 _ = p.next();
37 Ok(Expr::new(ExprData::Literal(Literal::Void), loc))
40 Expr::new(ExprData::Literal(Literal::Void), loc, comments)
3841 }
3942 Some(Token::Identifier(name)) => {
4043 let loc = p.loc();
......@@ -43,7 +46,7 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
4346 if let Some(id) = BuiltinId::from_str(name.as_str()) {
4447 if let Some(Token::OpenParen) = p.next() {
4548 let arguments = parse_call_arguments(p)?;
46 return Ok(Expr::new(ExprData::Builtin { id, arguments }, loc));
49 Expr::new(ExprData::Builtin { id, arguments }, loc, comments)
4750 } else {
4851 return Err(p
4952 .error_at(format!("Built-in function '{}' must be called", name), loc)
......@@ -55,7 +58,7 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
5558 .into());
5659 }
5760 } else {
58 Ok(Expr::new(ExprData::Identifier(name), loc))
61 Expr::new(ExprData::Identifier(name), loc, comments)
5962 }
6063 }
6164
......@@ -66,18 +69,21 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
6669 .into());
6770 } else {
6871 _ = p.next();
69
70 return Ok(Expr::new(ExprData::Return(Box::new(parse_expr(p)?)), loc));
72 return Ok(Expr::new(
73 ExprData::Return(Box::new(parse_expr(p)?)),
74 loc,
75 comments,
76 ));
7177 }
7278 }
7379
74 Some(Token::If) => Ok(Expr::new(ExprData::If(parse_expr_if(p)?), loc)),
80 Some(Token::If) => Expr::new(ExprData::If(parse_expr_if(p)?), loc, comments),
7581
76 Some(Token::OpenBrace) => Ok(Expr::new(ExprData::Block(parse_block(p)?), loc)),
82 Some(Token::OpenBrace) => Expr::new(ExprData::Block(parse_block(p)?), loc, comments),
7783
7884 Some(Token::OpenParen) => {
7985 _ = p.next();
80 let expr = parse_expr_basic(p)?;
86 let expr = parse_expr(p)?;
8187 match p.next() {
8288 Some(Token::ClosedParen) => (),
8389 Some(token) => {
......@@ -85,12 +91,12 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
8591 }
8692 None => return Err(p.error("Unexpected end of file".to_string()).into()),
8793 }
88 Ok(expr)
94 expr
8995 }
9096
9197 Some(t @ Token::Bang) | Some(t @ Token::Minus) | Some(t @ Token::Tilde) => {
9298 _ = p.next();
93 Ok(Expr::new(
99 Expr::new(
94100 ExprData::Unary {
95101 op: match t {
96102 Token::Bang => UnaryOperator::Not,
......@@ -101,10 +107,74 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
101107 value: Box::new(parse_expr_basic(p)?),
102108 },
103109 loc,
104 ))
110 comments,
111 )
105112 }
106113
107 Some(t) => Err(p.error(format!("Unexpected '{}'", t)).into()),
108 None => Err(p.error("Unexpected end of file".to_string()).into()),
114 Some(t) => return Err(p.error(format!("Unexpected '{}'", t)).into()),
115 None => return Err(p.error("Unexpected end of file".to_string()).into()),
116 };
117
118 // Parse function calls and field accesses
119 loop {
120 match p.peek() {
121 Some(Token::Dot) => {
122 let loc = p.loc();
123 _ = p.next();
124 let field = match p.peek() {
125 Some(Token::Identifier(name)) => {
126 let loc = p.loc();
127 p.next();
128 Expr::new(ExprData::Literal(Literal::String(name)), loc, None)
129 }
130 _ => {
131 return Err(p.error("Expected identifier after dot".to_string()).into());
132 }
133 };
134 expr = Expr::new(
135 ExprData::Field {
136 base: Box::new(expr),
137 field: Box::new(field),
138 },
139 loc,
140 None,
141 );
142 }
143 Some(Token::OpenParen) => {
144 let loc = p.loc();
145 _ = p.next();
146 let arguments = parse_call_arguments(p)?;
147 expr = Expr::new(
148 ExprData::Call {
149 base: Box::new(expr),
150 arguments,
151 },
152 loc,
153 None,
154 );
155 }
156 Some(Token::OpenBracket) => {
157 let loc = p.loc();
158 _ = p.next();
159 let index = parse_expr(p)?;
160 if let Some(Token::ClosedBracket) = p.next() {
161 expr = Expr::new(
162 ExprData::Field {
163 base: Box::new(expr),
164 field: Box::new(index),
165 },
166 loc,
167 None,
168 );
169 } else {
170 return Err(p
171 .error("Expected closing bracket after index".to_string())
172 .into());
173 }
174 }
175 _ => break,
176 }
109177 }
178
179 Ok(expr)
110180}
crates/parser/parse_expr_if.rs+3-1
......@@ -2,7 +2,9 @@ use ast::{BlockWithLoc, If};
22use miette::Result;
33use tokenizer::Token;
44
5use crate::{parse_block::parse_block, parse_expr::parse_expr, Parser};
5use crate::parse_block::parse_block;
6use crate::parse_expr::parse_expr;
7use crate::Parser;
68
79/// Precondition: the first token exists and is 'if'
810pub fn parse_expr_if(p: &mut Parser) -> Result<If> {
crates/parser/parse_fn_declaration.rs+9-5
......@@ -1,4 +1,4 @@
1use ast::{Decl, Expr, ExprData, Type};
1use ast::{Decl, DocComment, Expr, ExprData, Type};
22use miette::Result;
33use tokenizer::Token;
44
......@@ -8,7 +8,11 @@ use crate::parse_type::parse_type;
88use crate::Parser;
99
1010/// Precondition:
11pub fn parse_fn_declaration(p: &mut Parser, is_pub: bool) -> Result<Decl> {
11pub fn parse_fn_declaration(
12 p: &mut Parser,
13 is_pub: bool,
14 comment: Option<DocComment>,
15) -> Result<Decl> {
1216 let loc = p.loc();
1317 match p.next() {
1418 Some(Token::Fn) => (),
......@@ -96,11 +100,11 @@ pub fn parse_fn_declaration(p: &mut Parser, is_pub: bool) -> Result<Decl> {
96100 };
97101 Ok(Decl {
98102 name,
99 decl_type: Type::Any,
100 init: Expr::new(function_body, block_loc),
103 data_type: Type::Any,
104 init: Expr::new(function_body, block_loc, None),
101105 is_constant: true,
102106 is_pub,
103 comment: None,
107 comment,
104108 loc: loc,
105109 })
106110 } else {
crates/parser/parse_module.rs+16-9
......@@ -1,23 +1,27 @@
1use crate::{parse_top_level_statement::parse_top_level_statement, Parser};
1use crate::parse_doc_comment::maybe_read_doc_comment;
2use crate::parse_top_level_statement::parse_top_level_statement;
3use crate::Parser;
24use ast::Module;
35use miette::Result;
46use tokenizer::Token;
57
6pub fn parse_module(tokens: &mut Parser) -> Result<Module> {
8pub fn parse_module(p: &mut Parser) -> Result<Module> {
79 let mut statements = Vec::new();
810
11 let file_doc_comment = maybe_read_doc_comment(p);
12
913 loop {
10 let statement = parse_top_level_statement(tokens)?;
14 let statement = parse_top_level_statement(p)?;
1115
12 match tokens.peek() {
16 match p.peek() {
1317 Some(Token::Semicolon) => {
14 while let Some(Token::Semicolon) = tokens.peek() {
15 tokens.next();
18 while let Some(Token::Semicolon) = p.peek() {
19 p.next();
1620 }
1721 }
1822 _ => {
1923 if statement.requires_semicolon() {
20 return Err(tokens
24 return Err(p
2125 .error("Expected semicolon after statement".to_string())
2226 .into());
2327 }
......@@ -26,8 +30,11 @@ pub fn parse_module(tokens: &mut Parser) -> Result<Module> {
2630
2731 statements.push(statement);
2832
29 if tokens.empty() {
30 return Ok(Module { statements });
33 if p.empty() {
34 return Ok(Module {
35 statements,
36 comment: file_doc_comment,
37 });
3138 }
3239 }
3340}
crates/parser/parse_statement.rs+39-5
......@@ -3,26 +3,60 @@ use miette::Result;
33use tokenizer::Token;
44
55use crate::parse_declaration::parse_declaration;
6use crate::parse_doc_comment::maybe_read_doc_comment;
67use crate::parse_expr::parse_expr;
78use crate::parse_fn_declaration::parse_fn_declaration;
89use crate::Parser;
910
1011pub fn parse_statement(p: &mut Parser) -> Result<Statement> {
12 if let Some(comments) = p.take_comments() {
13 return Ok(Statement::Comment(comments));
14 }
15
16 let m_doc_comment = maybe_read_doc_comment(p);
17 if let Some(doc_comment) = m_doc_comment.clone() {
18 if p.has_comments() {
19 // this implies that comments are immediately after the doc
20 return Err(p
21 .error_at(format!("Unattached doc comment"), doc_comment.loc)
22 .into());
23 }
24 }
25
1126 match p.peek() {
27 Some(Token::DocComment(_)) => unreachable!(),
1228 Some(Token::Pub) => {
1329 _ = p.next();
1430 match p.peek() {
1531 Some(Token::Var) | Some(Token::Let) => {
16 Ok(Statement::Decl(parse_declaration(p, true)?))
32 Ok(Statement::Decl(parse_declaration(p, true, m_doc_comment)?))
1733 }
18 Some(Token::Fn) => Ok(Statement::Decl(parse_fn_declaration(p, true)?)),
34 Some(Token::Fn) => Ok(Statement::Decl(parse_fn_declaration(
35 p,
36 true,
37 m_doc_comment,
38 )?)),
1939 Some(_) => Err(p.error("Expected a declaration".to_string()).into()),
2040 None => Err(p.error("Unexpected end of file".to_string()).into()),
2141 }
2242 }
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)?)),
43 Some(Token::Var) | Some(Token::Let) => {
44 Ok(Statement::Decl(parse_declaration(p, false, m_doc_comment)?))
45 }
46 Some(Token::Fn) => Ok(Statement::Decl(parse_fn_declaration(
47 p,
48 false,
49 m_doc_comment,
50 )?)),
51 Some(_) => {
52 if let Some(doc_comment) = m_doc_comment {
53 Err(p
54 .error_at(format!("Unattached doc comment"), doc_comment.loc)
55 .into())
56 } else {
57 Ok(Statement::Expr(parse_expr(p)?))
58 }
59 }
2660 None => Err(p.error("Unexpected end of file".to_string()).into()),
2761 }
2862}
crates/parser/parser.rs+56-16
......@@ -1,6 +1,7 @@
11mod parse_block;
22mod parse_call_arguments;
33mod parse_declaration;
4mod parse_doc_comment;
45mod parse_expr;
56mod parse_expr_basic;
67mod parse_expr_if;
......@@ -11,15 +12,14 @@ mod parse_statement;
1112mod parse_top_level_statement;
1213mod parse_type;
1314
14use tokenizer::{Loc, Token};
15
16use miette::Diagnostic;
17use thiserror::Error;
18
15use ast::Comment;
1916use ast::Module;
17use miette::Diagnostic;
2018use miette::Result;
2119use parse_module::parse_module;
20use thiserror::Error;
2221use tokenizer::tokenize;
22use tokenizer::{Loc, Token};
2323
2424pub fn parse(tokens: Vec<Token>) -> Result<Module> {
2525 let mut p = Parser::new(tokens);
......@@ -47,11 +47,15 @@ impl ParseError {
4747
4848#[derive(Debug)]
4949pub struct Parser {
50 pub tokens: Vec<Token>,
51 pub cursor: usize,
50 /// TODO(@paperdave): switch to streaming tokenizer + one that can
51 /// report line and column info
52 tokens: Vec<Token>,
53 cursor: usize,
5254
5355 /// Whether or not the parser is at the top level
5456 pub is_top_level: bool,
57
58 comments: Vec<String>,
5559}
5660
5761impl Parser {
......@@ -60,6 +64,7 @@ impl Parser {
6064 tokens,
6165 cursor: 0,
6266 is_top_level: true,
67 comments: vec![],
6368 }
6469 }
6570
......@@ -67,26 +72,61 @@ impl Parser {
6772 self.cursor == self.tokens.len()
6873 }
6974
70 pub fn back(&mut self) {
71 self.cursor -= 1;
72 }
75 // TODO(@paperdave): fix parser token stuff. its very messy rn
7376
7477 pub fn next(&mut self) -> Option<Token> {
7578 if self.cursor == self.tokens.len() {
7679 return None;
7780 }
7881
79 let token = self.tokens[self.cursor].clone();
80 self.cursor += 1;
81 Some(token)
82 loop {
83 match self.tokens[self.cursor].clone() {
84 Token::Comment(comment) => {
85 self.comments.push(comment);
86 self.cursor += 1;
87 continue;
88 }
89 token => {
90 self.cursor += 1;
91 return Some(token);
92 }
93 }
94 }
8295 }
8396
84 pub fn peek(&self) -> Option<Token> {
85 if self.cursor == self.tokens.len() {
97 pub fn take_comments(&mut self) -> Option<Comment> {
98 if self.comments.len() == 0 {
8699 return None;
87100 }
101 let comments = self.comments.clone();
102 self.comments = vec![];
103 Some(Comment {
104 data: comments.join("\n"),
105 })
106 }
107
108 pub fn has_comments(&mut self) -> bool {
109 self.peek();
110 self.comments.len() > 0
111 }
88112
89 Some(self.tokens[self.cursor].clone())
113 pub fn peek(&mut self) -> Option<Token> {
114 loop {
115 if self.cursor == self.tokens.len() {
116 return None;
117 }
118
119 match self.tokens[self.cursor].clone() {
120 Token::Comment(comment) => {
121 self.comments.push(comment);
122 self.cursor += 1;
123 continue;
124 }
125 token => {
126 return Some(token);
127 }
128 }
129 }
90130 }
91131
92132 pub fn error(&self, error: String) -> ParseError {
crates/sema/Cargo.toml created+13
......@@ -0,0 +1,13 @@
1[package]
2name = "sema"
3version = "0.1.0"
4edition = "2021"
5
6[lib]
7
8[dependencies]
9miette.workspace = true
10thiserror.workspace = true
11num.workspace = true
12
13ast = { path = "../ast" }
\ No newline at end of file
crates/sema/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/sema/src/lib.rs created+96
......@@ -0,0 +1,96 @@
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/tokenizer/src/lib.rs+49-13
......@@ -71,12 +71,12 @@ pub enum Token {
7171impl std::fmt::Display for Token {
7272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7373 let token = match self {
74 Token::Identifier(_) => "an identifier".to_string(),
74 Token::Identifier(_) => "identifier".to_string(),
7575
7676 Token::Number(n) => n.to_string(),
7777 Token::String(s) => format!("\"{}\"", s),
78 Token::Comment(_) => "a comment".to_string(),
79 Token::DocComment(_) => "a documentation comment".to_string(),
78 Token::Comment(_) => "comment".to_string(),
79 Token::DocComment(_) => "documentation comment".to_string(),
8080
8181 Token::Else => "else".to_string(),
8282 Token::False => "false".to_string(),
......@@ -129,6 +129,7 @@ impl std::fmt::Display for Token {
129129 }
130130}
131131
132// TODO(@paperdave): rewrite this to not use a Vec<Token>. we probably dont need it. also store Loc
132133pub fn tokenize(content: String) -> Result<Vec<Token>> {
133134 let mut tokens: Vec<Token> = Vec::new();
134135 let mut chars = content.chars().enumerate().peekable();
......@@ -277,20 +278,55 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> {
277278
278279 let mut comment = String::new();
279280 loop {
280 let c = chars.next();
281 if c.is_none() {
282 break;
281 // skip leading whitespace
282 while let Some((_, c)) = chars.peek() {
283 if !c.is_whitespace() {
284 break;
285 }
286 chars.next();
283287 }
284 let (_, c) = c.unwrap();
285 match c {
286 '\n' => break,
287 _ => {
288 comment.push(c.clone());
288 // read the comment
289 while let Some((_, c)) = chars.next() {
290 if c == '\n' || c == '\r' {
291 break;
289292 }
293 comment.push(c);
290294 }
291 }
295 // trim trailing whitespace on line
296 comment = comment.trim_end().to_string();
297 // skip to next non-whitespace character
298 while let Some((_, c)) = chars.peek() {
299 if !c.is_whitespace() {
300 break;
301 }
302 chars.next();
303 }
304 // check if we've hit another comment
305 let mut tmp = chars.clone();
292306
293 comment = comment.trim().to_string();
307 let is_another_comment = match tmp.peek() {
308 Some((_, '/')) => {
309 tmp.next(); // Consume the second slash
310 match tmp.peek() {
311 Some((_, '/')) => {
312 tmp.next(); // Consume the third slash
313 is_doc_comment
314 }
315 _ => !is_doc_comment,
316 }
317 }
318 _ => false,
319 };
320
321 if is_another_comment {
322 // apply our temporary iterator to the main iterator
323 comment.push('\n');
324 chars = tmp;
325 chars.next();
326 } else {
327 break;
328 }
329 }
294330
295331 if is_doc_comment {
296332 tokens.push(Token::DocComment(comment));
examples/basics/comments.blight+2-9
......@@ -1,9 +1,2 @@
1// this is a regular comment
2let x: int = 0;
3
4/// this is a doc comment
5let y: int = 0;
6
7/// Doc comments can be multiple lines.
8/// This can be done by simply adding another triple-slash to the next line.
9let z: int = 0;
12 // yapping
2 + 2;
\ No newline at end of file