mirror of
https://github.com/Noratrieb/ub.git
synced 2026-01-15 00:55:04 +01:00
single craqte
This commit is contained in:
parent
ff78ae710b
commit
2fd78566a3
25 changed files with 36 additions and 64 deletions
197
src/ast.rs
Normal file
197
src/ast.rs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
use std::{ops::Range, path::PathBuf};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct NodeId(u32);
|
||||
|
||||
type Span = Range<usize>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct File {
|
||||
pub name: PathBuf,
|
||||
pub items: Vec<Item>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Ty {
|
||||
pub span: Span,
|
||||
pub kind: TyKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TyKind {
|
||||
U64,
|
||||
Ptr(Box<Ty>),
|
||||
Name(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Item {
|
||||
FnDecl(FnDecl),
|
||||
StructDecl(StructDecl),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FnDecl {
|
||||
pub name: String,
|
||||
pub params: Vec<NameTyPair>,
|
||||
pub ret_ty: Option<Ty>,
|
||||
pub id: NodeId,
|
||||
pub span: Span,
|
||||
pub body: Vec<Stmt>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct NameTyPair {
|
||||
pub name: String,
|
||||
pub ty: Ty,
|
||||
pub id: NodeId,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct StructDecl {
|
||||
pub name: String,
|
||||
pub fields: Vec<NameTyPair>,
|
||||
pub id: NodeId,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Stmt {
|
||||
VarDecl(VarDecl),
|
||||
Assignment(Assignment),
|
||||
IfStmt(IfStmt),
|
||||
WhileStmt(WhileStmt),
|
||||
LoopStmt(LoopStmt),
|
||||
Item(Item),
|
||||
Expr(Expr),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct VarDecl {
|
||||
pub name: String,
|
||||
pub ty: Option<Ty>,
|
||||
pub rhs: Option<Expr>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Assignment {
|
||||
pub place: Expr,
|
||||
pub rhs: Expr,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct IfStmt {
|
||||
pub cond: Expr,
|
||||
pub body: Vec<Stmt>,
|
||||
pub else_part: Option<ElsePart>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ElsePart {
|
||||
Else(Vec<Stmt>, Span),
|
||||
ElseIf(Box<IfStmt>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct WhileStmt {
|
||||
pub cond: Expr,
|
||||
pub body: Vec<Stmt>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LoopStmt {
|
||||
pub body: Vec<Stmt>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Expr {
|
||||
pub kind: ExprKind,
|
||||
pub id: NodeId,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ExprKind {
|
||||
BinOp(BinOp),
|
||||
UnaryOp(UnaryOp),
|
||||
FieldAccess(FieldAccess),
|
||||
Call(Call),
|
||||
Literal(Literal),
|
||||
Name(String),
|
||||
Array(Vec<Expr>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct BinOp {
|
||||
pub kind: BinOpKind,
|
||||
pub lhs: Box<Expr>,
|
||||
pub rhs: Box<Expr>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum BinOpKind {
|
||||
Eq,
|
||||
Neq,
|
||||
Gt,
|
||||
Lt,
|
||||
GtEq,
|
||||
LtEq,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
Shr,
|
||||
Shl,
|
||||
And,
|
||||
Or,
|
||||
BitAnd,
|
||||
BitOr,
|
||||
Xor,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct UnaryOp {
|
||||
pub expr: Box<Expr>,
|
||||
pub kind: UnaryOpKind,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum UnaryOpKind {
|
||||
Not,
|
||||
Neg,
|
||||
Deref,
|
||||
AddrOf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FieldAccess {
|
||||
pub expr: Box<Expr>,
|
||||
pub field_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Call {
|
||||
pub callee: Box<Expr>,
|
||||
pub args: Vec<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Literal {
|
||||
String(String, Span),
|
||||
Integer(u64, Span),
|
||||
}
|
||||
|
||||
impl NodeId {
|
||||
pub(crate) fn new(id: u32) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
193
src/lexer.rs
Normal file
193
src/lexer.rs
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
use std::fmt::{Debug, Display, Formatter};
|
||||
|
||||
use logos::Logos;
|
||||
|
||||
#[derive(Logos, Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub enum Token<'a> {
|
||||
#[regex("//[^\n]*", logos::skip)]
|
||||
Comment,
|
||||
|
||||
// punctuation
|
||||
#[token("{")]
|
||||
BraceO,
|
||||
#[token("}")]
|
||||
BraceC,
|
||||
#[token("[")]
|
||||
BracketO,
|
||||
#[token("]")]
|
||||
BracketC,
|
||||
#[token("(")]
|
||||
ParenO,
|
||||
#[token(")")]
|
||||
ParenC,
|
||||
#[token(".")]
|
||||
Dot,
|
||||
#[token(",")]
|
||||
Comma,
|
||||
#[token(";")]
|
||||
Semi,
|
||||
#[token("=")]
|
||||
Eq,
|
||||
#[token("==")]
|
||||
EqEq,
|
||||
#[token("!")]
|
||||
Bang,
|
||||
#[token("!=")]
|
||||
BangEq,
|
||||
#[token(">")]
|
||||
Greater,
|
||||
#[token("<")]
|
||||
Less,
|
||||
#[token(">=")]
|
||||
GreaterEq,
|
||||
#[token("<=")]
|
||||
LessEq,
|
||||
#[token("*")]
|
||||
Asterisk,
|
||||
#[token("/")]
|
||||
Slash,
|
||||
#[token("+")]
|
||||
Plus,
|
||||
#[token("-")]
|
||||
Minus,
|
||||
#[token("|")]
|
||||
Or,
|
||||
#[token("&")]
|
||||
Ampersand,
|
||||
#[token("||")]
|
||||
OrOr,
|
||||
#[token("&&")]
|
||||
AndAnd,
|
||||
#[token("^")]
|
||||
Caret,
|
||||
#[token("->")]
|
||||
Arrow,
|
||||
#[token(":")]
|
||||
Colon,
|
||||
|
||||
// keywords
|
||||
#[token("struct")]
|
||||
Struct,
|
||||
#[token("fn")]
|
||||
Fn,
|
||||
#[token("if")]
|
||||
If,
|
||||
#[token("else")]
|
||||
Else,
|
||||
#[token("while")]
|
||||
While,
|
||||
#[token("loop")]
|
||||
Loop,
|
||||
#[token("ptr")]
|
||||
Ptr,
|
||||
#[token("let")]
|
||||
Let,
|
||||
|
||||
#[regex(r"[a-zA-Z_]\w*")]
|
||||
Ident(&'a str),
|
||||
|
||||
#[regex(r##""[^"]*""##)]
|
||||
String(&'a str),
|
||||
|
||||
#[regex(r"\d+")]
|
||||
Integer(&'a str),
|
||||
|
||||
#[error]
|
||||
#[regex(r"[ \t\r\n]+", logos::skip)]
|
||||
Error,
|
||||
}
|
||||
|
||||
impl<'a> Display for Token<'a> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Token::Comment => f.write_str("comment"),
|
||||
Token::BraceO => f.write_str("{"),
|
||||
Token::BraceC => f.write_str("}"),
|
||||
Token::BracketO => f.write_str("["),
|
||||
Token::BracketC => f.write_str("]"),
|
||||
Token::ParenO => f.write_str("("),
|
||||
Token::ParenC => f.write_str(")"),
|
||||
Token::Dot => f.write_str("."),
|
||||
Token::Comma => f.write_str(","),
|
||||
Token::Semi => f.write_str(";"),
|
||||
Token::Eq => f.write_str("="),
|
||||
Token::EqEq => f.write_str("=="),
|
||||
Token::Bang => f.write_str("!"),
|
||||
Token::BangEq => f.write_str("!="),
|
||||
Token::Greater => f.write_str(">"),
|
||||
Token::Less => f.write_str("<"),
|
||||
Token::GreaterEq => f.write_str(">="),
|
||||
Token::LessEq => f.write_str("<="),
|
||||
Token::Asterisk => f.write_str("*"),
|
||||
Token::Slash => f.write_str("/"),
|
||||
Token::Plus => f.write_str("+"),
|
||||
Token::Minus => f.write_str("-"),
|
||||
Token::Or => f.write_str("|"),
|
||||
Token::Ampersand => f.write_str("&"),
|
||||
Token::OrOr => f.write_str("||"),
|
||||
Token::AndAnd => f.write_str("&&"),
|
||||
Token::Caret => f.write_str("^"),
|
||||
Token::Arrow => f.write_str("->"),
|
||||
Token::Colon => f.write_str(":"),
|
||||
Token::Struct => f.write_str("struct"),
|
||||
Token::Fn => f.write_str("fn"),
|
||||
Token::If => f.write_str("if"),
|
||||
Token::Else => f.write_str("else"),
|
||||
Token::While => f.write_str("while"),
|
||||
Token::Loop => f.write_str("loop"),
|
||||
Token::Ptr => f.write_str("ptr"),
|
||||
Token::Let => f.write_str("let"),
|
||||
Token::Ident(ident) => write!(f, "identifier `{ident}`"),
|
||||
Token::String(str) => write!(f, "\"{str}\""),
|
||||
Token::Integer(int) => write!(f, "{int}"),
|
||||
Token::Error => f.write_str("error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lex<'src>(code: &'src str) -> logos::Lexer<'_, Token<'src>> {
|
||||
Token::lexer(code)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lexer::Token;
|
||||
|
||||
fn lex_test(str: &str) -> Vec<Token<'_>> {
|
||||
let lexer = super::lex(str);
|
||||
lexer.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn punctuation() {
|
||||
let tokens = lex_test("{} [] () .,; = == != >= <= < > + - * / | || & && ^ -> :");
|
||||
insta::assert_debug_snapshot!(tokens);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitespace() {
|
||||
let tokens = lex_test(
|
||||
".
|
||||
\r\n \t .",
|
||||
);
|
||||
insta::assert_debug_snapshot!(tokens);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idents() {
|
||||
let tokens = lex_test("hello w_world b235_");
|
||||
insta::assert_debug_snapshot!(tokens);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literals() {
|
||||
let tokens = lex_test(r##""hello friend" 5 "morning" 3263475"##);
|
||||
insta::assert_debug_snapshot!(tokens);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keywords() {
|
||||
let tokens = lex_test("struct fn . if else while loop;");
|
||||
insta::assert_debug_snapshot!(tokens);
|
||||
}
|
||||
}
|
||||
120
src/lib.rs
Normal file
120
src/lib.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#![warn(rust_2018_idioms)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use ariadne::{Color, Fmt, Label, Report, ReportKind, Source};
|
||||
use chumsky::prelude::Simple;
|
||||
use logos::Logos;
|
||||
|
||||
use crate::lexer::Token;
|
||||
|
||||
mod ast;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
mod pretty;
|
||||
|
||||
pub fn parse(_str: &str, _file_name: PathBuf) -> Result<ast::File, ()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn test() {
|
||||
let src = "
|
||||
fn main(uwu: u64, owo: ptr WOW) -> ptr u64 {
|
||||
let uwu = &1;
|
||||
let owo = yeet(1+2*3, AA);
|
||||
|
||||
if 1 {
|
||||
10;
|
||||
} else {}
|
||||
|
||||
if 1 { if 1 { if 1 {} } }
|
||||
}
|
||||
|
||||
fn aa() {}
|
||||
";
|
||||
|
||||
let lexer = Token::lexer(src);
|
||||
let len = lexer.source().len();
|
||||
let state = parser::ParserState::default();
|
||||
|
||||
let (file, errors) = parser::parse(lexer.spanned(), &state, len, "test_file".into());
|
||||
|
||||
if let Some(file) = file {
|
||||
println!("{}", pretty::pretty_print_ast(&file));
|
||||
}
|
||||
|
||||
report_errors(src, errors);
|
||||
}
|
||||
|
||||
fn report_errors(src: &str, errors: Vec<Simple<Token<'_>>>) {
|
||||
errors
|
||||
.into_iter()
|
||||
.map(|e| e.map(|c| c.to_string()))
|
||||
.for_each(|e| {
|
||||
let report = Report::build(ReportKind::Error, (), e.span().start);
|
||||
|
||||
let report = match e.reason() {
|
||||
chumsky::error::SimpleReason::Unclosed { span, delimiter } => report
|
||||
.with_message(format!(
|
||||
"Unclosed delimiter {}",
|
||||
delimiter.fg(Color::Yellow)
|
||||
))
|
||||
.with_label(
|
||||
Label::new(span.clone())
|
||||
.with_message(format!(
|
||||
"Unclosed delimiter {}",
|
||||
delimiter.fg(Color::Yellow)
|
||||
))
|
||||
.with_color(Color::Yellow),
|
||||
)
|
||||
.with_label(
|
||||
Label::new(e.span())
|
||||
.with_message(format!(
|
||||
"Must be closed before this {}",
|
||||
e.found()
|
||||
.unwrap_or(&"end of file".to_string())
|
||||
.fg(Color::Red)
|
||||
))
|
||||
.with_color(Color::Red),
|
||||
),
|
||||
chumsky::error::SimpleReason::Unexpected => report
|
||||
.with_message(format!(
|
||||
"{}, expected {}",
|
||||
if e.found().is_some() {
|
||||
"Unexpected token in input"
|
||||
} else {
|
||||
"Unexpected end of input"
|
||||
},
|
||||
if e.expected().len() == 0 {
|
||||
"something else".to_string()
|
||||
} else {
|
||||
e.expected()
|
||||
.map(|expected| match expected {
|
||||
Some(expected) => expected.to_string(),
|
||||
None => "end of input".to_string(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
))
|
||||
.with_label(
|
||||
Label::new(e.span())
|
||||
.with_message(format!(
|
||||
"Unexpected token {}",
|
||||
e.found()
|
||||
.unwrap_or(&"end of file".to_string())
|
||||
.fg(Color::Red)
|
||||
))
|
||||
.with_color(Color::Red),
|
||||
),
|
||||
chumsky::error::SimpleReason::Custom(msg) => report.with_message(msg).with_label(
|
||||
Label::new(e.span())
|
||||
.with_message(format!("{}", msg.fg(Color::Red)))
|
||||
.with_color(Color::Red),
|
||||
),
|
||||
};
|
||||
|
||||
report.finish().print(Source::from(&src)).unwrap();
|
||||
});
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
fn main() {
|
||||
parser::test();
|
||||
ub::test();
|
||||
}
|
||||
|
||||
|
|
|
|||
517
src/parser.rs
Normal file
517
src/parser.rs
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
use std::{cell::Cell, ops::Range, path::PathBuf};
|
||||
|
||||
use chumsky::{prelude::*, Stream};
|
||||
|
||||
use crate::{
|
||||
ast::{
|
||||
Assignment, BinOp, BinOpKind, Call, ElsePart, Expr, ExprKind, File, FnDecl, IfStmt, Item,
|
||||
Literal, NameTyPair, NodeId, Stmt, StructDecl, Ty, TyKind, UnaryOp, UnaryOpKind, VarDecl,
|
||||
WhileStmt,
|
||||
},
|
||||
lexer::Token,
|
||||
};
|
||||
|
||||
type Error<'src> = Simple<Token<'src>>;
|
||||
type Span = Range<usize>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ParserState {
|
||||
next_id: Cell<u32>,
|
||||
}
|
||||
|
||||
impl ParserState {
|
||||
pub fn next_id(&self) -> NodeId {
|
||||
let next = self.next_id.get();
|
||||
self.next_id.set(next + 1);
|
||||
NodeId::new(next)
|
||||
}
|
||||
}
|
||||
|
||||
fn ident_parser<'src>() -> impl Parser<Token<'src>, String, Error = Error<'src>> + Clone {
|
||||
let ident = select! {
|
||||
Token::Ident(ident) => ident.to_owned(),
|
||||
};
|
||||
ident.labelled("identifier").boxed()
|
||||
}
|
||||
|
||||
fn ty_parser<'src>() -> impl Parser<Token<'src>, Ty, Error = Error<'src>> + Clone {
|
||||
recursive(|ty_parser| {
|
||||
let primitive = filter_map(|span, token| {
|
||||
let kind = match token {
|
||||
Token::Ident("u64") => TyKind::U64,
|
||||
_ => return Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
|
||||
};
|
||||
Ok(Ty { span, kind })
|
||||
})
|
||||
.labelled("primitive type");
|
||||
|
||||
let ptr = just(Token::Ptr)
|
||||
.ignore_then(ty_parser.clone())
|
||||
.map_with_span(|ty: Ty, span| Ty {
|
||||
kind: TyKind::Ptr(Box::new(ty)),
|
||||
span,
|
||||
})
|
||||
.labelled("pointer type");
|
||||
|
||||
let name = ident_parser()
|
||||
.map_with_span(|name: String, span| Ty {
|
||||
kind: TyKind::Name(name),
|
||||
span,
|
||||
})
|
||||
.labelled("name type");
|
||||
|
||||
primitive.or(ptr).or(name).labelled("type").boxed()
|
||||
})
|
||||
}
|
||||
|
||||
fn expr_parser<'src>(
|
||||
state: &'src ParserState,
|
||||
) -> impl Parser<Token<'src>, Expr, Error = Error<'src>> + Clone + 'src {
|
||||
recursive(|expr| {
|
||||
let literal = filter_map(|span: Span, token| match token {
|
||||
Token::String(str) => Ok(Expr {
|
||||
kind: ExprKind::Literal(Literal::String(
|
||||
str[1..str.len() - 2].to_owned(),
|
||||
span.clone(),
|
||||
)),
|
||||
id: state.next_id(),
|
||||
span,
|
||||
}),
|
||||
// todo lol unwrap
|
||||
Token::Integer(int) => Ok(Expr {
|
||||
kind: ExprKind::Literal(Literal::Integer(int.parse().unwrap(), span.clone())),
|
||||
id: state.next_id(),
|
||||
span,
|
||||
}),
|
||||
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
|
||||
})
|
||||
.labelled("literal");
|
||||
|
||||
let expr_list = expr
|
||||
.clone()
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.or_not()
|
||||
.map(|item| item.unwrap_or_default())
|
||||
.boxed();
|
||||
|
||||
let array = expr_list
|
||||
.clone()
|
||||
.delimited_by(just(Token::BracketO), just(Token::BracketC))
|
||||
.map_with_span(|exprs: Vec<Expr>, span| Expr {
|
||||
kind: ExprKind::Array(exprs),
|
||||
id: state.next_id(),
|
||||
span,
|
||||
});
|
||||
|
||||
let atom = literal
|
||||
.or(ident_parser().map_with_span(|name, span| Expr {
|
||||
kind: ExprKind::Name(name),
|
||||
id: state.next_id(),
|
||||
span,
|
||||
}))
|
||||
.or(array)
|
||||
.or(expr
|
||||
.clone()
|
||||
.delimited_by(just(Token::ParenO), just(Token::ParenC)))
|
||||
.boxed();
|
||||
|
||||
let call = atom
|
||||
.clone()
|
||||
.then(
|
||||
expr_list
|
||||
.delimited_by(just(Token::ParenO), just(Token::ParenC))
|
||||
.repeated(),
|
||||
)
|
||||
.foldl(|callee: Expr, args: Vec<Expr>| {
|
||||
let span =
|
||||
callee.span.start..args.last().map(|e| e.span.end).unwrap_or(callee.span.end);
|
||||
Expr {
|
||||
kind: ExprKind::Call(Call {
|
||||
callee: Box::new(callee),
|
||||
args,
|
||||
}),
|
||||
id: state.next_id(),
|
||||
span,
|
||||
}
|
||||
})
|
||||
.labelled("call")
|
||||
.boxed();
|
||||
|
||||
let unary_op = choice((
|
||||
just(Token::Minus).to(UnaryOpKind::Neg),
|
||||
just(Token::Bang).to(UnaryOpKind::Not),
|
||||
just(Token::Ampersand).to(UnaryOpKind::AddrOf),
|
||||
just(Token::Asterisk).to(UnaryOpKind::Deref),
|
||||
))
|
||||
.repeated()
|
||||
.then(call)
|
||||
.foldr(|kind, rhs| {
|
||||
let span = rhs.span.clone();
|
||||
Expr {
|
||||
kind: ExprKind::UnaryOp(UnaryOp {
|
||||
expr: Box::new(rhs),
|
||||
kind,
|
||||
span: span.clone(),
|
||||
}),
|
||||
id: state.next_id(),
|
||||
span,
|
||||
}
|
||||
})
|
||||
.labelled("unary")
|
||||
.boxed();
|
||||
|
||||
let op = just(Token::Asterisk)
|
||||
.to(BinOpKind::Mul)
|
||||
.or(just(Token::Slash).to(BinOpKind::Div));
|
||||
|
||||
let product = unary_op
|
||||
.clone()
|
||||
.then(op.then(unary_op).repeated())
|
||||
.foldl(|a, (kind, b)| {
|
||||
let span = a.span.start..b.span.end;
|
||||
Expr {
|
||||
kind: ExprKind::BinOp(BinOp {
|
||||
kind,
|
||||
lhs: Box::new(a),
|
||||
rhs: Box::new(b),
|
||||
span: span.clone(),
|
||||
}),
|
||||
id: state.next_id(),
|
||||
span,
|
||||
}
|
||||
});
|
||||
|
||||
// Sum ops (add and subtract) have equal precedence
|
||||
let op = just(Token::Plus)
|
||||
.to(BinOpKind::Add)
|
||||
.or(just(Token::Minus).to(BinOpKind::Sub));
|
||||
let sum = product
|
||||
.clone()
|
||||
.then(op.then(product).repeated())
|
||||
.foldl(|a, (kind, b)| {
|
||||
let span = a.span.start..b.span.end;
|
||||
Expr {
|
||||
kind: ExprKind::BinOp(BinOp {
|
||||
kind,
|
||||
lhs: Box::new(a),
|
||||
rhs: Box::new(b),
|
||||
span: span.clone(),
|
||||
}),
|
||||
id: state.next_id(),
|
||||
span,
|
||||
}
|
||||
})
|
||||
.labelled("product")
|
||||
.boxed();
|
||||
|
||||
// Comparison ops (equal, not-equal) have equal precedence
|
||||
let op = just(Token::EqEq)
|
||||
.to(BinOpKind::Eq)
|
||||
.or(just(Token::BangEq).to(BinOpKind::Neq));
|
||||
let compare = sum
|
||||
.clone()
|
||||
.then(op.then(sum).repeated())
|
||||
.foldl(|a, (kind, b)| {
|
||||
let span = a.span.start..b.span.end;
|
||||
Expr {
|
||||
kind: ExprKind::BinOp(BinOp {
|
||||
kind,
|
||||
lhs: Box::new(a),
|
||||
rhs: Box::new(b),
|
||||
span: span.clone(),
|
||||
}),
|
||||
id: state.next_id(),
|
||||
span,
|
||||
}
|
||||
});
|
||||
compare.labelled("comparison").boxed()
|
||||
})
|
||||
}
|
||||
|
||||
fn statement_parser<'src>(
|
||||
state: &'src ParserState,
|
||||
) -> impl Parser<Token<'src>, Stmt, Error = Error<'src>> + Clone {
|
||||
recursive(|stmt| {
|
||||
let var_decl = just(Token::Let)
|
||||
.ignore_then(ident_parser())
|
||||
.then(just(Token::Colon).ignore_then(ty_parser()).or_not())
|
||||
.then(just(Token::Eq).ignore_then(expr_parser(state)).or_not())
|
||||
.then_ignore(just(Token::Semi))
|
||||
.map(|((name, ty), rhs)| {
|
||||
Stmt::VarDecl(VarDecl {
|
||||
name,
|
||||
ty,
|
||||
rhs,
|
||||
span: Default::default(),
|
||||
})
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let assignment = expr_parser(state)
|
||||
.then_ignore(just(Token::Eq))
|
||||
.then(expr_parser(state))
|
||||
.then_ignore(just(Token::Semi))
|
||||
.map(|(place, rhs)| {
|
||||
Stmt::Assignment(Assignment {
|
||||
place,
|
||||
rhs,
|
||||
span: Default::default(),
|
||||
})
|
||||
});
|
||||
|
||||
let block = stmt
|
||||
.clone()
|
||||
.repeated()
|
||||
.delimited_by(just(Token::BraceO), just(Token::BraceC));
|
||||
|
||||
let while_loop = just(Token::While)
|
||||
.ignore_then(expr_parser(state))
|
||||
.then(block.clone())
|
||||
.map_with_span(|(cond, body), span| Stmt::WhileStmt(WhileStmt { cond, body, span }))
|
||||
.labelled("while loop");
|
||||
|
||||
let if_stmt = recursive(|if_stmt| {
|
||||
just(Token::If)
|
||||
.ignore_then(expr_parser(state))
|
||||
.then(block.clone())
|
||||
.then(
|
||||
just(Token::Else)
|
||||
.ignore_then(
|
||||
if_stmt
|
||||
.map(|if_stmt| ElsePart::ElseIf(Box::new(if_stmt)))
|
||||
.or(block.clone().map_with_span(ElsePart::Else)),
|
||||
)
|
||||
.or_not(),
|
||||
)
|
||||
.map_with_span(|((cond, body), else_part), span| IfStmt {
|
||||
cond,
|
||||
body,
|
||||
else_part,
|
||||
span,
|
||||
})
|
||||
})
|
||||
.map(Stmt::IfStmt)
|
||||
.boxed();
|
||||
|
||||
var_decl
|
||||
.or(assignment)
|
||||
.or(expr_parser(state)
|
||||
.then_ignore(just(Token::Semi))
|
||||
.map(Stmt::Expr))
|
||||
.or(if_stmt)
|
||||
.or(while_loop)
|
||||
})
|
||||
.labelled("statement")
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn name_ty_pair_parser<'src>(
|
||||
state: &'src ParserState,
|
||||
) -> impl Parser<Token<'src>, NameTyPair, Error = Error<'src>> + Clone {
|
||||
ident_parser()
|
||||
.then_ignore(just(Token::Colon))
|
||||
.then(ty_parser())
|
||||
.map_with_span(|(name, ty), span| NameTyPair {
|
||||
name,
|
||||
ty,
|
||||
id: state.next_id(),
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
fn struct_parser<'src>(
|
||||
state: &'src ParserState,
|
||||
) -> impl Parser<Token<'src>, StructDecl, Error = Error<'src>> + Clone {
|
||||
let name = just(Token::Struct).ignore_then(ident_parser());
|
||||
|
||||
let fields = name_ty_pair_parser(state)
|
||||
.separated_by(just(Token::Comma))
|
||||
.delimited_by(just(Token::BraceO), just(Token::BraceC));
|
||||
|
||||
name.then(fields)
|
||||
.map(|(name, fields)| StructDecl {
|
||||
name,
|
||||
fields,
|
||||
id: state.next_id(),
|
||||
span: Default::default(),
|
||||
})
|
||||
.labelled("struct")
|
||||
}
|
||||
|
||||
fn item_parser<'src>(
|
||||
state: &'src ParserState,
|
||||
) -> impl Parser<Token<'src>, Item, Error = Error<'src>> + Clone {
|
||||
// ---- function
|
||||
|
||||
let name = ident_parser();
|
||||
|
||||
let params = name_ty_pair_parser(state)
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::ParenO), just(Token::ParenC))
|
||||
.labelled("function arguments");
|
||||
|
||||
let ret_ty = just(Token::Arrow).ignore_then(ty_parser()).or_not();
|
||||
let function = just(Token::Fn)
|
||||
.ignore_then(name)
|
||||
.then(params)
|
||||
.then(ret_ty)
|
||||
.then(
|
||||
statement_parser(state)
|
||||
.repeated()
|
||||
.delimited_by(just(Token::BraceO), just(Token::BraceC)),
|
||||
)
|
||||
.map_with_span(|(((name, params), ret_ty), body), span| FnDecl {
|
||||
name,
|
||||
params,
|
||||
ret_ty,
|
||||
id: state.next_id(),
|
||||
span,
|
||||
body,
|
||||
})
|
||||
.labelled("function");
|
||||
|
||||
// ---- item
|
||||
|
||||
function
|
||||
.map(Item::FnDecl)
|
||||
.or(struct_parser(state).map(Item::StructDecl))
|
||||
.labelled("item")
|
||||
}
|
||||
|
||||
fn file_parser<'src>(
|
||||
file_name: PathBuf,
|
||||
state: &'src ParserState,
|
||||
) -> impl Parser<Token<'src>, File, Error = Error<'src>> + Clone {
|
||||
item_parser(state)
|
||||
.repeated()
|
||||
.then_ignore(end())
|
||||
.map(move |items| File {
|
||||
name: file_name.clone(),
|
||||
items,
|
||||
})
|
||||
.labelled("file")
|
||||
}
|
||||
|
||||
pub fn parse<'src, I>(
|
||||
lexer: I,
|
||||
state: &'src ParserState,
|
||||
len: usize,
|
||||
file_name: PathBuf,
|
||||
) -> (Option<File>, Vec<Error<'src>>)
|
||||
where
|
||||
I: 'src,
|
||||
I: Iterator<Item = (Token<'src>, Span)>,
|
||||
{
|
||||
file_parser(file_name, state).parse_recovery_verbose(Stream::from_iter(len..len + 1, lexer))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{fmt::Debug, path::PathBuf};
|
||||
|
||||
use logos::Logos;
|
||||
|
||||
use super::ParserState;
|
||||
use crate::lexer::Token;
|
||||
|
||||
fn parse<'src>(src: &'src str, state: &'src ParserState) -> impl Debug + 'src {
|
||||
let lexer = Token::lexer(src);
|
||||
let len = lexer.source().len();
|
||||
|
||||
super::parse(
|
||||
lexer.spanned(),
|
||||
state,
|
||||
len,
|
||||
PathBuf::from(module_path!().replace("::", "__")),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn addition() {
|
||||
let state = ParserState::default();
|
||||
let r = parse("fn main() { 1 + 4; }", &state);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expression() {
|
||||
let state = ParserState::default();
|
||||
let r = parse("fn main() { (4 / hallo()) + 5; }", &state);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unary() {
|
||||
let state = ParserState::default();
|
||||
let r = parse(
|
||||
"fn main() {
|
||||
-(*5);
|
||||
&5;
|
||||
2 + &8;
|
||||
*6 * *8; // :)
|
||||
}",
|
||||
&state,
|
||||
);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function() {
|
||||
let state = ParserState::default();
|
||||
let r = parse("fn foo() -> u64 { 1 + 5; }", &state);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_no_else() {
|
||||
let state = ParserState::default();
|
||||
|
||||
let r = parse("fn foo() -> u64 { if false {} }", &state);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else() {
|
||||
let state = ParserState::default();
|
||||
|
||||
let r = parse("fn foo() -> u64 { if false {} else {} }", &state);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn while_loop() {
|
||||
let state = ParserState::default();
|
||||
|
||||
let r = parse("fn foo() -> u64 { while false {} }", &state);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn var_decl() {
|
||||
let state = ParserState::default();
|
||||
|
||||
let r = parse("fn foo() -> u64 { let hello: u64 = 5; let owo = 0; let nice: u64; let nothing; }", &state);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_() {
|
||||
let state = ParserState::default();
|
||||
|
||||
let r = parse("struct X { y: u64, x: u64 }", &state);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn types() {
|
||||
let state = ParserState::default();
|
||||
|
||||
let r = parse(
|
||||
"fn types() -> ptr u64 { let test: Test = 2; let int: ptr u64 = 25; }",
|
||||
&state,
|
||||
);
|
||||
insta::assert_debug_snapshot!(r);
|
||||
}
|
||||
}
|
||||
277
src/pretty.rs
Normal file
277
src/pretty.rs
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use crate::ast::{
|
||||
BinOpKind, ElsePart, Expr, ExprKind, File, IfStmt, Item, Literal, NameTyPair, Stmt, Ty, TyKind,
|
||||
UnaryOpKind,
|
||||
};
|
||||
|
||||
pub fn pretty_print_ast(ast: &File) -> String {
|
||||
let mut printer = Printer {
|
||||
out: String::new(),
|
||||
indent: 0,
|
||||
};
|
||||
|
||||
printer.print_items(&ast.items);
|
||||
|
||||
printer.out
|
||||
}
|
||||
|
||||
struct Printer {
|
||||
out: String,
|
||||
indent: usize,
|
||||
}
|
||||
impl Printer {
|
||||
fn print_items(&mut self, items: &[Item]) {
|
||||
for item in items {
|
||||
self.print_item(item);
|
||||
}
|
||||
}
|
||||
|
||||
fn print_item(&mut self, item: &Item) {
|
||||
match item {
|
||||
Item::FnDecl(fn_decl) => {
|
||||
self.word("fn ");
|
||||
self.word(&fn_decl.name);
|
||||
self.word("(");
|
||||
let params = &fn_decl.params;
|
||||
if params.len() > 0 {
|
||||
self.linebreak_indent();
|
||||
for i in 0..params.len().saturating_sub(1) {
|
||||
let param = &fn_decl.params[i];
|
||||
self.print_name_ty(param);
|
||||
self.word(",");
|
||||
self.linebreak();
|
||||
}
|
||||
if params.len() > 0 {
|
||||
self.print_name_ty(params.last().unwrap());
|
||||
}
|
||||
self.linebreak_unindent();
|
||||
}
|
||||
|
||||
self.word(") ");
|
||||
if let Some(ret_ty) = &fn_decl.ret_ty {
|
||||
self.word("-> ");
|
||||
self.print_ty(ret_ty);
|
||||
self.word(" ");
|
||||
}
|
||||
self.print_block(&fn_decl.body);
|
||||
self.linebreak();
|
||||
}
|
||||
Item::StructDecl(_) => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_name_ty(&mut self, name_ty: &NameTyPair) {
|
||||
self.word(&name_ty.name);
|
||||
self.word(": ");
|
||||
self.print_ty(&name_ty.ty);
|
||||
}
|
||||
|
||||
fn print_ty(&mut self, ty: &Ty) {
|
||||
match &ty.kind {
|
||||
TyKind::U64 => self.word("u64"),
|
||||
TyKind::Name(name) => self.word(name),
|
||||
TyKind::Ptr(ty) => {
|
||||
self.word("ptr ");
|
||||
self.print_ty(ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts off after the `{`. Indents itself first if necessary. Stops at the place where `}` should be inserted.
|
||||
fn print_block(&mut self, stmts: &[Stmt]) {
|
||||
self.word("{");
|
||||
if let [first, rest @ ..] = &stmts {
|
||||
self.linebreak_indent();
|
||||
self.print_stmt(first);
|
||||
|
||||
for stmt in rest {
|
||||
self.linebreak();
|
||||
self.print_stmt(stmt);
|
||||
}
|
||||
|
||||
self.linebreak_unindent();
|
||||
}
|
||||
self.word("}");
|
||||
}
|
||||
|
||||
/// Leaves the cursor after the semicolon
|
||||
fn print_stmt(&mut self, stmt: &Stmt) {
|
||||
match stmt {
|
||||
Stmt::VarDecl(decl) => {
|
||||
self.word("let ");
|
||||
self.word(&decl.name);
|
||||
if let Some(ty) = &decl.ty {
|
||||
self.word(": ");
|
||||
self.print_ty(ty);
|
||||
}
|
||||
if let Some(rhs) = &decl.rhs {
|
||||
self.word(" = ");
|
||||
self.print_expr(rhs);
|
||||
}
|
||||
self.word(";");
|
||||
}
|
||||
Stmt::Assignment(assign) => {
|
||||
self.print_expr(&assign.place);
|
||||
self.word(" = ");
|
||||
self.print_expr(&assign.rhs);
|
||||
self.word(";");
|
||||
}
|
||||
Stmt::IfStmt(if_stmt) => {
|
||||
self.print_if(if_stmt);
|
||||
}
|
||||
Stmt::WhileStmt(while_stmt) => {
|
||||
self.word("while ");
|
||||
self.print_expr(&while_stmt.cond);
|
||||
self.print_block(&while_stmt.body);
|
||||
}
|
||||
Stmt::LoopStmt(loop_stmt) => {
|
||||
self.word("loop ");
|
||||
self.print_block(&loop_stmt.body);
|
||||
}
|
||||
Stmt::Item(item) => {
|
||||
self.print_item(item);
|
||||
}
|
||||
Stmt::Expr(expr) => {
|
||||
self.print_expr(expr);
|
||||
self.word(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_if(&mut self, if_stmt: &IfStmt) {
|
||||
self.word("if ");
|
||||
self.print_expr(&if_stmt.cond);
|
||||
self.word(" ");
|
||||
self.print_block(&if_stmt.body);
|
||||
if let Some(else_part) = &if_stmt.else_part {
|
||||
self.word(" else ");
|
||||
match else_part {
|
||||
ElsePart::Else(stmts, _) => {
|
||||
self.print_block(stmts);
|
||||
}
|
||||
ElsePart::ElseIf(if_stmt) => {
|
||||
self.print_if(if_stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_expr(&mut self, expr: &Expr) {
|
||||
match &expr.kind {
|
||||
ExprKind::BinOp(bin_op) => {
|
||||
self.print_expr_wrapped(&bin_op.lhs);
|
||||
self.word(match bin_op.kind {
|
||||
BinOpKind::Eq => " == ",
|
||||
BinOpKind::Neq => " != ",
|
||||
BinOpKind::Gt => " > ",
|
||||
BinOpKind::Lt => " < ",
|
||||
BinOpKind::GtEq => " >= ",
|
||||
BinOpKind::LtEq => " <= ",
|
||||
BinOpKind::Add => " + ",
|
||||
BinOpKind::Sub => " - ",
|
||||
BinOpKind::Mul => " * ",
|
||||
BinOpKind::Div => " / ",
|
||||
BinOpKind::Mod => " % ",
|
||||
BinOpKind::Shr => " >> ",
|
||||
BinOpKind::Shl => " << ",
|
||||
BinOpKind::And => " && ",
|
||||
BinOpKind::Or => " || ",
|
||||
BinOpKind::BitAnd => " & ",
|
||||
BinOpKind::BitOr => " | ",
|
||||
BinOpKind::Xor => " ^ ",
|
||||
});
|
||||
self.print_expr_wrapped(&bin_op.rhs);
|
||||
}
|
||||
ExprKind::UnaryOp(unary_op) => {
|
||||
self.word(match unary_op.kind {
|
||||
UnaryOpKind::Not => "!",
|
||||
UnaryOpKind::Neg => "-",
|
||||
UnaryOpKind::Deref => "*",
|
||||
UnaryOpKind::AddrOf => "&",
|
||||
});
|
||||
self.print_expr_wrapped(&unary_op.expr);
|
||||
}
|
||||
ExprKind::FieldAccess(field_access) => {
|
||||
self.print_expr(&field_access.expr);
|
||||
self.word(".");
|
||||
self.word(&field_access.field_name);
|
||||
}
|
||||
ExprKind::Call(call) => {
|
||||
self.print_expr(&call.callee);
|
||||
self.word("(");
|
||||
if let [first, rest @ ..] = &*call.args {
|
||||
self.print_expr(first);
|
||||
for expr in rest {
|
||||
self.word(", ");
|
||||
self.print_expr(expr);
|
||||
}
|
||||
}
|
||||
self.word(")");
|
||||
}
|
||||
ExprKind::Literal(literal) => match literal {
|
||||
Literal::Integer(int, _) => write!(self.out, "{int}").unwrap(),
|
||||
Literal::String(string, _) => {
|
||||
self.word("\"");
|
||||
// FIXME: Handle escapes.
|
||||
self.word(string);
|
||||
self.word("\"");
|
||||
}
|
||||
},
|
||||
ExprKind::Name(name) => {
|
||||
self.word(name);
|
||||
}
|
||||
ExprKind::Array(exprs) => {
|
||||
self.word("[");
|
||||
if let [first, rest @ ..] = exprs.as_slice() {
|
||||
self.print_expr(first);
|
||||
for expr in rest {
|
||||
self.word(", ");
|
||||
self.print_expr(expr);
|
||||
}
|
||||
}
|
||||
self.word("]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_expr_wrapped(&mut self, expr: &Expr) {
|
||||
match expr.kind {
|
||||
ExprKind::Literal(_)
|
||||
| ExprKind::Array(_)
|
||||
| ExprKind::Call(_)
|
||||
| ExprKind::Name(_)
|
||||
| ExprKind::FieldAccess(_) => {
|
||||
self.print_expr(expr);
|
||||
}
|
||||
ExprKind::BinOp(_) | ExprKind::UnaryOp(_) => {
|
||||
self.word("(");
|
||||
self.print_expr(expr);
|
||||
self.word(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// utility functions
|
||||
|
||||
fn word(&mut self, word: &str) {
|
||||
self.out.push_str(word);
|
||||
}
|
||||
|
||||
fn linebreak_indent(&mut self) {
|
||||
self.indent += 1;
|
||||
self.linebreak();
|
||||
}
|
||||
|
||||
fn linebreak_unindent(&mut self) {
|
||||
self.indent -= 1;
|
||||
self.linebreak();
|
||||
}
|
||||
|
||||
fn linebreak(&mut self) {
|
||||
self.word("\n");
|
||||
self.word(&" ".repeat(self.indent))
|
||||
}
|
||||
}
|
||||
15
src/snapshots/ub__lexer__tests__idents.snap
Normal file
15
src/snapshots/ub__lexer__tests__idents.snap
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
source: src/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Ident(
|
||||
"hello",
|
||||
),
|
||||
Ident(
|
||||
"w_world",
|
||||
),
|
||||
Ident(
|
||||
"b235_",
|
||||
),
|
||||
]
|
||||
14
src/snapshots/ub__lexer__tests__keywords.snap
Normal file
14
src/snapshots/ub__lexer__tests__keywords.snap
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
source: src/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Struct,
|
||||
Fn,
|
||||
Dot,
|
||||
If,
|
||||
Else,
|
||||
While,
|
||||
Loop,
|
||||
Semi,
|
||||
]
|
||||
18
src/snapshots/ub__lexer__tests__literals.snap
Normal file
18
src/snapshots/ub__lexer__tests__literals.snap
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
source: src/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
String(
|
||||
"\"hello friend\"",
|
||||
),
|
||||
Integer(
|
||||
"5",
|
||||
),
|
||||
String(
|
||||
"\"morning\"",
|
||||
),
|
||||
Integer(
|
||||
"3263475",
|
||||
),
|
||||
]
|
||||
33
src/snapshots/ub__lexer__tests__punctuation.snap
Normal file
33
src/snapshots/ub__lexer__tests__punctuation.snap
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
source: src/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
BraceO,
|
||||
BraceC,
|
||||
BracketO,
|
||||
BracketC,
|
||||
ParenO,
|
||||
ParenC,
|
||||
Dot,
|
||||
Comma,
|
||||
Semi,
|
||||
Eq,
|
||||
EqEq,
|
||||
BangEq,
|
||||
GreaterEq,
|
||||
LessEq,
|
||||
Less,
|
||||
Greater,
|
||||
Plus,
|
||||
Minus,
|
||||
Asterisk,
|
||||
Slash,
|
||||
Or,
|
||||
OrOr,
|
||||
Ampersand,
|
||||
AndAnd,
|
||||
Caret,
|
||||
Arrow,
|
||||
Colon,
|
||||
]
|
||||
8
src/snapshots/ub__lexer__tests__whitespace.snap
Normal file
8
src/snapshots/ub__lexer__tests__whitespace.snap
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Dot,
|
||||
Dot,
|
||||
]
|
||||
65
src/snapshots/ub__parser__tests__addition.snap
Normal file
65
src/snapshots/ub__parser__tests__addition.snap
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
FnDecl(
|
||||
FnDecl {
|
||||
name: "main",
|
||||
params: [],
|
||||
ret_ty: None,
|
||||
id: NodeId(
|
||||
6,
|
||||
),
|
||||
span: 0..20,
|
||||
body: [
|
||||
Expr(
|
||||
Expr {
|
||||
kind: BinOp(
|
||||
BinOp {
|
||||
kind: Add,
|
||||
lhs: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
1,
|
||||
12..13,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
3,
|
||||
),
|
||||
span: 12..13,
|
||||
},
|
||||
rhs: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
4,
|
||||
16..17,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
4,
|
||||
),
|
||||
span: 16..17,
|
||||
},
|
||||
span: 12..17,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
5,
|
||||
),
|
||||
span: 12..17,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
97
src/snapshots/ub__parser__tests__expression.snap
Normal file
97
src/snapshots/ub__parser__tests__expression.snap
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
FnDecl(
|
||||
FnDecl {
|
||||
name: "main",
|
||||
params: [],
|
||||
ret_ty: None,
|
||||
id: NodeId(
|
||||
12,
|
||||
),
|
||||
span: 0..32,
|
||||
body: [
|
||||
Expr(
|
||||
Expr {
|
||||
kind: BinOp(
|
||||
BinOp {
|
||||
kind: Add,
|
||||
lhs: Expr {
|
||||
kind: BinOp(
|
||||
BinOp {
|
||||
kind: Div,
|
||||
lhs: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
4,
|
||||
13..14,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
6,
|
||||
),
|
||||
span: 13..14,
|
||||
},
|
||||
rhs: Expr {
|
||||
kind: Call(
|
||||
Call {
|
||||
callee: Expr {
|
||||
kind: Name(
|
||||
"hallo",
|
||||
),
|
||||
id: NodeId(
|
||||
7,
|
||||
),
|
||||
span: 17..22,
|
||||
},
|
||||
args: [],
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
8,
|
||||
),
|
||||
span: 17..22,
|
||||
},
|
||||
span: 13..22,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
9,
|
||||
),
|
||||
span: 13..22,
|
||||
},
|
||||
rhs: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
5,
|
||||
28..29,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
10,
|
||||
),
|
||||
span: 28..29,
|
||||
},
|
||||
span: 13..29,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
11,
|
||||
),
|
||||
span: 13..29,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
70
src/snapshots/ub__parser__tests__function.snap
Normal file
70
src/snapshots/ub__parser__tests__function.snap
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
FnDecl(
|
||||
FnDecl {
|
||||
name: "foo",
|
||||
params: [],
|
||||
ret_ty: Some(
|
||||
Ty {
|
||||
span: 12..15,
|
||||
kind: U64,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
6,
|
||||
),
|
||||
span: 0..26,
|
||||
body: [
|
||||
Expr(
|
||||
Expr {
|
||||
kind: BinOp(
|
||||
BinOp {
|
||||
kind: Add,
|
||||
lhs: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
1,
|
||||
18..19,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
3,
|
||||
),
|
||||
span: 18..19,
|
||||
},
|
||||
rhs: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
5,
|
||||
22..23,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
4,
|
||||
),
|
||||
span: 22..23,
|
||||
},
|
||||
span: 18..23,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
5,
|
||||
),
|
||||
span: 18..23,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
53
src/snapshots/ub__parser__tests__if_else.snap
Normal file
53
src/snapshots/ub__parser__tests__if_else.snap
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
FnDecl(
|
||||
FnDecl {
|
||||
name: "foo",
|
||||
params: [],
|
||||
ret_ty: Some(
|
||||
Ty {
|
||||
span: 12..15,
|
||||
kind: U64,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
1,
|
||||
),
|
||||
span: 0..39,
|
||||
body: [
|
||||
IfStmt(
|
||||
IfStmt {
|
||||
cond: Expr {
|
||||
kind: Name(
|
||||
"false",
|
||||
),
|
||||
id: NodeId(
|
||||
0,
|
||||
),
|
||||
span: 21..26,
|
||||
},
|
||||
body: [],
|
||||
else_part: Some(
|
||||
Else(
|
||||
[],
|
||||
35..37,
|
||||
),
|
||||
),
|
||||
span: 18..37,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
48
src/snapshots/ub__parser__tests__if_no_else.snap
Normal file
48
src/snapshots/ub__parser__tests__if_no_else.snap
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
FnDecl(
|
||||
FnDecl {
|
||||
name: "foo",
|
||||
params: [],
|
||||
ret_ty: Some(
|
||||
Ty {
|
||||
span: 12..15,
|
||||
kind: U64,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
1,
|
||||
),
|
||||
span: 0..31,
|
||||
body: [
|
||||
IfStmt(
|
||||
IfStmt {
|
||||
cond: Expr {
|
||||
kind: Name(
|
||||
"false",
|
||||
),
|
||||
id: NodeId(
|
||||
0,
|
||||
),
|
||||
span: 21..26,
|
||||
},
|
||||
body: [],
|
||||
else_part: None,
|
||||
span: 18..29,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
47
src/snapshots/ub__parser__tests__struct_.snap
Normal file
47
src/snapshots/ub__parser__tests__struct_.snap
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
StructDecl(
|
||||
StructDecl {
|
||||
name: "X",
|
||||
fields: [
|
||||
NameTyPair {
|
||||
name: "y",
|
||||
ty: Ty {
|
||||
span: 14..17,
|
||||
kind: U64,
|
||||
},
|
||||
id: NodeId(
|
||||
0,
|
||||
),
|
||||
span: 11..17,
|
||||
},
|
||||
NameTyPair {
|
||||
name: "x",
|
||||
ty: Ty {
|
||||
span: 22..25,
|
||||
kind: U64,
|
||||
},
|
||||
id: NodeId(
|
||||
1,
|
||||
),
|
||||
span: 19..25,
|
||||
},
|
||||
],
|
||||
id: NodeId(
|
||||
2,
|
||||
),
|
||||
span: 0..0,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
96
src/snapshots/ub__parser__tests__types.snap
Normal file
96
src/snapshots/ub__parser__tests__types.snap
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
FnDecl(
|
||||
FnDecl {
|
||||
name: "types",
|
||||
params: [],
|
||||
ret_ty: Some(
|
||||
Ty {
|
||||
span: 14..21,
|
||||
kind: Ptr(
|
||||
Ty {
|
||||
span: 18..21,
|
||||
kind: U64,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
2,
|
||||
),
|
||||
span: 0..68,
|
||||
body: [
|
||||
VarDecl(
|
||||
VarDecl {
|
||||
name: "test",
|
||||
ty: Some(
|
||||
Ty {
|
||||
span: 34..38,
|
||||
kind: Name(
|
||||
"Test",
|
||||
),
|
||||
},
|
||||
),
|
||||
rhs: Some(
|
||||
Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
2,
|
||||
41..42,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
0,
|
||||
),
|
||||
span: 41..42,
|
||||
},
|
||||
),
|
||||
span: 0..0,
|
||||
},
|
||||
),
|
||||
VarDecl(
|
||||
VarDecl {
|
||||
name: "int",
|
||||
ty: Some(
|
||||
Ty {
|
||||
span: 53..60,
|
||||
kind: Ptr(
|
||||
Ty {
|
||||
span: 57..60,
|
||||
kind: U64,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
rhs: Some(
|
||||
Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
25,
|
||||
63..65,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
1,
|
||||
),
|
||||
span: 63..65,
|
||||
},
|
||||
),
|
||||
span: 0..0,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
203
src/snapshots/ub__parser__tests__unary.snap
Normal file
203
src/snapshots/ub__parser__tests__unary.snap
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
FnDecl(
|
||||
FnDecl {
|
||||
name: "main",
|
||||
params: [],
|
||||
ret_ty: None,
|
||||
id: NodeId(
|
||||
28,
|
||||
),
|
||||
span: 0..63,
|
||||
body: [
|
||||
Expr(
|
||||
Expr {
|
||||
kind: UnaryOp(
|
||||
UnaryOp {
|
||||
expr: Expr {
|
||||
kind: UnaryOp(
|
||||
UnaryOp {
|
||||
expr: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
5,
|
||||
19..20,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
3,
|
||||
),
|
||||
span: 19..20,
|
||||
},
|
||||
kind: Deref,
|
||||
span: 19..20,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
4,
|
||||
),
|
||||
span: 19..20,
|
||||
},
|
||||
kind: Neg,
|
||||
span: 19..20,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
5,
|
||||
),
|
||||
span: 19..20,
|
||||
},
|
||||
),
|
||||
Expr(
|
||||
Expr {
|
||||
kind: UnaryOp(
|
||||
UnaryOp {
|
||||
expr: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
5,
|
||||
28..29,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
8,
|
||||
),
|
||||
span: 28..29,
|
||||
},
|
||||
kind: AddrOf,
|
||||
span: 28..29,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
9,
|
||||
),
|
||||
span: 28..29,
|
||||
},
|
||||
),
|
||||
Expr(
|
||||
Expr {
|
||||
kind: BinOp(
|
||||
BinOp {
|
||||
kind: Add,
|
||||
lhs: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
2,
|
||||
35..36,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
14,
|
||||
),
|
||||
span: 35..36,
|
||||
},
|
||||
rhs: Expr {
|
||||
kind: UnaryOp(
|
||||
UnaryOp {
|
||||
expr: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
8,
|
||||
40..41,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
15,
|
||||
),
|
||||
span: 40..41,
|
||||
},
|
||||
kind: AddrOf,
|
||||
span: 40..41,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
16,
|
||||
),
|
||||
span: 40..41,
|
||||
},
|
||||
span: 35..41,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
17,
|
||||
),
|
||||
span: 35..41,
|
||||
},
|
||||
),
|
||||
Expr(
|
||||
Expr {
|
||||
kind: BinOp(
|
||||
BinOp {
|
||||
kind: Mul,
|
||||
lhs: Expr {
|
||||
kind: UnaryOp(
|
||||
UnaryOp {
|
||||
expr: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
6,
|
||||
48..49,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
23,
|
||||
),
|
||||
span: 48..49,
|
||||
},
|
||||
kind: Deref,
|
||||
span: 48..49,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
24,
|
||||
),
|
||||
span: 48..49,
|
||||
},
|
||||
rhs: Expr {
|
||||
kind: UnaryOp(
|
||||
UnaryOp {
|
||||
expr: Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
8,
|
||||
53..54,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
25,
|
||||
),
|
||||
span: 53..54,
|
||||
},
|
||||
kind: Deref,
|
||||
span: 53..54,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
26,
|
||||
),
|
||||
span: 53..54,
|
||||
},
|
||||
span: 48..54,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
27,
|
||||
),
|
||||
span: 48..54,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
100
src/snapshots/ub__parser__tests__var_decl.snap
Normal file
100
src/snapshots/ub__parser__tests__var_decl.snap
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
FnDecl(
|
||||
FnDecl {
|
||||
name: "foo",
|
||||
params: [],
|
||||
ret_ty: Some(
|
||||
Ty {
|
||||
span: 12..15,
|
||||
kind: U64,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
2,
|
||||
),
|
||||
span: 0..80,
|
||||
body: [
|
||||
VarDecl(
|
||||
VarDecl {
|
||||
name: "hello",
|
||||
ty: Some(
|
||||
Ty {
|
||||
span: 29..32,
|
||||
kind: U64,
|
||||
},
|
||||
),
|
||||
rhs: Some(
|
||||
Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
5,
|
||||
35..36,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
0,
|
||||
),
|
||||
span: 35..36,
|
||||
},
|
||||
),
|
||||
span: 0..0,
|
||||
},
|
||||
),
|
||||
VarDecl(
|
||||
VarDecl {
|
||||
name: "owo",
|
||||
ty: None,
|
||||
rhs: Some(
|
||||
Expr {
|
||||
kind: Literal(
|
||||
Integer(
|
||||
0,
|
||||
48..49,
|
||||
),
|
||||
),
|
||||
id: NodeId(
|
||||
1,
|
||||
),
|
||||
span: 48..49,
|
||||
},
|
||||
),
|
||||
span: 0..0,
|
||||
},
|
||||
),
|
||||
VarDecl(
|
||||
VarDecl {
|
||||
name: "nice",
|
||||
ty: Some(
|
||||
Ty {
|
||||
span: 61..64,
|
||||
kind: U64,
|
||||
},
|
||||
),
|
||||
rhs: None,
|
||||
span: 0..0,
|
||||
},
|
||||
),
|
||||
VarDecl(
|
||||
VarDecl {
|
||||
name: "nothing",
|
||||
ty: None,
|
||||
rhs: None,
|
||||
span: 0..0,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
47
src/snapshots/ub__parser__tests__while_loop.snap
Normal file
47
src/snapshots/ub__parser__tests__while_loop.snap
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: r
|
||||
---
|
||||
(
|
||||
Some(
|
||||
File {
|
||||
name: "ub__parser__tests",
|
||||
items: [
|
||||
FnDecl(
|
||||
FnDecl {
|
||||
name: "foo",
|
||||
params: [],
|
||||
ret_ty: Some(
|
||||
Ty {
|
||||
span: 12..15,
|
||||
kind: U64,
|
||||
},
|
||||
),
|
||||
id: NodeId(
|
||||
1,
|
||||
),
|
||||
span: 0..34,
|
||||
body: [
|
||||
WhileStmt(
|
||||
WhileStmt {
|
||||
cond: Expr {
|
||||
kind: Name(
|
||||
"false",
|
||||
),
|
||||
id: NodeId(
|
||||
0,
|
||||
),
|
||||
span: 24..29,
|
||||
},
|
||||
body: [],
|
||||
span: 18..32,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue