EX6RT4XJW7QK5NAQLAPBXKC2UTOBKC7QFOGNPRVQXXDNKYTMNEQAC
// asdf
3 + 5.45 - 0.25 * -18 / (!!5)
use crate::{
lexer::{Literal, Token, TokenType},
Lox,
};
use display_tree::DisplayTree;
use std::{fmt::Display, rc::Rc};
/* GRAMMER:
expression → equality ;
equality → comparison ( ( "!=" | "==" ) comparison )* ;
comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
term → factor ( ( "-" | "+" ) factor )* ;
factor → unary ( ( "/" | "*" ) unary )* ;
unary → ( "!" | "-" ) unary
| primary ;
primary → NUMBER | STRING | "true" | "false" | "nil"
| "(" expression ")" ;
*/
/* Recursive descent parsing:
A recursive descent parser is a literal translation of
the grammar’s rules straight into imperative code.
Each rule becomes a function.
The body of the rule translates to code roughly like:
| Grammar notation | Code representation |
| ---------------- | --------------------------------- |
| Terminal | Code to match and consume a token |
| Non-terminal | Call to that rule’s function |
| '|' | if or switch statement |
| '*' or '+' | while or for loop |
| '?' | if statement |
*/
#[derive(Debug, DisplayTree)]
pub enum Expression {
Binary {
#[tree]
left: Rc<Expression>,
operator: Token,
#[tree]
right: Rc<Expression>,
},
Grouping {
#[tree]
expression: Rc<Expression>,
},
Literal {
value: Literal,
},
Unary {
operator: Token,
#[tree]
right: Rc<Expression>,
},
}
impl Display for Expression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", parenthize(self))
}
}
fn parenthize(expression: &Expression) -> String {
match expression {
Expression::Binary {
left,
operator,
right,
} => {
format!(
"({} {} {})",
operator.lexeme,
parenthize(left),
parenthize(right)
)
}
Expression::Grouping { expression } => format!("(group {})", parenthize(expression)),
Expression::Literal { value } => format!("{}", value),
Expression::Unary { operator, right } => {
format!("({} {})", operator.lexeme, parenthize(right))
}
}
}
pub struct Parser<'a> {
pub lox: &'a mut Lox,
pub tokens: Vec<Token>,
pub current: usize,
}
impl<'a> Parser<'a> {
pub fn new(lox: &'a mut Lox, tokens: Vec<Token>) -> Self {
Self {
lox,
tokens,
current: 0,
}
}
pub fn peek(&self) -> Token {
self.tokens[self.current].clone()
}
pub fn previous(&self) -> Token {
self.tokens[self.current - 1].clone()
}
pub fn is_at_end(&self) -> bool {
self.current >= self.tokens.len() || self.peek().token_type == TokenType::Eof
}
pub fn advance(&mut self) -> Token {
if !self.is_at_end() {
self.current += 1;
}
self.previous()
}
pub fn check(&self, token_type: &TokenType) -> bool {
if self.is_at_end() {
false
} else {
&self.peek().token_type == token_type
}
}
pub fn match_tokens(&mut self, types: &[TokenType]) -> bool {
for token_type in types {
if self.check(token_type) {
self.advance();
return true;
}
}
false
}
fn expression(&mut self) -> Expression {
self.equality()
}
fn equality(&mut self) -> Expression {
let mut expression = self.comparison();
while self.match_tokens(&[TokenType::BangEqual, TokenType::EqualEqual]) {
let operator = self.previous();
let right = self.comparison();
expression = Expression::Binary {
left: Rc::new(expression),
operator,
right: Rc::new(right),
};
}
expression
}
fn comparison(&mut self) -> Expression {
let mut expression = self.term();
while self.match_tokens(&[
TokenType::Greater,
TokenType::GreaterEqual,
TokenType::Less,
TokenType::LessEqual,
]) {
let operator = self.previous();
let right = self.term();
expression = Expression::Binary {
left: Rc::new(expression),
operator,
right: Rc::new(right),
};
}
expression
}
fn term(&mut self) -> Expression {
let mut expression = self.factor();
while self.match_tokens(&[TokenType::Minus, TokenType::Plus]) {
let operator = self.previous();
let right = self.factor();
expression = Expression::Binary {
left: Rc::new(expression),
operator,
right: Rc::new(right),
};
}
expression
}
fn factor(&mut self) -> Expression {
let mut expression = self.unary();
while self.match_tokens(&[TokenType::Slash, TokenType::Star]) {
let operator = self.previous();
let right = self.unary();
expression = Expression::Binary {
left: Rc::new(expression),
operator,
right: Rc::new(right),
};
}
expression
}
fn unary(&mut self) -> Expression {
if self.match_tokens(&[TokenType::Bang, TokenType::Minus]) {
let operator = self.previous();
let right = self.unary();
Expression::Unary {
operator,
right: Rc::new(right),
}
} else {
self.primary()
}
}
fn consume(&mut self, token_type: TokenType, message: &str) {
if !self.check(&token_type) {
self.lox.error(self.peek().line, message);
panic!();
}
self.advance();
}
fn synchronize(&mut self) {
self.advance();
while !self.is_at_end() {
if self.previous().token_type == TokenType::Semicolon {
return;
}
match self.peek().token_type {
TokenType::Class
| TokenType::Fun
| TokenType::Var
| TokenType::For
| TokenType::If
| TokenType::While
| TokenType::Print
| TokenType::Return => return,
_ => _ = self.advance(),
}
}
}
fn primary(&mut self) -> Expression {
if self.match_tokens(&[TokenType::False]) {
Expression::Literal {
value: Literal::Boolean(false),
}
} else if self.match_tokens(&[TokenType::True]) {
Expression::Literal {
value: Literal::Boolean(true),
}
} else if self.match_tokens(&[TokenType::Nil]) {
Expression::Literal {
value: Literal::Nil,
}
} else if self.match_tokens(&[TokenType::Number]) {
let number = self.previous().literal.unwrap().as_number();
Expression::Literal {
value: Literal::Number(number),
}
} else if self.match_tokens(&[TokenType::String]) {
let string = self.previous().lexeme.clone();
Expression::Literal {
value: Literal::String(string),
}
} else if self.match_tokens(&[TokenType::LeftParen]) {
let expression = self.expression();
if self.match_tokens(&[TokenType::RightParen]) {
Expression::Grouping {
expression: Rc::new(expression),
}
} else {
panic!("Expect ')' after expression.");
}
} else {
panic!("Expect expression.");
}
}
pub fn parse(&mut self) -> Result<Expression, &'static str> {
let expression = self.expression();
if !self.is_at_end() {
self.lox
.error_token(self.tokens[self.current].clone(), "Expected EOF.");
}
Ok(expression)
}
}
#![allow(dead_code)]
use clap::Parser;
mod args;
mod lexer;
use args::Args;
use display_tree::{CharSet, Style, StyleBuilder};
use lox::{Lox, OutputStyle};
fn main() -> anyhow::Result<()> {
let Args { file } = Args::parse();
let mut lox = Lox::new();
lox.options.output_style = OutputStyle::Tree(
Style::default()
.indentation(1)
.char_set(CharSet::ASCII),
);
if let Some(file) = file {
lox.run_file(file)?;
} else {
lox.run_prompt()?;
}
Ok(())
}
#![allow(dead_code)]
use std::{fmt::Debug, io::Write, path::Path};
use display_tree::{format_tree, Style};
mod lexer;
mod parser;
use lexer::{Lexer, Token};
use parser::Parser;
pub enum OutputStyle {
Tree(Style),
Parentized,
}
pub struct LoxOptions {
pub output_style: OutputStyle,
}
pub struct Lox {
had_error: bool,
pub options: LoxOptions,
}
impl Debug for Lox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Lox {{ had_error: {} }}", self.had_error)
}
}
impl Lox {
pub fn new() -> Self {
Self {
had_error: false,
options: LoxOptions {
output_style: OutputStyle::Parentized,
},
}
}
pub fn error_token(&mut self, token: Token, message: &str) {
if token.token_type == lexer::TokenType::Eof {
self.report(token.line, " at end", message);
} else {
self.report(token.line, &format!(" at '{}'", token.lexeme), message);
}
}
pub fn error(&mut self, line: usize, message: &str) {
self.report(line, "", message);
}
fn report(&mut self, line: usize, location: &str, message: &str) {
eprintln!("[line {line}] Error {location}: {message}");
self.had_error = true;
}
fn run(&mut self, source: String) {
let mut lexer = Lexer::new(self, &source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(self, tokens);
let expression = parser.parse().unwrap();
match self.options.output_style {
OutputStyle::Tree(style) => println!("{}", format_tree!(expression, style)),
OutputStyle::Parentized => println!("{}", expression),
}
}
pub fn run_file<P: AsRef<Path>>(&mut self, file: P) -> anyhow::Result<()> {
let source = std::fs::read_to_string(file)?;
self.run(source);
if self.had_error {
std::process::exit(65);
}
Ok(())
}
pub fn run_prompt(&mut self) -> anyhow::Result<()> {
loop {
let mut input = String::new();
print!("> ");
std::io::stdout().flush()?;
match std::io::stdin().read_line(&mut input) {
Ok(0) => break,
Ok(_) => {
self.run(input);
self.had_error = false;
}
Err(e) => return Err(e.into()),
}
}
Ok(())
}
}
use std::fmt::{Display, Formatter};
use crate::Lox;
#[derive(Clone, Debug, PartialEq)]
pub enum TokenType {
// Single-character tokens.
LeftParen,
RightParen,
LeftBrace,
RightBrace,
Comma,
Dot,
Minus,
Plus,
Semicolon,
Slash,
Star,
// One or two character tokens.
Bang,
BangEqual,
Equal,
EqualEqual,
Greater,
GreaterEqual,
Less,
LessEqual,
// Literals.
Identifier,
String,
Number,
// Keywords.
And,
Class,
Else,
False,
Fun,
For,
If,
Nil,
Or,
Print,
Return,
Super,
This,
True,
Var,
While,
Eof,
}
#[derive(Clone, Debug)]
pub enum Literal {
String(String),
Number(f64),
Boolean(bool),
Nil,
}
impl Display for Literal {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Literal::String(s) => write!(f, "{}", s),
Literal::Number(n) => write!(f, "{}", n),
Literal::Boolean(b) => write!(f, "{}", b),
Literal::Nil => write!(f, "nil"),
}
}
}
impl Literal {
pub fn as_string(&self) -> String {
match self {
Literal::String(s) => s.clone(),
_ => panic!("Not a string"),
}
}
pub fn as_number(&self) -> f64 {
match self {
Literal::Number(n) => *n,
_ => panic!("Not a number"),
}
}
pub fn as_boolean(&self) -> bool {
match self {
Literal::Boolean(b) => *b,
_ => panic!("Not a boolean"),
}
}
}
#[derive(Clone, Debug)]
pub struct Token {
pub token_type: TokenType,
pub lexeme: String,
pub line: usize,
pub literal: Option<Literal>,
}
impl Display for Token {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Some(literal) = &self.literal {
write!(f, "{:?} {} {}", self.token_type, self.lexeme, literal)
} else {
write!(f, "{:?} {}", self.token_type, self.lexeme)
}
}
}
impl Token {
pub fn new(token_type: TokenType, lexeme: &str, line: usize) -> Self {
Self {
token_type,
lexeme: lexeme.to_string(),
line,
literal: None,
}
}
pub fn new_with_literal(
token_type: TokenType,
lexeme: &str,
line: usize,
literal: Literal,
) -> Self {
Self {
token_type,
lexeme: lexeme.to_string(),
line,
literal: Some(literal),
}
}
}
#[derive(Debug)]
pub struct Lexer<'a> {
lox: &'a mut Lox,
source: String,
start: usize,
current: usize,
line: usize,
tokens: Vec<Token>,
}
impl<'a> Lexer<'a> {
pub fn new(lox: &'a mut Lox, source: &str) -> Self {
Self {
lox,
source: source.to_string(),
start: 0,
current: 0,
line: 1,
tokens: Vec::new(),
}
}
fn is_at_end(&self) -> bool {
self.current >= self.source.len()
}
fn advance(&mut self) -> char {
let c = self.source.chars().nth(self.current).unwrap();
self.current += 1;
c
}
fn add_token(&mut self, token_type: TokenType) {
self.internal_add_token(token_type, None);
}
fn add_token_with_literal(&mut self, token_type: TokenType, literal: Literal) {
self.internal_add_token(token_type, Some(literal))
}
fn internal_add_token(&mut self, token_type: TokenType, literal: Option<Literal>) {
let text = &self.source[self.start..self.current];
self.tokens.push(Token {
token_type,
lexeme: text.to_string(),
line: self.line,
literal,
});
}
fn match_next(&mut self, expected: char) -> bool {
if self.is_at_end() {
return false;
}
if self.source.chars().nth(self.current).unwrap() != expected {
return false;
}
self.current += 1;
true
}
fn peek(&self) -> char {
if self.is_at_end() {
return '\0';
}
self.source.chars().nth(self.current).unwrap()
}
fn peek_next(&self) -> char {
if self.current + 1 >= self.source.len() {
return '\0';
}
self.source.chars().nth(self.current + 1).unwrap()
}
fn string(&mut self) {
while self.peek() != '"' && !self.is_at_end() {
if self.peek() == '\n' {
self.line += 1;
}
self.advance();
}
if self.is_at_end() {
self.lox.error(self.line, "Unterminated string.");
return;
}
self.advance();
let value = self.source[self.start + 1..self.current - 1].to_string();
self.add_token_with_literal(TokenType::String, Literal::String(value));
}
fn number(&mut self) {
while self.peek().is_digit(10) {
self.advance();
}
if self.peek() == '.' && self.peek_next().is_digit(10) {
self.advance();
while self.peek().is_digit(10) {
self.advance();
}
}
let value = self.source[self.start..self.current].parse().unwrap();
self.add_token_with_literal(TokenType::Number, Literal::Number(value));
}
fn identifier(&mut self) {
while self.peek().is_alphanumeric() {
self.advance();
}
let text = &self.source[self.start..self.current];
let token_type = match text {
"and" => TokenType::And,
"class" => TokenType::Class,
"else" => TokenType::Else,
"false" => TokenType::False,
"for" => TokenType::For,
"fun" => TokenType::Fun,
"if" => TokenType::If,
"nil" => TokenType::Nil,
"or" => TokenType::Or,
"print" => TokenType::Print,
"return" => TokenType::Return,
"super" => TokenType::Super,
"this" => TokenType::This,
"true" => TokenType::True,
"var" => TokenType::Var,
"while" => TokenType::While,
_ => TokenType::Identifier,
};
self.add_token(token_type);
}
fn scan_token(&mut self) {
let c = self.advance();
match c {
'(' => self.add_token(TokenType::LeftParen),
')' => self.add_token(TokenType::RightParen),
'{' => self.add_token(TokenType::LeftBrace),
'}' => self.add_token(TokenType::RightBrace),
',' => self.add_token(TokenType::Comma),
'.' => self.add_token(TokenType::Dot),
'-' => self.add_token(TokenType::Minus),
'+' => self.add_token(TokenType::Plus),
';' => self.add_token(TokenType::Semicolon),
'*' => self.add_token(TokenType::Star),
'!' => {
let token_type = if self.match_next('=') {
TokenType::BangEqual
} else {
TokenType::Bang
};
self.add_token(token_type);
}
'=' => {
let token_type = if self.match_next('=') {
TokenType::EqualEqual
} else {
TokenType::Equal
};
self.add_token(token_type);
}
'<' => {
let token_type = if self.match_next('=') {
TokenType::LessEqual
} else {
TokenType::Less
};
self.add_token(token_type);
}
'>' => {
let token_type = if self.match_next('=') {
TokenType::GreaterEqual
} else {
TokenType::Greater
};
self.add_token(token_type);
}
'/' => {
if self.match_next('/') {
while self.peek() != '\n' && !self.is_at_end() {
self.advance();
}
} else {
self.add_token(TokenType::Slash);
}
}
'\n' => self.line += 1,
'"' => {
self.string();
}
c if c.is_whitespace() => {
// skip whitespace
}
c if c.is_digit(10) => {
self.number();
}
c if c.is_alphabetic() => {
self.identifier();
}
c => {
self.lox
.error(self.line, &format!("Unexpected character: {}", c));
}
}
}
pub fn scan_tokens(&mut self) -> Vec<Token> {
while !self.is_at_end() {
self.start = self.current;
self.scan_token();
}
self.tokens.push(Token::new(TokenType::Eof, "", self.line));
self.tokens.clone()
}
}
use std::path::PathBuf;
use clap::Parser;
#[derive(Parser, Debug)]
pub struct Args {
pub file: Option<PathBuf>,
}
[package]
name = "lox"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.80"
clap = { version = "4.5.1", features = ["derive"] }
display_tree = "1.1.2"
thiserror = "1.0.57"
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstream"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
[[package]]
name = "clap"
version = "4.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "clap_lex"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "display_tree"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee681da17a6049f8d824e3e74e7131f09712f823fc4577c5667cd8a2527f783a"
dependencies = [
"display_tree_derive",
]
[[package]]
name = "display_tree_derive"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4d471d0edd933f0e5de085bf68c8525aea536dba512dcc07c98a7b7c0614af2"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "lox"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"display_tree",
"thiserror",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "strsim"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
.git
.DS_Store
/target