From 9ea6868938b10fa717e988df5a1dd5a0c183875f Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 11 Oct 2025 14:46:02 -0700 Subject: [PATCH] Shifting and naming. --- src/syntax.rs | 271 +------------------------------------ src/syntax/ast.rs | 228 +++++++++++++++++++++++++++++++ src/syntax/error.rs | 17 ++- src/syntax/location.rs | 5 +- src/syntax/name.rs | 4 + src/syntax/parse.rs | 111 ++++++++++++--- src/syntax/parser_tests.rs | 165 +++++++++++----------- src/syntax/tokens.rs | 6 +- src/syntax/universe.rs | 48 +++++++ 9 files changed, 478 insertions(+), 377 deletions(-) create mode 100644 src/syntax/ast.rs create mode 100644 src/syntax/universe.rs diff --git a/src/syntax.rs b/src/syntax.rs index 4476080..d15f8f9 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1,277 +1,14 @@ +mod ast; mod error; mod location; mod name; mod parse; #[cfg(test)] mod parser_tests; -pub mod tokens; +mod tokens; +mod universe; pub use crate::syntax::error::ParserError; -use crate::syntax::parse::Parser; -use crate::syntax::tokens::Lexer; -use internment::ArcIntern; +pub use ast::*; pub use location::{Located, Location}; -use memmap2::Mmap; 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, - pub modules: HashMap, -} - -impl Default for Universe { - fn default() -> Self { - Universe { - files: HashMap::new(), - modules: HashMap::new(), - } - } -} - -impl Universe { - pub fn add_file>(&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, -} - -#[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, -} - -#[derive(Debug)] -pub struct EnumerationVariant { - location: Location, - name: String, - argument: Option, -} - -#[derive(Debug)] -pub struct StructureDef { - name: String, - location: Location, - fields: Vec, -} - -#[derive(Debug)] -pub struct StructureField { - location: Location, - export: ExportClass, - name: String, - field_type: Option, -} - -#[derive(Debug)] -pub struct FunctionDef { - name: String, - location: Location, - arguments: Vec, - return_type: Option, - body: Vec, -} - -#[derive(Debug)] -pub struct FunctionArg { - name: String, - arg_type: Option, -} - -#[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>), - StructureValue(Name, Vec), - Conditional(ConditionalExpr), - Call(Box, CallKind, Vec), - Block(Location, Vec), -} - -#[derive(Debug)] -pub struct ConditionalExpr { - location: Location, - test: Box, - consequent: Box, - alternative: Option>, -} - -#[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, -} - -impl TypeRestrictions { - fn empty() -> Self { - TypeRestrictions { - restrictions: vec![], - } - } -} - -#[derive(Debug)] -pub struct TypeRestriction { - constructor: Type, - arguments: Vec, -} - -#[derive(Debug)] -pub enum Type { - Constructor(Location, String), - Variable(Location, String), - Primitive(Location, String), - Application(Box, Vec), - Function(Vec, Box), -} - -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, - value: u64, -} diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs new file mode 100644 index 0000000..a25fbae --- /dev/null +++ b/src/syntax/ast.rs @@ -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, +} + +#[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, +} + +#[derive(Debug)] +pub struct EnumerationVariant { + pub location: Location, + pub name: Name, + pub argument: Option, +} + +#[derive(Debug)] +pub struct StructureDef { + pub name: Name, + pub location: Location, + pub fields: Vec, +} + +#[derive(Debug)] +pub struct StructureField { + pub location: Location, + pub export: ExportClass, + pub name: Name, + pub field_type: Option, +} + +#[derive(Debug)] +pub struct FunctionDef { + pub name: Name, + pub location: Location, + pub arguments: Vec, + pub return_type: Option, + pub body: Vec, +} + +#[derive(Debug)] +pub struct FunctionArg { + pub name: Name, + pub arg_type: Option, +} + +#[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>), + StructureValue(Name, Vec), + Conditional(ConditionalExpr), + Match(MatchExpr), + Call(Box, CallKind, Vec), + Block(Location, Vec), +} + +#[derive(Debug)] +pub struct ConditionalExpr { + pub location: Location, + pub test: Box, + pub consequent: Box, + pub alternative: Option>, +} + +#[derive(Debug)] +pub struct MatchExpr { + pub location: Location, + pub value: Box, + pub cases: Vec, +} + +#[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, +} + +impl TypeRestrictions { + pub fn empty() -> Self { + TypeRestrictions { + restrictions: vec![], + } + } +} + +#[derive(Debug)] +pub struct TypeRestriction { + pub constructor: Type, + pub arguments: Vec, +} + +#[derive(Debug)] +pub enum Type { + Constructor(Location, Name), + Variable(Location, Name), + Primitive(Location, Name), + Application(Box, Vec), + Function(Vec, Box), +} + +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, + pub value: u64, +} diff --git a/src/syntax/error.rs b/src/syntax/error.rs index 7700cb1..33e3b42 100644 --- a/src/syntax/error.rs +++ b/src/syntax/error.rs @@ -1,17 +1,23 @@ //use codespan_reporting::diagnostic::{Diagnostic, Label}; use crate::syntax::tokens::Token; +use internment::ArcIntern; use std::ops::Range; use std::path::PathBuf; -use internment::ArcIntern; use thiserror::Error; #[derive(Debug, Error)] pub enum ParserError { #[error("Lexer error at {file}: {error}")] - LexerError { file: ArcIntern, error: LexerError }, + LexerError { + file: ArcIntern, + error: LexerError, + }, #[error("Unacceptable end of file at {file} while {place}")] - UnacceptableEof { file: ArcIntern, place: &'static str }, + UnacceptableEof { + file: ArcIntern, + place: &'static str, + }, #[error("Unexpected token at {file}: expected {expected}, saw {token}")] UnexpectedToken { @@ -28,7 +34,10 @@ pub enum ParserError { ReadError { file: String, error: std::io::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)] diff --git a/src/syntax/location.rs b/src/syntax/location.rs index 104d6d8..cd8bc78 100644 --- a/src/syntax/location.rs +++ b/src/syntax/location.rs @@ -32,7 +32,10 @@ impl Span for Location { impl Location { pub fn new(file: &ArcIntern, span: Range) -> Self { - Location { file: file.clone(), span } + Location { + file: file.clone(), + span, + } } pub fn extend_to(&self, other: &Location) -> Location { diff --git a/src/syntax/name.rs b/src/syntax/name.rs index fbfdb5f..f8069ad 100644 --- a/src/syntax/name.rs +++ b/src/syntax/name.rs @@ -57,4 +57,8 @@ impl Name { pub fn as_printed(&self) -> &str { self.printable.as_str() } + + pub fn bind_to(&mut self, other: &Name) { + self.identifier = other.identifier; + } } diff --git a/src/syntax/parse.rs b/src/syntax/parse.rs index d6c0d80..662cfe3 100644 --- a/src/syntax/parse.rs +++ b/src/syntax/parse.rs @@ -3,6 +3,8 @@ use crate::syntax::tokens::{Lexer, LocatedToken, Token}; use crate::syntax::*; use internment::ArcIntern; use std::collections::HashMap; +use std::ops::Range; +use std::path::{Path, PathBuf}; pub struct Parser<'lexer> { file: ArcIntern, @@ -26,10 +28,7 @@ impl<'lexer> Parser<'lexer> { /// error messages. If you don't care about either, you can use /// 0 with no loss of functionality. (Obviously, it will be harder /// to create quality error messages, but you already knew that.) - pub fn new>( - file: P, - lexer: Lexer<'lexer> - ) -> Parser<'lexer> { + pub fn new>(file: P, lexer: Lexer<'lexer>) -> Parser<'lexer> { Parser { file: ArcIntern::new(file.as_ref().to_path_buf()), lexer, @@ -200,10 +199,12 @@ impl<'lexer> Parser<'lexer> { let constructor = match maybe_constructor.token { 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) => { - 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 => { @@ -289,7 +290,7 @@ impl<'lexer> Parser<'lexer> { .next()? .ok_or_else(|| self.bad_eof("looking for structure name"))?; 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 { file: self.file.clone(), @@ -385,7 +386,7 @@ impl<'lexer> Parser<'lexer> { .ok_or_else(|| self.bad_eof("parsing field definition"))?; 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()); if matches!(export, ExportClass::Private) { @@ -474,7 +475,7 @@ impl<'lexer> Parser<'lexer> { .next()? .ok_or_else(|| self.bad_eof("looking for enumeration name"))?; 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 { file: self.file.clone(), @@ -531,7 +532,7 @@ impl<'lexer> Parser<'lexer> { .next()? .ok_or_else(|| self.bad_eof("looking for enumeration name"))?; 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 => { self.save(maybe_name); return Ok(None); @@ -613,7 +614,9 @@ impl<'lexer> Parser<'lexer> { self.save(next.clone()); 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" => { Ok(Expression::Conditional(self.parse_if_expression()?)) } @@ -621,7 +624,64 @@ impl<'lexer> Parser<'lexer> { } } - fn parse_match_expression(&mut self) -> Result { + fn parse_match_expression(&mut self) -> Result { + 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, ParserError> { unimplemented!() } @@ -668,7 +728,7 @@ impl<'lexer> Parser<'lexer> { }; Ok(ConditionalExpr { - location, + location: start.extend_to(&location), test: Box::new(test), consequent: Box::new(consequent), alternative, @@ -1164,8 +1224,14 @@ impl<'lexer> Parser<'lexer> { self.next()?.ok_or_else(|| self.bad_eof("parsing type"))?; let constructor = match token { - Token::TypeName(x) => Type::Constructor(self.to_location(span), x), - Token::PrimitiveTypeName(x) => Type::Primitive(self.to_location(span), x), + Token::TypeName(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 }); return self.parse_base_type(); @@ -1186,9 +1252,18 @@ impl<'lexer> Parser<'lexer> { self.next()?.ok_or_else(|| self.bad_eof("parsing type"))?; match token { - Token::TypeName(x) => Ok(Type::Constructor(self.to_location(span), x)), - Token::PrimitiveTypeName(x) => Ok(Type::Primitive(self.to_location(span), x)), - Token::ValueName(x) => Ok(Type::Variable(self.to_location(span), x)), + Token::TypeName(x) => { + let name = Name::new(self.to_location(span.clone()), 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 => { let t = self.parse_type()?; let closer = self diff --git a/src/syntax/parser_tests.rs b/src/syntax/parser_tests.rs index 0e1bbd5..aec9810 100644 --- a/src/syntax/parser_tests.rs +++ b/src/syntax/parser_tests.rs @@ -72,50 +72,47 @@ fn types() { assert!(matches!( parse_type("Cons"), 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() )); assert!(matches!( parse_type("cons"), - Ok(Type::Variable(_, c)) if c == "cons" + Ok(Type::Variable(_, c)) if c.as_printed() == "cons" )); assert!(matches!( parse_type("Cons 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)] - if b1 == "a" && b2 == "b") + if b1.as_printed() == "a" && b2.as_printed() == "b") )); assert!(matches!( parse_type("a -> z"), Ok(Type::Function(a, z)) - if matches!(a.as_slice(), [Type::Variable(_, a1)] if a1 == "a") && - matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z") + if matches!(a.as_slice(), [Type::Variable(_, a1)] if a1.as_printed() == "a") && + matches!(z.as_ref(), Type::Variable(_, z1) if z1.as_printed() == "z") )); - println!("-------------"); - println!("{:?}", parse_type("(a -> z)")); - println!("-------------"); assert!(matches!( parse_type("(a -> z)"), Ok(Type::Function(a, z)) - if matches!(a.as_slice(), [Type::Variable(_, a1)] if a1 == "a") && - matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z") + if matches!(a.as_slice(), [Type::Variable(_, a1)] if a1.as_printed() == "a") && + matches!(z.as_ref(), Type::Variable(_, z1) if z1.as_printed() == "z") )); assert!(matches!( parse_type("a b -> z"), Ok(Type::Function(a, z)) if matches!(a.as_slice(), [Type::Variable(_, a1), Type::Variable(_, b1)] - if a1 == "a" && b1 == "b") && - matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z") + if a1.as_printed() == "a" && b1.as_printed() == "b") && + matches!(z.as_ref(), Type::Variable(_, z1) if z1.as_printed() == "z") )); assert!(matches!( parse_type("Cons a b -> z"), Ok(Type::Function(a, z)) 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)] - if b1 == "a" && b2 == "b")) && - matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z") + if b1.as_printed() == "a" && b2.as_printed() == "b")) && + matches!(z.as_ref(), Type::Variable(_, z1) if z1.as_printed() == "z") )); } @@ -138,10 +135,10 @@ fn type_restrictions() { matches!(&restrictions[0], TypeRestriction { constructor, arguments, - } if matches!(constructor, Type::Constructor(_, x) if x == "Cons") && + } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Cons") && arguments.len() == 2 && - matches!(&arguments[0], Type::Variable(_, x) if x == "a") && - matches!(&arguments[1], Type::Variable(_, x) if x == "b")))); + matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") && + matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b")))); assert!(matches!( parse_tr("restrict(Cons a b,)"), @@ -149,10 +146,10 @@ fn type_restrictions() { matches!(&restrictions[0], TypeRestriction { constructor, arguments, - } if matches!(constructor, Type::Constructor(_, x) if x == "Cons") && + } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Cons") && arguments.len() == 2 && - matches!(&arguments[0], Type::Variable(_, x) if x == "a") && - matches!(&arguments[1], Type::Variable(_, x) if x == "b")))); + matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") && + matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b")))); assert!(matches!(parse_tr("restrict(,Cons a b,)"), Err(_))); @@ -162,16 +159,16 @@ fn type_restrictions() { matches!(&restrictions[0], TypeRestriction { constructor, arguments, - } if matches!(constructor, Type::Constructor(_, x) if x == "Cons") && + } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Cons") && arguments.len() == 2 && - matches!(&arguments[0], Type::Variable(_, x) if x == "a") && - matches!(&arguments[1], Type::Variable(_, x) if x == "b")) && + matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") && + matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b")) && matches!(&restrictions[1], TypeRestriction { constructor, arguments, - } if matches!(constructor, Type::Constructor(_, x) if x == "Monad") && + } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Monad") && 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!( parse_tr("restrict(Cons a b, Monad m,)"), @@ -179,16 +176,16 @@ fn type_restrictions() { matches!(&restrictions[0], TypeRestriction { constructor, arguments, - } if matches!(constructor, Type::Constructor(_, x) if x == "Cons") && + } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Cons") && arguments.len() == 2 && - matches!(&arguments[0], Type::Variable(_, x) if x == "a") && - matches!(&arguments[1], Type::Variable(_, x) if x == "b")) && + matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") && + matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b")) && matches!(&restrictions[1], TypeRestriction { constructor, arguments, - } if matches!(constructor, Type::Constructor(_, x) if x == "Monad") && + } if matches!(constructor, Type::Constructor(_, x) if x.as_printed() == "Monad") && arguments.len() == 1 && - matches!(&arguments[0], Type::Variable(_, x) if x == "m")))); + matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "m")))); } #[test] @@ -203,46 +200,46 @@ fn field_definition() { assert!(matches!( parse_fd("foo,"), Ok(Some(StructureField{ name, export: ExportClass::Private, field_type: None, .. })) - if name == "foo" + if name.as_printed() == "foo" )); assert!(matches!( parse_fd("foo}"), Ok(Some(StructureField{ name, export: ExportClass::Private, field_type: None, .. })) - if name == "foo" + if name.as_printed() == "foo" )); assert!(matches!( parse_fd("foo: Word8,"), Ok(Some(StructureField{ name, field_type, .. })) - if name == "foo" && + if name.as_printed() == "foo" && 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()))); assert!(matches!( parse_fd("foo: Cons a b,"), Ok(Some(StructureField{ name, field_type, .. })) - if name == "foo" && + if name.as_printed() == "foo" && 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)] - if v1 == "a" && v2 == "b")))); + if v1.as_printed() == "a" && v2.as_printed() == "b")))); assert!(matches!( parse_fd("foo: a -> b,"), Ok(Some(StructureField{ name, field_type, .. })) - if name == "foo" && + if name.as_printed() == "foo" && matches!(&field_type, Some(Type::Function(args, ret)) - if matches!(&args.as_slice(), &[Type::Variable(_, a)] if a == "a") && - matches!(ret.as_ref(), Type::Variable(_, b) if b == "b")))); + if matches!(&args.as_slice(), &[Type::Variable(_, a)] if a.as_printed() == "a") && + matches!(ret.as_ref(), Type::Variable(_, b) if b.as_printed() == "b")))); assert!(matches!( parse_fd("export foo: a -> b,"), 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)) - if matches!(&args.as_slice(), &[Type::Variable(_, a)] if a == "a") && - matches!(ret.as_ref(), Type::Variable(_, b) if b == "b")))); + if matches!(&args.as_slice(), &[Type::Variable(_, a)] if a.as_printed() == "a") && + matches!(ret.as_ref(), Type::Variable(_, b) if b.as_printed() == "b")))); } #[test] @@ -260,65 +257,65 @@ fn structures() { assert!(matches!( parse_st("structure Foo {}"), Ok(StructureDef { name, fields, .. }) - if name == "Foo" && fields.is_empty())); + if name.as_printed() == "Foo" && fields.is_empty())); assert!(matches!( parse_st("structure Foo { bar }"), Ok(StructureDef { name, fields, .. }) - if name == "Foo" && + if name.as_printed() == "Foo" && 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!( parse_st("structure Foo { bar: Word8 }"), Ok(StructureDef { name, fields, .. }) - if name == "Foo" && + if name.as_printed() == "Foo" && 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)) - 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())))); assert!(matches!( parse_st("structure Foo { bar: Word8, goo }"), Ok(StructureDef { name, fields, .. }) - if name == "Foo" && + if name.as_printed() == "Foo" && matches!(fields.as_slice(), &[StructureField { ref name, ref field_type, .. }, StructureField { name: ref name2, field_type: None, .. }] - if name == "bar" && - name2 == "goo" && + if name.as_printed() == "bar" && + name2.as_printed() == "goo" && 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())))); assert!(matches!( parse_st("structure Foo { bar: b c -> a, goo }"), Ok(StructureDef { name, fields, .. }) - if name == "Foo" && + if name.as_printed() == "Foo" && matches!(fields.as_slice(), &[StructureField { ref name, ref field_type, .. }, StructureField { name: ref name2, field_type: None, .. }] - if name == "bar" && - name2 == "goo" && + if name.as_printed() == "bar" && + name2.as_printed() == "goo" && matches!(field_type, Some(Type::Function(args, ret)) if matches!(&args.as_slice(), &[Type::Variable(_, b), Type::Variable(_, c)] - if b == "b" && c == "c") && - matches!(ret.as_ref(), Type::Variable(_, a) if a == "a"))))); + if b.as_printed() == "b" && c.as_printed() == "c") && + matches!(ret.as_ref(), Type::Variable(_, a) if a.as_printed() == "a"))))); assert!(matches!( parse_st("structure Foo { bar: b c -> a, goo, }"), Ok(StructureDef { name, fields, .. }) - if name == "Foo" && + if name.as_printed() == "Foo" && matches!(fields.as_slice(), &[StructureField { ref name, ref field_type, .. }, StructureField { name: ref name2, field_type: None, .. }] - if name == "bar" && - name2 == "goo" && + if name.as_printed() == "bar" && + name2.as_printed() == "goo" && matches!(field_type, Some(Type::Function(args, ret)) if matches!(&args.as_slice(), &[Type::Variable(_, b), Type::Variable(_, c)] - if b == "b" && c == "c") && - matches!(ret.as_ref(), Type::Variable(_, a) if a == "a"))))); + if b.as_printed() == "b" && c.as_printed() == "c") && + matches!(ret.as_ref(), Type::Variable(_, a) if a.as_printed() == "a"))))); } #[test] @@ -339,43 +336,43 @@ fn enum_variant() { assert!(matches!( parse_ev("Cons,"), Ok(Some(EnumerationVariant { name, argument, .. })) - if name == "Cons" && argument.is_none())); + if name.as_printed() == "Cons" && argument.is_none())); assert!(matches!( parse_ev("Cons }"), Ok(Some(EnumerationVariant { name, argument, .. })) - if name == "Cons" && argument.is_none())); + if name.as_printed() == "Cons" && argument.is_none())); assert!(matches!( parse_ev("Cons, }"), Ok(Some(EnumerationVariant { name, argument, .. })) - if name == "Cons" && argument.is_none())); + if name.as_printed() == "Cons" && argument.is_none())); assert!(matches!( parse_ev("Cons(Pair a),"), Ok(Some(EnumerationVariant { name, ref argument, .. })) - if name == "Cons" && + if name.as_printed() == "Cons" && matches!(argument, Some(Type::Application(typef, args)) if matches!(typef.as_ref(), Type::Constructor(_, name) - if name == "Pair") && + if name.as_printed() == "Pair") && matches!(&args.as_slice(), &[Type::Variable(_, argname)] - if argname == "a")))); + if argname.as_printed() == "a")))); assert!(matches!( parse_ev("Cons(Pair a) }"), Ok(Some(EnumerationVariant { name, ref argument, .. })) - if name == "Cons" && + if name.as_printed() == "Cons" && matches!(argument, Some(Type::Application(typef, args)) if matches!(typef.as_ref(), Type::Constructor(_, name) - if name == "Pair") && + if name.as_printed() == "Pair") && matches!(&args.as_slice(), &[Type::Variable(_, argname)] - if argname == "a")))); + if argname.as_printed() == "a")))); assert!(matches!( parse_ev("Cons(a b -> c) }"), Ok(Some(EnumerationVariant { name, ref argument, .. })) - if name == "Cons" && + if name.as_printed() == "Cons" && matches!(argument, Some(Type::Function(args, ret)) if matches!(&args.as_slice(), &[Type::Variable(_, a), Type::Variable(_, b)] - if a == "a" && b == "b") && - matches!(ret.as_ref(), Type::Variable(_, c) if c == "c")))); + if a.as_printed() == "a" && b.as_printed() == "b") && + matches!(ret.as_ref(), Type::Variable(_, c) if c.as_printed() == "c")))); } #[test] @@ -393,25 +390,25 @@ fn enumerations() { assert!(matches!( parse_en("enumeration Empty { }"), Ok(EnumerationDef { name, variants, .. }) - if name == "Empty" && variants.is_empty())); + if name.as_printed() == "Empty" && variants.is_empty())); assert!(matches!( parse_en("enumeration Alternates { A, B }"), Ok(EnumerationDef { name, variants, .. }) - if name == "Alternates" && + if name.as_printed() == "Alternates" && matches!(&variants.as_slice(), &[ EnumerationVariant { name: name1, argument: arg1, ..}, EnumerationVariant { name: name2, argument: arg2, ..}, - ] if name1 == "A" && arg1.is_none() && - name2 == "B" && arg2.is_none()))); + ] if name1.as_printed() == "A" && arg1.is_none() && + name2.as_printed() == "B" && arg2.is_none()))); assert!(matches!( parse_en("enumeration Alternates { A, B, }"), Ok(EnumerationDef { name, variants, .. }) - if name == "Alternates" && + if name.as_printed() == "Alternates" && matches!(&variants.as_slice(), &[ EnumerationVariant { name: name1, argument: arg1, ..}, EnumerationVariant { name: name2, argument: arg2, ..}, - ] if name1 == "A" && arg1.is_none() && - name2 == "B" && arg2.is_none()))); + ] if name1.as_printed() == "A" && arg1.is_none() && + name2.as_printed() == "B" && arg2.is_none()))); } #[test] diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index a990409..13c94fa 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -89,7 +89,7 @@ impl fmt::Display for Token { pub enum Lexer<'a> { Working(LexerState<'a>), Errored(LexerError), - Done(usize), + Done, } struct LexerState<'a> { @@ -120,7 +120,7 @@ impl<'a> Iterator for Lexer<'a> { fn next(&mut self) -> Option { match self { - Lexer::Done(_) => None, + Lexer::Done => None, Lexer::Errored(e) => Some(Err(e.clone())), Lexer::Working(state) => match state.next_token() { Err(e) => { @@ -130,7 +130,7 @@ impl<'a> Iterator for Lexer<'a> { } Ok(None) => { - *self = Lexer::Done(state.stream.offset()); + *self = Lexer::Done; None } diff --git a/src/syntax/universe.rs b/src/syntax/universe.rs new file mode 100644 index 0000000..0e60a05 --- /dev/null +++ b/src/syntax/universe.rs @@ -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, + pub modules: HashMap, +} + +impl Default for Universe { + fn default() -> Self { + Universe { + files: HashMap::new(), + modules: HashMap::new(), + } + } +} + +impl Universe { + pub fn add_file>(&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(()) + } +}