chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

73
vendor/chumsky/examples/brainfuck.rs vendored Normal file
View File

@@ -0,0 +1,73 @@
//! This is a Brainfuck parser and interpreter
//! Run it with the following command:
//! cargo run --example brainfuck -- examples/sample.bf
use chumsky::prelude::*;
use std::{
env, fs,
io::{self, Read},
};
#[derive(Clone)]
enum Instr {
Invalid,
Left,
Right,
Incr,
Decr,
Read,
Write,
Loop(Vec<Self>),
}
fn parser() -> impl Parser<char, Vec<Instr>, Error = Simple<char>> {
use Instr::*;
recursive(|bf| {
choice((
just('<').to(Left),
just('>').to(Right),
just('+').to(Incr),
just('-').to(Decr),
just(',').to(Read),
just('.').to(Write),
))
.or(bf.delimited_by(just('['), just(']')).map(Loop))
.recover_with(nested_delimiters('[', ']', [], |_| Invalid))
.recover_with(skip_then_retry_until([']']))
.repeated()
})
.then_ignore(end())
}
const TAPE_LEN: usize = 10_000;
fn execute(ast: &[Instr], ptr: &mut usize, tape: &mut [u8; TAPE_LEN]) {
use Instr::*;
for symbol in ast {
match symbol {
Invalid => unreachable!(),
Left => *ptr = (*ptr + TAPE_LEN - 1).rem_euclid(TAPE_LEN),
Right => *ptr = (*ptr + 1).rem_euclid(TAPE_LEN),
Incr => tape[*ptr] = tape[*ptr].wrapping_add(1),
Decr => tape[*ptr] = tape[*ptr].wrapping_sub(1),
Read => tape[*ptr] = io::stdin().bytes().next().unwrap().unwrap(),
Write => print!("{}", tape[*ptr] as char),
Loop(ast) => {
while tape[*ptr] != 0 {
execute(ast, ptr, tape)
}
}
}
}
}
fn main() {
let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument"))
.expect("Failed to read file");
// let src = "[!]+";
match parser().parse(src.trim()) {
Ok(ast) => execute(&ast, &mut 0, &mut [0; TAPE_LEN]),
Err(errs) => errs.into_iter().for_each(|e| println!("{:?}", e)),
}
}

196
vendor/chumsky/examples/foo.rs vendored Normal file
View File

