mirror of
https://github.com/Kindelia/Kind2.git
synced 2024-10-06 04:17:14 +03:00
feat: started parser
This commit is contained in:
parent
b09b544e55
commit
b5f28b5caf
137
grammar.md
Normal file
137
grammar.md
Normal file
@ -0,0 +1,137 @@
|
||||
# Lexical Grammar:
|
||||
|
||||
```
|
||||
whitespace:
|
||||
\t | \r | ' ' | \f | \v
|
||||
|
||||
line_terminator:
|
||||
\n
|
||||
|
||||
ident_letter_start:
|
||||
[a-zA-Z] | '_'
|
||||
|
||||
ident_letter:
|
||||
ident_letter_start | '$'
|
||||
|
||||
ident_atom:
|
||||
ident_letter+
|
||||
|
||||
ident_atom_start:
|
||||
ident_letter_start ident_letter+
|
||||
|
||||
ident:
|
||||
ident_atom_start ('.' ident_atom)*
|
||||
|
||||
string:
|
||||
'"' string_item '"'
|
||||
|
||||
string_item:
|
||||
string_char | string_slash
|
||||
|
||||
string_char:
|
||||
[^('"' | '\']
|
||||
|
||||
string_slash:
|
||||
'\' ('\' | ''' | '"' | "n" | "t"
|
||||
| "0" | "x" hex_char hex_char
|
||||
| "u" hex_char hex_char hex_char hex_char)
|
||||
|
||||
hex_char:
|
||||
[0-9a-fA-F]
|
||||
|
||||
bin_char:
|
||||
'0' | '1'
|
||||
|
||||
octal_char:
|
||||
[0-7]
|
||||
|
||||
num:
|
||||
[0-9]+ | '0x' hex_char+ | '0b' bin_char+ | '0o' octal_char+
|
||||
|
||||
chr:
|
||||
'\'' string_item '\''
|
||||
|
||||
doc:
|
||||
'//' [^(\n | eof)]*
|
||||
'///' [^(\n | eof)]*
|
||||
'/*' [^(*/)] '*/'
|
||||
|
||||
symbols:
|
||||
'(' | ')' | '{' | '}' | '[' | ']' |
|
||||
'=' | ':' | ';' | '=>' | '$' | ',' |
|
||||
'+' | '-' | '\' | '*' | '>' | '<' |
|
||||
'<=' | '>=' | '==' | '!=' | '>>' | '<<'
|
||||
|
||||
float:
|
||||
num+ '.' num+
|
||||
|
||||
keyword:
|
||||
'do' | 'if' | 'else' | 'match' | 'open' | 'ask' | 'let'
|
||||
|
||||
token:
|
||||
doc | symbol | keyword | chr | ident | string_item | num | float
|
||||
```
|
||||
|
||||
# How the Auto semicolon insert works:
|
||||
It works by adding semicolon when sequence of newlines are detected after
|
||||
some of the tokens
|
||||
- '='
|
||||
- 'let'
|
||||
- 'ask'
|
||||
|
||||
# Syntax
|
||||
|
||||
```
|
||||
Atom ::= ident ; Variable
|
||||
| num ; Integer literal
|
||||
| float ; Float literal
|
||||
| string ; String literal
|
||||
| hlp ; 'Help' marker
|
||||
| chr ; Character
|
||||
| '[' Expr* ']' ; Array without commas
|
||||
| '[' Expr (',' Expr)* ] ; Array with commas
|
||||
| '$' Atom Atom ; Sigma type constructor
|
||||
| '(' Expr ',' Expr ')' ; Tuple
|
||||
| '(' Expr '::' Expr ')' ; Type Annotation
|
||||
|
||||
Call ::= Atom ' ' Call ; Call
|
||||
|
||||
Arrow ::= Call -> Expr ; Arrow
|
||||
| Call ; Call
|
||||
|
||||
Sttm ::= ask Expr ';' ; Monadic bind statement without assignment
|
||||
| ask Ident '=' Expr ';' ; Moandic bind statement with assingment
|
||||
| return ';' ; Monadic return
|
||||
| Expr ';' ; Just executes an expression
|
||||
|
||||
Match ::= 'match' ident ident ('=' Expr)? '{' (ident '=>' Expr) '}'
|
||||
|
||||
Expr ::=
|
||||
| ident '=>' Expr ; Lambda
|
||||
| let Ident '=' Expr ';' Expr ; Variable binding
|
||||
| if Expr { Expr } else { Expr } ; If/else statement
|
||||
| Match ; Dependent eliminator for sum types
|
||||
| Open ; Dependent eliminator for record types
|
||||
| do '{' Sttm* '}' ; Do notation
|
||||
| '[' ident ':' Expr ']' -> Expr ; Sigma type
|
||||
| '(' ident ':' Expr ')' -> Expr ; Pi type
|
||||
| '(' Op Expr Expr ')' ; Binary operation
|
||||
| '(' Expr ')' ; Duplicated because it's easier to treat it here.
|
||||
| ## ident '/' ident ; Substitution
|
||||
|
||||
Pat ::= num | ident | string | (ident pat*)
|
||||
|
||||
Rule ::= Pat* '=' Expr
|
||||
|
||||
Impl := '(' Ident ':' Expr ')'
|
||||
| '<' Ident ':' Expr '>'
|
||||
| '<' Ident '>'
|
||||
|
||||
Binding := '(' Ident ':' Expr ')'
|
||||
| '<' Ident ':' Expr '>'
|
||||
|
||||
Entry ::= ident Binding* ':' Expr Semi
|
||||
ident Rule
|
||||
| ident Binding* ':' _ '{' Expr '}'
|
||||
|
||||
```
|
@ -6,3 +6,5 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
kind-span = { path = "../kind-span" }
|
||||
kind-tree = { path = "../kind-tree" }
|
21
src/kind-parser/src/errors.rs
Normal file
21
src/kind-parser/src/errors.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use kind_span::Span;
|
||||
|
||||
use crate::lexer::tokens::Token;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EncodeSequence {
|
||||
Hexa,
|
||||
Octal,
|
||||
Binary,
|
||||
Unicode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SyntaxError {
|
||||
UnfinishedString(Span),
|
||||
UnfinishedComment(Span),
|
||||
InvalidEscapeSequence(EncodeSequence, Span),
|
||||
InvalidNumberRepresentation(EncodeSequence, Span),
|
||||
UnexpectedChar(char, Span),
|
||||
UnexpectedToken(Token, Span, Option<Token>)
|
||||
}
|
48
src/kind-parser/src/expr.rs
Normal file
48
src/kind-parser/src/expr.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use kind_tree::expr::{Expr, ExprKind};
|
||||
use kind_tree::symbol::{Ident, Symbol};
|
||||
|
||||
use crate::errors::SyntaxError;
|
||||
use crate::lexer::tokens::Token;
|
||||
use crate::state::Parser;
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn parse_lambda(&mut self, name: String) -> Result<Box<Expr>, SyntaxError> {
|
||||
// We are assuming that it came from parse_atom.
|
||||
// so we just remove the argument and "=>"
|
||||
let name_span = self.advance().1;
|
||||
self.advance();
|
||||
|
||||
let expr = self.parse_expr()?;
|
||||
let end_range = expr.span;
|
||||
|
||||
let ident = Ident::new(Symbol(name), self.ctx, name_span);
|
||||
|
||||
Ok(Box::new(Expr {
|
||||
data: ExprKind::Lambda(ident, expr),
|
||||
span: name_span.mix(end_range),
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_atom(&mut self) -> Result<Box<Expr>, SyntaxError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn parse_expr(&mut self) -> Result<Box<Expr>, SyntaxError> {
|
||||
// Simply matching both on the current and the next one.
|
||||
// it's useful for a lot of tokens.
|
||||
match (self.get().clone(), self.peek()) {
|
||||
(Token::Id(name), Token::FatArrow) => self.parse_lambda(name),
|
||||
(Token::LPar, Token::Id(name)) => {
|
||||
let start = self.advance().1;
|
||||
let res = match self.peek() {
|
||||
_ => todo!()
|
||||
};
|
||||
self.eat_variant(&Token::LPar)?;
|
||||
todo!()
|
||||
}
|
||||
(Token::LBracket, Token::Id(name)) => todo!(),
|
||||
(Token::LPar, Token::Id(name)) => todo!(),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
65
src/kind-parser/src/lexer/comments.rs
Normal file
65
src/kind-parser/src/lexer/comments.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use kind_span::Span;
|
||||
|
||||
use crate::errors::SyntaxError;
|
||||
use crate::lexer::tokens::Token;
|
||||
use crate::Lexer;
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
/// Single line comments
|
||||
pub fn lex_comment(&mut self, start: usize) -> (Token, Span) {
|
||||
self.next_char();
|
||||
let mut is_doc = false;
|
||||
if let Some('/') = self.peekable.peek() {
|
||||
self.next_char();
|
||||
is_doc = true;
|
||||
}
|
||||
let cmt = self.accumulate_while(&|x| x != '\n');
|
||||
(Token::Comment(is_doc, cmt.to_string()), self.mk_span(start))
|
||||
}
|
||||
|
||||
/// Parses multi line comments with nested comments
|
||||
/// really useful
|
||||
pub fn lex_multiline_comment(&mut self, start: usize) -> (Token, Span) {
|
||||
let mut size = 0;
|
||||
self.next_char();
|
||||
|
||||
let mut next = | p: &mut Lexer<'a>, x: char | {
|
||||
size += x.len_utf8();
|
||||
p.peekable.next();
|
||||
p.adv_col(x);
|
||||
};
|
||||
|
||||
self.comment_depth += 1;
|
||||
|
||||
while let Some(&x) = self.peekable.peek() {
|
||||
match x {
|
||||
'*' => {
|
||||
next(self, x);
|
||||
if let Some('/') = self.peekable.peek() {
|
||||
self.comment_depth -= 1;
|
||||
if self.comment_depth == 0 {
|
||||
next(self, '/');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
'/' => {
|
||||
next(self, x);
|
||||
if let Some('*') = self.peekable.peek() {
|
||||
self.comment_depth += 1;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
next(self, x);
|
||||
}
|
||||
self.pos += size;
|
||||
if self.comment_depth != 0 {
|
||||
(Token::Error(Box::new(SyntaxError::UnfinishedComment(self.mk_span(start)))), self.mk_span(start))
|
||||
} else {
|
||||
let str = &self.input[..size - 2];
|
||||
self.input = &self.input[size..];
|
||||
(Token::Comment(false, str.to_string()), self.mk_span(start))
|
||||
}
|
||||
}
|
||||
}
|
118
src/kind-parser/src/lexer/literals.rs
Normal file
118
src/kind-parser/src/lexer/literals.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use kind_span::Span;
|
||||
|
||||
use crate::errors::{EncodeSequence, SyntaxError};
|
||||
use crate::lexer::tokens::Token;
|
||||
use crate::Lexer;
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
/// Lex a sequence of digits of the base @base@ with
|
||||
/// maximum length of @size@ and turns it into a char.
|
||||
fn lex_char_encoded(&mut self, start: usize, size: usize, base: u32, err: EncodeSequence) -> Result<char, SyntaxError> {
|
||||
let string = self.next_chars(size);
|
||||
let to_chr = string.and_then(|x| u32::from_str_radix(x, base).ok());
|
||||
if let Some(chr) = to_chr.and_then(char::from_u32) {
|
||||
return Ok(chr);
|
||||
}
|
||||
Err(SyntaxError::InvalidEscapeSequence(err, self.mk_span(start)))
|
||||
}
|
||||
|
||||
/// Turns a escaped char into a normal char.
|
||||
fn lex_escaped_char(&mut self, start: usize) -> Result<char, SyntaxError> {
|
||||
match self.peekable.peek() {
|
||||
None => Err(SyntaxError::UnfinishedString(self.mk_span(start))),
|
||||
Some(&x) => {
|
||||
self.next_char();
|
||||
match x {
|
||||
'\'' => Ok('\''),
|
||||
'\"' => Ok('\"'),
|
||||
'n' => Ok('\n'),
|
||||
'r' => Ok('\r'),
|
||||
't' => Ok('\t'),
|
||||
'0' => Ok('\0'),
|
||||
'\\' => Ok('\\'),
|
||||
'x' => self.lex_char_encoded(start, 2, 16, EncodeSequence::Hexa),
|
||||
'u' => self.lex_char_encoded(start, 4, 16, EncodeSequence::Unicode),
|
||||
other => Ok(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lex a base-10 digit.
|
||||
fn lex_digit(&mut self, start: usize) -> (Token, Span) {
|
||||
let num = self.accumulate_while(&|x| x.is_ascii_digit());
|
||||
(Token::Num(num.parse::<u64>().unwrap()), self.mk_span(start))
|
||||
}
|
||||
|
||||
/// Lexes a number of base @base@ removing the first
|
||||
/// character that indicates the encoding
|
||||
fn lex_base(&mut self, start: usize, base: u32, err: EncodeSequence) -> (Token, Span) {
|
||||
self.next_char();
|
||||
let num = self.accumulate_while(&|x| x.is_digit(base));
|
||||
if let Ok(res) = u64::from_str_radix(num, base) {
|
||||
(Token::Num(res), self.mk_span(start))
|
||||
} else {
|
||||
(Token::Error(Box::new(SyntaxError::InvalidNumberRepresentation(err, self.mk_span(start)))), self.mk_span(start))
|
||||
}
|
||||
}
|
||||
|
||||
/// Lex numbers with decimal, hexadecimal, binary or octal.
|
||||
pub fn lex_number(&mut self) -> (Token, Span) {
|
||||
let start = self.pos;
|
||||
match self.peekable.peek() {
|
||||
None => (Token::Eof, self.mk_span(start)),
|
||||
Some('0') => {
|
||||
self.next_char();
|
||||
match self.peekable.peek() {
|
||||
Some('x') => self.lex_base(start, 16, EncodeSequence::Hexa),
|
||||
Some('o') => self.lex_base(start, 8, EncodeSequence::Octal),
|
||||
Some('b') => self.lex_base(start, 2, EncodeSequence::Binary),
|
||||
Some('0'..='9') => self.lex_digit(start),
|
||||
Some(_) => (Token::Num(0), self.mk_span(start)),
|
||||
None => (Token::Num(0), self.mk_span(start)),
|
||||
}
|
||||
}
|
||||
Some('0'..='9') => self.lex_digit(start),
|
||||
Some(_) => (Token::Num(0), self.mk_span(start)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lexes a string that starts with '"' and ends with the
|
||||
/// same char. each string item can contain a escaped char
|
||||
/// and if the esaped char is not well-formed then it will
|
||||
/// acummulate the error until the end of the string.
|
||||
/// TODO: Accumulate multiple encoding errors?
|
||||
pub fn lex_string(&mut self) -> (Token, Span) {
|
||||
let start = self.pos;
|
||||
|
||||
self.next_char();
|
||||
|
||||
let mut string = String::new();
|
||||
let mut error: Option<(Token, Span)> = None;
|
||||
|
||||
while let Some(&x) = self.peekable.peek() {
|
||||
let chr_start = self.pos;
|
||||
match x {
|
||||
'\"' => break,
|
||||
'\\' => {
|
||||
self.next_char();
|
||||
match self.lex_escaped_char(chr_start) {
|
||||
Ok(x) => string.push(x),
|
||||
Err(t) => {
|
||||
self.accumulate_while(&|x| x != '"');
|
||||
error = Some((Token::Error(Box::new(t)), self.mk_span(start)));
|
||||
}
|
||||
}
|
||||
}
|
||||
x => string.push(x),
|
||||
}
|
||||
self.next_char();
|
||||
}
|
||||
|
||||
match (self.next_char(), error) {
|
||||
(_, Some(err)) => err,
|
||||
(Some('"'), _) => (Token::Str(string), self.mk_span(start)),
|
||||
_ => (Token::Error(Box::new(SyntaxError::UnfinishedString(self.mk_span(start)))), self.mk_span(start)),
|
||||
}
|
||||
}
|
||||
}
|
144
src/kind-parser/src/lexer/mod.rs
Normal file
144
src/kind-parser/src/lexer/mod.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use kind_span::Span;
|
||||
|
||||
use crate::{errors::SyntaxError};
|
||||
|
||||
use self::{state::Lexer, tokens::Token};
|
||||
|
||||
pub mod literals;
|
||||
pub mod state;
|
||||
pub mod comments;
|
||||
pub mod tokens;
|
||||
|
||||
fn is_whitespace(chr: char) -> bool {
|
||||
matches!(chr, ' ' | '\r' | '\t')
|
||||
}
|
||||
|
||||
fn is_valid_id(chr: char) -> bool {
|
||||
chr.is_alphanumeric() || matches!(chr, '_' | '$' | '.')
|
||||
}
|
||||
|
||||
fn is_valid_id_start(chr: char) -> bool {
|
||||
chr.is_alphabetic() || matches!(chr, '_')
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub fn single_token(&mut self, token: Token) -> (Token, Span) {
|
||||
let start = self.pos;
|
||||
self.next_char();
|
||||
(token, self.mk_span(start))
|
||||
}
|
||||
|
||||
pub fn is_breakline(&mut self) -> bool {
|
||||
self.accumulate_while(&is_whitespace);
|
||||
let count = self.accumulate_while(&|x| x == '\n').len();
|
||||
count > 0
|
||||
}
|
||||
|
||||
pub fn to_keyword(str: &str) -> Token {
|
||||
match str {
|
||||
"ask" => Token::Ask,
|
||||
"do" => Token::Do,
|
||||
"if" => Token::If,
|
||||
"else" => Token::Else,
|
||||
"match" => Token::Match,
|
||||
"let" => Token::Let,
|
||||
"open" => Token::Open,
|
||||
_ => Token::Id(str.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_next_no_error(&mut self, vec: &mut Vec<Box<SyntaxError>>) -> (Token, Span) {
|
||||
loop {
|
||||
let (token, span) = self.lex_token();
|
||||
match token {
|
||||
Token::Error(x) => {
|
||||
vec.push(x);
|
||||
continue
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
return (token, span)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lex_token(&mut self) -> (Token, Span) {
|
||||
let start = self.pos;
|
||||
match self.peekable.peek() {
|
||||
None => (Token::Eof, self.mk_span(start)),
|
||||
Some(chr) => match chr {
|
||||
c if is_whitespace(*c) => {
|
||||
self.accumulate_while(&is_whitespace);
|
||||
self.lex_next()
|
||||
}
|
||||
'\n' => {
|
||||
self.accumulate_while(&|x| x == '\n' || x == '\r');
|
||||
if self.semis > 0 {
|
||||
self.semis -= 1;
|
||||
(Token::Semi, self.mk_span(start))
|
||||
} else {
|
||||
self.lex_next()
|
||||
}
|
||||
}
|
||||
c if c.is_ascii_digit() => self.lex_number(),
|
||||
c if is_valid_id_start(*c) => {
|
||||
let str = self.accumulate_while(&is_valid_id);
|
||||
(Lexer::to_keyword(str), self.mk_span(start))
|
||||
}
|
||||
'(' => self.single_token(Token::LPar),
|
||||
')' => self.single_token(Token::RPar),
|
||||
'[' => self.single_token(Token::LBracket),
|
||||
']' => self.single_token(Token::RBracket),
|
||||
'{' => self.single_token(Token::LBrace),
|
||||
'}' => self.single_token(Token::RBrace),
|
||||
'=' => {
|
||||
self.next_char();
|
||||
match self.peekable.peek() {
|
||||
Some('>') => self.single_token(Token::FatArrow),
|
||||
Some('=') => self.single_token(Token::EqEq),
|
||||
_ => (Token::Eq, self.mk_span(start)),
|
||||
}
|
||||
}
|
||||
'>' => {
|
||||
self.next_char();
|
||||
match self.peekable.peek() {
|
||||
Some('>') => self.single_token(Token::GreaterGreater),
|
||||
Some('=') => self.single_token(Token::GreaterEq),
|
||||
_ => (Token::Greater, self.mk_span(start)),
|
||||
}
|
||||
}
|
||||
'<' => {
|
||||
self.next_char();
|
||||
match self.peekable.peek() {
|
||||
Some('<') => self.single_token(Token::LessLess),
|
||||
Some('=') => self.single_token(Token::LessEq),
|
||||
_ => (Token::Less, self.mk_span(start)),
|
||||
}
|
||||
}
|
||||
'/' => {
|
||||
self.next_char();
|
||||
match self.peekable.peek() {
|
||||
Some('/') => self.lex_comment(start),
|
||||
Some('*') => self.lex_multiline_comment(start),
|
||||
_ => (Token::Slash, self.mk_span(start)),
|
||||
}
|
||||
}
|
||||
':' => self.single_token(Token::Colon),
|
||||
';' => self.single_token(Token::Semi),
|
||||
'$' => self.single_token(Token::Dollar),
|
||||
',' => self.single_token(Token::Comma),
|
||||
'+' => self.single_token(Token::Plus),
|
||||
'-' => self.single_token(Token::Minus),
|
||||
'*' => self.single_token(Token::Star),
|
||||
'%' => self.single_token(Token::Percent),
|
||||
'&' => self.single_token(Token::Ampersand),
|
||||
'|' => self.single_token(Token::Bar),
|
||||
'^' => self.single_token(Token::Hat),
|
||||
'"' => self.lex_string(),
|
||||
&c => {
|
||||
self.next_char();
|
||||
(Token::Error(Box::new(SyntaxError::UnexpectedChar(c, self.mk_span(start)))), self.mk_span(start))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
92
src/kind-parser/src/lexer/state.rs
Normal file
92
src/kind-parser/src/lexer/state.rs
Normal file
@ -0,0 +1,92 @@
|
||||
use std::{iter::Peekable, str::Chars};
|
||||
|
||||
use kind_span::{Pos, Range, Span, SyntaxCtxIndex};
|
||||
|
||||
use crate::lexer::tokens::Token;
|
||||
|
||||
/// The lexer state.
|
||||
pub struct Lexer<'a> {
|
||||
pub input: &'a str,
|
||||
pub peekable: &'a mut Peekable<Chars<'a>>,
|
||||
pub pos: usize,
|
||||
pub ctx: SyntaxCtxIndex,
|
||||
|
||||
// Modes
|
||||
pub semis: u16,
|
||||
pub comment_depth: u16,
|
||||
pub column: u16,
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub fn new(input: &'a str, peekable: &'a mut Peekable<Chars<'a>>, ctx: SyntaxCtxIndex) -> Lexer<'a> {
|
||||
Lexer {
|
||||
input,
|
||||
pos: 0,
|
||||
ctx,
|
||||
peekable,
|
||||
semis: 0,
|
||||
comment_depth: 0,
|
||||
column: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mk_span(&self, start: usize) -> Span {
|
||||
Span::new(Range::new(Pos(start as u32), Pos(self.pos as u32), self.ctx))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn adv_col(&mut self, chr: char) {
|
||||
self.column = if chr == '\n' { 0 } else { self.column + 1 };
|
||||
}
|
||||
|
||||
pub fn next_char(&mut self) -> Option<char> {
|
||||
match self.peekable.next() {
|
||||
Some(chr) if !self.input.is_empty() => {
|
||||
self.input = &self.input[chr.len_utf8()..];
|
||||
self.pos += chr.len_utf8();
|
||||
self.adv_col(chr);
|
||||
Some(chr)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn accumulate_while(&mut self, condition: &dyn Fn(char) -> bool) -> &str {
|
||||
let mut size = 0;
|
||||
while let Some(&x) = self.peekable.peek() {
|
||||
if !condition(x) {
|
||||
break;
|
||||
}
|
||||
size += x.len_utf8();
|
||||
self.adv_col(x);
|
||||
self.peekable.next();
|
||||
}
|
||||
self.pos += size;
|
||||
let str = &self.input[..size];
|
||||
self.input = &self.input[size..];
|
||||
str
|
||||
}
|
||||
|
||||
pub fn next_chars(&mut self, size: usize) -> Option<&str> {
|
||||
let start = self.pos;
|
||||
for _ in 0..size {
|
||||
if let Some(&x) = self.peekable.peek() {
|
||||
self.pos += x.len_utf8();
|
||||
self.peekable.next();
|
||||
self.adv_col(x);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let len = self.pos - start;
|
||||
let str = &self.input[..len];
|
||||
self.input = &self.input[len..];
|
||||
Some(str)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Useful as entrypoint
|
||||
pub fn lex_next(&mut self) -> (Token, Span) {
|
||||
self.lex_token()
|
||||
}
|
||||
}
|
69
src/kind-parser/src/lexer/tokens.rs
Normal file
69
src/kind-parser/src/lexer/tokens.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use crate::errors::SyntaxError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Token {
|
||||
LPar, // (
|
||||
RPar, // )
|
||||
LBracket, // [
|
||||
RBracket, // ]
|
||||
LBrace, // {
|
||||
RBrace, // }
|
||||
Eq, // =
|
||||
Colon, // :
|
||||
Semi, // ;
|
||||
FatArrow, // =>
|
||||
Dollar, // $
|
||||
Comma, // ,
|
||||
|
||||
Help(String),
|
||||
Id(String),
|
||||
|
||||
// Keywords
|
||||
Do,
|
||||
If,
|
||||
Else,
|
||||
Match,
|
||||
Ask,
|
||||
Let,
|
||||
Open,
|
||||
|
||||
// Literals
|
||||
Char(char),
|
||||
Str(String),
|
||||
Num(u64),
|
||||
Float(u64, u64),
|
||||
Hole,
|
||||
|
||||
// TO Interpolation
|
||||
|
||||
// Operators
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
Percent,
|
||||
Ampersand,
|
||||
Bar,
|
||||
Hat,
|
||||
GreaterGreater,
|
||||
LessLess,
|
||||
Less,
|
||||
LessEq,
|
||||
EqEq,
|
||||
GreaterEq,
|
||||
Greater,
|
||||
NotEq,
|
||||
|
||||
Comment(bool, String),
|
||||
|
||||
Eof,
|
||||
|
||||
// The error token that is useful to error recovery.
|
||||
Error(Box<SyntaxError>),
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn same_variant(&self, b: &Token) -> bool {
|
||||
std::mem::discriminant(self) == std::mem::discriminant(b)
|
||||
}
|
||||
}
|
@ -1,14 +1,9 @@
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
}
|
||||
mod errors;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
pub mod state;
|
||||
pub mod expr;
|
||||
pub mod top_level;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod lexer;
|
||||
pub use lexer::state::*;
|
23
src/kind-parser/src/macros.rs
Normal file
23
src/kind-parser/src/macros.rs
Normal file
@ -0,0 +1,23 @@
|
||||
macro_rules! match_single {
|
||||
($pattern:pat) => {
|
||||
|x| match x {
|
||||
$pattern => Some(()),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
($pattern:pat => $then:expr) => {
|
||||
|x| match x {
|
||||
$pattern => Some($then),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! eat_single {
|
||||
($x:expr, $pattern:pat) => { $x.eat(match_single!($pattern)) };
|
||||
($x:expr, $pattern:pat => $then:expr) => { $x.eat(match_single!($pattern => $then)) }
|
||||
}
|
||||
|
||||
pub(crate) use eat_single;
|
||||
pub(crate) use match_single;
|
79
src/kind-parser/src/state.rs
Normal file
79
src/kind-parser/src/state.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use kind_span::{Span, SyntaxCtxIndex};
|
||||
|
||||
use crate::{lexer::tokens::Token, Lexer, errors::SyntaxError};
|
||||
|
||||
/// The parser state. it current have some parameters
|
||||
/// that makes the behaviour change
|
||||
/// - eaten: It counts how much tokens it has eaten
|
||||
/// it's useful to all of the rules that use "try_local"
|
||||
/// and similar functions
|
||||
pub struct Parser<'a> {
|
||||
pub lexer: Lexer<'a>,
|
||||
pub current: (Token, Span),
|
||||
pub next: (Token, Span),
|
||||
pub errs: Vec<Box<SyntaxError>>,
|
||||
pub eaten: u32,
|
||||
pub ctx: SyntaxCtxIndex
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn new(mut lexer: Lexer<'a>, ctx: SyntaxCtxIndex) -> Parser<'a> {
|
||||
let mut errs = Vec::new();
|
||||
let current = lexer.get_next_no_error(&mut errs);
|
||||
let next = lexer.get_next_no_error(&mut errs);
|
||||
Parser { lexer, next, current, errs, eaten: 0, ctx }
|
||||
}
|
||||
|
||||
pub fn advance(&mut self) -> (Token, Span) {
|
||||
let cur = self.current.clone();
|
||||
self.current = self.next.clone();
|
||||
self.next = self.lexer.get_next_no_error(&mut self.errs);
|
||||
self.eaten += 1;
|
||||
cur
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fail<T>(&mut self, expect: Option<Token>) -> Result<T, SyntaxError> {
|
||||
Err(SyntaxError::UnexpectedToken(self.current.0.clone(), self.current.1, expect))
|
||||
}
|
||||
|
||||
pub fn eat_variant(&mut self, expect: &Token) -> Result<(Token, Span), SyntaxError> {
|
||||
if self.current.0.same_variant(expect) {
|
||||
Ok(self.advance())
|
||||
} else {
|
||||
self.fail(Some(expect.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eat<T>(&mut self, expect: fn(&Token) -> Option<T>) -> Result<T, SyntaxError> {
|
||||
match expect(&self.current.0) {
|
||||
None => self.fail(None),
|
||||
Some(res) => Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&mut self) -> &Token {
|
||||
&self.current.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn peek(&mut self) -> &Token {
|
||||
&self.next.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn span(&mut self) -> &Span {
|
||||
&self.current.1
|
||||
}
|
||||
|
||||
pub fn try_single<T>(&mut self, fun: fn(&mut Parser<'a>) -> Result<T, SyntaxError>) -> Result<Option<T>, SyntaxError> {
|
||||
let current = self.eaten;
|
||||
match fun(self) {
|
||||
Err(_) if current == self.eaten => Ok(None),
|
||||
Err(err) => Err(err),
|
||||
Ok(res) => Ok(Some(res)),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
0
src/kind-parser/src/top_level.rs
Normal file
0
src/kind-parser/src/top_level.rs
Normal file
@ -54,7 +54,7 @@ impl Range {
|
||||
Range {
|
||||
start: self.start,
|
||||
end: self.end,
|
||||
ctx: ctx,
|
||||
ctx,
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,9 +90,7 @@ impl Span {
|
||||
pub fn set_ctx(&mut self, ctx: SyntaxCtxIndex) {
|
||||
match self {
|
||||
Span::Generated => (),
|
||||
Span::Locatable(span) => {
|
||||
*span = span.set_ctx(ctx)
|
||||
},
|
||||
Span::Locatable(span) => *span = span.set_ctx(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
/// This module describes a CONCRETE SYNTAX TREE
|
||||
/// without parenthesis. It helps when it comes to
|
||||
/// a static analysis of the tree with the syntax sugars
|
||||
/// and it makes it easier to split phases.
|
||||
|
||||
use crate::symbol::Ident;
|
||||
use kind_span::{Locatable, Span};
|
||||
use core::ascii;
|
||||
use kind_span::Span;
|
||||
use std::fmt::{Display, Error, Formatter};
|
||||
|
||||
/// Enum of binary operators.
|
||||
@ -54,7 +58,7 @@ pub struct Substution {
|
||||
pub name: Ident,
|
||||
pub redx: u64,
|
||||
pub indx: u64,
|
||||
pub expr: Box<Expr>
|
||||
pub expr: Box<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -69,7 +73,7 @@ pub enum Literal {
|
||||
/// A number literal of 60 bits (e.g 32132)
|
||||
Number(u64),
|
||||
// A String literal
|
||||
String(String)
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -86,10 +90,6 @@ pub enum ExprKind {
|
||||
Let(Ident, Box<Expr>, Box<Expr>),
|
||||
/// Type ascription (x : y)
|
||||
Ann(Box<Expr>, Box<Expr>),
|
||||
/// A constructor application
|
||||
Ctr(Ident, Spine),
|
||||
/// A function application
|
||||
Fun(Ident, Spine),
|
||||
/// Literal
|
||||
Lit(Literal),
|
||||
/// Binary operation (e.g. 2 + 3)
|
||||
@ -112,16 +112,6 @@ pub struct Expr {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl Locatable for Expr {
|
||||
fn locate(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn set_location(&mut self, location: Span) {
|
||||
self.span = location;
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Operator {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||
use Operator::*;
|
||||
@ -151,49 +141,17 @@ impl Expr {
|
||||
pub fn new_var(name: Ident) -> Expr {
|
||||
Expr {
|
||||
span: Span::Generated,
|
||||
data: ExprKind::Var(name)
|
||||
data: ExprKind::Var(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn traverse_pi_types<'a>(&'a self) -> String {
|
||||
pub fn traverse_pi_types(&self) -> String {
|
||||
match &self.data {
|
||||
ExprKind::All(binder, typ, body) => {
|
||||
match binder {
|
||||
None => format!("{} -> {}", typ, body.traverse_pi_types()),
|
||||
Some(binder) => format!("({} : {}) -> {}", binder, typ, body.traverse_pi_types()),
|
||||
}
|
||||
}
|
||||
_ => format!("{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interpret_as_string(&self) -> Option<String> {
|
||||
let mut text = String::new();
|
||||
let mut term = &self.data;
|
||||
|
||||
let string_nil = Ident::new_path("String", "nil");
|
||||
let string_cons = Ident::new_path("String", "cons");
|
||||
|
||||
loop {
|
||||
if let ExprKind::Ctr (name, args) = term {
|
||||
if name.data == string_cons.data && args.len() == 2 {
|
||||
// TODO: Change it to support escaped chars.
|
||||
if let ExprKind::Lit (Literal::Number(numb)) = args[0].data {
|
||||
if ascii::escape_default(numb as u8).count() > 1 {
|
||||
return None;
|
||||
} else {
|
||||
text.push(char::from_u32(numb as u32).unwrap_or('\0'));
|
||||
term = &args[1].data;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else if name.data == string_nil.data && args.is_empty() {
|
||||
return Some(text);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
ExprKind::All(binder, typ, body) => match binder {
|
||||
None => format!("{} -> {}", typ, body.traverse_pi_types()),
|
||||
Some(binder) => format!("({} : {}) -> {}", binder, typ, body.traverse_pi_types()),
|
||||
},
|
||||
_ => format!("{}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,32 +166,24 @@ impl Display for Literal {
|
||||
Literal::String(str) => write!(f, "\"{}\"", str),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for Expr {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||
if let Some(str) = self.interpret_as_string() {
|
||||
write!(f, "\"{}\"", str)
|
||||
} else {
|
||||
use ExprKind::*;
|
||||
match &self.data {
|
||||
All(_, __, _) => write!(f, "({}", self.traverse_pi_types()),
|
||||
Lit(lit) => write!(f, "{}", lit),
|
||||
Var(name) => write!(f, "{}", name),
|
||||
Lambda(binder, body) => write!(f, "({} => {})", binder, body),
|
||||
App(head, spine) => write!(f, "({}{})", head, spine.iter().map(|x| format!(" {}", x)).collect::<String>()),
|
||||
Let(name, expr, body) => write!(f, "(let {} = {}; {})", name, expr, body),
|
||||
Ann(expr, typ) => write!(f, "({} : {})", expr, typ),
|
||||
Ctr(head, spine) => write!(f, "({}{})", head, spine.iter().map(|x| format!(" {}", x)).collect::<String>()),
|
||||
Fun(head, spine) => write!(f, "({}{})", head, spine.iter().map(|x| format!(" {}", x)).collect::<String>()),
|
||||
Binary(op, expr, typ) => write!(f, "({} {} {})", op, expr, typ),
|
||||
Subst(Substution { name, redx, expr, .. }) => write!(f, "({} ## {}/{})", expr, name, redx),
|
||||
Hole(_) => todo!(),
|
||||
Match(_) => todo!(),
|
||||
Open(_) => todo!(),
|
||||
|
||||
}
|
||||
use ExprKind::*;
|
||||
match &self.data {
|
||||
All(_, _, _) => write!(f, "({}", self.traverse_pi_types()),
|
||||
Lit(lit) => write!(f, "{}", lit),
|
||||
Var(name) => write!(f, "{}", name),
|
||||
Lambda(binder, body) => write!(f, "({} => {})", binder, body),
|
||||
App(head, spine) => write!(f, "({}{})", head, spine.iter().map(|x| format!(" {}", x)).collect::<String>()),
|
||||
Let(name, expr, body) => write!(f, "(let {} = {}; {})", name, expr, body),
|
||||
Ann(expr, typ) => write!(f, "({} : {})", expr, typ),
|
||||
Binary(op, expr, typ) => write!(f, "({} {} {})", op, expr, typ),
|
||||
Subst(Substution { name, redx, expr, .. }) => write!(f, "({} ## {}/{})", expr, name, redx),
|
||||
Hole(_) => todo!(),
|
||||
Match(_) => todo!(),
|
||||
Open(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::{collections::HashMap, fmt::{Formatter, Display, Error}};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Display, Error, Formatter};
|
||||
|
||||
use expr::Expr;
|
||||
use kind_span::{Span, SyntaxCtxIndex};
|
||||
@ -14,7 +15,7 @@ pub mod visitor;
|
||||
pub enum AttributeStyle {
|
||||
Ident(Ident),
|
||||
String(String),
|
||||
Number(Span, u64)
|
||||
Number(Span, u64),
|
||||
}
|
||||
|
||||
/// A attribute is a kind of declaration
|
||||
@ -25,7 +26,7 @@ pub enum AttributeStyle {
|
||||
pub struct Attribute {
|
||||
pub name: Ident,
|
||||
pub value: Option<AttributeStyle>,
|
||||
pub span: Span
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// An argument is a 'binding' of a name to a type
|
||||
@ -61,6 +62,7 @@ pub struct Rule {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Entry {
|
||||
pub name: Ident,
|
||||
pub docs: Option<String>,
|
||||
pub args: Vec<Box<Argument>>,
|
||||
pub tipo: Box<Expr>,
|
||||
pub rules: Vec<Box<Rule>>,
|
||||
@ -92,6 +94,8 @@ impl Book {
|
||||
}
|
||||
}
|
||||
|
||||
// Display
|
||||
|
||||
impl Display for Book {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||
for name in &self.names {
|
||||
@ -101,8 +105,6 @@ impl Display for Book {
|
||||
}
|
||||
}
|
||||
|
||||
// Display
|
||||
|
||||
impl Display for Argument {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||
let (open, close) = match (self.erased, self.hidden) {
|
||||
@ -141,4 +143,4 @@ impl Display for Rule {
|
||||
}
|
||||
write!(f, " = {}", self.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use kind_span::{SyntaxCtxIndex, Span};
|
||||
use kind_span::{Span, SyntaxCtxIndex};
|
||||
|
||||
// Stores the name of a variable or constructor
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
@ -16,11 +16,7 @@ pub struct Ident {
|
||||
|
||||
impl Ident {
|
||||
pub fn new(data: Symbol, ctx: SyntaxCtxIndex, span: Span) -> Ident {
|
||||
Ident {
|
||||
data,
|
||||
ctx,
|
||||
span,
|
||||
}
|
||||
Ident { data, ctx, span }
|
||||
}
|
||||
|
||||
pub fn new_path(data: &str, id: &str) -> Ident {
|
||||
@ -33,12 +29,12 @@ impl Ident {
|
||||
|
||||
/// Changes the syntax context of the span and of the ident
|
||||
pub fn set_ctx(&self, ctx: SyntaxCtxIndex) -> Ident {
|
||||
let mut span = self.span.clone();
|
||||
let mut span = self.span;
|
||||
span.set_ctx(ctx);
|
||||
Ident {
|
||||
data: self.data.clone(),
|
||||
ctx,
|
||||
span
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,4 +43,4 @@ impl Display for Ident {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.data.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use kind_span::Span;
|
||||
|
||||
use crate::expr::{ExprKind, Literal, Open, Substution, Match};
|
||||
use crate::expr::{ExprKind, Literal, Match, Open, Substution};
|
||||
use crate::symbol::*;
|
||||
use crate::*;
|
||||
|
||||
@ -13,13 +13,13 @@ use crate::*;
|
||||
/// All of these functions are implemented so we can easily
|
||||
/// change these default implementations.
|
||||
pub trait Visitor {
|
||||
fn visit_span(&mut self, _: &mut Span) { }
|
||||
fn visit_span(&mut self, _: &mut Span) {}
|
||||
|
||||
fn visit_syntax_ctx(&mut self, _: &mut SyntaxCtxIndex) { }
|
||||
fn visit_syntax_ctx(&mut self, _: &mut SyntaxCtxIndex) {}
|
||||
|
||||
fn visit_operator(&mut self, _: &mut expr::Operator) { }
|
||||
fn visit_operator(&mut self, _: &mut expr::Operator) {}
|
||||
|
||||
fn visit_literal(&mut self, _: &mut Literal) { }
|
||||
fn visit_literal(&mut self, _: &mut Literal) {}
|
||||
|
||||
fn visit_ident(&mut self, ident: &mut Ident) {
|
||||
self.visit_span(&mut ident.span);
|
||||
@ -99,62 +99,43 @@ pub trait Visitor {
|
||||
ExprKind::All(None, typ, body) => {
|
||||
self.visit_expr(typ);
|
||||
self.visit_expr(body);
|
||||
},
|
||||
}
|
||||
ExprKind::All(Some(ident), typ, body) => {
|
||||
self.visit_ident(ident);
|
||||
self.visit_expr(typ);
|
||||
self.visit_expr(body);
|
||||
},
|
||||
}
|
||||
ExprKind::Lambda(ident, body) => {
|
||||
self.visit_ident(ident);
|
||||
self.visit_expr(body);
|
||||
},
|
||||
}
|
||||
ExprKind::App(expr, spine) => {
|
||||
self.visit_expr(expr);
|
||||
for arg in spine {
|
||||
self.visit_expr(arg);
|
||||
}
|
||||
},
|
||||
}
|
||||
ExprKind::Let(ident, val, body) => {
|
||||
self.visit_ident(ident);
|
||||
self.visit_expr(val);
|
||||
self.visit_expr(body);
|
||||
|
||||
},
|
||||
}
|
||||
ExprKind::Ann(val, ty) => {
|
||||
self.visit_expr(val);
|
||||
self.visit_expr(ty);
|
||||
},
|
||||
ExprKind::Ctr(ident, spine) => {
|
||||
self.visit_ident(ident);
|
||||
for arg in spine {
|
||||
self.visit_expr(arg);
|
||||
}
|
||||
},
|
||||
ExprKind::Fun(ident, spine) => {
|
||||
self.visit_ident(ident);
|
||||
for arg in spine {
|
||||
self.visit_expr(arg);
|
||||
}
|
||||
},
|
||||
}
|
||||
ExprKind::Lit(lit) => {
|
||||
self.visit_literal(lit);
|
||||
},
|
||||
}
|
||||
ExprKind::Binary(op, a, b) => {
|
||||
self.visit_operator(op);
|
||||
self.visit_expr(a);
|
||||
self.visit_expr(b);
|
||||
},
|
||||
ExprKind::Hole(_) => { },
|
||||
ExprKind::Subst(subst) => {
|
||||
self.visit_substitution(subst)
|
||||
},
|
||||
ExprKind::Match(matcher) => {
|
||||
self.visit_match(matcher)
|
||||
},
|
||||
ExprKind::Open(open) => {
|
||||
self.visit_open(open)
|
||||
},
|
||||
}
|
||||
ExprKind::Hole(_) => {}
|
||||
ExprKind::Subst(subst) => self.visit_substitution(subst),
|
||||
ExprKind::Match(matcher) => self.visit_match(matcher),
|
||||
ExprKind::Open(open) => self.visit_open(open),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user