Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
369 changes: 360 additions & 9 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ path = "src/main.rs"
logos = "0.14"
thiserror = "1.0"
miette = { version = "7.0", features = ["fancy"] }
rustyline = { version = "14.0", features = ["derive"] }
dirs = "5.0"

[dev-dependencies]
pretty_assertions = "1.4"
Expand Down
30 changes: 30 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,19 @@ pub struct QualifiedName {
pub span: Span,
}

/// Generic type parameter: `<T: Trait>` or just `<T>`
#[derive(Debug, Clone)]
pub struct TypeParam {
pub name: String,
pub bounds: Vec<String>, // Trait bounds (future use)
}

/// Function definition
#[derive(Debug, Clone)]
pub struct FunctionDef {
pub emote: Option<EmoteTag>,
pub name: String,
pub type_params: Vec<TypeParam>, // Generic type parameters: <T, U>
pub params: Vec<Parameter>,
pub return_type: Option<Type>,
pub hello: Option<String>,
Expand Down Expand Up @@ -290,6 +298,24 @@ pub enum Literal {
Float(f64),
String(String),
Bool(bool),
Unit, // The () value
}

/// Lambda expression body
#[derive(Debug, Clone)]
pub enum LambdaBody {
/// Expression body: `|x| -> x + 1`
Expr(Box<Spanned<Expr>>),
/// Block body: `|x| { give back x + 1; }`
Block(Vec<Statement>),
}

/// Lambda/closure expression: `|x, y| -> expr` or `|x, y| { ... }`
#[derive(Debug, Clone)]
pub struct LambdaExpr {
pub params: Vec<Parameter>,
pub return_type: Option<Type>,
pub body: LambdaBody,
}

/// Lambda expression body
Expand Down Expand Up @@ -384,6 +410,10 @@ pub enum Type {
Reference(Box<Type>),
/// Function type: (T1, T2) -> R
Function(Vec<Type>, Box<Type>),
/// Generic/parameterized type: Result<T, E>, Map<K, V>
Generic(String, Vec<Type>),
/// Type parameter reference: T (used inside generic functions)
TypeVar(String),
}

/// Type definition: `type Name = ...;`
Expand Down
1 change: 1 addition & 0 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ impl Interpreter {
Literal::Float(n) => Value::Float(*n),
Literal::String(s) => Value::String(s.clone()),
Literal::Bool(b) => Value::Bool(*b),
Literal::Unit => Value::Unit,
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ pub mod ast;
pub mod interpreter;
pub mod lexer;
pub mod parser;
pub mod repl;
pub mod typechecker;

pub use ast::Program;
pub use interpreter::Interpreter;
pub use lexer::Lexer;
pub use parser::Parser;
pub use repl::Repl;
pub use typechecker::TypeChecker;
40 changes: 39 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use miette::Result;
use std::env;
use std::fs;
use wokelang::{Interpreter, Lexer, Parser};
use wokelang::{Interpreter, Lexer, Parser, Repl, TypeChecker};

fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
Expand All @@ -10,14 +10,24 @@ fn main() -> Result<()> {
println!("WokeLang v0.1.0 - A human-centered, consent-driven programming language");
println!();
println!("Usage: woke <file.woke> Run a WokeLang program");
println!(" woke repl Start interactive REPL");
println!(" woke --tokenize <file> Show lexer tokens");
println!(" woke --parse <file> Show parsed AST");
println!(" woke --typecheck <file> Type-check without running");
return Ok(());
}

// Check for REPL mode first
if args.get(1).map(|s| s.as_str()) == Some("repl") {
let mut repl = Repl::new().expect("Failed to create REPL");
repl.run().expect("REPL error");
return Ok(());
}

let (mode, file_path) = match args.get(1).map(|s| s.as_str()) {
Some("--tokenize") => ("tokenize", args.get(2)),
Some("--parse") => ("parse", args.get(2)),
Some("--typecheck") => ("typecheck", args.get(2)),
Some(_) => ("run", Some(&args[1])),
None => {
eprintln!("Expected file path");
Expand Down Expand Up @@ -63,10 +73,38 @@ fn main() -> Result<()> {
}
}
}
"typecheck" => {
let mut parser = Parser::new(tokens, &source);
match parser.parse() {
Ok(program) => {
let mut typechecker = TypeChecker::new();
match typechecker.check_program(&program) {
Ok(()) => {
println!("Type check passed!");
}
Err(e) => {
eprintln!("Type error: {}", e);
}
}
}
Err(e) => {
eprintln!("{:?}", miette::Report::new(e));
}
}
}
"run" => {
let mut parser = Parser::new(tokens, &source);
match parser.parse() {
Ok(program) => {
// Type check first
let mut typechecker = TypeChecker::new();
if let Err(e) = typechecker.check_program(&program) {
eprintln!("Type error: {}", e);
eprintln!("\nType checking failed. Not running.");
return Ok(());
}

// Run the program
let mut interpreter = Interpreter::new();
if let Err(e) = interpreter.run(&program) {
eprintln!("Runtime error: {}", e);
Expand Down
82 changes: 81 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ impl<'src> Parser<'src> {
}

let name = self.expect_identifier()?;

// Parse optional type parameters: <T, U>
let type_params = if self.check(&Token::Less) {
self.parse_type_params()?
} else {
Vec::new()
};

self.expect(Token::LParen)?;
let params = self.parse_parameter_list()?;
self.expect(Token::RParen)?;
Expand Down Expand Up @@ -130,6 +138,7 @@ impl<'src> Parser<'src> {
Ok(FunctionDef {
emote,
name,
type_params,
params,
return_type,
hello,
Expand Down Expand Up @@ -171,6 +180,45 @@ impl<'src> Parser<'src> {
})
}

/// Parse type parameters: <T, U: Bound>
fn parse_type_params(&mut self) -> Result<Vec<TypeParam>, ParseError> {
self.expect(Token::Less)?; // Consume '<'
let mut params = Vec::new();

if self.check(&Token::Greater) {
self.advance();
return Ok(params);
}

params.push(self.parse_type_param()?);
while self.check(&Token::Comma) {
self.advance();
params.push(self.parse_type_param()?);
}

self.expect(Token::Greater)?; // Consume '>'
Ok(params)
}

/// Parse a single type parameter: T or T: Bound
fn parse_type_param(&mut self) -> Result<TypeParam, ParseError> {
let name = self.expect_identifier()?;
let bounds = if self.check(&Token::Colon) {
self.advance();
// Parse bounds separated by +
let mut bounds = Vec::new();
bounds.push(self.expect_identifier()?);
while self.check(&Token::Plus) {
self.advance();
bounds.push(self.expect_identifier()?);
}
bounds
} else {
Vec::new()
};
Ok(TypeParam { name, bounds })
}

// === Consent Block ===

fn parse_consent_block(&mut self) -> Result<ConsentBlock, ParseError> {
Expand Down Expand Up @@ -515,12 +563,38 @@ impl<'src> Parser<'src> {
Some(Token::Identifier(name)) => {
let name = name.clone();
self.advance();
Ok(Type::Basic(name))
// Check for generic type arguments: Result<T, E>
if self.check(&Token::Less) {
let args = self.parse_type_args()?;
Ok(Type::Generic(name, args))
} else {
Ok(Type::Basic(name))
}
}
_ => Err(self.error("Expected type")),
}
}

/// Parse type arguments: <Int, String>
fn parse_type_args(&mut self) -> Result<Vec<Type>, ParseError> {
self.expect(Token::Less)?;
let mut args = Vec::new();

if self.check(&Token::Greater) {
self.advance();
return Ok(args);
}

args.push(self.parse_type()?);
while self.check(&Token::Comma) {
self.advance();
args.push(self.parse_type()?);
}

self.expect(Token::Greater)?;
Ok(args)
}

// === Statement Parsing ===

fn parse_statement_list(&mut self) -> Result<Vec<Statement>, ParseError> {
Expand Down Expand Up @@ -1118,6 +1192,12 @@ impl<'src> Parser<'src> {
}
Some(Token::LParen) => {
self.advance();
// Check for Unit literal: ()
if self.check(&Token::RParen) {
self.advance();
let end = self.previous_span().end;
return Ok(Spanned::new(Expr::Literal(Literal::Unit), start..end));
}
let expr = self.parse_expression()?;
self.expect(Token::RParen)?;
Ok(expr)
Expand Down
Loading
Loading