#![doc = include_str!("../README.md")]
pub mod expr;
pub mod error;
pub mod instruction;
pub mod item;
pub mod sym_table;
use cas_compute::{consts::all as all_consts, funcs::all as all_funcs};
use cas_error::Error;
use cas_parser::parser::ast::{FuncHeader, LitSym, Stmt};
use error::{
OverrideBuiltinConstant,
OverrideBuiltinFunction,
UnknownVariable,
};
use std::collections::{HashMap, HashSet};
use expr::compile_stmts;
pub use instruction::{Instruction, InstructionKind};
use item::{FuncDecl, Item, Symbol, SymbolDecl};
use std::ops::Range;
use sym_table::{Scope, SymbolTable};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Label(usize);
#[derive(Clone, Debug, Default)]
pub struct CompilerState {
pub loop_start: Option<Label>,
pub loop_end: Option<Label>,
pub last_stmt: bool,
pub top_level_assign: bool,
}
#[derive(Clone, Debug, Default)]
pub struct Chunk {
pub instructions: Vec<Instruction>,
pub arity: usize,
}
impl Chunk {
pub fn new(arity: usize) -> Self {
Self {
instructions: Vec::new(),
arity,
}
}
}
pub struct NewChunk {
pub id: usize,
pub chunk: usize,
pub captures: HashSet<usize>,
}
fn check_override_builtin(symbol: &LitSym) -> Result<(), Error> {
if all_consts().contains(&*symbol.name) {
return Err(Error::new(vec![symbol.span.clone()], OverrideBuiltinConstant {
name: symbol.name.to_string(),
}));
}
if all_funcs().contains_key(&*symbol.name) {
return Err(Error::new(vec![symbol.span.clone()], OverrideBuiltinFunction {
name: symbol.name.to_string(),
}));
}
Ok(())
}
fn resolve_builtin(symbol: &LitSym) -> Option<Symbol> {
all_consts()
.get(&*symbol.name)
.map(|name| Symbol::Builtin(name))
.or_else(|| {
all_funcs()
.get(&*symbol.name)
.map(|func| Symbol::Builtin(func.name()))
})
}
#[derive(Clone, Debug)]
pub struct Compiler {
pub chunks: Vec<Chunk>,
pub labels: HashMap<Label, Option<(usize, usize)>>,
pub sym_table: SymbolTable,
chunk: usize,
next_item_id: usize,
state: CompilerState,
}
impl Default for Compiler {
fn default() -> Self {
Self {
chunks: vec![Chunk::default()], labels: Default::default(),
sym_table: Default::default(),
chunk: 0,
next_item_id: 0,
state: Default::default(),
}
}
}
impl Compiler {
pub fn new() -> Self {
Self::default()
}
pub fn compile<T: Compile>(expr: T) -> Result<Self, Error> {
let mut compiler = Self::new();
expr.compile(&mut compiler)?;
Ok(compiler)
}
pub fn compile_program(stmts: Vec<Stmt>) -> Result<Self, Error> {
let mut compiler = Self::new();
compile_stmts(&stmts, &mut compiler)?;
Ok(compiler)
}
pub fn with_state<F, G>(&mut self, modify_state: F, compile: G) -> Result<(), Error>
where
F: FnOnce(&mut CompilerState),
G: FnOnce(&mut Self) -> Result<(), Error>,
{
let old_state = self.state.clone();
modify_state(&mut self.state);
compile(&mut *self)?;
self.state = old_state;
Ok(())
}
pub fn chunk(&self) -> &Chunk {
self.chunks.get(self.chunk).unwrap()
}
pub fn chunk_mut(&mut self) -> &mut Chunk {
self.chunks.get_mut(self.chunk).unwrap()
}
pub fn add_item(&mut self, symbol: &LitSym, item: Item) -> Result<(), Error> {
if self.sym_table.is_global_scope() {
check_override_builtin(symbol)?;
}
self.sym_table.insert(symbol.name.to_string(), item);
Ok(())
}
pub fn new_scope<F>(&mut self, f: F) -> Result<(), Error>
where F: FnOnce(&mut Compiler) -> Result<(), Error>
{
self.sym_table.enter_scope();
f(self)?;
self.sym_table.exit_scope();
Ok(())
}
pub(crate) fn new_scope_get<F>(&mut self, f: F) -> Result<&Scope, Error>
where F: FnOnce(&mut Compiler) -> Result<(), Error>
{
self.sym_table.enter_scope();
f(self)?;
Ok(self.sym_table.exit_scope_get())
}
pub fn new_chunk<F>(&mut self, header: &FuncHeader, f: F) -> Result<NewChunk, Error>
where F: FnOnce(&mut Compiler) -> Result<(), Error>
{
let old_chunk_idx = self.chunk;
self.chunks.push(Chunk::new(header.params.len()));
let new_chunk_idx = self.chunks.len() - 1;
let id = self.next_item_id;
self.add_item(
&header.name,
Item::Func(FuncDecl::new(
id,
self.sym_table.next_id(),
new_chunk_idx,
header.params.clone(),
)),
)?;
self.next_item_id += 1;
self.chunk = new_chunk_idx;
let scope = self.new_scope_get(f)?;
let captures = scope.captures()
.iter()
.map(|symbol| match symbol {
Symbol::User(id) => *id,
_ => unreachable!(),
})
.collect();
self.chunk = old_chunk_idx;
Ok(NewChunk {
id,
chunk: new_chunk_idx,
captures,
})
}
pub(crate) fn new_chunk_get<F>(&mut self, f: F) -> Result<Chunk, Error>
where F: FnOnce(&mut Compiler) -> Result<(), Error>
{
let old_chunk_idx = self.chunk;
self.chunks.push(Chunk::default());
let new_chunk_idx = self.chunks.len() - 1;
self.chunk = new_chunk_idx;
f(self)?;
let chunk = self.chunks.pop().unwrap();
self.chunk = old_chunk_idx;
Ok(chunk)
}
pub fn add_symbol(&mut self, symbol: &LitSym) -> Result<usize, Error> {
let id = self.next_item_id;
self.add_item(symbol, Item::Symbol(SymbolDecl { id }))?;
self.next_item_id += 1;
Ok(id)
}
pub fn resolve_user_symbol_or_insert(&mut self, symbol: &LitSym) -> Result<usize, Error> {
if let Some(item) = self.sym_table.resolve_item(&symbol.name) {
Ok(item.id())
} else {
self.add_symbol(symbol)
}
}
pub fn resolve_symbol(&mut self, symbol: &LitSym) -> Result<Symbol, Error> {
if let Some(symbol) = self.sym_table.resolve_item_mark_capture(&symbol.name) {
Ok(symbol)
} else {
if let Some(symbol) = resolve_builtin(symbol) {
Ok(symbol)
} else {
Err(Error::new(vec![symbol.span.clone()], UnknownVariable {
name: symbol.name.clone(),
}))
}
}
}
pub fn add_instr(&mut self, instruction: impl Into<Instruction>) {
let chunk = self.chunk_mut();
chunk.instructions.push(instruction.into());
}
pub fn add_instr_with_spans(
&mut self,
instruction: impl Into<Instruction>,
spans: Vec<Range<usize>>,
) {
let mut instruction = instruction.into();
instruction.spans = spans;
let chunk = self.chunk_mut();
chunk.instructions.push(instruction);
}
pub(crate) fn add_chunk_instrs(&mut self, new_chunk: Chunk) {
let chunk = self.chunk_mut();
chunk.instructions.extend(new_chunk.instructions);
}
pub fn replace_instr(&mut self, idx: usize, instruction: Instruction) {
let chunk = self.chunk_mut();
chunk.instructions[idx] = instruction;
}
pub fn new_unassociated_label(&mut self) -> Label {
let label = Label(self.labels.len());
self.labels.insert(label, None);
label
}
pub fn new_end_label(&mut self) -> Label {
let label = Label(self.labels.len());
let chunk_instrs = self.chunk().instructions.len();
self.labels.insert(label, Some((self.chunk, chunk_instrs)));
label
}
pub fn set_end_label(&mut self, label: Label) {
let chunk_instrs = self.chunk().instructions.len();
self.labels.insert(label, Some((self.chunk, chunk_instrs)));
}
}
pub trait Compile {
fn compile(&self, compiler: &mut Compiler) -> Result<(), Error>;
}
impl<T: Compile> Compile for &T {
fn compile(&self, compiler: &mut Compiler) -> Result<(), Error> {
(*self).compile(compiler)
}
}
#[cfg(test)]
mod tests {
use super::*;
use cas_parser::parser::{ast::stmt::Stmt, Parser};
fn compile(source: &str) -> Result<Compiler, Error> {
let mut parser = Parser::new(source);
let stmts = parser.try_parse_full_many::<Stmt>().unwrap();
Compiler::compile_program(stmts)
}
#[test]
fn function_declaration() {
compile("f(x) = {
g(x) = {
h(x) = x
h(x) % 2 == 0
}
x % 3 == 0 && g(x)
}
f(18)").unwrap();
}
#[test]
fn scoping() {
let err = compile("f() = j + 6
g() = {
j = 10
f()
}
g()").unwrap_err();
assert_eq!(err.spans[0], 6..7);
}
#[test]
fn advanced_scoping() {
compile("{ x = 25 }; x").unwrap_err();
compile("x = 25; { x *= 2 }; x").unwrap();
compile("f(x) = y = 25; y").unwrap_err();
compile("f(x) = { y = 25 }; y").unwrap_err();
compile("loop { t = rand(); if t < 0.2 break t }; t").unwrap_err();
compile("a = for i in 1..5 { t = i }; t").unwrap_err();
compile("loop t = break 2; t").unwrap_err();
compile("while true break t = 2; t").unwrap_err();
compile("(sum n in 1..5 of n) > n").unwrap_err();
}
#[test]
fn scoping_with_compiler_declared_variables() {
compile("for n in 1..n then print(n)").unwrap_err();
compile("n = 50; for n in 0..n then print(n)").unwrap();
compile("for n in n..50 then print(n)").unwrap_err();
}
#[test]
fn shadowing() {
compile("pi = 5").unwrap_err();
compile("f() = pi = 5").unwrap(); }
#[test]
fn no_override_builtin() {
compile("i = 5").unwrap_err(); compile("pi(x) = x").unwrap_err(); compile("sqrt = 3").unwrap_err(); compile("ncr(a) = a").unwrap_err(); }
#[test]
fn define_and_call() {
compile("f(x) = return x + 1/sqrt(x)
g(x, y) = f(x) + f(y)
g(2, 3)").unwrap();
}
#[test]
fn refer_to_parent() {
compile("f(x) = g(x) = h(x) = f(x)").unwrap();
compile("f(x) = g(x) = h(x) = g(x)").unwrap();
compile("f(x) = g(x) = h(x) = h(x)").unwrap();
}
#[test]
fn derivative() {
compile("f(x) = x^2; f'(2)").unwrap();
compile("ncr''(5, 3)").unwrap();
}
#[test]
fn list_index() {
compile("arr = [1, 2, 3]
arr[0] = 5
arr[0] + arr[1] + arr[2] == 10").unwrap();
}
}