Compare commits

..

6 Commits

Author SHA1 Message Date
0337e4f5ff Define CLI interface 2023-12-09 20:04:44 -05:00
cae07fcc49 small changes 2023-12-09 19:23:03 -05:00
dcffe062a1 Parse binary operators 2023-11-21 21:40:11 -05:00
d4a633f7c0 Parse binary / * 2023-11-21 21:29:55 -05:00
a54abcc394 Structure for parsing operators. Parse unary operator 2023-11-21 21:02:50 -05:00
c045721f46 Change grammar to incorporate binary operators 2023-11-21 07:49:49 -05:00
12 changed files with 346 additions and 29 deletions

View File

@ -2,10 +2,8 @@
## TODO ## TODO
- Parse multiple statements - Parse __more__ binary operators
- Parse binary operators
- Parse more complex bindings - Parse more complex bindings
- Parse block of code
- Watch mode - Watch mode
- Improve error messages - Improve error messages
- Parse other language constructions - Parse other language constructions
@ -14,10 +12,30 @@
- Namespace identifiers in the symbol table - Namespace identifiers in the symbol table
- Stdlib - Stdlib
- Document code - Document code
- Watch mode
- Formatter
- Simple language server
## v0.0.10
- [ ] Typecheck current AST
## v0.0.9
- [ ] Hand made CLI, remove clap
- [ ] Compile a single file
- [ ] Implement code generation for ast nodes implemented as of now
- [ ] Display error messages during compilation
- [ ] Improve errro messages
## v0.0.8 ## v0.0.8
- Parse block of code
- Parse multiple statements inside a block - Parse multiple statements inside a block
- Parse unary operator (`!` & `-`)
- Parse binary operators
## v0.0.7 ## v0.0.7

23
CLI.md Normal file
View File

@ -0,0 +1,23 @@
The THP compiler & formatter, v0.0.1
Usage: `thp [command] [options]`
## Commands
c _file_ Compiles `file` in-place
f _file_ Formats `file`
r Starts the REPL
init Initializes a new project in the current directory
build, b Builds the project
fmt Formats all files in the project
watch Starts compilation of the project in watch mode
help, h Print this message & exit
## General options
-h, --help Print command-specific usage

View File

@ -18,6 +18,12 @@ impl Transpilable for Expression {
Expression::Boolean(value) => String::from(if *value { "true" } else { "false" }), Expression::Boolean(value) => String::from(if *value { "true" } else { "false" }),
Expression::Identifier(value) => format!("{}", *value), Expression::Identifier(value) => format!("{}", *value),
Expression::FunctionCall(_) => todo!("FunctionCall codegen is not implemented yet"), Expression::FunctionCall(_) => todo!("FunctionCall codegen is not implemented yet"),
Expression::BinaryOperator(_, _, _) => {
todo!("BinaryOperator codegen is not implemented yet")
}
Expression::UnaryOperator(_, _) => {
todo!("UnaryOperator codegen is not implemented yet")
}
} }
} }
} }

View File

@ -35,4 +35,6 @@ pub enum Expression {
Boolean(bool), Boolean(bool),
Identifier(Box<String>), Identifier(Box<String>),
FunctionCall(FunctionCall), FunctionCall(FunctionCall),
UnaryOperator(Box<String>, Box<Expression>),
BinaryOperator(Box<Expression>, Box<Expression>, Box<String>),
} }

View File

@ -0,0 +1,49 @@
use crate::{
lexic::token::Token,
syntax::{ast::Expression, ParseResult},
};
/// Parses a factor expression.
///
/// ```ebnf
/// comparison = term, ((">" | ">=" | "<" | "<="), term)*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
let (term, next_pos) = match super::term::try_parse(tokens, pos) {
ParseResult::Ok(expr, next_pos) => (expr, next_pos),
_ => return ParseResult::Unmatched,
};
parse_many(tokens, next_pos, term)
}
fn parse_many(
tokens: &Vec<Token>,
pos: usize,
prev_expr: Expression,
) -> ParseResult<Expression, ()> {
// comparison = term, ((">" | ">=" | "<" | "<="), term)*;
match tokens.get(pos) {
Some(token)
if token.value == "<"
|| token.value == "<="
|| token.value == ">"
|| token.value == ">=" =>
{
match super::term::try_parse(tokens, pos + 1) {
ParseResult::Ok(expr, next_pos) => {
let expr = Expression::BinaryOperator(
Box::new(prev_expr),
Box::new(expr),
Box::new(token.value.clone()),
);
parse_many(tokens, next_pos, expr)
}
_ => ParseResult::Unmatched,
}
}
_ => ParseResult::Ok(prev_expr, pos),
}
}

