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) +)