Reworking semantic analyzer

This commit is contained in:
Logan 2024-10-26 00:58:41 -05:00
parent 7e110a9560
commit 22c78a9945
14 changed files with 552 additions and 430 deletions

View file

@ -1,3 +1,2 @@
10 + 2 + 3 * 4;
b := 10.0 / 5.0;
c := b;

View file

@ -4,18 +4,8 @@
</head>
<script>
const importObject = {
console: {
log(arg) {
console.log(arg);
}
}
};
WebAssembly.instantiateStreaming(fetch("/test.wasm"), importObject)
.then(obj => {
obj.instance.exports.main();
});
const url = '/test.wasm';
WebAssembly.instantiateStreaming(fetch(url), { console });
</script>
<body>

View file

@ -1,9 +1,8 @@
use std::path::Path;
use std::{io::Write, path::Path};
use crate::{
Parser, Tokenizer,
err::*,
ir::{Compiler, IR},
semantic::{self},
};
@ -11,7 +10,6 @@ use crate::{
pub struct Module {
file_name: String,
source: String,
pub program: Vec<IR>,
}
impl Module {
@ -31,12 +29,20 @@ impl Module {
let tokens = Tokenizer::new(source.chars()).filter(|t| t.0.is_meaningful());
let statements = Parser::new(tokens);
let program = semantic::Analyzer::typecheck(statements.collect());
let mut compiler = Compiler::new();
compiler.compile(program);
Self {
file_name,
source: source.into(),
program: compiler.ir,
}
}
/*
pub fn write_to(&self, path: impl AsRef<Path>) {
let watpath = path.as_ref().to_owned().with_extension("wat");
let mut file = std::fs::File::create(watpath).unwrap();
file.write_all(&self.wat.as_bytes()).unwrap();
let wasmpath = path.as_ref().to_owned().with_extension("wasm");
let mut file = std::fs::File::create(wasmpath).unwrap();
file.write_all(&self.wasm).unwrap();
}
*/
}

144
src/ir.rs
View file

@ -1,27 +1,28 @@
use crate::{
BinaryOp, Expression, ExpressionKind, Immediate, Statement, StatementKind,
UnaryOp,
semantic::{Type, VarKind, uid},
semantic::{Primitive, Type, UID},
};
/*
#[derive(Debug, Clone)]
pub enum IR {
BinOp { op: BinaryOp, type_: Type },
UnOp { op: UnaryOp, type_: Type },
Imm(Immediate),
NewLocal { uid: uid, type_: Type },
AssignLocal { uid: uid },
GetLocal { uid: uid },
NewGlobal { uid: uid, type_: Type },
AssignGlobal { uid: uid },
GetGlobal { uid: uid },
StartFunc { uid: uid },
Push { prim: Primitive, value: Immediate },
NewLocal { uid: UID, type_: Type },
AssignLocal { uid: UID },
GetLocal { uid: UID },
NewGlobal { uid: UID, type_: Type },
AssignGlobal { uid: UID },
GetGlobal { uid: UID },
StartFunc { uid: UID },
EndFunc,
ReturnType { type_: Type },
NewParam { uid: uid, type_: Type },
NewParam { uid: UID, type_: Type },
Return,
Call { uid: uid },
Call { uid: UID },
Drop,
Print,
}
impl std::fmt::Display for IR {
@ -30,7 +31,7 @@ impl std::fmt::Display for IR {
match self {
BinOp { op, type_ } => write!(f, "{op} ({type_})"),
UnOp { op, type_ } => write!(f, "{op}, {type_}"),
Imm(immediate) => write!(f, "push {immediate}"),
Push { prim, value } => write!(f, "push {value} ({prim})"),
NewLocal { uid, type_ } => write!(f, "local ${uid} = {type_}"),
AssignLocal { uid } => write!(f, "pop local ${uid}"),
GetLocal { uid } => write!(f, "push local ${uid}"),
@ -44,24 +45,109 @@ impl std::fmt::Display for IR {
Call { uid } => write!(f, "call ${uid}"),
Drop => write!(f, "pop"),
ReturnType { type_ } => write!(f, "result {type_}"),
Print => write!(f, "print [DEBUG]"),
}
}
}
impl IR {
pub fn to_wat(&self) -> String {
use BinaryOp as b;
use IR::*;
use Primitive as p;
use Type as t;
match self {
// Primitive
BinOp {
op,
type_: t::Prim(p),
} => match (op, p) {
// Addition
(b::Plus, p::i32 | p::w32 | p::integer | p::whole) => "i32.add",
(b::Plus, p::i64 | p::w64) => "i64.add",
(b::Plus, p::r32) => "f32.add",
(b::Plus, p::r64) => "f64.add",
// Subtraction
(b::Minus, p::i32 | p::w32 | p::integer | p::whole) => "i32.sub",
(b::Minus, p::i64 | p::w64) => "i64.sub",
(b::Minus, p::r32) => "f32.sub",
(b::Minus, p::r64) => "f64.sub",
// Multiplication
(b::Star, p::i32 | p::w32 | p::integer | p::whole) => "i32.mul",
(b::Star, p::i64 | p::w64) => "i64.mul",
(b::Star, p::r32) => "f32.mul",
(b::Star, p::r64) => "f64.mul",
// Division
(b::Slash, p::i32 | p::integer) => "i32.div_s",
(b::Slash, p::w32 | p::whole) => "i32.div_u",
(b::Slash, p::i64) => "i64.div_s",
(b::Slash, p::w64) => "i64.div_u",
(b::Slash, p::r32) => "f32.div",
(b::Slash, p::r64) => "f64.div",
_ => todo!(),
}
.into(),
UnOp {
op,
type_: t::Prim(p),
} => todo!(),
Push { prim, value } => todo!(),
NewLocal {
uid,
type_: t::Prim(p),
} => format!("(local ${uid} {})", p.as_wat()),
AssignLocal { uid } => format!("local.set ${uid}"),
GetLocal { uid } => format!("local.get ${uid}"),
NewGlobal {
uid,
type_: t::Prim(p),
} => format!(
"(global ${uid} (mut {}) ({}.const 0))",
p.as_wat(),
p.as_wat()
),
AssignGlobal { uid } => format!("global.set ${uid}"),
GetGlobal { uid } => format!("global.get ${uid}"),
StartFunc { uid } => format!("(func ${uid}"),
EndFunc => ")".into(),
ReturnType { type_: t::Prim(p) } => format!("(result {})", p.as_wat()),
NewParam {
uid,
type_: t::Prim(p),
} => format!("(param ${uid} {})", p.as_wat()),
Return => "return".into(),
Call { uid } => format!("call ${uid}"),
Drop => "drop".into(),
Print => "call $log".into(),
_ => todo!(),
}
.into()
}
}
pub struct Compiler {
pub ir: Vec<IR>,
}
impl Compiler {
pub fn new() -> Self {
Self { ir: vec![] }
}
const IMPORTS: &'static str =
r#"(import "console" "log" (func $log (param i32)))"#;
pub fn compile(&mut self, block: Vec<Statement>) {
pub fn compile(block: Vec<Statement>) -> (Vec<u8>, String) {
let mut this = Self { ir: vec![] };
for s in block {
self.statement(s);
this.statement(s);
}
self.ir = self.hoist_functions();
this.ir = this.hoist();
let mut output = String::new();
for ir in &this.ir {
//println!("{ir}");
let s = ir.to_wat();
output.push_str(&format!("{s}\n"));
}
output = format!("(module\n{}\n{output}\n(start $0)\n)", Self::IMPORTS);
println!("{output}");
(wat::parse_str(&output).unwrap(), output)
}
fn statement(&mut self, statement: Statement) {
@ -109,7 +195,10 @@ impl Compiler {
else_,
} => todo!(),
While { predicate, block } => todo!(),
Print(expression) => todo!(),
Print(expression) => {
self.expression(expression);
self.ir.push(IR::Print);
},
Expression(expression) => {
if expression.type_ == Type::Nothing {
self.expression(expression);
@ -139,7 +228,13 @@ impl Compiler {
use ExpressionKind::*;
match expression.kind {
Immediate(immediate) => {
self.ir.push(IR::Imm(immediate));
let Type::Prim(p) = expression.type_ else {
panic!();
};
self.ir.push(IR::Push {
value: immediate,
prim: p,
})
},
Identifier(_, var_kind) => match var_kind {
VarKind::Global(uid) => self.ir.push(IR::GetGlobal { uid }),
@ -152,8 +247,6 @@ impl Compiler {
mut left,
mut right,
} => {
left.type_ = Type::coerce(&expression.type_, &left.type_).unwrap();
right.type_ = Type::coerce(&expression.type_, &right.type_).unwrap();
assert!(&left.type_ == &right.type_);
self.expression(*left.clone());
self.expression(*right);
@ -163,7 +256,6 @@ impl Compiler {
});
},
Unary { op, mut child } => {
child.type_ = Type::coerce(&expression.type_, &child.type_).unwrap();
self.expression(*child);
self.ir.push(IR::UnOp {
op,
@ -171,7 +263,6 @@ impl Compiler {
})
},
Parenthesis(mut e) => {
e.type_ = Type::coerce(&expression.type_, &e.type_).unwrap();
self.expression(*e);
},
FunctionDef {
@ -211,7 +302,7 @@ impl Compiler {
}
}
fn hoist_functions(&self) -> Vec<IR> {
fn hoist(&self) -> Vec<IR> {
let mut functions = vec![(vec![], vec![])];
let mut result = vec![];
for index in 0..self.ir.len() {
@ -259,3 +350,4 @@ impl Compiler {
result
}
}
*/

View file

@ -11,6 +11,7 @@ use std::ops::Add;
use err::*;
use lookahead::*;
use parse::*;
use semantic::Analyzer;
use token::*;
#[derive(Clone, Copy, Debug)]
@ -85,11 +86,24 @@ fn prints(st: &Statement) {
};
}
fn tokenize(input: &'static str) -> impl Iterator<Item = Token> {
Tokenizer::new(input.chars()).filter(|t| t.0.is_meaningful())
}
fn parse(input: &'static str) -> impl Iterator<Item = Statement> {
Parser::new(tokenize(input))
}
fn typecheck(input: &'static str) -> Vec<Statement> {
Analyzer::typecheck(parse(input).collect())
}
fn main() -> Result<()> {
//test_expression("a.b.c()");
let module = frontend::Module::from_file("./demo.hal")?;
for s in &module.program {
println!("{s}");
for s in parse(include_str!("../demo.hal")) {
println!("{s:#?}");
println!("------------------");
}
//let module = frontend::Module::from_file("./demo.hal")?;
//module.write_to("test");
Ok(())
}

View file

@ -1,4 +1,4 @@
use crate::semantic::*;
use crate::{Base, semantic::*};
use super::*;
@ -17,8 +17,8 @@ impl std::fmt::Debug for Parameter {
#[derive(Debug, Clone)]
pub enum Immediate {
Integer(i64),
Real(f64),
Integer(String, Base),
Real(String),
String(String),
Boolean(bool),
}
@ -26,7 +26,7 @@ pub enum Immediate {
impl std::fmt::Display for Immediate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Immediate::Integer(i) => write!(f, "{i}"),
Immediate::Integer(i, b) => write!(f, "{i} ({b:?})"),
Immediate::Real(r) => write!(f, "{r}"),
Immediate::String(s) => write!(f, "{s}"),
Immediate::Boolean(b) => write!(f, "{b}"),
@ -37,7 +37,7 @@ impl std::fmt::Display for Immediate {
#[derive(Clone)]
pub enum ExpressionKind {
Immediate(Immediate),
Identifier(String, VarKind),
Identifier(String, UID),
Binary {
op: BinaryOp,
left: Box<Expression>,
@ -53,11 +53,13 @@ pub enum ExpressionKind {
returns_str: Option<String>,
returns_actual: Type,
body: Vec<Statement>,
id: uid,
id: UID,
},
FunctionCall {
callee: Box<Expression>,
args: Vec<Expression>,
is_reference: bool,
id: UID,
},
StructDef(Vec<Parameter>),
StructLiteral {
@ -67,6 +69,7 @@ pub enum ExpressionKind {
Field {
namespace: Box<Expression>,
field: Box<Expression>,
uid: UID,
},
}
@ -78,26 +81,16 @@ pub struct Expression {
}
impl Expression {
pub fn new(kind: ExpressionKind, span: Span) -> Self {
Self {
kind,
span,
type_: Type::Ambiguous,
}
pub fn new(kind: ExpressionKind, span: Span, type_: Type) -> Self {
Self { kind, span, type_ }
}
}
impl std::fmt::Debug for ExpressionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ExpressionKind as e;
use Immediate as im;
match self {
e::Immediate(i) => match i {
im::Integer(i) => write!(f, "{i}"),
im::Real(fp) => write!(f, "{fp}"),
im::String(s) => write!(f, r#""{s}""#),
im::Boolean(b) => write!(f, "{b}"),
},
e::Immediate(i) => write!(f, "{i}"),
e::Binary {
op: token,
left,
@ -113,7 +106,9 @@ impl std::fmt::Debug for ExpressionKind {
e::FunctionCall { callee, args, .. } => {
write!(f, "({callee:?} call {args:?})")
},
e::Field { namespace, field } => {
e::Field {
namespace, field, ..
} => {
write!(f, "({namespace:?} . {field:?})")
},
e::FunctionDef {
@ -131,7 +126,7 @@ impl std::fmt::Debug for ExpressionKind {
impl std::fmt::Debug for Expression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.kind)
write!(f, "({:?} {})", self.kind, self.type_)
}
}
@ -139,6 +134,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
pub fn expression(&mut self, precedence: Precedence) -> Result<Expression> {
use ExpressionKind as e;
use TokenKind as t;
let mut type_ = Type::Ambiguous;
let next = self.peek(0)?;
// Unary prefix expression
let mut current = if let Ok(operator) = UnaryOp::try_from(&next.0) {
@ -160,6 +156,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
child: child.into(),
},
span,
Type::Ambiguous,
)
}
// Terminal or paren
@ -191,6 +188,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
right: rhs.into(),
},
span,
Type::Ambiguous,
);
}
// Field
@ -206,8 +204,10 @@ impl<I: Iterator<Item = Token>> Parser<I> {
e::Field {
namespace: current.into(),
field: field.into(),
uid: "".into(),
},
span,
Type::Ambiguous,
)
}
// Function call
@ -234,8 +234,11 @@ impl<I: Iterator<Item = Token>> Parser<I> {
e::FunctionCall {
callee: current.into(),
args,
is_reference: false,
id: "".into(),
},
span + span2,
Type::Ambiguous,
);
}
// Unary postfix
@ -256,6 +259,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
child: current.into(),
},
span,
Type::Ambiguous,
);
} else {
break;
@ -268,28 +272,34 @@ impl<I: Iterator<Item = Token>> Parser<I> {
use ExpressionKind as e;
use Immediate as im;
use TokenKind as t;
let mut type_ = Type::Ambiguous;
let next = self.peek(0)?;
let mut span = next.1;
let kind = match next.0 {
t::IntegerLiteral(i) => {
t::IntegerLiteral(i, b) => {
self.skip(1);
e::Immediate(im::Integer(i))
type_ = Type::Prim(Primitive::integer_ambiguous);
e::Immediate(im::Integer(i, b))
},
t::FloatLiteral(f) => {
self.skip(1);
type_ = Type::Prim(Primitive::real_ambiguous);
e::Immediate(im::Real(f))
},
t::StringLiteral(s) => {
self.skip(1);
type_ = Type::Prim(Primitive::string);
e::Immediate(im::String(s))
},
t::True => {
self.skip(1);
type_ = Type::Prim(Primitive::boolean);
e::Immediate(im::Boolean(true))
},
t::False => {
self.skip(1);
e::Immediate(im::Boolean(true))
type_ = Type::Prim(Primitive::boolean);
e::Immediate(im::Boolean(false))
},
// Function definition
t::LeftParen
@ -345,7 +355,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
returns_str,
returns_actual: Type::Ambiguous,
body,
id: 0,
id: "".into(),
}
},
// Struct definition
@ -405,7 +415,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
},
t::Identifier(i) => {
self.skip(1);
e::Identifier(i, VarKind::Undefined)
e::Identifier(i, "".into())
},
// Parenthetical
t::LeftParen => {
@ -425,7 +435,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
.reason(format!("Expected expression, found {}", next.0));
},
};
Ok(Expression::new(kind, span))
Ok(Expression::new(kind, span, type_))
}
fn identifier(&mut self) -> Result<(String, Span)> {

View file

@ -1,4 +1,4 @@
use crate::semantic::{Type, VarKind};
use crate::semantic::{Type, UID};
use super::*;
@ -10,12 +10,12 @@ pub enum StatementKind {
type_actual: Type,
value: Expression,
mutable: bool,
varkind: VarKind,
uid: UID,
},
Assignment {
name: String,
value: Expression,
varkind: VarKind,
uid: UID,
},
If {
predicate: Expression,
@ -86,7 +86,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
type_actual: Type::Ambiguous,
value,
mutable,
varkind: VarKind::Undefined,
uid: "".into(),
},
span,
};
@ -107,7 +107,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
kind: s::Assignment {
name,
value,
varkind: VarKind::Undefined,
uid: "".into(),
},
}
},

