Add function/macro docstrings
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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())]);
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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
@@ -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!();
|
||||
|
||||
@@ -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}");
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user