Skip to content

Commit

Permalink
Big refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Glyphack committed Sep 23, 2023
1 parent cfb7523 commit a423736
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ Cargo.lock
*.pdb

typings/
node_modules
out/
client/dist/
12 changes: 10 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
[workspace]
members = ["parser", "cli", "typechecker", "ruff_python_import_resolver", "lsp"]
members = ["parser", "enderpy", "typechecker", "ruff_python_import_resolver", "lsp"]
resolver = "2"

[workspace.package]
edition = "2021"
rust-version = "1.72.0"
homepage = "https://github.com/Glyphack/enderpy"
documentation = "https://github.com/Glyphack/enderpy"
authors = ["Shaygan Hooshyari"]
license = "APGL-3.0"
repository = "https://github.com/Glyphack/enderpy"

[workspace.dependencies]
log = { version = "0.4.17" }
serde = { version = "1.0.152", features = ["derive"] }
Expand All @@ -10,6 +19,5 @@ insta = { version = "1.31.0", feature = ["filters", "glob"] }
[profile.dev.package.insta]
opt-level = 3


[profile.dev.package.similar]
opt-level = 3
14 changes: 14 additions & 0 deletions enderpy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "enderpy"
description = "A typechecker for Python"
authors = ["Shaygan Hooshyari"]
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.71"
clap = { version = "4.2.7", features = ["derive"] }
parser = { path = "../parser" , version = "0.1.0" }
typechecker = { path = "../typechecker" , version = "0.1.0" }
38 changes: 38 additions & 0 deletions enderpy/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::path::PathBuf;

use clap::{Parser, Subcommand};

/// Enderpy CLI
#[derive(Parser)]
#[command(name = "Enderpy", author, version, about)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}

#[derive(Subcommand)]
pub enum Commands {
/// Print lexer tokens
Tokenize {
/// Path to source file
file: PathBuf,
},
/// Print abstract syntax tree
Parse {
/// Path to source file
file: PathBuf,
},
/// Type check
Check { path: PathBuf },
/// Symbol table
Symbols { path: PathBuf },

/// Watch changes to type check
Watch,
}

#[test]
fn verify_cli() {
use clap::CommandFactory;
Cli::command().debug_assert()
}
105 changes: 105 additions & 0 deletions enderpy/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use anyhow::{anyhow, Result, bail};
use clap::Parser as ClapParser;
use cli::{Cli, Commands};
use parser::{token, Lexer, Parser};
use std::{fs, path::PathBuf};
use typechecker::{
build::{BuildManager, BuildSource},
settings::{ImportDiscovery, Settings}, project::find_project_root,
};

mod cli;

fn main() -> Result<()> {
let cli = Cli::parse();

match &cli.command {
Commands::Tokenize { file } => tokenize(file),
Commands::Parse { file } => parse(file),
Commands::Check { path } => check(path),
Commands::Watch => watch(),
Commands::Symbols { path } => symbols(path),
}
}

fn symbols(path: &PathBuf) -> std::result::Result<(), anyhow::Error> {
let source = fs::read_to_string(path)?;
let initial_source = BuildSource {
path: path.to_owned(),
module: String::from("test"),
source,
followed: false,
};
let dir_of_path = path.parent().unwrap();
let python_executable = Some( get_python_executable()? );
let settings = Settings { debug: true, root: dir_of_path.to_path_buf(), import_discovery: ImportDiscovery { python_executable } };

let mut manager = BuildManager::new(vec![initial_source], settings);
manager.build();

for (name, module) in manager.modules.iter() {
println!("{}", name);
println!("{}", module.get_symbol_table());
}

Ok(())
}

fn get_python_executable() -> Result<PathBuf> {
let output = std::process::Command::new("python")
.arg("-c")
.arg("import sys; print(sys.executable)")
.output()?;
let path = String::from_utf8(output.stdout)?;
Ok(PathBuf::from(path))
}

fn tokenize(file: &PathBuf) -> Result<()> {
let source = fs::read_to_string(file)?;
let mut lexer = Lexer::new(&source);
let mut tokens = Vec::new();
while let Ok(token) = lexer.next_token() {
tokens.push(token.clone());
if token.kind == token::Kind::Eof {
break;
}
}
println!("{:#?}", tokens);
Ok(())
}

