✍️ Switch to a handwritten lexer and parser. #1

Open
acw wants to merge 33 commits from handwritten-lexer into master
9 changed files with 478 additions and 377 deletions
Showing only changes of commit 9ea6868938 - Show all commits

View File

@@ -1,277 +1,14 @@
mod ast;
mod error; mod error;
mod location; mod location;
mod name; mod name;
mod parse; mod parse;
#[cfg(test)] #[cfg(test)]
mod parser_tests; mod parser_tests;
pub mod tokens; mod tokens;
mod universe;
pub use crate::syntax::error::ParserError; pub use crate::syntax::error::ParserError;
use crate::syntax::parse::Parser; pub use ast::*;
use crate::syntax::tokens::Lexer;
use internment::ArcIntern;
pub use location::{Located, Location}; pub use location::{Located, Location};
use memmap2::Mmap;
pub use name::Name; pub use name::Name;
use proptest_derive::Arbitrary;
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Range;
use std::path::{Path, PathBuf};
pub struct Universe {
pub files: HashMap<PathBuf, Mmap>,
pub modules: HashMap<PathBuf, Module>,
}
impl Default for Universe {
fn default() -> Self {
Universe {
files: HashMap::new(),
modules: HashMap::new(),
}
}
}
impl Universe {
pub fn add_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ParserError> {
let filename = file.as_ref().to_string_lossy().into_owned();
let file_handle = std::fs::File::open(&file)
.map_err(|e| ParserError::OpenError {
file: filename.clone(),
error: e,
})?;
let contents = unsafe { Mmap::map(&file_handle) }
.map_err(|e| ParserError::ReadError {
file: filename.clone(),
error: e,
})?;
let string_contents = std::str::from_utf8(&contents)
.map_err(|e| ParserError::Utf8Error {
file: filename.clone(),
error: e,
})?;
let lexer = Lexer::from(string_contents);
let mut parser = Parser::new(&file, lexer);
let module = parser.parse_module()?;
self.modules.insert(file.as_ref().to_path_buf(), module);
Ok(())
}
}
#[derive(Debug)]
pub struct Module {
definitions: Vec<Definition>,
}
#[derive(Debug)]
pub struct Definition {
location: Location,
export: ExportClass,
type_restrictions: TypeRestrictions,
definition: Def,
}
impl Located for Definition {
fn location(&self) -> Location {
self.location.clone()
}
}
#[derive(Debug)]
pub enum Def {
Enumeration(EnumerationDef),
Structure(StructureDef),
Function(FunctionDef),
Value(ValueDef),
}
impl Located for Def {
fn location(&self) -> Location {
match self {
Def::Enumeration(def) => def.location.clone(),
Def::Structure(def) => def.location.clone(),
Def::Function(def) => def.location.clone(),
Def::Value(def) => def.location.clone(),
}
}
}
#[derive(Debug)]
pub struct EnumerationDef {
name: String,
location: Location,
variants: Vec<EnumerationVariant>,
}
#[derive(Debug)]
pub struct EnumerationVariant {
location: Location,
name: String,
argument: Option<Type>,
}
#[derive(Debug)]
pub struct StructureDef {
name: String,
location: Location,
fields: Vec<StructureField>,
}
#[derive(Debug)]
pub struct StructureField {
location: Location,
export: ExportClass,
name: String,
field_type: Option<Type>,
}
#[derive(Debug)]
pub struct FunctionDef {
name: String,
location: Location,
arguments: Vec<FunctionArg>,
return_type: Option<Type>,
body: Vec<Statement>,
}
#[derive(Debug)]
pub struct FunctionArg {
name: String,
arg_type: Option<Type>,
}
#[derive(Debug)]
pub struct ValueDef {
name: String,
location: Location,
value: Expression,
}
#[derive(Debug)]
pub enum ExportClass {
Public,
Private,
}
#[derive(Debug)]
pub enum Statement {
Binding(BindingStmt),
Expression(Expression),
}
#[derive(Debug)]
pub struct BindingStmt {
location: Location,
mutable: bool,
variable: Name,
value: Expression,
}
#[derive(Debug)]
pub enum Expression {
Value(ConstantValue),
Reference(Name),
EnumerationValue(Name, Name, Option<Box<Expression>>),
StructureValue(Name, Vec<FieldValue>),
Conditional(ConditionalExpr),
Call(Box<Expression>, CallKind, Vec<Expression>),
Block(Location, Vec<Statement>),
}
#[derive(Debug)]
pub struct ConditionalExpr {
location: Location,
test: Box<Expression>,
consequent: Box<Expression>,
alternative: Option<Box<Expression>>,
}
#[derive(Debug)]
pub enum CallKind {
Infix,
Normal,
Postfix,
Prefix,
}
#[derive(Debug)]
pub struct FieldValue {
field: Name,
value: Expression,
}
#[derive(Debug)]
pub struct TypeRestrictions {
restrictions: Vec<TypeRestriction>,
}
impl TypeRestrictions {
fn empty() -> Self {
TypeRestrictions {
restrictions: vec![],
}
}
}
#[derive(Debug)]
pub struct TypeRestriction {
constructor: Type,
arguments: Vec<Type>,
}
#[derive(Debug)]
pub enum Type {
Constructor(Location, String),
Variable(Location, String),
Primitive(Location, String),
Application(Box<Type>, Vec<Type>),
Function(Vec<Type>, Box<Type>),
}
impl Located for Type {
fn location(&self) -> Location {
match self {
Type::Constructor(l, _) => l.clone(),
Type::Variable(l, _) => l.clone(),
Type::Primitive(l, _) => l.clone(),
Type::Application(t1, ts) => {
let mut result = t1.location();
if let Some(last) = ts.last() {
result = result.extend_to(&last.location());
}
result
}
Type::Function(args, ret) => {
if let Some(first) = args.first() {
first.location().extend_to(&ret.location())
} else {
ret.location()
}
}
}
}
}
#[derive(Debug)]
pub enum ConstantValue {
Integer(Location, IntegerWithBase),
Character(Location, char),
String(Location, String),
}
#[derive(Clone, Debug, PartialEq, Eq, Arbitrary)]
pub struct IntegerWithBase {
#[proptest(strategy = "proptest::prop_oneof![ \
proptest::strategy::Just(None), \
proptest::strategy::Just(Some(2)), \
proptest::strategy::Just(Some(8)), \
proptest::strategy::Just(Some(10)), \
proptest::strategy::Just(Some(16)), \
]")]
base: Option<u8>,
value: u64,
}

