Compare commits

...

2 commits

Author SHA1 Message Date
eebcbf8d1e refactor 2024-12-02 04:38:08 -06:00
4249d67308 wip compiler 2024-12-02 04:29:18 -06:00
10 changed files with 284 additions and 117 deletions

30
README.md Normal file
View file

@ -0,0 +1,30 @@
# AOC Lang
This is a language I am writing to complete Advent of Code 2024.
See `examples/*.aoc` for the programs.
Goals/Features:
- Interpreter
- Lambda functions
Future goals/features:
- Fix the parser
- Error reporting (i.e some kind of graph?)
- Language features
- Records
- Enums
- Type checking
- Effects
- Compiler
- Codegen
- x86
- ARM
- Garbage collector
- JIT
- REPL
I will probably not finish during AOC 2024.
I'll continue working on it over time though.

View file

@ -0,0 +1,76 @@
use std::collections::HashSet;
use crate::ast::{Expr, Stmt};
pub fn free_variables_stmt_rec(
st: &Stmt,
ctx: &mut HashSet<String>,
s: &mut HashSet<String>,
) {
match st {
Stmt::Input(_, _) => {}
Stmt::Let(n, expr) => {
free_variables_expr_rec(expr, ctx, s);
ctx.insert(n.to_owned());
}
Stmt::Print(expr) => free_variables_expr_rec(expr, ctx, s),
}
}
pub fn free_variables_expr_rec(
e: &Expr,
ctx: &mut HashSet<String>,
s: &mut HashSet<String>,
) {
match e {
Expr::Block(vec, expr) => {
for v in vec {
free_variables_stmt_rec(v, ctx, s);
}
if let Some(expr) = expr {
free_variables_expr_rec(expr, ctx, s);
}
}
Expr::Array(vec) => {
for v in vec {
free_variables_expr_rec(v, ctx, s);
}
}
Expr::Bool(_) => {}
Expr::Int(_) => {}
Expr::Ident(n) => {
if !ctx.contains(n) {
s.insert(n.to_owned());
}
}
Expr::IfThenElse(expr, expr1, expr2) => {
free_variables_expr_rec(expr, ctx, s);
free_variables_expr_rec(expr1, ctx, s);
free_variables_expr_rec(expr2, ctx, s);
}
Expr::BinOp(expr, _, expr1) => {
free_variables_expr_rec(expr, ctx, s);
free_variables_expr_rec(expr1, ctx, s);
}
Expr::Field(expr, _) => {
free_variables_expr_rec(expr, ctx, s);
}
Expr::Call(expr, vec) => {
free_variables_expr_rec(expr, ctx, s);
for v in vec {
free_variables_expr_rec(v, ctx, s);
}
}
Expr::Index(expr, expr1) => {
free_variables_expr_rec(expr, ctx, s);
free_variables_expr_rec(expr1, ctx, s);
}
Expr::Lambda(vec, expr) => {
let mut new_set = ctx.clone();
for v in vec {
new_set.insert(v.to_owned());
}
free_variables_expr_rec(expr, &mut new_set, s);
}
}
}

1
src/algo/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod free_variables;

5
src/arch/aarch64.rs Normal file
View file

@ -0,0 +1,5 @@
use crate::ir::{IrBlock, IrProgram};
pub enum ISA {}
pub fn compile_program(program: IrProgram<ISA>) {}

1
src/arch/mod.rs Normal file
View file

@ -0,0 +1 @@
mod aarch64;

View file

