basic parser working

This commit is contained in:
Daniel Bulant 2025-04-03 15:45:39 +02:00
parent dfbc6ecf17
commit 4501a4af58
No known key found for this signature in database
2 changed files with 170 additions and 30 deletions

View file

@ -1,14 +1,14 @@
use chumsky::{error::{EmptyErr, Rich, Simple}, prelude::{choice, just, none_of, one_of, recursive}, text, IterParser, Parser};
use chumsky::{error::{EmptyErr, Rich, Simple}, prelude::{any, choice, end, just, none_of, one_of, recursive}, text, IterParser, Parser};
#[derive(Debug, Clone)]
struct Index {
pub struct Index {
value: Box<Value>,
index: Box<Value>
}
#[derive(Debug, Clone)]
enum Primitive {
pub enum Primitive {
Number(f64),
String(String),
Variable(String),
@ -16,38 +16,38 @@ enum Primitive {
}
#[derive(Debug, Clone)]
enum Bindable {
Primitive(Primitive),
pub enum Bindable {
Primitive(Primitive)
}
#[derive(Debug, Clone)]
struct Command {
pub struct Command {
name: Box<Value>,
args: Vec<Value>
}
#[derive(Debug, Clone)]
struct Set {
pub struct Set {
name: Box<Bindable>,
value: Box<Value>
}
#[derive(Debug, Clone)]
struct If {
pub struct If {
condition: Box<Command>,
body: Vec<Statement>,
else_body: Option<Vec<Statement>>
}
#[derive(Debug, Clone)]
struct While {
pub struct While {
condition: Box<Command>,
body: Vec<Statement>,
else_body: Option<Vec<Statement>>
}
#[derive(Debug, Clone)]
struct For {
pub struct For {
name: Box<Bindable>,
iterable: Box<Value>,
body: Vec<Statement>,
@ -55,7 +55,14 @@ struct For {
}
#[derive(Debug, Clone)]
struct Loop {
pub struct Loop {
body: Vec<Statement>
}
#[derive(Debug, Clone)]
pub struct Function {
name: String,
args: Vec<Bindable>,
body: Vec<Statement>
}
@ -67,11 +74,14 @@ pub enum Statement {
While(While),
If(If),
Loop(Loop),
Break
Function(Function),
Return(Option<Value>),
Break,
Continue,
}
#[derive(Debug, Clone)]
enum Value {
pub enum Value {
Primitive(Primitive),
Group(Vec<Statement>)
}
@ -145,10 +155,11 @@ pub fn parse<'a>() -> impl Parser<'a, &'a str, Vec<Statement>, chumsky::extra::D
direct_string.clone(),
));
let eol = one_of("\n\r;");
// let r#break = just("break").map(|_| Statement::Break);
let variable = just('$').ignore_then(text::ident());
let comment = just('#').then(any().and_is(just('\n').not()).repeated());
recursive(|expr| {
let primitive = choice((
number.map(Primitive::Number),
@ -156,36 +167,165 @@ pub fn parse<'a>() -> impl Parser<'a, &'a str, Vec<Statement>, chumsky::extra::D
string.map(Primitive::String),
));
let bindable = primitive.clone();
let bindable = primitive.clone().map(Bindable::Primitive);
let group = just('(')
.ignore_then(expr.clone())
.then_ignore(just(')'))
let bindable_group = bindable
.clone()
.padded()
.separated_by(just(","))
.collect()
.delimited_by(just('('), just(')'));
let group = expr.clone()
.delimited_by(just('('), just(')'))
.map(|v| Value::Group(v));
let block = expr.clone()
.delimited_by(just('{'), just('}'));
let value = choice((
group,
primitive.map(Value::Primitive),
));
let command = value
.separated_by(text::whitespace().at_least(1))
.at_least(1)
.allow_trailing()
.allow_leading()
.collect()
.map(|args: Vec<Value>| {
let name = args[0].clone();
let args = args[1..].to_vec();
let cmdname = value.clone()
.and_is(choice((
just("set"),
just("if"),
just("while"),
just("for"),
just("loop"),
just("break"),
just("continue"),
just("return"),
just("fn"),
)).then(end()).not());
let args = value.clone()
.separated_by(text::inline_whitespace().at_least(1))
.allow_leading()
.allow_trailing()
.collect();
let command =
cmdname
.padded_by(text::inline_whitespace())
.then(args)
.map(|(name, args): (Value, Vec<Value>)| {
Command {
name: Box::new(name),
args: args.into_iter().map(|v| v.clone()).collect()
}
});
let statement = command.map(Statement::Command);
let set = just("set")
.then_ignore(text::inline_whitespace().at_least(1))
.ignore_then(bindable.clone())
.then_ignore(just('=').padded())
.then(value.clone())
.map(|(name, value): (Bindable, Value)| {
Set {
name: Box::new(name),
value: Box::new(value)
}
});
statement.separated_by(eol).at_least(1).collect()
let else_ = just("else")
.then(text::inline_whitespace().at_least(1))
.ignore_then(choice((
block.clone(),
expr.clone()
)));
let if_ = just("if")
.then_ignore(text::inline_whitespace().at_least(1))
.ignore_then(command.clone())
.then(block.clone().padded())
.then(else_.clone().or_not())
.map(|((cond, body), else_body): ((Command, Vec<Statement>), _)| {
If {
condition: Box::new(cond),
body,
else_body
}
});
let while_ = just("while")
.then_ignore(text::inline_whitespace().at_least(1))
.ignore_then(command.clone())
.then(block.clone().padded())
.then(else_.clone().or_not())
.map(|((cond, body), else_body): ((Command, Vec<Statement>), _)| {
While {
condition: Box::new(cond),
body,
else_body
}
});
let for_ = just("for")
.then_ignore(text::inline_whitespace().at_least(1))
.ignore_then(bindable.clone())
.then_ignore(just("in").padded())
.then(value.clone())
.then(block.clone().padded())
.then(else_.clone().or_not())
.map(|(((name, iterable), body), else_body): (((Bindable, Value), Vec<Statement>), _)| {
For {
name: Box::new(name),
iterable: Box::new(iterable),
body,
else_body
}
});
let loop_ = just("loop")
.ignore_then(block.clone().padded())
.map(|body: Vec<Statement>| {
Loop {
body
}
});
let return_ = just("return")
.then_ignore(text::inline_whitespace().at_least(1))
.ignore_then(value.clone().or_not())
.map(|v: Option<Value>| {
Statement::Return(v)
});
let function = just("fn")
.then_ignore(text::inline_whitespace().at_least(1))
.ignore_then(text::ident())
.then(bindable_group.clone())
.then(block.clone().padded())
.map(|((name, args), body): ((&str, Vec<Bindable>), Vec<Statement>)| {
Function {
name: name.to_string(),
args: args,
body
}
});
let statement = choice((
set.map(Statement::Set),
if_.map(Statement::If),
while_.map(Statement::While),
for_.map(Statement::For),
loop_.map(Statement::Loop),
function.map(Statement::Function),
return_,
just("break").to(Statement::Break),
just("continue").to(Statement::Continue),
command.map(Statement::Command),
));
statement
.padded_by(text::inline_whitespace().ignored().or(comment.ignored()))
.separated_by(eol.repeated().at_least(1))
.at_least(1)
.allow_trailing()
.allow_leading()
.collect()
})
}

View file

@ -1 +1 @@
test ( test 2 )
fn test() {echo}