Compare commits

...

2 Commits

8 changed files with 156 additions and 45 deletions

View File

@ -24,11 +24,11 @@ Syntax for Reid is very much inspired by rust, and examples of the language can
be found in the [examples](../examples/)-folder. be found in the [examples](../examples/)-folder.
In Reid **modules** (or files) on the top-level are comprised of imports, type In Reid **modules** (or files) on the top-level are comprised of imports, type
definitions, binop-definitions and functions. definitions, binop-definitions, functions and type-associated function blocks.
In formal grammar In formal grammar
```bnf ```bnf
<module> :: (<import> | <type-definition> | <binop-definition> | <function>)* <module> :: (<import> | <type-definition> | <binop-definition> | <function> | <assoc-function-block>)*
``` ```
Table of Contents: Table of Contents:
@ -38,6 +38,7 @@ Table of Contents:
- [Struct types](#struct-types) - [Struct types](#struct-types)
- [Binary operation Definitions](#binary-operation-definitions) - [Binary operation Definitions](#binary-operation-definitions)
- [Function definitions](#function-definition) - [Function definitions](#function-definition)
- [Associated functions](#associated-functions)
- [Statement](#statement) - [Statement](#statement)
- [Expression](#expression) - [Expression](#expression)
@ -140,7 +141,7 @@ impl binop (lhs: u16) + (rhs: u32) -> u32 {
### Function Definition ### Function Definition
Rust syntax for defining functions is similar to rust. There are two types of functions: Reid syntax for defining functions is similar to rust. There are two types of functions:
1. `extern` functions which are defined in another module, used to define functions from outside modules such as `libc`. 1. `extern` functions which are defined in another module, used to define functions from outside modules such as `libc`.
2. `local` functions which are defined locally in the module in Reid. Their 2. `local` functions which are defined locally in the module in Reid. Their
definition is contained within a `block` which contains a list of definition is contained within a `block` which contains a list of
@ -153,8 +154,9 @@ In formal grammar:
<local-function> :: [ "pub" ] "fn" <signature> <block> <local-function> :: [ "pub" ] "fn" <signature> <block>
<signature> :: <ident> "(" [ <params> ] ")" [ "->" <type> ] <signature> :: <ident> "(" [ <params> ] ")" [ "->" <type> ]
<params> <param> ( "," <param> )* <params> :: <param-or-self> ( "," <param> )*
<param> :: <ident> ":" <type> <param-or-self> = <param> | ( [ "&" [ "mut" ] ] "self")
<param> :: (<ident> ":" <type>)
<block> :: "{" <statement>* "}" <block> :: "{" <statement>* "}"
``` ```
@ -167,6 +169,27 @@ fn main() -> u8 {
} }
``` ```
#### Associated Functions
Reid also has a very similar syntax for defining associated functions as Rust
does. They are also the only types of functions where usage of initial
"self"-param is allowed, referring to a potential self-type. Associated
functions are functions that are defined within certain types such that you can
have multiple functions of the same name, as long as they are associated with a
different type. In formal grammar associated function blocks are:
```bnf
<assoc-function-block> :: "impl" <type> "{" <function-definition>* "}"
```
An example of such a block could be:
```rust
impl Test {
fn get_field(&self) -> u32 {
*self.field
}
}
```
### Statement ### Statement
Statements in Reid is how you tell the program to do anything. Currently supported statements include: Statements in Reid is how you tell the program to do anything. Currently supported statements include:
@ -222,6 +245,8 @@ calls, literals, or if-expressions. Types of supported expressions include:
- **Binary operations** (such as add/sub/mult) - **Binary operations** (such as add/sub/mult)
- **Unary operations** (such as !value or -value) - **Unary operations** (such as !value or -value)
- **Function calls**, to invoke a predefined function with given parameters - **Function calls**, to invoke a predefined function with given parameters
- **Associated function calls**, to invoke a predefined function on a certain
*associated type* with given parameters.
- **Block-expressions**, which can return a value to the higher-level expression - **Block-expressions**, which can return a value to the higher-level expression
if they have a statement with a soft-return. Otherwise they return void. if they have a statement with a soft-return. Otherwise they return void.
- **If-expressions**, which can execute one of two expressions depending on the - **If-expressions**, which can execute one of two expressions depending on the
@ -238,8 +263,8 @@ In formal grammar:
<array> | <struct> | <array> | <struct> |
<indexing> | <accessing> | <indexing> | <accessing> |
<binary-exp> | <unary-exp> | <binary-exp> | <unary-exp> |
<function-call> | <block> | <function-call> | <assoc-function-call>
<if-expr> | <cast> | <block> | <if-expr> | <cast> |
( "(" <expression> ")" ) ( "(" <expression> ")" )
<variable> :: <ident> <variable> :: <ident>
@ -253,6 +278,7 @@ In formal grammar:
<binary-exp> :: <expression> <binop> <expression> <binary-exp> :: <expression> <binop> <expression>
<unary-exp> :: <unary> <expression> <unary-exp> :: <unary> <expression>
<function-call> :: <expression> "(" [ <expression> ( "," <expression> )* ] ")" <function-call> :: <expression> "(" [ <expression> ( "," <expression> )* ] ")"
<assoc-function-call> :: <type> "::" <function-call>
<if-expr> :: "if" <expression> <expression> [ "else" <expression> ] <if-expr> :: "if" <expression> <expression> [ "else" <expression> ]
<cast> :: <expression> "as" <type> <cast> :: <expression> "as" <type>
``` ```
@ -269,6 +295,7 @@ test.first // Accessing
7 + value // Binop 7 + value // Binop
!bool_value // Unary !bool_value // Unary
func(value, 14) // Function call func(value, 14) // Function call
Test::get_field(&test); // Associated function call
if varname {} else {} // If-expression if varname {} else {} // If-expression
value as u32 // cast value as u32 // cast
(value + 2) // Binop within parenthesis (value + 2) // Binop within parenthesis

View File

@ -0,0 +1,27 @@
import std::print;
import std::from_str;
import std::String;
struct Otus {
field: u32,
}
impl Otus {
fn test(self) -> u32 {
self.field
}
}
impl i32 {
fn test(self) -> u32 {
43
}
}
fn main() -> u32 {
let otus = Otus { field: 17 };
print(from_str("otus: ") + otus.test() as u64);
return otus.test();
}

View File

@ -89,6 +89,8 @@ pub enum ExpressionKind {
Indexed(Box<Expression>, Box<Expression>), Indexed(Box<Expression>, Box<Expression>),
/// Struct-accessed, e.g. <expr>.<expr> /// Struct-accessed, e.g. <expr>.<expr>
Accessed(Box<Expression>, String), Accessed(Box<Expression>, String),
/// Associated function call, but with a shorthand
AccessCall(Box<Expression>, Box<FunctionCallExpression>),
Binop(BinaryOperator, Box<Expression>, Box<Expression>), Binop(BinaryOperator, Box<Expression>, Box<Expression>),
FunctionCall(Box<FunctionCallExpression>), FunctionCall(Box<FunctionCallExpression>),
AssociatedFunctionCall(Type, Box<FunctionCallExpression>), AssociatedFunctionCall(Type, Box<FunctionCallExpression>),

View File

@ -358,12 +358,20 @@ impl Parse for PrimaryExpression {
stream.get_range().unwrap(), stream.get_range().unwrap(),
); );
} }
ValueIndex::Struct(StructValueIndex(name)) => { ValueIndex::Dot(val) => match val {
expr = Expression( DotIndexKind::StructValueIndex(name) => {
ExpressionKind::Accessed(Box::new(expr), name), expr = Expression(
stream.get_range().unwrap(), ExpressionKind::Accessed(Box::new(expr), name),
); stream.get_range().unwrap(),
} );
}
DotIndexKind::FunctionCall(function_call_expression) => {
expr = Expression(
ExpressionKind::AccessCall(Box::new(expr), Box::new(function_call_expression)),
stream.get_range().unwrap(),
);
}
},
} }
} }
@ -473,27 +481,35 @@ impl Parse for BinaryOperator {
impl Parse for FunctionCallExpression { impl Parse for FunctionCallExpression {
fn parse(mut stream: TokenStream) -> Result<Self, Error> { fn parse(mut stream: TokenStream) -> Result<Self, Error> {
if let Some(Token::Identifier(name)) = stream.next() { if let Some(Token::Identifier(name)) = stream.next() {
stream.expect(Token::ParenOpen)?; let args = stream.parse::<FunctionArgs>()?;
Ok(FunctionCallExpression(name, args.0, stream.get_range().unwrap()))
let mut args = Vec::new();
if let Ok(exp) = stream.parse() {
args.push(exp);
while stream.expect(Token::Comma).is_ok() {
args.push(stream.parse()?);
}
}
stream.expect(Token::ParenClose)?;
Ok(FunctionCallExpression(name, args, stream.get_range().unwrap()))
} else { } else {
Err(stream.expected_err("identifier")?) Err(stream.expected_err("identifier")?)
} }
} }
} }
#[derive(Debug)]
pub struct FunctionArgs(Vec<Expression>);
impl Parse for FunctionArgs {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
stream.expect(Token::ParenOpen)?;
let mut params = Vec::new();
if let Ok(exp) = stream.parse() {
params.push(exp);
while stream.expect(Token::Comma).is_ok() {
params.push(stream.parse()?);
}
}
stream.expect(Token::ParenClose)?;
Ok(FunctionArgs(params))
}
}
impl Parse for IfExpression { impl Parse for IfExpression {
fn parse(mut stream: TokenStream) -> Result<Self, Error> { fn parse(mut stream: TokenStream) -> Result<Self, Error> {
stream.expect(Token::If)?; stream.expect(Token::If)?;
@ -766,14 +782,14 @@ impl<T: Parse + std::fmt::Debug> Parse for NamedField<T> {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ValueIndex { pub enum ValueIndex {
Array(ArrayValueIndex), Array(ArrayValueIndex),
Struct(StructValueIndex), Dot(DotIndexKind),
} }
impl Parse for ValueIndex { impl Parse for ValueIndex {
fn parse(mut stream: TokenStream) -> Result<Self, Error> { fn parse(mut stream: TokenStream) -> Result<Self, Error> {
match stream.peek() { match stream.peek() {
Some(Token::BracketOpen) => Ok(ValueIndex::Array(stream.parse()?)), Some(Token::BracketOpen) => Ok(ValueIndex::Array(stream.parse()?)),
Some(Token::Dot) => Ok(ValueIndex::Struct(stream.parse()?)), Some(Token::Dot) => Ok(ValueIndex::Dot(stream.parse()?)),
_ => Err(stream.expecting_err("value or struct index")?), _ => Err(stream.expecting_err("value or struct index")?),
} }
} }
@ -792,13 +808,24 @@ impl Parse for ArrayValueIndex {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StructValueIndex(String); pub enum DotIndexKind {
StructValueIndex(String),
FunctionCall(FunctionCallExpression),
}
impl Parse for StructValueIndex { impl Parse for DotIndexKind {
fn parse(mut stream: TokenStream) -> Result<Self, Error> { fn parse(mut stream: TokenStream) -> Result<Self, Error> {
stream.expect(Token::Dot)?; stream.expect(Token::Dot)?;
if let Some(Token::Identifier(name)) = stream.next() { if let Some(Token::Identifier(name)) = stream.next() {
Ok(StructValueIndex(name)) if let Ok(args) = stream.parse::<FunctionArgs>() {
Ok(Self::FunctionCall(FunctionCallExpression(
name,
args.0,
stream.get_range_prev().unwrap(),
)))
} else {
Ok(Self::StructValueIndex(name))
}
} else { } else {
return Err(stream.expected_err("struct index (number)")?); return Err(stream.expected_err("struct index (number)")?);
} }

View File

@ -422,6 +422,19 @@ impl ast::Expression {
meta: fn_call_expr.2.as_meta(module_id), meta: fn_call_expr.2.as_meta(module_id),
}, },
), ),
ast::ExpressionKind::AccessCall(expression, fn_call_expr) => {
let mut params: Vec<_> = fn_call_expr.1.iter().map(|e| e.process(module_id)).collect();
params.insert(0, expression.process(module_id));
mir::ExprKind::AssociatedFunctionCall(
mir::TypeKind::Vague(mir::VagueType::Unknown),
mir::FunctionCall {
name: fn_call_expr.0.clone(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: params,
meta: fn_call_expr.2.as_meta(module_id),
},
)
}
}; };
mir::Expression(kind, self.1.as_meta(module_id)) mir::Expression(kind, self.1.as_meta(module_id))

View File

@ -82,6 +82,8 @@ pub enum ErrorKind {
BinaryOpAlreadyDefined(BinaryOperator, TypeKind, TypeKind), BinaryOpAlreadyDefined(BinaryOperator, TypeKind, TypeKind),
#[error("Binary operation {0} between {1} and {2} is not defined")] #[error("Binary operation {0} between {1} and {2} is not defined")]
InvalidBinop(BinaryOperator, TypeKind, TypeKind), InvalidBinop(BinaryOperator, TypeKind, TypeKind),
#[error("Could not infer type for {0:?}. Try adding type annotations.")]
CouldNotInferType(String),
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -294,13 +296,13 @@ impl TypeKind {
} }
} }
pub(super) fn assert_known(&self, refs: &TypeRefs, state: &TypecheckPassState) -> Result<TypeKind, ErrorKind> { pub(super) fn assert_known(&self, state: &TypecheckPassState) -> Result<TypeKind, ErrorKind> {
self.is_known(refs, state).map(|_| self.clone()) self.is_known(state).map(|_| self.clone())
} }
pub(super) fn is_known(&self, refs: &TypeRefs, state: &TypecheckPassState) -> Result<(), ErrorKind> { pub(super) fn is_known(&self, state: &TypecheckPassState) -> Result<(), ErrorKind> {
match &self { match &self {
TypeKind::Array(type_kind, _) => type_kind.as_ref().is_known(refs, state), TypeKind::Array(type_kind, _) => type_kind.as_ref().is_known(state),
TypeKind::CustomType(custom_type_key) => { TypeKind::CustomType(custom_type_key) => {
state state
.scope .scope
@ -311,9 +313,9 @@ impl TypeKind {
state.module_id.unwrap(), state.module_id.unwrap(),
)) ))
} }
TypeKind::Borrow(type_kind, _) => type_kind.is_known(refs, state), TypeKind::Borrow(type_kind, _) => type_kind.is_known(state),
TypeKind::UserPtr(type_kind) => type_kind.is_known(refs, state), TypeKind::UserPtr(type_kind) => type_kind.is_known(state),
TypeKind::CodegenPtr(type_kind) => type_kind.is_known(refs, state), TypeKind::CodegenPtr(type_kind) => type_kind.is_known(state),
TypeKind::Vague(vague_type) => Err(ErrorKind::TypeIsVague(*vague_type)), TypeKind::Vague(vague_type) => Err(ErrorKind::TypeIsVague(*vague_type)),
_ => Ok(()), _ => Ok(()),
} }

View File

@ -112,7 +112,7 @@ impl BinopDefinition {
fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> { fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> {
for param in vec![&self.lhs, &self.rhs] { for param in vec![&self.lhs, &self.rhs] {
let param_t = state.or_else( let param_t = state.or_else(
param.1.assert_known(typerefs, state), param.1.assert_known(state),
TypeKind::Vague(Vague::Unknown), TypeKind::Vague(Vague::Unknown),
self.signature(), self.signature(),
); );
@ -130,7 +130,7 @@ impl BinopDefinition {
state.ok(res, self.signature()); state.ok(res, self.signature());
} }
let return_type = self.return_type.clone().assert_known(typerefs, state)?; let return_type = self.return_type.clone().assert_known(state)?;
state.scope.return_type_hint = Some(self.return_type.clone()); state.scope.return_type_hint = Some(self.return_type.clone());
let inferred = self let inferred = self
@ -150,7 +150,7 @@ impl FunctionDefinition {
fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> { fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> {
for param in &self.parameters { for param in &self.parameters {
let param_t = state.or_else( let param_t = state.or_else(
param.1.assert_known(typerefs, state), param.1.assert_known(state),
TypeKind::Vague(Vague::Unknown), TypeKind::Vague(Vague::Unknown),
self.signature(), self.signature(),
); );
@ -168,7 +168,7 @@ impl FunctionDefinition {
state.ok(res, self.signature()); state.ok(res, self.signature());
} }
let return_type = self.return_type.clone().assert_known(typerefs, state)?; let return_type = self.return_type.clone().assert_known(state)?;
let inferred = self.kind.typecheck(typerefs, state, Some(self.return_type.clone())); let inferred = self.kind.typecheck(typerefs, state, Some(self.return_type.clone()));
match inferred { match inferred {
@ -327,7 +327,7 @@ impl Block {
} }
StmtKind::While(WhileStatement { condition, block, meta }) => { StmtKind::While(WhileStatement { condition, block, meta }) => {
let condition_ty = condition.typecheck(&mut state, typerefs, HintKind::Coerce(TypeKind::Bool))?; let condition_ty = condition.typecheck(&mut state, typerefs, HintKind::Coerce(TypeKind::Bool))?;
if condition_ty.assert_known(typerefs, &state)? != TypeKind::Bool { if condition_ty.assert_known(&state)? != TypeKind::Bool {
state.note_errors(&vec![ErrorKind::TypesIncompatible(condition_ty, TypeKind::Bool)], *meta); state.note_errors(&vec![ErrorKind::TypesIncompatible(condition_ty, TypeKind::Bool)], *meta);
} }

View File

@ -595,6 +595,19 @@ impl Expression {
Ok(type_refs.from_type(type_kind).unwrap()) Ok(type_refs.from_type(type_kind).unwrap())
} }
ExprKind::AssociatedFunctionCall(type_kind, function_call) => { ExprKind::AssociatedFunctionCall(type_kind, function_call) => {
if type_kind.is_known(state).is_err() {
let first_param = function_call
.parameters
.get_mut(0)
.expect("Unknown-type associated function NEEDS to always have at least one parameter!");
let param_ty = first_param.infer_types(state, type_refs).unwrap().resolve_deep();
*type_kind = state.or_else(
param_ty.ok_or(ErrorKind::CouldNotInferType(format!("{}", first_param))),
Void,
first_param.1,
);
}
// Get function definition and types // Get function definition and types
let fn_call = state let fn_call = state
.scope .scope