@ -1 +1,84 @@
use std::{
env::consts::{ARCH, OS},
fs,
path::PathBuf,
};
use anyhow::{bail, Result};
use crate::parser::parse;
#[derive(Debug)]
pub struct TargetTriple {
arch: Arch,
vendor: Vendor,
os: Os,
}
impl TargetTriple {
pub fn detect_build_machine() -> Result<Self> {
Ok(Self {
arch: Arch::detect_build_machine()?,
vendor: Vendor::detect_build_machine()?,
os: Os::detect_build_machine()?,
})
}
}
#[derive(Debug)]
pub enum Arch {
Aarch64,
X86_64,
}
impl Arch {
pub fn detect_build_machine() -> Result<Self> {
Ok(match ARCH {
"x86_64" => Arch::X86_64,
"aarch64" => Arch::Aarch64,
_ => bail!("could not determine arch from {ARCH} or not supported"),
})
}
}
#[derive(Debug)]
pub enum Vendor {
Apple,
Pc,
Unknown,
}
impl Vendor {
pub fn detect_build_machine() -> Result<Self> {
Ok(match OS {
"apple" | "macos" => Vendor::Apple,
"linux" => Vendor::Unknown,
_ => bail!("could not determine vendor from {OS} or not supported"),
})
}
}
#[derive(Debug)]
pub enum Os {
LinuxGnu,
WindowsGnu,
WindowsMsvc,
Darwin,
}
impl Os {
pub fn detect_build_machine() -> Result<Self> {
Ok(match OS {
"apple" | "macos" => Os::Darwin,
"linux" => Os::LinuxGnu,
_ => bail!("could not determine os from {OS} or not supported"),
})
}
}
pub fn compile(path: PathBuf, target: TargetTriple) -> Result<()> {
let source = fs::read_to_string(&path)?;
let tree = parse(&path, &source)?;
Ok(())
}

View file

