wip: working on type resolution.
This commit is contained in:
parent
4e476626ed
commit
40717b6c78
47 changed files with 883 additions and 350 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -14,6 +14,7 @@
|
|||
"defstruct",
|
||||
"defstructfield",
|
||||
"defstructproperty",
|
||||
"defuseproperty",
|
||||
"hashrocket",
|
||||
"indoc",
|
||||
"lexer",
|
||||
|
|
|
@ -3,5 +3,5 @@ pub mod stack;
|
|||
pub mod unique_vec;
|
||||
|
||||
pub use indexed_vec::{IndexedVec, IndexedVecKey};
|
||||
pub use stack::Stack;
|
||||
pub use stack::{Stack, StackError};
|
||||
pub use unique_vec::{UniqueVec, UniqueVecKey};
|
||||
|
|
|
@ -25,16 +25,23 @@ impl<T> Stack<T> {
|
|||
self.0.pop().ok_or(StackError::Underflow)
|
||||
}
|
||||
|
||||
pub fn peek(&self) -> Option<&T> {
|
||||
self.0.last()
|
||||
pub fn peek(&self) -> Result<&T> {
|
||||
self.0.last().ok_or(StackError::Underflow)
|
||||
}
|
||||
|
||||
pub fn peek_mut(&mut self) -> Option<&mut T> {
|
||||
self.0.last_mut()
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
&self.0
|
||||
pub fn iter(&self) -> std::slice::Iter<T> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
pub fn find<F>(&self, predicate: F) -> Option<&T>
|
||||
where
|
||||
F: Fn(&T) -> bool,
|
||||
{
|
||||
self.0.iter().find(|t| predicate(*t))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ pub enum NodeKind {
|
|||
Struct,
|
||||
TypeName,
|
||||
Unary,
|
||||
Use,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -161,4 +162,8 @@ pub enum NodeValue {
|
|||
op: Operator,
|
||||
rhs: Box<Node>,
|
||||
},
|
||||
Use {
|
||||
name: Box<Node>,
|
||||
props: HashMap<String, Node>,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ comment_block = @{ "###" ~ (!"###" ~ ANY)* ~ "###" }
|
|||
comment_line = @{ "#" ~ (!("\r" | "\n") ~ ANY)* }
|
||||
documentation = @{ "```doc" ~ (!"```" ~ ANY)* ~ "```"}
|
||||
|
||||
module = _{ documentation? ~ (defstruct | defprotocol | defimpl) }
|
||||
module = _{ documentation? ~ (defstruct | defprotocol | defimpl | defuse) }
|
||||
|
||||
defstruct = { "struct" ~ typename ~ defstructfields? ~ defstructproperties }
|
||||
defstructfields = { ("(" ~ (defstructfield ~ ("," ~ defstructfield)*)? ~ ")")? }
|
||||
|
@ -23,6 +23,10 @@ defimpl = { "impl" ~ typename ~ defimplproperties }
|
|||
defimplproperties = { ("," ~ defimplproperty)* }
|
||||
defimplproperty = { keyword ~ (typeblock | typeunion | typename) }
|
||||
|
||||
defuse = { "use" ~ typename ~ defuseproperties }
|
||||
defuseproperties = { ("," ~ defuseproperty)* }
|
||||
defuseproperty = { keyword ~ typename }
|
||||
|
||||
typeunion = { typename ~ ("+" ~ typename)+ }
|
||||
typeblock = { "do" ~ typeblockcontents* ~ "end" }
|
||||
typeblockcontents = _{ documentation | alias | defp | defs | def }
|
||||
|
|
|
@ -36,6 +36,7 @@ pub fn visit_pair(pair: Pair<'_, Rule>) -> Result<Node, Error> {
|
|||
Rule::defprotocol => visit_protocol(pair),
|
||||
Rule::defs => visit_defs(pair),
|
||||
Rule::defstruct => visit_struct(pair),
|
||||
Rule::defuse => visit_use(pair),
|
||||
Rule::documentation => Ok(Node::new(
|
||||
NodeKind::Documentation,
|
||||
NodeValue::Documentation(pair.as_str().to_string()),
|
||||
|
@ -225,6 +226,37 @@ fn visit_struct_properties(pairs: Pairs<'_, Rule>) -> Result<HashMap<String, Nod
|
|||
Ok(properties)
|
||||
}
|
||||
|
||||
fn visit_use(pair: Pair<'_, Rule>) -> Result<Node, Error> {
|
||||
assert_is_rule(&pair, Rule::defuse)?;
|
||||
|
||||
let span = pair.as_span();
|
||||
let mut pairs = pair.into_inner();
|
||||
|
||||
let name = visit_typename(pairs.next().unwrap()).map(Box::new)?;
|
||||
let props = visit_use_properties(pairs.next().unwrap().into_inner())?;
|
||||
|
||||
Ok(Node::new(
|
||||
NodeKind::Use,
|
||||
NodeValue::Use { name, props },
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
fn visit_use_properties(pairs: Pairs<'_, Rule>) -> Result<HashMap<String, Node>, Error> {
|
||||
let mut properties = HashMap::new();
|
||||
|
||||
for prop in pairs {
|
||||
assert_is_rule(&prop, Rule::defuseproperty)?;
|
||||
|
||||
let mut inner = prop.into_inner();
|
||||
let key = keyword_to_string(inner.next().unwrap())?;
|
||||
let value = visit_pair(inner.next().unwrap())?;
|
||||
properties.insert(key, value);
|
||||
}
|
||||
|
||||
Ok(properties)
|
||||
}
|
||||
|
||||
fn visit_typeblock(pair: Pair<'_, Rule>) -> Result<Node, Error> {
|
||||
assert_is_rule(&pair, Rule::typeblock)?;
|
||||
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
use super::{Error, Expression, Type, TypeVariable};
|
||||
use crate::grammar::NodeKind;
|
||||
use outrun_common::Stack;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Environment {
|
||||
pub types: Stack<Arc<Type>>,
|
||||
pub functions: Stack<Function>,
|
||||
pub docs: Stack<String>,
|
||||
type_id: u64,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn find_concrete_type<T: ToString>(&self, name: T) -> Result<Arc<Type>, Error> {
|
||||
let name = name.to_string();
|
||||
|
||||
self.types
|
||||
.as_slice()
|
||||
.iter()
|
||||
.find(|t| t.to_string() == name)
|
||||
.map(Clone::clone)
|
||||
.ok_or(Error::UnknownType(name))
|
||||
}
|
||||
|
||||
pub fn reference_type<T: ToString>(&mut self, name: T) -> Result<Arc<Type>, Error> {
|
||||
self.find_concrete_type(name.to_string()).or_else(|_| {
|
||||
let r#type = RefCell::new(TypeVariable::MaybeReference {
|
||||
name: name.to_string(),
|
||||
});
|
||||
let r#type = Arc::new(Type::Variable { r#type });
|
||||
self.types.push(r#type.clone());
|
||||
Ok(r#type)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn unbound_type(&mut self) -> Result<Arc<Type>, Error> {
|
||||
let type_id = self.type_id;
|
||||
self.type_id += 1;
|
||||
let r#type = RefCell::new(TypeVariable::Unbound { id: type_id });
|
||||
let r#type = Arc::new(Type::Variable { r#type });
|
||||
self.types.push(r#type.clone());
|
||||
Ok(r#type)
|
||||
}
|
||||
|
||||
pub fn current_function(&mut self) -> Result<&mut Function, Error> {
|
||||
self.functions.peek_mut().ok_or(Error::NotAFunction)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Environment {
|
||||
fn default() -> Self {
|
||||
let mut result = Environment {
|
||||
types: Stack::new(),
|
||||
functions: Stack::new(),
|
||||
docs: Stack::new(),
|
||||
type_id: 0,
|
||||
};
|
||||
result.types.push(Arc::new(Type::Atom));
|
||||
result.types.push(Arc::new(Type::Boolean));
|
||||
result.types.push(Arc::new(Type::Integer));
|
||||
result.types.push(Arc::new(Type::Float));
|
||||
result.types.push(Arc::new(Type::String));
|
||||
result.types.push(Arc::new(Type::Array));
|
||||
result.types.push(Arc::new(Type::Map));
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Access {
|
||||
Public,
|
||||
Private,
|
||||
Static,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
pub arguments: Vec<String>,
|
||||
pub r#type: Arc<Type>,
|
||||
pub body: Vec<Expression>,
|
||||
pub guards: Vec<Expression>,
|
||||
pub access: Access,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn new<T: Into<Access>>(
|
||||
name: String,
|
||||
arguments: Vec<String>,
|
||||
r#type: Arc<Type>,
|
||||
access: T,
|
||||
) -> Self {
|
||||
Function {
|
||||
name,
|
||||
arguments,
|
||||
r#type,
|
||||
body: Vec::new(),
|
||||
guards: Vec::new(),
|
||||
access: access.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_body_expression(&mut self, expression: Expression) {
|
||||
self.body.push(expression);
|
||||
}
|
||||
|
||||
pub fn push_guard_expression(&mut self, expression: Expression) {
|
||||
self.body.push(expression);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NodeKind> for Access {
|
||||
fn from(node_kind: NodeKind) -> Access {
|
||||
match node_kind {
|
||||
NodeKind::DefPrivate => Access::Private,
|
||||
NodeKind::DefStatic => Access::Static,
|
||||
_ => Access::Public,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use crate::grammar::NodeKind;
|
||||
use outrun_common::StackError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
|
@ -9,4 +10,11 @@ pub enum Error {
|
|||
UnknownType(String),
|
||||
MissingProperty(String),
|
||||
NotAFunction,
|
||||
StackError(StackError),
|
||||
}
|
||||
|
||||
impl From<StackError> for Error {
|
||||
fn from(e: StackError) -> Self {
|
||||
Error::StackError(e)
|
||||
}
|
||||
}
|
||||
|
|
48
outrun-compiler/src/ir/function.rs
Normal file
48
outrun-compiler/src/ir/function.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use super::{Expression, Type};
|
||||
use crate::grammar::NodeKind;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Access {
|
||||
Public,
|
||||
Private,
|
||||
Static,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
pub arguments: Vec<String>,
|
||||
pub r#type: Arc<Type>,
|
||||
pub body: Vec<Expression>,
|
||||
pub guards: Vec<Expression>,
|
||||
pub access: Access,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn new<T: Into<Access>>(
|
||||
name: String,
|
||||
arguments: Vec<String>,
|
||||
r#type: Arc<Type>,
|
||||
access: T,
|
||||
) -> Self {
|
||||
Function {
|
||||
name,
|
||||
arguments,
|
||||
r#type,
|
||||
body: Vec::new(),
|
||||
guards: Vec::new(),
|
||||
access: access.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NodeKind> for Access {
|
||||
fn from(node_kind: NodeKind) -> Access {
|
||||
match node_kind {
|
||||
NodeKind::DefPrivate => Access::Private,
|
||||
NodeKind::DefStatic => Access::Static,
|
||||
_ => Access::Public,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,20 @@
|
|||
mod environment;
|
||||
mod error;
|
||||
mod expression;
|
||||
mod function;
|
||||
mod module;
|
||||
mod notice;
|
||||
mod stages;
|
||||
mod r#type;
|
||||
mod visitor;
|
||||
|
||||
pub use environment::{Environment, Function};
|
||||
pub use error::Error;
|
||||
pub use expression::{Expression, ExpressionValue};
|
||||
pub use function::Function;
|
||||
pub use module::Module;
|
||||
pub use notice::{Notice, NoticeKind, Severity};
|
||||
pub use r#type::{Type, TypeVariable};
|
||||
|
||||
pub use visitor::{visit_node, visit_nodes};
|
||||
|
||||
pub fn improve(mut module: Module) -> Result<Module, Error> {
|
||||
stages::link_type_references(module)
|
||||
}
|
||||
|
|
146
outrun-compiler/src/ir/module.rs
Normal file
146
outrun-compiler/src/ir/module.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
use super::{Error, Function, Notice, NoticeKind, Severity, Type, TypeVariable};
|
||||
use crate::span::Span;
|
||||
use outrun_common::Stack;
|
||||
use std::cell::RefCell;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Module {
|
||||
pub path: Option<PathBuf>,
|
||||
pub types: Stack<Arc<Type>>,
|
||||
pub functions: Stack<Function>,
|
||||
pub docs: Stack<String>,
|
||||
pub notices: Vec<Notice>,
|
||||
type_id: u64,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn new(path: &Path) -> Module {
|
||||
bootstrap(Module {
|
||||
path: Some(path.to_owned()),
|
||||
types: Stack::new(),
|
||||
functions: Stack::new(),
|
||||
docs: Stack::new(),
|
||||
notices: Vec::new(),
|
||||
type_id: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_named_type<T: ToString>(&self, name: T) -> Result<Arc<Type>, Error> {
|
||||
let name = name.to_string();
|
||||
|
||||
self.types
|
||||
.iter()
|
||||
.find(|t| t.to_string() == name)
|
||||
.map(Clone::clone)
|
||||
.ok_or(Error::UnknownType(name))
|
||||
}
|
||||
|
||||
pub fn reference_type<T: ToString>(
|
||||
&mut self,
|
||||
name: T,
|
||||
span: Option<Span>,
|
||||
) -> Result<Arc<Type>, Error> {
|
||||
self.find_named_type(name.to_string()).or_else(|_| {
|
||||
let r#type = RefCell::new(TypeVariable::Reference {
|
||||
name: name.to_string(),
|
||||
});
|
||||
let r#type = Arc::new(Type::Variable { r#type, span });
|
||||
self.types.push(r#type.clone())?;
|
||||
Ok(r#type)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn unbound_type(&mut self) -> Result<Arc<Type>, Error> {
|
||||
let type_id = self.type_id;
|
||||
self.type_id += 1;
|
||||
let r#type = RefCell::new(TypeVariable::Unbound { id: type_id });
|
||||
let r#type = Arc::new(Type::Variable { r#type, span: None });
|
||||
self.types.push(r#type.clone())?;
|
||||
Ok(r#type)
|
||||
}
|
||||
|
||||
pub fn external_type<T: ToString>(&mut self, name: T, span: Span) -> Result<Arc<Type>, Error> {
|
||||
let ty = Arc::new(Type::External {
|
||||
name: name.to_string(),
|
||||
span,
|
||||
});
|
||||
self.types.push(ty.clone())?;
|
||||
Ok(ty)
|
||||
}
|
||||
|
||||
pub fn alias_type<T: ToString>(
|
||||
&mut self,
|
||||
name: T,
|
||||
target: Arc<Type>,
|
||||
span: Span,
|
||||
) -> Result<Arc<Type>, Error> {
|
||||
let ty = Arc::new(Type::Alias {
|
||||
name: name.to_string(),
|
||||
target,
|
||||
span,
|
||||
});
|
||||
self.types.push(ty.clone())?;
|
||||
Ok(ty)
|
||||
}
|
||||
|
||||
pub fn current_function(&mut self) -> Result<&mut Function, Error> {
|
||||
self.functions.peek_mut().ok_or(Error::NotAFunction)
|
||||
}
|
||||
|
||||
pub fn info(&mut self, span: Option<Span>, kind: NoticeKind) {
|
||||
self.notices.push(Notice {
|
||||
severity: Severity::Info,
|
||||
kind,
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn warning(&mut self, span: Option<Span>, kind: NoticeKind) {
|
||||
self.notices.push(Notice {
|
||||
severity: Severity::Warning,
|
||||
kind,
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn error(&mut self, span: Option<Span>, kind: NoticeKind) {
|
||||
self.notices.push(Notice {
|
||||
severity: Severity::Error,
|
||||
kind,
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn search_type<F>(&self, predicate: F) -> Option<Arc<Type>>
|
||||
where
|
||||
F: Fn(Arc<Type>) -> bool,
|
||||
{
|
||||
self.types.find(|t| predicate((*t).clone())).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Module {
|
||||
fn default() -> Self {
|
||||
bootstrap(Module {
|
||||
path: None,
|
||||
types: Stack::new(),
|
||||
functions: Stack::new(),
|
||||
docs: Stack::new(),
|
||||
notices: Vec::new(),
|
||||
type_id: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn bootstrap(mut module: Module) -> Module {
|
||||
module.types.push(Arc::new(Type::Atom)).unwrap();
|
||||
module.types.push(Arc::new(Type::Boolean)).unwrap();
|
||||
module.types.push(Arc::new(Type::Integer)).unwrap();
|
||||
module.types.push(Arc::new(Type::Float)).unwrap();
|
||||
module.types.push(Arc::new(Type::String)).unwrap();
|
||||
module.types.push(Arc::new(Type::Array)).unwrap();
|
||||
module.types.push(Arc::new(Type::Map)).unwrap();
|
||||
module
|
||||
}
|
24
outrun-compiler/src/ir/notice.rs
Normal file
24
outrun-compiler/src/ir/notice.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use crate::span::Span;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Severity {
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Notice {
|
||||
pub severity: Severity,
|
||||
pub kind: NoticeKind,
|
||||
pub span: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NoticeKind {
|
||||
UnexpectedFunctionProperty(String),
|
||||
UnexpectedImplProperty(String),
|
||||
UnexpectedProtocolProperty(String),
|
||||
UnexpectedStructProperty(String),
|
||||
CannotResolveType(String),
|
||||
}
|
24
outrun-compiler/src/ir/stages.rs
Normal file
24
outrun-compiler/src/ir/stages.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use super::{Error, Module, NoticeKind, Type};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn link_type_references(mut module: Module) -> Result<Module, Error> {
|
||||
let types: Vec<Arc<Type>> = module
|
||||
.types
|
||||
.iter()
|
||||
.filter(|t| t.is_reference())
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
for ty in types {
|
||||
let target_name = ty.to_string();
|
||||
let maybe_type = module.search_type(|ty| ty.is_linkable() && ty.to_string() == target_name);
|
||||
|
||||
if let Some(target) = maybe_type {
|
||||
ty.link_to(target);
|
||||
} else {
|
||||
module.error(ty.span(), NoticeKind::CannotResolveType(target_name));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(module)
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use crate::span::Span;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
@ -8,21 +9,27 @@ pub enum Type {
|
|||
documentation: Option<String>,
|
||||
name: String,
|
||||
fields: HashMap<String, Arc<Type>>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
Protocol {
|
||||
documentation: Option<String>,
|
||||
name: String,
|
||||
requires: Option<Arc<Type>>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
Impl {
|
||||
documentation: Option<String>,
|
||||
protocol: Arc<Type>,
|
||||
augments: Arc<Type>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
Union(Vec<Arc<Type>>),
|
||||
Union {
|
||||
types: Vec<Arc<Type>>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
Atom,
|
||||
Boolean,
|
||||
|
@ -37,11 +44,82 @@ pub enum Type {
|
|||
associated_type: Arc<Type>,
|
||||
arguments: Vec<Arc<Type>>,
|
||||
result: Arc<Type>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
Variable {
|
||||
r#type: RefCell<TypeVariable>,
|
||||
span: Option<Span>,
|
||||
},
|
||||
|
||||
External {
|
||||
name: String,
|
||||
span: Span,
|
||||
},
|
||||
Alias {
|
||||
name: String,
|
||||
target: Arc<Type>,
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn is_protocol(&self) -> bool {
|
||||
matches!(self, Type::Protocol { .. })
|
||||
}
|
||||
|
||||
pub fn is_named(&self) -> bool {
|
||||
match self {
|
||||
Type::Variable { r#type, .. } => r#type.borrow().is_named(),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reference(&self) -> bool {
|
||||
match self {
|
||||
Type::Variable { r#type, .. } => r#type.borrow().is_reference(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_linkable(&self) -> bool {
|
||||
match self {
|
||||
Type::Alias { target, .. } => target.is_linkable(),
|
||||
Type::Array => true,
|
||||
Type::Atom => true,
|
||||
Type::Boolean => true,
|
||||
Type::External { .. } => true,
|
||||
Type::Float => true,
|
||||
Type::Integer => true,
|
||||
Type::Map => true,
|
||||
Type::Protocol { .. } => true,
|
||||
Type::Struct { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Type::Struct { span, .. } => Some(span.clone()),
|
||||
Type::Protocol { span, .. } => Some(span.clone()),
|
||||
Type::Impl { span, .. } => Some(span.clone()),
|
||||
Type::Union { span, .. } => Some(span.clone()),
|
||||
Type::Function { span, .. } => Some(span.clone()),
|
||||
Type::Variable { span, .. } => span.clone(),
|
||||
Type::External { span, .. } => Some(span.clone()),
|
||||
Type::Alias { span, .. } => Some(span.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link_to(&self, target: Arc<Type>) {
|
||||
match self {
|
||||
Type::Variable { r#type, .. } => {
|
||||
r#type.replace(TypeVariable::Link { r#type: target });
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Type {
|
||||
|
@ -56,7 +134,7 @@ impl ToString for Type {
|
|||
let augments_name = augments.to_string();
|
||||
format!("Impl<{},{}>", protocol_name, augments_name)
|
||||
}
|
||||
Type::Union(types) => {
|
||||
Type::Union { types, .. } => {
|
||||
let inner = types
|
||||
.iter()
|
||||
.map(|t| t.to_string())
|
||||
|
@ -64,6 +142,8 @@ impl ToString for Type {
|
|||
.join(",");
|
||||
format!("Union<{}>", inner)
|
||||
}
|
||||
Type::External { name, .. } => name.clone(),
|
||||
Type::Alias { name, .. } => name.clone(),
|
||||
Type::Atom => "Outrun.Native.Atom".to_string(),
|
||||
Type::Boolean => "Outrun.Native.Boolean".to_string(),
|
||||
Type::Integer => "Outrun.Native.Integer".to_string(),
|
||||
|
@ -86,7 +166,7 @@ impl ToString for Type {
|
|||
.join(",");
|
||||
format!("Function<{}, [{}] -> {}>", related, arguments, result)
|
||||
}
|
||||
Type::Variable { r#type } => r#type.borrow().to_string(),
|
||||
Type::Variable { r#type, .. } => r#type.borrow().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,15 +174,29 @@ impl ToString for Type {
|
|||
#[derive(Debug, Clone)]
|
||||
pub enum TypeVariable {
|
||||
Unbound { id: u64 },
|
||||
MaybeReference { name: String },
|
||||
Reference { name: String },
|
||||
Link { r#type: Arc<Type> },
|
||||
}
|
||||
|
||||
impl TypeVariable {
|
||||
pub fn is_named(&self) -> bool {
|
||||
match self {
|
||||
TypeVariable::Reference { .. } => true,
|
||||
TypeVariable::Link { r#type } => r#type.is_named(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reference(&self) -> bool {
|
||||
matches!(self, TypeVariable::Reference { .. })
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for TypeVariable {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
TypeVariable::Unbound { id } => format!("T{}", id),
|
||||
TypeVariable::MaybeReference { name } => name.to_string(),
|
||||
TypeVariable::Reference { name } => name.to_string(),
|
||||
TypeVariable::Link { r#type } => r#type.to_string(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,54 +1,55 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use super::{Environment, Error, Expression, ExpressionValue, Function, Type};
|
||||
use super::{Error, Expression, ExpressionValue, Function, Module, NoticeKind, Type};
|
||||
use crate::grammar::{Node, NodeKind, NodeValue, Operator};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn visit_nodes(mut env: Environment, mut nodes: Vec<Node>) -> Result<Environment, Error> {
|
||||
pub fn visit_nodes(mut module: Module, mut nodes: Vec<Node>) -> Result<Module, Error> {
|
||||
while let Some(node) = nodes.pop() {
|
||||
env = visit_node(env, node)?;
|
||||
module = visit_node(module, node)?;
|
||||
}
|
||||
|
||||
Ok(env)
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
pub fn visit_node(env: Environment, node: Node) -> Result<Environment, Error> {
|
||||
consume_documentation(env.clone(), node.clone())
|
||||
.or_else(|_| consume_expression(env.clone(), node.clone()))
|
||||
.or_else(|_| consume_statement(env, node))
|
||||
pub fn visit_node(module: Module, node: Node) -> Result<Module, Error> {
|
||||
consume_documentation(module.clone(), node.clone())
|
||||
.or_else(|_| consume_expression(module.clone(), node.clone()))
|
||||
.or_else(|_| consume_statement(module, node))
|
||||
}
|
||||
|
||||
fn consume_documentation(mut env: Environment, node: Node) -> Result<Environment, Error> {
|
||||
fn consume_documentation(mut module: Module, node: Node) -> Result<Module, Error> {
|
||||
assert_is_kind(&node, NodeKind::Documentation)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::Documentation(docs) => {
|
||||
println!("docs push: {:?}", docs);
|
||||
env.docs.push(docs).unwrap();
|
||||
Ok(env)
|
||||
module.docs.push(docs)?;
|
||||
Ok(module)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_expression(mut env: Environment, node: Node) -> Result<Environment, Error> {
|
||||
let (new_env, expression) = visit_expression(env, node)?;
|
||||
env = new_env;
|
||||
env.current_function()
|
||||
.map(|fun| fun.push_body_expression(expression))?;
|
||||
fn consume_expression(module: Module, node: Node) -> Result<Module, Error> {
|
||||
let (mut module, expression) = visit_expression(module, node)?;
|
||||
module
|
||||
.current_function()
|
||||
.map(|fun| fun.body.push(expression))?;
|
||||
|
||||
Ok(env)
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
fn consume_statement(env: Environment, node: Node) -> Result<Environment, Error> {
|
||||
fn consume_statement(module: Module, node: Node) -> Result<Module, Error> {
|
||||
match &node.kind {
|
||||
NodeKind::Block => visit_block(env, node),
|
||||
NodeKind::DefPrivate => visit_function_statement(env, node),
|
||||
NodeKind::DefPublic => visit_function_statement(env, node),
|
||||
NodeKind::DefStatic => visit_function_statement(env, node),
|
||||
NodeKind::Impl => consume_type_statement(env, node),
|
||||
NodeKind::Protocol => consume_type_statement(env, node),
|
||||
NodeKind::Struct => consume_type_statement(env, node),
|
||||
NodeKind::Block => visit_block(module, node),
|
||||
NodeKind::DefPrivate => visit_function_statement(module, node),
|
||||
NodeKind::DefPublic => visit_function_statement(module, node),
|
||||
NodeKind::DefStatic => visit_function_statement(module, node),
|
||||
NodeKind::Impl => consume_type_statement(module, node),
|
||||
NodeKind::Protocol => consume_type_statement(module, node),
|
||||
NodeKind::Struct => consume_type_statement(module, node),
|
||||
NodeKind::Use => visit_use_statement(module, node),
|
||||
other => Err(Error::ExpectedReceived {
|
||||
expected: vec![
|
||||
NodeKind::Block,
|
||||
|
@ -65,27 +66,54 @@ fn consume_statement(env: Environment, node: Node) -> Result<Environment, Error>
|
|||
}
|
||||
}
|
||||
|
||||
fn consume_type_statement(env: Environment, node: Node) -> Result<Environment, Error> {
|
||||
let (env, _) = visit_type_statement(env, node)?;
|
||||
Ok(env)
|
||||
fn consume_type_statement(module: Module, node: Node) -> Result<Module, Error> {
|
||||
let (module, _) = visit_type_statement(module, node)?;
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
fn eval_type_union(env: Environment, node: Node) -> Result<(Environment, Arc<Type>), Error> {
|
||||
let (mut env, types) = eval_type_union_collect(env, &node)?;
|
||||
fn eval_type_union(mut module: Module, node: Node) -> Result<(Module, Arc<Type>), Error> {
|
||||
match node.value {
|
||||
NodeValue::TypeName(name) => {
|
||||
if name == "Self" {
|
||||
self_type(module)
|
||||
} else if name.starts_with("Self.") {
|
||||
let (mut module, parent) = self_type(module)?;
|
||||
if parent.is_named() {
|
||||
let name = parent.to_string();
|
||||
let ty = module.reference_type(name, Some(node.span))?;
|
||||
Ok((module, ty))
|
||||
} else {
|
||||
let inner = module.unbound_type()?;
|
||||
let ty = module.alias_type(name, inner, node.span)?;
|
||||
Ok((module, ty))
|
||||
}
|
||||
} else {
|
||||
let ty = module.reference_type(name, Some(node.span))?;
|
||||
Ok((module, ty))
|
||||
}
|
||||
}
|
||||
NodeValue::Infix { .. } => {
|
||||
let (mut module, types) = eval_type_union_collect(module, &node)?;
|
||||
|
||||
if types.len() == 1 {
|
||||
Ok((env, types[0].clone()))
|
||||
} else {
|
||||
let ty = Arc::new(Type::Union(types));
|
||||
env.types.push(ty.clone()).unwrap();
|
||||
Ok((env, ty))
|
||||
if types.len() == 1 {
|
||||
Ok((module, types[0].clone()))
|
||||
} else {
|
||||
let ty = Arc::new(Type::Union {
|
||||
types,
|
||||
span: node.span,
|
||||
});
|
||||
module.types.push(ty.clone())?;
|
||||
Ok((module, ty))
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_type_union_collect(
|
||||
mut env: Environment,
|
||||
mut module: Module,
|
||||
node: &Node,
|
||||
) -> Result<(Environment, Vec<Arc<Type>>), Error> {
|
||||
) -> Result<(Module, Vec<Arc<Type>>), Error> {
|
||||
if !matches!(node.kind, NodeKind::Infix | NodeKind::TypeName) {
|
||||
return Err(Error::ExpectedReceived {
|
||||
expected: vec![NodeKind::Infix, NodeKind::TypeName],
|
||||
|
@ -99,41 +127,41 @@ fn eval_type_union_collect(
|
|||
op: Operator::Plus,
|
||||
ref rhs,
|
||||
} => {
|
||||
let (new_env, mut lhs) = eval_type_union_collect(env, lhs)?;
|
||||
let (new_env, mut rhs) = eval_type_union_collect(new_env, rhs)?;
|
||||
let (module, mut lhs) = eval_type_union_collect(module, lhs)?;
|
||||
let (module, mut rhs) = eval_type_union_collect(module, rhs)?;
|
||||
lhs.append(&mut rhs);
|
||||
Ok((new_env, lhs))
|
||||
Ok((module, lhs))
|
||||
}
|
||||
NodeValue::TypeName(name) => {
|
||||
let ty = env.reference_type(name)?;
|
||||
Ok((env, vec![ty]))
|
||||
let ty = module.reference_type(name, Some(node.span))?;
|
||||
Ok((module, vec![ty]))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_block(env: Environment, node: Node) -> Result<Environment, Error> {
|
||||
fn visit_block(module: Module, node: Node) -> Result<Module, Error> {
|
||||
assert_is_kind(&node, NodeKind::Block)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::Block(block) => visit_nodes(env, block),
|
||||
NodeValue::Block(block) => visit_nodes(module, block),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expression(env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_expression(module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
match &node.kind {
|
||||
NodeKind::Array => visit_array(env, node),
|
||||
NodeKind::Atom => visit_atom(env, node),
|
||||
NodeKind::Boolean => visit_boolean(env, node),
|
||||
NodeKind::Constructor => visit_constructor(env, node),
|
||||
NodeKind::Float => visit_float(env, node),
|
||||
NodeKind::GetLocal => visit_get_local(env, node),
|
||||
NodeKind::Integer => visit_integer(env, node),
|
||||
NodeKind::Let => visit_let_expression(env, node),
|
||||
NodeKind::Map => visit_map(env, node),
|
||||
NodeKind::RemoteCall => visit_remote_call(env, node),
|
||||
NodeKind::String => visit_string(env, node),
|
||||
NodeKind::Array => visit_array(module, node),
|
||||
NodeKind::Atom => visit_atom(module, node),
|
||||
NodeKind::Boolean => visit_boolean(module, node),
|
||||
NodeKind::Constructor => visit_constructor(module, node),
|
||||
NodeKind::Float => visit_float(module, node),
|
||||
NodeKind::GetLocal => visit_get_local(module, node),
|
||||
NodeKind::Integer => visit_integer(module, node),
|
||||
NodeKind::Let => visit_let_expression(module, node),
|
||||
NodeKind::Map => visit_map(module, node),
|
||||
NodeKind::RemoteCall => visit_remote_call(module, node),
|
||||
NodeKind::String => visit_string(module, node),
|
||||
other => Err(Error::ExpectedReceived {
|
||||
expected: vec![
|
||||
NodeKind::Array,
|
||||
|
@ -153,23 +181,23 @@ fn visit_expression(env: Environment, node: Node) -> Result<(Environment, Expres
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_array(mut env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_array(mut module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::Array)?;
|
||||
|
||||
let r#type = env.find_concrete_type("Outrun.Native.Array")?;
|
||||
let r#type = module.find_named_type("Outrun.Native.Array")?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::Array(nodes) => {
|
||||
let mut array = Vec::new();
|
||||
for node in nodes {
|
||||
let (new_env, expr) = visit_expression(env, node)?;
|
||||
env = new_env;
|
||||
let (new_module, expr) = visit_expression(module, node)?;
|
||||
module = new_module;
|
||||
array.push(expr);
|
||||
}
|
||||
let value = ExpressionValue::Array(array);
|
||||
let span = node.span;
|
||||
Ok((
|
||||
env,
|
||||
module,
|
||||
Expression {
|
||||
r#type,
|
||||
span,
|
||||
|
@ -181,14 +209,14 @@ fn visit_array(mut env: Environment, node: Node) -> Result<(Environment, Express
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_atom(env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_atom(module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::Atom)?;
|
||||
|
||||
let r#type = env.find_concrete_type("Outrun.Native.Atom")?;
|
||||
let r#type = module.find_named_type("Outrun.Native.Atom")?;
|
||||
let span = node.span;
|
||||
let value = node.value.into();
|
||||
Ok((
|
||||
env,
|
||||
module,
|
||||
Expression {
|
||||
r#type,
|
||||
span,
|
||||
|
@ -197,14 +225,14 @@ fn visit_atom(env: Environment, node: Node) -> Result<(Environment, Expression),
|
|||
))
|
||||
}
|
||||
|
||||
fn visit_boolean(env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_boolean(module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::Boolean)?;
|
||||
|
||||
let r#type = env.find_concrete_type("Outrun.Native.Boolean")?;
|
||||
let r#type = module.find_named_type("Outrun.Native.Boolean")?;
|
||||
let span = node.span;
|
||||
let value = node.value.into();
|
||||
Ok((
|
||||
env,
|
||||
module,
|
||||
Expression {
|
||||
r#type,
|
||||
span,
|
||||
|
@ -213,26 +241,26 @@ fn visit_boolean(env: Environment, node: Node) -> Result<(Environment, Expressio
|
|||
))
|
||||
}
|
||||
|
||||
fn visit_constructor(mut env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_constructor(mut module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::Constructor)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::Constructor { name, fields } => {
|
||||
let name = typename_to_string(&name)?;
|
||||
let r#type = env.reference_type(name)?;
|
||||
let r#type = module.reference_type(name, Some(node.span))?;
|
||||
let span = node.span;
|
||||
let mut value = Vec::new();
|
||||
|
||||
for field in fields.values() {
|
||||
let (new_env, expression) = visit_expression(env, field.clone())?;
|
||||
env = new_env;
|
||||
let (new_module, expression) = visit_expression(module, field.clone())?;
|
||||
module = new_module;
|
||||
value.push(expression);
|
||||
}
|
||||
|
||||
let value = ExpressionValue::Array(value);
|
||||
|
||||
Ok((
|
||||
env,
|
||||
module,
|
||||
Expression {
|
||||
r#type,
|
||||
span,
|
||||
|
@ -244,17 +272,17 @@ fn visit_constructor(mut env: Environment, node: Node) -> Result<(Environment, E
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_float(env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_float(module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::Float)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::Float(value) => {
|
||||
let r#type = env.find_concrete_type("Outrun.Native.Float")?;
|
||||
let r#type = module.find_named_type("Outrun.Native.Float")?;
|
||||
let span = node.span;
|
||||
let value = ExpressionValue::Float(value);
|
||||
|
||||
Ok((
|
||||
env,
|
||||
module,
|
||||
Expression {
|
||||
r#type,
|
||||
span,
|
||||
|
@ -266,7 +294,7 @@ fn visit_float(env: Environment, node: Node) -> Result<(Environment, Expression)
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_function_statement(mut env: Environment, node: Node) -> Result<Environment, Error> {
|
||||
fn visit_function_statement(mut module: Module, node: Node) -> Result<Module, Error> {
|
||||
if !matches!(
|
||||
node.kind,
|
||||
NodeKind::DefPublic | NodeKind::DefPrivate | NodeKind::DefStatic
|
||||
|
@ -288,52 +316,54 @@ fn visit_function_statement(mut env: Environment, node: Node) -> Result<Environm
|
|||
props,
|
||||
result,
|
||||
} => {
|
||||
let associated_type = env.types.peek().unwrap().clone();
|
||||
let associated_type = module.types.peek().cloned()?;
|
||||
let span = node.span;
|
||||
|
||||
let mut argument_names = Vec::new();
|
||||
let mut argument_types = Vec::new();
|
||||
|
||||
for (key, value) in arguments {
|
||||
argument_names.push(key);
|
||||
let (new_env, ty) = eval_type_union(env, value)?;
|
||||
env = new_env;
|
||||
|
||||
let (new_module, ty) = eval_type_union(module, value)?;
|
||||
module = new_module;
|
||||
argument_types.push(ty);
|
||||
}
|
||||
|
||||
let result_type = match result {
|
||||
Some(node) => {
|
||||
let (new_env, result) = eval_type_union(env, *node)?;
|
||||
env = new_env;
|
||||
let (new_module, result) = eval_type_union(module, *node)?;
|
||||
module = new_module;
|
||||
result
|
||||
}
|
||||
None => env.unbound_type()?,
|
||||
None => module.unbound_type()?,
|
||||
};
|
||||
|
||||
let documentation = env.docs.pop().ok();
|
||||
let documentation = module.docs.pop().ok();
|
||||
println!("docs pop: {:?}", documentation);
|
||||
|
||||
let function_type = Arc::new(Type::Function {
|
||||
associated_type,
|
||||
arguments: argument_types,
|
||||
result: result_type,
|
||||
documentation,
|
||||
span,
|
||||
});
|
||||
env.types.push(function_type.clone()).unwrap();
|
||||
module.types.push(function_type.clone())?;
|
||||
|
||||
let function = Function::new(name, argument_names, function_type, node.kind);
|
||||
env.functions.push(function.clone()).unwrap();
|
||||
module.functions.push(function.clone())?;
|
||||
|
||||
if let Some(guard) = props.get("when") {
|
||||
let (new_env, expr) = visit_expression(env, guard.clone())?;
|
||||
env = new_env;
|
||||
env.current_function()
|
||||
.map(|fun| fun.push_guard_expression(expr))?;
|
||||
let (new_module, expr) = visit_expression(module, guard.clone())?;
|
||||
module = new_module;
|
||||
module.current_function().map(|fun| fun.guards.push(expr))?;
|
||||
}
|
||||
|
||||
if let Some(body) = props.get("as") {
|
||||
let (new_env, expr) = visit_expression(env, body.clone())?;
|
||||
env = new_env;
|
||||
env.current_function()
|
||||
.map(|fun| fun.push_body_expression(expr))?
|
||||
let (new_module, expr) = visit_expression(module, body.clone())?;
|
||||
module = new_module;
|
||||
module.current_function().map(|fun| fun.body.push(expr))?
|
||||
}
|
||||
|
||||
for key in props.keys() {
|
||||
|
@ -344,23 +374,25 @@ fn visit_function_statement(mut env: Environment, node: Node) -> Result<Environm
|
|||
continue;
|
||||
}
|
||||
|
||||
// We should really emit a warning...
|
||||
eprintln!("Unexpected key {}", key);
|
||||
module.warning(
|
||||
Some(span),
|
||||
NoticeKind::UnexpectedFunctionProperty(key.to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(env)
|
||||
Ok(module)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_get_local(mut env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_get_local(mut module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::GetLocal)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::GetLocal(local) => {
|
||||
let span = node.span;
|
||||
let r#type = env.unbound_type()?;
|
||||
let r#type = module.unbound_type()?;
|
||||
let value = ExpressionValue::GetLocal(local);
|
||||
let expr = Expression {
|
||||
r#type,
|
||||
|
@ -368,36 +400,41 @@ fn visit_get_local(mut env: Environment, node: Node) -> Result<(Environment, Exp
|
|||
value,
|
||||
};
|
||||
|
||||
Ok((env, expr))
|
||||
Ok((module, expr))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_impl(mut env: Environment, node: Node) -> Result<(Environment, Arc<Type>), Error> {
|
||||
fn visit_impl(mut module: Module, node: Node) -> Result<(Module, Arc<Type>), Error> {
|
||||
assert_is_kind(&node, NodeKind::Impl)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::Impl { name, props } => {
|
||||
let protocol = typename_to_string(&name).and_then(|name| env.reference_type(name))?;
|
||||
let protocol = typename_to_string(&name)
|
||||
.and_then(|name| module.reference_type(name, Some(node.span)))?;
|
||||
let augments = props
|
||||
.get("for")
|
||||
.ok_or(Error::MissingProperty("for".to_string()))
|
||||
.and_then(|node| typename_to_string(&node))
|
||||
.and_then(|name| env.reference_type(name))?;
|
||||
.and_then(|node| {
|
||||
let name = typename_to_string(&node)?;
|
||||
let ty = module.reference_type(name, Some(node.span))?;
|
||||
Ok(ty)
|
||||
})?;
|
||||
|
||||
let documentation = env.docs.pop().ok();
|
||||
let documentation = module.docs.pop().ok();
|
||||
|
||||
let ty = Arc::new(Type::Impl {
|
||||
protocol,
|
||||
augments,
|
||||
documentation,
|
||||
span: node.span,
|
||||
});
|
||||
env.types.push(ty.clone()).unwrap();
|
||||
module.types.push(ty.clone())?;
|
||||
|
||||
if let Some(node) = props.get("as") {
|
||||
let new_env = consume_statement(env, node.clone())?;
|
||||
env = new_env;
|
||||
let new_module = consume_statement(module, node.clone())?;
|
||||
module = new_module;
|
||||
}
|
||||
|
||||
for key in props.keys() {
|
||||
|
@ -408,27 +445,29 @@ fn visit_impl(mut env: Environment, node: Node) -> Result<(Environment, Arc<Type
|
|||
continue;
|
||||
}
|
||||
|
||||
// We should really emit a warning...
|
||||
eprintln!("Unexpected key {}", key);
|
||||
module.warning(
|
||||
Some(node.span),
|
||||
NoticeKind::UnexpectedImplProperty(key.to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
Ok((env, ty))
|
||||
Ok((module, ty))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_integer(env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_integer(module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::Integer)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::Integer(value) => {
|
||||
let r#type = env.find_concrete_type("Outrun.Native.Integer")?;
|
||||
let r#type = module.find_named_type("Outrun.Native.Integer")?;
|
||||
let span = node.span;
|
||||
let value = ExpressionValue::Integer(value);
|
||||
|
||||
Ok((
|
||||
env,
|
||||
module,
|
||||
Expression {
|
||||
r#type,
|
||||
span,
|
||||
|
@ -440,10 +479,7 @@ fn visit_integer(env: Environment, node: Node) -> Result<(Environment, Expressio
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_let_expression(
|
||||
mut env: Environment,
|
||||
node: Node,
|
||||
) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_let_expression(mut module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::Let)?;
|
||||
|
||||
match node.value {
|
||||
|
@ -456,14 +492,14 @@ fn visit_let_expression(
|
|||
let expression_type;
|
||||
|
||||
if let Some(ty) = r#type {
|
||||
let (new_env, ty) = eval_type_union(env, *ty)?;
|
||||
env = new_env;
|
||||
let (new_module, ty) = eval_type_union(module, *ty)?;
|
||||
module = new_module;
|
||||
expression_type = ty;
|
||||
} else {
|
||||
expression_type = env.unbound_type()?;
|
||||
expression_type = module.unbound_type()?;
|
||||
}
|
||||
|
||||
let (new_env, value) = visit_expression(env, *value)?;
|
||||
let (new_module, value) = visit_expression(module, *value)?;
|
||||
|
||||
let value = ExpressionValue::Let {
|
||||
name,
|
||||
|
@ -476,32 +512,32 @@ fn visit_let_expression(
|
|||
value,
|
||||
};
|
||||
|
||||
Ok((new_env, expr))
|
||||
Ok((new_module, expr))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_map(mut env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_map(mut module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::Map)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::Map(map) => {
|
||||
let r#type = env.find_concrete_type("Outrun.Native.Map")?;
|
||||
let r#type = module.find_named_type("Outrun.Native.Map")?;
|
||||
let span = node.span;
|
||||
let mut values = Vec::new();
|
||||
|
||||
for (key, value) in map {
|
||||
let (new_env, key) = visit_expression(env, key)?;
|
||||
let (new_env, value) = visit_expression(new_env, value)?;
|
||||
env = new_env;
|
||||
let (new_module, key) = visit_expression(module, key)?;
|
||||
let (new_module, value) = visit_expression(new_module, value)?;
|
||||
module = new_module;
|
||||
values.push((key, value));
|
||||
}
|
||||
|
||||
let value = ExpressionValue::Map(values);
|
||||
|
||||
Ok((
|
||||
env,
|
||||
module,
|
||||
Expression {
|
||||
r#type,
|
||||
span,
|
||||
|
@ -513,7 +549,7 @@ fn visit_map(mut env: Environment, node: Node) -> Result<(Environment, Expressio
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_protocol(mut env: Environment, node: Node) -> Result<(Environment, Arc<Type>), Error> {
|
||||
fn visit_protocol(mut module: Module, node: Node) -> Result<(Module, Arc<Type>), Error> {
|
||||
assert_is_kind(&node, NodeKind::Protocol)?;
|
||||
|
||||
match node.value {
|
||||
|
@ -523,21 +559,23 @@ fn visit_protocol(mut env: Environment, node: Node) -> Result<(Environment, Arc<
|
|||
let mut requires = None;
|
||||
|
||||
if let Some(union) = props.get("requires") {
|
||||
let (new_env, ty) = eval_type_union(env, union.clone())?;
|
||||
env = new_env;
|
||||
let (new_module, ty) = eval_type_union(module, union.clone())?;
|
||||
module = new_module;
|
||||
requires = Some(ty);
|
||||
}
|
||||
|
||||
let documentation = env.docs.pop().ok();
|
||||
let documentation = module.docs.pop().ok();
|
||||
println!("docs pop: {:?}", documentation);
|
||||
|
||||
let ty = Arc::new(Type::Protocol {
|
||||
name,
|
||||
requires,
|
||||
documentation,
|
||||
span: node.span,
|
||||
});
|
||||
|
||||
if let Some(node) = props.get("as") {
|
||||
env = consume_statement(env, node.clone())?;
|
||||
module = consume_statement(module, node.clone())?;
|
||||
}
|
||||
|
||||
for key in props.keys() {
|
||||
|
@ -548,17 +586,19 @@ fn visit_protocol(mut env: Environment, node: Node) -> Result<(Environment, Arc<
|
|||
continue;
|
||||
}
|
||||
|
||||
// We should really emit a warning...
|
||||
eprintln!("Unexpected key {}", key);
|
||||
module.warning(
|
||||
Some(node.span),
|
||||
NoticeKind::UnexpectedProtocolProperty(key.to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
Ok((env, ty))
|
||||
Ok((module, ty))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_remote_call(mut env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_remote_call(mut module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::RemoteCall)?;
|
||||
|
||||
match node.value {
|
||||
|
@ -568,15 +608,15 @@ fn visit_remote_call(mut env: Environment, node: Node) -> Result<(Environment, E
|
|||
arguments,
|
||||
} => {
|
||||
let ty = typename_to_string(&associated_type)?;
|
||||
let ty = env.reference_type(ty)?;
|
||||
let ty = module.reference_type(ty, Some(node.span))?;
|
||||
|
||||
let span = node.span;
|
||||
|
||||
let mut args = Vec::new();
|
||||
for argument in arguments {
|
||||
let (new_env, arg) = visit_expression(env, argument)?;
|
||||
let (new_module, arg) = visit_expression(module, argument)?;
|
||||
args.push(arg);
|
||||
env = new_env;
|
||||
module = new_module;
|
||||
}
|
||||
|
||||
let value = ExpressionValue::RemoteCall {
|
||||
|
@ -585,10 +625,10 @@ fn visit_remote_call(mut env: Environment, node: Node) -> Result<(Environment, E
|
|||
arguments: args,
|
||||
};
|
||||
|
||||
let r#type = env.unbound_type()?;
|
||||
let r#type = module.unbound_type()?;
|
||||
|
||||
Ok((
|
||||
env,
|
||||
module,
|
||||
Expression {
|
||||
r#type,
|
||||
span,
|
||||
|
@ -600,17 +640,17 @@ fn visit_remote_call(mut env: Environment, node: Node) -> Result<(Environment, E
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_string(env: Environment, node: Node) -> Result<(Environment, Expression), Error> {
|
||||
fn visit_string(module: Module, node: Node) -> Result<(Module, Expression), Error> {
|
||||
assert_is_kind(&node, NodeKind::String)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::String(ref string) => {
|
||||
let r#type = env.find_concrete_type("Outrun.Native.String")?;
|
||||
let r#type = module.find_named_type("Outrun.Native.String")?;
|
||||
let span = node.span;
|
||||
let value = ExpressionValue::String(string.to_string());
|
||||
|
||||
Ok((
|
||||
env,
|
||||
module,
|
||||
Expression {
|
||||
r#type,
|
||||
span,
|
||||
|
@ -622,7 +662,7 @@ fn visit_string(env: Environment, node: Node) -> Result<(Environment, Expression
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_struct(mut env: Environment, node: Node) -> Result<(Environment, Arc<Type>), Error> {
|
||||
fn visit_struct(mut module: Module, node: Node) -> Result<(Module, Arc<Type>), Error> {
|
||||
assert_is_kind(&node, NodeKind::Struct)?;
|
||||
|
||||
match node.value {
|
||||
|
@ -635,23 +675,25 @@ fn visit_struct(mut env: Environment, node: Node) -> Result<(Environment, Arc<Ty
|
|||
let mut new_fields = HashMap::new();
|
||||
|
||||
for (key, value) in fields.iter() {
|
||||
let (new_env, value) = eval_type_union(env, value.clone())?;
|
||||
env = new_env;
|
||||
let (new_module, value) = eval_type_union(module, value.clone())?;
|
||||
module = new_module;
|
||||
new_fields.insert(key.clone(), value);
|
||||
}
|
||||
|
||||
let documentation = env.docs.pop().ok();
|
||||
let documentation = module.docs.pop().ok();
|
||||
println!("docs pop: {:?}", documentation);
|
||||
|
||||
let r#type = Arc::new(Type::Struct {
|
||||
name,
|
||||
fields: new_fields,
|
||||
documentation,
|
||||
span: node.span,
|
||||
});
|
||||
|
||||
env.types.push(r#type.clone()).unwrap();
|
||||
module.types.push(r#type.clone())?;
|
||||
|
||||
if let Some(node) = props.get("as") {
|
||||
env = consume_statement(env, node.clone())?;
|
||||
module = consume_statement(module, node.clone())?;
|
||||
}
|
||||
|
||||
for key in props.keys() {
|
||||
|
@ -662,21 +704,23 @@ fn visit_struct(mut env: Environment, node: Node) -> Result<(Environment, Arc<Ty
|
|||
continue;
|
||||
}
|
||||
|
||||
// We should really emit a warning...
|
||||
eprintln!("Unexpected key {}", key);
|
||||
module.warning(
|
||||
Some(node.span),
|
||||
NoticeKind::UnexpectedStructProperty(key.to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
Ok((env, r#type))
|
||||
Ok((module, r#type))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_type_statement(env: Environment, node: Node) -> Result<(Environment, Arc<Type>), Error> {
|
||||
fn visit_type_statement(module: Module, node: Node) -> Result<(Module, Arc<Type>), Error> {
|
||||
match &node.kind {
|
||||
NodeKind::Struct => visit_struct(env, node),
|
||||
NodeKind::Protocol => visit_protocol(env, node),
|
||||
NodeKind::Impl => visit_impl(env, node),
|
||||
NodeKind::Struct => visit_struct(module, node),
|
||||
NodeKind::Protocol => visit_protocol(module, node),
|
||||
NodeKind::Impl => visit_impl(module, node),
|
||||
other => Err(Error::ExpectedReceived {
|
||||
expected: vec![NodeKind::Struct, NodeKind::Protocol, NodeKind::Impl],
|
||||
received: other.clone(),
|
||||
|
@ -684,6 +728,35 @@ fn visit_type_statement(env: Environment, node: Node) -> Result<(Environment, Ar
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_use_statement(mut module: Module, node: Node) -> Result<Module, Error> {
|
||||
assert_is_kind(&node, NodeKind::Use)?;
|
||||
|
||||
match node.value {
|
||||
NodeValue::Use { name, props } => {
|
||||
let name = typename_to_string(&name)?;
|
||||
|
||||
let ty = module.external_type(name, node.span)?;
|
||||
|
||||
for key in props.keys() {
|
||||
if key == "as" {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We should really emit a warning...
|
||||
eprintln!("Unexpected key {}", key);
|
||||
}
|
||||
|
||||
if let Some(alias) = props.get("as") {
|
||||
let name = typename_to_string(alias)?;
|
||||
module.alias_type(name, ty, alias.span)?;
|
||||
}
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn typename_to_string(node: &Node) -> Result<String, Error> {
|
||||
assert_is_kind(&node, NodeKind::TypeName)?;
|
||||
|
||||
|
@ -703,3 +776,14 @@ fn assert_is_kind(node: &Node, kind: NodeKind) -> Result<(), Error> {
|
|||
received: node.kind.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn self_type(mut module: Module) -> Result<(Module, Arc<Type>), Error> {
|
||||
let parent = module.types.peek().cloned()?;
|
||||
|
||||
if parent.is_protocol() {
|
||||
let ty = module.unbound_type()?;
|
||||
Ok((module, ty))
|
||||
} else {
|
||||
Ok((module, parent))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,13 @@ pub use error::Error;
|
|||
|
||||
pub fn compile_file(input: &str) -> Result<(), Error> {
|
||||
let untyped_ast = grammar::parse_file(input)?;
|
||||
let env = ir::Environment::default();
|
||||
let env = ir::visit_nodes(env, untyped_ast)?;
|
||||
// println!("AST: {:#?}", untyped_ast);
|
||||
|
||||
println!("{:#?}", env);
|
||||
let module = ir::Module::default();
|
||||
let module = ir::visit_nodes(module, untyped_ast)?;
|
||||
let module = ir::improve(module)?;
|
||||
|
||||
println!("IR {:#?}", module);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -37,6 +40,10 @@ mod tests {
|
|||
#[test]
|
||||
fn test_it_works() {
|
||||
let input = r#"
|
||||
use AbsoluteValue
|
||||
use Option
|
||||
use Outrun.NIF, as: NIF
|
||||
|
||||
```doc
|
||||
# Outrun.Core.Integer
|
||||
|
||||
|
@ -44,18 +51,18 @@ mod tests {
|
|||
|
||||
Has no constructor as it is created internally in the virtual machine by literals.
|
||||
```
|
||||
struct Outrun.Core.Integer(fruit: Wat), as: do
|
||||
struct Outrun.Core.Integer, as: do
|
||||
```doc
|
||||
Computes the absolute value of `self`.
|
||||
|
||||
Returns none on overflow.
|
||||
```
|
||||
def abs(self: Self): Option, as: Outrun.NIF.integer_abs(self)
|
||||
def abs(self: Self): Option, as: NIF.integer_abs(self)
|
||||
|
||||
end
|
||||
|
||||
impl AbsoluteValue, for: Outrun.Core.Integer, as: do
|
||||
def abs(self: Self): Self, as: Outrun.NIF.integer_abs(self)
|
||||
def abs(self: Self): Self, as: NIF.integer_abs(self)
|
||||
end
|
||||
"#;
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Addition
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Bitwise And
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Bitwise Or
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Bitwise Xor
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use Outrun.Core.Boolean
|
||||
|
||||
```doc
|
||||
# Boolean
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Division
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Boolean
|
||||
|
||||
```doc
|
||||
# Equality
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use Outrun.NIF, as: NIF
|
||||
|
||||
```doc
|
||||
# Error
|
||||
```
|
||||
|
@ -6,6 +8,6 @@ protocol Error, as: do
|
|||
def message(self: Self): String
|
||||
|
||||
defs raise(error: Error): Any, as: do
|
||||
Outrun.NIF.error_raise(error)
|
||||
NIF.error_raise(error)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Exponentiation
|
||||
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
use AbsoluteValue
|
||||
use Addition
|
||||
use Boolean
|
||||
use Division
|
||||
use Equality
|
||||
use Greater
|
||||
use GreaterOrEqual
|
||||
use Less
|
||||
use LessOrEqual
|
||||
use Multiplication
|
||||
use Negation
|
||||
|
||||
```doc
|
||||
# Float
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Boolean
|
||||
|
||||
```doc
|
||||
# Greater
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
use Any
|
||||
use Boolean
|
||||
use Greater
|
||||
use Equality
|
||||
|
||||
```doc
|
||||
# GreaterOrEqual
|
||||
|
||||
|
@ -11,7 +16,7 @@ protocol GreaterOrEqual, requires: Greater + Equality, as: do
|
|||
|
||||
The default implementation delegates to the `Greater` and `Equality` protocols, however you should probably implement this function as a single comparison is more than likely much faster.
|
||||
```
|
||||
def greater_or_equal?(self: Greater, other: Any): Boolean, when: Greater.greater?(self, other), as: true
|
||||
def greater_or_equal?(self: Equality, other: Any): Boolean, when: Equality.equal?(self, other), as: true
|
||||
def greater_or_equal?(_self: Self, _other: Any), as: false
|
||||
def greater_or_equal?(self: Self, other: Any): Boolean, when: Greater.greater?(self, other), as: true
|
||||
def greater_or_equal?(self: Self, other: Any): Boolean, when: Equality.equal?(self, other), as: true
|
||||
def greater_or_equal?(_self: Self, _other: Any): Boolean, as: false
|
||||
end
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
use AbsoluteValue
|
||||
use Addition
|
||||
use BitwiseAnd
|
||||
use BitwiseOr
|
||||
use BitwiseXor
|
||||
use Boolean
|
||||
use Division
|
||||
use Equality
|
||||
use Exponentiation
|
||||
use GreaterOrEqual
|
||||
use Greater
|
||||
use LessOrEqual
|
||||
use Less
|
||||
use Modulus
|
||||
use Multiplication
|
||||
use Negation
|
||||
use ShiftLeft
|
||||
use ShiftRight
|
||||
use Subtraction
|
||||
|
||||
```doc
|
||||
# Integer
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Boolean
|
||||
|
||||
```doc
|
||||
# Less
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
use Any
|
||||
use Boolean
|
||||
use Equality
|
||||
use Less
|
||||
|
||||
```doc
|
||||
# LessOrEqual
|
||||
|
||||
|
@ -11,7 +16,7 @@ protocol LessOrEqual, requires: Less + Equality, as: do
|
|||
|
||||
The default implementation delegates to the `Less` and `Equality` protocols, however you should probably implement this function as a single comparison is more than likely much faster.
|
||||
```
|
||||
def less_or_equal?(self: Less, other: Any): Boolean, when: Less.less?(self, other), as: true
|
||||
def less_or_equal?(self: Equality, other: Any): Boolean, when: Equality.equal?(self, other), as: true
|
||||
def less_or_equal?(self: Self, other: Any): Boolean, when: Less.less?(self, other), as: true
|
||||
def less_or_equal?(self: Self, other: Any): Boolean, when: Equality.equal?(self, other), as: true
|
||||
def less_or_equal?(_self: Self, _other: Any): Boolean, as: false
|
||||
end
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
use Any
|
||||
use Boolean
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# LogicalAnd
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Boolean
|
||||
|
||||
```doc
|
||||
# LogicalOr
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Modulus
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Multiplication
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
use Any
|
||||
use Boolean
|
||||
use Option
|
||||
use Outrun.Core.Some, as: Some
|
||||
use Outrun.Core.None, as: None
|
||||
|
||||
```doc
|
||||
# Option
|
||||
|
||||
|
@ -29,10 +35,10 @@ protocol Option, as: do
|
|||
```doc
|
||||
Create a new "some" value using the default some type.
|
||||
```
|
||||
defs some(value: Any): Option, as: Outrun.Core.Some.new(value)
|
||||
defs some(value: Any): Option, as: Some.new(value)
|
||||
|
||||
```doc
|
||||
Create a new "none" value using the default none type.
|
||||
```
|
||||
defs none(): Option, as: Outrun.Core.None.new()
|
||||
defs none(): Option, as: None.new()
|
||||
end
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Boolean
|
||||
use Outrun.NIF, as: NIF
|
||||
|
||||
```doc
|
||||
# Outrun.Core.Boolean
|
||||
|
||||
|
@ -15,7 +18,7 @@ end
|
|||
impl Equality, for: Outrun.Core.Boolean, as: do
|
||||
def equal?(self: Self, other: Boolean): Boolean, as: do
|
||||
let other = Boolean.true?(other)
|
||||
Outrun.NIF.bool_eq(self, other)
|
||||
NIF.bool_eq(self, other)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,12 +29,12 @@ impl LogicalAnd, for: Outrun.Core.Boolean, as: do
|
|||
Concrete implementation of `&&`.
|
||||
```
|
||||
def and?(self: Self, other: Self): Option, as: do
|
||||
boolean_to_option(Outrun.NIF.bool_and(self, other))
|
||||
boolean_to_option(NIF.bool_and(self, other))
|
||||
end
|
||||
|
||||
def and?(self: Self, other: Boolean): Option, as: do
|
||||
let other = Boolean.true?(other)
|
||||
let result = Outrun.NIF.bool_and(self, other)
|
||||
let result = NIF.bool_and(self, other)
|
||||
boolean_to_option(result)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Error
|
||||
use Result
|
||||
|
||||
```doc
|
||||
# Outrun.Core.Error
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
use Boolean
|
||||
use Option
|
||||
use Outrun.NIF
|
||||
|
||||
```doc
|
||||
# Outrun.Core.Float
|
||||
|
||||
|
@ -6,60 +10,73 @@ The native implementation of `Float` based around Rust's `f64` type.
|
|||
Has no constructor as it is created internally in the virtual machine by literals.
|
||||
```
|
||||
struct Outrun.Core.Float, as: do
|
||||
def nan?(self: Self): Boolean, as: Outrun.NIF.float_is_nan(self)
|
||||
def infinite?(self: Self): Boolean, as: Outrun.NIF.float_is_infinite(self)
|
||||
def finite?(self: Self): Boolean, as: Outrun.NIF.float_is_finite(self)
|
||||
def subnormal?(self: Self): Boolean, as: Outrun.NIF.float_is_subnormal(self)
|
||||
def normal?(self: Self): Boolean, as: Outrun.NIF.float_is_normal(self)
|
||||
def sign_positive?(self: Self): Boolean, as: Outrun.NIF.float_is_sign_positive(self)
|
||||
def sign_negative?(self: Self): Boolean, as: Outrun.NIF.float_is_sign_negative(self)
|
||||
def inverse(self: Self): Option, as: Outrun.NIF.float_inverse(self)
|
||||
def nan?(self: Self): Boolean, as: NIF.float_is_nan(self)
|
||||
def infinite?(self: Self): Boolean, as: NIF.float_is_infinite(self)
|
||||
def finite?(self: Self): Boolean, as: NIF.float_is_finite(self)
|
||||
def subnormal?(self: Self): Boolean, as: NIF.float_is_subnormal(self)
|
||||
def normal?(self: Self): Boolean, as: NIF.float_is_normal(self)
|
||||
def sign_positive?(self: Self): Boolean, as: NIF.float_is_sign_positive(self)
|
||||
def sign_negative?(self: Self): Boolean, as: NIF.float_is_sign_negative(self)
|
||||
def inverse(self: Self): Option, as: NIF.float_inverse(self)
|
||||
end
|
||||
|
||||
use AbsoluteValue
|
||||
impl AbsoluteValue, for: Outrun.Core.Float, as: do
|
||||
def abs(self: Self): Option, as: Outrun.NIF.float_abs(self)
|
||||
def abs(self: Self): Option, as: NIF.float_abs(self)
|
||||
end
|
||||
|
||||
use Addition
|
||||
impl Addition, for: Outrun.Core.Float, as: do
|
||||
def add(self: Self, other: Self): Option, as: Outrun.NIF.float_add(self, other)
|
||||
def add(self: Self, other: Self): Option, as: NIF.float_add(self, other)
|
||||
def add(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use Division
|
||||
impl Division, for: Outrun.Core.Float, as: do
|
||||
def divide(self: Self, other: Self): Option, as: Outrun.NIF.float_div(self, other)
|
||||
def divide(self: Self, other: Self): Option, as: NIF.float_div(self, other)
|
||||
def divide(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use Equality
|
||||
impl Equality, for: Outrun.Core.Float, as: do
|
||||
def equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_is_eq(self, other)
|
||||
def equal?(self: Self, other: Self): Boolean, as: NIF.float_is_eq(self, other)
|
||||
def equal?(_self: Self, _other: Self): Boolean, as: false
|
||||
end
|
||||
|
||||
use Float
|
||||
impl Float, for: Outrun.Core.Float
|
||||
|
||||
use Greater
|
||||
impl Greater, for: Outrun.Core.Float, as: do
|
||||
def greater?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_gt(self, other)
|
||||
def greater?(self: Self, other: Self): Boolean, as: NIF.float_gt(self, other)
|
||||
def greater?(_self: Self, _other: Self): Boolean, as: false
|
||||
end
|
||||
|
||||
use GreaterOrEqual
|
||||
impl GreaterOrEqual, for: Outrun.Core.Float, as: do
|
||||
def greater_or_equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_gte(self, other)
|
||||
def greater_or_equal?(self: Self, other: Self): Boolean, as: NIF.float_gte(self, other)
|
||||
def greater_or_equal?(_self: Self, _other: Self): Boolean, as: false
|
||||
end
|
||||
|
||||
use Less
|
||||
impl Less, for: Outrun.Core.Float, as: do
|
||||
def less?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_lt(self, other)
|
||||
def less?(self: Self, other: Self): Boolean, as: NIF.float_lt(self, other)
|
||||
def less?(_self: Self, _other: Self): Boolean, as: false
|
||||
end
|
||||
|
||||
use LessOrEqual
|
||||
impl LessOrEqual, for: Outrun.Core.Float, as: do
|
||||
def less_or_equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_lte(self, other)
|
||||
def less_or_equal?(self: Self, other: Self): Boolean, as: NIF.float_lte(self, other)
|
||||
def less_or_equal?(_self: Self, _other: Self): Boolean, as: false
|
||||
end
|
||||
|
||||
use Multiplication
|
||||
impl Multiplication, for: Outrun.Core.Float, as: do
|
||||
def multiply(self: Self, other: Self): Option, as: Outrun.NIF.float_mul(self, other)
|
||||
def multiply(self: Self, other: Self): Option, as: NIF.float_mul(self, other)
|
||||
def multiply(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use Negation
|
||||
impl Negation, for: Outrun.Core.Float, as: do
|
||||
def neg(self: Self): Option, as: Outrun.NIF.float_neg(self)
|
||||
def neg(self: Self): Option, as: NIF.float_neg(self)
|
||||
end
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Option
|
||||
use Outrun.NIF, as: NIF
|
||||
|
||||
```doc
|
||||
# Outrun.Core.Integer
|
||||
|
||||
|
@ -11,100 +14,117 @@ struct Outrun.Core.Integer, as: do
|
|||
|
||||
Returns none on overflow.
|
||||
```
|
||||
def abs(self: Self): Option, as: Outrun.NIF.integer_abs(self)
|
||||
def abs(self: Self): Option, as: NIF.integer_abs(self)
|
||||
|
||||
```doc
|
||||
Returns whether the value of `self` is greater than zero.
|
||||
```
|
||||
def positive?(self: Self): Self, as: Outrun.NIF.integer_gt(self, 0)
|
||||
def positive?(self: Self): Self, as: NIF.integer_gt(self, 0)
|
||||
|
||||
```doc
|
||||
Returns whether the value of `self` equals zero.
|
||||
```
|
||||
def zero?(self: Self): Self, as: Outrun.NIF.integer_eq(self, 0)
|
||||
def zero?(self: Self): Self, as: NIF.integer_eq(self, 0)
|
||||
|
||||
```doc
|
||||
Returns whether the value of `self` is less than zero.
|
||||
```
|
||||
def negative?(self: Self): Self, as: Outrun.NIF.integer_lt(self, 0)
|
||||
def negative?(self: Self): Self, as: NIF.integer_lt(self, 0)
|
||||
end
|
||||
|
||||
use AbsoluteValue
|
||||
impl AbsoluteValue, for: Outrun.Core.Integer, as: do
|
||||
def abs(self: Self): Self, as: Outrun.NIF.integer_abs(self)
|
||||
def abs(self: Self): Self, as: NIF.integer_abs(self)
|
||||
end
|
||||
|
||||
use Addition
|
||||
impl Addition, for: Outrun.Core.Integer, as: do
|
||||
def add(self: Self, other: Self): Option, as: Outrun.NIF.integer_add(self, other)
|
||||
def add(self: Self, other: Self): Option, as: NIF.integer_add(self, other)
|
||||
def add(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use BitwiseAnd
|
||||
impl BitwiseAnd, for: Outrun.Core.Integer, as: do
|
||||
def and(self: Self, other: Self): Option, as: Outrun.NIF.integer_band(self, other)
|
||||
def and(self: Self, other: Self): Option, as: NIF.integer_band(self, other)
|
||||
def and(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use BitwiseOr
|
||||
impl BitwiseOr, for: Outrun.Core.Integer, as: do
|
||||
def or(self: Self, other: Self): Option, as: Outrun.NIF.integer_bor(self, other)
|
||||
def or(self: Self, other: Self): Option, as: NIF.integer_bor(self, other)
|
||||
def or(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use BitwiseXor
|
||||
impl BitwiseXor, for: Outrun.Core.Integer, as: do
|
||||
def xor(self: Self, other: Self): Option, as: Outrun.NIF.integer_bxor(self, other)
|
||||
def xor(self: Self, other: Self): Option, as: NIF.integer_bxor(self, other)
|
||||
def xor(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use Division
|
||||
impl Division, for: Outrun.Core.Integer, as: do
|
||||
def divide(self: Self, other: Self): Option, as: Outrun.NIF.integer_div(self, other)
|
||||
def divide(self: Self, other: Self): Option, as: NIF.integer_div(self, other)
|
||||
def divide(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use Equality
|
||||
impl Equality, for: Outrun.Core.Integer, as: do
|
||||
def equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_eq(self, other)
|
||||
def equal?(self: Self, other: Self): Boolean, as: NIF.integer_eq(self, other)
|
||||
def equal?(_self: Self, _other: Any): Boolean, as: false
|
||||
end
|
||||
|
||||
use Exponentiation
|
||||
impl Exponentiation, for: Outrun.Core.Integer, as: do
|
||||
def raise(self: Self, other: Self): Option, as: Outrun.NIF.integer_expo(self, other)
|
||||
def raise(self: Self, other: Self): Option, as: NIF.integer_expo(self, other)
|
||||
def raise(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use GreaterOrEqual
|
||||
impl GreaterOrEqual, for: Outrun.Core.Integer, as: do
|
||||
def greater_or_equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_gte(self, other)
|
||||
def greater_or_equal?(self: Self, other: Self): Boolean, as: NIF.integer_gte(self, other)
|
||||
def greater_or_equal?(_self: Self, _other: Any): Boolean, as: false
|
||||
end
|
||||
|
||||
use Greater
|
||||
impl Greater, for: Outrun.Core.Integer, as: do
|
||||
def greater?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_gt(self, other)
|
||||
def greater?(self: Self, other: Self): Boolean, as: NIF.integer_gt(self, other)
|
||||
def greater?(_self: Self, _other: Any): Boolean, as: false
|
||||
end
|
||||
|
||||
use Integer
|
||||
impl Integer, for: Outrun.Core.Integer
|
||||
|
||||
use LessOrEqual
|
||||
impl LessOrEqual, for: Outrun.Core.Integer, as: do
|
||||
def less_or_equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_lte(self, other)
|
||||
def less_or_equal?(self: Self, other: Self): Boolean, as: NIF.integer_lte(self, other)
|
||||
def less_or_equal?(_self: Self, _other: Self): Boolean, as: false
|
||||
end
|
||||
|
||||
use Less
|
||||
impl Less, for: Outrun.Core.Integer, as: do
|
||||
def less?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_lt(self, other)
|
||||
def less?(self: Self, other: Self): Boolean, as: NIF.integer_lt(self, other)
|
||||
def less?(_self: Self, _other: Self): Boolean, as: false
|
||||
end
|
||||
|
||||
use Modulus
|
||||
impl Modulus, for: Outrun.Core.Integer, as: do
|
||||
def modulo(self: Self, other: Self): Option, as: Outrun.NIF.integer_mod(self, other)
|
||||
def modulo(self: Self, other: Self): Option, as: NIF.integer_mod(self, other)
|
||||
def modulo(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use Multiplication
|
||||
impl Multiplication, for: Outrun.Core.Integer, as: do
|
||||
def multiply(self: Self, other: Self): Option, as: Outrun.NIF.integer_mul(self, other)
|
||||
def multiply(self: Self, other: Self): Option, as: NIF.integer_mul(self, other)
|
||||
def multiply(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
||||
use Negation
|
||||
impl Negation, for: Outrun.Core.Integer, as: do
|
||||
def neg(self: Self): Option, as: Outrun.NIF.integer_neg(self)
|
||||
def neg(self: Self): Option, as: NIF.integer_neg(self)
|
||||
end
|
||||
|
||||
use Subtraction
|
||||
impl Subtraction, for: Outrun.Core.Integer, as: do
|
||||
def subtract(self: Self, other: Self): Option, as: Outrun.NIF.integer_sub(self, other)
|
||||
def subtract(self: Self, other: Self): Option, as: NIF.integer_sub(self, other)
|
||||
def subtract(_self: Self, _other: Any): Option, as: Option.none()
|
||||
end
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Boolean
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Outrun.Core.Some
|
||||
|
||||
|
@ -20,6 +23,7 @@ end
|
|||
|
||||
struct Outrun.Core.None.UnwrapError
|
||||
|
||||
use Error
|
||||
impl Error, for: Outrun.Core.None.UnwrapError, as: do
|
||||
def message(self: Self): String, as: "Attempt to unwrap a None value"
|
||||
end
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
use Any
|
||||
use Result
|
||||
use String
|
||||
|
||||
```doc
|
||||
# Outrun.Core.Okay
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
use Any
|
||||
use Boolean
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Outrun.Core.Some
|
||||
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
use Any
|
||||
use Boolean
|
||||
use Error
|
||||
use Result
|
||||
use Outrun.Core.Okay
|
||||
use Outrun.Core.Error
|
||||
|
||||
```doc
|
||||
# Result
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Shift Left
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Shift Right
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use Any
|
||||
use Option
|
||||
|
||||
```doc
|
||||
# Subtraction
|
||||
|
||||
|
|
Loading…
Reference in a new issue