add function parameters and type declarations

This commit is contained in:
2024-10-16 01:14:26 -04:00
parent 5701bf8268
commit 34077344ca
4 changed files with 333 additions and 81 deletions

View File

@@ -1,4 +1,4 @@
use super::{Value, Type, FunctionDeclaration};
use super::{Value, Type, Function, FunctionType};
use super::parser::{ParseTree, ParseError};
use std::collections::HashMap;
@@ -17,13 +17,14 @@ pub enum RuntimeError {
FunctionUndefined(String),
NotAVariable(String),
ParseFail(String, Type),
TypeError(Type, Type),
IO(io::Error),
}
impl Display for RuntimeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ParseError(e) => write!(f, "{e}"),
Self::ParseError(e) => write!(f, "Parser Error: {e}"),
Self::NoOverloadForTypes(op, values)
=> write!(f, "No overload of `{op}` exists for the operands `[{}]`",
values.iter().map(|x| format!("{}({x})", x.get_type())).collect::<Vec<_>>().join(", ")),
@@ -34,6 +35,7 @@ impl Display for RuntimeError {
Self::NotAVariable(ident) => write!(f, "`{ident}` is a function but was attempted to be used like a variable"),
Self::ParseFail(s, t) => write!(f, "`\"{s}\"` couldn't be parsed into {}", t),
Self::IO(e) => write!(f, "{e}"),
Self::TypeError(left, right) => write!(f, "expected type `{left}` but got type `{right}`"),
}
}
}
@@ -50,12 +52,6 @@ enum Evaluation {
Uncomputed(Box<ParseTree>),
}
#[derive(Clone, Debug)]
struct Function {
decl: FunctionDeclaration,
body: Option<Box<ParseTree>>,
}
#[derive(Clone, Debug)]
enum Object {
Variable(Evaluation),
@@ -116,8 +112,8 @@ where
(Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)),
(Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))),
(Value::Array(x), y) => Ok(Value::Array([x, vec![y]].concat())),
(x, Value::Array(y)) => Ok(Value::Array([vec![x], y].concat())),
(Value::Array(_, x), y) => Ok(Value::Array(Type::Any, [x, vec![y]].concat())),
(x, Value::Array(_, y)) => Ok(Value::Array(Type::Any, [vec![x], y].concat())),
(x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y]))
},
ParseTree::Sub(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) {
@@ -235,19 +231,29 @@ where
self.exec(scope, &mut Cow::Borrowed(&locals))
}
},
ParseTree::FunctionDefinition(ident, args, body, scope) => {
let existing = locals.get(&ident).or(self.globals.get(&ident)).cloned();
ParseTree::FunctionDefinition(func, scope) => {
let ident = func.name.clone().unwrap();
let existing = locals.get(&ident)
.or(self.globals.get(&ident));
match existing {
Some(_) => Err(RuntimeError::ImmutableError(ident.clone())),
Some(Object::Function(f)) => {
if f.body.is_some() || f.arg_names.is_some() {
return Err(RuntimeError::ImmutableError(ident.clone()));
}
let new_func = Function::named(func.name.unwrap().as_str(), func.t.clone(), func.arg_names.clone(), func.body.clone());
let locals = locals.to_mut();
locals.insert(ident.clone(), Object::Function(new_func));
self.exec(scope, &mut Cow::Borrowed(&locals))
}
Some(Object::Variable(_)) => Err(RuntimeError::ImmutableError(ident.clone())),
None => {
let locals = locals.to_mut();
locals.insert(ident.clone(), Object::Function(Function {
decl: FunctionDeclaration { _name: ident.clone(), args },
body: Some(body)
}));
locals.insert(ident.clone(), Object::Function(func));
self.exec(scope, &mut Cow::Borrowed(&locals))
}
}
@@ -262,8 +268,9 @@ where
Value::Int(i) => i != 0,
Value::Bool(b) => b,
Value::String(s) => !s.is_empty(),
Value::Array(vec) => !vec.is_empty(),
Value::Array(_, vec) => !vec.is_empty(),
Value::Nil => false,
x => return Err(RuntimeError::NoOverloadForTypes("?".into(), vec![x])),
} {
self.exec(body, locals)
} else {
@@ -274,8 +281,9 @@ where
Value::Int(i) => i != 0,
Value::Bool(b) => b,
Value::String(s) => !s.is_empty(),
Value::Array(vec) => !vec.is_empty(),
Value::Array(_, vec) => !vec.is_empty(),
Value::Nil => false,
x => return Err(RuntimeError::NoOverloadForTypes("?".into(), vec![x])),
} {
self.exec(istrue, locals)
} else {
@@ -284,17 +292,26 @@ where
ParseTree::FunctionCall(ident, args) => {
let obj = locals.get(&ident).or(self.globals.get(&ident)).cloned();
if let Some(Object::Function(f)) = obj {
let locals = locals.to_mut();
let body = f.body.ok_or(RuntimeError::FunctionUndefined(ident.clone()))?;
match obj {
Some(Object::Function(f)) => {
let locals = locals.to_mut();
for (name, tree) in std::iter::zip(f.decl.args, args) {
locals.insert(name.clone(), Object::Variable(Evaluation::Computed(self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?)));
for ((t, name), tree) in std::iter::zip(std::iter::zip(f.t.1, f.arg_names.unwrap()), args) {
let v = self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?;
if v.get_type() != t && t != Type::Any {
return Err(RuntimeError::TypeError(t, v.get_type()));
}
locals.insert(name.clone(), match v {
Value::Function(func) => Object::Function(func),
_ => Object::Variable(Evaluation::Computed(v))
});
}
self.exec(f.body.unwrap(), &mut Cow::Borrowed(&locals))
}
self.exec(body, &mut Cow::Borrowed(&locals))
} else {
Err(RuntimeError::FunctionUndeclared(ident.clone()))
_ => Err(RuntimeError::FunctionUndefined(ident.clone()))
}
},
ParseTree::Variable(ident) => {
@@ -342,7 +359,7 @@ where
Value::Float(x) => Ok(Value::Bool(x != 0.0)),
Value::Bool(x) => Ok(Value::Bool(x)),
Value::String(x) => Ok(Value::Bool(!x.is_empty())),
Value::Array(vec) => Ok(Value::Bool(!vec.is_empty())),
Value::Array(_, vec) => Ok(Value::Bool(!vec.is_empty())),
x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])),
},
ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))),
@@ -352,6 +369,18 @@ where
Ok(Value::Nil)
}
}
ParseTree::FunctionDeclaration(func, scope) => {
let locals = locals.to_mut();
let name = func.name.clone().unwrap();
if locals.contains_key(&name) {
Err(RuntimeError::ImmutableError(name.clone()))
} else {
locals.insert(name, Object::Function(func));
self.exec(scope, &mut Cow::Borrowed(&locals))
}
}
ParseTree::LambdaDefinition(func) => Ok(Value::Function(func)),
}
}
}

