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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[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 {
|
mod errors;
|
||||||
left + right
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
pub mod state;
|
||||||
mod tests {
|
pub mod expr;
|
||||||
use super::*;
|
pub mod top_level;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
pub mod lexer;
|
||||||
let result = add(2, 2);
|
pub use lexer::state::*;
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
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 {
|
Range {
|
||||||
start: self.start,
|
start: self.start,
|
||||||
end: self.end,
|
end: self.end,
|
||||||
ctx: ctx,
|
ctx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,9 +90,7 @@ impl Span {
|
|||||||
pub fn set_ctx(&mut self, ctx: SyntaxCtxIndex) {
|
pub fn set_ctx(&mut self, ctx: SyntaxCtxIndex) {
|
||||||
match self {
|
match self {
|
||||||
Span::Generated => (),
|
Span::Generated => (),
|
||||||
Span::Locatable(span) => {
|
Span::Locatable(span) => *span = span.set_ctx(ctx),
|
||||||
*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 crate::symbol::Ident;
|
||||||
use kind_span::{Locatable, Span};
|
use kind_span::Span;
|
||||||
use core::ascii;
|
|
||||||
use std::fmt::{Display, Error, Formatter};
|
use std::fmt::{Display, Error, Formatter};
|
||||||
|
|
||||||
/// Enum of binary operators.
|
/// Enum of binary operators.
|
||||||
@ -54,7 +58,7 @@ pub struct Substution {
|
|||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub redx: u64,
|
pub redx: u64,
|
||||||
pub indx: u64,
|
pub indx: u64,
|
||||||
pub expr: Box<Expr>
|
pub expr: Box<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -69,7 +73,7 @@ pub enum Literal {
|
|||||||
/// A number literal of 60 bits (e.g 32132)
|
/// A number literal of 60 bits (e.g 32132)
|
||||||
Number(u64),
|
Number(u64),
|
||||||
// A String literal
|
// A String literal
|
||||||
String(String)
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -86,10 +90,6 @@ pub enum ExprKind {
|
|||||||
Let(Ident, Box<Expr>, Box<Expr>),
|
Let(Ident, Box<Expr>, Box<Expr>),
|
||||||
/// Type ascription (x : y)
|
/// Type ascription (x : y)
|
||||||
Ann(Box<Expr>, Box<Expr>),
|
Ann(Box<Expr>, Box<Expr>),
|
||||||
/// A constructor application
|
|
||||||
Ctr(Ident, Spine),
|
|
||||||
/// A function application
|
|
||||||
Fun(Ident, Spine),
|
|
||||||
/// Literal
|
/// Literal
|
||||||
Lit(Literal),
|
Lit(Literal),
|
||||||
/// Binary operation (e.g. 2 + 3)
|
/// Binary operation (e.g. 2 + 3)
|
||||||
@ -112,16 +112,6 @@ pub struct Expr {
|
|||||||
pub span: Span,
|
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 {
|
impl Display for Operator {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||||
use Operator::*;
|
use Operator::*;
|
||||||
@ -151,49 +141,17 @@ impl Expr {
|
|||||||
pub fn new_var(name: Ident) -> Expr {
|
pub fn new_var(name: Ident) -> Expr {
|
||||||
Expr {
|
Expr {
|
||||||
span: Span::Generated,
|
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 {
|
match &self.data {
|
||||||
ExprKind::All(binder, typ, body) => {
|
ExprKind::All(binder, typ, body) => match binder {
|
||||||
match binder {
|
None => format!("{} -> {}", typ, body.traverse_pi_types()),
|
||||||
None => format!("{} -> {}", typ, body.traverse_pi_types()),
|
Some(binder) => format!("({} : {}) -> {}", binder, typ, body.traverse_pi_types()),
|
||||||
Some(binder) => format!("({} : {}) -> {}", binder, typ, body.traverse_pi_types()),
|
},
|
||||||
}
|
_ => format!("{}", self),
|
||||||
}
|
|
||||||
_ => 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,32 +166,24 @@ impl Display for Literal {
|
|||||||
Literal::String(str) => write!(f, "\"{}\"", str),
|
Literal::String(str) => write!(f, "\"{}\"", str),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Expr {
|
impl Display for Expr {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||||
if let Some(str) = self.interpret_as_string() {
|
use ExprKind::*;
|
||||||
write!(f, "\"{}\"", str)
|
match &self.data {
|
||||||
} else {
|
All(_, _, _) => write!(f, "({}", self.traverse_pi_types()),
|
||||||
use ExprKind::*;
|
Lit(lit) => write!(f, "{}", lit),
|
||||||
match &self.data {
|
Var(name) => write!(f, "{}", name),
|
||||||
All(_, __, _) => write!(f, "({}", self.traverse_pi_types()),
|
Lambda(binder, body) => write!(f, "({} => {})", binder, body),
|
||||||
Lit(lit) => write!(f, "{}", lit),
|
App(head, spine) => write!(f, "({}{})", head, spine.iter().map(|x| format!(" {}", x)).collect::<String>()),
|
||||||
Var(name) => write!(f, "{}", name),
|
Let(name, expr, body) => write!(f, "(let {} = {}; {})", name, expr, body),
|
||||||
Lambda(binder, body) => write!(f, "({} => {})", binder, body),
|
Ann(expr, typ) => write!(f, "({} : {})", expr, typ),
|
||||||
App(head, spine) => write!(f, "({}{})", head, spine.iter().map(|x| format!(" {}", x)).collect::<String>()),
|
Binary(op, expr, typ) => write!(f, "({} {} {})", op, expr, typ),
|
||||||
Let(name, expr, body) => write!(f, "(let {} = {}; {})", name, expr, body),
|
Subst(Substution { name, redx, expr, .. }) => write!(f, "({} ## {}/{})", expr, name, redx),
|
||||||
Ann(expr, typ) => write!(f, "({} : {})", expr, typ),
|
Hole(_) => todo!(),
|
||||||
Ctr(head, spine) => write!(f, "({}{})", head, spine.iter().map(|x| format!(" {}", x)).collect::<String>()),
|
Match(_) => todo!(),
|
||||||
Fun(head, spine) => write!(f, "({}{})", head, spine.iter().map(|x| format!(" {}", x)).collect::<String>()),
|
Open(_) => todo!(),
|
||||||
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 expr::Expr;
|
||||||
use kind_span::{Span, SyntaxCtxIndex};
|
use kind_span::{Span, SyntaxCtxIndex};
|
||||||
@ -14,7 +15,7 @@ pub mod visitor;
|
|||||||
pub enum AttributeStyle {
|
pub enum AttributeStyle {
|
||||||
Ident(Ident),
|
Ident(Ident),
|
||||||
String(String),
|
String(String),
|
||||||
Number(Span, u64)
|
Number(Span, u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A attribute is a kind of declaration
|
/// A attribute is a kind of declaration
|
||||||
@ -25,7 +26,7 @@ pub enum AttributeStyle {
|
|||||||
pub struct Attribute {
|
pub struct Attribute {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub value: Option<AttributeStyle>,
|
pub value: Option<AttributeStyle>,
|
||||||
pub span: Span
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An argument is a 'binding' of a name to a type
|
/// An argument is a 'binding' of a name to a type
|
||||||
@ -61,6 +62,7 @@ pub struct Rule {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Entry {
|
pub struct Entry {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
|
pub docs: Option<String>,
|
||||||
pub args: Vec<Box<Argument>>,
|
pub args: Vec<Box<Argument>>,
|
||||||
pub tipo: Box<Expr>,
|
pub tipo: Box<Expr>,
|
||||||
pub rules: Vec<Box<Rule>>,
|
pub rules: Vec<Box<Rule>>,
|
||||||
@ -92,6 +94,8 @@ impl Book {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display
|
||||||
|
|
||||||
impl Display for Book {
|
impl Display for Book {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||||
for name in &self.names {
|
for name in &self.names {
|
||||||
@ -101,8 +105,6 @@ impl Display for Book {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display
|
|
||||||
|
|
||||||
impl Display for Argument {
|
impl Display for Argument {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||||
let (open, close) = match (self.erased, self.hidden) {
|
let (open, close) = match (self.erased, self.hidden) {
|
||||||
@ -141,4 +143,4 @@ impl Display for Rule {
|
|||||||
}
|
}
|
||||||
write!(f, " = {}", self.body)
|
write!(f, " = {}", self.body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use kind_span::{SyntaxCtxIndex, Span};
|
use kind_span::{Span, SyntaxCtxIndex};
|
||||||
|
|
||||||
// Stores the name of a variable or constructor
|
// Stores the name of a variable or constructor
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
@ -16,11 +16,7 @@ pub struct Ident {
|
|||||||
|
|
||||||
impl Ident {
|
impl Ident {
|
||||||
pub fn new(data: Symbol, ctx: SyntaxCtxIndex, span: Span) -> Ident {
|
pub fn new(data: Symbol, ctx: SyntaxCtxIndex, span: Span) -> Ident {
|
||||||
Ident {
|
Ident { data, ctx, span }
|
||||||
data,
|
|
||||||
ctx,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_path(data: &str, id: &str) -> Ident {
|
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
|
/// Changes the syntax context of the span and of the ident
|
||||||
pub fn set_ctx(&self, ctx: SyntaxCtxIndex) -> Ident {
|
pub fn set_ctx(&self, ctx: SyntaxCtxIndex) -> Ident {
|
||||||
let mut span = self.span.clone();
|
let mut span = self.span;
|
||||||
span.set_ctx(ctx);
|
span.set_ctx(ctx);
|
||||||
Ident {
|
Ident {
|
||||||
data: self.data.clone(),
|
data: self.data.clone(),
|
||||||
ctx,
|
ctx,
|
||||||
span
|
span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,4 +43,4 @@ impl Display for Ident {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.data.0)
|
write!(f, "{}", self.data.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use kind_span::Span;
|
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::symbol::*;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
@ -13,13 +13,13 @@ use crate::*;
|
|||||||
/// All of these functions are implemented so we can easily
|
/// All of these functions are implemented so we can easily
|
||||||
/// change these default implementations.
|
/// change these default implementations.
|
||||||
pub trait Visitor {
|
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) {
|
fn visit_ident(&mut self, ident: &mut Ident) {
|
||||||
self.visit_span(&mut ident.span);
|
self.visit_span(&mut ident.span);
|
||||||
@ -99,62 +99,43 @@ pub trait Visitor {
|
|||||||
ExprKind::All(None, typ, body) => {
|
ExprKind::All(None, typ, body) => {
|
||||||
self.visit_expr(typ);
|
self.visit_expr(typ);
|
||||||
self.visit_expr(body);
|
self.visit_expr(body);
|
||||||
},
|
}
|
||||||
ExprKind::All(Some(ident), typ, body) => {
|
ExprKind::All(Some(ident), typ, body) => {
|
||||||
self.visit_ident(ident);
|
self.visit_ident(ident);
|
||||||
self.visit_expr(typ);
|
self.visit_expr(typ);
|
||||||
self.visit_expr(body);
|
self.visit_expr(body);
|
||||||
},
|
}
|
||||||
ExprKind::Lambda(ident, body) => {
|
ExprKind::Lambda(ident, body) => {
|
||||||
self.visit_ident(ident);
|
self.visit_ident(ident);
|
||||||
self.visit_expr(body);
|
self.visit_expr(body);
|
||||||
},
|
}
|
||||||
ExprKind::App(expr, spine) => {
|
ExprKind::App(expr, spine) => {
|
||||||
self.visit_expr(expr);
|
self.visit_expr(expr);
|
||||||
for arg in spine {
|
for arg in spine {
|
||||||
self.visit_expr(arg);
|
self.visit_expr(arg);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
ExprKind::Let(ident, val, body) => {
|
ExprKind::Let(ident, val, body) => {
|
||||||
self.visit_ident(ident);
|
self.visit_ident(ident);
|
||||||
self.visit_expr(val);
|
self.visit_expr(val);
|
||||||
self.visit_expr(body);
|
self.visit_expr(body);
|
||||||
|
}
|
||||||
},
|
|
||||||
ExprKind::Ann(val, ty) => {
|
ExprKind::Ann(val, ty) => {
|
||||||
self.visit_expr(val);
|
self.visit_expr(val);
|
||||||
self.visit_expr(ty);
|
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) => {
|
ExprKind::Lit(lit) => {
|
||||||
self.visit_literal(lit);
|
self.visit_literal(lit);
|
||||||
},
|
}
|
||||||
ExprKind::Binary(op, a, b) => {
|
ExprKind::Binary(op, a, b) => {
|
||||||
self.visit_operator(op);
|
self.visit_operator(op);
|
||||||
self.visit_expr(a);
|
self.visit_expr(a);
|
||||||
self.visit_expr(b);
|
self.visit_expr(b);
|
||||||
},
|
}
|
||||||
ExprKind::Hole(_) => { },
|
ExprKind::Hole(_) => {}
|
||||||
ExprKind::Subst(subst) => {
|
ExprKind::Subst(subst) => self.visit_substitution(subst),
|
||||||
self.visit_substitution(subst)
|
ExprKind::Match(matcher) => self.visit_match(matcher),
|
||||||
},
|
ExprKind::Open(open) => self.visit_open(open),
|
||||||
ExprKind::Match(matcher) => {
|
|
||||||
self.visit_match(matcher)
|
|
||||||
},
|
|
||||||
ExprKind::Open(open) => {
|
|
||||||
self.visit_open(open)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user