bobascript_parser/
lib.rs

1use std::{convert::From, fmt::Display, string::String};
2
3use ast::Ast;
4use lalrpop_util::{lalrpop_mod, ParseError};
5use thiserror::Error;
6
7pub mod ast;
8lalrpop_mod!(#[allow(clippy::all)] pub grammar);
9
10#[derive(Debug, Error, Clone)]
11pub enum SyntaxError {
12  #[error("{0}")]
13  Generic(String),
14  #[error("Expected {0}.")]
15  Expected(String),
16  #[error("Expected {0}; found token '{1}'.")]
17  UnexpectedToken(String, String),
18  #[error("Found extra token {0}.")]
19  ExtraToken(String),
20  #[error("Invalid token.")]
21  Invalid,
22}
23type Result<T> = std::result::Result<T, SyntaxError>;
24
25impl<T1, T2> From<ParseError<usize, T1, T2>> for SyntaxError
26where
27  T1: Display,
28  T2: Into<String>,
29{
30  fn from(error: ParseError<usize, T1, T2>) -> Self {
31    match error {
32      ParseError::InvalidToken { location: _ } => SyntaxError::Invalid,
33      ParseError::UnrecognizedEOF {
34        location: _,
35        expected,
36      } => SyntaxError::Expected(expected.join(", ")),
37      ParseError::UnrecognizedToken { token, expected } => {
38        SyntaxError::UnexpectedToken(expected.join(", "), token.1.to_string())
39      }
40      ParseError::ExtraToken { token } => SyntaxError::ExtraToken(token.1.to_string()),
41      ParseError::User { error } => SyntaxError::Generic(error.into()),
42    }
43  }
44}
45
46pub trait Parser<T> {
47  fn parse_ast(input: &'_ str) -> Result<T>;
48}
49impl Parser<Ast> for crate::grammar::AstParser {
50  fn parse_ast(input: &'_ str) -> Result<Ast> {
51    let parser = crate::grammar::AstParser::new();
52    let mut errors = Vec::new();
53    let expr = parser.parse(&mut errors, input);
54
55    if errors.is_empty() {
56      Ok(expr.unwrap())
57    } else {
58      Err(errors.pop().unwrap().into())
59    }
60  }
61}
62
63mod tests {
64  #![allow(unused_imports)]
65  use crate::{ast::Expr, grammar::AstParser, Parser};
66
67  #[test]
68  fn parse_function_stmt() {
69    let stmt = AstParser::parse_ast("fn test() { 3 };").unwrap();
70    assert_eq!(
71      &format!("{:?}", stmt),
72      r#"Ast([Function("test", [], Block([], Some(Constant(Number(3.0)))))], None)"#
73    );
74    let stmt = AstParser::parse_ast("fn test(t1, t2, t3,) { 3 };").unwrap();
75    assert_eq!(
76      &format!("{:?}", stmt),
77      r#"Ast([Function("test", ["t1", "t2", "t3"], Block([], Some(Constant(Number(3.0)))))], None)"#
78    );
79  }
80
81  #[test]
82  fn parse_declaration_stmt() {
83    let stmt = AstParser::parse_ast("const test = 5.2 * 3;").unwrap();
84    assert_eq!(
85      &format!("{:?}", stmt),
86      r#"Ast([Const("test", Binary(Constant(Number(5.2)), Multiply, Constant(Number(3.0))))], None)"#
87    );
88
89    let stmt = AstParser::parse_ast("let test = 5.2 * 3;").unwrap();
90    assert_eq!(
91      &format!("{:?}", stmt),
92      r#"Ast([Let("test", Some(Binary(Constant(Number(5.2)), Multiply, Constant(Number(3.0)))))], None)"#
93    );
94
95    let stmt = AstParser::parse_ast("let test = 22.5 * if true {3} else {4};").unwrap();
96    assert_eq!(
97      &format!("{:?}", stmt),
98      r#"Ast([Let("test", Some(Binary(Constant(Number(22.5)), Multiply, If(Constant(True), Block([], Some(Constant(Number(3.0)))), Some(Block([], Some(Constant(Number(4.0)))))))))], None)"#
99    );
100
101    let stmt = AstParser::parse_ast("let test;").unwrap();
102    assert_eq!(&format!("{:?}", stmt), r#"Ast([Let("test", None)], None)"#);
103  }
104
105  #[test]
106  fn parse_return_stmt() {
107    let stmt = AstParser::parse_ast(r#"return "howdy!";"#).unwrap();
108    assert_eq!(
109      &format!("{:?}", stmt),
110      r#"Ast([Return(Some(Constant(String("\"howdy!\""))))], None)"#
111    );
112  }
113
114  #[test]
115  fn parse_expression_stmt() {
116    let stmt = AstParser::parse_ast("22.5 * (44 + 66);").unwrap();
117    assert_eq!(
118      &format!("{:?}", stmt),
119      "Ast([Expression(Binary(Constant(Number(22.5)), Multiply, Binary(Constant(Number(44.0)), Add, Constant(Number(66.0)))))], None)"
120    );
121  }
122
123  #[test]
124  fn parse_block_expr() {
125    let expr = AstParser::parse_ast("{15 + 1; 3}").unwrap();
126    assert_eq!(
127      &format!("{:?}", expr),
128      "Ast([], Some(Block([Expression(Binary(Constant(Number(15.0)), Add, Constant(Number(1.0))))], Some(Constant(Number(3.0))))))"
129    );
130  }
131
132  #[test]
133  fn parse_if_expr() {
134    let expr = AstParser::parse_ast(r#"if 3 == "3" {15 + 1; 3}"#).unwrap();
135    assert_eq!(
136      &format!("{:?}", expr),
137      r#"Ast([], Some(If(Binary(Constant(Number(3.0)), Equal, Constant(String("\"3\""))), Block([Expression(Binary(Constant(Number(15.0)), Add, Constant(Number(1.0))))], Some(Constant(Number(3.0)))), None)))"#
138    );
139
140    let expr = AstParser::parse_ast("if 3 {3} else if 6 {6}").unwrap();
141    assert_eq!(
142      &format!("{:?}", expr),
143      "Ast([], Some(If(Constant(Number(3.0)), Block([], Some(Constant(Number(3.0)))), Some(If(Constant(Number(6.0)), Block([], Some(Constant(Number(6.0)))), None)))))"
144    );
145  }
146
147  #[test]
148  fn parse_while_expr() {
149    let expr = AstParser::parse_ast("while true {15 + 1;}").unwrap();
150    assert_eq!(
151      &format!("{:?}", expr),
152      "Ast([], Some(While(Constant(True), [Expression(Binary(Constant(Number(15.0)), Add, Constant(Number(1.0))))])))"
153    );
154  }
155
156  #[test]
157  fn parse_log_expr() {
158    let expr = AstParser::parse_ast(r#"log(a = "arg")"#).unwrap();
159    assert_eq!(
160      &format!("{:?}", expr),
161      r#"Ast([], Some(Log(Assign(Constant(Ident("a")), Assign, Constant(String("\"arg\""))))))"#
162    );
163  }
164
165  #[test]
166  fn parse_assign_expr() {
167    let expr = AstParser::parse_ast("a = 5").unwrap();
168    assert_eq!(
169      &format!("{:?}", expr),
170      r#"Ast([], Some(Assign(Constant(Ident("a")), Assign, Constant(Number(5.0)))))"#
171    );
172    let expr = AstParser::parse_ast("a *= b = 5").unwrap();
173    assert_eq!(
174      &format!("{:?}", expr),
175      r#"Ast([], Some(Assign(Constant(Ident("a")), MultiplyAssign, Assign(Constant(Ident("b")), Assign, Constant(Number(5.0))))))"#
176    );
177  }
178
179  #[test]
180  fn parse_binary_expr() {
181    let expr = AstParser::parse_ast("22.5 * -44 + 66").unwrap();
182    assert_eq!(
183      &format!("{:?}", expr),
184      "Ast([], Some(Binary(Binary(Constant(Number(22.5)), Multiply, Unary(Negate, Constant(Number(44.0)))), Add, Constant(Number(66.0)))))"
185    );
186  }
187
188  #[test]
189  fn parse_call_expr() {
190    let expr = AstParser::parse_ast("test(3 * 5, 4,)").unwrap();
191    assert_eq!(
192      &format!("{:?}", expr),
193      r#"Ast([], Some(Call(Constant(Ident("test")), [Binary(Constant(Number(3.0)), Multiply, Constant(Number(5.0))), Constant(Number(4.0))])))"#
194    );
195  }
196
197  #[test]
198  fn parse_complex_tuples() {
199    let expr = AstParser::parse_ast(r#"#[1, 3, 5, #["test", "I hope this works!!"]]"#).unwrap();
200    assert_eq!(
201      &format!("{:?}", expr),
202      r#"Ast([], Some(Constant(Tuple([Constant(Number(1.0)), Constant(Number(3.0)), Constant(Number(5.0)), Constant(Tuple([Constant(String("\"test\"")), Constant(String("\"I hope this works!!\""))]))]))))"#
203    );
204
205    let expr =
206      AstParser::parse_ast(r#"#[1, 3, 5, #["test", "I hope this works!!"]][3][1]"#).unwrap();
207    assert_eq!(
208      &format!("{:?}", expr),
209      r#"Ast([], Some(Index(Index(Constant(Tuple([Constant(Number(1.0)), Constant(Number(3.0)), Constant(Number(5.0)), Constant(Tuple([Constant(String("\"test\"")), Constant(String("\"I hope this works!!\""))]))])), Constant(Number(3.0))), Constant(Number(1.0)))))"#
210    );
211  }
212}