compiler ^^
This commit is contained in:
parent
a6c77f6633
commit
a80e9bbccb
@ -10,7 +10,7 @@ pub enum NodeKind {
|
|||||||
Error,
|
Error,
|
||||||
Id(u64),
|
Id(u64),
|
||||||
Int(i64),
|
Int(i64),
|
||||||
String(String),
|
Str(String),
|
||||||
Group(Box<Node>),
|
Group(Box<Node>),
|
||||||
Block(Vec<Node>),
|
Block(Vec<Node>),
|
||||||
Call {
|
Call {
|
||||||
@ -56,10 +56,10 @@ pub enum Type {
|
|||||||
Unit,
|
Unit,
|
||||||
I32,
|
I32,
|
||||||
U32,
|
U32,
|
||||||
String,
|
Str,
|
||||||
Fn {
|
Fn {
|
||||||
id: u64,
|
id: u64,
|
||||||
params: Vec<Node>,
|
params: Vec<Type>,
|
||||||
return_typ: Box<Type>,
|
return_typ: Box<Type>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ pub struct Checker<FnIdGen: IdGen = RandIdGen> {
|
|||||||
syms: Syms,
|
syms: Syms,
|
||||||
fn_id_gen: FnIdGen,
|
fn_id_gen: FnIdGen,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Checker {
|
impl Checker {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -71,7 +72,13 @@ impl<FnIdGen: IdGen> Checker<FnIdGen> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let params = params.into_iter().map(|(_, param)| param).collect();
|
let params = params
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, param)| {
|
||||||
|
let NodeKind::Param { subject: _, typ } = param.kind else {unreachable!()};
|
||||||
|
typ.as_ref().cloned().unwrap()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let parsed::Node::Id(id) = subject.as_ref() else { unreachable!() };
|
let parsed::Node::Id(id) = subject.as_ref() else { unreachable!() };
|
||||||
|
|
||||||
@ -132,7 +139,7 @@ impl<FnIdGen: IdGen> Checker<FnIdGen> {
|
|||||||
Type::I32
|
Type::I32
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
parsed::Node::String(value) => self.node(NodeKind::String(value.clone()), Type::String),
|
parsed::Node::Str(value) => self.node(NodeKind::Str(value.clone()), Type::Str),
|
||||||
parsed::Node::Group(expr) => {
|
parsed::Node::Group(expr) => {
|
||||||
let expr = self.check_expr(expr);
|
let expr = self.check_expr(expr);
|
||||||
let typ = expr.typ.clone();
|
let typ = expr.typ.clone();
|
||||||
@ -171,8 +178,8 @@ impl<FnIdGen: IdGen> Checker<FnIdGen> {
|
|||||||
}
|
}
|
||||||
if args
|
if args
|
||||||
.iter()
|
.iter()
|
||||||
.zip(params)
|
.zip(params.clone())
|
||||||
.map(|(arg, param)| self.compatible(&arg.typ, ¶m.typ))
|
.map(|(arg, param)| self.compatible(&arg.typ, ¶m))
|
||||||
.any(|is_compatible| !is_compatible)
|
.any(|is_compatible| !is_compatible)
|
||||||
{
|
{
|
||||||
self.error("incorrect args");
|
self.error("incorrect args");
|
||||||
@ -228,13 +235,24 @@ impl<FnIdGen: IdGen> Checker<FnIdGen> {
|
|||||||
let subject = Box::new(self.check_expr(subject));
|
let subject = Box::new(self.check_expr(subject));
|
||||||
let value = Box::new(self.check_expr(value));
|
let value = Box::new(self.check_expr(value));
|
||||||
|
|
||||||
let typ = if !self.compatible(&subject.typ, &value.typ) {
|
match subject.kind {
|
||||||
|
NodeKind::Error => {
|
||||||
|
return *subject;
|
||||||
|
}
|
||||||
|
NodeKind::Id(_) => {}
|
||||||
|
_ => {
|
||||||
|
self.error("cannot assign to expr");
|
||||||
|
return self.node(NodeKind::Error, Type::Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _typ = if !self.compatible(&subject.typ, &value.typ) {
|
||||||
self.error("incompatible types #3");
|
self.error("incompatible types #3");
|
||||||
Type::Error
|
Type::Error
|
||||||
} else {
|
} else {
|
||||||
subject.typ.clone()
|
subject.typ.clone()
|
||||||
};
|
};
|
||||||
self.node(NodeKind::Assign { subject, value }, typ)
|
self.node(NodeKind::Assign { subject, value }, Type::Unit)
|
||||||
}
|
}
|
||||||
parsed::Node::Let { subject, value } => {
|
parsed::Node::Let { subject, value } => {
|
||||||
let (subject, subject_typ) = match subject.as_ref() {
|
let (subject, subject_typ) = match subject.as_ref() {
|
||||||
@ -274,11 +292,11 @@ impl<FnIdGen: IdGen> Checker<FnIdGen> {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.node(NodeKind::Let { subject, value }, typ)
|
self.node(NodeKind::Let { subject, value }, Type::Unit)
|
||||||
}
|
}
|
||||||
parsed::Node::Fn {
|
parsed::Node::Fn {
|
||||||
subject,
|
subject,
|
||||||
params: _,
|
params,
|
||||||
return_typ: _,
|
return_typ: _,
|
||||||
body,
|
body,
|
||||||
} => {
|
} => {
|
||||||
@ -289,20 +307,33 @@ impl<FnIdGen: IdGen> Checker<FnIdGen> {
|
|||||||
return self.node(NodeKind::Error,Type::Error);
|
return self.node(NodeKind::Error,Type::Error);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Type::Fn { id: fn_id, params, return_typ } = sym.typ else {
|
let Type::Fn { id: fn_id, params: param_typs, return_typ } = sym.typ else {
|
||||||
self.error("redefintion");
|
self.error("redefintion");
|
||||||
return self.node(NodeKind::Error,Type::Error);
|
return self.node(NodeKind::Error,Type::Error);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.syms.enter_scope();
|
self.syms.enter_scope();
|
||||||
|
|
||||||
|
let params = params
|
||||||
|
.iter()
|
||||||
|
.zip(param_typs)
|
||||||
|
.map(|(param, typ)| {
|
||||||
|
let parsed::Node::Param { subject, .. } = param else { unreachable!() };
|
||||||
|
let parsed::Node::Id(id) = subject.as_ref() else { unreachable!() };
|
||||||
|
self.node(
|
||||||
|
NodeKind::Param {
|
||||||
|
subject: Box::new(self.node(NodeKind::Id(*id), Type::Unit)),
|
||||||
|
typ: Some(typ),
|
||||||
|
},
|
||||||
|
Type::Unit,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for param in ¶ms {
|
for param in ¶ms {
|
||||||
let NodeKind::Param {
|
let NodeKind::Param { ref subject, ref typ } = param.kind else { unreachable!() };
|
||||||
ref subject,
|
|
||||||
typ: Some(ref typ),
|
|
||||||
} = param.kind else { unreachable!() };
|
|
||||||
let NodeKind::Id(id) = subject.kind else { unreachable!() };
|
let NodeKind::Id(id) = subject.kind else { unreachable!() };
|
||||||
self.syms.define(id, typ.clone());
|
self.syms.define(id, typ.as_ref().cloned().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = Box::new(self.check_expr(body));
|
let body = Box::new(self.check_expr(body));
|
||||||
@ -415,7 +446,7 @@ fn test_checker() {
|
|||||||
table_id: 0,
|
table_id: 0,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
typ: I32,
|
typ: Unit,
|
||||||
table_id: 0,
|
table_id: 0,
|
||||||
},
|
},
|
||||||
Node {
|
Node {
|
||||||
@ -449,7 +480,7 @@ fn test_checker() {
|
|||||||
table_id: 0,
|
table_id: 0,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
typ: I32,
|
typ: Unit,
|
||||||
table_id: 0,
|
table_id: 0,
|
||||||
},
|
},
|
||||||
Node {
|
Node {
|
||||||
@ -464,6 +495,7 @@ fn test_checker() {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
println!("intentionally undefined");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check("let a = 5; a; { a; let b = 5; b; } a; b;"),
|
check("let a = 5; a; { a; let b = 5; b; } a; b;"),
|
||||||
vec![
|
vec![
|
||||||
@ -487,7 +519,7 @@ fn test_checker() {
|
|||||||
table_id: 0,
|
table_id: 0,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
typ: I32,
|
typ: Unit,
|
||||||
table_id: 0,
|
table_id: 0,
|
||||||
},
|
},
|
||||||
Node {
|
Node {
|
||||||
@ -522,7 +554,7 @@ fn test_checker() {
|
|||||||
table_id: 1,
|
table_id: 1,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
typ: I32,
|
typ: Unit,
|
||||||
table_id: 1,
|
table_id: 1,
|
||||||
},
|
},
|
||||||
Node {
|
Node {
|
||||||
|
31
src/ir.rs
31
src/ir.rs
@ -1,27 +1,12 @@
|
|||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
pub struct Data {
|
|
||||||
pub kind: DataKind,
|
|
||||||
pub id: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
pub enum DataKind {
|
|
||||||
U8(Vec<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct Fn {
|
pub struct Fn {
|
||||||
pub blocks: Vec<Block>,
|
pub ops: Vec<Op>,
|
||||||
|
pub data: Vec<Data>,
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub arg_count: i32,
|
pub arg_count: i32,
|
||||||
pub local_count: i32,
|
pub local_count: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
pub struct Block {
|
|
||||||
pub ops: Vec<Op>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum Op {
|
pub enum Op {
|
||||||
Pop,
|
Pop,
|
||||||
@ -31,8 +16,20 @@ pub enum Op {
|
|||||||
StoreLocal(i32),
|
StoreLocal(i32),
|
||||||
LoadLocal(i32),
|
LoadLocal(i32),
|
||||||
Jump(u64),
|
Jump(u64),
|
||||||
|
JumpIfFalse(u64),
|
||||||
JumpIfTrue(u64),
|
JumpIfTrue(u64),
|
||||||
CallFn(u64, i32),
|
CallFn(u64, i32),
|
||||||
CallPtr(i32),
|
CallPtr(i32),
|
||||||
Return,
|
Return,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub struct Data {
|
||||||
|
pub kind: DataKind,
|
||||||
|
pub id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub enum DataKind {
|
||||||
|
U8(Vec<u8>),
|
||||||
|
}
|
||||||
|
@ -4,13 +4,12 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
checked::{self, Node, NodeKind},
|
checked::{self, Node, NodeKind},
|
||||||
ir::{Block, Data, DataKind, Fn, Op},
|
ir::{Data, DataKind, Fn, Op},
|
||||||
sym::Syms,
|
sym::Syms,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub fns: Vec<Fn>,
|
pub fns: Vec<Fn>,
|
||||||
pub data: Vec<Data>,
|
|
||||||
pub entry: u64,
|
pub entry: u64,
|
||||||
pub sym: Syms,
|
pub sym: Syms,
|
||||||
}
|
}
|
||||||
@ -25,10 +24,9 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(self, ast: &[Node]) -> Result<Program, ()> {
|
pub fn compile(self, ast: &[Node]) -> Result<Program, ()> {
|
||||||
let (fns, data) = FnCompiler::new(&self.syms).compile_entry(ast)?;
|
let fns = FnCompiler::new(&self.syms).compile_entry(ast)?;
|
||||||
Ok(Program {
|
Ok(Program {
|
||||||
fns,
|
fns,
|
||||||
data,
|
|
||||||
sym: self.syms,
|
sym: self.syms,
|
||||||
entry: 0,
|
entry: 0,
|
||||||
})
|
})
|
||||||
@ -42,10 +40,11 @@ impl Compiler {
|
|||||||
struct FnCompiler<'syms> {
|
struct FnCompiler<'syms> {
|
||||||
syms: &'syms Syms,
|
syms: &'syms Syms,
|
||||||
ops: Vec<Op>,
|
ops: Vec<Op>,
|
||||||
|
data: Vec<Data>,
|
||||||
|
fns: Vec<Fn>,
|
||||||
local_count: i32,
|
local_count: i32,
|
||||||
local_map: HashMap<u64, i32>,
|
local_map: HashMap<u64, i32>,
|
||||||
fns: Vec<Fn>,
|
break_stack: Vec<usize>,
|
||||||
data: Vec<Data>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'syms> FnCompiler<'syms> {
|
impl<'syms> FnCompiler<'syms> {
|
||||||
@ -53,35 +52,36 @@ impl<'syms> FnCompiler<'syms> {
|
|||||||
Self {
|
Self {
|
||||||
syms,
|
syms,
|
||||||
ops: Vec::new(),
|
ops: Vec::new(),
|
||||||
local_count: 0,
|
|
||||||
local_map: HashMap::new(),
|
|
||||||
fns: Vec::new(),
|
fns: Vec::new(),
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
|
local_count: 0,
|
||||||
|
local_map: HashMap::new(),
|
||||||
|
break_stack: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_entry(mut self, ast: &[Node]) -> Result<(Vec<Fn>, Vec<Data>), ()> {
|
pub fn compile_entry(mut self, ast: &[Node]) -> Result<Vec<Fn>, ()> {
|
||||||
let mut blocks = Vec::new();
|
|
||||||
for stmt in ast {
|
for stmt in ast {
|
||||||
blocks.append(&mut self.compile_stmt(stmt)?);
|
self.compile_stmt(stmt)?;
|
||||||
if stmt.typ != checked::Type::Unit {
|
if stmt.typ != checked::Type::Unit {
|
||||||
self.ops.push(Op::Pop);
|
self.ops.push(Op::Pop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.fns.push(Fn {
|
self.fns.push(Fn {
|
||||||
blocks,
|
ops: self.ops,
|
||||||
|
data: self.data,
|
||||||
id: 0,
|
id: 0,
|
||||||
arg_count: 0,
|
arg_count: 0,
|
||||||
local_count: self.local_count,
|
local_count: self.local_count,
|
||||||
});
|
});
|
||||||
Ok((self.fns, self.data))
|
Ok(self.fns)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(mut self, node: &Node) -> Result<Fn, ()> {
|
pub fn compile(mut self, node: &Node) -> Result<Vec<Fn>, ()> {
|
||||||
let Node {
|
let Node {
|
||||||
kind:
|
kind:
|
||||||
NodeKind::Fn {
|
NodeKind::Fn {
|
||||||
subject: _,
|
subject,
|
||||||
params,
|
params,
|
||||||
return_typ: _,
|
return_typ: _,
|
||||||
body,
|
body,
|
||||||
@ -92,69 +92,103 @@ impl<'syms> FnCompiler<'syms> {
|
|||||||
else {
|
else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
for param in params {
|
||||||
|
let NodeKind::Param { subject, typ } = ¶m.kind else { unreachable!() };
|
||||||
|
let NodeKind::Id(param_id) = subject.kind else { unreachable!() };
|
||||||
|
let sym_table = self.syms.view(body.table_id);
|
||||||
|
let sym = sym_table.get(param_id).unwrap();
|
||||||
|
let local_id = self.local_count;
|
||||||
|
self.local_count += 1;
|
||||||
|
self.local_map.insert(sym.uid, local_id);
|
||||||
|
}
|
||||||
let NodeKind::Block(body) = &body.kind else {
|
let NodeKind::Block(body) = &body.kind else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
let mut blocks = Vec::new();
|
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
blocks.append(&mut self.compile_stmt(stmt)?);
|
self.compile_stmt(stmt)?;
|
||||||
if stmt.typ != checked::Type::Unit {
|
if stmt.typ != checked::Type::Unit {
|
||||||
self.ops.push(Op::Pop);
|
self.push(Op::Pop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.push_ops(&mut blocks);
|
self.fns.push(Fn {
|
||||||
Ok(Fn {
|
ops: self.ops,
|
||||||
blocks,
|
data: self.data,
|
||||||
id: *id,
|
id: *id,
|
||||||
arg_count: params.len() as i32,
|
arg_count: params.len() as i32,
|
||||||
local_count: self.local_count,
|
local_count: self.local_count,
|
||||||
})
|
});
|
||||||
|
Ok(self.fns)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_stmt(&mut self, stmt: &Node) -> Result<Vec<Block>, ()> {
|
fn compile_stmt(&mut self, stmt: &Node) -> Result<(), ()> {
|
||||||
let syms = self.syms.view(stmt.table_id);
|
let syms = self.syms.view(stmt.table_id);
|
||||||
let mut blocks = Vec::new();
|
|
||||||
match &stmt.kind {
|
match &stmt.kind {
|
||||||
NodeKind::Error => return Err(()),
|
NodeKind::Error => return Err(()),
|
||||||
NodeKind::Break => todo!(),
|
NodeKind::Break => {
|
||||||
NodeKind::Assign { subject, value } => todo!(),
|
let addr = self.ops.len();
|
||||||
NodeKind::Let { subject, value } => todo!(),
|
self.push(Op::Jump(0));
|
||||||
NodeKind::Fn {
|
self.break_stack.push(addr);
|
||||||
subject,
|
}
|
||||||
params,
|
NodeKind::Assign { subject, value } => {
|
||||||
return_typ,
|
self.compile_expr(value)?;
|
||||||
body,
|
match subject.kind {
|
||||||
id,
|
NodeKind::Error => {
|
||||||
} => todo!(),
|
return Err(());
|
||||||
|
}
|
||||||
|
NodeKind::Id(id) => {
|
||||||
|
let sym_table = self.syms.view(stmt.table_id);
|
||||||
|
let sym = sym_table.get(id).unwrap();
|
||||||
|
let local_id = self.local_map.get(&sym.uid).unwrap();
|
||||||
|
self.push(Op::StoreLocal(*local_id));
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NodeKind::Let { subject, value } => {
|
||||||
|
let NodeKind::Param { subject, typ: _ } = &subject.kind else { unreachable!() };
|
||||||
|
let NodeKind::Id(subject_id) = subject.kind else { unreachable!()};
|
||||||
|
let sym_table = self.syms.view(stmt.table_id);
|
||||||
|
let sym = sym_table.get(subject_id).unwrap();
|
||||||
|
let local_id = self.local_count;
|
||||||
|
self.local_map.insert(sym.uid, local_id);
|
||||||
|
self.local_count += 1;
|
||||||
|
self.compile_expr(value)?;
|
||||||
|
self.push(Op::StoreLocal(local_id));
|
||||||
|
}
|
||||||
|
NodeKind::Fn { subject, .. } => {
|
||||||
|
let mut compiled_fn = FnCompiler::new(self.syms).compile(stmt)?;
|
||||||
|
self.fns.append(&mut compiled_fn);
|
||||||
|
let NodeKind::Id(subject_id) = subject.kind else { unreachable!()};
|
||||||
|
let sym_table = self.syms.view(stmt.table_id);
|
||||||
|
let sym = sym_table.get(subject_id).unwrap();
|
||||||
|
self.local_map.insert(sym.uid, self.local_count);
|
||||||
|
self.local_count += 1;
|
||||||
|
}
|
||||||
NodeKind::Return { value } => {
|
NodeKind::Return { value } => {
|
||||||
self.ops.push(Op::Return);
|
self.ops.push(Op::Return);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
blocks.append(&mut self.compile_expr(stmt)?);
|
self.compile_expr(stmt)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if stmt.typ != checked::Type::Unit {
|
Ok(())
|
||||||
self.ops.push(Op::Pop);
|
|
||||||
}
|
|
||||||
Ok(blocks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_expr(&mut self, expr: &Node) -> Result<Vec<Block>, ()> {
|
fn compile_expr(&mut self, expr: &Node) -> Result<(), ()> {
|
||||||
let syms = self.syms.view(expr.table_id);
|
let syms = self.syms.view(expr.table_id);
|
||||||
let mut blocks = Vec::new();
|
|
||||||
match &expr.kind {
|
match &expr.kind {
|
||||||
NodeKind::Error => return Err(()),
|
NodeKind::Error => return Err(()),
|
||||||
NodeKind::Id(id) => {
|
NodeKind::Id(id) => {
|
||||||
let sym = syms.get(*id).unwrap();
|
let sym = syms.get(*id).unwrap();
|
||||||
let local_id = self.local_map.get(&sym.uid).unwrap();
|
let local_id = self.local_map.get(&sym.uid).unwrap();
|
||||||
self.ops.push(Op::LoadLocal(*local_id));
|
self.push(Op::LoadLocal(*local_id));
|
||||||
}
|
}
|
||||||
NodeKind::Int(value) => match expr.typ {
|
NodeKind::Int(value) => match expr.typ {
|
||||||
checked::Type::I32 => self.ops.push(Op::PushI32(*value as i32)),
|
checked::Type::I32 => self.push(Op::PushI32(*value as i32)),
|
||||||
checked::Type::U32 => self.ops.push(Op::PushU32(*value as u32)),
|
checked::Type::U32 => self.push(Op::PushU32(*value as u32)),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
NodeKind::String(value) => {
|
NodeKind::Str(value) => {
|
||||||
let id = self.data.len() as u64;
|
let id = self.data.len() as u64;
|
||||||
let bytes = value.bytes().collect();
|
let bytes = value.bytes().collect();
|
||||||
self.data.push(Data {
|
self.data.push(Data {
|
||||||
@ -164,13 +198,13 @@ impl<'syms> FnCompiler<'syms> {
|
|||||||
self.ops.push(Op::PushStaticPtr(id));
|
self.ops.push(Op::PushStaticPtr(id));
|
||||||
}
|
}
|
||||||
NodeKind::Group(expr) => {
|
NodeKind::Group(expr) => {
|
||||||
blocks.append(&mut self.compile_expr(expr)?);
|
self.compile_expr(expr)?;
|
||||||
}
|
}
|
||||||
NodeKind::Block(stmts) => {
|
NodeKind::Block(stmts) => {
|
||||||
let mut last_typ = None;
|
let mut last_typ = None;
|
||||||
for stmt in stmts {
|
for stmt in stmts {
|
||||||
if last_typ.filter(|typ| *typ != checked::Type::Unit).is_some() {
|
if last_typ.filter(|typ| *typ != checked::Type::Unit).is_some() {
|
||||||
self.ops.push(Op::Pop);
|
self.push(Op::Pop);
|
||||||
}
|
}
|
||||||
last_typ = Some(stmt.typ.clone());
|
last_typ = Some(stmt.typ.clone());
|
||||||
self.compile_stmt(stmt)?;
|
self.compile_stmt(stmt)?;
|
||||||
@ -187,10 +221,10 @@ impl<'syms> FnCompiler<'syms> {
|
|||||||
params: _,
|
params: _,
|
||||||
return_typ: _,
|
return_typ: _,
|
||||||
} => {
|
} => {
|
||||||
self.ops.push(Op::CallFn(id, args.len() as i32));
|
self.push(Op::CallFn(id, args.len() as i32));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.ops.push(Op::CallPtr(args.len() as i32));
|
self.push(Op::CallPtr(args.len() as i32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,59 +234,39 @@ impl<'syms> FnCompiler<'syms> {
|
|||||||
falsy,
|
falsy,
|
||||||
} => {
|
} => {
|
||||||
self.compile_expr(cond)?;
|
self.compile_expr(cond)?;
|
||||||
let cond_idx = blocks.len();
|
let l0 = self.ops.len();
|
||||||
self.push_ops(&mut blocks);
|
self.push(Op::JumpIfFalse(0));
|
||||||
|
self.compile_expr(truthy)?;
|
||||||
match falsy {
|
let l1 = self.ops.len();
|
||||||
Some(falsy) => {
|
if let Op::JumpIfFalse(ref mut addr) = self.ops[l0] {
|
||||||
let truthy_first_idx = blocks.len() as u64;
|
*addr = l1 as u64;
|
||||||
let mut truthy = self.compile_expr(truthy)?;
|
|
||||||
let truthy_last_idx = blocks.len();
|
|
||||||
self.push_ops(&mut truthy);
|
|
||||||
blocks.append(&mut truthy);
|
|
||||||
|
|
||||||
let falsy_first_idx = blocks.len() as u64;
|
|
||||||
let mut falsy = self.compile_expr(falsy)?;
|
|
||||||
let falsy_last_idx = blocks.len();
|
|
||||||
self.push_ops(&mut falsy);
|
|
||||||
blocks.append(&mut falsy);
|
|
||||||
|
|
||||||
let after_idx = blocks.len() as u64;
|
|
||||||
|
|
||||||
blocks[cond_idx].ops.push(Op::JumpIfTrue(truthy_first_idx));
|
|
||||||
blocks[cond_idx].ops.push(Op::Jump(falsy_first_idx));
|
|
||||||
blocks[truthy_last_idx].ops.push(Op::Jump(after_idx));
|
|
||||||
blocks[falsy_last_idx].ops.push(Op::Jump(after_idx));
|
|
||||||
}
|
}
|
||||||
None => {
|
if let Some(falsy) = falsy {
|
||||||
let truthy_first_idx = blocks.len() as u64;
|
self.push(Op::Jump(0));
|
||||||
let mut truthy = self.compile_expr(truthy)?;
|
self.compile_expr(falsy)?;
|
||||||
let truthy_last_idx = blocks.len();
|
let l2 = self.ops.len();
|
||||||
self.push_ops(&mut truthy);
|
if let Op::Jump(ref mut addr) = self.ops[l1] {
|
||||||
blocks.append(&mut truthy);
|
*addr = l2 as u64;
|
||||||
|
|
||||||
let after_idx = blocks.len() as u64;
|
|
||||||
|
|
||||||
blocks[cond_idx].ops.push(Op::JumpIfTrue(truthy_first_idx));
|
|
||||||
blocks[cond_idx].ops.push(Op::Jump(after_idx));
|
|
||||||
blocks[truthy_last_idx].ops.push(Op::Jump(after_idx));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeKind::Loop { body } => {
|
NodeKind::Loop { body } => {
|
||||||
let body = self.compile_expr(body)?;
|
let l0 = self.ops.len() as u64;
|
||||||
let body_idx = blocks.len();
|
self.compile_expr(body)?;
|
||||||
|
let l1 = self.ops.len() as u64;
|
||||||
|
for op in self.break_stack.drain(..) {
|
||||||
|
let Op::Jump(ref mut addr) = self.ops[op] else { unreachable!() };
|
||||||
|
*addr = l1;
|
||||||
|
}
|
||||||
|
self.push(Op::Jump(l0));
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
self.push_ops(&mut blocks);
|
Ok(())
|
||||||
Ok(blocks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_ops(&mut self, blocks: &mut Vec<Block>) {
|
fn push(&mut self, op: Op) {
|
||||||
let mut ops = Vec::new();
|
self.ops.push(op);
|
||||||
std::mem::swap(&mut self.ops, &mut ops);
|
|
||||||
blocks.push(Block { ops });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error<S: Into<String>>(&mut self, msg: S) {
|
fn error<S: Into<String>>(&mut self, msg: S) {
|
||||||
@ -264,9 +278,10 @@ impl<'syms> FnCompiler<'syms> {
|
|||||||
fn test_compiler() {
|
fn test_compiler() {
|
||||||
use crate::checker::{Checker, IdGen};
|
use crate::checker::{Checker, IdGen};
|
||||||
use crate::parser::Parser;
|
use crate::parser::Parser;
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use Op::*;
|
use Op::*;
|
||||||
|
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
struct SeqIdGen(u64);
|
struct SeqIdGen(u64);
|
||||||
impl IdGen for SeqIdGen {
|
impl IdGen for SeqIdGen {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
@ -285,18 +300,112 @@ fn test_compiler() {
|
|||||||
let checked = checker.check(&Parser::new(text).parse());
|
let checked = checker.check(&Parser::new(text).parse());
|
||||||
let syms = checker.finish();
|
let syms = checker.finish();
|
||||||
let compiled = Compiler::new(syms).compile(&checked);
|
let compiled = Compiler::new(syms).compile(&checked);
|
||||||
compiled
|
compiled.map(|program| program.fns)
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compile("123;").map(|program| program.fns),
|
compile("fn test(a: i32) -> i32 { a; } test(123);"),
|
||||||
|
Ok(vec![
|
||||||
|
Fn {
|
||||||
|
ops: vec![LoadLocal(0), Pop],
|
||||||
|
data: vec![],
|
||||||
|
id: 0,
|
||||||
|
arg_count: 1,
|
||||||
|
local_count: 1
|
||||||
|
},
|
||||||
|
Fn {
|
||||||
|
ops: vec![LoadLocal(0), PushI32(123), CallFn(0, 1), Pop],
|
||||||
|
data: vec![],
|
||||||
|
id: 0,
|
||||||
|
arg_count: 0,
|
||||||
|
local_count: 1
|
||||||
|
}
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compile("let a = 5; let b = 3; a; b; a = b;"),
|
||||||
Ok(vec![Fn {
|
Ok(vec![Fn {
|
||||||
blocks: vec![Block {
|
ops: vec![
|
||||||
ops: vec![PushI32(123)]
|
PushI32(5),
|
||||||
|
StoreLocal(0),
|
||||||
|
PushI32(3),
|
||||||
|
StoreLocal(1),
|
||||||
|
LoadLocal(0),
|
||||||
|
Pop,
|
||||||
|
LoadLocal(1),
|
||||||
|
Pop,
|
||||||
|
LoadLocal(1),
|
||||||
|
StoreLocal(0),
|
||||||
|
],
|
||||||
|
data: vec![],
|
||||||
|
id: 0,
|
||||||
|
arg_count: 0,
|
||||||
|
local_count: 2,
|
||||||
|
}])
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compile("let a = \"hello\";"),
|
||||||
|
Ok(vec![Fn {
|
||||||
|
ops: vec![PushStaticPtr(0), StoreLocal(0)],
|
||||||
|
data: vec![Data {
|
||||||
|
kind: DataKind::U8(vec![104, 101, 108, 108, 111]),
|
||||||
|
id: 0
|
||||||
}],
|
}],
|
||||||
id: 0,
|
id: 0,
|
||||||
arg_count: 0,
|
arg_count: 0,
|
||||||
|
local_count: 1
|
||||||
|
}])
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compile("if 1 { 2; } if 1 { 2; } else { 3; }"),
|
||||||
|
Ok(vec![Fn {
|
||||||
|
ops: vec![
|
||||||
|
PushI32(1),
|
||||||
|
JumpIfFalse(3),
|
||||||
|
PushI32(2),
|
||||||
|
PushI32(1),
|
||||||
|
JumpIfFalse(6),
|
||||||
|
PushI32(2),
|
||||||
|
Jump(8),
|
||||||
|
PushI32(3),
|
||||||
|
Pop,
|
||||||
|
],
|
||||||
|
data: vec![],
|
||||||
|
id: 0,
|
||||||
|
arg_count: 0,
|
||||||
local_count: 0
|
local_count: 0
|
||||||
}])
|
}])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compile("let a = if 1 { 2; } else { 3; };"),
|
||||||
|
Ok(vec![Fn {
|
||||||
|
ops: vec![
|
||||||
|
PushI32(1),
|
||||||
|
JumpIfFalse(3),
|
||||||
|
PushI32(2),
|
||||||
|
Jump(5),
|
||||||
|
PushI32(3),
|
||||||
|
StoreLocal(0),
|
||||||
|
],
|
||||||
|
data: vec![],
|
||||||
|
id: 0,
|
||||||
|
arg_count: 0,
|
||||||
|
local_count: 1
|
||||||
|
}])
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compile("loop { break; }"),
|
||||||
|
Ok(vec![Fn {
|
||||||
|
ops: vec![Jump(1), Jump(0)],
|
||||||
|
data: vec![],
|
||||||
|
id: 0,
|
||||||
|
arg_count: 0,
|
||||||
|
local_count: 0,
|
||||||
|
},])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
10
src/lexer.rs
10
src/lexer.rs
@ -114,7 +114,7 @@ impl<'a> Lexer<'a> {
|
|||||||
Some('"') => {
|
Some('"') => {
|
||||||
self.step();
|
self.step();
|
||||||
break self
|
break self
|
||||||
.token_with_value(TokenKind::String, TokenValue::String(value));
|
.token_with_value(TokenKind::Str, TokenValue::Str(value));
|
||||||
}
|
}
|
||||||
Some(ch) => {
|
Some(ch) => {
|
||||||
value.push(ch);
|
value.push(ch);
|
||||||
@ -261,18 +261,18 @@ fn test_lexer() {
|
|||||||
|
|
||||||
assert_eq!(lex("abc"), vec![(TK::Id, TV::Id(hash("abc")))]);
|
assert_eq!(lex("abc"), vec![(TK::Id, TV::Id(hash("abc")))]);
|
||||||
assert_eq!(lex("123"), vec![(TK::Int, TV::Int(123))]);
|
assert_eq!(lex("123"), vec![(TK::Int, TV::Int(123))]);
|
||||||
assert_eq!(lex("\"\""), vec![(TK::String, TV::String("".to_string()))]);
|
assert_eq!(lex("\"\""), vec![(TK::Str, TV::Str("".to_string()))]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex("\"hello\""),
|
lex("\"hello\""),
|
||||||
vec![(TK::String, TV::String("hello".to_string()))]
|
vec![(TK::Str, TV::Str("hello".to_string()))]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex("\"new\\nline\""),
|
lex("\"new\\nline\""),
|
||||||
vec![(TK::String, TV::String("new\nline".to_string()))]
|
vec![(TK::Str, TV::Str("new\nline".to_string()))]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex("\"backslash\\\\\""),
|
lex("\"backslash\\\\\""),
|
||||||
vec![(TK::String, TV::String("backslash\\".to_string()))]
|
vec![(TK::Str, TV::Str("backslash\\".to_string()))]
|
||||||
);
|
);
|
||||||
assert_eq!(lex("->"), vec![(TK::MinusLt, TV::None)]);
|
assert_eq!(lex("->"), vec![(TK::MinusLt, TV::None)]);
|
||||||
assert_eq!(lex("let"), vec![(TK::Let, TV::None)]);
|
assert_eq!(lex("let"), vec![(TK::Let, TV::None)]);
|
||||||
|
@ -3,7 +3,7 @@ pub enum Node {
|
|||||||
Error,
|
Error,
|
||||||
Id(u64),
|
Id(u64),
|
||||||
Int(i64),
|
Int(i64),
|
||||||
String(String),
|
Str(String),
|
||||||
Group(Box<Node>),
|
Group(Box<Node>),
|
||||||
Block(Vec<Node>),
|
Block(Vec<Node>),
|
||||||
Call {
|
Call {
|
||||||
|
@ -111,10 +111,6 @@ impl<'a> Parser<'a> {
|
|||||||
self.step();
|
self.step();
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
if !self.curr_is(TokenKind::RParen) {
|
if !self.curr_is(TokenKind::RParen) {
|
||||||
if !self.curr_is(TokenKind::RParen) {
|
|
||||||
self.error("expected ')'");
|
|
||||||
return Err(Node::Error);
|
|
||||||
}
|
|
||||||
if !self.curr_is(TokenKind::Id) {
|
if !self.curr_is(TokenKind::Id) {
|
||||||
self.error("expected id");
|
self.error("expected id");
|
||||||
return Err(Node::Error);
|
return Err(Node::Error);
|
||||||
@ -158,7 +154,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
fn parse_param(&mut self) -> Node {
|
fn parse_param(&mut self) -> Node {
|
||||||
let subject = Box::new(self.parse_id());
|
let subject = Box::new(self.parse_id());
|
||||||
let typ = if let Some(TokenKind::Comma) = self.curr_kind() {
|
let typ = if let Some(TokenKind::Colon) = self.curr_kind() {
|
||||||
self.step();
|
self.step();
|
||||||
Some(Box::new(self.parse_typ()))
|
Some(Box::new(self.parse_typ()))
|
||||||
} else {
|
} else {
|
||||||
@ -239,7 +235,7 @@ impl<'a> Parser<'a> {
|
|||||||
match self.curr_kind() {
|
match self.curr_kind() {
|
||||||
Some(TokenKind::Id) => self.parse_id(),
|
Some(TokenKind::Id) => self.parse_id(),
|
||||||
Some(TokenKind::Int) => self.parse_int(),
|
Some(TokenKind::Int) => self.parse_int(),
|
||||||
Some(TokenKind::String) => self.parse_string(),
|
Some(TokenKind::Str) => self.parse_string(),
|
||||||
Some(TokenKind::LParen) => self.parse_group(),
|
Some(TokenKind::LParen) => self.parse_group(),
|
||||||
Some(TokenKind::LBrace) => self.parse_block(),
|
Some(TokenKind::LBrace) => self.parse_block(),
|
||||||
Some(TokenKind::If) => self.parse_if(),
|
Some(TokenKind::If) => self.parse_if(),
|
||||||
@ -280,15 +276,15 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
fn parse_string(&mut self) -> Node {
|
fn parse_string(&mut self) -> Node {
|
||||||
let Some(Token {
|
let Some(Token {
|
||||||
kind: TokenKind::String,
|
kind: TokenKind::Str,
|
||||||
value: TokenValue::String(value),
|
value: TokenValue::Str(value),
|
||||||
..
|
..
|
||||||
}) = self.current.clone()
|
}) = self.current.clone()
|
||||||
else {
|
else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
self.step();
|
self.step();
|
||||||
Node::String(value.clone())
|
Node::Str(value.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_group(&mut self) -> Node {
|
fn parse_group(&mut self) -> Node {
|
||||||
@ -307,6 +303,10 @@ impl<'a> Parser<'a> {
|
|||||||
let mut stmts = Vec::new();
|
let mut stmts = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
match self.curr_kind() {
|
match self.curr_kind() {
|
||||||
|
None => {
|
||||||
|
self.error("expected ')'");
|
||||||
|
break Node::Error;
|
||||||
|
}
|
||||||
Some(TokenKind::RBrace) => {
|
Some(TokenKind::RBrace) => {
|
||||||
self.step();
|
self.step();
|
||||||
break Node::Block(stmts);
|
break Node::Block(stmts);
|
||||||
@ -392,7 +392,7 @@ fn test_parser() {
|
|||||||
assert_eq!(Parser::new("123;").parse(), vec![Int(123)]);
|
assert_eq!(Parser::new("123;").parse(), vec![Int(123)]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Parser::new("\"hello\";").parse(),
|
Parser::new("\"hello\";").parse(),
|
||||||
vec![String("hello".to_string())]
|
vec![Str("hello".to_string())]
|
||||||
);
|
);
|
||||||
assert_eq!(Parser::new("0;").parse(), vec![Int(0)]);
|
assert_eq!(Parser::new("0;").parse(), vec![Int(0)]);
|
||||||
assert_eq!(Parser::new("0;abc;").parse(), vec![Int(0), Id(hash("abc"))]);
|
assert_eq!(Parser::new("0;abc;").parse(), vec![Int(0), Id(hash("abc"))]);
|
||||||
|
@ -12,7 +12,7 @@ pub enum TokenKind {
|
|||||||
Error,
|
Error,
|
||||||
Id,
|
Id,
|
||||||
Int,
|
Int,
|
||||||
String,
|
Str,
|
||||||
If,
|
If,
|
||||||
Else,
|
Else,
|
||||||
Loop,
|
Loop,
|
||||||
@ -36,5 +36,5 @@ pub enum TokenValue {
|
|||||||
None,
|
None,
|
||||||
Id(u64),
|
Id(u64),
|
||||||
Int(i64),
|
Int(i64),
|
||||||
String(String),
|
Str(String),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user