fn parse(file: &PathBuf) -> Result<()> {
let source = fs::read_to_string(file)?;
let mut parser = Parser::new(source);
let ast = parser.parse();
println!("{:#?}", ast);
Ok(())
}

fn check(path: &PathBuf) -> Result<()> {
if path.is_dir() {
return bail!("Path must be a file");
}
let source = fs::read_to_string(path)?;
let initial_source = BuildSource {
path: path.to_owned(),
module: String::from("test"),
source,
followed: false,
};
let root = find_project_root(path);
let python_executable = Some( get_python_executable()? );
let settings = Settings { debug: true, root: PathBuf::from(root), import_discovery: ImportDiscovery { python_executable } };
let mut build_manager = BuildManager::new(vec![initial_source], settings);
build_manager.type_check();

for err in build_manager.get_errors() {
println!("{:#?}", err);
}

Ok(())
}

fn watch() -> Result<()> {
todo!()
}
12 changes: 10 additions & 2 deletions parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
[package]
name = "parser"
name = "enderpy-python-parser"
description = "A Python parser written in Rust"
version = "0.1.0"
edition = "2021"
authors = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
documentation = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
readme = "../../README.md"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
Expand Down
2 changes: 2 additions & 0 deletions typechecker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ config = "0.13.3"
serde = { version = "1.0.164", features = ["derive"] }
miette = "5.10.0"
thiserror = "1.0.48"
log.workspace = true
env_logger = "0.10.0"

[dev-dependencies]
insta = { version = "1.28.0", features = ["yaml"] }
25 changes: 19 additions & 6 deletions typechecker/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{collections::HashMap, path::PathBuf};
use log::info;

use parser::Parser;
use ruff_python_resolver::config::Config;
Expand All @@ -21,9 +22,17 @@ pub struct BuildSource {
pub followed: bool,
}

#[derive(Debug, Clone)]
pub struct BuildError {
pub msg: String,
pub line: u32,
pub start: u32,
pub end: u32,
}

#[derive(Debug)]
pub struct BuildManager {
errors: Vec<String>,
errors: Vec<BuildError>,
pub modules: HashMap<String, State>,
options: Settings,
}
Expand Down Expand Up @@ -64,7 +73,7 @@ impl BuildManager {
self.modules.insert(module, State::new(file));
}

pub fn get_errors(&self) -> Vec<String> {
pub fn get_errors(&self) -> Vec<BuildError> {
self.errors.clone()
}

Expand All @@ -81,20 +90,20 @@ impl BuildManager {

pub fn get_module_name(path: &PathBuf) -> String {
path.to_str()
.unwrap()
.unwrap_or_default()
.replace("/", ".")
.replace("\\", ".")
}

// Entry point to analyze the program
pub fn build(&mut self) {
let files = self.modules.values().collect();
info!("files: {:#?}", files);
let new_files = self.gather_files(files);
for file in new_files {
self.modules.insert(file.file.module_name.clone(), file);
}

println!("modules: {:#?}", self.modules.keys().collect::<Vec<&String>>());
self.pre_analysis();
}

Expand All @@ -116,8 +125,12 @@ impl BuildManager {
}
for error in checker.errors {
let line = get_line_number_of_character_position(&state.1.file.source, error.start);
let error = format!("{} line {}: {}", state.0, line, error.msg);
self.errors.push(error);
self.errors.push(BuildError {
msg: error.msg,
line: line as u32,
start: error.start as u32,
end: error.end as u32,
});
}
}
}
Expand Down
1 change: 1 addition & 0 deletions typechecker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod type_check;
pub mod build;
pub mod semantic_analyzer;
pub mod settings;
pub mod project;

pub use parser::ast;
pub use ruff_python_resolver;
18 changes: 18 additions & 0 deletions typechecker/src/project.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::path::{Path, PathBuf};
const PROJECT_ROOT_MARKERS: [&str; 2] = ["__init__.py", "pyproject.toml"];

pub fn find_project_root(path: &PathBuf) -> &Path {
let root = path
.ancestors()
.find(|p| PROJECT_ROOT_MARKERS.iter().any(|m| p.join(m).exists()));
match root {
Some(root) => root,
None => {
if path.is_dir() {
path
} else {
path.parent().unwrap_or(path)
}
}
}
}

0 comments on commit a423736

Please sign in to comment.