228
src/syntax/ast.rs Normal file
View File

@@ -0,0 +1,228 @@
use crate::syntax::location::{Located, Location};
use crate::syntax::name::Name;
use proptest_derive::Arbitrary;
#[derive(Debug)]
pub struct Module {
pub definitions: Vec<Definition>,
}
#[derive(Debug)]
pub struct Definition {
pub location: Location,
pub export: ExportClass,
pub type_restrictions: TypeRestrictions,
pub definition: Def,
}
impl Located for Definition {
fn location(&self) -> Location {
self.location.clone()
}
}
#[derive(Debug)]
pub enum Def {
Enumeration(EnumerationDef),
Structure(StructureDef),
Function(FunctionDef),
Value(ValueDef),
}
impl Located for Def {
fn location(&self) -> Location {
match self {
Def::Enumeration(def) => def.location.clone(),
Def::Structure(def) => def.location.clone(),
Def::Function(def) => def.location.clone(),
Def::Value(def) => def.location.clone(),
}
}
}
#[derive(Debug)]
pub struct EnumerationDef {
pub name: Name,
pub location: Location,
pub variants: Vec<EnumerationVariant>,
}
#[derive(Debug)]
pub struct EnumerationVariant {
pub location: Location,
pub name: Name,
pub argument: Option<Type>,
}
#[derive(Debug)]
pub struct StructureDef {
pub name: Name,
pub location: Location,
pub fields: Vec<StructureField>,
}
#[derive(Debug)]
pub struct StructureField {
pub location: Location,
pub export: ExportClass,
pub name: Name,
pub field_type: Option<Type>,
}
#[derive(Debug)]
pub struct FunctionDef {
pub name: Name,
pub location: Location,
pub arguments: Vec<FunctionArg>,
pub return_type: Option<Type>,
pub body: Vec<Statement>,
}
#[derive(Debug)]
pub struct FunctionArg {
pub name: Name,
pub arg_type: Option<Type>,
}
#[derive(Debug)]
pub struct ValueDef {
pub name: Name,
pub location: Location,
pub value: Expression,
}
#[derive(Debug)]
pub enum ExportClass {
Public,
Private,
}
#[derive(Debug)]
pub enum Statement {
Binding(BindingStmt),
Expression(Expression),
}
#[derive(Debug)]
pub struct BindingStmt {
pub location: Location,
pub mutable: bool,
pub variable: Name,
pub value: Expression,
}
#[derive(Debug)]
pub enum Expression {
Value(ConstantValue),
Reference(Name),
EnumerationValue(Name, Name, Option<Box<Expression>>),
StructureValue(Name, Vec<FieldValue>),
Conditional(ConditionalExpr),
Match(MatchExpr),
Call(Box<Expression>, CallKind, Vec<Expression>),
Block(Location, Vec<Statement>),
}
#[derive(Debug)]
pub struct ConditionalExpr {
pub location: Location,
pub test: Box<Expression>,
pub consequent: Box<Expression>,
pub alternative: Option<Box<Expression>>,
}
#[derive(Debug)]
pub struct MatchExpr {
pub location: Location,
pub value: Box<Expression>,
pub cases: Vec<MatchCase>,
}
#[derive(Debug)]
pub struct MatchCase {}
#[derive(Debug)]
pub enum CallKind {
Infix,
Normal,
Postfix,
Prefix,
}
#[derive(Debug)]
pub struct FieldValue {
pub field: Name,
pub value: Expression,
}
#[derive(Debug)]
pub struct TypeRestrictions {
pub restrictions: Vec<TypeRestriction>,
}
impl TypeRestrictions {
pub fn empty() -> Self {
TypeRestrictions {
restrictions: vec![],
}
}
}
#[derive(Debug)]
pub struct TypeRestriction {
pub constructor: Type,
pub arguments: Vec<Type>,
}
#[derive(Debug)]
pub enum Type {
Constructor(Location, Name),
Variable(Location, Name),
Primitive(Location, Name),
Application(Box<Type>, Vec<Type>),
Function(Vec<Type>, Box<Type>),
}
impl Located for Type {
fn location(&self) -> Location {
match self {
Type::Constructor(l, _) => l.clone(),
Type::Variable(l, _) => l.clone(),
Type::Primitive(l, _) => l.clone(),
Type::Application(t1, ts) => {
let mut result = t1.location();
if let Some(last) = ts.last() {
result = result.extend_to(&last.location());
}
result
}
Type::Function(args, ret) => {
if let Some(first) = args.first() {
first.location().extend_to(&ret.location())
} else {
ret.location()
}
}
}
}
}
#[derive(Debug)]
pub enum ConstantValue {
Integer(Location, IntegerWithBase),
Character(Location, char),
String(Location, String),
}
#[derive(Clone, Debug, PartialEq, Eq, Arbitrary)]
pub struct IntegerWithBase {
#[proptest(strategy = "proptest::prop_oneof![ \
proptest::strategy::Just(None), \
proptest::strategy::Just(Some(2)), \
proptest::strategy::Just(Some(8)), \
proptest::strategy::Just(Some(10)), \
proptest::strategy::Just(Some(16)), \
]")]
pub base: Option<u8>,
pub value: u64,
}

View File