View File

@ -0,0 +1,44 @@
use crate::{
lexic::token::Token,
syntax::{ast::Expression, ParseResult},
};
/// Parses a factor expression.
///
/// ```ebnf
/// equality = comparison, (("==" | "!="), comparison )*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
let (comparison, next_pos) = match super::comparison::try_parse(tokens, pos) {
ParseResult::Ok(expr, next_pos) => (expr, next_pos),
_ => return ParseResult::Unmatched,
};
parse_many(tokens, next_pos, comparison)
}
fn parse_many(
tokens: &Vec<Token>,
pos: usize,
prev_expr: Expression,
) -> ParseResult<Expression, ()> {
// equality = comparison, (("==" | "!="), comparison )*;
match tokens.get(pos) {
Some(token) if token.value == "==" || token.value == "!=" => {
match super::comparison::try_parse(tokens, pos + 1) {
ParseResult::Ok(expr, next_pos) => {
let expr = Expression::BinaryOperator(
Box::new(prev_expr),
Box::new(expr),
Box::new(token.value.clone()),
);
parse_many(tokens, next_pos, expr)
}
_ => ParseResult::Unmatched,
}
}
_ => ParseResult::Ok(prev_expr, pos),
}
}

View File

@ -0,0 +1,44 @@
use crate::{
lexic::token::Token,
syntax::{ast::Expression, ParseResult},
};
/// Parses a factor expression.
///
/// ```ebnf
/// factor = unary, (("/" | "*"), unary)*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
let (unary, next_pos) = match super::unary::try_parse(tokens, pos) {
ParseResult::Ok(expr, next_pos) => (expr, next_pos),
_ => return ParseResult::Unmatched,
};
parse_many(tokens, next_pos, unary)
}
fn parse_many(
tokens: &Vec<Token>,
pos: usize,
prev_expr: Expression,
) -> ParseResult<Expression, ()> {
// (("/" | "*"), unary)*
match tokens.get(pos) {
Some(token) if token.value == "/" || token.value == "*" => {
match super::unary::try_parse(tokens, pos + 1) {
ParseResult::Ok(expr, next_pos) => {
let expr = Expression::BinaryOperator(
Box::new(prev_expr),
Box::new(expr),
Box::new(token.value.clone()),
);
parse_many(tokens, next_pos, expr)
}
_ => ParseResult::Unmatched,
}
}
_ => ParseResult::Ok(prev_expr, pos),
}
}

View File

@ -0,0 +1,14 @@
use super::{ast::Expression, ParseResult};
use crate::lexic::token::Token;
mod comparison;
mod equality;
mod factor;
mod primary;
mod term;
mod unary;
/// Expression is defined in the grammar.
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
return equality::try_parse(tokens, pos);
}

View File