View File

@@ -3,11 +3,12 @@ mod parser;
mod executor;
use executor::{Executor, RuntimeError};
use parser::Parser;
use parser::{ParseTree, Parser};
use tokenizer::Tokenizer;
use std::fmt::Display;
use std::io::{Write, Read, BufRead};
use std::fmt;
#[derive(Clone, Debug, PartialEq)]
pub enum Type {
@@ -15,21 +16,21 @@ pub enum Type {
Int,
Bool,
String,
Array,
_Function(Box<Type>, Vec<Type>),
Array(Box<Type>),
Function(FunctionType),
Nil,
Any,
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", match self {
Self::Float => "Float".into(),
Self::Int => "Int".into(),
Self::Bool => "Bool".into(),
Self::String => "String".into(),
Self::Array => format!("Array"),
Self::_Function(r, _) => format!("Function -> {}", *r),
Self::Array(t) => format!("[{t}]"),
Self::Function(r) => format!("{r}"),
Self::Nil => "Nil".into(),
Self::Any => "Any".into(),
})
@@ -43,7 +44,8 @@ pub enum Value {
Int(i64),
Bool(bool),
String(String),
Array(Vec<Value>),
Array(Type, Vec<Value>),
Function(Function),
Nil,
}
@@ -54,8 +56,9 @@ impl Value {
Self::Int(_) => Type::Int,
Self::Bool(_) => Type::Bool,
Self::String(_) => Type::String,
Self::Array(_) => Type::Array,
Self::Array(t, _) => Type::Array(Box::new(t.clone())),
Self::Nil => Type::Nil,
Self::Function(f) => Type::Function(f.t.clone()),
}
}
}
@@ -67,16 +70,54 @@ impl Display for Value {
Self::Int(x) => write!(f, "{x}"),
Self::Bool(x) => write!(f, "{}", if *x { "true" } else { "false" }),
Self::String(x) => write!(f, "\"{x}\""),
Self::Array(v) => write!(f, "[{}]", v.iter().map(|x| format!("{x}")).collect::<Vec<_>>().join(" ")),
Self::Array(_t, v) => write!(f, "[{}]", v.iter().map(|x| format!("{x}")).collect::<Vec<_>>().join(" ")),
Self::Function(func) => {
if let Some(name) = &func.name {
write!(f, "Function({}, {}, {})", name, func.t.0, func.t.1.iter().map(|x| format!("{x}")).collect::<Vec<_>>().join(", "))
} else {
write!(f, "{}", func.t)
}
}
Self::Nil => write!(f, "nil"),
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct FunctionDeclaration {
_name: String,
args: Vec<String>,
#[derive(Clone, Debug, PartialEq)]
pub struct FunctionType(Box<Type>, Vec<Type>);
impl Display for FunctionType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Function({}, {})", self.0, self.1.iter().map(|x| format!("{x}")).collect::<Vec<_>>().join(", "))
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Function {
name: Option<String>,
t: FunctionType,
arg_names: Option<Vec<String>>,
body: Option<Box<ParseTree>>,
}
impl Function {
pub fn lambda(t: FunctionType, arg_names: Vec<String>, body: Option<Box<ParseTree>>) -> Self {
Self {
name: None,
t,
arg_names: Some(arg_names),
body
}
}
pub fn named(name: &str, t: FunctionType, arg_names: Option<Vec<String>>, body: Option<Box<ParseTree>>) -> Self {
Self {
name: Some(name.to_string()),
t,
arg_names,
body
}
}
}
pub struct Runtime<'a, R: BufRead> {

View File

@@ -1,4 +1,4 @@
use super::{Type, Value, FunctionDeclaration};
use super::{Value, Type, Function, FunctionType};
use super::tokenizer::{Token, TokenizeError, Op};
use std::error;
@@ -15,7 +15,9 @@ pub enum ParseError {
FunctionUndefined(String),
VariableUndefined(String),
UnmatchedArrayClose,
UnwantedToken(Token),
TokenizeError(TokenizeError),
ImmutableError(String),
}
impl Display for ParseError {
@@ -28,14 +30,16 @@ impl Display for ParseError {
ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"),
ParseError::NoInput => write!(f, "No input given"),
ParseError::UnmatchedArrayClose => write!(f, "there was an unmatched array closing operator `]`"),
ParseError::TokenizeError(e) => write!(f, "{e}"),
ParseError::TokenizeError(e) => write!(f, "Tokenizer Error: {e}"),
ParseError::ImmutableError(i) => write!(f, "attempt to redeclare {i} met with force"),
ParseError::UnwantedToken(_t) => write!(f, "unexpected token"),
}
}
}
impl error::Error for ParseError {}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum ParseTree {
// Mathematical Operators
Add(Box<ParseTree>, Box<ParseTree>),
@@ -59,7 +63,9 @@ pub(crate) enum ParseTree {
// Defining Objects
Equ(String, Box<ParseTree>, Box<ParseTree>),
LazyEqu(String, Box<ParseTree>, Box<ParseTree>),
FunctionDefinition(String, Vec<String>, Box<ParseTree>, Box<ParseTree>),
FunctionDefinition(Function, Box<ParseTree>),
FunctionDeclaration(Function, Box<ParseTree>),
LambdaDefinition(Function),
// Functional Operations
Compose(Box<ParseTree>, Box<ParseTree>),
@@ -111,8 +117,8 @@ macro_rules! three_arg {
impl ParseTree {
fn parse<I>(
tokens: &mut I,
globals: &HashMap<String, FunctionDeclaration>,
locals: &mut Cow<HashMap<String, FunctionDeclaration>>) -> Result<Self, ParseError>
globals: &HashMap<String, Function>,
locals: &mut Cow<HashMap<String, Function>>) -> Result<Self, ParseError>
where
I: Iterator<Item = Result<Token, TokenizeError>>,
{
@@ -124,8 +130,8 @@ impl ParseTree {
// If it is found to be a function, get its argument count.
// During parsing, we only keep track of function definitions
// so that we know how many arguments it takes
if let Some(decl) = locals.clone().get(&ident).or(globals.clone().get(&ident)) {
let args = decl.args.iter()
if let Some(f) = locals.clone().get(&ident).or(globals.clone().get(&ident)) {
let args = f.t.1.iter()
.map(|_| ParseTree::parse(tokens, globals, locals)).collect::<Result<Vec<_>, ParseError>>()?;
Ok(ParseTree::FunctionCall(ident.clone(), args))
@@ -162,35 +168,59 @@ impl ParseTree {
Err(ParseError::InvalidIdentifier)
}
}
Op::FunctionDeclare(nargs) => {
Op::FunctionDefine(nargs) => {
let token = tokens.next()
.ok_or(ParseError::UnexpectedEndInput)?
.map_err(|e| ParseError::TokenizeError(e))?;
if let Token::Identifier(ident) = token {
let args: Vec<String> = tokens.take(nargs)
.map(|token| match token {
Ok(Token::Identifier(ident)) => Ok(ident),
Ok(_) => Err(ParseError::InvalidIdentifier),
Err(e) => Err(ParseError::TokenizeError(e)),
})
.collect::<Result<Vec<_>, ParseError>>()?;
if let Token::Identifier(ident) = token {
let args: Vec<String> = tokens.take(nargs)
.map(|token| match token {
Ok(Token::Identifier(ident)) => Ok(ident),
Ok(_) => Err(ParseError::InvalidIdentifier),
Err(e) => Err(ParseError::TokenizeError(e)),
})
.collect::<Result<Vec<_>, ParseError>>()?;
let f = if locals.contains_key(&ident) {
let locals = locals.to_mut();
let f = locals.get(&ident).unwrap();
let f = f.clone();
// iterate over f's types and push them
for (t, name) in std::iter::zip(f.t.1.clone(), args.clone()) {
match t {
Type::Function(finner) => {
locals.insert(name.clone(), Function::named(&name, finner, None, None));
}
_ => (),
}
}
Function::named(
&ident,
f.t.clone(),
Some(args),
Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?)))
} else {
let f = Function::named(
&ident,
FunctionType(Box::new(Type::Any), args.iter().map(|_| Type::Any).collect()),
Some(args),
Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?)));
let locals = locals.to_mut();
locals.insert(ident.clone(), FunctionDeclaration {
_name: ident.clone(),
args: args.clone(),
});
locals.insert(ident.clone(), f.clone());
Ok(ParseTree::FunctionDefinition(
ident,
args,
Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?),
Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?)))
} else {
Err(ParseError::InvalidIdentifier)
}
f
};
Ok(ParseTree::FunctionDefinition(f,
Box::new(ParseTree::parse(tokens, globals, locals)?)))
} else {
Err(ParseError::InvalidIdentifier)
}
}
Op::Compose => two_arg!(Compose, tokens, globals, locals),
Op::Id => one_arg!(Id, tokens, globals, locals),
@@ -232,25 +262,119 @@ impl ParseTree {
.collect::<Result<_, ParseError>>()?;
let tree = trees.into_iter().fold(
ParseTree::Constant(Value::Array(vec![])),
ParseTree::Constant(Value::Array(Type::Any, vec![])),
|acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())),
);
Ok(tree)
}
Op::Empty => Ok(ParseTree::Constant(Value::Array(vec![]))),
Op::Empty => Ok(ParseTree::Constant(Value::Array(Type::Any, vec![]))),
Op::CloseArray => Err(ParseError::UnmatchedArrayClose),
Op::NotEqualTo => two_arg!(NotEqualTo, tokens, globals, locals),
Op::And => two_arg!(And, tokens, globals, locals),
Op::Or => two_arg!(Or, tokens, globals, locals),
Op::FunctionDeclare(arg_count) => {
let name = match tokens.next()
.ok_or(ParseError::UnexpectedEndInput)?
.map_err(|e| ParseError::TokenizeError(e))?
{
Token::Identifier(x) => x,
_ => return Err(ParseError::InvalidIdentifier),
};
let args: Vec<Type> = (0..arg_count)
.map(|_| Self::parse_type(tokens))
.collect::<Result<_, ParseError>>()?;
let rett = Self::parse_type(tokens)?;
if locals.contains_key(&name) {
println!("{name} already found: {locals:?}");
return Err(ParseError::ImmutableError(name.clone()));
}
let f = Function::named(
&name,
FunctionType(Box::new(rett), args),
None,
None);
let locals = locals.to_mut();
locals.insert(name, f.clone());
Ok(ParseTree::FunctionDeclaration(
f,
Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?)))
}
Op::LambdaDefine(arg_count) => {
let args: Vec<String> = tokens.take(arg_count)
.map(|token| match token {
Ok(Token::Identifier(ident)) => Ok(ident),
Ok(_) => Err(ParseError::InvalidIdentifier),
Err(e) => Err(ParseError::TokenizeError(e)),
})
.collect::<Result<Vec<_>, ParseError>>()?;
Ok(ParseTree::LambdaDefinition(
Function::lambda(
FunctionType(Box::new(Type::Any), args.clone().into_iter().map(|_| Type::Any).collect()),
args,
Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?)))))
}
Op::NonCall => {
let ident = match tokens.next().ok_or(ParseError::UnexpectedEndInput)?
.map_err(|e| ParseError::TokenizeError(e))?
{
Token::Identifier(x) => x,
_ => return Err(ParseError::InvalidIdentifier),
};
if let Some(f) = locals.clone().get(&ident).or(globals.clone().get(&ident)).cloned() {
Ok(ParseTree::Constant(Value::Function(f)))
} else {
Err(ParseError::FunctionUndefined(ident.clone()))
}
}
}
}
t => Err(ParseError::UnwantedToken(t)),
}
},
Some(Err(e)) => Err(ParseError::TokenizeError(e)),
None => Err(ParseError::NoInput),
}
}
fn parse_type<I>(tokens: &mut I) -> Result<Type, ParseError>
where
I: Iterator<Item = Result<Token, TokenizeError>>,
{
match tokens.next() {
Some(Ok(Token::Type(t))) => Ok(t),
Some(Ok(Token::Operator(Op::FunctionDefine(n)))) => {
let args: Vec<Type> = (0..n)
.map(|_| Self::parse_type(tokens))
.collect::<Result<_, ParseError>>()?;
let rett = Self::parse_type(tokens)?;
Ok(Type::Function(FunctionType(Box::new(rett), args.clone())))
},
Some(Ok(Token::Operator(Op::OpenArray))) => {
let t = Self::parse_type(tokens)?;
let _ = match tokens.next() {
Some(Ok(Token::Operator(Op::CloseArray))) => (),
_ => return Err(ParseError::UnmatchedArrayClose),
};
Ok(Type::Array(Box::new(t)))
}
Some(Ok(t)) => Err(ParseError::UnwantedToken(t.clone())),
Some(Err(e)) => Err(ParseError::TokenizeError(e)),
None => Err(ParseError::UnexpectedEndInput),
}
}
}
/// Parses input tokens and produces ParseTrees for an Executor
@@ -260,8 +384,8 @@ pub(crate) struct Parser<I: Iterator<Item = Result<Token, TokenizeError>>> {
// These are used to keep track of functions in the current context
// by the parser. otherwise the parser would have no way to tell
// if the program `* a b 12` is supposed to be ((* a b) (12)) or (* (a b) 12)
globals: HashMap<String, FunctionDeclaration>,
locals: HashMap<String, FunctionDeclaration>,
globals: HashMap<String, Function>,
locals: HashMap<String, Function>,
}
impl<I: Iterator<Item = Result<Token, TokenizeError>>> Parser<I> {
@@ -273,7 +397,7 @@ impl<I: Iterator<Item = Result<Token, TokenizeError>>> Parser<I> {
}
}
pub fn globals(self, globals: HashMap<String, FunctionDeclaration>) -> Self {
pub fn globals(self, globals: HashMap<String, Function>) -> Self {
Self {
tokens: self.tokens,
globals,
@@ -281,7 +405,7 @@ impl<I: Iterator<Item = Result<Token, TokenizeError>>> Parser<I> {
}
}
pub fn locals(self, locals: HashMap<String, FunctionDeclaration>) -> Self {
pub fn locals(self, locals: HashMap<String, Function>) -> Self {
Self {
tokens: self.tokens,
globals: self.globals,

View File

@@ -2,6 +2,8 @@ use std::iter::Peekable;
use std::{error, io};
use std::collections::{VecDeque, HashMap};
use crate::Type;
use super::Value;
use std::fmt::{Display, Formatter};
use std::io::{BufRead, Cursor};
@@ -49,7 +51,9 @@ pub(crate) enum Op {
Equ,
Mod,
LazyEqu,
FunctionDefine(usize),
FunctionDeclare(usize),
LambdaDefine(usize),
Compose,
Id,
If,
@@ -71,6 +75,7 @@ pub(crate) enum Op {
Empty,
And,
Or,
NonCall,
}
#[derive(Debug, Clone)]
@@ -78,6 +83,7 @@ pub(crate) enum Token {
Identifier(String),
Operator(Op),
Constant(Value),
Type(Type),
}
fn get_dot_count<I: Iterator<Item = char>>(s: &mut Peekable<I>) -> Option<usize> {
@@ -111,6 +117,13 @@ impl Token {
"print" => Ok(Token::Operator(Op::Print)),
"empty" => Ok(Token::Operator(Op::Empty)),
// Types
"Any" => Ok(Token::Type(Type::Any)),
"Int" => Ok(Token::Type(Type::Int)),
"Float" => Ok(Token::Type(Type::Float)),
"Bool" => Ok(Token::Type(Type::Bool)),
"String" => Ok(Token::Type(Type::String)),
// then identifiers and numbers
_ => {
if identifier.is_match(s) {
@@ -156,7 +169,9 @@ impl<R: BufRead> Tokenizer<R> {
("%", Op::Mod),
("=", Op::Equ),
(".", Op::LazyEqu),
(":", Op::FunctionDeclare(1)),
(":", Op::FunctionDefine(1)),
("?:", Op::FunctionDeclare(1)),
(";", Op::LambdaDefine(1)),
("~", Op::Compose),
(",", Op::Id),
("?", Op::If),
@@ -172,6 +187,7 @@ impl<R: BufRead> Tokenizer<R> {
("!", Op::Not),
("&&", Op::And),
("||", Op::Or),
("'", Op::NonCall),
]);
let c = if let Some(c) = iter.next() {
@@ -256,6 +272,26 @@ impl<R: BufRead> Tokenizer<R> {
};
Op::FunctionDeclare(n + count)
}
Op::FunctionDefine(n) => {
let count = match get_dot_count(&mut iter) {
Some(count) => count,
None => {
self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token)));
return;
}
};
Op::FunctionDefine(n + count)
}
Op::LambdaDefine(n) => {
let count = match get_dot_count(&mut iter) {
Some(count) => count,
None => {
self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token)));
return;
}
};
Op::LambdaDefine(n + count)
}
op => op.clone(),
})));
@@ -327,3 +363,25 @@ impl<R: BufRead> std::iter::Iterator for Tokenizer<R> {
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use crate::parser::{Parser, ParseTree, ParseError};
use super::*;
#[test]
fn uwu() {
let program = "?:. apply : Any Any Any Any :. apply f x f x : id x x apply ; x id x 12";
let tokens: Vec<Token> = Tokenizer::from_str(program).unwrap().collect::<Result<_, TokenizeError>>().unwrap();
println!("{tokens:?}");
let trees: Result<Vec<ParseTree>, ParseError> = Parser::new(tokens.into_iter().map(|x| Ok(x))).collect();
println!("{trees:?}");
}
}