@@ -0,0 +1,196 @@
/// This is the parser and interpreter for the 'Foo' language. See `tutorial.md` in the repository's root to learn
/// about it.
use chumsky::prelude::*;
#[derive(Debug)]
enum Expr {
Num(f64),
Var(String),
Neg(Box<Expr>),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
Call(String, Vec<Expr>),
Let {
name: String,
rhs: Box<Expr>,
then: Box<Expr>,
},
Fn {
name: String,
args: Vec<String>,
body: Box<Expr>,
then: Box<Expr>,
},
}
fn parser() -> impl Parser<char, Expr, Error = Simple<char>> {
let ident = text::ident().padded();
let expr = recursive(|expr| {
let int = text::int(10)
.map(|s: String| Expr::Num(s.parse().unwrap()))
.padded();
let call = ident
.then(
expr.clone()
.separated_by(just(','))
.allow_trailing()
.delimited_by(just('('), just(')')),
)
.map(|(f, args)| Expr::Call(f, args));
let atom = int
.or(expr.delimited_by(just('('), just(')')))
.or(call)
.or(ident.map(Expr::Var));
let op = |c| just(c).padded();
let unary = op('-')
.repeated()
.then(atom)
.foldr(|_op, rhs| Expr::Neg(Box::new(rhs)));
let product = unary
.clone()
.then(
op('*')
.to(Expr::Mul as fn(_, _) -> _)
.or(op('/').to(Expr::Div as fn(_, _) -> _))
.then(unary)
.repeated(),
)
.foldl(|lhs, (op, rhs)| op(Box::new(lhs), Box::new(rhs)));
let sum = product
.clone()
.then(
op('+')
.to(Expr::Add as fn(_, _) -> _)
.or(op('-').to(Expr::Sub as fn(_, _) -> _))
.then(product)
.repeated(),
)
.foldl(|lhs, (op, rhs)| op(Box::new(lhs), Box::new(rhs)));
sum
});
let decl = recursive(|decl| {
let r#let = text::keyword("let")
.ignore_then(ident)
.then_ignore(just('='))
.then(expr.clone())
.then_ignore(just(';'))
.then(decl.clone())
.map(|((name, rhs), then)| Expr::Let {
name,
rhs: Box::new(rhs),
then: Box::new(then),
});
let r#fn = text::keyword("fn")
.ignore_then(ident)
.then(ident.repeated())
.then_ignore(just('='))
.then(expr.clone())
.then_ignore(just(';'))
.then(decl)
.map(|(((name, args), body), then)| Expr::Fn {
name,
args,
body: Box::new(body),
then: Box::new(then),
});
r#let.or(r#fn).or(expr).padded()
});
decl.then_ignore(end())
}
fn eval<'a>(
expr: &'a Expr,
vars: &mut Vec<(&'a String, f64)>,
funcs: &mut Vec<(&'a String, &'a [String], &'a Expr)>,
) -> Result<f64, String> {
match expr {
Expr::Num(x) => Ok(*x),
Expr::Neg(a) => Ok(-eval(a, vars, funcs)?),
Expr::Add(a, b) => Ok(eval(a, vars, funcs)? + eval(b, vars, funcs)?),
Expr::Sub(a, b) => Ok(eval(a, vars, funcs)? - eval(b, vars, funcs)?),
Expr::Mul(a, b) => Ok(eval(a, vars, funcs)? * eval(b, vars, funcs)?),
Expr::Div(a, b) => Ok(eval(a, vars, funcs)? / eval(b, vars, funcs)?),
Expr::Var(name) => {
if let Some((_, val)) = vars.iter().rev().find(|(var, _)| *var == name) {
Ok(*val)
} else {
Err(format!("Cannot find variable `{}` in scope", name))
}
}
Expr::Let { name, rhs, then } => {
let rhs = eval(rhs, vars, funcs)?;
vars.push((name, rhs));
let output = eval(then, vars, funcs);
vars.pop();
output
}
Expr::Call(name, args) => {
if let Some((_, arg_names, body)) =
funcs.iter().rev().find(|(var, _, _)| *var == name).copied()
{
if arg_names.len() == args.len() {
let mut args = args
.iter()
.map(|arg| eval(arg, vars, funcs))
.zip(arg_names.iter())
.map(|(val, name)| Ok((name, val?)))
.collect::<Result<_, String>>()?;
vars.append(&mut args);
let output = eval(body, vars, funcs);
vars.truncate(vars.len() - args.len());
output
} else {
Err(format!(
"Wrong number of arguments for function `{}`: expected {}, found {}",
name,
arg_names.len(),
args.len(),
))
}
} else {
Err(format!("Cannot find function `{}` in scope", name))
}
}
Expr::Fn {
name,
args,
body,
then,
} => {
funcs.push((name, args, body));
let output = eval(then, vars, funcs);
funcs.pop();
output
}
}
}
fn main() {
let src = std::fs::read_to_string(std::env::args().nth(1).unwrap()).unwrap();
match parser().parse(src) {
Ok(ast) => match eval(&ast, &mut Vec::new(), &mut Vec::new()) {
Ok(output) => println!("{}", output),
Err(eval_err) => println!("Evaluation error: {}", eval_err),
},
Err(parse_errs) => parse_errs
.into_iter()
.for_each(|e| println!("Parse error: {}", e)),
}
}

175
vendor/chumsky/examples/json.rs vendored Normal file
View File

@@ -0,0 +1,175 @@
//! This is a parser for JSON.
//! Run it with the following command:
//! cargo run --example json -- examples/sample.json
use ariadne::{Color, Fmt, Label, Report, ReportKind, Source};
use chumsky::prelude::*;
use std::{collections::HashMap, env, fs};
#[derive(Clone, Debug)]
enum Json {
Invalid,
Null,
Bool(bool),
Str(String),
Num(f64),
Array(Vec<Json>),
Object(HashMap<String, Json>),
}
fn parser() -> impl Parser<char, Json, Error = Simple<char>> {
recursive(|value| {
let frac = just('.').chain(text::digits(10));
let exp = just('e')
.or(just('E'))
.chain(just('+').or(just('-')).or_not())
.chain::<char, _, _>(text::digits(10));
let number = just('-')
.or_not()
.chain::<char, _, _>(text::int(10))
.chain::<char, _, _>(frac.or_not().flatten())
.chain::<char, _, _>(exp.or_not().flatten())
.collect::<String>()
.from_str()
.unwrapped()
.labelled("number");
let escape = just('\\').ignore_then(
just('\\')
.or(just('/'))
.or(just('"'))
.or(just('b').to('\x08'))
.or(just('f').to('\x0C'))
.or(just('n').to('\n'))
.or(just('r').to('\r'))
.or(just('t').to('\t'))
.or(just('u').ignore_then(
filter(|c: &char| c.is_digit(16))
.repeated()
.exactly(4)
.collect::<String>()
.validate(|digits, span, emit| {
char::from_u32(u32::from_str_radix(&digits, 16).unwrap())
.unwrap_or_else(|| {
emit(Simple::custom(span, "invalid unicode character"));
'\u{FFFD}' // unicode replacement character
})
}),
)),
);
let string = just('"')
.ignore_then(filter(|c| *c != '\\' && *c != '"').or(escape).repeated())
.then_ignore(just('"'))
.collect::<String>()
.labelled("string");
let array = value
.clone()
.chain(just(',').ignore_then(value.clone()).repeated())
.or_not()
.flatten()
.delimited_by(just('['), just(']'))
.map(Json::Array)
.labelled("array");
let member = string.clone().then_ignore(just(':').padded()).then(value);
let object = member
.clone()
.chain(just(',').padded().ignore_then(member).repeated())
.or_not()
.flatten()
.padded()
.delimited_by(just('{'), just('}'))
.collect::<HashMap<String, Json>>()
.map(Json::Object)
.labelled("object");
just("null")
.to(Json::Null)
.labelled("null")
.or(just("true").to(Json::Bool(true)).labelled("true"))
.or(just("false").to(Json::Bool(false)).labelled("false"))
.or(number.map(Json::Num))
.or(string.map(Json::Str))
.or(array)
.or(object)
.recover_with(nested_delimiters('{', '}', [('[', ']')], |_| Json::Invalid))
.recover_with(nested_delimiters('[', ']', [('{', '}')], |_| Json::Invalid))
.recover_with(skip_then_retry_until(['}', ']']))
.padded()
})
.then_ignore(end().recover_with(skip_then_retry_until([])))
}
fn main() {
let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument"))
.expect("Failed to read file");
let (json, errs) = parser().parse_recovery(src.trim());
println!("{:#?}", json);
errs.into_iter().for_each(|e| {
let msg = if let chumsky::error::SimpleReason::Custom(msg) = e.reason() {
msg.clone()
} else {
format!(
"{}{}, expected {}",
if e.found().is_some() {
"Unexpected token"
} else {
"Unexpected end of input"
},
if let Some(label) = e.label() {
format!(" while parsing {}", label)
} else {
String::new()
},
if e.expected().len() == 0 {
"something else".to_string()
} else {
e.expected()
.map(|expected| match expected {
Some(expected) => expected.to_string(),
None => "end of input".to_string(),
})
.collect::<Vec<_>>()
.join(", ")
},
)
};
let report = Report::build(ReportKind::Error, (), e.span().start)
.with_code(3)
.with_message(msg)
.with_label(
Label::new(e.span())
.with_message(match e.reason() {
chumsky::error::SimpleReason::Custom(msg) => msg.clone(),
_ => format!(
"Unexpected {}",
e.found()
.map(|c| format!("token {}", c.fg(Color::Red)))
.unwrap_or_else(|| "end of input".to_string())
),
})
.with_color(Color::Red),
);
let report = match e.reason() {
chumsky::error::SimpleReason::Unclosed { span, delimiter } => report.with_label(
Label::new(span.clone())
.with_message(format!(
"Unclosed delimiter {}",
delimiter.fg(Color::Yellow)
))
.with_color(Color::Yellow),
),
chumsky::error::SimpleReason::Unexpected => report,
chumsky::error::SimpleReason::Custom(_) => report,
};
report.finish().print(Source::from(&src)).unwrap();
});
}

639
vendor/chumsky/examples/nano_rust.rs vendored Normal file
View File

@@ -0,0 +1,639 @@
//! This is an entire parser and interpreter for a dynamically-typed Rust-like expression-oriented
//! programming language. See `sample.nrs` for sample source code.
//! Run it with the following command:
//! cargo run --example nano_rust -- examples/sample.nrs
use ariadne::{Color, Fmt, Label, Report, ReportKind, Source};
use chumsky::{prelude::*, stream::Stream};
use std::{collections::HashMap, env, fmt, fs};
pub type Span = std::ops::Range<usize>;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum Token {
Null,
Bool(bool),
Num(String),
Str(String),
Op(String),
Ctrl(char),
Ident(String),
Fn,
Let,
Print,
If,
Else,
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Token::Null => write!(f, "null"),
Token::Bool(x) => write!(f, "{}", x),
Token::Num(n) => write!(f, "{}", n),
Token::Str(s) => write!(f, "{}", s),
Token::Op(s) => write!(f, "{}", s),
Token::Ctrl(c) => write!(f, "{}", c),
Token::Ident(s) => write!(f, "{}", s),
Token::Fn => write!(f, "fn"),
Token::Let => write!(f, "let"),
Token::Print => write!(f, "print"),
Token::If => write!(f, "if"),
Token::Else => write!(f, "else"),
}
}
}
fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
// A parser for numbers
let num = text::int(10)
.chain::<char, _, _>(just('.').chain(text::digits(10)).or_not().flatten())
.collect::<String>()
.map(Token::Num);
// A parser for strings
let str_ = just('"')
.ignore_then(filter(|c| *c != '"').repeated())
.then_ignore(just('"'))
.collect::<String>()
.map(Token::Str);
// A parser for operators
let op = one_of("+-*/!=")
.repeated()
.at_least(1)
.collect::<String>()
.map(Token::Op);
// A parser for control characters (delimiters, semicolons, etc.)
let ctrl = one_of("()[]{};,").map(|c| Token::Ctrl(c));
// A parser for identifiers and keywords
let ident = text::ident().map(|ident: String| match ident.as_str() {
"fn" => Token::Fn,
"let" => Token::Let,
"print" => Token::Print,
"if" => Token::If,
"else" => Token::Else,
"true" => Token::Bool(true),
"false" => Token::Bool(false),
"null" => Token::Null,
_ => Token::Ident(ident),
});
// A single token can be one of the above
let token = num
.or(str_)
.or(op)
.or(ctrl)
.or(ident)
.recover_with(skip_then_retry_until([]));
let comment = just("//").then(take_until(just('\n'))).padded();
token
.map_with_span(|tok, span| (tok, span))
.padded_by(comment.repeated())
.padded()
.repeated()
}
#[derive(Clone, Debug, PartialEq)]
enum Value {
Null,
Bool(bool),
Num(f64),
Str(String),
List(Vec<Value>),
Func(String),
}
impl Value {
fn num(self, span: Span) -> Result<f64, Error> {
if let Value::Num(x) = self {
Ok(x)
} else {
Err(Error {
span,
msg: format!("'{}' is not a number", self),
})
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Null => write!(f, "null"),
Self::Bool(x) => write!(f, "{}", x),
Self::Num(x) => write!(f, "{}", x),
Self::Str(x) => write!(f, "{}", x),
Self::List(xs) => write!(
f,
"[{}]",
xs.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ")
),
Self::Func(name) => write!(f, "<function: {}>", name),
}
}
}
#[derive(Clone, Debug)]
enum BinaryOp {
Add,
Sub,
Mul,
Div,
Eq,
NotEq,
}
pub type Spanned<T> = (T, Span);
// An expression node in the AST. Children are spanned so we can generate useful runtime errors.
#[derive(Debug)]
enum Expr {
Error,
Value(Value),
List(Vec<Spanned<Self>>),
Local(String),
Let(String, Box<Spanned<Self>>, Box<Spanned<Self>>),
Then(Box<Spanned<Self>>, Box<Spanned<Self>>),
Binary(Box<Spanned<Self>>, BinaryOp, Box<Spanned<Self>>),
Call(Box<Spanned<Self>>, Vec<Spanned<Self>>),
If(Box<Spanned<Self>>, Box<Spanned<Self>>, Box<Spanned<Self>>),
Print(Box<Spanned<Self>>),
}
// A function node in the AST.
#[derive(Debug)]
struct Func {
args: Vec<String>,
body: Spanned<Expr>,
}
fn expr_parser() -> impl Parser<Token, Spanned<Expr>, Error = Simple<Token>> + Clone {
recursive(|expr| {
let raw_expr = recursive(|raw_expr| {
let val = select! {
Token::Null => Expr::Value(Value::Null),
Token::Bool(x) => Expr::Value(Value::Bool(x)),
Token::Num(n) => Expr::Value(Value::Num(n.parse().unwrap())),
Token::Str(s) => Expr::Value(Value::Str(s)),
}
.labelled("value");
let ident = select! { Token::Ident(ident) => ident.clone() }.labelled("identifier");
// A list of expressions
let items = expr
.clone()
.separated_by(just(Token::Ctrl(',')))
.allow_trailing();
// A let expression
let let_ = just(Token::Let)
.ignore_then(ident)
.then_ignore(just(Token::Op("=".to_string())))
.then(raw_expr)
.then_ignore(just(Token::Ctrl(';')))
.then(expr.clone())
.map(|((name, val), body)| Expr::Let(name, Box::new(val), Box::new(body)));
let list = items
.clone()
.delimited_by(just(Token::Ctrl('[')), just(Token::Ctrl(']')))
.map(Expr::List);
// 'Atoms' are expressions that contain no ambiguity
let atom = val
.or(ident.map(Expr::Local))
.or(let_)
.or(list)
// In Nano Rust, `print` is just a keyword, just like Python 2, for simplicity
.or(just(Token::Print)
.ignore_then(
expr.clone()
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))),
)
.map(|expr| Expr::Print(Box::new(expr))))
.map_with_span(|expr, span| (expr, span))
// Atoms can also just be normal expressions, but surrounded with parentheses
.or(expr
.clone()
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))))
// Attempt to recover anything that looks like a parenthesised expression but contains errors
.recover_with(nested_delimiters(
Token::Ctrl('('),
Token::Ctrl(')'),
[
(Token::Ctrl('['), Token::Ctrl(']')),
(Token::Ctrl('{'), Token::Ctrl('}')),
],
|span| (Expr::Error, span),
))
// Attempt to recover anything that looks like a list but contains errors
.recover_with(nested_delimiters(
Token::Ctrl('['),
Token::Ctrl(']'),
[
(Token::Ctrl('('), Token::Ctrl(')')),
(Token::Ctrl('{'), Token::Ctrl('}')),
],
|span| (Expr::Error, span),
));
// Function calls have very high precedence so we prioritise them
let call = atom
.then(
items
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')')))
.map_with_span(|args, span: Span| (args, span))
.repeated(),
)
.foldl(|f, args| {
let span = f.1.start..args.1.end;
(Expr::Call(Box::new(f), args.0), span)
});
// Product ops (multiply and divide) have equal precedence
let op = just(Token::Op("*".to_string()))
.to(BinaryOp::Mul)
.or(just(Token::Op("/".to_string())).to(BinaryOp::Div));
let product = call
.clone()
.then(op.then(call).repeated())
.foldl(|a, (op, b)| {
let span = a.1.start..b.1.end;
(Expr::Binary(Box::new(a), op, Box::new(b)), span)
});
// Sum ops (add and subtract) have equal precedence
let op = just(Token::Op("+".to_string()))
.to(BinaryOp::Add)
.or(just(Token::Op("-".to_string())).to(BinaryOp::Sub));
let sum = product
.clone()
.then(op.then(product).repeated())
.foldl(|a, (op, b)| {
let span = a.1.start..b.1.end;
(Expr::Binary(Box::new(a), op, Box::new(b)), span)
});
// Comparison ops (equal, not-equal) have equal precedence
let op = just(Token::Op("==".to_string()))
.to(BinaryOp::Eq)
.or(just(Token::Op("!=".to_string())).to(BinaryOp::NotEq));
let compare = sum
.clone()
.then(op.then(sum).repeated())
.foldl(|a, (op, b)| {
let span = a.1.start..b.1.end;
(Expr::Binary(Box::new(a), op, Box::new(b)), span)
});
compare
});
// Blocks are expressions but delimited with braces
let block = expr
.clone()
.delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}')))
// Attempt to recover anything that looks like a block but contains errors
.recover_with(nested_delimiters(
Token::Ctrl('{'),
Token::Ctrl('}'),
[
(Token::Ctrl('('), Token::Ctrl(')')),
(Token::Ctrl('['), Token::Ctrl(']')),
],
|span| (Expr::Error, span),
));
let if_ = recursive(|if_| {
just(Token::If)
.ignore_then(expr.clone())
.then(block.clone())
.then(
just(Token::Else)
.ignore_then(block.clone().or(if_))
.or_not(),
)
.map_with_span(|((cond, a), b), span: Span| {
(
Expr::If(
Box::new(cond),
Box::new(a),
Box::new(match b {
Some(b) => b,
// If an `if` expression has no trailing `else` block, we magic up one that just produces null
None => (Expr::Value(Value::Null), span.clone()),
}),
),
span,
)
})
});
// Both blocks and `if` are 'block expressions' and can appear in the place of statements
let block_expr = block.or(if_).labelled("block");
let block_chain = block_expr
.clone()
.then(block_expr.clone().repeated())
.foldl(|a, b| {
let span = a.1.start..b.1.end;
(Expr::Then(Box::new(a), Box::new(b)), span)
});
block_chain
// Expressions, chained by semicolons, are statements
.or(raw_expr.clone())
.then(just(Token::Ctrl(';')).ignore_then(expr.or_not()).repeated())
.foldl(|a, b| {
// This allows creating a span that covers the entire Then expression.
// b_end is the end of b if it exists, otherwise it is the end of a.
let a_start = a.1.start;
let b_end = b.as_ref().map(|b| b.1.end).unwrap_or(a.1.end);
(
Expr::Then(
Box::new(a),
Box::new(match b {
Some(b) => b,
// Since there is no b expression then its span is empty.
None => (Expr::Value(Value::Null), b_end..b_end),
}),
),
a_start..b_end,
)
})
})
}
fn funcs_parser() -> impl Parser<Token, HashMap<String, Func>, Error = Simple<Token>> + Clone {
let ident = filter_map(|span, tok| match tok {
Token::Ident(ident) => Ok(ident.clone()),
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))),
});
// Argument lists are just identifiers separated by commas, surrounded by parentheses
let args = ident
.clone()
.separated_by(just(Token::Ctrl(',')))
.allow_trailing()
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')')))
.labelled("function args");
let func = just(Token::Fn)
.ignore_then(
ident
.map_with_span(|name, span| (name, span))
.labelled("function name"),
)
.then(args)
.then(
expr_parser()
.delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}')))
// Attempt to recover anything that looks like a function body but contains errors
.recover_with(nested_delimiters(
Token::Ctrl('{'),
Token::Ctrl('}'),
[
(Token::Ctrl('('), Token::Ctrl(')')),
(Token::Ctrl('['), Token::Ctrl(']')),
],
|span| (Expr::Error, span),
)),
)
.map(|((name, args), body)| (name, Func { args, body }))
.labelled("function");
func.repeated()
.try_map(|fs, _| {
let mut funcs = HashMap::new();
for ((name, name_span), f) in fs {
if funcs.insert(name.clone(), f).is_some() {
return Err(Simple::custom(
name_span.clone(),
format!("Function '{}' already exists", name),
));
}
}
Ok(funcs)
})
.then_ignore(end())
}
struct Error {
span: Span,
msg: String,
}
fn eval_expr(
expr: &Spanned<Expr>,
funcs: &HashMap<String, Func>,
stack: &mut Vec<(String, Value)>,
) -> Result<Value, Error> {
Ok(match &expr.0 {
Expr::Error => unreachable!(), // Error expressions only get created by parser errors, so cannot exist in a valid AST
Expr::Value(val) => val.clone(),
Expr::List(items) => Value::List(
items
.iter()
.map(|item| eval_expr(item, funcs, stack))
.collect::<Result<_, _>>()?,
),
Expr::Local(name) => stack
.iter()
.rev()
.find(|(l, _)| l == name)
.map(|(_, v)| v.clone())
.or_else(|| Some(Value::Func(name.clone())).filter(|_| funcs.contains_key(name)))
.ok_or_else(|| Error {
span: expr.1.clone(),
msg: format!("No such variable '{}' in scope", name),
})?,
Expr::Let(local, val, body) => {
let val = eval_expr(val, funcs, stack)?;
stack.push((local.clone(), val));
let res = eval_expr(body, funcs, stack)?;
stack.pop();
res
}
Expr::Then(a, b) => {
eval_expr(a, funcs, stack)?;
eval_expr(b, funcs, stack)?
}
Expr::Binary(a, BinaryOp::Add, b) => Value::Num(
eval_expr(a, funcs, stack)?.num(a.1.clone())?
+ eval_expr(b, funcs, stack)?.num(b.1.clone())?,
),
Expr::Binary(a, BinaryOp::Sub, b) => Value::Num(
eval_expr(a, funcs, stack)?.num(a.1.clone())?
- eval_expr(b, funcs, stack)?.num(b.1.clone())?,
),
Expr::Binary(a, BinaryOp::Mul, b) => Value::Num(
eval_expr(a, funcs, stack)?.num(a.1.clone())?
* eval_expr(b, funcs, stack)?.num(b.1.clone())?,
),
Expr::Binary(a, BinaryOp::Div, b) => Value::Num(
eval_expr(a, funcs, stack)?.num(a.1.clone())?
/ eval_expr(b, funcs, stack)?.num(b.1.clone())?,
),
Expr::Binary(a, BinaryOp::Eq, b) => {
Value::Bool(eval_expr(a, funcs, stack)? == eval_expr(b, funcs, stack)?)
}
Expr::Binary(a, BinaryOp::NotEq, b) => {
Value::Bool(eval_expr(a, funcs, stack)? != eval_expr(b, funcs, stack)?)
}
Expr::Call(func, args) => {
let f = eval_expr(func, funcs, stack)?;
match f {
Value::Func(name) => {
let f = &funcs[&name];
let mut stack = if f.args.len() != args.len() {
return Err(Error {
span: expr.1.clone(),
msg: format!("'{}' called with wrong number of arguments (expected {}, found {})", name, f.args.len(), args.len()),
});
} else {
f.args
.iter()
.zip(args.iter())
.map(|(name, arg)| Ok((name.clone(), eval_expr(arg, funcs, stack)?)))
.collect::<Result<_, _>>()?
};
eval_expr(&f.body, funcs, &mut stack)?
}
f => {
return Err(Error {
span: func.1.clone(),
msg: format!("'{:?}' is not callable", f),
})
}
}
}
Expr::If(cond, a, b) => {
let c = eval_expr(cond, funcs, stack)?;
match c {
Value::Bool(true) => eval_expr(a, funcs, stack)?,
Value::Bool(false) => eval_expr(b, funcs, stack)?,
c => {
return Err(Error {
span: cond.1.clone(),
msg: format!("Conditions must be booleans, found '{:?}'", c),
})
}
}
}
Expr::Print(a) => {
let val = eval_expr(a, funcs, stack)?;
println!("{}", val);
val
}
})
}
fn main() {
let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument"))
.expect("Failed to read file");
let (tokens, mut errs) = lexer().parse_recovery(src.as_str());
let parse_errs = if let Some(tokens) = tokens {
//dbg!(tokens);
let len = src.chars().count();
let (ast, parse_errs) =
funcs_parser().parse_recovery(Stream::from_iter(len..len + 1, tokens.into_iter()));
//dbg!(ast);
if let Some(funcs) = ast.filter(|_| errs.len() + parse_errs.len() == 0) {
if let Some(main) = funcs.get("main") {
assert_eq!(main.args.len(), 0);
match eval_expr(&main.body, &funcs, &mut Vec::new()) {
Ok(val) => println!("Return value: {}", val),
Err(e) => errs.push(Simple::custom(e.span, e.msg)),
}
} else {
panic!("No main function!");
}
}
parse_errs
} else {
Vec::new()
};
errs.into_iter()
.map(|e| e.map(|c| c.to_string()))
.chain(parse_errs.into_iter().map(|e| e.map(|tok| tok.to_string())))
.for_each(|e| {
let report = Report::build(ReportKind::Error, (), e.span().start);
let report = match e.reason() {
chumsky::error::SimpleReason::Unclosed { span, delimiter } => report
.with_message(format!(
"Unclosed delimiter {}",
delimiter.fg(Color::Yellow)
))
.with_label(
Label::new(span.clone())
.with_message(format!(
"Unclosed delimiter {}",
delimiter.fg(Color::Yellow)
))
.with_color(Color::Yellow),
)
.with_label(
Label::new(e.span())
.with_message(format!(
"Must be closed before this {}",
e.found()
.unwrap_or(&"end of file".to_string())
.fg(Color::Red)
))
.with_color(Color::Red),
),
chumsky::error::SimpleReason::Unexpected => report
.with_message(format!(
"{}, expected {}",
if e.found().is_some() {
"Unexpected token in input"
} else {
"Unexpected end of input"
},
if e.expected().len() == 0 {
"something else".to_string()
} else {
e.expected()
.map(|expected| match expected {
Some(expected) => expected.to_string(),
None => "end of input".to_string(),
})
.collect::<Vec<_>>()
.join(", ")
}
))
.with_label(
Label::new(e.span())
.with_message(format!(
"Unexpected token {}",
e.found()
.unwrap_or(&"end of file".to_string())
.fg(Color::Red)
))
.with_color(Color::Red),
),
chumsky::error::SimpleReason::Custom(msg) => report.with_message(msg).with_label(
Label::new(e.span())
.with_message(format!("{}", msg.fg(Color::Red)))
.with_color(Color::Red),
),
};
report.finish().print(Source::from(&src)).unwrap();
});
}

