Merge pull request #35 from mist-go/errors-and-caching
Errors and caching
This commit is contained in:
Generated
+64
@@ -11,6 +11,39 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0061da739915fae12ea00e16397555ed4371a6bb285431aab930f61b0aa4ba"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
@@ -78,6 +111,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
name = "mist-lang"
|
||||
version = "0.0.2-alpha0"
|
||||
dependencies = [
|
||||
"cargo_metadata",
|
||||
"mist-parser",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -154,6 +188,16 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -219,6 +263,26 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.20.0"
|
||||
|
||||
@@ -13,6 +13,7 @@ categories = ["compilers"]
|
||||
serde = { version = "1.0.228", features = ["serde_derive"] }
|
||||
serde_json = "1.0.149"
|
||||
mist-parser = { path = "./parser", version = "0.1.1"}
|
||||
cargo_metadata = "0.23.1"
|
||||
|
||||
[[bin]]
|
||||
name = "mist"
|
||||
|
||||
@@ -2,7 +2,7 @@ use <std::fmt::Formatter>;
|
||||
use <std::fmt>;
|
||||
|
||||
pub trait TestLogging<T: fmt::Display>: fmt::Display {
|
||||
String get();
|
||||
String get(self*);
|
||||
}
|
||||
|
||||
pub enum LogLevel {
|
||||
@@ -29,6 +29,12 @@ impl fmt::Display for LogLevel {
|
||||
}
|
||||
}
|
||||
|
||||
impl TestLogging<LogLevel> for LogLevel {
|
||||
String get(self*) {
|
||||
return format!("{self}");
|
||||
}
|
||||
}
|
||||
|
||||
pub class Logger {
|
||||
String prefix;
|
||||
|
||||
@@ -55,7 +61,7 @@ pub class Logger {
|
||||
}
|
||||
|
||||
LogLevel::Warning => {
|
||||
println!("{level} {} {}", self.prefix, message);
|
||||
println!("{} {} {}", level.get(), self.prefix, message);
|
||||
}
|
||||
|
||||
LogLevel::Error => {
|
||||
|
||||
+14
-1
@@ -17,6 +17,9 @@ pub struct Identifier(pub String);
|
||||
#[derive(Debug, Clone, Serialize, Default)]
|
||||
pub struct ParamList(pub Vec<VarDecl>);
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct TypeExpr(pub TypeExprKind, pub Vec<TypePostfix>);
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub enum TypePostfix {
|
||||
Ref,
|
||||
@@ -34,7 +37,11 @@ pub enum TypeExprKind {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct TypeExpr(pub TypeExprKind, pub Vec<TypePostfix>);
|
||||
pub struct Spanned<T> {
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
pub item: T,
|
||||
}
|
||||
|
||||
impl TypeExpr {
|
||||
pub fn no_px(kind: TypeExprKind) -> Self {
|
||||
@@ -52,3 +59,9 @@ impl From<GenericDecl> for Generic {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Spanned<T> {
|
||||
pub fn get_comment(&self) -> String {
|
||||
format!("/* {}:{} */", self.line, self.column)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use serde::Serialize;
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Default)]
|
||||
pub struct Block(pub Vec<Statement>);
|
||||
pub struct Block(pub Vec<Spanned<Statement>>);
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub enum Statement {
|
||||
|
||||
@@ -22,7 +22,7 @@ pub enum Attribute {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct TopLevel(pub TopLevelKind, pub Vec<Attribute>);
|
||||
pub struct TopLevel(pub Spanned<TopLevelKind>, pub Vec<Attribute>);
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub enum TopLevelKind {
|
||||
@@ -35,35 +35,35 @@ pub enum TopLevelKind {
|
||||
name: Identifier,
|
||||
generics: GenericsDecl,
|
||||
requirements: Vec<TypeExpr>,
|
||||
items: Vec<FunctionDecl>,
|
||||
items: Vec<Spanned<FunctionDecl>>,
|
||||
},
|
||||
EnumDecl {
|
||||
visibility: Visibility,
|
||||
name: Identifier,
|
||||
generics: GenericsDecl,
|
||||
fields: Vec<EnumItem>,
|
||||
fields: Vec<Spanned<EnumItem>>,
|
||||
},
|
||||
StructDecl {
|
||||
visibility: Visibility,
|
||||
name: Identifier,
|
||||
generics: GenericsDecl,
|
||||
fields: Vec<FieldDecl>,
|
||||
fields: Vec<Spanned<FieldDecl>>,
|
||||
},
|
||||
FunctionDecl(FunctionDecl),
|
||||
ClassDecl {
|
||||
visibility: Visibility,
|
||||
name: Identifier,
|
||||
generics: GenericsDecl,
|
||||
fields: Vec<FieldDeclStmt>,
|
||||
constructor: ClassConstructor,
|
||||
fields: Vec<Spanned<FieldDeclStmt>>,
|
||||
constructor: Spanned<ClassConstructor>,
|
||||
items: Vec<ClassItem>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub enum ClassItem {
|
||||
Method(FunctionDecl),
|
||||
ImplDecl(ImplDecl),
|
||||
Method(Spanned<FunctionDecl>),
|
||||
ImplDecl(Spanned<ImplDecl>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Default)]
|
||||
@@ -105,7 +105,7 @@ pub struct ImplDecl {
|
||||
pub generics: GenericsDecl,
|
||||
pub target: TypeExpr,
|
||||
pub trait_: Option<TypeExpr>,
|
||||
pub methods: Vec<FunctionDecl>,
|
||||
pub methods: Vec<Spanned<FunctionDecl>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
|
||||
@@ -4,6 +4,7 @@ use pest_derive::Parser;
|
||||
pub mod ast;
|
||||
pub mod error;
|
||||
pub mod parser;
|
||||
pub mod rev_mapper;
|
||||
|
||||
use ast::*;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
Rule,
|
||||
ast::*,
|
||||
ast_expr,
|
||||
error::{AstError, AstResult, IntoErr, collect_recovered},
|
||||
error::{AstError, IntoErr, collect_recovered},
|
||||
parser::consume_rule,
|
||||
};
|
||||
|
||||
@@ -21,10 +21,7 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for TopLevel {
|
||||
let attributes = collect_recovered(inner.next().unwrap().into_inner());
|
||||
|
||||
ast_expr!(TopLevel(
|
||||
inner
|
||||
.next()
|
||||
.map(TopLevelKind::try_from)
|
||||
.unwrap_or(Ok(TopLevelKind::ModAttribute)),
|
||||
inner.next().map(Spanned::try_from).unwrap(),
|
||||
attributes,
|
||||
))
|
||||
}
|
||||
@@ -57,7 +54,7 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for TopLevelKind {
|
||||
|
||||
fields: inner
|
||||
.next()
|
||||
.map(|pair| collect_recovered::<FieldDecl, FieldDecl>(pair.into_inner()))
|
||||
.map(|pair| collect_recovered(pair.into_inner()))
|
||||
.transpose()
|
||||
.map(|v| v.unwrap_or_default()),
|
||||
}),
|
||||
@@ -116,7 +113,7 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for TopLevelKind {
|
||||
.transpose()
|
||||
.map(|v| v.unwrap_or_default()),
|
||||
|
||||
items: Ok(Vec::new()) as AstResult<'_, Vec<_>>,
|
||||
items: collect_recovered(&mut inner),
|
||||
}),
|
||||
|
||||
_ => AstError::bug_unimplemented(pair),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pub mod common;
|
||||
pub mod items;
|
||||
|
||||
use crate::Rule;
|
||||
use crate::{Rule, ast::Spanned, error::AstError};
|
||||
|
||||
pub fn listen_rule(pairs: &mut pest::iterators::Pairs<'_, Rule>, rule: Rule) -> bool {
|
||||
let consumed = pairs
|
||||
@@ -27,3 +27,28 @@ pub fn consume_rule<'a>(
|
||||
|
||||
if consumed { pairs.next() } else { None }
|
||||
}
|
||||
|
||||
impl<'a, T: TryFrom<pest::iterators::Pair<'a, Rule>, Error = AstError<'a, T>>>
|
||||
TryFrom<pest::iterators::Pair<'a, Rule>> for Spanned<T>
|
||||
{
|
||||
type Error = AstError<'a, Self>;
|
||||
|
||||
fn try_from(pair: pest::iterators::Pair<'a, Rule>) -> Result<Self, Self::Error> {
|
||||
let span = pair.as_span().start_pos().line_col();
|
||||
|
||||
Ok(Self {
|
||||
line: span.0,
|
||||
column: span.1,
|
||||
item: pair.try_into().map_err(|err: AstError<'_, T>| AstError {
|
||||
span: err.span,
|
||||
error_code: err.error_code,
|
||||
error_message: err.error_message,
|
||||
recovered: err.recovered.map(|v| Spanned {
|
||||
line: span.0,
|
||||
column: span.1,
|
||||
item: v,
|
||||
}),
|
||||
})?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
WHITESPACE = _{ " " | "\t" | "\r" | "\n" }
|
||||
|
||||
integer = @{ ASCII_DIGIT+ }
|
||||
|
||||
next_line = { "+" }
|
||||
|
||||
map = { "/*" ~ integer ~ ":" ~ integer ~ next_line? ~ "*/" }
|
||||
|
||||
non_map = { (!map ~ ANY)+ }
|
||||
|
||||
mapping = { SOI ~ (map | non_map)* ~ EOI }
|
||||
@@ -0,0 +1,61 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use pest::Parser;
|
||||
use pest_derive::Parser;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[grammar = "./src/rev_mapper/grammar.pest"]
|
||||
pub struct MistMapperParser;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MistMap(pub usize, pub usize);
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct RustMap(pub usize, pub usize);
|
||||
|
||||
pub fn get_mapping(input: &str) -> HashSet<(RustMap, MistMap)> {
|
||||
let mut mapping = HashSet::new();
|
||||
|
||||
let pairs = MistMapperParser::parse(Rule::mapping, input)
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
|
||||
for pair in pairs {
|
||||
if pair.as_rule() == Rule::map {
|
||||
let rs = pair.as_span().start_pos().line_col();
|
||||
let mut inner = pair.into_inner();
|
||||
|
||||
let (line, col) = (
|
||||
inner.next().unwrap().as_str().parse().unwrap(),
|
||||
inner.next().unwrap().as_str().parse().unwrap(),
|
||||
);
|
||||
|
||||
mapping.insert((
|
||||
RustMap(rs.0, rs.1),
|
||||
MistMap(
|
||||
if inner.next().is_some() {
|
||||
line
|
||||
} else {
|
||||
line - 1
|
||||
},
|
||||
col,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
mapping
|
||||
}
|
||||
|
||||
pub fn find_mapping(
|
||||
mapping: &HashSet<(RustMap, MistMap)>,
|
||||
target: &RustMap,
|
||||
) -> Option<(RustMap, MistMap)> {
|
||||
mapping
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|(rust, _)| rust <= target)
|
||||
.max_by_key(|(rust, _)| *rust)
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env, fs,
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
use cargo_metadata::{CompilerMessage, Message};
|
||||
use mist_parser::rev_mapper::{RustMap, find_mapping, get_mapping};
|
||||
|
||||
use crate::transpiler::Config;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MistDiagnosticMessage {
|
||||
pub message: String,
|
||||
pub file_path: PathBuf,
|
||||
pub file_name: String,
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MistDiagnostic {
|
||||
Error(MistDiagnosticMessage),
|
||||
Warning(MistDiagnosticMessage),
|
||||
Rust(CompilerMessage),
|
||||
}
|
||||
|
||||
pub fn build(mut args: Vec<String>, config: Config, root: PathBuf) -> bool {
|
||||
args.remove(0);
|
||||
args.insert(1, "--message-format=json".to_string());
|
||||
|
||||
let is_root = root == env::current_dir().unwrap();
|
||||
|
||||
let mut command = Command::new("cargo")
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::inherit())
|
||||
.stdin(Stdio::inherit())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let mut reader = std::io::BufReader::new(command.stdout.take().unwrap());
|
||||
|
||||
let mut diagnostics = Vec::new();
|
||||
|
||||
let mut mapping = HashMap::new();
|
||||
|
||||
for message in cargo_metadata::Message::parse_stream(&mut reader) {
|
||||
match message {
|
||||
Ok(Message::CompilerMessage(msg)) => {
|
||||
for span in &msg.message.spans {
|
||||
if span.is_primary {
|
||||
let rust_path = root.join(&span.file_name);
|
||||
|
||||
let mist_file = span
|
||||
.file_name
|
||||
.replacen(&config.output, &config.src, 1)
|
||||
.replace(".rs", ".mist");
|
||||
|
||||
let mist_path = root.join(&mist_file);
|
||||
|
||||
if !fs::exists(&mist_path).unwrap() {
|
||||
diagnostics.push(MistDiagnostic::Rust(msg));
|
||||
break;
|
||||
}
|
||||
|
||||
let map = mapping.entry(rust_path.clone()).or_insert_with(|| {
|
||||
get_mapping(&fs::read_to_string(rust_path).unwrap())
|
||||
});
|
||||
|
||||
let mist_span =
|
||||
find_mapping(&map, &RustMap(span.line_end, span.column_start)).unwrap();
|
||||
|
||||
let mist_msg = MistDiagnosticMessage {
|
||||
message: span.label.clone().unwrap_or(msg.message.message.clone()),
|
||||
file_name: if is_root {
|
||||
mist_file
|
||||
} else {
|
||||
mist_path.to_string_lossy().to_string()
|
||||
},
|
||||
file_path: mist_path,
|
||||
line: mist_span.1.0,
|
||||
column: mist_span.1.1,
|
||||
};
|
||||
|
||||
match msg.message.level {
|
||||
cargo_metadata::diagnostic::DiagnosticLevel::Error => {
|
||||
diagnostics.push(MistDiagnostic::Error(mist_msg))
|
||||
}
|
||||
|
||||
cargo_metadata::diagnostic::DiagnosticLevel::Warning => {
|
||||
diagnostics.push(MistDiagnostic::Warning(mist_msg))
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Message::BuildFinished(finish)) => {
|
||||
print_diagnostics(&diagnostics);
|
||||
|
||||
let mut raw_reader = reader.into_inner();
|
||||
let mut stdout = std::io::stdout();
|
||||
|
||||
std::io::copy(&mut raw_reader, &mut stdout).unwrap();
|
||||
|
||||
command.wait().unwrap();
|
||||
|
||||
return finish.success;
|
||||
}
|
||||
|
||||
Ok(Message::TextLine(text)) => println!("{text}"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn print_diagnostics(diagnostics: &Vec<MistDiagnostic>) {
|
||||
let mut files = HashMap::new();
|
||||
|
||||
for diag in diagnostics {
|
||||
match diag {
|
||||
MistDiagnostic::Error(msg) => {
|
||||
let line = get_line(&mut files, &msg);
|
||||
|
||||
println!(
|
||||
"\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}\n\t{}",
|
||||
msg.file_name,
|
||||
msg.line + 1,
|
||||
msg.column,
|
||||
msg.message,
|
||||
line.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
MistDiagnostic::Warning(msg) => {
|
||||
let line = get_line(&mut files, &msg);
|
||||
|
||||
println!(
|
||||
"\n{}:{}:{}\n \x1b[33mWarning\x1b[0m: {}\n\t{}",
|
||||
msg.file_name,
|
||||
msg.line + 1,
|
||||
msg.column,
|
||||
msg.message,
|
||||
line.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
MistDiagnostic::Rust(rs) => println!("{rs}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_line(
|
||||
files: &mut HashMap<PathBuf, Vec<String>>,
|
||||
msg: &MistDiagnosticMessage,
|
||||
) -> Option<String> {
|
||||
let src_path = msg.file_path.clone();
|
||||
|
||||
let lines = files.entry(src_path.clone()).or_insert_with(|| {
|
||||
fs::read_to_string(src_path)
|
||||
.unwrap()
|
||||
.lines()
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.collect()
|
||||
});
|
||||
|
||||
lines.get(msg.line).map(|v| v.trim().to_string())
|
||||
}
|
||||
+41
-15
@@ -1,8 +1,8 @@
|
||||
use mist_parser::ast::{
|
||||
Attribute, BinaryOp, Block, ClassItem, EnumItem, ExprPath, ExprPathSegment, Expression,
|
||||
FieldDecl, FunctionDecl, Generic, GenericDecl, Generics, GenericsDecl, Identifier, ImplDecl,
|
||||
Literal, Path, Pattern, Postfix, Prefix, Statement, StatementBranch, TopLevel, TopLevelKind,
|
||||
TypeExpr, TypeExprKind, TypePostfix, VarDecl, VarDeclStmt, Visibility,
|
||||
Literal, Path, Pattern, Postfix, Prefix, Spanned, Statement, StatementBranch, TopLevel,
|
||||
TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, VarDecl, VarDeclStmt, Visibility,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -88,6 +88,17 @@ impl Default for RustCodegen {
|
||||
// GetRust — pure string production (expressions, types)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
impl<T: GetRust> GetRust for Spanned<T> {
|
||||
fn get_rust(self) -> String {
|
||||
format!(
|
||||
"/* {}:{} */ {}",
|
||||
self.line,
|
||||
self.column,
|
||||
self.item.get_rust()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GetRust for TypeExpr {
|
||||
fn get_rust(self) -> String {
|
||||
get_type_postfixes(self.1) + &self.0.get_rust()
|
||||
@@ -315,6 +326,13 @@ impl GetRust for Vec<Postfix> {
|
||||
// ToRust — output-writing (top-level, statements, blocks)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
impl<T: ToRust> ToRust for Spanned<T> {
|
||||
fn to_rust(self, cg: &mut RustCodegen) {
|
||||
cg.add_indentedln(&format!("/* {}:{} */", self.line, self.column));
|
||||
self.item.to_rust(cg);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToRust for Block {
|
||||
fn to_rust(self, cg: &mut RustCodegen) {
|
||||
for stmt in self.0 {
|
||||
@@ -325,18 +343,15 @@ impl ToRust for Block {
|
||||
|
||||
impl ToRust for TopLevel {
|
||||
fn to_rust(self, cg: &mut RustCodegen) {
|
||||
match self.0 {
|
||||
TopLevelKind::ModAttribute => {
|
||||
if let TopLevelKind::ModAttribute = self.0.item {
|
||||
for attr in self.1 {
|
||||
cg.addln(&format!("#![{}]", attr.get_rust()));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
} else {
|
||||
for attr in self.1 {
|
||||
cg.addln(&format!("#[{}]", attr.get_rust()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.0.to_rust(cg);
|
||||
}
|
||||
@@ -369,9 +384,9 @@ impl ToRust for TopLevelKind {
|
||||
match self {
|
||||
Self::ModAttribute => {}
|
||||
Self::Import(vis, path) => {
|
||||
cg.addln(&format!("{} use {};", vis.get_rust(), path.get_rust()))
|
||||
cg.addln(&format!("{}use {};", vis.get_rust(), path.get_rust()))
|
||||
}
|
||||
Self::Mod(vis, id) => cg.addln(&format!("{} mod {};", vis.get_rust(), id.get_rust())),
|
||||
Self::Mod(vis, id) => cg.addln(&format!("{}mod {};", vis.get_rust(), id.get_rust())),
|
||||
Self::FunctionDecl(decl) => decl.to_rust(cg),
|
||||
Self::ImplDecl(impl_) => impl_.to_rust(cg),
|
||||
Self::StructDecl {
|
||||
@@ -410,7 +425,7 @@ impl ToRust for TopLevelKind {
|
||||
cg.indent += 1;
|
||||
|
||||
for field in fields {
|
||||
cg.add_indentedln(&(format!("{}", field.get_rust()) + ","));
|
||||
cg.add_indentedln(&(field.get_rust() + ","));
|
||||
}
|
||||
|
||||
cg.indent -= 1;
|
||||
@@ -466,7 +481,8 @@ impl ToRust for TopLevelKind {
|
||||
cg.indent += 1;
|
||||
|
||||
for field in fields.clone() {
|
||||
cg.add_indentedln(&field.decl.get_rust());
|
||||
cg.add_indentedln(&field.get_comment());
|
||||
cg.add_indentedln(&field.item.decl.get_rust());
|
||||
}
|
||||
|
||||
cg.indent -= 1;
|
||||
@@ -490,6 +506,10 @@ impl ToRust for TopLevelKind {
|
||||
));
|
||||
cg.indent += 1;
|
||||
|
||||
let constructor_comment = constructor.get_comment();
|
||||
|
||||
let constructor = constructor.item;
|
||||
|
||||
let params_str = constructor
|
||||
.params
|
||||
.0
|
||||
@@ -500,6 +520,7 @@ impl ToRust for TopLevelKind {
|
||||
.join(", ");
|
||||
|
||||
cg.add_indentedln("#[allow(invalid_value)]");
|
||||
cg.add_indentedln(&constructor_comment);
|
||||
cg.add_indentedln(&format!(
|
||||
"{}fn new{}({}) -> Self {{",
|
||||
constructor.visibility.clone().get_rust(),
|
||||
@@ -511,10 +532,14 @@ impl ToRust for TopLevelKind {
|
||||
cg.add_indentedln("let mut this: Self = unsafe { std::mem::MaybeUninit::<Self>::zeroed().assume_init() };");
|
||||
|
||||
for field in fields {
|
||||
if let Some(init) = field.init {
|
||||
let comment = field.get_comment();
|
||||
|
||||
if let Some(init) = field.item.init {
|
||||
cg.add_indentedln(&comment);
|
||||
|
||||
cg.add_indentedln(&format!(
|
||||
"this.{} = {};",
|
||||
field.decl.name.get_rust(),
|
||||
field.item.decl.name.get_rust(),
|
||||
init.get_rust()
|
||||
));
|
||||
}
|
||||
@@ -537,6 +562,7 @@ impl ToRust for TopLevelKind {
|
||||
cg.add_indentedln("}\n");
|
||||
|
||||
// Constructor function
|
||||
cg.add_indentedln(&constructor_comment);
|
||||
cg.add_indentedln(&format!(
|
||||
"{}fn construct_class{}(&mut self, {}) {{",
|
||||
constructor.visibility.get_rust(),
|
||||
@@ -565,8 +591,8 @@ impl ToRust for TopLevelKind {
|
||||
ClassItem::ImplDecl(impl_) => {
|
||||
let mut impl_ = impl_.clone();
|
||||
|
||||
impl_.trait_ = Some(impl_.target);
|
||||
impl_.target =
|
||||
impl_.item.trait_ = Some(impl_.item.target);
|
||||
impl_.item.target =
|
||||
TypeExpr(TypeExprKind::Path(Path(vec![name.clone()])), Vec::new());
|
||||
|
||||
impl_.to_rust(cg);
|
||||
|
||||
+20
-9
@@ -1,7 +1,8 @@
|
||||
use std::process::{self, Command};
|
||||
use std::process::{self};
|
||||
|
||||
pub mod builder;
|
||||
pub mod codegen;
|
||||
pub mod compiler;
|
||||
pub mod transpiler;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
@@ -12,11 +13,18 @@ fn main() {
|
||||
}
|
||||
|
||||
match args[1].as_str() {
|
||||
"run" | "build" | "r" | "b" => {
|
||||
compiler::build();
|
||||
Command::new("cargo").args(&args[1..]).status().unwrap();
|
||||
"run" | "build" | "check" | "r" | "b" | "c" => {
|
||||
let (config, root) = transpiler::build();
|
||||
println!("");
|
||||
if builder::build(args, config, root) {
|
||||
println!("\x1b[32m\nBuild successful\x1b[0m");
|
||||
} else {
|
||||
println!("\x1b[31m\nBuild failed\x1b[0m");
|
||||
}
|
||||
}
|
||||
"transpile" | "t" => {
|
||||
transpiler::build();
|
||||
}
|
||||
"transpile" | "t" => compiler::build(),
|
||||
"version" | "--version" | "-v" => {
|
||||
println!("mist {}", env!("CARGO_PKG_VERSION"));
|
||||
}
|
||||
@@ -36,12 +44,15 @@ fn print_usage() {
|
||||
println!(" View our documentation! \x1b[35mhttps://mist.selimaj.dev\x1b[0m");
|
||||
println!(" Follow us on github! \x1b[35mhttps://github.com/mist-go\x1b[0m\n");
|
||||
println!("usage:");
|
||||
println!(" \x1b[36m mist run\x1b[0m run the project in the current directory\n");
|
||||
println!(" \x1b[36m mist run\x1b[0m,\x1b[36m r\x1b[0m run the project in the current directory\n");
|
||||
println!(
|
||||
" \x1b[36m mist build\x1b[0m build the project in the current directory\n"
|
||||
" \x1b[36m mist build\x1b[0m,\x1b[36m b\x1b[0m build the project in the current directory\n"
|
||||
);
|
||||
println!(
|
||||
" \x1b[36m mist transpile\x1b[0m transpile the project in the current directory\n"
|
||||
" \x1b[36m mist transpile\x1b[0m,\x1b[36m t\x1b[0m transpile the project in the current directory\n"
|
||||
);
|
||||
println!(
|
||||
" \x1b[36m mist check\x1b[0m,\x1b[36m c\x1b[0m check the project in the current directory\n"
|
||||
);
|
||||
println!(" \x1b[36m mist version \x1b[0m print the compiler version\n");
|
||||
println!(" \x1b[36m mist help\x1b[0m print this message\n");
|
||||
|
||||
@@ -9,22 +9,18 @@ use mist_parser::error::ParseError;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Config {
|
||||
src: String,
|
||||
output: String,
|
||||
pub struct Config {
|
||||
pub src: String,
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
pub fn build() {
|
||||
pub fn build() -> (Config, PathBuf) {
|
||||
let start = Instant::now();
|
||||
|
||||
// 1. find project root
|
||||
let root = find_project_root().unwrap_or_else(|| {
|
||||
panic!("error: could not find project root (mist.json)");
|
||||
});
|
||||
|
||||
println!("mistc build ({})", root.display());
|
||||
|
||||
// 2. load config
|
||||
let config = load_config(&root);
|
||||
|
||||
let src_dir = root.join(&config.src);
|
||||
@@ -34,7 +30,12 @@ pub fn build() {
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
println!("build finished in {:.2?}", elapsed);
|
||||
println!(
|
||||
"\x1b[32m\nTranspile successful\x1b[0m in \x1b[34m{:.2?}\x1b[0m",
|
||||
elapsed
|
||||
);
|
||||
|
||||
(config, root)
|
||||
}
|
||||
|
||||
fn build_dir(root: &Path, base_src: &Path, current_dir: &Path, out_dir: &Path) {
|
||||
@@ -71,13 +72,30 @@ fn build_dir(root: &Path, base_src: &Path, current_dir: &Path, out_dir: &Path) {
|
||||
|
||||
let relative = path.strip_prefix(base_src).unwrap();
|
||||
|
||||
// Handle non-mist files with a cache check
|
||||
if path.extension().and_then(|e| e.to_str()) != Some("mist") {
|
||||
fs::copy(&path, out_dir.join(relative)).expect("Failed to copy non-mist file");
|
||||
let dest_path = out_dir.join(relative);
|
||||
|
||||
// Create parent directories for static assets if needed
|
||||
if let Some(parent) = dest_path.parent() {
|
||||
let _ = fs::create_dir_all(parent);
|
||||
}
|
||||
|
||||
if should_skip(&path, &dest_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fs::copy(&path, dest_path).expect("Failed to copy non-mist file");
|
||||
continue;
|
||||
}
|
||||
|
||||
let output_path = out_dir.join(relative).with_extension("rs");
|
||||
|
||||
// Cache layer: Skip if the generated .rs file is newer than the .mist source
|
||||
if should_skip(&path, &output_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create parent directories
|
||||
if let Some(parent) = output_path.parent() {
|
||||
if let Err(e) = fs::create_dir_all(parent) {
|
||||
@@ -147,6 +165,15 @@ fn build_dir(root: &Path, base_src: &Path, current_dir: &Path, out_dir: &Path) {
|
||||
}
|
||||
}
|
||||
|
||||
fn should_skip(source: &Path, output: &Path) -> bool {
|
||||
if let (Ok(src_meta), Ok(out_meta)) = (fs::metadata(source), fs::metadata(output)) {
|
||||
if let (Ok(src_time), Ok(out_time)) = (src_meta.modified(), out_meta.modified()) {
|
||||
return out_time >= src_time;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn find_project_root() -> Option<PathBuf> {
|
||||
let mut dir = std::env::current_dir().ok()?;
|
||||
|
||||
Reference in New Issue
Block a user