Add function/macro docstrings

This commit is contained in:
2026-05-21 09:26:55 +03:00
parent 92d0a80fb1
commit 43ff442d89
10 changed files with 171 additions and 135 deletions
+9 -2
View File
@@ -26,6 +26,7 @@ use crate::{
pub struct FunctionBlock {
parent: Option<usize>,
identifier: Option<IdentifierValue>,
docstring: Option<StringValue>,
// Data
pub(crate) constants: Vec<Value>,
@@ -107,11 +108,12 @@ impl CompileContext {
pub fn compile_function(
&mut self,
identifier: Option<IdentifierValue>,
docstring: Option<StringValue>,
signature: &FunctionSignature,
body: &FunctionBody,
) -> Result<usize, CompileError> {
// TODO signature
let index = self.push_lambda_context(identifier, signature)?;
let index = self.push_lambda_context(identifier, docstring, signature)?;
for expression in body.head.iter() {
self.compile_statement(expression)?;
}
@@ -130,12 +132,13 @@ impl CompileContext {
pub fn push_lambda_context(
&mut self,
identifier: Option<IdentifierValue>,
docstring: Option<StringValue>,
signature: &FunctionSignature,
) -> Result<usize, CompileError> {
if self.options.trace_compile {
eprintln!("COMPILE: push_lambda_context({identifier:?})");
}
let block = FunctionBlock::new(Some(self.current), identifier, signature);
let block = FunctionBlock::new(Some(self.current), identifier, docstring, signature);
let index = self.function_blocks.len();
self.function_blocks.push(block);
self.current = index;
@@ -353,11 +356,13 @@ impl FunctionBlock {
fn new(
parent: Option<usize>,
identifier: Option<IdentifierValue>,
docstring: Option<StringValue>,
signature: &FunctionSignature,
) -> Self {
let mut block = Self {
parent,
identifier,
docstring,
constants: vec![],
locals: vec![],
upvalues: vec![],
@@ -379,6 +384,7 @@ impl FunctionBlock {
Self::new(
None,
identifier,
None,
&FunctionSignature {
required_arguments: vec![],
optional_arguments: vec![],
@@ -481,6 +487,7 @@ impl FunctionBlock {
Ok(Rc::new(BytecodeFunction {
identifier: self.identifier.clone(),
instructions: instructions.into(),
docstring: self.docstring.clone(),
constants: self.constants.iter().cloned().collect(),
upvalues: self.upvalues.iter().copied().collect(),
arity: self.arity,
+18 -3
View File
@@ -66,14 +66,24 @@ impl Compile for CallExpression {
impl Compile for LambdaExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let function = cx.compile_function(Some("lambda".into()), &self.signature, &self.body)?;
let function = cx.compile_function(
Some("lambda".into()),
self.docstring.clone(),
&self.signature,
&self.body,
)?;
Ok(CompileValue::LocalFunction(function))
}
}
impl Compile for DefunExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let function = cx.compile_function(Some(self.name.clone()), &self.signature, &self.body)?;
let function = cx.compile_function(
Some(self.name.clone()),
self.docstring.clone(),
&self.signature,
&self.body,
)?;
cx.compile_set_global(self.name.clone(), CompileValue::LocalFunction(function))?;
Ok(CompileValue::Nil)
}
@@ -81,7 +91,12 @@ impl Compile for DefunExpression {
impl Compile for DefmacroExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let function = cx.compile_function(Some(self.name.clone()), &self.signature, &self.body)?;
let function = cx.compile_function(
Some(self.name.clone()),
self.docstring.clone(),
&self.signature,
&self.body,
)?;
cx.compile_declare_macro(self.name.clone(), CompileValue::LocalFunction(function))?;
Ok(CompileValue::Nil)
}
+109 -3
View File
@@ -5,9 +5,10 @@ use crate::{
function::FunctionSignature,
syntax::{
CollectErrors, ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind,
maybe_docstring,
},
},
vm::value::{ConsCell, IdentifierValue, Keyword, Value},
vm::value::{ConsCell, IdentifierValue, Keyword, StringValue, Value},
};
#[derive(Debug, PartialEq)]
@@ -16,9 +17,17 @@ pub struct FunctionBody {
pub tail: Rc<Expression>,
}
#[derive(Debug, PartialEq)]
pub struct LambdaExpression {
pub docstring: Option<StringValue>,
pub signature: FunctionSignature,
pub body: FunctionBody,
}
#[derive(Debug, PartialEq)]
pub struct DefunExpression {
pub name: IdentifierValue,
pub docstring: Option<StringValue>,
pub signature: FunctionSignature,
pub body: FunctionBody,
}
@@ -185,6 +194,29 @@ impl PrognExpression {
}
}
impl LambdaExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let Value::Cons(value) = value else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::ArgumentList,
ExpectedWhere::AfterKeyword(Keyword::Lambda),
),
});
};
let ConsCell(car, cdr) = value.as_ref();
let signature = FunctionSignature::parse(car, input)?;
let (cdr, docstring) = maybe_docstring(cdr);
let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?;
Ok(Self {
docstring,
signature,
body,
})
}
}
impl DefunExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let Value::Cons(value) = value else {
@@ -218,11 +250,12 @@ impl DefunExpression {
});
};
let ConsCell(car, cdr) = cdr.as_ref();
let signature = FunctionSignature::parse(car, input)?;
let (cdr, docstring) = maybe_docstring(cdr);
let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?;
Ok(Self {
name: identifier.clone(),
docstring,
signature,
body,
})
@@ -240,6 +273,12 @@ impl CollectErrors<ParseError> for FunctionBody {
}
}
impl CollectErrors<ParseError> for LambdaExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.body.collect_errors(errors)
}
}
impl CollectErrors<ParseError> for DefunExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.body.collect_errors(errors)
@@ -260,12 +299,79 @@ mod tests {
compile::{
function::FunctionSignature,
syntax::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
CallExpression, ExpectedWhat, ExpectedWhere, Expression, FunctionBody,
LambdaExpression, ParseError, ParseErrorKind,
},
},
vm::value::{Keyword, Value},
};
#[test]
fn test_parse_lambda() {
let args = Value::list_or_nil([
Value::Identifier("a".into()),
Value::Keyword(Keyword::Optional),
Value::Identifier("b".into()),
Value::Keyword(Keyword::Rest),
Value::Identifier("c".into()),
]);
let body = Value::list_or_nil([
Value::Identifier("+".into()),
Value::Identifier("a".into()),
Value::Number(1.into()),
]);
let lambda = Value::Keyword(Keyword::Lambda).cons(args.cons(body.cons(Value::Nil)));
let expr = Expression::parse(&lambda).unwrap();
assert_eq!(
expr.as_ref(),
&Expression::Lambda(LambdaExpression {
signature: FunctionSignature {
required_arguments: vec!["a".into()],
optional_arguments: vec!["b".into()],
rest_argument: Some("c".into())
},
body: FunctionBody {
head: vec![],
tail: Expression::Call(CallExpression {
callee: Expression::Identifier("+".into()).into(),
arguments: vec![
Rc::new(Expression::Identifier("a".into())),
Rc::new(Expression::IntegerLiteral(1.into()))
]
})
.into()
}
})
);
// syntax errors
let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda)]);
let e = Expression::parse(&lambda).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: lambda,
error: ParseErrorKind::Expected(
ExpectedWhat::ArgumentList,
ExpectedWhere::AfterKeyword(Keyword::Lambda)
)
}]
);
let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda), Value::Nil]);
let e = Expression::parse(&lambda).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: lambda,
error: ParseErrorKind::Expected(
ExpectedWhat::Expression,
ExpectedWhere::AfterArgumentList(Keyword::Lambda)
)
}]
);
}
#[test]
fn test_parse_function_body() {
let v = Value::list_or_nil([Value::Number(1.into())]);
-121
View File
@@ -1,121 +0,0 @@
use crate::{
compile::{
function::FunctionSignature,
syntax::{
CollectErrors, ExpectedWhat, ExpectedWhere, FunctionBody, ParseError, ParseErrorKind,
},
},
vm::value::{ConsCell, Keyword, Value},
};
#[derive(Debug, PartialEq)]
pub struct LambdaExpression {
pub signature: FunctionSignature,
pub body: FunctionBody,
}
impl LambdaExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let Value::Cons(value) = value else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::ArgumentList,
ExpectedWhere::AfterKeyword(Keyword::Lambda),
),
});
};
let ConsCell(car, cdr) = value.as_ref();
let signature = FunctionSignature::parse(car, input)?;
let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?;
Ok(Self { signature, body })
}
}
impl CollectErrors<ParseError> for LambdaExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.body.collect_errors(errors)
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use crate::{
compile::{
function::FunctionSignature,
syntax::{
CallExpression, ExpectedWhat, ExpectedWhere, Expression, FunctionBody,
LambdaExpression, ParseError, ParseErrorKind,
},
},
vm::value::{Keyword, Value},
};
#[test]
fn test_parse_lambda() {
let args = Value::list_or_nil([
Value::Identifier("a".into()),
Value::Keyword(Keyword::Optional),
Value::Identifier("b".into()),
Value::Keyword(Keyword::Rest),
Value::Identifier("c".into()),
]);
let body = Value::list_or_nil([
Value::Identifier("+".into()),
Value::Identifier("a".into()),
Value::Number(1.into()),
]);
let lambda = Value::Keyword(Keyword::Lambda).cons(args.cons(body.cons(Value::Nil)));
let expr = Expression::parse(&lambda).unwrap();
assert_eq!(
expr.as_ref(),
&Expression::Lambda(LambdaExpression {
signature: FunctionSignature {
required_arguments: vec!["a".into()],
optional_arguments: vec!["b".into()],
rest_argument: Some("c".into())
},
body: FunctionBody {
head: vec![],
tail: Expression::Call(CallExpression {
callee: Expression::Identifier("+".into()).into(),
arguments: vec![
Rc::new(Expression::Identifier("a".into())),
Rc::new(Expression::IntegerLiteral(1.into()))
]
})
.into()
}
})
);
// syntax errors
let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda)]);
let e = Expression::parse(&lambda).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: lambda,
error: ParseErrorKind::Expected(
ExpectedWhat::ArgumentList,
ExpectedWhere::AfterKeyword(Keyword::Lambda)
)
}]
);
let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda), Value::Nil]);
let e = Expression::parse(&lambda).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: lambda,
error: ParseErrorKind::Expected(
ExpectedWhat::Expression,
ExpectedWhere::AfterArgumentList(Keyword::Lambda)
)
}]
);
}
}
+5 -1
View File
@@ -4,14 +4,16 @@ use crate::{
syntax::{
CollectErrors, FunctionBody,
error::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
maybe_docstring,
},
},
vm::value::{ConsCell, IdentifierValue, Keyword, Value},
vm::value::{ConsCell, IdentifierValue, Keyword, StringValue, Value},
};
#[derive(Debug, PartialEq)]
pub struct DefmacroExpression {
pub name: IdentifierValue,
pub docstring: Option<StringValue>,
pub signature: FunctionSignature,
pub body: FunctionBody,
}
@@ -51,9 +53,11 @@ impl DefmacroExpression {
let ConsCell(car, cdr) = cdr.as_ref();
let signature = FunctionSignature::parse(car, input)?;
let (cdr, docstring) = maybe_docstring(cdr);
let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?;
Ok(Self {
name: identifier.clone(),
docstring,
signature,
body,
})
+8 -2
View File
@@ -9,7 +9,6 @@ mod call;
mod condition;
mod error;
mod function;
mod lambda;
mod loops;
mod macros;
@@ -18,7 +17,6 @@ pub use call::*;
pub use condition::*;
pub use error::*;
pub use function::*;
pub use lambda::*;
pub use loops::*;
pub use macros::*;
@@ -161,6 +159,14 @@ impl CollectErrors<ParseError> for Expression {
}
}
fn maybe_docstring(input: &Value) -> (&Value, Option<StringValue>) {
if let Some((Value::String(docstring), cdr)) = input.uncons_ref() {
(cdr, Some(docstring.clone()))
} else {
(input, None)
}
}
#[cfg(test)]
mod tests {
use crate::{
+5 -1
View File
@@ -88,7 +88,11 @@ fn handle_eval_error(value: Option<&Value>, input: MachineErrorAt) -> Error {
eprintln!();
}
match input.error {
MachineError::Compile(_) => todo!(),
MachineError::Compile(error) => {
eprintln!("Compilation error:");
eprintln!();
eprintln!(":: {error}");
}
MachineError::Read(error) => {
eprintln!("Syntax error:");
eprintln!();
+4
View File
@@ -240,6 +240,10 @@ impl Machine {
}
};
if argument_count != closure.function.arity {
todo!("TODO error here")
}
if self.trace_calls {
eprintln!("TRACE: Call closure");
eprintln!("TRACE: {closure}");
+6 -2
View File
@@ -12,6 +12,7 @@ use crate::{
#[derive(Debug, PartialEq)]
pub struct BytecodeFunction {
pub identifier: Option<IdentifierValue>,
pub docstring: Option<StringValue>,
pub instructions: Box<[u8]>,
pub constants: Box<[Value]>,
pub upvalues: Box<[UpvalueDef]>,
@@ -40,8 +41,11 @@ impl BytecodeFunction {
}
}
pub fn docstring(&self) -> &StringValue {
todo!()
pub fn docstring(&self) -> &str {
match self.docstring.as_ref() {
Some(docstring) => docstring.as_ref(),
None => "",
}
}
fn trace_immediate_integer_at(&self, address: usize) -> Option<ImmediateInteger> {
+7
View File
@@ -71,6 +71,13 @@ impl Value {
}
}
pub fn uncons_ref(&self) -> Option<(&Value, &Value)> {
match self {
Self::Cons(cons) => Some((&cons.0, &cons.1)),
_ => None,
}
}
pub fn quote(self) -> Value {
Value::Quote(self.into())
}