EX6RT4XJW7QK5NAQLAPBXKC2UTOBKC7QFOGNPRVQXXDNKYTMNEQAC // asdf3 + 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 ofthe 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