@ -1,20 +1,23 @@
use super::{ast::Expression, functions::function_call, ParseResult}; use crate::{
use crate::lexic::token::{Token, TokenType}; lexic::token::{Token, TokenType},
syntax::{ast::Expression, ParseResult},
};
/// An expression can be: /// This grammar may not be up to date. Refer to the spec for the latest grammar.
/// ///
/// - A number /// ```ebnf
/// - A string /// primary = number | string | boolean | identifier | ("(", expression, ")");
/// - A boolean /// ```
/// - An identifier
/// - A function call
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
/*
TODO: Incorporate function_call into the grammar, figure out its precedence.
match function_call::try_parse(tokens, pos) { match function_call::try_parse(tokens, pos) {
super::ParseResult::Ok(function_call, next_pos) => { super::ParseResult::Ok(function_call, next_pos) => {
return ParseResult::Ok::<_, ()>(Expression::FunctionCall(function_call), next_pos) return ParseResult::Ok::<_, ()>(Expression::FunctionCall(function_call), next_pos)
} }
_ => {} _ => {}
}; };
*/
match tokens.get(pos) { match tokens.get(pos) {
Some(token) => match token.token_type { Some(token) => match token.token_type {
@ -31,12 +34,27 @@ pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()>
Expression::Identifier(Box::new(token.value.clone())), Expression::Identifier(Box::new(token.value.clone())),
pos + 1, pos + 1,
), ),
TokenType::LeftParen => parse_parenthesized_expression(tokens, pos),
_ => ParseResult::Unmatched, _ => ParseResult::Unmatched,
}, },
None => ParseResult::Unmatched, None => ParseResult::Unmatched,
} }
} }
fn parse_parenthesized_expression(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
let expression = super::try_parse(tokens, pos + 1);
match expression {
ParseResult::Ok(expression, next_pos) => match tokens.get(next_pos) {
Some(token) => match token.token_type {
TokenType::RightParen => ParseResult::Ok(expression, next_pos + 1),
_ => ParseResult::Unmatched,
},
None => ParseResult::Unmatched,
},
_ => ParseResult::Unmatched,
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -0,0 +1,44 @@
use crate::{
lexic::token::Token,
syntax::{ast::Expression, ParseResult},
};
/// Parses a factor expression.
///
/// ```ebnf
/// term = factor, (("-" | "+"), factor)*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
let (factor, next_pos) = match super::factor::try_parse(tokens, pos) {
ParseResult::Ok(expr, next_pos) => (expr, next_pos),
_ => return ParseResult::Unmatched,
};
parse_many(tokens, next_pos, factor)
}
fn parse_many(
tokens: &Vec<Token>,
pos: usize,
prev_expr: Expression,
) -> ParseResult<Expression, ()> {
// term = factor, (("-" | "+"), factor)*;
match tokens.get(pos) {
Some(token) if token.value == "+" || token.value == "-" => {
match super::factor::try_parse(tokens, pos + 1) {
ParseResult::Ok(expr, next_pos) => {
let expr = Expression::BinaryOperator(
Box::new(prev_expr),
Box::new(expr),
Box::new(token.value.clone()),
);
parse_many(tokens, next_pos, expr)
}
_ => ParseResult::Unmatched,
}
}
_ => ParseResult::Ok(prev_expr, pos),
}
}

View File

@ -0,0 +1,25 @@
use crate::{
lexic::token::Token,
syntax::{ast::Expression, expression::primary, ParseResult},
};
/// Parses an unary expression.
///
/// ```ebnf
/// unary = ("!" | "-"), expression
/// | primary;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
match tokens.get(pos) {
Some(token) if token.value == "!" || token.value == "-" => {
match super::try_parse(tokens, pos + 1) {
ParseResult::Ok(expression, next_pos) => ParseResult::Ok(
Expression::UnaryOperator(Box::new(token.value.clone()), Box::new(expression)),
next_pos,
),
_ => ParseResult::Unmatched,
}
}
_ => primary::try_parse(tokens, pos),
}
}

View File

@ -9,6 +9,7 @@ A module is (commonly) a single source file.
module = top level declaration* module = top level declaration*
``` ```
## Top level declaration ## Top level declaration
```ebnf ```ebnf
@ -19,18 +20,10 @@ top level declaration = function declaration
## Function declaration ## Function declaration
```ebnf ```ebnf
function declaration = "fun", identifier, params list, return type?, block function declaration = "fun", identifier, params list, return type?, block;
```
### Params list params list = "(", ")";
```ebnf
params list = "(", ")"
```
### Return type
```ebnf
return type = ; return type = ;
``` ```
@ -38,28 +31,65 @@ return type = ;
### Block ### Block
```ebnf ```ebnf
block = "{", (statement, (new line, statement)*)?, "}" block = "{", (statement, (new line, statement)*)?, "}"
``` ```
### Statement ### Statement
```ebnf ```ebnf
statement = function call | binding statement = binding
| function call
``` ```
## Function call ## Function call
```ebnf ```ebnf
function call = identifier, arguments list function call = identifier, arguments list;
```
### Arguments list
```ebnf
arguments list = "(", ")" arguments list = "(", ")"
``` ```
## Binding
```ebnf
binding = ("val" | "var"), identifier, "=", expression
```
## Operator precedence
From highest to lowest:
- `== !=`, left associative
- `> >= < <=`, left associative
- `- +`, left associative
- `/ *`, left associative
- `! -`, left associative
## Expression
```ebnf
expression = equality;
equality = comparison, (("==" | "!="), comparison )*;
comparison = term, ((">" | ">=" | "<" | "<="), term)*;
term = factor, (("-" | "+"), factor)*;
factor = unary, (("/" | "*"), unary)*;
unary = ("!" | "-"), expression
| primary;
function call = primary, (arguments list)?;
primary = number | string | boolean | identifier | ("(", expression, ")");
```