100
vendor/chumsky/examples/pythonic.rs vendored Normal file
View File

@@ -0,0 +1,100 @@
use chumsky::{prelude::*, BoxStream, Flat};
use std::ops::Range;
// Represents the different kinds of delimiters we care about
#[derive(Copy, Clone, Debug)]
enum Delim {
Paren,
Block,
}
// An 'atomic' token (i.e: it has no child tokens)
#[derive(Clone, Debug)]
enum Token {
Int(u64),
Ident(String),
Op(String),
Open(Delim),
Close(Delim),
}
// The output of the lexer: a recursive tree of nested tokens
#[derive(Debug)]
enum TokenTree {
Token(Token),
Tree(Delim, Vec<Spanned<TokenTree>>),
}
type Span = Range<usize>;
type Spanned<T> = (T, Span);
// A parser that turns pythonic code with semantic whitespace into a token tree
fn lexer() -> impl Parser<char, Vec<Spanned<TokenTree>>, Error = Simple<char>> {
let tt = recursive(|tt| {
// Define some atomic tokens
let int = text::int(10).from_str().unwrapped().map(Token::Int);
let ident = text::ident().map(Token::Ident);
let op = one_of("=.:%,")
.repeated()
.at_least(1)
.collect()
.map(Token::Op);
let single_token = int.or(op).or(ident).map(TokenTree::Token);
// Tokens surrounded by parentheses get turned into parenthesised token trees
let token_tree = tt
.padded()
.repeated()
.delimited_by(just('('), just(')'))
.map(|tts| TokenTree::Tree(Delim::Paren, tts));
single_token
.or(token_tree)
.map_with_span(|tt, span| (tt, span))
});
// Whitespace indentation creates code block token trees
text::semantic_indentation(tt, |tts, span| (TokenTree::Tree(Delim::Block, tts), span))
.then_ignore(end())
}
/// Flatten a series of token trees into a single token stream, ready for feeding into the main parser
fn tts_to_stream(
eoi: Span,
token_trees: Vec<Spanned<TokenTree>>,
) -> BoxStream<'static, Token, Span> {
use std::iter::once;
BoxStream::from_nested(eoi, token_trees.into_iter(), |(tt, span)| match tt {
// Single tokens remain unchanged
TokenTree::Token(token) => Flat::Single((token, span)),
// Nested token trees get flattened into their inner contents, surrounded by `Open` and `Close` tokens
TokenTree::Tree(delim, tree) => Flat::Many(
once((TokenTree::Token(Token::Open(delim)), span.clone()))
.chain(tree.into_iter())
.chain(once((TokenTree::Token(Token::Close(delim)), span))),
),
})
}
fn main() {
let code = include_str!("sample.py");
// First, lex the code into some nested token trees
let tts = lexer().parse(code).unwrap();
println!("--- Token Trees ---\n{:#?}", tts);
// Next, flatten
let eoi = 0..code.chars().count();
let mut token_stream = tts_to_stream(eoi, tts);
// At this point, we have a token stream that can be fed into the main parser! Because this is just an example,
// we're instead going to just collect the token stream into a vector and print it.
let flattened_trees = token_stream.fetch_tokens().collect::<Vec<_>>();
println!("--- Flattened Token Trees ---\n{:?}", flattened_trees);
}

