Compare commits
4 Commits
91a7abf4cd
...
60ddac9774
Author | SHA1 | Date | |
---|---|---|---|
|
60ddac9774 | ||
|
ae6a79077f | ||
|
36f06b38de | ||
|
c9c65b050c |
@ -15,7 +15,7 @@ struct RecursiveDescentFn {
|
|||||||
impl Fold for RecursiveDescentFn {
|
impl Fold for RecursiveDescentFn {
|
||||||
fn fold_item_fn(&mut self, mut i: syn::ItemFn) -> syn::ItemFn {
|
fn fold_item_fn(&mut self, mut i: syn::ItemFn) -> syn::ItemFn {
|
||||||
let box block = i.block;
|
let box block = i.block;
|
||||||
let ref ident = i.ident;
|
let ident = &i.ident;
|
||||||
|
|
||||||
let new_block: syn::Block = parse_quote! {
|
let new_block: syn::Block = parse_quote! {
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::rc::Rc;
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
#![allow(clippy::enum_variant_names)]
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
use crate::derivative::Derivative;
|
use crate::derivative::Derivative;
|
||||||
|
|
||||||
mod walker;
|
mod walker;
|
||||||
|
@ -6,26 +6,28 @@ use crate::ast::*;
|
|||||||
|
|
||||||
pub trait ASTVisitor: Sized {
|
pub trait ASTVisitor: Sized {
|
||||||
fn ast(&mut self, _ast: &AST) {}
|
fn ast(&mut self, _ast: &AST) {}
|
||||||
fn block(&mut self, _statements: &Vec<Statement>) {}
|
fn block(&mut self, _statements: &[ Statement ]) {}
|
||||||
fn statement(&mut self, _statement: &Statement) {}
|
fn statement(&mut self, _statement: &Statement) {}
|
||||||
fn declaration(&mut self, _declaration: &Declaration) {}
|
fn declaration(&mut self, _declaration: &Declaration) {}
|
||||||
fn signature(&mut self, _signature: &Signature) {}
|
fn signature(&mut self, _signature: &Signature) {}
|
||||||
fn type_declaration(&mut self, _name: &TypeSingletonName, _body: &TypeBody, _mutable: bool) {}
|
fn type_declaration(&mut self, _name: &TypeSingletonName, _body: &TypeBody, _mutable: bool) {}
|
||||||
fn type_alias(&mut self, _alias: &Rc<String>, _original: &Rc<String>) {}
|
fn type_alias(&mut self, _alias: &Rc<String>, _original: &Rc<String>) {}
|
||||||
fn binding(&mut self, _name: &Rc<String>, _constant: bool, _type_anno: Option<&TypeIdentifier>, _expr: &Expression) {}
|
fn binding(&mut self, _name: &Rc<String>, _constant: bool, _type_anno: Option<&TypeIdentifier>, _expr: &Expression) {}
|
||||||
fn implemention(&mut self, _type_name: &TypeIdentifier, _interface_name: Option<&TypeSingletonName>, _block: &Vec<Declaration>) {}
|
fn implemention(&mut self, _type_name: &TypeIdentifier, _interface_name: Option<&TypeSingletonName>, _block: &[ Declaration ]) {}
|
||||||
fn interface(&mut self, _name: &Rc<String>, _signatures: &Vec<Signature>) {}
|
fn interface(&mut self, _name: &Rc<String>, _signatures: &[ Signature ]) {}
|
||||||
fn expression(&mut self, _expression: &Expression) {}
|
fn expression(&mut self, _expression: &Expression) {}
|
||||||
fn expression_kind(&mut self, _kind: &ExpressionKind) {}
|
fn expression_kind(&mut self, _kind: &ExpressionKind) {}
|
||||||
fn type_annotation(&mut self, _type_anno: Option<&TypeIdentifier>) {}
|
fn type_annotation(&mut self, _type_anno: Option<&TypeIdentifier>) {}
|
||||||
fn named_struct(&mut self, _name: &QualifiedName, _fields: &Vec<(Rc<String>, Expression)>) {}
|
fn named_struct(&mut self, _name: &QualifiedName, _fields: &[ (Rc<String>, Expression) ]) {}
|
||||||
fn call(&mut self, _f: &Expression, _arguments: &Vec<InvocationArgument>) {}
|
fn call(&mut self, _f: &Expression, _arguments: &[ InvocationArgument ]) {}
|
||||||
fn index(&mut self, _indexee: &Expression, _indexers: &Vec<Expression>) {}
|
fn index(&mut self, _indexee: &Expression, _indexers: &[ Expression ]) {}
|
||||||
fn if_expression(&mut self, _discrim: Option<&Expression>, _body: &IfExpressionBody) {}
|
fn if_expression(&mut self, _discrim: Option<&Expression>, _body: &IfExpressionBody) {}
|
||||||
fn condition_arm(&mut self, _arm: &ConditionArm) {}
|
fn condition_arm(&mut self, _arm: &ConditionArm) {}
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
fn while_expression(&mut self, _condition: Option<&Expression>, _body: &Block) {}
|
fn while_expression(&mut self, _condition: Option<&Expression>, _body: &Block) {}
|
||||||
fn for_expression(&mut self, _enumerators: &Vec<Enumerator>, _body: &ForBody) {}
|
fn for_expression(&mut self, _enumerators: &[ Enumerator ], _body: &ForBody) {}
|
||||||
fn lambda(&mut self, _params: &Vec<FormalParam>, _type_anno: Option<&TypeIdentifier>, _body: &Block) {}
|
#[allow(clippy::ptr_arg)]
|
||||||
|
fn lambda(&mut self, _params: &[ FormalParam ], _type_anno: Option<&TypeIdentifier>, _body: &Block) {}
|
||||||
fn invocation_argument(&mut self, _arg: &InvocationArgument) {}
|
fn invocation_argument(&mut self, _arg: &InvocationArgument) {}
|
||||||
fn formal_param(&mut self, _param: &FormalParam) {}
|
fn formal_param(&mut self, _param: &FormalParam) {}
|
||||||
fn import(&mut self, _import: &ImportSpecifier) {}
|
fn import(&mut self, _import: &ImportSpecifier) {}
|
||||||
|
@ -9,6 +9,7 @@ pub fn walk_ast<V: ASTVisitor>(v: &mut V, ast: &AST) {
|
|||||||
walk_block(v, &ast.statements);
|
walk_block(v, &ast.statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
fn walk_block<V: ASTVisitor>(v: &mut V, block: &Vec<Statement>) {
|
fn walk_block<V: ASTVisitor>(v: &mut V, block: &Vec<Statement>) {
|
||||||
for s in block {
|
for s in block {
|
||||||
v.statement(s);
|
v.statement(s);
|
||||||
@ -39,12 +40,12 @@ fn declaration<V: ASTVisitor>(v: &mut V, decl: &Declaration) {
|
|||||||
use Declaration::*;
|
use Declaration::*;
|
||||||
match decl {
|
match decl {
|
||||||
FuncSig(sig) => {
|
FuncSig(sig) => {
|
||||||
v.signature(&sig);
|
v.signature(sig);
|
||||||
signature(v, &sig);
|
signature(v, sig);
|
||||||
},
|
},
|
||||||
FuncDecl(sig, block) => {
|
FuncDecl(sig, block) => {
|
||||||
v.signature(&sig);
|
v.signature(sig);
|
||||||
v.block(&block);
|
v.block(block);
|
||||||
walk_block(v, block);
|
walk_block(v, block);
|
||||||
},
|
},
|
||||||
TypeDecl { name, body, mutable } => v.type_declaration(name, body, *mutable),
|
TypeDecl { name, body, mutable } => v.type_declaration(name, body, *mutable),
|
||||||
@ -52,8 +53,8 @@ fn declaration<V: ASTVisitor>(v: &mut V, decl: &Declaration) {
|
|||||||
Binding { name, constant, type_anno, expr } => {
|
Binding { name, constant, type_anno, expr } => {
|
||||||
v.binding(name, *constant, type_anno.as_ref(), expr);
|
v.binding(name, *constant, type_anno.as_ref(), expr);
|
||||||
v.type_annotation(type_anno.as_ref());
|
v.type_annotation(type_anno.as_ref());
|
||||||
v.expression(&expr);
|
v.expression(expr);
|
||||||
expression(v, &expr);
|
expression(v, expr);
|
||||||
},
|
},
|
||||||
Impl { type_name, interface_name, block } => {
|
Impl { type_name, interface_name, block } => {
|
||||||
v.implemention(type_name, interface_name.as_ref(), block);
|
v.implemention(type_name, interface_name.as_ref(), block);
|
||||||
@ -79,7 +80,7 @@ fn expression<V: ASTVisitor>(v: &mut V, expression: &Expression) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn call<V: ASTVisitor>(v: &mut V, f: &Expression, args: &Vec<InvocationArgument>) {
|
fn call<V: ASTVisitor>(v: &mut V, f: &Expression, args: &[ InvocationArgument ]) {
|
||||||
v.expression(f);
|
v.expression(f);
|
||||||
expression(v, f);
|
expression(v, f);
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
@ -103,20 +104,21 @@ fn invocation_argument<V: ASTVisitor>(v: &mut V, arg: &InvocationArgument) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn index<V: ASTVisitor>(v: &mut V, indexee: &Expression, indexers: &Vec<Expression>) {
|
fn index<V: ASTVisitor>(v: &mut V, indexee: &Expression, indexers: &[ Expression ]) {
|
||||||
v.expression(indexee);
|
v.expression(indexee);
|
||||||
for i in indexers.iter() {
|
for i in indexers.iter() {
|
||||||
v.expression(i);
|
v.expression(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn named_struct<V: ASTVisitor>(v: &mut V, n: &QualifiedName, fields: &Vec<(Rc<String>, Expression)>) {
|
fn named_struct<V: ASTVisitor>(v: &mut V, n: &QualifiedName, fields: &[ (Rc<String>, Expression) ]) {
|
||||||
v.qualified_name(n);
|
v.qualified_name(n);
|
||||||
for (_, expr) in fields.iter() {
|
for (_, expr) in fields.iter() {
|
||||||
v.expression(expr);
|
v.expression(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
fn lambda<V: ASTVisitor>(v: &mut V, params: &Vec<FormalParam>, type_anno: Option<&TypeIdentifier>, body: &Block) {
|
fn lambda<V: ASTVisitor>(v: &mut V, params: &Vec<FormalParam>, type_anno: Option<&TypeIdentifier>, body: &Block) {
|
||||||
for param in params {
|
for param in params {
|
||||||
v.formal_param(param);
|
v.formal_param(param);
|
||||||
@ -128,11 +130,11 @@ fn lambda<V: ASTVisitor>(v: &mut V, params: &Vec<FormalParam>, type_anno: Option
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn formal_param<V: ASTVisitor>(v: &mut V, param: &FormalParam) {
|
fn formal_param<V: ASTVisitor>(v: &mut V, param: &FormalParam) {
|
||||||
param.default.as_ref().map(|p| {
|
if let Some(p) = param.default.as_ref() {
|
||||||
v.expression(p);
|
v.expression(p);
|
||||||
expression(v, p);
|
expression(v, p);
|
||||||
});
|
};
|
||||||
v.type_annotation(param.anno.as_ref());
|
v.type_annotation(param.anno.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression_kind<V: ASTVisitor>(v: &mut V, expression_kind: &ExpressionKind) {
|
fn expression_kind<V: ASTVisitor>(v: &mut V, expression_kind: &ExpressionKind) {
|
||||||
@ -172,7 +174,7 @@ fn expression_kind<V: ASTVisitor>(v: &mut V, expression_kind: &ExpressionKind) {
|
|||||||
},
|
},
|
||||||
IfExpression { discriminator, body } => {
|
IfExpression { discriminator, body } => {
|
||||||
v.if_expression(deref_optional_box(discriminator), body);
|
v.if_expression(deref_optional_box(discriminator), body);
|
||||||
discriminator.as_ref().map(|d| expression(v, d));
|
if let Some(d) = discriminator.as_ref() { expression(v, d) }
|
||||||
if_expression_body(v, body);
|
if_expression_body(v, body);
|
||||||
},
|
},
|
||||||
WhileExpression { condition, body } => v.while_expression(deref_optional_box(condition), body),
|
WhileExpression { condition, body } => v.while_expression(deref_optional_box(condition), body),
|
||||||
@ -195,13 +197,13 @@ fn if_expression_body<V: ASTVisitor>(v: &mut V, body: &IfExpressionBody) {
|
|||||||
match body {
|
match body {
|
||||||
SimpleConditional { then_case, else_case } => {
|
SimpleConditional { then_case, else_case } => {
|
||||||
walk_block(v, then_case);
|
walk_block(v, then_case);
|
||||||
else_case.as_ref().map(|block| walk_block(v, block));
|
if let Some(block) = else_case.as_ref() { walk_block(v, block) }
|
||||||
},
|
},
|
||||||
SimplePatternMatch { pattern, then_case, else_case } => {
|
SimplePatternMatch { pattern, then_case, else_case } => {
|
||||||
v.pattern(pattern);
|
v.pattern(pattern);
|
||||||
walk_pattern(v, pattern);
|
walk_pattern(v, pattern);
|
||||||
walk_block(v, then_case);
|
walk_block(v, then_case);
|
||||||
else_case.as_ref().map(|block| walk_block(v, block));
|
if let Some(block) = else_case.as_ref() { walk_block(v, block) }
|
||||||
},
|
},
|
||||||
CondList(arms) => {
|
CondList(arms) => {
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
@ -230,10 +232,10 @@ fn condition_arm<V: ASTVisitor>(v: &mut V, arm: &ConditionArm) {
|
|||||||
},
|
},
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
arm.guard.as_ref().map(|guard| {
|
if let Some(guard) = arm.guard.as_ref() {
|
||||||
v.expression(guard);
|
v.expression(guard);
|
||||||
expression(v, guard);
|
expression(v, guard);
|
||||||
});
|
};
|
||||||
v.block(&arm.body);
|
v.block(&arm.body);
|
||||||
walk_block(v, &arm.body);
|
walk_block(v, &arm.body);
|
||||||
}
|
}
|
||||||
|
@ -13,15 +13,15 @@ fn evaluate_all_outputs(input: &str) -> Vec<Result<String, String>> {
|
|||||||
let reduced = reduce(&ast, &symbol_table);
|
let reduced = reduce(&ast, &symbol_table);
|
||||||
|
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
let all_output = state.evaluate(reduced, true);
|
|
||||||
all_output
|
state.evaluate(reduced, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! test_in_fresh_env {
|
macro_rules! test_in_fresh_env {
|
||||||
($string:expr, $correct:expr) => {
|
($string:expr, $correct:expr) => {
|
||||||
{
|
{
|
||||||
let all_output = evaluate_all_outputs($string);
|
let all_output = evaluate_all_outputs($string);
|
||||||
let ref output = all_output.last().unwrap();
|
let output = &all_output.last().unwrap();
|
||||||
assert_eq!(**output, Ok($correct.to_string()));
|
assert_eq!(**output, Ok($correct.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
//! # Parsing
|
//! # Parsing
|
||||||
//! This module is where the recursive-descent parsing methods live.
|
//! This module is where the recursive-descent parsing methods live.
|
||||||
//!
|
//!
|
||||||
@ -230,15 +232,15 @@ impl TokenHandler {
|
|||||||
self.peek_n(n).kind
|
self.peek_n(n).kind
|
||||||
}
|
}
|
||||||
fn peek(&mut self) -> Token {
|
fn peek(&mut self) -> Token {
|
||||||
self.tokens.get(self.idx).map(|t: &Token| { t.clone()}).unwrap_or(Token { kind: TokenKind::EOF, location: self.end_of_file })
|
self.tokens.get(self.idx).cloned().unwrap_or(Token { kind: TokenKind::EOF, location: self.end_of_file })
|
||||||
}
|
}
|
||||||
/// calling peek_n(0) is the same thing as peek()
|
/// calling peek_n(0) is the same thing as peek()
|
||||||
fn peek_n(&mut self, n: usize) -> Token {
|
fn peek_n(&mut self, n: usize) -> Token {
|
||||||
self.tokens.get(self.idx + n).map(|t: &Token| { t.clone()}).unwrap_or(Token { kind: TokenKind::EOF, location: self.end_of_file })
|
self.tokens.get(self.idx + n).cloned().unwrap_or(Token { kind: TokenKind::EOF, location: self.end_of_file })
|
||||||
}
|
}
|
||||||
fn next(&mut self) -> Token {
|
fn next(&mut self) -> Token {
|
||||||
self.idx += 1;
|
self.idx += 1;
|
||||||
self.tokens.get(self.idx - 1).map(|t: &Token| { t.clone() }).unwrap_or(Token { kind: TokenKind::EOF, location: self.end_of_file })
|
self.tokens.get(self.idx - 1).cloned().unwrap_or(Token { kind: TokenKind::EOF, location: self.end_of_file })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,11 +373,11 @@ impl Parser {
|
|||||||
let kind = match tok.get_kind() {
|
let kind = match tok.get_kind() {
|
||||||
Keyword(Type) => self.type_declaration().map(|decl| { StatementKind::Declaration(decl) }),
|
Keyword(Type) => self.type_declaration().map(|decl| { StatementKind::Declaration(decl) }),
|
||||||
Keyword(Func)=> self.func_declaration().map(|func| { StatementKind::Declaration(func) }),
|
Keyword(Func)=> self.func_declaration().map(|func| { StatementKind::Declaration(func) }),
|
||||||
Keyword(Let) => self.binding_declaration().map(|decl| StatementKind::Declaration(decl)),
|
Keyword(Let) => self.binding_declaration().map(StatementKind::Declaration),
|
||||||
Keyword(Interface) => self.interface_declaration().map(|decl| StatementKind::Declaration(decl)),
|
Keyword(Interface) => self.interface_declaration().map(StatementKind::Declaration),
|
||||||
Keyword(Impl) => self.impl_declaration().map(|decl| StatementKind::Declaration(decl)),
|
Keyword(Impl) => self.impl_declaration().map(StatementKind::Declaration),
|
||||||
Keyword(Import) => self.import_declaration().map(|spec| StatementKind::Import(spec)),
|
Keyword(Import) => self.import_declaration().map(StatementKind::Import),
|
||||||
Keyword(Module) => self.module_declaration().map(|spec| StatementKind::Module(spec)),
|
Keyword(Module) => self.module_declaration().map(StatementKind::Module),
|
||||||
_ => self.expression().map(|expr| { StatementKind::Expression(expr) } ),
|
_ => self.expression().map(|expr| { StatementKind::Expression(expr) } ),
|
||||||
}?;
|
}?;
|
||||||
let id = self.id_store.fresh();
|
let id = self.id_store.fresh();
|
||||||
@ -417,15 +419,10 @@ impl Parser {
|
|||||||
|
|
||||||
#[recursive_descent_method]
|
#[recursive_descent_method]
|
||||||
fn type_body(&mut self) -> ParseResult<TypeBody> {
|
fn type_body(&mut self) -> ParseResult<TypeBody> {
|
||||||
let mut variants = Vec::new();
|
let mut variants = vec![self.variant_specifier()?];
|
||||||
variants.push(self.variant_specifier()?);
|
while let Pipe = self.token_handler.peek_kind() {
|
||||||
loop {
|
|
||||||
if let Pipe = self.token_handler.peek_kind() {
|
|
||||||
self.token_handler.next();
|
self.token_handler.next();
|
||||||
variants.push(self.variant_specifier()?);
|
variants.push(self.variant_specifier()?);
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(TypeBody(variants))
|
Ok(TypeBody(variants))
|
||||||
}
|
}
|
||||||
@ -472,7 +469,7 @@ impl Parser {
|
|||||||
expect!(self, Keyword(Func));
|
expect!(self, Keyword(Func));
|
||||||
let (name, operator) = match self.token_handler.peek_kind() {
|
let (name, operator) = match self.token_handler.peek_kind() {
|
||||||
Operator(s) => {
|
Operator(s) => {
|
||||||
let name = s.clone();
|
let name = s;
|
||||||
self.token_handler.next();
|
self.token_handler.next();
|
||||||
(name, true)
|
(name, true)
|
||||||
},
|
},
|
||||||
@ -582,7 +579,7 @@ impl Parser {
|
|||||||
Colon => Some(self.type_anno()?),
|
Colon => Some(self.type_anno()?),
|
||||||
_ => None
|
_ => None
|
||||||
};
|
};
|
||||||
if let Some(_) = expr_body.type_anno {
|
if expr_body.type_anno.is_some() {
|
||||||
return ParseError::new_with_token("Bad parse state encountered", self.token_handler.peek());
|
return ParseError::new_with_token("Bad parse state encountered", self.token_handler.peek());
|
||||||
}
|
}
|
||||||
expr_body.type_anno = type_anno;
|
expr_body.type_anno = type_anno;
|
||||||
@ -616,10 +613,11 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this implements Pratt parsing, see http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
|
// this implements Pratt parsing, see http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
|
||||||
|
#[allow(clippy::while_let_loop)]
|
||||||
fn precedence_expr(&mut self, precedence: i32) -> ParseResult<Expression> {
|
fn precedence_expr(&mut self, precedence: i32) -> ParseResult<Expression> {
|
||||||
let record = ParseRecord {
|
let record = ParseRecord {
|
||||||
production_name: "precedence_expr".to_string(),
|
production_name: "precedence_expr".to_string(),
|
||||||
next_token: format!("{}", self.token_handler.peek().to_string_with_metadata()),
|
next_token: self.token_handler.peek().to_string_with_metadata(),
|
||||||
level: self.parse_level,
|
level: self.parse_level,
|
||||||
};
|
};
|
||||||
self.parse_level += 1;
|
self.parse_level += 1;
|
||||||
@ -691,7 +689,7 @@ impl Parser {
|
|||||||
self.token_handler.next();
|
self.token_handler.next();
|
||||||
self.token_handler.next();
|
self.token_handler.next();
|
||||||
let expr = self.expression()?;
|
let expr = self.expression()?;
|
||||||
InvocationArgument::Keyword { name: s.clone(), expr }
|
InvocationArgument::Keyword { name: s, expr }
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let expr = self.expression()?;
|
let expr = self.expression()?;
|
||||||
@ -799,14 +797,11 @@ impl Parser {
|
|||||||
#[recursive_descent_method]
|
#[recursive_descent_method]
|
||||||
fn qualified_identifier(&mut self) -> ParseResult<Vec<Rc<String>>> {
|
fn qualified_identifier(&mut self) -> ParseResult<Vec<Rc<String>>> {
|
||||||
let mut components = vec![self.identifier()?];
|
let mut components = vec![self.identifier()?];
|
||||||
loop {
|
|
||||||
match (self.token_handler.peek_kind(), self.token_handler.peek_kind_n(1)) {
|
while let (Colon, Colon) = (self.token_handler.peek_kind(), self.token_handler.peek_kind_n(1)) {
|
||||||
(Colon, Colon) => {
|
self.token_handler.next();
|
||||||
self.token_handler.next(); self.token_handler.next();
|
self.token_handler.next();
|
||||||
components.push(self.identifier()?);
|
components.push(self.identifier()?);
|
||||||
},
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(components)
|
Ok(components)
|
||||||
}
|
}
|
||||||
@ -1023,7 +1018,7 @@ impl Parser {
|
|||||||
let pat = self.pattern()?;
|
let pat = self.pattern()?;
|
||||||
(name, pat)
|
(name, pat)
|
||||||
},
|
},
|
||||||
_ => (name.clone(), Pattern::Literal(PatternLiteral::StringPattern(name.clone())))
|
_ => (name.clone(), Pattern::Literal(PatternLiteral::StringPattern(name)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1054,7 +1049,7 @@ impl Parser {
|
|||||||
self.restrictions.no_struct_literal = true;
|
self.restrictions.no_struct_literal = true;
|
||||||
let x = self.while_cond();
|
let x = self.while_cond();
|
||||||
self.restrictions.no_struct_literal = false;
|
self.restrictions.no_struct_literal = false;
|
||||||
x?.map(|expr| Box::new(expr))
|
x?.map(Box::new)
|
||||||
};
|
};
|
||||||
let body = self.block()?;
|
let body = self.block()?;
|
||||||
Ok(Expression::new(self.id_store.fresh(), WhileExpression {condition, body}))
|
Ok(Expression::new(self.id_store.fresh(), WhileExpression {condition, body}))
|
||||||
@ -1140,7 +1135,7 @@ impl Parser {
|
|||||||
StrLiteral {s, ..} => {
|
StrLiteral {s, ..} => {
|
||||||
self.token_handler.next();
|
self.token_handler.next();
|
||||||
let id = self.id_store.fresh();
|
let id = self.id_store.fresh();
|
||||||
Ok(Expression::new(id, StringLiteral(s.clone())))
|
Ok(Expression::new(id, StringLiteral(s)))
|
||||||
}
|
}
|
||||||
e => ParseError::new_with_token(format!("Expected a literal expression, got {:?}", e), tok),
|
e => ParseError::new_with_token(format!("Expected a literal expression, got {:?}", e), tok),
|
||||||
}
|
}
|
||||||
@ -1180,7 +1175,7 @@ impl Parser {
|
|||||||
let mut digits = self.digits()?;
|
let mut digits = self.digits()?;
|
||||||
if let Period = self.token_handler.peek_kind() {
|
if let Period = self.token_handler.peek_kind() {
|
||||||
self.token_handler.next();
|
self.token_handler.next();
|
||||||
digits.push_str(".");
|
digits.push('.');
|
||||||
digits.push_str(&self.digits()?);
|
digits.push_str(&self.digits()?);
|
||||||
match digits.parse::<f64>() {
|
match digits.parse::<f64>() {
|
||||||
Ok(f) => Ok(Expression::new(self.id_store.fresh(), FloatLiteral(f))),
|
Ok(f) => Ok(Expression::new(self.id_store.fresh(), FloatLiteral(f))),
|
||||||
@ -1211,20 +1206,16 @@ impl Parser {
|
|||||||
#[recursive_descent_method]
|
#[recursive_descent_method]
|
||||||
fn import_declaration(&mut self) -> ParseResult<ImportSpecifier> {
|
fn import_declaration(&mut self) -> ParseResult<ImportSpecifier> {
|
||||||
expect!(self, Keyword(Import));
|
expect!(self, Keyword(Import));
|
||||||
let mut path_components = vec![];
|
let mut path_components = vec![self.identifier()?];
|
||||||
path_components.push(self.identifier()?);
|
|
||||||
loop {
|
while let (Colon, Colon) = (self.token_handler.peek_kind(), self.token_handler.peek_kind_n(1)) {
|
||||||
match (self.token_handler.peek_kind(), self.token_handler.peek_kind_n(1)) {
|
self.token_handler.next();
|
||||||
(Colon, Colon) => {
|
self.token_handler.next();
|
||||||
self.token_handler.next(); self.token_handler.next();
|
if let Identifier(_) = self.token_handler.peek_kind() {
|
||||||
if let Identifier(_) = self.token_handler.peek_kind() {
|
|
||||||
path_components.push(self.identifier()?);
|
path_components.push(self.identifier()?);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let imported_names = match self.token_handler.peek_kind() {
|
let imported_names = match self.token_handler.peek_kind() {
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#![cfg(test)]
|
#![cfg(test)]
|
||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
#![allow(clippy::vec_init_then_push)]
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::tokenizing::Location;
|
use crate::tokenizing::Location;
|
||||||
@ -170,12 +173,12 @@ fn parsing_tuples() {
|
|||||||
parse_test_wrap_ast!("()", exst!(TupleLiteral(vec![])));
|
parse_test_wrap_ast!("()", exst!(TupleLiteral(vec![])));
|
||||||
parse_test_wrap_ast!("(\"hella\", 34)", exst!(
|
parse_test_wrap_ast!("(\"hella\", 34)", exst!(
|
||||||
TupleLiteral(
|
TupleLiteral(
|
||||||
vec![ex!(s r#""hella""#).into(), ex!(s "34").into()]
|
vec![ex!(s r#""hella""#), ex!(s "34")]
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
parse_test_wrap_ast!("((1+2), \"slough\")", exst!(TupleLiteral(vec![
|
parse_test_wrap_ast!("((1+2), \"slough\")", exst!(TupleLiteral(vec![
|
||||||
ex!(binexp!("+", NatLiteral(1), NatLiteral(2))).into(),
|
ex!(binexp!("+", NatLiteral(1), NatLiteral(2))),
|
||||||
ex!(StringLiteral(rc!(slough))).into(),
|
ex!(StringLiteral(rc!(slough))),
|
||||||
])))
|
])))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +258,7 @@ fn parsing_functions() {
|
|||||||
parse_test_wrap_ast!("oi()", exst!(Call { f: bx!(ex!(val!("oi"))), arguments: vec![] }));
|
parse_test_wrap_ast!("oi()", exst!(Call { f: bx!(ex!(val!("oi"))), arguments: vec![] }));
|
||||||
parse_test_wrap_ast!("oi(a, 2 + 2)", exst!(Call
|
parse_test_wrap_ast!("oi(a, 2 + 2)", exst!(Call
|
||||||
{ f: bx!(ex!(val!("oi"))),
|
{ f: bx!(ex!(val!("oi"))),
|
||||||
arguments: vec![inv!(ex!(val!("a"))), inv!(ex!(binexp!("+", NatLiteral(2), NatLiteral(2)))).into()]
|
arguments: vec![inv!(ex!(val!("a"))), inv!(ex!(binexp!("+", NatLiteral(2), NatLiteral(2))))]
|
||||||
}));
|
}));
|
||||||
parse_error!("a(b,,c)");
|
parse_error!("a(b,,c)");
|
||||||
|
|
||||||
@ -541,7 +544,7 @@ fn parsing_lambdas() {
|
|||||||
type_anno: None,
|
type_anno: None,
|
||||||
body: vec![exst!(s "y")] }
|
body: vec![exst!(s "y")] }
|
||||||
)),
|
)),
|
||||||
arguments: vec![inv!(ex!(NatLiteral(1))).into()] })
|
arguments: vec![inv!(ex!(NatLiteral(1)))] })
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_test_wrap_ast! {
|
parse_test_wrap_ast! {
|
||||||
@ -589,7 +592,7 @@ fn more_advanced_lambdas() {
|
|||||||
exst! {
|
exst! {
|
||||||
Call {
|
Call {
|
||||||
f: bx!(ex!(Call { f: bx!(ex!(val!("wahoo"))), arguments: vec![] })),
|
f: bx!(ex!(Call { f: bx!(ex!(val!("wahoo"))), arguments: vec![] })),
|
||||||
arguments: vec![inv!(ex!(NatLiteral(3))).into()],
|
arguments: vec![inv!(ex!(NatLiteral(3)))],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -92,7 +92,7 @@ impl<'a> ASTVisitor for Resolver<'a> {
|
|||||||
self.symbol_table.id_to_fqsn.insert(qualified_name.id.clone(), fqsn);
|
self.symbol_table.id_to_fqsn.insert(qualified_name.id.clone(), fqsn);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn named_struct(&mut self, qualified_name: &QualifiedName, _fields: &Vec<(Rc<String>, Expression)>) {
|
fn named_struct(&mut self, qualified_name: &QualifiedName, _fields: &[ (Rc<String>, Expression) ]) {
|
||||||
let fqsn = self.lookup_name_in_scope(qualified_name);
|
let fqsn = self.lookup_name_in_scope(qualified_name);
|
||||||
self.symbol_table.id_to_fqsn.insert(qualified_name.id.clone(), fqsn);
|
self.symbol_table.id_to_fqsn.insert(qualified_name.id.clone(), fqsn);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::{iter::{Iterator, Peekable}, convert::TryFrom, rc::Rc, fmt};
|
use std::{iter::{Iterator, Peekable}, convert::TryFrom, rc::Rc, fmt};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
|
||||||
/// A location in a particular source file. Note that the
|
/// A location in a particular source file. Note that the
|
||||||
/// sizes of the internal unsigned integer types limit
|
/// sizes of the internal unsigned integer types limit
|
||||||
/// the size of a source file to 2^32 lines of
|
/// the size of a source file to 2^32 lines of
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::fmt::Write;
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use ena::unify::{UnifyKey, InPlaceUnificationTable, UnificationTable, EqUnifyValue};
|
use ena::unify::{UnifyKey, InPlaceUnificationTable, UnificationTable, EqUnifyValue};
|
||||||
@ -79,19 +78,21 @@ pub enum TypeConst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TypeConst {
|
impl TypeConst {
|
||||||
|
/*
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
use self::TypeConst::*;
|
use self::TypeConst::*;
|
||||||
match self {
|
match self {
|
||||||
Unit => format!("()"),
|
Unit => "()".to_string(),
|
||||||
Nat => format!("Nat"),
|
Nat => "Nat".to_string(),
|
||||||
Int => format!("Int"),
|
Int => "Int".to_string(),
|
||||||
Float => format!("Float"),
|
Float => "Float".to_string(),
|
||||||
StringT => format!("String"),
|
StringT => "String".to_string(),
|
||||||
Bool => format!("Bool"),
|
Bool => "Bool".to_string(),
|
||||||
Ordering => format!("Ordering"),
|
Ordering => "Ordering".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EqUnifyValue for TypeConst { }
|
impl EqUnifyValue for TypeConst { }
|
||||||
@ -110,6 +111,7 @@ macro_rules! ty {
|
|||||||
|
|
||||||
//TODO find a better way to capture the to/from string logic
|
//TODO find a better way to capture the to/from string logic
|
||||||
impl Type {
|
impl Type {
|
||||||
|
/*
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
use self::Type::*;
|
use self::Type::*;
|
||||||
@ -117,7 +119,7 @@ impl Type {
|
|||||||
Const(c) => c.to_string(),
|
Const(c) => c.to_string(),
|
||||||
Var(v) => format!("t_{}", v.0),
|
Var(v) => format!("t_{}", v.0),
|
||||||
Arrow { params, box ref ret } => {
|
Arrow { params, box ref ret } => {
|
||||||
if params.len() == 0 {
|
if params.is_empty() {
|
||||||
format!("-> {}", ret.to_string())
|
format!("-> {}", ret.to_string())
|
||||||
} else {
|
} else {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
@ -128,9 +130,10 @@ impl Type {
|
|||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Compound { .. } => format!("<some compound type>")
|
Compound { .. } => "<some compound type>".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
fn from_string(string: &str) -> Option<Type> {
|
fn from_string(string: &str) -> Option<Type> {
|
||||||
Some(match string {
|
Some(match string {
|
||||||
@ -256,7 +259,7 @@ impl<'a> TypeContext<'a> {
|
|||||||
use self::TypeIdentifier::*;
|
use self::TypeIdentifier::*;
|
||||||
Ok(match name {
|
Ok(match name {
|
||||||
Singleton(TypeSingletonName { name,.. }) => {
|
Singleton(TypeSingletonName { name,.. }) => {
|
||||||
match Type::from_string(&name) {
|
match Type::from_string(name) {
|
||||||
Some(ty) => ty,
|
Some(ty) => ty,
|
||||||
None => return TypeError::new(format!("Unknown type name: {}", name))
|
None => return TypeError::new(format!("Unknown type name: {}", name))
|
||||||
}
|
}
|
||||||
@ -279,7 +282,7 @@ impl<'a> TypeContext<'a> {
|
|||||||
fn statement(&mut self, statement: &Statement) -> InferResult<Type> {
|
fn statement(&mut self, statement: &Statement) -> InferResult<Type> {
|
||||||
match &statement.kind {
|
match &statement.kind {
|
||||||
StatementKind::Expression(e) => self.expr(e),
|
StatementKind::Expression(e) => self.expr(e),
|
||||||
StatementKind::Declaration(decl) => self.decl(&decl),
|
StatementKind::Declaration(decl) => self.decl(decl),
|
||||||
StatementKind::Import(_) => Ok(ty!(Unit)),
|
StatementKind::Import(_) => Ok(ty!(Unit)),
|
||||||
StatementKind::Module(_) => Ok(ty!(Unit)),
|
StatementKind::Module(_) => Ok(ty!(Unit)),
|
||||||
}
|
}
|
||||||
@ -287,12 +290,9 @@ impl<'a> TypeContext<'a> {
|
|||||||
|
|
||||||
fn decl(&mut self, decl: &Declaration) -> InferResult<Type> {
|
fn decl(&mut self, decl: &Declaration) -> InferResult<Type> {
|
||||||
use self::Declaration::*;
|
use self::Declaration::*;
|
||||||
match decl {
|
if let Binding { name, expr, .. } = decl {
|
||||||
Binding { name, expr, .. } => {
|
|
||||||
let ty = self.expr(expr)?;
|
let ty = self.expr(expr)?;
|
||||||
self.variable_map.insert(name.clone(), ty);
|
self.variable_map.insert(name.clone(), ty);
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
Ok(ty!(Unit))
|
Ok(ty!(Unit))
|
||||||
}
|
}
|
||||||
@ -361,10 +361,11 @@ impl<'a> TypeContext<'a> {
|
|||||||
use self::IfExpressionBody::*;
|
use self::IfExpressionBody::*;
|
||||||
match (discriminator, body) {
|
match (discriminator, body) {
|
||||||
(Some(expr), SimpleConditional{ then_case, else_case }) => self.handle_simple_if(expr, then_case, else_case),
|
(Some(expr), SimpleConditional{ then_case, else_case }) => self.handle_simple_if(expr, then_case, else_case),
|
||||||
_ => TypeError::new(format!("Complex conditionals not supported"))
|
_ => TypeError::new("Complex conditionals not supported".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
fn handle_simple_if(&mut self, expr: &Expression, then_clause: &Block, else_clause: &Option<Block>) -> InferResult<Type> {
|
fn handle_simple_if(&mut self, expr: &Expression, then_clause: &Block, else_clause: &Option<Block>) -> InferResult<Type> {
|
||||||
let t1 = self.expr(expr)?;
|
let t1 = self.expr(expr)?;
|
||||||
let t2 = self.block(then_clause)?;
|
let t2 = self.block(then_clause)?;
|
||||||
@ -377,6 +378,7 @@ impl<'a> TypeContext<'a> {
|
|||||||
self.unify(t2, t3)
|
self.unify(t2, t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
fn lambda(&mut self, params: &Vec<FormalParam>, type_anno: &Option<TypeIdentifier>, _body: &Block) -> InferResult<Type> {
|
fn lambda(&mut self, params: &Vec<FormalParam>, type_anno: &Option<TypeIdentifier>, _body: &Block) -> InferResult<Type> {
|
||||||
let argument_types: InferResult<Vec<Type>> = params.iter().map(|param: &FormalParam| {
|
let argument_types: InferResult<Vec<Type>> = params.iter().map(|param: &FormalParam| {
|
||||||
if let FormalParam { anno: Some(type_identifier), .. } = param {
|
if let FormalParam { anno: Some(type_identifier), .. } = param {
|
||||||
@ -394,7 +396,7 @@ impl<'a> TypeContext<'a> {
|
|||||||
Ok(ty!(argument_types, ret_type))
|
Ok(ty!(argument_types, ret_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, f: &Expression, args: &Vec<InvocationArgument>) -> InferResult<Type> {
|
fn call(&mut self, f: &Expression, args: &[ InvocationArgument ]) -> InferResult<Type> {
|
||||||
let tf = self.expr(f)?;
|
let tf = self.expr(f)?;
|
||||||
let arg_types: InferResult<Vec<Type>> = args.iter().map(|ex| self.invoc(ex)).collect();
|
let arg_types: InferResult<Vec<Type>> = args.iter().map(|ex| self.invoc(ex)).collect();
|
||||||
let arg_types = arg_types?;
|
let arg_types = arg_types?;
|
||||||
@ -410,10 +412,11 @@ impl<'a> TypeContext<'a> {
|
|||||||
t_ret.clone()
|
t_ret.clone()
|
||||||
},
|
},
|
||||||
Type::Arrow { .. } => return TypeError::new("Wrong length"),
|
Type::Arrow { .. } => return TypeError::new("Wrong length"),
|
||||||
_ => return TypeError::new(format!("Not a function"))
|
_ => return TypeError::new("Not a function".to_string())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
fn block(&mut self, block: &Block) -> InferResult<Type> {
|
fn block(&mut self, block: &Block) -> InferResult<Type> {
|
||||||
let mut output = ty!(Unit);
|
let mut output = ty!(Unit);
|
||||||
for statement in block.iter() {
|
for statement in block.iter() {
|
||||||
@ -438,26 +441,26 @@ impl<'a> TypeContext<'a> {
|
|||||||
(Const(ref c1), Const(ref c2)) if c1 == c2 => Ok(Const(c1.clone())), //choice of c1 is arbitrary I *think*
|
(Const(ref c1), Const(ref c2)) if c1 == c2 => Ok(Const(c1.clone())), //choice of c1 is arbitrary I *think*
|
||||||
(a @ Var(_), b @ Const(_)) => self.unify(b, a),
|
(a @ Var(_), b @ Const(_)) => self.unify(b, a),
|
||||||
(Const(ref c1), Var(ref v2)) => {
|
(Const(ref c1), Var(ref v2)) => {
|
||||||
self.unification_table.unify_var_value(v2.clone(), Some(c1.clone()))
|
self.unification_table.unify_var_value(*v2, Some(c1.clone()))
|
||||||
.or_else(|_| TypeError::new(format!("Couldn't unify {:?} and {:?}", Const(c1.clone()), Var(*v2))))?;
|
.or_else(|_| TypeError::new(format!("Couldn't unify {:?} and {:?}", Const(c1.clone()), Var(*v2))))?;
|
||||||
Ok(Const(c1.clone()))
|
Ok(Const(c1.clone()))
|
||||||
},
|
},
|
||||||
(Var(v1), Var(v2)) => {
|
(Var(v1), Var(v2)) => {
|
||||||
//TODO add occurs check
|
//TODO add occurs check
|
||||||
self.unification_table.unify_var_var(v1.clone(), v2.clone())
|
self.unification_table.unify_var_var(v1, v2)
|
||||||
.or_else(|e| {
|
.or_else(|e| {
|
||||||
println!("Unify error: {:?}", e);
|
println!("Unify error: {:?}", e);
|
||||||
TypeError::new(format!("Two type variables {:?} and {:?} couldn't unify", v1, v2))
|
TypeError::new(format!("Two type variables {:?} and {:?} couldn't unify", v1, v2))
|
||||||
})?;
|
})?;
|
||||||
Ok(Var(v1.clone())) //arbitrary decision I think
|
Ok(Var(v1)) //arbitrary decision I think
|
||||||
},
|
},
|
||||||
(a, b) => TypeError::new(format!("{:?} and {:?} do not unify", a, b)),
|
(a, b) => TypeError::new(format!("{:?} and {:?} do not unify", a, b)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fresh_type_variable(&mut self) -> TypeVar {
|
fn fresh_type_variable(&mut self) -> TypeVar {
|
||||||
let new_type_var = self.unification_table.new_key(None);
|
|
||||||
new_type_var
|
self.unification_table.new_key(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,7 +471,7 @@ mod typechecking_tests {
|
|||||||
macro_rules! assert_type_in_fresh_context {
|
macro_rules! assert_type_in_fresh_context {
|
||||||
($string:expr, $type:expr) => {
|
($string:expr, $type:expr) => {
|
||||||
let mut tc = TypeContext::new();
|
let mut tc = TypeContext::new();
|
||||||
let ref ast = crate::util::quick_ast($string);
|
let ast = &crate::util::quick_ast($string);
|
||||||
let ty = tc.typecheck(ast).unwrap();
|
let ty = tc.typecheck(ast).unwrap();
|
||||||
assert_eq!(ty, $type)
|
assert_eq!(ty, $type)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use std::cmp::Eq;
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
pub fn deref_optional_box<T>(x: &Option<Box<T>>) -> Option<&T> {
|
pub fn deref_optional_box<T>(x: &Option<Box<T>>) -> Option<&T> {
|
||||||
x.as_ref().map(|b: &Box<T>| Deref::deref(b))
|
x.as_ref().map(Deref::deref)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
Loading…
Reference in New Issue
Block a user