@@ -1,17 +1,23 @@
//use codespan_reporting::diagnostic::{Diagnostic, Label}; //use codespan_reporting::diagnostic::{Diagnostic, Label};
use crate::syntax::tokens::Token; use crate::syntax::tokens::Token;
use internment::ArcIntern;
use std::ops::Range; use std::ops::Range;
use std::path::PathBuf; use std::path::PathBuf;
use internment::ArcIntern;
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ParserError { pub enum ParserError {
#[error("Lexer error at {file}: {error}")] #[error("Lexer error at {file}: {error}")]
LexerError { file: ArcIntern<PathBuf>, error: LexerError }, LexerError {
file: ArcIntern<PathBuf>,
error: LexerError,
},
#[error("Unacceptable end of file at {file} while {place}")] #[error("Unacceptable end of file at {file} while {place}")]
UnacceptableEof { file: ArcIntern<PathBuf>, place: &'static str }, UnacceptableEof {
file: ArcIntern<PathBuf>,
place: &'static str,
},
#[error("Unexpected token at {file}: expected {expected}, saw {token}")] #[error("Unexpected token at {file}: expected {expected}, saw {token}")]
UnexpectedToken { UnexpectedToken {
@@ -28,7 +34,10 @@ pub enum ParserError {
ReadError { file: String, error: std::io::Error }, ReadError { file: String, error: std::io::Error },
#[error("UTF-8 problem reading file {file}: {error}")] #[error("UTF-8 problem reading file {file}: {error}")]
Utf8Error { file: String, error: std::str::Utf8Error }, Utf8Error {
file: String,
error: std::str::Utf8Error,
},
} }
#[derive(Clone, Debug, Error, PartialEq)] #[derive(Clone, Debug, Error, PartialEq)]

View File

@@ -32,7 +32,10 @@ impl Span for Location {
impl Location { impl Location {
pub fn new(file: &ArcIntern<PathBuf>, span: Range<usize>) -> Self { pub fn new(file: &ArcIntern<PathBuf>, span: Range<usize>) -> Self {
Location { file: file.clone(), span } Location {
file: file.clone(),
span,
}
} }
pub fn extend_to(&self, other: &Location) -> Location { pub fn extend_to(&self, other: &Location) -> Location {

View File

@@ -57,4 +57,8 @@ impl Name {
pub fn as_printed(&self) -> &str { pub fn as_printed(&self) -> &str {
self.printable.as_str() self.printable.as_str()
} }
pub fn bind_to(&mut self, other: &Name) {
self.identifier = other.identifier;
}
} }

View File

@@ -3,6 +3,8 @@ use crate::syntax::tokens::{Lexer, LocatedToken, Token};
use crate::syntax::*; use crate::syntax::*;
use internment::ArcIntern; use internment::ArcIntern;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Range;
use std::path::{Path, PathBuf};
pub struct Parser<'lexer> { pub struct Parser<'lexer> {
file: ArcIntern<PathBuf>, file: ArcIntern<PathBuf>,
@@ -26,10 +28,7 @@ impl<'lexer> Parser<'lexer> {
/// error messages. If you don't care about either, you can use /// error messages. If you don't care about either, you can use
/// 0 with no loss of functionality. (Obviously, it will be harder /// 0 with no loss of functionality. (Obviously, it will be harder
/// to create quality error messages, but you already knew that.) /// to create quality error messages, but you already knew that.)
pub fn new<P: AsRef<Path>>( pub fn new<P: AsRef<Path>>(file: P, lexer: Lexer<'lexer>) -> Parser<'lexer> {
file: P,
lexer: Lexer<'lexer>
) -> Parser<'lexer> {
Parser { Parser {
file: ArcIntern::new(file.as_ref().to_path_buf()), file: ArcIntern::new(file.as_ref().to_path_buf()),
lexer, lexer,
@@ -200,10 +199,12 @@ impl<'lexer> Parser<'lexer> {
let constructor = match maybe_constructor.token { let constructor = match maybe_constructor.token {
Token::TypeName(str) => { Token::TypeName(str) => {
Type::Constructor(self.to_location(maybe_constructor.span), str) let name = Name::new(self.to_location(maybe_constructor.span.clone()), str);
Type::Constructor(self.to_location(maybe_constructor.span), name)
} }
Token::PrimitiveTypeName(str) => { Token::PrimitiveTypeName(str) => {
Type::Primitive(self.to_location(maybe_constructor.span), str) let name = Name::new(self.to_location(maybe_constructor.span.clone()), str);
Type::Primitive(self.to_location(maybe_constructor.span), name)
} }
token @ Token::CloseParen | token @ Token::Comma => { token @ Token::CloseParen | token @ Token::Comma => {
@@ -289,7 +290,7 @@ impl<'lexer> Parser<'lexer> {
.next()? .next()?
.ok_or_else(|| self.bad_eof("looking for structure name"))?; .ok_or_else(|| self.bad_eof("looking for structure name"))?;
let structure_name = match name.token { let structure_name = match name.token {
Token::TypeName(str) => str, Token::TypeName(str) => Name::new(self.to_location(name.span), str),
_ => { _ => {
return Err(ParserError::UnexpectedToken { return Err(ParserError::UnexpectedToken {
file: self.file.clone(), file: self.file.clone(),
@@ -385,7 +386,7 @@ impl<'lexer> Parser<'lexer> {
.ok_or_else(|| self.bad_eof("parsing field definition"))?; .ok_or_else(|| self.bad_eof("parsing field definition"))?;
let name = match maybe_name.token { let name = match maybe_name.token {
Token::ValueName(x) => x, Token::ValueName(x) => Name::new(self.to_location(maybe_name.span.clone()), x),
_ => { _ => {
self.save(maybe_name.clone()); self.save(maybe_name.clone());
if matches!(export, ExportClass::Private) { if matches!(export, ExportClass::Private) {
@@ -474,7 +475,7 @@ impl<'lexer> Parser<'lexer> {
.next()? .next()?
.ok_or_else(|| self.bad_eof("looking for enumeration name"))?; .ok_or_else(|| self.bad_eof("looking for enumeration name"))?;
let enumeration_name = match name.token { let enumeration_name = match name.token {
Token::TypeName(str) => str, Token::TypeName(str) => Name::new(self.to_location(name.span), str),
_ => { _ => {
return Err(ParserError::UnexpectedToken { return Err(ParserError::UnexpectedToken {
file: self.file.clone(), file: self.file.clone(),
@@ -531,7 +532,7 @@ impl<'lexer> Parser<'lexer> {
.next()? .next()?
.ok_or_else(|| self.bad_eof("looking for enumeration name"))?; .ok_or_else(|| self.bad_eof("looking for enumeration name"))?;
let name = match maybe_name.token { let name = match maybe_name.token {
Token::TypeName(x) => x, Token::TypeName(x) => Name::new(self.to_location(maybe_name.span.clone()), x),
Token::CloseBrace => { Token::CloseBrace => {
self.save(maybe_name); self.save(maybe_name);
return Ok(None); return Ok(None);
@@ -613,7 +614,9 @@ impl<'lexer> Parser<'lexer> {
self.save(next.clone()); self.save(next.clone());
match next.token { match next.token {
Token::ValueName(x) if x == "match" => self.parse_match_expression(), Token::ValueName(x) if x == "match" => {
Ok(Expression::Match(self.parse_match_expression()?))
}
Token::ValueName(x) if x == "if" => { Token::ValueName(x) if x == "if" => {
Ok(Expression::Conditional(self.parse_if_expression()?)) Ok(Expression::Conditional(self.parse_if_expression()?))
} }
@@ -621,7 +624,64 @@ impl<'lexer> Parser<'lexer> {
} }
} }
fn parse_match_expression(&mut self) -> Result<Expression, ParserError> { fn parse_match_expression(&mut self) -> Result<MatchExpr, ParserError> {
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for a 'match' to open a pattern match"))?;
if !matches!(next.token, Token::ValueName(ref x) if x == "match") {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "an 'match' to start a pattern match",
});
}
let start = self.to_location(next.span);
let value = Box::new(self.parse_arithmetic(0)?);
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for an open brace after 'match'"))?;
if !matches!(next.token, Token::OpenBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "an open brace after the match expression",
});
}
let mut cases = vec![];
while let Some(case) = self.parse_match_case()? {
cases.push(case);
}
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for an open brace after 'match'"))?;
if !matches!(next.token, Token::CloseBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "a close brace to end a match expression",
});
}
let end = self.to_location(next.span);
let location = start.extend_to(&end);
Ok(MatchExpr {
location,
value,
cases,
})
}
fn parse_match_case(&mut self) -> Result<Option<MatchCase>, ParserError> {
unimplemented!() unimplemented!()
} }
@@ -668,7 +728,7 @@ impl<'lexer> Parser<'lexer> {
}; };
Ok(ConditionalExpr { Ok(ConditionalExpr {
location, location: start.extend_to(&location),
test: Box::new(test), test: Box::new(test),
consequent: Box::new(consequent), consequent: Box::new(consequent),
alternative, alternative,
@@ -1164,8 +1224,14 @@ impl<'lexer> Parser<'lexer> {
self.next()?.ok_or_else(|| self.bad_eof("parsing type"))?; self.next()?.ok_or_else(|| self.bad_eof("parsing type"))?;
let constructor = match token { let constructor = match token {
Token::TypeName(x) => Type::Constructor(self.to_location(span), x), Token::TypeName(x) => {
Token::PrimitiveTypeName(x) => Type::Primitive(self.to_location(span), x), let name = Name::new(self.to_location(span.clone()), x);
Type::Constructor(self.to_location(span), name)
}
Token::PrimitiveTypeName(x) => {
let name = Name::new(self.to_location(span.clone()), x);
Type::Primitive(self.to_location(span), name)
}
_ => { _ => {
self.save(LocatedToken { token, span }); self.save(LocatedToken { token, span });
return self.parse_base_type(); return self.parse_base_type();
@@ -1186,9 +1252,18 @@ impl<'lexer> Parser<'lexer> {
self.next()?.ok_or_else(|| self.bad_eof("parsing type"))?; self.next()?.ok_or_else(|| self.bad_eof("parsing type"))?;
match token { match token {
Token::TypeName(x) => Ok(Type::Constructor(self.to_location(span), x)), Token::TypeName(x) => {
Token::PrimitiveTypeName(x) => Ok(Type::Primitive(self.to_location(span), x)), let name = Name::new(self.to_location(span.clone()), x);
Token::ValueName(x) => Ok(Type::Variable(self.to_location(span), x)), Ok(Type::Constructor(self.to_location(span), name))
}
Token::PrimitiveTypeName(x) => {
let name = Name::new(self.to_location(span.clone()), x);
Ok(Type::Primitive(self.to_location(span), name))
}
Token::ValueName(x) => {
let name = Name::new(self.to_location(span.clone()), x);
Ok(Type::Variable(self.to_location(span), name))
}
Token::OpenParen => { Token::OpenParen => {
let t = self.parse_type()?; let t = self.parse_type()?;
let closer = self let closer = self

View File

@@ -72,50 +72,47 @@ fn types() {
assert!(matches!( assert!(matches!(
parse_type("Cons"), parse_type("Cons"),
Ok(Type::Application(cons, empty)) if Ok(Type::Application(cons, empty)) if
matches!(cons.as_ref(), Type::Constructor(_, c) if c == "Cons") && matches!(cons.as_ref(), Type::Constructor(_, c) if c.as_printed() == "Cons") &&
empty.is_empty() empty.is_empty()
)); ));
assert!(matches!( assert!(matches!(
parse_type("cons"), parse_type("cons"),
Ok(Type::Variable(_, c)) if c == "cons" Ok(Type::Variable(_, c)) if c.as_printed() == "cons"
)); ));
assert!(matches!( assert!(matches!(
parse_type("Cons a b"), parse_type("Cons a b"),
Ok(Type::Application(a, b)) Ok(Type::Application(a, b))
if matches!(a.as_ref(), Type::Constructor(_, c) if c == "Cons") && if matches!(a.as_ref(), Type::Constructor(_, c) if c.as_printed() == "Cons") &&
matches!(b.as_slice(), [Type::Variable(_, b1), Type::Variable(_, b2)] matches!(b.as_slice(), [Type::Variable(_, b1), Type::Variable(_, b2)]
if b1 == "a" && b2 == "b") if b1.as_printed() == "a" && b2.as_printed() == "b")
)); ));
assert!(matches!( assert!(matches!(
parse_type("a -> z"), parse_type("a -> z"),
Ok(Type::Function(a, z)) Ok(Type::Function(a, z))
if matches!(a.as_slice(), [Type::Variable(_, a1)] if a1 == "a") && if matches!(a.as_slice(), [Type::Variable(_, a1)] if a1.as_printed() == "a") &&
matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z") matches!(z.as_ref(), Type::Variable(_, z1) if z1.as_printed() == "z")
)); ));
println!("-------------");
println!("{:?}", parse_type("(a -> z)"));
println!("-------------");
assert!(matches!( assert!(matches!(
parse_type("(a -> z)"), parse_type("(a -> z)"),
Ok(Type::Function(a, z)) Ok(Type::Function(a, z))
if matches!(a.as_slice(), [Type::Variable(_, a1)] if a1 == "a") && if matches!(a.as_slice(), [Type::Variable(_, a1)] if a1.as_printed() == "a") &&
matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z") matches!(z.as_ref(), Type::Variable(_, z1) if z1.as_printed() == "z")
)); ));
assert!(matches!( assert!(matches!(
parse_type("a b -> z"), parse_type("a b -> z"),
Ok(Type::Function(a, z)) Ok(Type::Function(a, z))
if matches!(a.as_slice(), [Type::Variable(_, a1), Type::Variable(_, b1)] if matches!(a.as_slice(), [Type::Variable(_, a1), Type::Variable(_, b1)]
if a1 == "a" && b1 == "b") && if a1.as_printed() == "a" && b1.as_printed() == "b") &&
matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z") matches!(z.as_ref(), Type::Variable(_, z1) if z1.as_printed() == "z")
)); ));
assert!(matches!( assert!(matches!(
parse_type("Cons a b -> z"), parse_type("Cons a b -> z"),
Ok(Type::Function(a, z)) Ok(Type::Function(a, z))
if matches!(a.as_slice(), [Type::Application(cons, appargs)] if matches!(a.as_slice(), [Type::Application(cons, appargs)]
if matches!(cons.as_ref(), Type::Constructor(_, c) if c == "Cons") && if matches!(cons.as_ref(), Type::Constructor(_, c) if c.as_printed() == "Cons") &&
matches!(appargs.as_slice(), [Type::Variable(_, b1), Type::Variable(_, b2)] matches!(appargs.as_slice(), [Type::Variable(_, b1), Type::Variable(_, b2)]
if b1 == "a" && b2 == "b")) && if b1.as_printed() == "a" && b2.as_printed() == "b")) &&
matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z") matches!(z.as_ref(), Type::Variable(_, z1) if z1.as_printed() == "z")
)); ));
} }
@@ -138,10 +135,10 @@ fn type_restrictions() {
matches!(&restrictions[0], TypeRestriction { matches!(&restrictions[0], TypeRestriction {
constructor, constructor,
arguments, arguments,
} if matches!(constructor, Type::Constructor(_, x) if x == "Cons") && } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Cons") &&
arguments.len() == 2 && arguments.len() == 2 &&
matches!(&arguments[0], Type::Variable(_, x) if x == "a") && matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") &&
matches!(&arguments[1], Type::Variable(_, x) if x == "b")))); matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b"))));
assert!(matches!( assert!(matches!(
parse_tr("restrict(Cons a b,)"), parse_tr("restrict(Cons a b,)"),
@@ -149,10 +146,10 @@ fn type_restrictions() {
matches!(&restrictions[0], TypeRestriction { matches!(&restrictions[0], TypeRestriction {
constructor, constructor,
arguments, arguments,
} if matches!(constructor, Type::Constructor(_, x) if x == "Cons") && } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Cons") &&
arguments.len() == 2 && arguments.len() == 2 &&
matches!(&arguments[0], Type::Variable(_, x) if x == "a") && matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") &&
matches!(&arguments[1], Type::Variable(_, x) if x == "b")))); matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b"))));
assert!(matches!(parse_tr("restrict(,Cons a b,)"), Err(_))); assert!(matches!(parse_tr("restrict(,Cons a b,)"), Err(_)));
@@ -162,16 +159,16 @@ fn type_restrictions() {
matches!(&restrictions[0], TypeRestriction { matches!(&restrictions[0], TypeRestriction {
constructor, constructor,
arguments, arguments,
} if matches!(constructor, Type::Constructor(_, x) if x == "Cons") && } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Cons") &&
arguments.len() == 2 && arguments.len() == 2 &&
matches!(&arguments[0], Type::Variable(_, x) if x == "a") && matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") &&
matches!(&arguments[1], Type::Variable(_, x) if x == "b")) && matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b")) &&
matches!(&restrictions[1], TypeRestriction { matches!(&restrictions[1], TypeRestriction {
constructor, constructor,
arguments, arguments,
} if matches!(constructor, Type::Constructor(_, x) if x == "Monad") && } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Monad") &&
arguments.len() == 1 && arguments.len() == 1 &&
matches!(&arguments[0], Type::Variable(_, x) if x == "m")))); matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "m"))));
assert!(matches!( assert!(matches!(
parse_tr("restrict(Cons a b, Monad m,)"), parse_tr("restrict(Cons a b, Monad m,)"),
@@ -179,16 +176,16 @@ fn type_restrictions() {
matches!(&restrictions[0], TypeRestriction { matches!(&restrictions[0], TypeRestriction {
constructor, constructor,
arguments, arguments,
} if matches!(constructor, Type::Constructor(_, x) if x == "Cons") && } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Cons") &&
arguments.len() == 2 && arguments.len() == 2 &&
matches!(&arguments[0], Type::Variable(_, x) if x == "a") && matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") &&
matches!(&arguments[1], Type::Variable(_, x) if x == "b")) && matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b")) &&
matches!(&restrictions[1], TypeRestriction { matches!(&restrictions[1], TypeRestriction {
constructor, constructor,
arguments, arguments,
} if matches!(constructor, Type::Constructor(_, x) if x == "Monad") && } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Monad") &&
arguments.len() == 1 && arguments.len() == 1 &&
matches!(&arguments[0], Type::Variable(_, x) if x == "m")))); matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "m"))));
} }
#[test] #[test]
@@ -203,46 +200,46 @@ fn field_definition() {
assert!(matches!( assert!(matches!(
parse_fd("foo,"), parse_fd("foo,"),
Ok(Some(StructureField{ name, export: ExportClass::Private, field_type: None, .. })) Ok(Some(StructureField{ name, export: ExportClass::Private, field_type: None, .. }))
if name == "foo" if name.as_printed() == "foo"
)); ));
assert!(matches!( assert!(matches!(
parse_fd("foo}"), parse_fd("foo}"),
Ok(Some(StructureField{ name, export: ExportClass::Private, field_type: None, .. })) Ok(Some(StructureField{ name, export: ExportClass::Private, field_type: None, .. }))
if name == "foo" if name.as_printed() == "foo"
)); ));
assert!(matches!( assert!(matches!(
parse_fd("foo: Word8,"), parse_fd("foo: Word8,"),
Ok(Some(StructureField{ name, field_type, .. })) Ok(Some(StructureField{ name, field_type, .. }))
if name == "foo" && if name.as_printed() == "foo" &&
matches!(&field_type, Some(Type::Application(c, args)) matches!(&field_type, Some(Type::Application(c, args))
if matches!(c.as_ref(), Type::Constructor(_, c) if c == "Word8") && if matches!(c.as_ref(), Type::Constructor(_, c) if c.as_printed() == "Word8") &&
args.is_empty()))); args.is_empty())));
assert!(matches!( assert!(matches!(
parse_fd("foo: Cons a b,"), parse_fd("foo: Cons a b,"),
Ok(Some(StructureField{ name, field_type, .. })) Ok(Some(StructureField{ name, field_type, .. }))
if name == "foo" && if name.as_printed() == "foo" &&
matches!(&field_type, Some(Type::Application(c, args)) matches!(&field_type, Some(Type::Application(c, args))
if matches!(c.as_ref(), Type::Constructor(_, c) if c == "Cons") && if matches!(c.as_ref(), Type::Constructor(_, c) if c.as_printed() == "Cons") &&
matches!(&args.as_slice(), &[Type::Variable(_, v1), Type::Variable(_, v2)] matches!(&args.as_slice(), &[Type::Variable(_, v1), Type::Variable(_, v2)]
if v1 == "a" && v2 == "b")))); if v1.as_printed() == "a" && v2.as_printed() == "b"))));
assert!(matches!( assert!(matches!(
parse_fd("foo: a -> b,"), parse_fd("foo: a -> b,"),
Ok(Some(StructureField{ name, field_type, .. })) Ok(Some(StructureField{ name, field_type, .. }))
if name == "foo" && if name.as_printed() == "foo" &&
matches!(&field_type, Some(Type::Function(args, ret)) matches!(&field_type, Some(Type::Function(args, ret))
if matches!(&args.as_slice(), &[Type::Variable(_, a)] if a == "a") && if matches!(&args.as_slice(), &[Type::Variable(_, a)] if a.as_printed() == "a") &&
matches!(ret.as_ref(), Type::Variable(_, b) if b == "b")))); matches!(ret.as_ref(), Type::Variable(_, b) if b.as_printed() == "b"))));
assert!(matches!( assert!(matches!(
parse_fd("export foo: a -> b,"), parse_fd("export foo: a -> b,"),
Ok(Some(StructureField{ name, export: ExportClass::Public, field_type, .. })) Ok(Some(StructureField{ name, export: ExportClass::Public, field_type, .. }))
if name == "foo" && if name.as_printed() == "foo" &&
matches!(&field_type, Some(Type::Function(args, ret)) matches!(&field_type, Some(Type::Function(args, ret))
if matches!(&args.as_slice(), &[Type::Variable(_, a)] if a == "a") && if matches!(&args.as_slice(), &[Type::Variable(_, a)] if a.as_printed() == "a") &&
matches!(ret.as_ref(), Type::Variable(_, b) if b == "b")))); matches!(ret.as_ref(), Type::Variable(_, b) if b.as_printed() == "b"))));
} }
#[test] #[test]
@@ -260,65 +257,65 @@ fn structures() {
assert!(matches!( assert!(matches!(
parse_st("structure Foo {}"), parse_st("structure Foo {}"),
Ok(StructureDef { name, fields, .. }) Ok(StructureDef { name, fields, .. })
if name == "Foo" && fields.is_empty())); if name.as_printed() == "Foo" && fields.is_empty()));
assert!(matches!( assert!(matches!(
parse_st("structure Foo { bar }"), parse_st("structure Foo { bar }"),
Ok(StructureDef { name, fields, .. }) Ok(StructureDef { name, fields, .. })
if name == "Foo" && if name.as_printed() == "Foo" &&
matches!(fields.as_slice(), &[StructureField { ref name, ref field_type, .. }] matches!(fields.as_slice(), &[StructureField { ref name, ref field_type, .. }]
if name == "bar" && matches!(field_type, None)))); if name.as_printed() == "bar" && matches!(field_type, None))));
assert!(matches!( assert!(matches!(
parse_st("structure Foo { bar: Word8 }"), parse_st("structure Foo { bar: Word8 }"),
Ok(StructureDef { name, fields, .. }) Ok(StructureDef { name, fields, .. })
if name == "Foo" && if name.as_printed() == "Foo" &&
matches!(fields.as_slice(), &[StructureField { ref name, ref field_type, .. }] matches!(fields.as_slice(), &[StructureField { ref name, ref field_type, .. }]
if name == "bar" && if name.as_printed() == "bar" &&
matches!(field_type, Some(Type::Application(c, args)) matches!(field_type, Some(Type::Application(c, args))
if matches!(c.as_ref(), Type::Constructor(_, c) if c == "Word8") && if matches!(c.as_ref(), Type::Constructor(_, c) if c.as_printed() == "Word8") &&
args.is_empty())))); args.is_empty()))));
assert!(matches!( assert!(matches!(
parse_st("structure Foo { bar: Word8, goo }"), parse_st("structure Foo { bar: Word8, goo }"),
Ok(StructureDef { name, fields, .. }) Ok(StructureDef { name, fields, .. })
if name == "Foo" && if name.as_printed() == "Foo" &&
matches!(fields.as_slice(), matches!(fields.as_slice(),
&[StructureField { ref name, ref field_type, .. }, &[StructureField { ref name, ref field_type, .. },
StructureField { name: ref name2, field_type: None, .. }] StructureField { name: ref name2, field_type: None, .. }]
if name == "bar" && if name.as_printed() == "bar" &&
name2 == "goo" && name2.as_printed() == "goo" &&
matches!(field_type, Some(Type::Application(c, args)) matches!(field_type, Some(Type::Application(c, args))
if matches!(c.as_ref(), Type::Constructor(_, c) if c == "Word8") && if matches!(c.as_ref(), Type::Constructor(_, c) if c.as_printed() == "Word8") &&
args.is_empty())))); args.is_empty()))));
assert!(matches!( assert!(matches!(
parse_st("structure Foo { bar: b c -> a, goo }"), parse_st("structure Foo { bar: b c -> a, goo }"),
Ok(StructureDef { name, fields, .. }) Ok(StructureDef { name, fields, .. })
if name == "Foo" && if name.as_printed() == "Foo" &&
matches!(fields.as_slice(), matches!(fields.as_slice(),
&[StructureField { ref name, ref field_type, .. }, &[StructureField { ref name, ref field_type, .. },
StructureField { name: ref name2, field_type: None, .. }] StructureField { name: ref name2, field_type: None, .. }]
if name == "bar" && if name.as_printed() == "bar" &&
name2 == "goo" && name2.as_printed() == "goo" &&
matches!(field_type, Some(Type::Function(args, ret)) matches!(field_type, Some(Type::Function(args, ret))
if matches!(&args.as_slice(), &[Type::Variable(_, b), Type::Variable(_, c)] if matches!(&args.as_slice(), &[Type::Variable(_, b), Type::Variable(_, c)]
if b == "b" && c == "c") && if b.as_printed() == "b" && c.as_printed() == "c") &&
matches!(ret.as_ref(), Type::Variable(_, a) if a == "a"))))); matches!(ret.as_ref(), Type::Variable(_, a) if a.as_printed() == "a")))));
assert!(matches!( assert!(matches!(
parse_st("structure Foo { bar: b c -> a, goo, }"), parse_st("structure Foo { bar: b c -> a, goo, }"),
Ok(StructureDef { name, fields, .. }) Ok(StructureDef { name, fields, .. })
if name == "Foo" && if name.as_printed() == "Foo" &&
matches!(fields.as_slice(), matches!(fields.as_slice(),
&[StructureField { ref name, ref field_type, .. }, &[StructureField { ref name, ref field_type, .. },
StructureField { name: ref name2, field_type: None, .. }] StructureField { name: ref name2, field_type: None, .. }]
if name == "bar" && if name.as_printed() == "bar" &&
name2 == "goo" && name2.as_printed() == "goo" &&
matches!(field_type, Some(Type::Function(args, ret)) matches!(field_type, Some(Type::Function(args, ret))
if matches!(&args.as_slice(), &[Type::Variable(_, b), Type::Variable(_, c)] if matches!(&args.as_slice(), &[Type::Variable(_, b), Type::Variable(_, c)]
if b == "b" && c == "c") && if b.as_printed() == "b" && c.as_printed() == "c") &&
matches!(ret.as_ref(), Type::Variable(_, a) if a == "a"))))); matches!(ret.as_ref(), Type::Variable(_, a) if a.as_printed() == "a")))));
} }
#[test] #[test]
@@ -339,43 +336,43 @@ fn enum_variant() {
assert!(matches!( assert!(matches!(
parse_ev("Cons,"), parse_ev("Cons,"),
Ok(Some(EnumerationVariant { name, argument, .. })) Ok(Some(EnumerationVariant { name, argument, .. }))
if name == "Cons" && argument.is_none())); if name.as_printed() == "Cons" && argument.is_none()));
assert!(matches!( assert!(matches!(
parse_ev("Cons }"), parse_ev("Cons }"),
Ok(Some(EnumerationVariant { name, argument, .. })) Ok(Some(EnumerationVariant { name, argument, .. }))
if name == "Cons" && argument.is_none())); if name.as_printed() == "Cons" && argument.is_none()));
assert!(matches!( assert!(matches!(
parse_ev("Cons, }"), parse_ev("Cons, }"),
Ok(Some(EnumerationVariant { name, argument, .. })) Ok(Some(EnumerationVariant { name, argument, .. }))
if name == "Cons" && argument.is_none())); if name.as_printed() == "Cons" && argument.is_none()));
assert!(matches!( assert!(matches!(
parse_ev("Cons(Pair a),"), parse_ev("Cons(Pair a),"),
Ok(Some(EnumerationVariant { name, ref argument, .. })) Ok(Some(EnumerationVariant { name, ref argument, .. }))
if name == "Cons" && if name.as_printed() == "Cons" &&
matches!(argument, Some(Type::Application(typef, args)) matches!(argument, Some(Type::Application(typef, args))
if matches!(typef.as_ref(), Type::Constructor(_, name) if matches!(typef.as_ref(), Type::Constructor(_, name)
if name == "Pair") && if name.as_printed() == "Pair") &&
matches!(&args.as_slice(), &[Type::Variable(_, argname)] matches!(&args.as_slice(), &[Type::Variable(_, argname)]
if argname == "a")))); if argname.as_printed() == "a"))));
assert!(matches!( assert!(matches!(
parse_ev("Cons(Pair a) }"), parse_ev("Cons(Pair a) }"),
Ok(Some(EnumerationVariant { name, ref argument, .. })) Ok(Some(EnumerationVariant { name, ref argument, .. }))
if name == "Cons" && if name.as_printed() == "Cons" &&
matches!(argument, Some(Type::Application(typef, args)) matches!(argument, Some(Type::Application(typef, args))
if matches!(typef.as_ref(), Type::Constructor(_, name) if matches!(typef.as_ref(), Type::Constructor(_, name)
if name == "Pair") && if name.as_printed() == "Pair") &&
matches!(&args.as_slice(), &[Type::Variable(_, argname)] matches!(&args.as_slice(), &[Type::Variable(_, argname)]
if argname == "a")))); if argname.as_printed() == "a"))));
assert!(matches!( assert!(matches!(
parse_ev("Cons(a b -> c) }"), parse_ev("Cons(a b -> c) }"),
Ok(Some(EnumerationVariant { name, ref argument, .. })) Ok(Some(EnumerationVariant { name, ref argument, .. }))
if name == "Cons" && if name.as_printed() == "Cons" &&
matches!(argument, Some(Type::Function(args, ret)) matches!(argument, Some(Type::Function(args, ret))
if matches!(&args.as_slice(), &[Type::Variable(_, a), Type::Variable(_, b)] if matches!(&args.as_slice(), &[Type::Variable(_, a), Type::Variable(_, b)]
if a == "a" && b == "b") && if a.as_printed() == "a" && b.as_printed() == "b") &&
matches!(ret.as_ref(), Type::Variable(_, c) if c == "c")))); matches!(ret.as_ref(), Type::Variable(_, c) if c.as_printed() == "c"))));
} }
#[test] #[test]
@@ -393,25 +390,25 @@ fn enumerations() {
assert!(matches!( assert!(matches!(
parse_en("enumeration Empty { }"), parse_en("enumeration Empty { }"),
Ok(EnumerationDef { name, variants, .. }) Ok(EnumerationDef { name, variants, .. })
if name == "Empty" && variants.is_empty())); if name.as_printed() == "Empty" && variants.is_empty()));
assert!(matches!( assert!(matches!(
parse_en("enumeration Alternates { A, B }"), parse_en("enumeration Alternates { A, B }"),
Ok(EnumerationDef { name, variants, .. }) Ok(EnumerationDef { name, variants, .. })
if name == "Alternates" && if name.as_printed() == "Alternates" &&
matches!(&variants.as_slice(), &[ matches!(&variants.as_slice(), &[
EnumerationVariant { name: name1, argument: arg1, ..}, EnumerationVariant { name: name1, argument: arg1, ..},
EnumerationVariant { name: name2, argument: arg2, ..}, EnumerationVariant { name: name2, argument: arg2, ..},
] if name1 == "A" && arg1.is_none() && ] if name1.as_printed() == "A" && arg1.is_none() &&
name2 == "B" && arg2.is_none()))); name2.as_printed() == "B" && arg2.is_none())));
assert!(matches!( assert!(matches!(
parse_en("enumeration Alternates { A, B, }"), parse_en("enumeration Alternates { A, B, }"),
Ok(EnumerationDef { name, variants, .. }) Ok(EnumerationDef { name, variants, .. })
if name == "Alternates" && if name.as_printed() == "Alternates" &&
matches!(&variants.as_slice(), &[ matches!(&variants.as_slice(), &[
EnumerationVariant { name: name1, argument: arg1, ..}, EnumerationVariant { name: name1, argument: arg1, ..},
EnumerationVariant { name: name2, argument: arg2, ..}, EnumerationVariant { name: name2, argument: arg2, ..},
] if name1 == "A" && arg1.is_none() && ] if name1.as_printed() == "A" && arg1.is_none() &&
name2 == "B" && arg2.is_none()))); name2.as_printed() == "B" && arg2.is_none())));
} }
#[test] #[test]

View File

@@ -89,7 +89,7 @@ impl fmt::Display for Token {
pub enum Lexer<'a> { pub enum Lexer<'a> {
Working(LexerState<'a>), Working(LexerState<'a>),
Errored(LexerError), Errored(LexerError),
Done(usize), Done,
} }
struct LexerState<'a> { struct LexerState<'a> {
@@ -120,7 +120,7 @@ impl<'a> Iterator for Lexer<'a> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
match self { match self {
Lexer::Done(_) => None, Lexer::Done => None,
Lexer::Errored(e) => Some(Err(e.clone())), Lexer::Errored(e) => Some(Err(e.clone())),
Lexer::Working(state) => match state.next_token() { Lexer::Working(state) => match state.next_token() {
Err(e) => { Err(e) => {
@@ -130,7 +130,7 @@ impl<'a> Iterator for Lexer<'a> {
} }
Ok(None) => { Ok(None) => {
*self = Lexer::Done(state.stream.offset()); *self = Lexer::Done;
None None
} }

48
src/syntax/universe.rs Normal file
View File

@@ -0,0 +1,48 @@
use crate::syntax::ast::*;
use crate::syntax::error::ParserError;
use crate::syntax::parse::Parser;
use crate::syntax::tokens::Lexer;
use memmap2::Mmap;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
pub struct Universe {
pub files: HashMap<PathBuf, Mmap>,
pub modules: HashMap<PathBuf, Module>,
}
impl Default for Universe {
fn default() -> Self {
Universe {
files: HashMap::new(),
modules: HashMap::new(),
}
}
}
impl Universe {
pub fn add_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ParserError> {
let filename = file.as_ref().to_string_lossy().into_owned();
let file_handle = std::fs::File::open(&file).map_err(|e| ParserError::OpenError {
file: filename.clone(),
error: e,
})?;
let contents = unsafe { Mmap::map(&file_handle) }.map_err(|e| ParserError::ReadError {
file: filename.clone(),
error: e,
})?;
let string_contents =
std::str::from_utf8(&contents).map_err(|e| ParserError::Utf8Error {
file: filename.clone(),
error: e,
})?;
let lexer = Lexer::from(string_contents);
let mut parser = Parser::new(&file, lexer);
let module = parser.parse_module()?;
self.modules.insert(file.as_ref().to_path_buf(), module);
Ok(())
}
}