Add type checking

This commit is contained in:
Jeff 2023-11-27 10:27:44 -05:00
parent ab769b4b2a
commit 0646d010c5
11 changed files with 9758 additions and 9333 deletions

View File

@ -28,7 +28,7 @@ if (random_boolean) {
}
```
Dust is an interpreted, dynamically typed language with first class functions. It emphasises concurrency by allowing any group of statements to be executed in parallel. It is *data-oriented*, with extensive tools to manage structured and relational data. Dust includes built-in tooling to import and export data in a variety of formats, including JSON, TOML, YAML and CSV.
Dust is an interpreted, strictly typed language with first class functions. It emphasises concurrency by allowing any group of statements to be executed in parallel. Dust includes built-in tooling to import and export data in a variety of formats, including JSON, TOML, YAML and CSV.
<!--toc:start-->
- [Dust](#dust)
@ -50,10 +50,10 @@ Dust is an interpreted, dynamically typed language with first class functions. I
## Features
- Simplicity: Dust is designed to be easy to learn.
- Speed: Dust is built on [Tree Sitter] and [Rust] to prioritize performance and correctness.
- Data format: Dust is data-oriented, making it a great language for defining data.
- Format conversion: Effortlessly convert between dust and formats like JSON, CSV and TOML.
- Structured data: Dust can represent data with more than just strings. Lists, maps and tables are easy to make and manage.
- Speed: Dust is built on [Tree Sitter] and [Rust] to prioritize performance and correctness. See [Benchmarks] below.
- Concurrency: Easily and safely write code that runs in parallel.
- Safety: Written in safe, stable Rust.
- Correctness: Type checking makes it easy to write good code that works.
## Usage

View File

@ -1,11 +1,12 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, Value};
use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, Type, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Assignment {
identifier: Identifier,
r#type: Option<Type>,
operator: AssignmentOperator,
statement: Statement,
}
@ -21,10 +22,21 @@ impl AbstractTree for Assignment {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
Error::expect_syntax_node(source, "assignment", node)?;
let identifier_node = node.child(0).unwrap();
let identifier_node = node.child_by_field_name("identifier").unwrap();
let identifier = Identifier::from_syntax_node(source, identifier_node)?;
let operator_node = node.child(1).unwrap().child(0).unwrap();
let type_node = node.child_by_field_name("type");
let r#type = if let Some(type_node) = type_node {
Some(Type::from_syntax_node(source, type_node)?)
} else {
None
};
let operator_node = node
.child_by_field_name("assignment_operator")
.unwrap()
.child(0)
.unwrap();
let operator = match operator_node.kind() {
"=" => AssignmentOperator::Equal,
"+=" => AssignmentOperator::PlusEqual,
@ -39,11 +51,12 @@ impl AbstractTree for Assignment {
}
};
let statement_node = node.child(2).unwrap();
let statement_node = node.child_by_field_name("statement").unwrap();
let statement = Statement::from_syntax_node(source, statement_node)?;
Ok(Assignment {
identifier,
r#type,
operator,
statement,
})
@ -73,6 +86,28 @@ impl AbstractTree for Assignment {
AssignmentOperator::Equal => value,
};
let expected_type = self.r#type.as_ref().unwrap_or(&Type::Any);
match (expected_type, new_value.r#type()) {
(Type::Any, _)
| (Type::Boolean, Type::Boolean)
| (Type::Float, Type::Float)
| (Type::Function, Type::Function)
| (Type::Integer, Type::Integer)
| (Type::List, Type::List)
| (Type::Map, Type::Map)
| (Type::String, Type::String)
| (Type::Table, Type::Table) => {}
(Type::Boolean, _) => return Err(Error::ExpectedBoolean { actual: new_value }),
(Type::Float, _) => return Err(Error::ExpectedFloat { actual: new_value }),
(Type::Function, _) => return Err(Error::ExpectedFunction { actual: new_value }),
(Type::Integer, _) => return Err(Error::ExpectedInteger { actual: new_value }),
(Type::List, _) => return Err(Error::ExpectedList { actual: new_value }),
(Type::Map, _) => return Err(Error::ExpectedMap { actual: new_value }),
(Type::String, _) => return Err(Error::ExpectedString { actual: new_value }),
(Type::Table, _) => return Err(Error::ExpectedTable { actual: new_value }),
}
context.variables_mut()?.insert(key, new_value);
Ok(Value::Empty)

View File

@ -20,7 +20,9 @@ impl AbstractTree for Type {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
Error::expect_syntax_node(source, "type", node)?;
let r#type = match &source[node.byte_range()] {
let range_without_punctuation = node.start_byte() + 1..node.end_byte() - 1;
let r#type = match &source[range_without_punctuation] {
"any" => Type::Any,
"bool" => Type::Boolean,
"float" => Type::Float,

View File

@ -59,7 +59,7 @@ pub enum Error {
actual: Value,
},
ExpectedInt {
ExpectedInteger {
actual: Value,
},
@ -270,24 +270,20 @@ impl fmt::Display for Error {
"{identifier} expected a minimum of {minimum} arguments, but got {actual}.",
),
ExpectedString { actual } => {
write!(f, "Expected a Value::String, but got {:?}.", actual)
write!(f, "Expected a string but got {:?}.", actual)
}
ExpectedInteger { actual } => write!(f, "Expected an integer, but got {:?}.", actual),
ExpectedFloat { actual } => write!(f, "Expected a float, but got {:?}.", actual),
ExpectedNumber { actual } => {
write!(f, "Expected a float or integer but got {:?}.", actual)
}
ExpectedNumberOrString { actual } => {
write!(f, "Expected a number or string, but got {:?}.", actual)
}
ExpectedInt { actual } => write!(f, "Expected a Value::Int, but got {:?}.", actual),
ExpectedFloat { actual } => write!(f, "Expected a Value::Float, but got {:?}.", actual),
ExpectedNumber { actual } => write!(
f,
"Expected a Value::Float or Value::Int, but got {:?}.",
actual
),
ExpectedNumberOrString { actual } => write!(
f,
"Expected a Value::Number or a Value::String, but got {:?}.",
actual
),
ExpectedBoolean { actual } => {
write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
write!(f, "Expected a boolean, but got {:?}.", actual)
}
ExpectedList { actual } => write!(f, "Expected a Value::List, but got {:?}.", actual),
ExpectedList { actual } => write!(f, "Expected a list, but got {:?}.", actual),
ExpectedMinLengthList {
minimum_len,
actual_len,
@ -300,12 +296,12 @@ impl fmt::Display for Error {
actual,
} => write!(
f,
"Expected a Value::List of len {}, but got {:?}.",
"Expected a list of len {}, but got {:?}.",
expected_len, actual
),
ExpectedEmpty { actual } => write!(f, "Expected a Value::Empty, but got {:?}.", actual),
ExpectedMap { actual } => write!(f, "Expected a Value::Map, but got {:?}.", actual),
ExpectedTable { actual } => write!(f, "Expected a Value::Table, but got {:?}.", actual),
ExpectedEmpty { actual } => write!(f, "Expected an empty value, but got {:?}.", actual),
ExpectedMap { actual } => write!(f, "Expected a map, but got {:?}.", actual),
ExpectedTable { actual } => write!(f, "Expected a table, but got {:?}.", actual),
ExpectedFunction { actual } => {
write!(f, "Expected Value::Function, but got {:?}.", actual)
}

View File

@ -52,9 +52,9 @@ impl Value {
Value::Function(_) => Type::Function,
Value::String(_) => Type::String,
Value::Float(_) => Type::Float,
Value::Integer(_) => todo!(),
Value::Boolean(_) => todo!(),
Value::Empty => todo!(),
Value::Integer(_) => Type::Integer,
Value::Boolean(_) => Type::Boolean,
Value::Empty => Type::Any,
}
}
@ -116,7 +116,7 @@ impl Value {
pub fn as_integer(&self) -> Result<i64> {
match self {
Value::Integer(i) => Ok(*i),
value => Err(Error::ExpectedInt {
value => Err(Error::ExpectedInteger {
actual: value.clone(),
}),
}
@ -540,7 +540,7 @@ impl TryFrom<Value> for i64 {
if let Value::Integer(value) = value {
Ok(value)
} else {
Err(Error::ExpectedInt { actual: value })
Err(Error::ExpectedInteger { actual: value })
}
}
}

View File

@ -15,6 +15,24 @@ x = y
(expression
(identifier))))))
================================================================================
Simple Assignment with Type
================================================================================
x <int> = y
--------------------------------------------------------------------------------
(root
(statement
(assignment
(identifier)
(type)
(assignment_operator)
(statement
(expression
(identifier))))))
================================================================================
Map Item Assignment
================================================================================

View File

@ -62,7 +62,7 @@ Function Call
Complex Function
================================================================================
|message:str number:int| => {
|message <str> number <int>| => {
(output message)
(output number)
}
@ -75,9 +75,9 @@ Complex Function
(value
(function
(identifier)
(type_definition)
(type)
(identifier)
(type_definition)
(type)
(block
(statement
(expression

View File

@ -44,7 +44,7 @@ module.exports = grammar({
seq('(', $._expression_kind, ')'),
)),
_expression_kind: $ => choice(
_expression_kind: $ => prec.right(choice(
$.function_call,
$.identifier,
$.index,
@ -52,7 +52,7 @@ module.exports = grammar({
$.math,
$.value,
$.yield,
),
)),
_expression_list: $ => repeat1(prec.right(seq(
$.expression,
@ -152,9 +152,10 @@ module.exports = grammar({
),
assignment: $ => seq(
$.identifier,
$.assignment_operator,
$.statement,
field('identifier', $.identifier),
optional(field('type', $.type)),
field('assignment_operator', $.assignment_operator),
field('statement', $.statement),
),
index_assignment: $ => seq(
@ -258,15 +259,19 @@ module.exports = grammar({
$.string,
),
type_definition: $ => choice(
'any',
'bool',
'fn',
'int',
'list',
'map',
'str',
'table',
type: $ => seq(
'<',
choice(
'any',
'bool',
'fn',
'int',
'list',
'map',
'str',
'table',
),
'>',
),
function: $ => seq(
@ -274,8 +279,7 @@ module.exports = grammar({
'|',
field('parameter', repeat(seq(
$.identifier,
':',
$.type_definition,
$.type,
optional(',')
))),
'|',

View File

@ -154,37 +154,41 @@
}
},
"_expression_kind": {
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "function_call"
},
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "SYMBOL",
"name": "index"
},
{
"type": "SYMBOL",
"name": "logic"
},
{
"type": "SYMBOL",
"name": "math"
},
{
"type": "SYMBOL",
"name": "value"
},
{
"type": "SYMBOL",
"name": "yield"
}
]
"type": "PREC_RIGHT",
"value": 0,
"content": {
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "function_call"
},
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "SYMBOL",
"name": "index"
},
{
"type": "SYMBOL",
"name": "logic"
},
{
"type": "SYMBOL",
"name": "math"
},
{
"type": "SYMBOL",
"name": "value"
},
{
"type": "SYMBOL",
"name": "yield"
}
]
}
},
"_expression_list": {
"type": "REPEAT1",
@ -704,16 +708,44 @@
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "identifier"
"type": "FIELD",
"name": "identifier",
"content": {
"type": "SYMBOL",
"name": "identifier"
}
},
{
"type": "SYMBOL",
"name": "assignment_operator"
"type": "CHOICE",
"members": [
{
"type": "FIELD",
"name": "type",
"content": {
"type": "SYMBOL",
"name": "type"
}
},
{
"type": "BLANK"
}
]
},
{
"type": "SYMBOL",
"name": "statement"
"type": "FIELD",
"name": "assignment_operator",
"content": {
"type": "SYMBOL",
"name": "assignment_operator"
}
},
{
"type": "FIELD",
"name": "statement",
"content": {
"type": "SYMBOL",
"name": "statement"
}
}
]
},
@ -1073,36 +1105,53 @@
}
]
},
"type_definition": {
"type": "CHOICE",
"type": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "bool"
"value": "<"
},
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "any"
},
{
"type": "STRING",
"value": "bool"
},
{
"type": "STRING",
"value": "fn"
},
{
"type": "STRING",
"value": "int"
},
{
"type": "STRING",
"value": "list"
},
{
"type": "STRING",
"value": "map"
},
{
"type": "STRING",
"value": "str"
},
{
"type": "STRING",
"value": "table"
}
]
},
{
"type": "STRING",
"value": "fn"
},
{
"type": "STRING",
"value": "int"
},
{
"type": "STRING",
"value": "list"
},
{
"type": "STRING",
"value": "map"
},
{
"type": "STRING",
"value": "str"
},
{
"type": "STRING",
"value": "table"
"value": ">"
}
]
},
@ -1131,13 +1180,9 @@
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "STRING",
"value": ":"
},
{
"type": "SYMBOL",
"name": "type_definition"
"name": "type"
},
{
"type": "CHOICE",

View File

@ -2,24 +2,47 @@
{
"type": "assignment",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "assignment_operator",
"named": true
},
{
"type": "identifier",
"named": true
},
{
"type": "statement",
"named": true
}
]
"fields": {
"assignment_operator": {
"multiple": false,
"required": true,
"types": [
{
"type": "assignment_operator",
"named": true
}
]
},
"identifier": {
"multiple": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
}
]
},
"statement": {
"multiple": false,
"required": true,
"types": [
{
"type": "statement",
"named": true
}
]
},
"type": {
"multiple": false,
"required": false,
"types": [
{
"type": "type",
"named": true
}
]
}
}
},
{
@ -180,16 +203,12 @@
"type": ",",
"named": false
},
{
"type": ":",
"named": false
},
{
"type": "identifier",
"named": true
},
{
"type": "type_definition",
"type": "type",
"named": true
}
]
@ -562,7 +581,7 @@
}
},
{
"type": "type_definition",
"type": "type",
"named": true,
"fields": {}
},
@ -762,6 +781,10 @@
"type": "]",
"named": false
},
{
"type": "any",
"named": false
},
{
"type": "append",
"named": false

File diff suppressed because it is too large Load Diff