Modify library for new parser

This commit is contained in:
Jeff 2023-09-28 15:58:01 -04:00
parent 5fed0984a7
commit 589d66a90f
8 changed files with 175 additions and 5192 deletions

5033
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +0,0 @@
[package]
name = "dust-lang"
version = "0.1.2"
description = "Data-oriented programming language and interactive shell."
authors = ["jeff <dev@jeffa.io.com>"]
repository = "https://git.jeffa.io/jeff/dust.git"
homepage = "https://git.jeffa.io/jeff/dust"
readme = "README.md"
license = "MIT"
edition = "2018"
default-run = "dust"
[[bin]]
name = "dust"
[[bin]]
name = "gui"
[lib]
name = "dust_lib"
path = "src/lib.rs"
[dependencies]
rand = "0.8.5"
chrono = "0.4.26"
trash = "3.0.3"
rayon = "1.7.0"
serde = { version = "1.0.171", features = ["derive"] }
sysinfo = "0.29.6"
toml = "0.7.6"
toml_edit = "0.19.14"
comfy-table = "7.0.1"
clap = { version = "4.3.19", features = ["derive"] }
git2 = "0.17.2"
csv = "1.2.2"
json = "0.12.4"
reqwest = { version = "0.11.18", features = ["blocking", "json"] }
serde_json = "1.0.104"
egui_extras = "0.22.0"
rustyline = { version = "12.0.0", features = ["with-file-history", "derive"] }
ansi_term = "0.12.1"
iced = "0.10.0"
egui = "0.22.0"
eframe = "0.22.0"
env_logger = "0.10.0"
once_cell = "1.18.0"

40
build.rs Normal file
View File

@ -0,0 +1,40 @@
fn main() {
let src_dir = std::path::Path::new("src");
let mut c_config = cc::Build::new();
c_config.include(&src_dir);
c_config
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-but-set-variable")
.flag_if_supported("-Wno-trigraphs");
let parser_path = src_dir.join("parser.c");
c_config.file(&parser_path);
// If your language uses an external scanner written in C,
// then include this block of code:
/*
let scanner_path = src_dir.join("scanner.c");
c_config.file(&scanner_path);
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
*/
c_config.compile("parser");
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
// If your language uses an external scanner written in C++,
// then include this block of code:
/*
let mut cpp_config = cc::Build::new();
cpp_config.cpp(true);
cpp_config.include(&src_dir);
cpp_config
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-but-set-variable");
let scanner_path = src_dir.join("scanner.cc");
cpp_config.file(&scanner_path);
cpp_config.compile("scanner");
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
*/
}

View File

@ -1,88 +0,0 @@
use dust_lib::eval;
use iced::widget::{column, container, text_input, Column};
use iced::{executor, Application, Command, Element, Settings, Theme};
use once_cell::sync::Lazy;
static INPUT_ID: Lazy<text_input::Id> = Lazy::new(text_input::Id::unique);
pub fn main() -> iced::Result {
DustGui::run(Settings::default())
}
struct DustGui {
text_buffer: String,
results: Vec<String>,
}
impl Application for DustGui {
type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
type Flags = ();
fn new(_flags: Self::Flags) -> (Self, iced::Command<Self::Message>) {
(
DustGui {
text_buffer: String::new(),
results: Vec::new(),
},
Command::none(),
)
}
fn title(&self) -> String {
"Dust".to_string()
}
fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> {
match message {
Message::TextInput(input) => {
self.text_buffer = input;
Command::none()
}
Message::Evaluate => {
let eval_result = eval(&self.text_buffer);
match eval_result {
Ok(result) => self.results.push(result.to_string()),
Err(error) => self.results.push(error.to_string()),
}
Command::batch(vec![])
}
}
}
fn view(&self) -> Element<'_, Self::Message, iced::Renderer<Self::Theme>> {
let input = text_input("What needs to be done?", &self.text_buffer)
.id(INPUT_ID.clone())
.on_input(Message::TextInput)
.on_submit(Message::Evaluate)
.padding(15)
.size(30);
let result_display: Column<Message> = {
let mut text_widgets = Vec::new();
for _result in &self.results {
// text_widgets.push(text(result).style().into());
}
text_widgets.reverse();
Column::with_children(text_widgets)
};
container(column![input, result_display]).into()
}
}
#[derive(Debug, Clone)]
enum Message {
TextInput(String),
Evaluate,
}

View File

@ -14,6 +14,13 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Error {
UnexpectedSourceNode {
expected: &'static str,
actual: &'static str,
},
ExpectedFieldName,
/// Dust's internal type checking failed to identify a type mismatch. This should never happen, /// the error prompts the user to report the bug.
TypeCheckFailure {
tool_info: ToolInfo<'static>,
@ -628,6 +635,8 @@ impl fmt::Display for Error {
macro_info.identifier,
macro_info.inputs
),
UnexpectedSourceNode { expected, actual } => write!(f, "Unexpected source node. Expected {expected}, but found {actual}."),
ExpectedFieldName => write!(f, "Expected a field name for this node, but none was found."),
}
}
}

