Compare commits
2 commits
61d0adb25b
...
eebcbf8d1e
Author | SHA1 | Date | |
---|---|---|---|
eebcbf8d1e | |||
4249d67308 |
10 changed files with 284 additions and 117 deletions
30
README.md
Normal file
30
README.md
Normal 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.
|
76
src/algo/free_variables.rs
Normal file
76
src/algo/free_variables.rs
Normal 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
1
src/algo/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod free_variables;
|
5
src/arch/aarch64.rs
Normal file
5
src/arch/aarch64.rs
Normal 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
1
src/arch/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
mod aarch64;
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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
11
src/ir.rs
Normal 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),
|
||||
}
|
57
src/main.rs
57
src/main.rs
|
@ -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
59
src/parser.rs
Normal 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)
|
||||
}
|
Loading…
Reference in a new issue