Reworking semantic analyzer
This commit is contained in:
parent
7e110a9560
commit
22c78a9945
14
index.html
14
index.html
|
@ -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>
|
||||
|
|
|
@ -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
144
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<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
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
22
src/main.rs
22
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<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(())
|
||||
}
|
||||
|
|
|
@ -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)> {
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
147
src/token.rs
147
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(_) => "<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();
|
||||
|
|
Loading…
Reference in a new issue