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 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
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 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)]
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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
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