Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8b45d34
move some files around
timfennis Dec 5, 2025
4d05582
WIP pre collapse types
timfennis Dec 5, 2025
6dd7465
implemented a really stupid type inference system that almost always …
timfennis Dec 5, 2025
e0d4730
Collapse Value and Param type into StaticType
timfennis Dec 5, 2025
9ee8eb4
Cleanup
timfennis Dec 5, 2025
cdebf0e
Remove number type
timfennis Dec 5, 2025
646d256
rename type checking bits
timfennis Dec 5, 2025
7185054
WIP fixing bug
timfennis Dec 5, 2025
2b68dfb
All tests pass
timfennis Dec 5, 2025
5101628
Cleanup StaticType printing
timfennis Dec 5, 2025
8d65a6c
Very basic return types
timfennis Dec 5, 2025
dd3065a
Update macro crate to get return types from native rust functions (WIP)
timfennis Dec 6, 2025
e0d4b8d
Improve return types for builtins
timfennis Dec 6, 2025
8a4339c
Added test for weird behavior we found
timfennis Dec 6, 2025
3c45cad
Add return types most builtins
timfennis Dec 6, 2025
3ac72c7
Clippy fixes
timfennis Dec 6, 2025
c83a778
WIP: Also store global functions in a vec without merging
timfennis Dec 6, 2025
cb08296
WIP resolve correct function during resolution pass
timfennis Dec 6, 2025
ab41963
Way too static function matching
timfennis Dec 6, 2025
6a3bb0b
Get some function calling working
timfennis Dec 6, 2025
bce0814
stupid function type matching
timfennis Dec 6, 2025
0bd9438
Messy dynamic binding for OpAssign
timfennis Dec 7, 2025
bf93068
It can run puzzles but it's very slow
timfennis Dec 7, 2025
9da43c7
Remove overloaded function
timfennis Dec 7, 2025
f68d3cb
Some WIP before generic type parameters on EVERYTHING
timfennis Dec 7, 2025
552e7a4
Crazy WIP, parameterize all sequence types
timfennis Dec 7, 2025
0f7ab71
Vibe coded type system, what could go wrong
timfennis Dec 8, 2025
f29ed20
Fixed return type inference by making it worse
timfennis Dec 8, 2025
6e3c788
Slightly improved error message
timfennis Dec 8, 2025
cdddca9
Fallback to normal ident if no function is found (RIP) and improved e…
timfennis Dec 8, 2025
6b471ec
Small pre changes
timfennis Dec 11, 2025
4365a05
Fixed some booges
timfennis Dec 11, 2025
ae5f20d
Added some add_span logic
timfennis Dec 11, 2025
e112950
Jan Itor
timfennis Dec 11, 2025
3f87cd4
WIP: adding back vectorization for the 12th time
timfennis Dec 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 14 additions & 8 deletions ndc_bin/src/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ pub fn docs(query: Option<&str>) -> anyhow::Result<()> {

let matched_functions = functions
.into_iter()
.flat_map(|x| x.implementations().collect::<Vec<_>>())
.filter(|(_, func)| {
.filter(|func| {
if let Some(query) = query {
string_match(query, func.name())
} else {
Expand All @@ -28,7 +27,7 @@ pub fn docs(query: Option<&str>) -> anyhow::Result<()> {
})
.collect::<Vec<_>>()
.tap_mut(|list| {
list.sort_by(|(_, l), (_, r)| {
list.sort_by(|l, r| {
if let Some(query) = query {
normalized_damerau_levenshtein(l.name(), query)
.partial_cmp(&normalized_damerau_levenshtein(r.name(), query))
Expand All @@ -46,31 +45,38 @@ pub fn docs(query: Option<&str>) -> anyhow::Result<()> {
skin.headers[1].align = Alignment::Left;
skin.headers[2].align = Alignment::Left;

for (type_sig, function) in matched_functions {
for function in matched_functions {
let mut signature = String::new();
let type_sig = function.type_signature();
match type_sig {
TypeSignature::Variadic => {
writeln!(signature, "(*args**)")?;
write!(signature, "(*args**)")?;
}
TypeSignature::Exact(params) => {
write!(signature, "(")?;
let mut param_iter = params.iter().peekable();
while let Some(Parameter { name, type_name }) = param_iter.next() {
write!(signature, "*{name}*: **{}**", type_name.as_str().green())?;
write!(
signature,
"*{name}*: **{}**",
format!("{}", type_name).green()
)?;

if param_iter.peek().is_some() {
write!(signature, ", ")?;
}
}

writeln!(signature, ")")?;
write!(signature, ")")?;
}
}
let name = function.name();
let documentation = function.documentation().trim();
let return_type = function.return_type();
let markdown = format!(
"---\n\n## **{}**{signature}\n{documentation}{}",
"---\n\n## **{}**{signature} -> {}\n\n{documentation}{}",
name.green(),
format!("{}", return_type).green().bold(),
if documentation.is_empty() { "" } else { "\n\n" }
);

Expand Down
1 change: 0 additions & 1 deletion ndc_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ thiserror.workspace = true
# Crypto
md5 = { version = "0.8.0", optional = true }
sha1 = { version = "0.10.6", optional = true }
log = "0.4.29"

[features]
default = ["ahash", "crypto"]
Expand Down
23 changes: 17 additions & 6 deletions ndc_lib/src/ast/expression.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
use crate::ast::operator::LogicalOperator;
use crate::ast::parser::Error as ParseError;
use crate::interpreter::evaluate::EvaluationError;
use crate::interpreter::function::StaticType;
use crate::lexer::Span;
use num::BigInt;
use num::complex::Complex64;

#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Binding {
None,
Resolved(ResolvedVar),
Dynamic(Vec<ResolvedVar>), // figure it out at runtime
}

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum ResolvedVar {
Captured { depth: usize, slot: usize },
Expand All @@ -28,7 +36,7 @@ pub enum Expression {
ComplexLiteral(Complex64),
Identifier {
name: String,
resolved: Option<ResolvedVar>,
resolved: Binding,
},
Statement(Box<ExpressionLocation>),
Logical {
Expand All @@ -49,14 +57,15 @@ pub enum Expression {
l_value: Lvalue,
r_value: Box<ExpressionLocation>,
operation: String,
resolved_assign_operation: Option<ResolvedVar>,
resolved_operation: Option<ResolvedVar>,
resolved_assign_operation: Binding,
resolved_operation: Binding,
},
FunctionDeclaration {
name: Option<String>,
resolved_name: Option<ResolvedVar>,
arguments: Box<ExpressionLocation>,
parameters: Box<ExpressionLocation>,
body: Box<ExpressionLocation>,
return_type: Option<StaticType>,
pure: bool,
},
Block {
Expand Down Expand Up @@ -332,15 +341,17 @@ impl std::fmt::Debug for ExpressionLocation {
.finish(),
Expression::FunctionDeclaration {
name,
arguments,
parameters,
return_type,
body,
pure,
resolved_name,
} => f
.debug_struct("FunctionDeclaration")
.field("name", name)
.field("resolved_name", resolved_name)
.field("arguments", arguments)
.field("parameters", parameters)
.field("return_type", return_type)
.field("body", body)
.field("pure", pure)
.finish(),
Expand Down
4 changes: 3 additions & 1 deletion ndc_lib/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ mod expression;
mod operator;
mod parser;

pub use expression::{Expression, ExpressionLocation, ForBody, ForIteration, Lvalue, ResolvedVar};
pub use expression::{
Binding, Expression, ExpressionLocation, ForBody, ForIteration, Lvalue, ResolvedVar,
};
pub use operator::{BinaryOperator, LogicalOperator, UnaryOperator};

pub use parser::Error;
Expand Down
23 changes: 12 additions & 11 deletions ndc_lib/src/ast/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt::Write;
use miette::Diagnostic;

use crate::ast::Expression;
use crate::ast::expression::{ExpressionLocation, ForBody, ForIteration, Lvalue};
use crate::ast::expression::{Binding, ExpressionLocation, ForBody, ForIteration, Lvalue};
use crate::ast::operator::{BinaryOperator, LogicalOperator, UnaryOperator};
use crate::lexer::{Span, Token, TokenLocation};

Expand Down Expand Up @@ -201,7 +201,7 @@ impl Parser {
function: Box::new(
Expression::Identifier {
name: operator_token_loc.token.to_string(),
resolved: None,
resolved: Binding::None,
}
.to_location(operator_token_loc.span),
),
Expand All @@ -214,7 +214,7 @@ impl Parser {
function: Box::new(
Expression::Identifier {
name: not_token.token.to_string(),
resolved: None,
resolved: Binding::None,
}
.to_location(not_token.span),
),
Expand Down Expand Up @@ -246,7 +246,7 @@ impl Parser {
function: Box::new(
Expression::Identifier {
name: operator.to_string(),
resolved: None,
resolved: Binding::None,
}
.to_location(operator_span),
),
Expand Down Expand Up @@ -385,8 +385,8 @@ impl Parser {
.expect("guaranteed to produce an lvalue"),
r_value: Box::new(expression),
operation: operation_identifier,
resolved_assign_operation: None,
resolved_operation: None,
resolved_assign_operation: Binding::None,
resolved_operation: Binding::None,
};

Ok(op_assign.to_location(start.merge(end)))
Expand Down Expand Up @@ -483,7 +483,7 @@ impl Parser {
function: Box::new(
Expression::Identifier {
name: operator_token_loc.token.to_string(),
resolved: None,
resolved: Binding::None,
}
.to_location(operator_span),
),
Expand Down Expand Up @@ -610,7 +610,7 @@ impl Parser {
function: Box::new(
Expression::Identifier {
name: operator_token_loc.token.to_string(),
resolved: None,
resolved: Binding::None,
}
.to_location(token_span),
),
Expand Down Expand Up @@ -689,7 +689,7 @@ impl Parser {
function: Box::new(
Expression::Identifier {
name: identifier,
resolved: None,
resolved: Binding::None,
}
.to_location(identifier_span),
),
Expand Down Expand Up @@ -961,7 +961,7 @@ impl Parser {
Token::String(value) => Expression::StringLiteral(value),
Token::Identifier(identifier) => Expression::Identifier {
name: identifier,
resolved: None,
resolved: Binding::None,
},
_ => {
// TODO: this error might not be the best way to describe what's happening here
Expand Down Expand Up @@ -1155,8 +1155,9 @@ impl Parser {
Ok(ExpressionLocation {
expression: Expression::FunctionDeclaration {
name: identifier,
arguments: Box::new(argument_list),
parameters: Box::new(argument_list),
body: Box::new(body),
return_type: None, // At some point in the future we could use type declarations here to insert the type (return type inference is cringe anyway)
pure: is_pure,
resolved_name: None,
},
Expand Down
47 changes: 8 additions & 39 deletions ndc_lib/src/interpreter/environment.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::interpreter::function::{Function, OverloadedFunction};
use crate::interpreter::function::{Function, StaticType};

use crate::ast::ResolvedVar;
use crate::interpreter::resolve::LexicalIdentifier;
use crate::interpreter::value::Value;
use std::cell::RefCell;
use std::fmt;
Expand All @@ -12,7 +11,7 @@ use std::rc::Rc;
pub struct RootEnvironment {
pub output: Box<dyn InterpreterOutput>,
// These are global values
global_functions: Vec<OverloadedFunction>,
global_functions: Vec<Function>,
}

pub struct Environment {
Expand Down Expand Up @@ -68,48 +67,27 @@ impl Environment {
env
}

pub fn create_global_scope_mapping(&self) -> Vec<LexicalIdentifier> {
pub fn get_global_identifiers(&self) -> Vec<(String, StaticType)> {
self.root
.borrow()
.global_functions
.iter()
.filter_map(|function| {
function.name().map(|name| LexicalIdentifier::Function {
name: name.to_string(),
arity: function.arity(),
})
})
.collect::<Vec<LexicalIdentifier>>()
.map(|function| (function.name().to_string(), function.static_type()))
.collect::<Vec<(String, StaticType)>>()
}

#[must_use]
pub fn get_all_functions(&self) -> Vec<OverloadedFunction> {
pub fn get_all_functions(&self) -> Vec<Function> {
self.root.borrow().global_functions.clone()
}

pub fn set(&mut self, var: ResolvedVar, value: Value) {
match var {
ResolvedVar::Captured { depth: 0, slot } => {
// This whole mess (special handling for functions) can be removed once we check
// types during the resolver pass
if let Value::Function(value_to_insert) = &value {
if let Some(existing_value) = self.values.get(slot) {
if let Value::Function(func) = existing_value {
func.borrow_mut().merge(&mut value_to_insert.borrow_mut())
}
} else {
self.values.push(value);
// self.values.insert(slot, value);
}

return;
}

if self.values.len() > slot {
self.values[slot] = value
} else {
debug_assert!(slot == self.values.len());
// self.values.insert(slot, value) // TODO: push should work here right
self.values.push(value);
}
}
Expand Down Expand Up @@ -139,18 +117,9 @@ impl Environment {
pub fn declare_global_fn(&mut self, function: impl Into<Function>) {
let new_function = function.into();

let gb = &mut self.root.borrow_mut().global_functions;

// Try to add it to an existing definition
for fun in gb.iter_mut() {
if fun.name() == Some(new_function.name()) && fun.arity() == new_function.arity() {
fun.add(new_function);
return;
}
}
let root: &mut RootEnvironment = &mut self.root.borrow_mut();

// Create a new definition
gb.push(OverloadedFunction::from_multiple(vec![new_function]))
root.global_functions.push(new_function.clone());
}

fn get_copy_from_slot(&self, depth: usize, slot: usize) -> Value {
Expand Down
6 changes: 3 additions & 3 deletions ndc_lib/src/interpreter/evaluate/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ pub fn get_at_index(
.clone())
}
_ => Err(EvaluationError::syntax_error(
format!("cannot insert into {} at index", lhs.value_type()),
format!("cannot insert into {} at index", lhs.static_type()),
span,
)
.into()),
Expand Down Expand Up @@ -294,7 +294,7 @@ pub fn set_at_index(
}
} else {
return Err(EvaluationError::syntax_error(
format!("cannot insert {} into a string", rhs.value_type()),
format!("cannot insert {} into a string", rhs.static_type()),
span,
)
.into());
Expand All @@ -317,7 +317,7 @@ pub fn set_at_index(
}
_ => {
return Err(EvaluationError::syntax_error(
format!("cannot insert into {} at index", lhs.value_type()),
format!("cannot insert into {} at index", lhs.static_type()),
span,
)
.into());
Expand Down
Loading
Loading