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}