wip: working on type resolution.

This commit is contained in:
James Harton 2022-08-19 16:25:42 +12:00
parent 4e476626ed
commit 40717b6c78
47 changed files with 883 additions and 350 deletions

View file

@ -14,6 +14,7 @@
"defstruct",
"defstructfield",
"defstructproperty",
"defuseproperty",
"hashrocket",
"indoc",
"lexer",

View file

@ -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};

View file

@ -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))
}
}

View file

@ -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>,
},
}

View file

@ -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 }

View file

@ -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)?;

View file

@ -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,
}
}
}

View file

@ -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)
}
}

View 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,
}
}
}

View file

@ -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)
}

View 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
}

View 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),
}

View 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)
}

View file

@ -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(),
}
}

View file

@ -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()))
Ok((module, types[0].clone()))
} else {
let ty = Arc::new(Type::Union(types));
env.types.push(ty.clone()).unwrap();
Ok((env, ty))
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))
}
}

View file

@ -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
"#;

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Addition

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Bitwise And

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Bitwise Or

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Bitwise Xor

View file

@ -1,3 +1,5 @@
use Outrun.Core.Boolean
```doc
# Boolean

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Division

View file

@ -1,3 +1,6 @@
use Any
use Boolean
```doc
# Equality

View file

@ -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

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Exponentiation

View file

@ -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

View file

@ -1,3 +1,6 @@
use Any
use Boolean
```doc
# Greater

View file

@ -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

View file

@ -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

View file

@ -1,3 +1,6 @@
use Any
use Boolean
```doc
# Less

View file

@ -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

View file

@ -1,3 +1,7 @@
use Any
use Boolean
use Option
```doc
# LogicalAnd

View file

@ -1,3 +1,6 @@
use Any
use Boolean
```doc
# LogicalOr

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Modulus

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Multiplication

View file

@ -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

View file

@ -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

View file

@ -1,3 +1,6 @@
use Error
use Result
```doc
# Outrun.Core.Error

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,3 +1,7 @@
use Any
use Result
use String
```doc
# Outrun.Core.Okay

View file

@ -1,3 +1,7 @@
use Any
use Boolean
use Option
```doc
# Outrun.Core.Some

View file

@ -1,3 +1,10 @@
use Any
use Boolean
use Error
use Result
use Outrun.Core.Okay
use Outrun.Core.Error
```doc
# Result

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Shift Left

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Shift Right

View file

@ -1,3 +1,6 @@
use Any
use Option
```doc
# Subtraction