diff --git a/examples/fibonacci.ds b/examples/fibonacci.ds index c9aed88..17ab762 100644 --- a/examples/fibonacci.ds +++ b/examples/fibonacci.ds @@ -1,9 +1,9 @@ -fib = (i ) { +fib = (i , fib <(int) -> int>) { if i <= 1 { 1 } else { - self(i - 1) + self(i - 2) + fib(i - 1) + fib(i - 2) } } -fib(8) +fib(8, fib) diff --git a/src/abstract_tree/for.rs b/src/abstract_tree/for.rs index 14bbd53..57f9e7d 100644 --- a/src/abstract_tree/for.rs +++ b/src/abstract_tree/for.rs @@ -52,9 +52,38 @@ impl AbstractTree for For { fn run(&self, source: &str, context: &Map) -> Result { let expression_run = self.collection.run(source, context)?; - let values = expression_run.as_list()?.items(); let key = self.item_id.inner(); + if let Value::Range(range) = expression_run { + if self.is_async { + let mut iterable = Vec::with_capacity((range.end - range.start) as usize); + + for i in range.start..range.end { + iterable.push(Value::Integer(i)); + } + + iterable.par_iter().try_for_each(|value| { + let iter_context = Map::clone_from(context)?; + + iter_context.set(key.clone(), value.clone())?; + + self.block.run(source, &iter_context).map(|_value| ()) + })?; + } else { + let loop_context = Map::clone_from(context)?; + + for i in range.start..range.end { + loop_context.set(key.clone(), Value::Integer(i))?; + + self.block.run(source, &loop_context)?; + } + } + + return Ok(Value::none()); + } + + let values = expression_run.as_list()?.items(); + if self.is_async { values.par_iter().try_for_each(|value| { let iter_context = Map::clone_from(context)?; diff --git a/src/abstract_tree/function_call.rs b/src/abstract_tree/function_call.rs index 2ecd926..b743aca 100644 --- a/src/abstract_tree/function_call.rs +++ b/src/abstract_tree/function_call.rs @@ -96,7 +96,7 @@ impl AbstractTree for FunctionCall { if let Some((value, _)) = variables.get(key) { value.clone() } else { - return Err(Error::FunctionIdentifierNotFound( + return Err(Error::VariableIdentifierNotFound( identifier.inner().clone(), )); } diff --git a/src/abstract_tree/function_node.rs b/src/abstract_tree/function_node.rs index abf82e7..f48b928 100644 --- a/src/abstract_tree/function_node.rs +++ b/src/abstract_tree/function_node.rs @@ -26,12 +26,14 @@ impl FunctionNode { r#type: Type, syntax_position: SyntaxPosition, ) -> Self { - Self { + let context = Map::new(); + + FunctionNode { parameters, body, r#type, syntax_position, - context: Map::new(), + context, } } @@ -55,6 +57,10 @@ impl FunctionNode { &self.syntax_position } + pub fn context(&self) -> &Map { + &self.context + } + pub fn return_type(&self) -> &Type { match &self.r#type { Type::Function { @@ -122,12 +128,6 @@ impl AbstractTree for FunctionNode { let function_context = Map::new(); - for (key, (_value, r#type)) in outer_context.variables()?.iter() { - if r#type.is_function() { - function_context.set_type(key.clone(), r#type.clone())?; - } - } - for (parameter, parameter_type) in parameters.iter().zip(parameter_types.iter()) { function_context.set_type(parameter.inner().clone(), parameter_type.clone())?; } @@ -157,9 +157,9 @@ impl AbstractTree for FunctionNode { } fn run(&self, _source: &str, _context: &Map) -> Result { - Ok(Value::Function(Function::ContextDefined(Arc::new( - self.clone(), - )))) + let self_as_value = Value::Function(Function::ContextDefined(Arc::new(self.clone()))); + + Ok(self_as_value) } fn expected_type(&self, _context: &Map) -> Result { diff --git a/src/abstract_tree/index.rs b/src/abstract_tree/index.rs index 5f33b0c..59332dc 100644 --- a/src/abstract_tree/index.rs +++ b/src/abstract_tree/index.rs @@ -61,24 +61,32 @@ impl AbstractTree for Index { Ok(item) } Value::Map(map) => { - let value = if let IndexExpression::Identifier(identifier) = &self.index { + let (key, value) = if let IndexExpression::Identifier(identifier) = &self.index { let key = identifier.inner(); - - map.variables()? + let value = map + .variables()? .get(key) .map(|(value, _)| value.clone()) - .unwrap_or_default() - } else { - let value = self.index.run(source, context)?; - let key = value.as_string()?; + .unwrap_or_default(); - map.variables()? + (key.clone(), value) + } else { + let index_value = self.index.run(source, context)?; + let key = index_value.as_string()?; + let value = map + .variables()? .get(key.as_str()) .map(|(value, _)| value.clone()) - .unwrap_or_default() + .unwrap_or_default(); + + (key.clone(), value) }; - Ok(value) + if value.is_none() { + Err(Error::VariableIdentifierNotFound(key)) + } else { + Ok(value) + } } Value::String(string) => { let index = self.index.run(source, context)?.as_integer()? as usize; diff --git a/src/abstract_tree/value_node.rs b/src/abstract_tree/value_node.rs index 4b751d3..20e081f 100644 --- a/src/abstract_tree/value_node.rs +++ b/src/abstract_tree/value_node.rs @@ -50,6 +50,7 @@ impl AbstractTree for ValueNode { if current_node.is_named() { let expression = Expression::from_syntax(current_node, source, context)?; + expressions.push(expression); } } @@ -175,7 +176,7 @@ impl AbstractTree for ValueNode { _ => { return Err(Error::UnexpectedSyntaxNode { expected: - "string, integer, float, boolean, range, list, map, option or structure" + "string, integer, float, boolean, range, list, map, option, function or structure" .to_string(), actual: child.kind().to_string(), location: child.start_position(), diff --git a/src/error.rs b/src/error.rs index 3c85f26..f964869 100644 --- a/src/error.rs +++ b/src/error.rs @@ -160,9 +160,6 @@ pub enum Error { /// Failed to find a variable with a value for this key. VariableIdentifierNotFound(String), - /// Failed to find a variable with a function value for this key. - FunctionIdentifierNotFound(String), - /// The function failed due to an external error. External(String), @@ -179,6 +176,9 @@ pub enum Error { SerdeJson(String), ParserCancelled, + ExpectedIterable { + actual: Value, + }, } impl Error { @@ -391,14 +391,7 @@ impl fmt::Display for Error { "Expected a string, list, map or table, but got {actual}.", ) } - VariableIdentifierNotFound(key) => write!( - f, - "Variable identifier is not bound to anything by context: {key}.", - ), - FunctionIdentifierNotFound(key) => write!( - f, - "Function identifier is not bound to anything by context: {key}." - ), + VariableIdentifierNotFound(key) => write!(f, "Variable {key} does not exist.",), UnexpectedSyntaxNode { expected, actual, @@ -449,6 +442,9 @@ impl fmt::Display for Error { "Parsing was cancelled either manually or because it took too long." ), ExpectedFunctionType { actual } => write!(f, "Expected a function but got {actual}."), + ExpectedIterable { actual } => { + write!(f, "Expected an iterable value but got {actual}.") + } } } } diff --git a/src/main.rs b/src/main.rs index aa05dc7..f6be6bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,7 +43,7 @@ pub enum CliCommand { Format, /// Output a concrete syntax tree of the input. - Syntax, + Syntax { path: String }, } fn main() { @@ -87,7 +87,9 @@ fn main() { let mut interpreter = Interpreter::new(context); - if let Some(CliCommand::Syntax) = args.cli_command { + if let Some(CliCommand::Syntax { path }) = args.cli_command { + let source = read_to_string(path).unwrap(); + interpreter.parse(&source).unwrap(); println!("{}", interpreter.syntax_tree().unwrap()); @@ -96,6 +98,8 @@ fn main() { } if let Some(CliCommand::Format) = args.cli_command { + interpreter.parse(&source).unwrap(); + println!("{}", interpreter.format()); return; @@ -251,18 +255,32 @@ impl Completer for DustCompleter { let last_word = if let Some(word) = line.rsplit([' ', ':']).next() { word } else { - "" + line }; if let Ok(path) = PathBuf::try_from(last_word) { if let Ok(read_dir) = path.read_dir() { for entry in read_dir { if let Ok(entry) = entry { + let description = if let Ok(file_type) = entry.file_type() { + if file_type.is_dir() { + "directory" + } else if file_type.is_file() { + "file" + } else if file_type.is_symlink() { + "symlink" + } else { + "unknown" + } + } else { + "unknown" + }; + suggestions.push(Suggestion { - value: entry.file_name().into_string().unwrap(), - description: None, + value: entry.path().to_string_lossy().to_string(), + description: Some(description.to_string()), extra: None, - span: Span::new(pos, pos), + span: Span::new(pos - last_word.len(), pos), append_whitespace: false, }); } @@ -335,9 +353,12 @@ fn run_shell(context: Map) -> Result<()> { ReedlineEvent::Edit(vec![EditCommand::InsertString(" ".to_string())]), ); keybindings.add_binding( - KeyModifiers::CONTROL, - KeyCode::Char('h'), - ReedlineEvent::Menu("variable menu".to_string()), + KeyModifiers::NONE, + KeyCode::Tab, + ReedlineEvent::Multiple(vec![ + ReedlineEvent::Menu("context menu".to_string()), + ReedlineEvent::MenuNext, + ]), ); let edit_mode = Box::new(Emacs::new(keybindings)); @@ -357,8 +378,10 @@ fn run_shell(context: Map) -> Result<()> { .with_completer(Box::new(completer)) .with_menu(ReedlineMenu::EngineCompleter(Box::new( ColumnarMenu::default() - .with_name("variable menu") - .with_text_style(Style::new().fg(Color::White)), + .with_name("context menu") + .with_text_style(Style::new().fg(Color::White)) + .with_columns(1) + .with_column_padding(10), ))); let mut prompt = StarshipPrompt::new(); @@ -369,8 +392,6 @@ fn run_shell(context: Map) -> Result<()> { match sig { Ok(Signal::Success(buffer)) => { - prompt.reload(); - if buffer.trim().is_empty() { continue; } @@ -383,8 +404,10 @@ fn run_shell(context: Map) -> Result<()> { println!("{value}") } } - Err(error) => println!("Error: {error}"), + Err(error) => println!("{error}"), } + + prompt.reload(); } Ok(Signal::CtrlD) | Ok(Signal::CtrlC) => { println!("\nLeaving the Dust shell."); diff --git a/src/value/function.rs b/src/value/function.rs index 013cee3..cbdd701 100644 --- a/src/value/function.rs +++ b/src/value/function.rs @@ -35,6 +35,13 @@ impl Function { } } } + + pub fn set(&self, key: String, value: Value) -> Result> { + match self { + Function::BuiltIn(_) => todo!(), + Function::ContextDefined(function_node) => function_node.set(key, value), + } + } } impl Format for Function { diff --git a/src/value/map.rs b/src/value/map.rs index a98597c..ed30e2e 100644 --- a/src/value/map.rs +++ b/src/value/map.rs @@ -204,7 +204,7 @@ impl<'de> Visitor<'de> for MapVisitor { type Value = Map; fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("Any valid whale data.") + formatter.write_str("key-value pairs") } fn visit_map(self, mut access: M) -> std::result::Result diff --git a/tree-sitter-dust/corpus/examples.txt b/tree-sitter-dust/corpus/examples.txt new file mode 100644 index 0000000..814ed5f --- /dev/null +++ b/tree-sitter-dust/corpus/examples.txt @@ -0,0 +1,75 @@ +================================================================================ +Fibonacci +================================================================================ + +fib = (i ) { + if i <= 1 { + 1 + } else { + self(i - 1) + self(i - 2) + } +} + +-------------------------------------------------------------------------------- + +(root + (statement + (assignment + (identifier) + (assignment_operator) + (statement + (expression + (value + (function + (identifier) + (type_specification + (type)) + (type_specification + (type)) + (block + (statement + (if_else + (if + (expression + (logic + (expression + (identifier)) + (logic_operator) + (expression + (value + (integer))))) + (block + (statement + (expression + (value + (integer)))))) + (else + (block + (statement + (expression + (math + (expression + (function_call + (function_expression + (identifier)) + (expression + (math + (expression + (identifier)) + (math_operator) + (expression + (value + (integer))))))) + (math_operator) + (expression + (function_call + (function_expression + (identifier)) + (expression + (math + (expression + (identifier)) + (math_operator) + (expression + (value + (integer))))))))))))))))))))))