View file

@ -1,13 +1,8 @@
use std::any::Any;
use crate::{
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
StatementKind, UnaryOp,
semantic::{Symbol, SymbolTable, Type, VarKind},
Expression, ExpressionKind, Statement, err::*, semantic::SymbolTable,
};
use super::primitives::*;
use crate::err::*;
use super::Type;
pub struct Analyzer {
table: SymbolTable,
@ -15,12 +10,160 @@ pub struct Analyzer {
impl Analyzer {
pub fn typecheck(statements: Vec<Statement>) -> Vec<Statement> {
/*
let mut this = Self {
table: SymbolTable::new(),
};
this.block(statements)
*/
statements
}
// Bottom up type inference
fn propogate_up(
&mut self,
mut expr: Box<Expression>,
) -> Result<Box<Expression>> {
use ExpressionKind as e;
expr.kind = match expr.kind {
e::Immediate(imm) => e::Immediate(imm),
e::Identifier(id, mut mangle) => {
let symbol = self.table.lookup(&id).span(&expr.span)?;
mangle = symbol.uid;
expr.type_ = symbol.type_;
e::Identifier(id, mangle)
},
e::Binary {
op,
mut left,
mut right,
} => {
left = self.propogate_up(left)?;
right = self.propogate_up(right)?;
expr.type_ =
Type::binary_op(&left.type_, op, &right.type_).span(&expr.span)?;
e::Binary { op, left, right }
},
e::Unary { op, mut child } => {
child = self.propogate_up(child)?;
expr.type_ = Type::unary_op(op, &child.type_).span(&expr.span)?;
e::Unary { op, child }
},
e::Parenthesis(mut inner) => {
inner = self.propogate_up(inner)?;
expr.type_ = inner.type_.clone();
e::Parenthesis(inner)
},
// Define anonymous function, do not evaluate body yet
e::FunctionDef {
mut params,
returns_str,
mut returns_actual,
body,
mut id,
} => {
for p in &mut params {
p.type_actual =
self.table.lookup_typedef(&p.type_str).span(&expr.span)?;
}
let param_types: Vec<_> =
params.iter().map(|p| p.type_actual.clone()).collect();
returns_actual = match returns_str {
Some(ref s) => self.table.lookup_typedef(s).span(&expr.span)?,
None => Type::Nothing,
};
id = self
.table
.start_func(param_types.clone(), returns_actual.clone());
expr.type_ = Type::FunctionDef {
params: param_types,
returns: returns_actual.clone().into(),
id: id.clone(),
};
e::FunctionDef {
params,
returns_str,
returns_actual,
body,
id,
}
},
e::FunctionCall {
mut callee,
mut args,
mut is_reference,
mut id,
} => {
callee = self.propogate_up(callee)?;
let (params, returns) = match callee.type_.clone() {
Type::FunctionRef {
id: uid,
params,
returns,
} => {
is_reference = true;
id = uid;
(params, returns)
},
Type::FunctionDef {
id: uid,
params,
returns,
} => {
is_reference = false;
id = uid;
(params, returns)
},
_ => {
return error()
.reason(format!("Cannot call type '{}'", callee.type_))
.span(&expr.span);
},
};
expr.type_ = *returns;
// Check that args are correct
if args.len() != params.len() {
return error().reason(format!(
"Expected {} arguments, found {}",
params.len(),
args.len()
));
}
for (index, a) in args.iter_mut().enumerate() {
*a = *self.propogate_up(a.clone().into())?;
*a = *self.propogate_down(a.clone().into(), params[index].clone())?;
}
e::FunctionCall {
callee,
args,
is_reference,
id,
}
},
e::StructDef(vec) => todo!(),
e::StructLiteral { ref name, mut args } => {
self.table.lookup_typedef(name).span(&expr.span)?;
todo!()
},
e::Field {
namespace,
field,
uid,
} => todo!(),
};
Ok(expr)
}
// Top down type assertion
fn propogate_down(
&mut self,
expr: Box<Expression>,
expect: Type,
) -> Result<Box<Expression>> {
todo!()
}
/*
fn block(&mut self, block: Vec<Statement>) -> Vec<Statement> {
let mut new_block = vec![];
for st in block {
@ -134,7 +277,9 @@ impl Analyzer {
self.table.end_block();
},
s::Print(e) => {
stmt.kind = s::Print(*self.expression(e.into(), None)?);
let mut e = *self.expression(e.into(), None)?;
e.type_ = Type::coerce(&Type::Prim(p::integer), &e.type_)?;
stmt.kind = s::Print(e);
},
s::Expression(e) => {
let mut expr = *self.expression(e.into(), None)?;
@ -153,7 +298,8 @@ impl Analyzer {
let return_type = self.table.get_return_type().span(&stmt.span)?;
let type_ = match expression {
Some(e) => {
let e = self.expression(e.into(), Some(return_type.clone()))?;
let mut e = self.expression(e.into(), Some(return_type.clone()))?;
e.type_ = Type::coerce(&return_type, &e.type_).span(&e.span)?;
let type_ = e.type_.clone();
expression = Some(*e);
type_
@ -178,7 +324,7 @@ impl Analyzer {
use Primitive as p;
let type_ = match expr.kind {
e::Immediate(ref i) => Type::Prim(match i {
i::Integer(_) => p::integer_ambiguous,
i::Integer(_, _) => p::integer_ambiguous,
i::Real(_) => p::real_ambiguous,
i::String(_) => p::string,
i::Boolean(_) => p::boolean,
@ -394,4 +540,5 @@ impl Analyzer {
expr.type_ = type_;
Ok(expr)
}
*/
}

View file

@ -2,201 +2,145 @@ mod analyzer;
mod primitives;
mod types;
use crate::{Parameter, err::*};
use crate::err::*;
pub use analyzer::*;
pub use primitives::*;
pub use types::*;
#[allow(non_camel_case_types)]
pub type uid = u32;
// Variable and function numbering
#[derive(Clone, Copy, Debug)]
pub enum VarKind {
Global(uid),
Local(uid),
Function(uid),
Undefined,
}
// Mangled name
pub type UID = String;
impl VarKind {
pub fn unwrap(self) -> uid {
match self {
VarKind::Global(i) | VarKind::Local(i) | VarKind::Function(i) => i,
VarKind::Undefined => unreachable!("Failed unwrapping uid"),
}
}
}
#[derive(Clone, Debug)]
#[derive(Debug, Clone)]
pub struct Symbol {
pub name: String,
pub type_: Type,
pub mutable: bool,
pub kind: VarKind,
name: String,
type_: Type,
uid: UID,
}
#[derive(Debug, Clone)]
pub enum Definition {
Symbol(Symbol),
enum Definition {
Ident(Symbol),
FuncStart,
BlockStart,
FuncStart(Type),
}
fn next(array: &mut [uid]) -> uid {
let current = array.last_mut().unwrap();
let ret = *current;
*current += 1;
ret
}
#[derive(Debug, Clone)]
pub struct SymbolTable {
syms: Vec<Definition>,
defs: Vec<Definition>,
mangle_num: usize,
nesting: usize,
local_varno: Vec<uid>,
global_varno: Vec<uid>,
funcno: uid,
}
impl SymbolTable {
pub fn new() -> Self {
Self {
syms: vec![],
defs: vec![],
mangle_num: 0,
nesting: 0,
global_varno: vec![0],
local_varno: vec![0],
funcno: 1,
}
}
fn define_symbol(
&mut self,
name: String,
type_: Type,
mutable: bool,
) -> Result<VarKind> {
let kind = match type_ {
Type::Prim(_) | Type::Struct(_) | Type::FunctionRef { .. } => {
if self.nesting == 0 {
VarKind::Global(next(&mut self.global_varno))
} else {
VarKind::Local(next(&mut self.local_varno))
}
},
Type::StructDef(_) => {
if mutable {
return error().reason("Struct definition must be immutable");
}
VarKind::Undefined
},
Type::FunctionDef { id, .. } => {
if !mutable {
VarKind::Function(id)
} else {
return error().reason("Function declaration must be immutable");
}
},
_ => VarKind::Undefined,
};
self.syms.push(Definition::Symbol(Symbol {
name,
type_,
mutable,
kind,
}));
Ok(kind)
fn generate_uid(&mut self, name: &str) -> UID {
let uid = format!("${name}${}", self.mangle_num);
self.mangle_num += 1;
uid
}
fn define_param(&mut self, name: String, type_: Type) -> Result<VarKind> {
let kind = VarKind::Local(next(&mut self.local_varno));
self.syms.push(Definition::Symbol(Symbol {
name,
type_,
mutable: false,
kind,
}));
Ok(kind)
}
fn start_func(&mut self, returns: Type) -> uid {
pub fn start_func(&mut self, params: Vec<Type>, returns: Type) -> UID {
self.nesting += 1;
self.local_varno.push(0);
self.syms.push(Definition::FuncStart(returns));
let old = self.funcno;
self.funcno += 1;
old
let uid = self.generate_uid("func");
let type_ = Type::FunctionDef {
params,
returns: returns.into(),
id: uid.clone(),
};
self.define("<anonymous function>", type_.clone());
self.defs.push(Definition::FuncStart);
uid
}
fn get_return_type(&mut self) -> Result<Type> {
for def in &self.syms {
if let Definition::FuncStart(t) = def {
return Ok(t.clone());
}
}
error().reason("Return outside of function")
}
fn end_func(&mut self) {
self.nesting -= 1;
self.local_varno.pop();
while !self.syms.is_empty() {
if let Some(Definition::FuncStart(_)) = self.syms.pop() {
pub fn end_func(&mut self) {
while !self.defs.is_empty() {
if let Some(Definition::FuncStart) = self.defs.pop() {
return;
}
}
unreachable!("Tried to exit global scope in symbol table")
unreachable!("Cannot end global scope")
}
fn start_block(&mut self) {
self.syms.push(Definition::BlockStart);
}
fn end_block(&mut self) {
while !self.syms.is_empty() {
if let Some(Definition::BlockStart) = self.syms.pop() {
pub fn start_block(&mut self) {
while !self.defs.is_empty() {
if let Some(Definition::BlockStart) = self.defs.pop() {
return;
}
}
unreachable!("Tried to exit global scope in symbol table")
unreachable!("Cannot end global scope")
}
fn find_symbol(&self, find_name: &str) -> Result<Symbol> {
let mut nesting = self.nesting;
for s in self.syms.iter().rev() {
match s {
Definition::Symbol(sym)
// Only search function local and global scope
if nesting == self.nesting || nesting == 0 => {
// Convert function definition to function reference
if find_name == sym.name {
return Ok(sym.clone());
pub fn define(&mut self, name: &str, type_: Type) -> UID {
let uid = self.generate_uid(name);
self.defs.push(Definition::Ident(Symbol {
name: name.to_string(),
type_,
uid: uid.clone(),
}));
uid
}
},
Definition::FuncStart(_) => {
nesting -= 1;
pub fn define_func(&mut self, name: &str, uid: UID) {
for def in &mut self.defs {
match def {
Definition::Ident(symbol) if symbol.uid == uid => {
symbol.name = name.to_string();
return;
},
_ => {},
};
}
error().reason(format!("Symbol '{find_name}' is not defined"))
panic!("Tried to name nonexistant function");
}
}
fn get_type(&self, name: &str) -> Result<Type> {
for s in self.syms.iter().rev() {
if let Definition::Symbol(Symbol {
name: name2,
type_: Type::StructDef(params),
..
}) = s
pub fn lookup_typedef(&self, name: &str) -> Result<Type> {
let mut nesting = self.nesting;
for def in &self.defs {
match def {
Definition::Ident(symbol)
if (symbol.name == name)
&& (nesting == 0 || nesting == self.nesting) =>
{
if name == name2 {
return Ok(Type::Struct(params.clone()));
if let Type::StructDef(vec) = &symbol.type_ {
return Ok(Type::Struct(
vec.iter().map(|p| p.type_actual.clone()).collect(),
));
}
},
Definition::FuncStart => nesting -= 1,
_ => {},
}
}
if let Some(p) = Primitive::from_string(name) {
return Ok(Type::Prim(p));
}
error().reason(format!("Type {name} is not defined"))
error().reason(format!("Cannot find type definition '{}'", name))
}
pub fn lookup(&self, name: &str) -> Result<Symbol> {
let mut nesting = self.nesting;
for def in &self.defs {
match def {
Definition::Ident(symbol)
if (symbol.name == name)
&& (nesting == 0 || nesting == self.nesting) =>
{
if let Type::StructDef(_) = symbol.type_ {
continue;
}
return Ok(symbol.clone());
},
Definition::FuncStart => nesting -= 1,
_ => {},
}
}
error().reason(format!("Cannot find the definition of '{}'", name))
}
}

View file

@ -40,6 +40,22 @@ primitives! {
string, glyph
}
impl Primitive {
pub fn as_wat(&self) -> &'static str {
use Primitive::*;
match self {
boolean | glyph | w8 | w16 | w32 | whole | i8 | i16 | i32 | integer => {
"i32"
},
w64 | i64 => "i64",
r32 | real => "f32",
r64 => "f64",
string => todo!(),
_ => panic!(),
}
}
}
macro_rules! selfsame_basic {
( $lhs:ident, $op:ident, $rhs:ident, $binop:ident, $i:ident ) => {
if ($i == $rhs && $rhs == $lhs) && $op == BinaryOp::$binop {
@ -91,47 +107,44 @@ macro_rules! comparison {
}
impl Primitive {
pub fn coerce_ambiguous(
lhs: Primitive,
rhs: Primitive,
) -> (Primitive, Primitive) {
pub fn coerce(&mut self, into: Primitive) {
use Primitive::*;
let is_int = |i| {
if let i8 | i16 | i32 | i64 | integer | integer_ambiguous = i {
true
} else {
false
}
*self = match (*self, into) {
(
integer_ambiguous,
i8 | i16 | i32 | i64 | integer | w8 | w16 | w32 | w64 | whole,
) => into,
(real_ambiguous, r32 | r64) => into,
_ => *self,
};
let is_real = |r| {
if let r32 | r64 | real | real_ambiguous = r {
true
} else {
false
}
};
// Ambiguous integer coercion
if lhs == integer_ambiguous && is_int(rhs) {
return (rhs, rhs);
} else if rhs == integer_ambiguous && is_int(lhs) {
return (lhs, lhs);
pub fn is_ambiguous(&self) -> bool {
match self {
Primitive::integer_ambiguous | Primitive::real_ambiguous => true,
_ => false,
}
// Ambiguous real coercion
if lhs == real_ambiguous && is_real(rhs) {
return (rhs, rhs);
} else if rhs == real_ambiguous && is_real(lhs) {
return (lhs, lhs);
}
return (lhs, rhs);
pub fn promote(&mut self) {
*self = match *self {
Primitive::integer_ambiguous => Primitive::integer,
Primitive::real_ambiguous => Primitive::real,
_ => *self,
}
}
pub fn binary_op(
lhs: Primitive,
mut lhs: Primitive,
op: BinaryOp,
rhs: Primitive,
mut rhs: Primitive,
) -> Result<Primitive> {
use Primitive::*;
let (lhs, rhs) = Primitive::coerce_ambiguous(lhs, rhs);
if lhs.is_ambiguous() && !rhs.is_ambiguous() {
lhs.coerce(rhs);
} else if rhs.is_ambiguous() && !lhs.is_ambiguous() {
rhs.coerce(lhs);
}
selfsame_basic! {
lhs, op, rhs; w8, w16, w32, w64, i8, i16, i32, i64,
integer, integer_ambiguous, real, real_ambiguous
@ -155,7 +168,7 @@ impl Primitive {
error().reason(format!("Unary {} is not defined for {}", op, child));
match op {
Minus => match child {
boolean | string | glyph | w8 | w16 | w32 | w64 => e,
boolean | string | glyph | whole | w8 | w16 | w32 | w64 => e,
_ => Ok(child),
},
Plus => match child {

View file

@ -1,10 +1,9 @@
use crate::{
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
StatementKind, UnaryOp,
semantic::{Symbol, SymbolTable},
};
use super::{primitives::*, uid};
use super::{UID, primitives::*};
use crate::err::*;
#[derive(Debug, Clone)]
@ -12,16 +11,17 @@ pub enum Type {
Ambiguous,
Nothing,
Prim(Primitive),
Struct(Vec<Parameter>),
Struct(Vec<Type>),
StructDef(Vec<Parameter>),
FunctionRef {
params: Vec<Type>,
returns: Box<Type>,
id: UID,
},
FunctionDef {
params: Vec<Type>,
returns: Box<Type>,
id: uid,
id: UID,
},
}
@ -32,14 +32,14 @@ impl std::fmt::Display for Type {
Type::Nothing => write!(f, "nothing"),
Type::Prim(primitive) => write!(f, "{primitive}"),
Type::Struct(vec) => write!(f, "struct {vec:?}"),
Type::StructDef(vec) => write!(f, "struct definition"),
Type::FunctionRef { params, returns } => {
Type::StructDef(_) => write!(f, "struct definition"),
Type::FunctionRef {
params, returns, ..
} => {
write!(f, "({params:?}) -> {returns}")
},
Type::FunctionDef {
params,
returns,
id,
params, returns, ..
} => write!(f, "({params:?}) -> {returns}"),
}
}
@ -51,20 +51,8 @@ impl PartialEq for Type {
match (self, other) {
(Ambiguous, Ambiguous) => true,
(Prim(p1), Prim(p2)) if p1 == p2 => true,
(Struct(p1), Struct(p2)) => p1
.iter()
.map(|p| p.type_actual.clone())
.eq(p2.iter().map(|p| p.type_actual.clone())),
(
FunctionRef {
params: p1,
returns: r1,
},
FunctionRef {
params: p2,
returns: r2,
},
) => p1.iter().eq(p2.iter()) && r1 == r2,
(Struct(p1), Struct(p2)) => p1.iter().eq(p2.iter()),
(FunctionRef { id: id1, .. }, FunctionRef { id: id2, .. }) => id1 == id2,
(FunctionDef { id: id1, .. }, FunctionDef { id: id2, .. }) => id1 == id2,
(Nothing, Nothing) => true,
_ => false,
@ -97,25 +85,17 @@ impl Type {
}
}
pub fn coerce(expect: &Type, actual: &Type) -> Result<Type> {
use Primitive as p;
pub fn coerce(&mut self, expect: &Type) {
use Type::*;
let e = || {
error().reason(format!(
"Could not coerce type '{actual:?}' into '{expect:?}'"
))
};
match (expect, actual) {
(Ambiguous, Ambiguous) => e(),
(Ambiguous, Prim(p::integer_ambiguous)) => Ok(Prim(p::integer)),
(Ambiguous, Prim(p::real_ambiguous)) => Ok(Prim(p::real)),
(Ambiguous, t) => Ok(t.clone()),
(Prim(p1), Prim(p2)) => {
let (p1, p2) = Primitive::coerce_ambiguous(*p1, *p2);
if p1 != p2 { e() } else { Ok(Type::Prim(p1)) }
*self = match (expect, self.clone()) {
(Ambiguous, Ambiguous) => Ambiguous,
(Ambiguous, t) => t.clone(),
(Prim(mut p1), Prim(p2)) => {
p1.coerce(p2);
Prim(p1)
},
(t1, t2) if t1 == t2 => Ok(t1.clone()),
_ => e(),
}
(t1, t2) if t1 == &t2 => t1.clone(),
_ => Ambiguous,
};
}
}

View file

@ -1,5 +1,13 @@
use crate::err::*;
use crate::Span;
use crate::err::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Base {
Binary = 2,
Octal = 8,
Decimal = 10,
Hex = 16,
}
#[derive(Debug, Clone)]
pub enum TokenKind {
@ -50,8 +58,8 @@ pub enum TokenKind {
Identifier(String),
StringLiteral(String),
GlyphLiteral(char),
IntegerLiteral(i64),
FloatLiteral(f64),
IntegerLiteral(String, Base),
FloatLiteral(String),
If,
Else,
@ -106,82 +114,7 @@ impl Eq for TokenKind {
impl std::fmt::Display for TokenKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use TokenKind::*;
write!(
f,
"'{}'",
match self {
LeftParen => "(",
RightParen => ")",
LeftBrace => "{",
RightBrace => "}",
LeftSquare => "[",
RightSquare => "]",
Comma => ",",
Colon => ":",
Semicolon => ";",
Dot => ".",
DotDot => "..",
Plus => "+",
Minus => "-",
Slash => "/",
Star => "*",
Percent => "%",
Arrow => "->",
FatArrow => "=>",
PlusEqual => "+=",
MinusEqual => "-=",
SlashEqual => "/=",
StarEqual => "*=",
PercentEqual => "%=",
Bang => "!",
BangEqual => "!=",
Question => "?",
QuestionEqual => "?=",
Equal => "=",
DoubleEqual => "==",
Greater => ">",
GreaterEqual => ">=",
Less => "<",
LessEqual => "<=",
Pipe => "|",
Ampersand => "&",
Carrot => "^",
Hash => "#",
DotDotEqual => "..=",
Identifier(i) => i,
StringLiteral(s) => s.as_str(),
GlyphLiteral(_) => "<glyph>",
IntegerLiteral(_) => "<integer>",
FloatLiteral(_) => "<real>",
If => "if",
Else => "else",
And => "and",
Or => "or",
Xor => "xor",
Not => "not",
Nand => "nand",
Nor => "nor",
Xnor => "xnor",
Print => "print",
Break => "break",
Return => "return",
Continue => "continue",
For => "for",
While => "while",
True => "true",
False => "false",
Struct => "struct",
Enum => "enum",
Union => "union",
Whitespace(_) => "<whitespace>",
SmallComment(_) => "<comment>",
BigComment(_) => "<comment>",
Error(_) => "<error>",
Idk => unreachable!(),
EOF => "<end of file>",
}
)
write!(f, "{self:?}")
}
}
@ -449,7 +382,24 @@ impl<I: Iterator<Item = char>> Tokenizer<I> {
buffer.push(c);
let _ = self.next_char();
}
return t(parse_number(&buffer).span(&position)?, position);
// Determine base
let base = if buffer.starts_with("0b") {
Base::Binary
} else if buffer.starts_with("0o") || buffer.starts_with("0O") {
Base::Octal
} else if buffer.starts_with("0x") || buffer.starts_with("0X") {
Base::Hex
} else {
Base::Decimal
};
// Determine integer or float
if base == Base::Decimal
&& (encountered_dot || buffer.contains("e") || buffer.contains("E"))
{
return t(FloatLiteral(buffer), position);
} else {
return t(IntegerLiteral(buffer, base), position);
}
}
// Match keyword or identifier
while let Some(c) = self.peek(0) {
@ -504,43 +454,6 @@ impl<I: Iterator<Item = char>> Iterator for Tokenizer<I> {
}
}
fn parse_number(num: &str) -> Result<TokenKind> {
use TokenKind::*;
let num = num.replace('_', "");
// Floating point (only decimal)
if num.contains('.') {
num
.parse::<f64>()
.map(|f| FloatLiteral(f))
.reason("Could not parse real number")
}
// Hex integer
else if let Some(hex) = num.strip_prefix("0x") {
i64::from_str_radix(hex, 16)
.map(|i| IntegerLiteral(i))
.reason("Could not parse hex integer number")
}
// Octal integer
else if let Some(oct) = num.strip_prefix("0o") {
i64::from_str_radix(oct, 8)
.map(|i| IntegerLiteral(i))
.reason("Could not parse octal integer number")
}
// Binary integer
else if let Some(bin) = num.strip_prefix("0b") {
i64::from_str_radix(bin, 2)
.map(|i| IntegerLiteral(i))
.reason("Could not parse binary integer number")
}
// Decimal integer
else {
num
.parse::<i64>()
.map(|i| IntegerLiteral(i))
.reason("Could not parse integer number")
}
}
fn bake_string(s: &str) -> Result<String> {
let mut baked = String::with_capacity(s.len());
let mut it = s.chars();

BIN
test.wasm

Binary file not shown.

14
test.wat Normal file
View file

@ -0,0 +1,14 @@
(module
(import "console" "log" (func $log (param i32)))
(global $a$0 (mut i32) (i32.const 0))
(func $0
i32.const 10
i32.const 5
i32.div_s
global.set $a$0
global.get $a$0
call $log
)
(start $0)
)