1
vendor/chumsky/examples/sample.bf vendored Normal file
View File

@@ -0,0 +1 @@
--[>--->->->++>-<<<<<-------]>--.>---------.>--..+++.>----.>+++++++++.<<.+++.------.<-.>>+.

4
vendor/chumsky/examples/sample.foo vendored Normal file
View File

@@ -0,0 +1,4 @@
let five = 5;
let eight = 3 + five;
fn add x y = x + y;
add(five, eight)

24
vendor/chumsky/examples/sample.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
"leaving": {
"tail": [{
-2063823378.8597813,
!true,
false,
!null,!
-153646.6402,
"board"
],
"fed": -283765067.9149623,
"cowboy": --355139449,
"although": 794127593.3922591,
"front": "college",
"origin": 981339097
},
"though": ~true,
"invalid": "\uDFFF",
"activity": "value",
"office": -342325541.1937506,
"noise": fallse,
"acres": "home",
"foo": [}]
}

37
vendor/chumsky/examples/sample.nrs vendored Normal file
View File

@@ -0,0 +1,37 @@
// Run this example with `cargo run --example nano_rust -- examples/sample.nrs`
// Feel free to play around with this sample to see what errors you can generate!
// Spans are propagated to the interpreted AST so you can even invoke runtime
// errors and still have an error message that points to source code emitted!
fn mul(x, y) {
x * y
}
// Calculate the factorial of a number
fn factorial(x) {
// Conditionals are supported!
if x == 0 {
1
} else {
mul(x, factorial(x - 1))
}
}
// The main function
fn main() {
let three = 3;
let meaning_of_life = three * 14 + 1;
print("Hello, world!");
print("The meaning of life is...");
if meaning_of_life == 42 {
print(meaning_of_life);
} else {
print("...something we cannot know");
print("However, I can tell you that the factorial of 10 is...");
// Function calling
print(factorial(10));
}
}

16
vendor/chumsky/examples/sample.py vendored Normal file
View File

@@ -0,0 +1,16 @@
import turtle
board = turtle.Turtle(
foo,
bar,
baz,
)
for i in range(6):
board.forward(50)
if i % 2 == 0:
board.right(144)
else:
board.left(72)
turtle.done()