@ -7,7 +7,10 @@ use std::{
use anyhow::{bail, ensure, Context, Result};
use once_cell::unsync::OnceCell;
use crate::ast::{Expr, Op, Stmt, Value};
use crate::{
algo::free_variables::free_variables_expr_rec,
ast::{Expr, Op, Stmt, Value},
};
thread_local! {
static PARENT: OnceCell<PathBuf> = OnceCell::new();
@ -324,76 +327,3 @@ fn insert_builtins(ctx: &mut HashMap<String, Value>) {
}
ctx.insert("sum".to_owned(), Value::Builtin(sum));
}
fn free_variables_stmt_rec(
st: &Stmt,
ctx: &mut HashSet<String>,
s: &mut HashSet<String>,
) {
match st {
Stmt::Input(_, _) => {}
Stmt::Let(n, expr) => {
free_variables_expr_rec(expr, ctx, s);
ctx.insert(n.to_owned());
}
Stmt::Print(expr) => free_variables_expr_rec(expr, ctx, s),
}
}
fn free_variables_expr_rec(
e: &Expr,
ctx: &mut HashSet<String>,
s: &mut HashSet<String>,
) {
match e {
Expr::Block(vec, expr) => {
for v in vec {
free_variables_stmt_rec(v, ctx, s);
}
if let Some(expr) = expr {
free_variables_expr_rec(expr, ctx, s);
}
}
Expr::Array(vec) => {
for v in vec {
free_variables_expr_rec(v, ctx, s);
}
}
Expr::Bool(_) => {}
Expr::Int(_) => {}
Expr::Ident(n) => {
if !ctx.contains(n) {
s.insert(n.to_owned());
}
}
Expr::IfThenElse(expr, expr1, expr2) => {
free_variables_expr_rec(expr, ctx, s);
free_variables_expr_rec(expr1, ctx, s);
free_variables_expr_rec(expr2, ctx, s);
}
Expr::BinOp(expr, _, expr1) => {
free_variables_expr_rec(expr, ctx, s);
free_variables_expr_rec(expr1, ctx, s);
}
Expr::Field(expr, _) => {
free_variables_expr_rec(expr, ctx, s);
}
Expr::Call(expr, vec) => {
free_variables_expr_rec(expr, ctx, s);
for v in vec {
free_variables_expr_rec(v, ctx, s);
}
}
Expr::Index(expr, expr1) => {
free_variables_expr_rec(expr, ctx, s);
free_variables_expr_rec(expr1, ctx, s);
}
Expr::Lambda(vec, expr) => {
let mut new_set = ctx.clone();
for v in vec {
new_set.insert(v.to_owned());
}
free_variables_expr_rec(expr, &mut new_set, s);
}
}
}

11
src/ir.rs Normal file
View file

@ -0,0 +1,11 @@
pub struct IrProgram<ISA> {
blocks: IrBlock<ISA>,
}
pub struct IrBlock<ISA> {
instructions: Vec<IrInstruction<ISA>>,
}
pub enum IrInstruction<ISA> {
Raw(ISA),
}

View file

@ -1,6 +1,10 @@
mod algo;
mod arch;
mod ast;
mod compiler;
mod interpreter;
mod ir;
mod parser;
use std::{fs, path::PathBuf, process::exit};
@ -14,8 +18,11 @@ use codespan_reporting::{
termcolor::{ColorChoice, StandardStream},
},
};
use interpreter::run;
use compiler::{compile, TargetTriple};
use lalrpop_util::{lalrpop_mod, ParseError};
use parser::parse;
use crate::interpreter::run;
lalrpop_mod!(grammar);
@ -41,50 +48,14 @@ fn main() -> Result<()> {
let opt = Opt::parse();
match opt.command {
Command::Compile { path } => todo!(),
Command::Compile { path } => {
let target_triple = TargetTriple::detect_build_machine()?;
println!("Target triple: {target_triple:?}");
compile(path, target_triple)?;
}
Command::Run { path } => {
let source = fs::read_to_string(&path)?;
let parser = grammar::ProgramParser::new();
let tree = match parser.parse(&source) {
Ok(v) => v,
Err(e) => {
let mut files = SimpleFiles::new();
let file_id = files.add(path.display().to_string(), &source);
let diagnostic = match e {
ParseError::InvalidToken { location } => Diagnostic::error()
.with_message("invalid token")
.with_labels(vec![Label::primary(file_id, location..location)]),
ParseError::UnrecognizedEof { location, expected } => {
Diagnostic::error()
.with_message(format!(
"unrecognized eof, expected [{}]",
expected.join(", ")
))
.with_labels(vec![Label::primary(file_id, location..location)])
}
ParseError::UnrecognizedToken { token, expected } => {
Diagnostic::error()
.with_message(format!(
"unrecognized token {}, expecting [{}]",
token.1,
expected.join(", ")
))
.with_labels(vec![Label::primary(file_id, token.0..token.2)])
}
ParseError::ExtraToken { token } => Diagnostic::error()
.with_message(format!("extra token {}", token.1))
.with_labels(vec![Label::primary(file_id, token.0..token.2)]),
ParseError::User { error } => {
Diagnostic::error().with_message(error)
}
};
let writer = StandardStream::stderr(ColorChoice::Always);
let config = codespan_reporting::term::Config::default();
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
exit(1);
}
};
let tree = parse(&path, &source)?;
run(path, tree)?;
}

59
src/parser.rs Normal file
View file

@ -0,0 +1,59 @@
use std::{path::Path, process::exit};
use anyhow::Result;
use codespan_reporting::{
diagnostic::{Diagnostic, Label},
files::SimpleFiles,
term::{
self,
termcolor::{ColorChoice, StandardStream},
},
};
use lalrpop_util::ParseError;
use crate::{ast::Stmt, grammar::ProgramParser};
pub fn parse(path: impl AsRef<Path>, source: &str) -> Result<Vec<Stmt>> {
let parser = ProgramParser::new();
let tree = match parser.parse(source) {
Ok(v) => v,
Err(e) => {
let mut files = SimpleFiles::new();
let file_id = files.add(path.as_ref().display().to_string(), &source);
let diagnostic = match e {
ParseError::InvalidToken { location } => Diagnostic::error()
.with_message("invalid token")
.with_labels(vec![Label::primary(file_id, location..location)]),
ParseError::UnrecognizedEof { location, expected } => {
Diagnostic::error()
.with_message(format!(
"unrecognized eof, expected [{}]",
expected.join(", ")
))
.with_labels(vec![Label::primary(file_id, location..location)])
}
ParseError::UnrecognizedToken { token, expected } => {
Diagnostic::error()
.with_message(format!(
"unrecognized token {}, expecting [{}]",
token.1,
expected.join(", ")
))
.with_labels(vec![Label::primary(file_id, token.0..token.2)])
}
ParseError::ExtraToken { token } => Diagnostic::error()
.with_message(format!("extra token {}", token.1))
.with_labels(vec![Label::primary(file_id, token.0..token.2)]),
ParseError::User { error } => Diagnostic::error().with_message(error),
};
let writer = StandardStream::stderr(ColorChoice::Always);
let config = codespan_reporting::term::Config::default();
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
// TODO: Report the error up
exit(1);
}
};
Ok(tree)
}