diff --git a/demo.hal b/demo.hal
index 62aaf05..c1680c6 100644
--- a/demo.hal
+++ b/demo.hal
@@ -1,3 +1,2 @@
-10 + 2 + 3 * 4;
-
-
+b := 10.0 / 5.0;
+c := b;
diff --git a/index.html b/index.html
index 9e19deb..325175c 100644
--- a/index.html
+++ b/index.html
@@ -4,18 +4,8 @@
diff --git a/src/frontend.rs b/src/frontend.rs
index 143d688..02dcb07 100644
--- a/src/frontend.rs
+++ b/src/frontend.rs
@@ -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,
}
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) {
+ 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();
+ }
+ */
}
diff --git a/src/ir.rs b/src/ir.rs
index 3313fa8..748c946 100644
--- a/src/ir.rs
+++ b/src/ir.rs
@@ -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,
}
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) {
+ pub fn compile(block: Vec) -> (Vec, 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 {
+ fn hoist(&self) -> Vec {
let mut functions = vec![(vec![], vec![])];
let mut result = vec![];
for index in 0..self.ir.len() {
@@ -259,3 +350,4 @@ impl Compiler {
result
}
}
+*/
diff --git a/src/main.rs b/src/main.rs
index 22b5eb6..2613dbb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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- {
+ Tokenizer::new(input.chars()).filter(|t| t.0.is_meaningful())
+}
+
+fn parse(input: &'static str) -> impl Iterator
- {
+ Parser::new(tokenize(input))
+}
+
+fn typecheck(input: &'static str) -> Vec {
+ 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(())
}
diff --git a/src/parse/expression.rs b/src/parse/expression.rs
index 3d2e9c2..c8e57b1 100644
--- a/src/parse/expression.rs
+++ b/src/parse/expression.rs
@@ -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,
@@ -53,11 +53,13 @@ pub enum ExpressionKind {
returns_str: Option,
returns_actual: Type,
body: Vec,
- id: uid,
+ id: UID,
},
FunctionCall {
callee: Box,
args: Vec,
+ is_reference: bool,
+ id: UID,
},
StructDef(Vec),
StructLiteral {
@@ -67,6 +69,7 @@ pub enum ExpressionKind {
Field {
namespace: Box,
field: Box,
+ 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> Parser {
pub fn expression(&mut self, precedence: Precedence) -> Result {
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> Parser {
child: child.into(),
},
span,
+ Type::Ambiguous,
)
}
// Terminal or paren
@@ -191,6 +188,7 @@ impl> Parser {
right: rhs.into(),
},
span,
+ Type::Ambiguous,
);
}
// Field
@@ -206,8 +204,10 @@ impl> Parser {
e::Field {
namespace: current.into(),
field: field.into(),
+ uid: "".into(),
},
span,
+ Type::Ambiguous,
)
}
// Function call
@@ -234,8 +234,11 @@ impl> Parser {
e::FunctionCall {
callee: current.into(),
args,
+ is_reference: false,
+ id: "".into(),
},
span + span2,
+ Type::Ambiguous,
);
}
// Unary postfix
@@ -256,6 +259,7 @@ impl> Parser {
child: current.into(),
},
span,
+ Type::Ambiguous,
);
} else {
break;
@@ -268,28 +272,34 @@ impl> Parser {
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> Parser {
returns_str,
returns_actual: Type::Ambiguous,
body,
- id: 0,
+ id: "".into(),
}
},
// Struct definition
@@ -405,7 +415,7 @@ impl> Parser {
},
t::Identifier(i) => {
self.skip(1);
- e::Identifier(i, VarKind::Undefined)
+ e::Identifier(i, "".into())
},
// Parenthetical
t::LeftParen => {
@@ -425,7 +435,7 @@ impl> Parser {
.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)> {
diff --git a/src/parse/statement.rs b/src/parse/statement.rs
index 8e76ae4..0961907 100644
--- a/src/parse/statement.rs
+++ b/src/parse/statement.rs
@@ -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> Parser {
type_actual: Type::Ambiguous,
value,
mutable,
- varkind: VarKind::Undefined,
+ uid: "".into(),
},
span,
};
@@ -107,7 +107,7 @@ impl> Parser {
kind: s::Assignment {
name,
value,
- varkind: VarKind::Undefined,
+ uid: "".into(),
},
}
},
diff --git a/src/semantic/analyzer.rs b/src/semantic/analyzer.rs
index de2ecce..e150acf 100644
--- a/src/semantic/analyzer.rs
+++ b/src/semantic/analyzer.rs
@@ -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) -> Vec {
+ /*
let mut this = Self {
table: SymbolTable::new(),
};
this.block(statements)
+ */
+ statements
}
+ // Bottom up type inference
+ fn propogate_up(
+ &mut self,
+ mut expr: Box,
+ ) -> Result> {
+ 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,
+ expect: Type,
+ ) -> Result> {
+ todo!()
+ }
+
+ /*
fn block(&mut self, block: Vec) -> Vec {
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)
}
+ */
}
diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs
index 770a64b..c31e658 100644
--- a/src/semantic/mod.rs
+++ b/src/semantic/mod.rs
@@ -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,
+ defs: Vec,
+ mangle_num: usize,
nesting: usize,
- local_varno: Vec,
- global_varno: Vec,
- 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 {
- 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 {
- 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, 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("", type_.clone());
+ self.defs.push(Definition::FuncStart);
+ uid
}
- fn get_return_type(&mut self) -> Result {
- 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 {
- 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());
- }
- },
- Definition::FuncStart(_) => {
- nesting -= 1;
+ 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
+ }
+
+ 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;
},
_ => {},
- };
+ }
+ panic!("Tried to name nonexistant function");
}
- error().reason(format!("Symbol '{find_name}' is not defined"))
}
- fn get_type(&self, name: &str) -> Result {
- for s in self.syms.iter().rev() {
- if let Definition::Symbol(Symbol {
- name: name2,
- type_: Type::StructDef(params),
- ..
- }) = s
- {
- if name == name2 {
- return Ok(Type::Struct(params.clone()));
- }
+ pub fn lookup_typedef(&self, name: &str) -> Result {
+ 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(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 {
+ 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))
}
}
diff --git a/src/semantic/primitives.rs b/src/semantic/primitives.rs
index 7be5272..3a42acc 100644
--- a/src/semantic/primitives.rs
+++ b/src/semantic/primitives.rs
@@ -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);
+ }
+
+ pub fn promote(&mut self) {
+ *self = match *self {
+ Primitive::integer_ambiguous => Primitive::integer,
+ Primitive::real_ambiguous => Primitive::real,
+ _ => *self,
}
- return (lhs, rhs);
}
pub fn binary_op(
- lhs: Primitive,
+ mut lhs: Primitive,
op: BinaryOp,
- rhs: Primitive,
+ mut rhs: Primitive,
) -> Result {
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 {
diff --git a/src/semantic/types.rs b/src/semantic/types.rs
index b6cd8e8..f26a1ce 100644
--- a/src/semantic/types.rs
+++ b/src/semantic/types.rs
@@ -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),
+ Struct(Vec),
StructDef(Vec),
FunctionRef {
params: Vec,
returns: Box,
+ id: UID,
},
FunctionDef {
params: Vec,
returns: Box,
- 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 {
- 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,
+ };
}
}
diff --git a/src/token.rs b/src/token.rs
index 44714e9..d0e394b 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -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(_) => "",
- IntegerLiteral(_) => "",
- FloatLiteral(_) => "",
- 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(_) => "",
- SmallComment(_) => "",
- BigComment(_) => "",
- Error(_) => "",
- Idk => unreachable!(),
- EOF => "",
- }
- )
+ write!(f, "{self:?}")
}
}
@@ -449,7 +382,24 @@ impl> Tokenizer {
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> Iterator for Tokenizer {
}
}
-fn parse_number(num: &str) -> Result {
- use TokenKind::*;
- let num = num.replace('_', "");
- // Floating point (only decimal)
- if num.contains('.') {
- num
- .parse::()
- .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::()
- .map(|i| IntegerLiteral(i))
- .reason("Could not parse integer number")
- }
-}
-
fn bake_string(s: &str) -> Result {
let mut baked = String::with_capacity(s.len());
let mut it = s.chars();
diff --git a/test.wasm b/test.wasm
index cb1cd8b..202d84d 100644
Binary files a/test.wasm and b/test.wasm differ
diff --git a/test.wat b/test.wat
new file mode 100644
index 0000000..a4f9aab
--- /dev/null
+++ b/test.wat
@@ -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)
+)