1#[rust_sitter::grammar("lingua")]
2pub mod grammar {
3 #[rust_sitter::language]
4 #[derive(Debug, Clone, PartialEq, Eq)]
5 pub struct Program {
6 pub statements: Vec<Statement>,
7 }
8
9 #[derive(Debug, Clone, PartialEq, Eq)]
10 pub enum Statement {
11 Function(Function),
12 Expression(Expression),
13 }
14
15 #[derive(Debug, Clone, PartialEq, Eq)]
16 pub struct Function {
17 #[rust_sitter::leaf(text = "fn")]
18 _fn: (),
19 pub name: Identifier,
20 #[rust_sitter::leaf(text = "(")]
21 _open_parameters: (),
22 #[rust_sitter::leaf(text = ")")]
23 _close_parameters: (),
24 pub body: Block,
25 }
26
27 #[derive(Debug, Clone, PartialEq, Eq)]
28 pub struct Block {
29 #[rust_sitter::leaf(text = "{")]
30 _open: (),
31 pub statements: Vec<Statement>,
32 #[rust_sitter::leaf(text = "}")]
33 _close: (),
34 }
35
36 #[derive(Debug, Clone, PartialEq, Eq)]
37 pub enum Expression {
38 Number(#[rust_sitter::leaf(pattern = r"[0-9]+", transform = ToOwned::to_owned)] String),
39 String(#[rust_sitter::leaf(pattern = r#""[^"]*""#, transform = ToOwned::to_owned)] String),
40 Boolean(Boolean),
41 }
42
43 #[derive(Debug, Clone, PartialEq, Eq)]
44 pub enum Boolean {
45 #[rust_sitter::leaf(text = "true")]
46 True,
47 #[rust_sitter::leaf(text = "false")]
48 False,
49 }
50
51 #[derive(Debug, Clone, PartialEq, Eq)]
52 pub struct Identifier(
53 #[rust_sitter::leaf(
54 pattern = r"[a-zA-Z_][a-zA-Z0-9_]*",
55 transform = ToOwned::to_owned
56 )]
57 pub String,
58 );
59
60 #[rust_sitter::extra]
61 #[allow(dead_code)]
62 struct Whitespace {
63 #[rust_sitter::leaf(pattern = r"\s")]
64 _whitespace: (),
65 }
66}
67
68pub use grammar::{Block, Boolean, Expression, Function, Identifier, Program, Statement};
69
70pub fn parse(source: &str) -> Result<Program, Vec<rust_sitter::errors::ParseError>> {
71 grammar::parse(source)
72}
73
74pub fn format(source: &str) -> Result<String, Vec<rust_sitter::errors::ParseError>> {
75 parse(source).map(|program| format_program(&program))
76}
77
78pub fn format_program(program: &Program) -> String {
79 let mut output = String::new();
80 write_statements(&mut output, &program.statements, 0);
81 output
82}
83
84fn write_statements(output: &mut String, statements: &[Statement], indent: usize) {
85 for (index, statement) in statements.iter().enumerate() {
86 if index > 0 {
87 output.push('\n');
88 }
89
90 write_statement(output, statement, indent);
91 }
92}
93
94fn write_statement(output: &mut String, statement: &Statement, indent: usize) {
95 output.push_str(&" ".repeat(indent));
96
97 match statement {
98 Statement::Function(function) => write_function(output, function, indent),
99 Statement::Expression(expression) => write_expression(output, expression),
100 }
101}
102
103fn write_function(output: &mut String, function: &Function, indent: usize) {
104 output.push_str("fn ");
105 output.push_str(&function.name.0);
106 output.push_str("() {\n");
107 write_statements(output, &function.body.statements, indent + 4);
108 output.push('\n');
109 output.push_str(&" ".repeat(indent));
110 output.push('}');
111}
112
113fn write_expression(output: &mut String, expression: &Expression) {
114 match expression {
115 Expression::Number(number) => output.push_str(number),
116 Expression::String(string) => output.push_str(string),
117 Expression::Boolean(Boolean::True) => output.push_str("true"),
118 Expression::Boolean(Boolean::False) => output.push_str("false"),
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn parses_primitives() {
128 assert_eq!(
129 parse("true\n6174\n\"hello world\"\n").unwrap(),
130 Program {
131 statements: vec![
132 Statement::Expression(Expression::Boolean(Boolean::True)),
133 Statement::Expression(Expression::Number("6174".to_owned())),
134 Statement::Expression(Expression::String("\"hello world\"".to_owned())),
135 ],
136 }
137 );
138 }
139
140 #[test]
141 fn parses_functions() {
142 let program = parse("fn test_function() {\n true\n}\n").unwrap();
143
144 let Statement::Function(function) = &program.statements[0] else {
145 panic!("expected a function");
146 };
147
148 assert_eq!(function.name.0, "test_function");
149 assert_eq!(
150 function.body.statements,
151 vec![Statement::Expression(Expression::Boolean(Boolean::True))]
152 );
153 }
154
155 #[test]
156 fn formats_functions() {
157 assert_eq!(
158 format("fn test( ) {\ntrue\n}").unwrap(),
159 "fn test() {\n true\n}"
160 );
161 }
162
163 #[test]
164 fn rejects_syntax_errors() {
165 assert!(parse("fn broken(").is_err());
166 }
167
168 #[test]
169 fn parses_example_files() {
170 for entry in std::fs::read_dir("test/examples").unwrap() {
171 let path = entry.unwrap().path();
172 if path.extension().and_then(|extension| extension.to_str()) != Some("lingua") {
173 continue;
174 }
175
176 let source = std::fs::read_to_string(&path).unwrap();
177 parse(&source).unwrap_or_else(|err| {
178 panic!("failed to parse {}: {err:?}", path.display());
179 });
180 }
181 }
182}