1pub mod ast {
5 pub use ruka_frontend::*;
6}
7
8pub mod syntax {
10 pub use crate::SourceLocation;
11 pub use crate::SyntaxError;
12 pub use crate::ast;
13 pub use crate::browser_graph;
14 pub use crate::cst;
15 pub use crate::lexer;
16 pub use crate::parse_program;
17 pub use crate::parse_program_cst;
18 pub use crate::parse_program_with_file_id;
19 pub use crate::token;
20}
21
22pub mod browser_graph;
24pub mod cst;
26pub mod lexer;
28pub mod token;
30
31use lalrpop_util::ParseError;
32use thiserror::Error;
33
34use crate::ast::{ExternFunctionDecl, Program, TypeExpr};
35use crate::cst::SyntaxNode;
36use crate::lexer::Lexer;
37use crate::token::{LexError, Token};
38
39mod grammar {
41 include!(concat!(env!("OUT_DIR"), "/grammar.rs"));
42}
43
44pub use ruka_frontend::{FileId, SourceLocation};
45
46#[derive(Debug, Error)]
48pub enum SyntaxError {
49 #[error("{error}")]
51 Lex {
52 error: LexError,
54 },
55 #[error(
57 "unexpected token {found:?} at {line}:{column} (byte {byte})",
58 line = location.line,
59 column = location.column,
60 byte = location.byte
61 )]
62 UnexpectedToken {
63 location: SourceLocation,
65 found: Token,
67 },
68 #[error(
70 "unexpected end of input at {line}:{column} (byte {byte})",
71 line = location.line,
72 column = location.column,
73 byte = location.byte
74 )]
75 UnexpectedEof {
76 location: SourceLocation,
78 },
79 #[error(
81 "invalid token at {line}:{column} (byte {byte})",
82 line = location.line,
83 column = location.column,
84 byte = location.byte
85 )]
86 InvalidToken {
87 location: SourceLocation,
89 },
90 #[error(
92 "generic type applications are not allowed in extern signatures at {line}:{column} (byte {byte})",
93 line = location.line,
94 column = location.column,
95 byte = location.byte
96 )]
97 InvalidExternTypeApplication {
98 location: SourceLocation,
100 },
101}
102
103pub fn parse_program(input: &str) -> Result<Program, SyntaxError> {
105 parse_program_with_file_id(FileId::from_u32(0), input)
106}
107
108pub fn parse_program_with_file_id(file_id: FileId, input: &str) -> Result<Program, SyntaxError> {
110 let mut tokens = Vec::new();
111 for item in Lexer::new(input) {
112 tokens.push(item.map_err(|error| SyntaxError::Lex { error })?);
113 }
114 let tokens: Vec<_> = tokens
115 .into_iter()
116 .filter(|(_, token, _)| !matches!(token, Token::Comment))
117 .collect();
118 let program = grammar::ProgramParser::new()
119 .parse(file_id, tokens.into_iter())
120 .map_err(map_parse_error)?;
121 validate_extern_signatures(&program)?;
122 Ok(program)
123}
124
125pub fn parse_program_cst(input: &str) -> Result<SyntaxNode, SyntaxError> {
127 let ast = parse_program(input)?;
128 Ok(cst::build_program_cst(&ast))
129}
130
131fn map_parse_error(error: ParseError<SourceLocation, Token, LexError>) -> SyntaxError {
132 match error {
133 ParseError::InvalidToken { location } => SyntaxError::InvalidToken { location },
134 ParseError::UnrecognizedEof { location, .. } => SyntaxError::UnexpectedEof { location },
135 ParseError::UnrecognizedToken { token, .. } => {
136 let (start, found, _) = token;
137 SyntaxError::UnexpectedToken {
138 location: start,
139 found,
140 }
141 }
142 ParseError::ExtraToken { token } => {
143 let (start, found, _) = token;
144 SyntaxError::UnexpectedToken {
145 location: start,
146 found,
147 }
148 }
149 ParseError::User { error } => SyntaxError::Lex { error },
150 }
151}
152
153fn validate_extern_signatures(program: &Program) -> Result<(), SyntaxError> {
154 for module in &program.extern_modules {
155 for function in &module.functions {
156 validate_extern_function(function)?;
157 }
158 }
159 Ok(())
160}
161
162fn validate_extern_function(function: &ExternFunctionDecl) -> Result<(), SyntaxError> {
163 for param in &function.params {
164 reject_extern_type_apply(¶m.ty.ty, function)?;
165 }
166 reject_extern_type_apply(&function.return_type.ty, function)
167}
168
169fn reject_extern_type_apply(
170 ty: &TypeExpr,
171 function: &ExternFunctionDecl,
172) -> Result<(), SyntaxError> {
173 match ty {
174 TypeExpr::TypeKind | TypeExpr::Named(_) => Ok(()),
175 TypeExpr::Pointer { item } | TypeExpr::Slice { item } => {
176 reject_extern_type_apply(item, function)
177 }
178 TypeExpr::Array { item, .. } => reject_extern_type_apply(item, function),
179 TypeExpr::Tuple(items) => {
180 for item in items {
181 reject_extern_type_apply(item, function)?;
182 }
183 Ok(())
184 }
185 TypeExpr::Apply { .. } => Err(SyntaxError::InvalidExternTypeApplication {
186 location: function.span.start,
187 }),
188 TypeExpr::Struct { fields } | TypeExpr::Union { fields } => {
189 for field in fields {
190 reject_extern_type_apply(&field.ty, function)?;
191 }
192 Ok(())
193 }
194 TypeExpr::Enum { variants } => {
195 for variant in variants {
196 for payload in &variant.payload {
197 reject_extern_type_apply(payload, function)?;
198 }
199 }
200 Ok(())
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests;