Shifting and naming.
This commit is contained in:
271
src/syntax.rs
271
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<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
228
src/syntax/ast.rs
Normal 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,
|
||||
}
|
||||
@@ -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<PathBuf>, error: LexerError },
|
||||
LexerError {
|
||||
file: ArcIntern<PathBuf>,
|
||||
error: LexerError,
|
||||
},
|
||||
|
||||
#[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}")]
|
||||
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)]
|
||||
|
||||
@@ -32,7 +32,10 @@ impl Span for Location {
|
||||
|
||||
impl Location {
|
||||
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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PathBuf>,
|
||||
@@ -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<P: AsRef<Path>>(
|
||||
file: P,
|
||||
lexer: Lexer<'lexer>
|
||||
) -> Parser<'lexer> {
|
||||
pub fn new<P: AsRef<Path>>(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<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!()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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<Self::Item> {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
48
src/syntax/universe.rs
Normal file
48
src/syntax/universe.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user