View File

@ -1,6 +1,10 @@
//! The top level of Dust's API with functions in interpret Dust code.
use crate::{token, tree, Result, Value, VariableMap};
use std::ops::Range;
use tree_sitter::{Parser, TreeCursor};
use crate::{language, token, tree, Error, Result, Value, VariableMap};
/// Evaluate the given expression string.
///
@ -12,9 +16,10 @@ use crate::{token, tree, Result, Value, VariableMap};
/// ```
///
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
pub fn eval(string: &str) -> Result<Value> {
pub fn eval(source: &str) -> Result<Value> {
let mut context = VariableMap::new();
eval_with_context(string, &mut context)
eval_with_context(source, &mut context)
}
/// Evaluate the given expression string with the given context.
@ -30,32 +35,93 @@ pub fn eval(string: &str) -> Result<Value> {
/// assert_eq!(eval_with_context("one + two + three", &mut context), Ok(Value::from(6)));
/// ```
pub fn eval_with_context(input: &str, context: &mut VariableMap) -> Result<Value> {
let without_comments = input
.lines()
.map(|line| {
let split = line.split_once('#');
let mut parser = Parser::new();
if let Some((code, _comment)) = split {
code
} else {
line
}
})
.collect::<String>();
parser.set_language(language()).unwrap();
let split = without_comments.split_once("->");
let tree = parser.parse(input, None).unwrap();
let sexp = tree.root_node().to_sexp();
if let Some((left, right)) = split {
let left_result = tree::tokens_to_operator_tree(token::tokenize(left)?)?
.eval_with_context_mut(context)?;
println!("{sexp}");
context.set_value("input", left_result)?;
let mut cursor = tree.walk();
let right_result = eval_with_context(right, context)?;
cursor.goto_first_child();
Ok(right_result)
} else {
tree::tokens_to_operator_tree(token::tokenize(&without_comments)?)?
.eval_with_context_mut(context)
let statement = Statement::from_cursor(cursor);
println!("{statement:?}");
Ok(Value::Empty)
}
#[derive(Debug)]
struct EvalTree {
root: Source,
}
#[derive(Debug)]
enum Source {
Comment(String),
Statement(Statement),
}
#[derive(Debug)]
enum Statement {
Closed(Expression),
}
impl Statement {
fn from_cursor(mut cursor: TreeCursor) -> Result<Self> {
let node = cursor.node();
cursor.goto_first_child();
if node.kind() == "statement" {
Ok(Statement::Closed(Expression::from_cursor(cursor)?))
} else {
Err(Error::UnexpectedSourceNode {
expected: "statement",
actual: node.kind(),
})
}
}
}
#[derive(Debug)]
enum Expression {
Identifier(&'static str),
Value(Range<usize>),
}
impl Expression {
fn from_cursor(mut cursor: TreeCursor) -> Result<Self> {
let parent = cursor.node();
cursor.goto_first_child();
let child = cursor.node();
if parent.kind() == "expression" {
if child.kind() == "identifier" {
if let Some(name) = cursor.field_name() {
Ok(Expression::Identifier(name))
} else {
Err(Error::ExpectedFieldName)
}
} else if child.kind() == "value" {
Ok(Self::Value(child.byte_range()))
} else {
Err(Error::UnexpectedSourceNode {
expected: "identifier or value",
actual: child.kind(),
})
}
} else {
Err(Error::UnexpectedSourceNode {
expected: "expression",
actual: parent.kind(),
})
}
}
}

View File

@ -3,7 +3,6 @@
//!
//! Using this library is simple and straightforward, see the [inferface] module for instructions on
//! interpreting Dust code. Most of the language's features are implemented in the [tools] module.
#![forbid(unsafe_code)]
pub use crate::{
error::*,
@ -26,3 +25,39 @@ mod operator;
mod token;
mod tree;
mod value;
use tree_sitter::Language;
extern "C" {
fn tree_sitter_dust() -> Language;
}
/// Get the tree-sitter [Language][] for this grammar.
///
/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
pub fn language() -> Language {
unsafe { tree_sitter_dust() }
}
/// The content of the [`node-types.json`][] file for this grammar.
///
/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
pub const NODE_TYPES: &'static str = include_str!("../../../src/node-types.json");
// Uncomment these to include any queries that this grammar contains
// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm");
// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
#[cfg(test)]
mod tests {
#[test]
fn test_can_load_grammar() {
let mut parser = tree_sitter::Parser::new();
parser
.set_language(super::language())
.expect("Error loading dust language");
}
}