readme
This commit is contained in:
parent
38a3a5eab0
commit
c1ae2b4e48
5
demo.hal
5
demo.hal
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
S :: struct {
|
||||
a: integer,
|
||||
b: glyph,
|
||||
|
@ -9,5 +10,9 @@ s := foo(S{a: 1, b: 'a', c: 1.0}, 2);
|
|||
foo :: (s: S, a: integer) -> S {
|
||||
return s;
|
||||
}
|
||||
*/
|
||||
|
||||
S :: struct { a, b, c: real }
|
||||
|
||||
|
||||
|
||||
|
|
37
readme.md
Normal file
37
readme.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Halcyon Compiler
|
||||
The Halcyon language is a strongly typed compiled language for the
|
||||
[WebAssembly](https://webassembly.org/) virtual machine. Its major features
|
||||
are implicit types, move semantics, and memory safety.
|
||||
|
||||
## Semantics
|
||||
The syntax of Halcyon is designed to be minimal and readable. While Halcyon is
|
||||
strongly typed, types can be assumed from context at compile time. Below is an
|
||||
annotated "FizzBuzz" program
|
||||
```
|
||||
fizzbuzz :: (number) {
|
||||
sum := 0;
|
||||
if number == 0 {
|
||||
println("Input cannot be zero");
|
||||
break sum; // Early return
|
||||
}
|
||||
for i : 0..number {
|
||||
if (i % 3 == 0) and (i % 5 == 0) {
|
||||
println("fizzbuzz");
|
||||
sum += 1;
|
||||
}
|
||||
else if i % 3 == 0 {
|
||||
println("fizz");
|
||||
}
|
||||
else if i % 5 == 0 {
|
||||
println("buzz");
|
||||
}
|
||||
}
|
||||
sum // return the number of fizzbuzz's
|
||||
}
|
||||
|
||||
fizzbuzz(15);
|
||||
```
|
||||
|
||||
## In Depth
|
||||
I have written a more comprehensive (but outdated) language specification
|
||||
on my blog [here](https://lgatlin.dev/writings/2-language-ideas.html).
|
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
use crate::{Expression, ExpressionKind, Statement, StatementKind};
|
||||
|
||||
use super::*;
|
||||
|
@ -247,3 +248,4 @@ impl Compiler {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
mod generate;
|
||||
mod wasm;
|
||||
|
||||
|
@ -194,3 +195,4 @@ impl Compiler {
|
|||
self.ir = result;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
use crate::{
|
||||
Base, BinaryOp, Immediate,
|
||||
err::*,
|
||||
|
@ -209,3 +210,4 @@ impl Compiler {
|
|||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
43
src/main.rs
43
src/main.rs
|
@ -3,13 +3,11 @@ mod ir;
|
|||
mod lookahead;
|
||||
mod parse;
|
||||
mod semantic;
|
||||
mod semantic2;
|
||||
mod token;
|
||||
mod treewalk;
|
||||
use std::ops::Add;
|
||||
|
||||
use err::*;
|
||||
use ir::Compiler;
|
||||
use lookahead::*;
|
||||
use parse::*;
|
||||
use semantic::Analyzer;
|
||||
|
@ -64,29 +62,6 @@ fn test_expression(expr: &str) {
|
|||
println!("{:?}", parser.expression(0).unwrap());
|
||||
}
|
||||
|
||||
fn prints(st: &Statement) {
|
||||
use StatementKind as s;
|
||||
println!("{st:?}");
|
||||
match &st.kind {
|
||||
s::Declaration {
|
||||
value:
|
||||
Expression {
|
||||
kind: ExpressionKind::FunctionDef { body: block, .. },
|
||||
..
|
||||
},
|
||||
..
|
||||
}
|
||||
| s::If { block, .. }
|
||||
| s::While { block, .. }
|
||||
| s::Block(block) => {
|
||||
for s in block {
|
||||
prints(s);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
||||
fn tokenize(input: &'static str) -> impl Iterator<Item = Token> {
|
||||
Tokenizer::new(input.chars()).filter(|t| t.0.is_meaningful())
|
||||
}
|
||||
|
@ -95,25 +70,23 @@ fn parse(input: &'static str) -> impl Iterator<Item = Statement> {
|
|||
Parser::new(tokenize(input))
|
||||
}
|
||||
|
||||
fn typecheck(input: &'static str) -> Vec<Statement> {
|
||||
Analyzer::new().typecheck(parse(input).collect())
|
||||
}
|
||||
|
||||
fn compile(input: &'static str) {
|
||||
let mut a = Analyzer::new();
|
||||
let s = a.typecheck(parse(input).collect());
|
||||
let mut c = Compiler::new(a.table);
|
||||
c.compile(s);
|
||||
fn typecheck(input: &'static str) {
|
||||
let parsed: Vec<_> = parse(input).collect();
|
||||
let mut s = semantic::Analyzer::new();
|
||||
s.block(parsed);
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
for p in parse(include_str!("../demo.hal")) {
|
||||
println!("------------------");
|
||||
println!("{p:#?}");
|
||||
}
|
||||
/*
|
||||
for s in typecheck(include_str!("../demo.hal")) {
|
||||
println!("------------------");
|
||||
println!("{s:#?}");
|
||||
}
|
||||
*/
|
||||
compile(include_str!("../demo.hal"));
|
||||
//let module = frontend::Module::from_file("./demo.hal")?;
|
||||
//module.write_to("test");
|
||||
Ok(())
|
||||
|
|
|
@ -2,16 +2,22 @@ use crate::{Base, semantic::*};
|
|||
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Parameter {
|
||||
pub name: String,
|
||||
pub type_str: String,
|
||||
pub type_actual: Type,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Parameters {
|
||||
pub arity: usize,
|
||||
pub names: Vec<String>,
|
||||
pub type_names: Vec<String>,
|
||||
pub types: Vec<String>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Parameter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}: {:?}", self.name, self.type_actual)
|
||||
impl Default for Parameters {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
arity: 0,
|
||||
names: vec![],
|
||||
type_names: vec![],
|
||||
types: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +45,10 @@ impl std::fmt::Display for Immediate {
|
|||
#[derive(Clone, Debug)]
|
||||
pub enum ExpressionKind {
|
||||
Immediate(Immediate),
|
||||
Identifier(String, UID),
|
||||
Identifier {
|
||||
name: String,
|
||||
uid: UID,
|
||||
},
|
||||
Binary {
|
||||
op: BinaryOp,
|
||||
left: Box<Expression>,
|
||||
|
@ -51,29 +60,34 @@ pub enum ExpressionKind {
|
|||
},
|
||||
Parenthesis(Box<Expression>),
|
||||
FunctionDef {
|
||||
params: Vec<Parameter>,
|
||||
params: Parameters,
|
||||
returns_str: Option<String>,
|
||||
returns_actual: Type,
|
||||
body: Vec<Statement>,
|
||||
id: UID,
|
||||
uid: UID,
|
||||
},
|
||||
FunctionCall {
|
||||
callee: Box<Expression>,
|
||||
args: Vec<Expression>,
|
||||
is_reference: bool,
|
||||
id: UID,
|
||||
uid: UID,
|
||||
},
|
||||
StructDef(Vec<Parameter>, usize),
|
||||
StructDef(Parameters, UID),
|
||||
StructLiteral {
|
||||
name: String,
|
||||
args: Vec<(String, Expression)>,
|
||||
id: UID,
|
||||
uid: UID,
|
||||
},
|
||||
Field {
|
||||
namespace: Box<Expression>,
|
||||
field: Box<Expression>,
|
||||
uid: UID,
|
||||
},
|
||||
Block(Vec<Statement>),
|
||||
If {
|
||||
predicate: Box<Expression>,
|
||||
block: Vec<Statement>,
|
||||
else_: Option<Box<Expression>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -105,7 +119,7 @@ impl std::fmt::Display for ExpressionKind {
|
|||
e::Unary { op: token, child } => {
|
||||
write!(f, "({token} {child})")
|
||||
},
|
||||
e::Identifier(i, _) => write!(f, "{i}"),
|
||||
e::Identifier { name, .. } => write!(f, "{name}"),
|
||||
e::FunctionCall { callee, args, .. } => {
|
||||
write!(f, "({callee} call {args:?})")
|
||||
},
|
||||
|
@ -123,6 +137,28 @@ impl std::fmt::Display for ExpressionKind {
|
|||
},
|
||||
e::StructDef(params, _) => write!(f, "struct {{ {params:?} }}"),
|
||||
e::StructLiteral { name, args, .. } => write!(f, "{name} {{ {args:?} }}"),
|
||||
e::Block(block) => {
|
||||
write!(f, "{{\n")?;
|
||||
for s in block {
|
||||
write!(f, "{:#?}", s)?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
},
|
||||
e::If {
|
||||
predicate,
|
||||
block,
|
||||
else_,
|
||||
} => {
|
||||
write!(f, "{{\n")?;
|
||||
for s in block {
|
||||
write!(f, "{:#?}", s)?;
|
||||
}
|
||||
write!(f, "}}")?;
|
||||
if let Some(else_) = else_ {
|
||||
write!(f, "{else_}")?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +197,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
Type::Ambiguous,
|
||||
)
|
||||
}
|
||||
// Terminal or paren
|
||||
// Primary
|
||||
else {
|
||||
self.primary().reason("Expected expression")?
|
||||
};
|
||||
|
@ -236,8 +272,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
e::FunctionCall {
|
||||
callee: current.into(),
|
||||
args,
|
||||
is_reference: false,
|
||||
id: "".into(),
|
||||
uid: "".into(),
|
||||
},
|
||||
span + span2,
|
||||
Type::Ambiguous,
|
||||
|
@ -270,6 +305,73 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
Ok(current)
|
||||
}
|
||||
|
||||
pub fn parameters(&mut self, mut span: Span) -> Result<Parameters> {
|
||||
use TokenKind as t;
|
||||
let mut arity = 0;
|
||||
let mut names = vec![];
|
||||
let mut type_names = vec![];
|
||||
let mut types = vec![];
|
||||
let mut strongly_typed = false;
|
||||
loop {
|
||||
// Param name
|
||||
let (name, span2) = match self.identifier() {
|
||||
Ok((ident, span)) => (ident, span),
|
||||
Err(_) => break,
|
||||
};
|
||||
println!("{name}");
|
||||
span = span + span2;
|
||||
names.push(name.clone());
|
||||
// Param type (optional)
|
||||
if self.eat(t::Colon).is_ok() {
|
||||
let (type_name, span2) = self.identifier().trace_span(
|
||||
span + span2,
|
||||
format!("While parsing type of '{}'", name),
|
||||
)?;
|
||||
strongly_typed = true;
|
||||
span = span + span2;
|
||||
type_names.push(type_name);
|
||||
} else {
|
||||
type_names.push("".into());
|
||||
}
|
||||
types.push("".into());
|
||||
arity += 1;
|
||||
// Comma
|
||||
if !self.eat(t::Comma).is_ok() {
|
||||
if self.look(0, t::Identifier("".into())).is_ok() {
|
||||
return error().reason("Expected comma (,) here").span(&span);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Back-propogate type names
|
||||
if strongly_typed {
|
||||
let mut last_name = "".to_string();
|
||||
for name in type_names.iter_mut().rev() {
|
||||
if name.is_empty() {
|
||||
if last_name.is_empty() {
|
||||
return error()
|
||||
.reason("Cannot mix typed and untyped parameters")
|
||||
.span(&span);
|
||||
} else {
|
||||
*name = last_name.clone();
|
||||
}
|
||||
} else {
|
||||
last_name = name.clone();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return error()
|
||||
.reason("Untyped parameters are not allowed (temporarily)")
|
||||
.span(&span);
|
||||
}
|
||||
Ok(Parameters {
|
||||
arity,
|
||||
names,
|
||||
type_names,
|
||||
types,
|
||||
})
|
||||
}
|
||||
|
||||
fn primary(&mut self) -> Result<Expression> {
|
||||
use ExpressionKind as e;
|
||||
use Immediate as im;
|
||||
|
@ -301,36 +403,21 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
self.skip(1);
|
||||
e::Immediate(im::Boolean(false))
|
||||
},
|
||||
t::If => return self.if_else(),
|
||||
t::LeftBrace => {
|
||||
let (block, span1) = self.block()?;
|
||||
span = span + span1;
|
||||
e::Block(block)
|
||||
},
|
||||
// Function definition
|
||||
t::LeftParen
|
||||
if (self.look(1, t::Identifier("".into())).is_ok()
|
||||
&& self.look(2, t::Colon).is_ok())
|
||||
&& (self.look(2, t::Colon).is_ok()
|
||||
|| self.look(2, t::Comma).is_ok()))
|
||||
|| self.look(1, t::RightParen).is_ok() =>
|
||||
{
|
||||
self.skip(1);
|
||||
let mut params = vec![];
|
||||
loop {
|
||||
let (name, span2) = match self.identifier() {
|
||||
Ok((name, span)) => (name, span),
|
||||
Err(_) => break,
|
||||
};
|
||||
span = span + span2;
|
||||
self
|
||||
.eat(t::Colon)
|
||||
.trace_span(span, "while parsing function parameter type")?;
|
||||
let (type_, span2) = self
|
||||
.identifier()
|
||||
.trace_span(span, "while parsing function parameter type")?;
|
||||
span = span + span2;
|
||||
params.push(Parameter {
|
||||
name,
|
||||
type_str: type_,
|
||||
type_actual: Type::Ambiguous,
|
||||
});
|
||||
if !self.eat(t::Comma).is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let params = self.parameters(span)?;
|
||||
let Token(_, span2) = self
|
||||
.eat(t::RightParen)
|
||||
.span(&span)
|
||||
|
@ -355,38 +442,16 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
returns_str,
|
||||
returns_actual: Type::Ambiguous,
|
||||
body,
|
||||
id: "".into(),
|
||||
uid: "".into(),
|
||||
}
|
||||
},
|
||||
// Struct definition
|
||||
t::Struct => {
|
||||
self.skip(1);
|
||||
self.eat(t::LeftBrace)?;
|
||||
let mut params = vec![];
|
||||
loop {
|
||||
let (name, span2) = match self.identifier() {
|
||||
Ok((name, span)) => (name, span),
|
||||
Err(_) => break,
|
||||
};
|
||||
span = span + span2;
|
||||
self
|
||||
.eat(t::Colon)
|
||||
.trace_span(span, "while parsing struct parameter type")?;
|
||||
let (type_, span2) = self
|
||||
.identifier()
|
||||
.trace_span(span, "while parsing struct parameter type")?;
|
||||
span = span + span2;
|
||||
params.push(Parameter {
|
||||
name,
|
||||
type_str: type_,
|
||||
type_actual: Type::Ambiguous,
|
||||
});
|
||||
if !self.eat(t::Comma).is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let params = self.parameters(span)?;
|
||||
self.eat(t::RightBrace)?;
|
||||
e::StructDef(params, usize::MAX)
|
||||
e::StructDef(params, "".into())
|
||||
},
|
||||
// Struct literal
|
||||
t::Identifier(name) if self.look(1, t::LeftBrace).is_ok() => {
|
||||
|
@ -414,12 +479,15 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
e::StructLiteral {
|
||||
name,
|
||||
args,
|
||||
id: "".into(),
|
||||
uid: "".into(),
|
||||
}
|
||||
},
|
||||
t::Identifier(i) => {
|
||||
self.skip(1);
|
||||
e::Identifier(i, "".into())
|
||||
e::Identifier {
|
||||
name: i,
|
||||
uid: "".into(),
|
||||
}
|
||||
},
|
||||
// Parenthetical
|
||||
t::LeftParen => {
|
||||
|
@ -442,6 +510,41 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
Ok(Expression::new(kind, span, Type::Ambiguous))
|
||||
}
|
||||
|
||||
fn if_else(&mut self) -> Result<Expression> {
|
||||
use TokenKind as t;
|
||||
if let Ok(Token(_, span)) = self.eat(t::If) {
|
||||
let predicate = self
|
||||
.expression(0)
|
||||
.trace_span(span, "in predicate of 'if' statement")?;
|
||||
let span = span + predicate.span;
|
||||
let block = self
|
||||
.block()
|
||||
.trace_span(span, "in block of 'if' statement")?;
|
||||
let (block, span) = (block.0, span + block.1);
|
||||
let else_ = if self.eat(t::Else).is_ok() {
|
||||
Some(Box::new(self.if_else()?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Expression {
|
||||
kind: ExpressionKind::If {
|
||||
predicate: predicate.into(),
|
||||
block,
|
||||
else_,
|
||||
},
|
||||
span,
|
||||
type_: Type::Ambiguous,
|
||||
})
|
||||
} else {
|
||||
let (block, span) = self.block()?;
|
||||
Ok(Expression {
|
||||
kind: ExpressionKind::Block(block),
|
||||
span,
|
||||
type_: Type::Ambiguous,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn identifier(&mut self) -> Result<(String, Span)> {
|
||||
use TokenKind as t;
|
||||
match self.peek(0) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::semantic::{Type, UID};
|
||||
use crate::semantic::UID;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -7,7 +7,7 @@ pub enum StatementKind {
|
|||
Declaration {
|
||||
name: String,
|
||||
type_str: Option<String>,
|
||||
type_actual: Type,
|
||||
type_uid: UID,
|
||||
value: Expression,
|
||||
mutable: bool,
|
||||
uid: UID,
|
||||
|
@ -17,18 +17,13 @@ pub enum StatementKind {
|
|||
value: Expression,
|
||||
uid: UID,
|
||||
},
|
||||
If {
|
||||
predicate: Expression,
|
||||
block: Vec<Statement>,
|
||||
else_: Option<Box<Statement>>,
|
||||
},
|
||||
While {
|
||||
predicate: Expression,
|
||||
block: Vec<Statement>,
|
||||
},
|
||||
Print(Expression),
|
||||
Expression(Expression),
|
||||
Block(Vec<Statement>),
|
||||
Remainder(Expression),
|
||||
Return(Option<Expression>),
|
||||
Error(Diagnostic),
|
||||
}
|
||||
|
@ -83,7 +78,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
kind: s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
type_actual: Type::Ambiguous,
|
||||
type_uid: "".into(),
|
||||
value,
|
||||
mutable,
|
||||
uid: "".into(),
|
||||
|
@ -111,10 +106,6 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
},
|
||||
}
|
||||
},
|
||||
// If
|
||||
(Token(t::If, _), _) => {
|
||||
return self.if_else();
|
||||
},
|
||||
// While
|
||||
(Token(t::While, span2), _) => {
|
||||
self.skip(1);
|
||||
|
@ -149,19 +140,6 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
kind: s::Print(expr),
|
||||
}
|
||||
},
|
||||
// Block
|
||||
(Token(t::LeftBrace, span2), _) => {
|
||||
// Skip check for semicolon
|
||||
span = span + span2;
|
||||
let (block, span2) = self
|
||||
.block()
|
||||
.trace_span(span, "while parsing block statement")?;
|
||||
span = span + span2;
|
||||
return Ok(Statement {
|
||||
kind: s::Block(block),
|
||||
span,
|
||||
});
|
||||
},
|
||||
// Return
|
||||
(Token(t::Return, span2), _) => {
|
||||
span = span + span2;
|
||||
|
@ -182,9 +160,26 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
.expression(0)
|
||||
.trace_span(span, "while parsing expression statement")?;
|
||||
span = span + expr.span;
|
||||
Statement {
|
||||
if self.look(0, t::RightBrace).is_ok() {
|
||||
return Ok(Statement {
|
||||
span,
|
||||
kind: s::Remainder(expr),
|
||||
});
|
||||
} else {
|
||||
use ExpressionKind as e;
|
||||
// Optional semicolon for some expressions
|
||||
match expr.kind {
|
||||
e::Block(..) | e::If { .. } => {
|
||||
return Ok(Statement {
|
||||
span,
|
||||
kind: s::Expression(expr),
|
||||
});
|
||||
},
|
||||
_ => Statement {
|
||||
span,
|
||||
kind: s::Expression(expr),
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -195,37 +190,4 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
error().reason("Expected ;").span(&span)
|
||||
}
|
||||
}
|
||||
|
||||
fn if_else(&mut self) -> Result<Statement> {
|
||||
use TokenKind as t;
|
||||
if let Ok(Token(_, span)) = self.eat(t::If) {
|
||||
let predicate = self
|
||||
.expression(0)
|
||||
.trace_span(span, "in predicate of 'if' statement")?;
|
||||
let span = span + predicate.span;
|
||||
let block = self
|
||||
.block()
|
||||
.trace_span(span, "in block of 'if' statement")?;
|
||||
let (block, span) = (block.0, span + block.1);
|
||||
let else_ = if self.eat(t::Else).is_ok() {
|
||||
Some(Box::new(self.if_else()?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Statement {
|
||||
kind: StatementKind::If {
|
||||
predicate,
|
||||
block,
|
||||
else_,
|
||||
},
|
||||
span,
|
||||
})
|
||||
} else {
|
||||
let (block, span) = self.block()?;
|
||||
Ok(Statement {
|
||||
kind: StatementKind::Block(block),
|
||||
span,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use crate::{Statement, semantic::SymbolTable};
|
||||
use crate::{Expression, ExpressionKind, Statement, StatementKind};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct Analyzer {
|
||||
pub table: SymbolTable,
|
||||
table: SymbolTable,
|
||||
}
|
||||
|
||||
impl Analyzer {
|
||||
|
@ -11,25 +13,79 @@ impl Analyzer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn typecheck(
|
||||
&mut self,
|
||||
mut statements: Vec<Statement>,
|
||||
) -> Vec<Statement> {
|
||||
for s in &mut statements {
|
||||
*s = *self.naming_pass_stmt(s.clone().into()).unwrap();
|
||||
pub fn typecheck() {
|
||||
todo!()
|
||||
}
|
||||
for s in &mut statements {
|
||||
*s = *self.bottom_up_stmt(s.clone().into()).unwrap();
|
||||
}
|
||||
for s in &mut statements {
|
||||
*s = *self.top_down_stmt(s.clone().into()).unwrap();
|
||||
}
|
||||
println!("-----TABLE------");
|
||||
println!("{:#?}", self.table.table);
|
||||
println!("-----FUNCS------");
|
||||
println!("{:#?}", self.table.functions);
|
||||
println!("-----STRUCTS----");
|
||||
println!("{:#?}", self.table.structs);
|
||||
statements
|
||||
|
||||
/// Analyzing a block:
|
||||
/// 1. Name structs
|
||||
/// 2. Type structs
|
||||
/// 3. Name and type functions
|
||||
/// 4. Name variables (recurse on blocks) (track moves)
|
||||
/// 5. Type variables
|
||||
/// 6. Type assert variables
|
||||
pub fn block(&mut self, mut block: Vec<Statement>) -> Result<Vec<Statement>> {
|
||||
// 1. Name structs
|
||||
for s in &mut block {
|
||||
if let StatementKind::Declaration {
|
||||
name,
|
||||
value:
|
||||
Expression {
|
||||
kind: ExpressionKind::StructDef(params, _),
|
||||
type_,
|
||||
..
|
||||
},
|
||||
..
|
||||
} = &mut s.kind
|
||||
{
|
||||
*type_ = Type::Nothing;
|
||||
self.table.declare_struct(name, s.span)?;
|
||||
}
|
||||
}
|
||||
// 2. Type structs
|
||||
for s in &mut block {
|
||||
if let StatementKind::Declaration {
|
||||
name,
|
||||
value:
|
||||
Expression {
|
||||
kind: ExpressionKind::StructDef(params, _),
|
||||
..
|
||||
},
|
||||
..
|
||||
} = &mut s.kind
|
||||
{
|
||||
self.table.declare_struct(name, s.span)?;
|
||||
}
|
||||
}
|
||||
// 3. Name and type functions
|
||||
for s in &mut block {
|
||||
if let StatementKind::Declaration {
|
||||
name,
|
||||
value:
|
||||
Expression {
|
||||
kind:
|
||||
ExpressionKind::FunctionDef {
|
||||
params,
|
||||
returns_str,
|
||||
returns_actual,
|
||||
body,
|
||||
uid,
|
||||
},
|
||||
type_,
|
||||
span,
|
||||
},
|
||||
..
|
||||
} = &mut s.kind
|
||||
{
|
||||
let uid = self.table.define_function(
|
||||
name,
|
||||
params.clone(),
|
||||
returns_str.as_ref().map(|s| s.as_str()),
|
||||
*span,
|
||||
)?;
|
||||
*type_ = Type::Function(uid);
|
||||
}
|
||||
}
|
||||
Ok(block)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,297 +0,0 @@
|
|||
use super::Analyzer;
|
||||
use crate::{Expression, ExpressionKind, Statement, StatementKind, err::*};
|
||||
|
||||
use super::Type;
|
||||
|
||||
impl Analyzer {
|
||||
pub fn bottom_up_stmt(
|
||||
&mut self,
|
||||
mut stmt: Box<Statement>,
|
||||
) -> Result<Box<Statement>> {
|
||||
use StatementKind as s;
|
||||
stmt.kind = match stmt.kind {
|
||||
s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
mut type_actual,
|
||||
mut value,
|
||||
mutable,
|
||||
uid,
|
||||
} => {
|
||||
value = *self.bottom_up_expr(value.into())?;
|
||||
if let Some(ref uid) = type_str {
|
||||
type_actual =
|
||||
self.table.resolve_type(uid)?.is_alias().span(&stmt.span)?;
|
||||
}
|
||||
value.type_ = value
|
||||
.type_
|
||||
.deduce(&type_actual)
|
||||
.span(&value.span)?
|
||||
.promote();
|
||||
type_actual = value.type_.clone();
|
||||
if let Type::Ambiguous = type_actual {
|
||||
return error()
|
||||
.reason(format!("Cannot deduce type of {name}"))
|
||||
.span(&stmt.span);
|
||||
}
|
||||
self.table.modify_ident(
|
||||
uid.clone(),
|
||||
None,
|
||||
Some(type_actual.clone()),
|
||||
None,
|
||||
true,
|
||||
self.table.in_global_scope(),
|
||||
)?;
|
||||
s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
type_actual,
|
||||
value,
|
||||
mutable,
|
||||
uid,
|
||||
}
|
||||
},
|
||||
s::Assignment {
|
||||
name,
|
||||
mut value,
|
||||
uid,
|
||||
} => {
|
||||
value = *self.bottom_up_expr(value.into())?;
|
||||
self.table.modify_ident(
|
||||
uid.clone(),
|
||||
None,
|
||||
Some(value.type_.clone()),
|
||||
Some(true),
|
||||
true,
|
||||
false,
|
||||
)?;
|
||||
s::Assignment { name, value, uid }
|
||||
},
|
||||
s::If {
|
||||
mut predicate,
|
||||
mut block,
|
||||
mut else_,
|
||||
} => {
|
||||
predicate = *self.bottom_up_expr(predicate.into())?;
|
||||
self.table.start_block();
|
||||
for s in &mut block {
|
||||
*s = *self.bottom_up_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_block();
|
||||
else_ = if let Some(else_) = else_ {
|
||||
Some(self.bottom_up_stmt(else_)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
s::If {
|
||||
predicate,
|
||||
block,
|
||||
else_,
|
||||
}
|
||||
},
|
||||
s::While {
|
||||
mut predicate,
|
||||
mut block,
|
||||
} => {
|
||||
predicate = *self.bottom_up_expr(predicate.into())?;
|
||||
self.table.start_block();
|
||||
for s in &mut block {
|
||||
*s = *self.bottom_up_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_block();
|
||||
s::While { predicate, block }
|
||||
},
|
||||
s::Print(mut expression) => {
|
||||
expression = *self.bottom_up_expr(expression.into())?;
|
||||
s::Print(expression)
|
||||
},
|
||||
s::Expression(mut expression) => {
|
||||
expression = *self.bottom_up_expr(expression.into())?;
|
||||
s::Expression(expression)
|
||||
},
|
||||
s::Block(mut block) => {
|
||||
for s in &mut block {
|
||||
*s = *self.bottom_up_stmt(s.clone().into())?;
|
||||
}
|
||||
s::Block(block)
|
||||
},
|
||||
s::Return(mut expression) => {
|
||||
expression = if let Some(e) = expression {
|
||||
Some(*self.bottom_up_expr(e.into())?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
s::Return(expression)
|
||||
},
|
||||
s::Error(diagnostic) => s::Error(diagnostic),
|
||||
};
|
||||
Ok(stmt)
|
||||
}
|
||||
|
||||
// Bottom up type inference
|
||||
pub fn bottom_up_expr(
|
||||
&mut self,
|
||||
mut expr: Box<Expression>,
|
||||
) -> Result<Box<Expression>> {
|
||||
use ExpressionKind as e;
|
||||
expr.kind = match expr.kind {
|
||||
// Type assigned in naming step
|
||||
e::Immediate(immediate) => e::Immediate(immediate),
|
||||
e::Identifier(name, mangle) => {
|
||||
expr.type_ = self
|
||||
.table
|
||||
.resolve_type(&mangle)
|
||||
.trace_span(expr.span, format!("While resolving type of '{name}'"))?;
|
||||
e::Identifier(name, mangle)
|
||||
},
|
||||
e::Binary {
|
||||
op,
|
||||
mut left,
|
||||
mut right,
|
||||
} => {
|
||||
left = self.bottom_up_expr(left)?;
|
||||
right = self.bottom_up_expr(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.bottom_up_expr(child)?;
|
||||
expr.type_ = Type::unary_op(op, &child.type_)?;
|
||||
e::Unary { op, child }
|
||||
},
|
||||
e::Parenthesis(mut expression) => {
|
||||
expression = self.bottom_up_expr(expression)?;
|
||||
expr.type_ = expression.type_.clone();
|
||||
e::Parenthesis(expression)
|
||||
},
|
||||
e::FunctionDef {
|
||||
mut params,
|
||||
returns_str,
|
||||
mut returns_actual,
|
||||
mut body,
|
||||
id,
|
||||
} => {
|
||||
let funcdef = self.table.functions.get(&id).unwrap().clone();
|
||||
self.table.start_function();
|
||||
for (uid, param) in funcdef.params.iter().zip(params.iter_mut()) {
|
||||
let type_ = self.table.resolve_type(uid).span(&expr.span)?;
|
||||
param.name = uid.clone();
|
||||
param.type_actual = type_;
|
||||
}
|
||||
for s in &mut body {
|
||||
*s = *self.bottom_up_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_function();
|
||||
returns_actual = self
|
||||
.table
|
||||
.resolve_type(&funcdef.returns)
|
||||
.span(&expr.span)?
|
||||
.is_alias()
|
||||
.span(&expr.span)?;
|
||||
e::FunctionDef {
|
||||
params,
|
||||
returns_str,
|
||||
returns_actual,
|
||||
body,
|
||||
id,
|
||||
}
|
||||
},
|
||||
e::FunctionCall {
|
||||
mut callee,
|
||||
mut args,
|
||||
is_reference,
|
||||
mut id,
|
||||
} => {
|
||||
callee = self.bottom_up_expr(callee)?;
|
||||
match callee.type_ {
|
||||
Type::Function(ref uid) => {
|
||||
id = uid.clone();
|
||||
expr.type_ = self
|
||||
.table
|
||||
.resolve_type(&self.table.functions.get(uid).unwrap().returns)
|
||||
.span(&callee.span)?
|
||||
.is_alias()
|
||||
.span(&expr.span)?
|
||||
},
|
||||
_ => {
|
||||
return error()
|
||||
.reason(format!("Cannot call type {}", callee.type_))
|
||||
.span(&expr.span);
|
||||
},
|
||||
}
|
||||
for a in &mut args {
|
||||
*a = *self.bottom_up_expr(a.clone().into())?;
|
||||
}
|
||||
e::FunctionCall {
|
||||
callee,
|
||||
args,
|
||||
is_reference,
|
||||
id,
|
||||
}
|
||||
},
|
||||
e::StructDef(mut params, sid) => {
|
||||
let struct_ = &self.table.structs[sid].0;
|
||||
for ((_, uid), p) in struct_.iter().zip(params.iter_mut()) {
|
||||
p.type_actual = self
|
||||
.table
|
||||
.resolve_type(uid)
|
||||
.trace_span(expr.span, "For struct field")?;
|
||||
}
|
||||
e::StructDef(params, sid)
|
||||
},
|
||||
e::StructLiteral { name, mut args, id } => {
|
||||
expr.type_ = self
|
||||
.table
|
||||
.resolve_type(&id)
|
||||
.span(&expr.span)?
|
||||
.is_alias()
|
||||
.span(&expr.span)?;
|
||||
for (_name, arg) in &mut args {
|
||||
*arg = *self.bottom_up_expr(arg.clone().into())?;
|
||||
}
|
||||
e::StructLiteral { name, args, id }
|
||||
},
|
||||
e::Field {
|
||||
mut namespace,
|
||||
field,
|
||||
mut uid,
|
||||
} => {
|
||||
namespace = self.bottom_up_expr(namespace)?;
|
||||
let field_name = if let Type::Struct(s) = namespace.type_ {
|
||||
if let e::Identifier(name, _) = &field.kind {
|
||||
expr.type_ = self
|
||||
.table
|
||||
.get_field(s, name)
|
||||
.span(&expr.span)?
|
||||
.is_alias()
|
||||
.reason("Expected type, found value")
|
||||
.span(&expr.span)?;
|
||||
name.clone()
|
||||
} else {
|
||||
return error().reason("Field must be identifier").span(&expr.span);
|
||||
}
|
||||
} else {
|
||||
return error()
|
||||
.reason(format!(
|
||||
"Type '{}' does not contain fields",
|
||||
namespace.type_
|
||||
))
|
||||
.span(&expr.span);
|
||||
};
|
||||
// Name mangling
|
||||
if let e::Identifier(_, mangle) = &namespace.kind {
|
||||
uid = mangle.clone() + "$" + &field_name;
|
||||
} else if uid != "" {
|
||||
uid = uid + "$" + &field_name;
|
||||
}
|
||||
e::Field {
|
||||
namespace,
|
||||
field,
|
||||
uid,
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(expr)
|
||||
}
|
||||
}
|
|
@ -1,369 +1,357 @@
|
|||
mod analyzer;
|
||||
mod bottom_up;
|
||||
mod naming;
|
||||
mod primitives;
|
||||
mod sizing;
|
||||
mod top_down;
|
||||
mod types;
|
||||
pub mod analyzer;
|
||||
pub mod primitives;
|
||||
|
||||
pub use analyzer::*;
|
||||
pub use primitives::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{Parameter, err::*};
|
||||
pub use analyzer::*;
|
||||
pub use primitives::*;
|
||||
pub use types::*;
|
||||
use crate::{Parameters, Span, err::*, semantic::Primitive};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum Scope {
|
||||
Block,
|
||||
Function,
|
||||
pub type UID = String;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Type {
|
||||
Ambiguous,
|
||||
Prim(Primitive),
|
||||
Nothing,
|
||||
Struct(UID),
|
||||
Function(UID),
|
||||
}
|
||||
|
||||
// Mangled name
|
||||
pub type UID = String;
|
||||
impl std::fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Type::Ambiguous => write!(f, "ambiguous"),
|
||||
Type::Prim(primitive) => write!(f, "{primitive}"),
|
||||
Type::Nothing => write!(f, "nothing"),
|
||||
Type::Struct(s) => write!(f, "struct {s}"),
|
||||
Type::Function(func) => write!(f, "func {func}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SymbolKind {
|
||||
Variable {
|
||||
type_: UID,
|
||||
mutable: bool,
|
||||
global: bool,
|
||||
children: Vec<UID>,
|
||||
},
|
||||
Function {
|
||||
params: Parameters,
|
||||
returns: UID,
|
||||
},
|
||||
Struct {
|
||||
params: Parameters,
|
||||
size: usize,
|
||||
align: usize,
|
||||
},
|
||||
TypeDef {
|
||||
actual: Type,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Symbol {
|
||||
pub name: String,
|
||||
pub type_: Type,
|
||||
pub uid: UID,
|
||||
initialized: bool,
|
||||
pub mutable: Option<bool>,
|
||||
pub global: bool,
|
||||
pub span: Option<Span>,
|
||||
pub kind: SymbolKind,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn is_variable(&self) -> Result<()> {
|
||||
let name = &self.name;
|
||||
match &self.kind {
|
||||
SymbolKind::Variable { .. } => Ok(()),
|
||||
SymbolKind::Function { .. } => {
|
||||
error().reason(format!("'{name}' refers to a function, not a value"))
|
||||
},
|
||||
_ => error().reason(format!("'{name}' refers to a type, not a value")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_type(&self) -> Result<Type> {
|
||||
let name = &self.name;
|
||||
match &self.kind {
|
||||
SymbolKind::Struct { .. } => Ok(Type::Struct(self.uid.clone())),
|
||||
SymbolKind::TypeDef { actual } => Ok(actual.clone()),
|
||||
_ => error().reason(format!("'{name}' refers to a value, not a type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Definition {
|
||||
Ident(Symbol),
|
||||
FuncStart,
|
||||
BlockStart,
|
||||
pub enum Event {
|
||||
Declared { name: String, uid: UID },
|
||||
Moved { name: String, uid: UID, span: Span },
|
||||
Func { returns: Vec<UID>, uid: UID },
|
||||
Block { returns: UID },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SymbolTable {
|
||||
pub structs: Vec<StructureDef>,
|
||||
pub functions: HashMap<UID, FunctionDef>,
|
||||
scope: Vec<Definition>,
|
||||
pub table: HashMap<UID, Symbol>,
|
||||
mangle_num: usize,
|
||||
syms: HashMap<UID, Symbol>,
|
||||
scope: Vec<Event>,
|
||||
nesting: usize,
|
||||
mangle_num: usize,
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
pub fn new() -> Self {
|
||||
// Setup builtin symbols
|
||||
let prims = primitive_symbols();
|
||||
let mut scope = vec![];
|
||||
let mut table = HashMap::new();
|
||||
for p in prims {
|
||||
scope.push(Definition::Ident(p.clone()));
|
||||
table.insert(p.uid.clone(), p);
|
||||
}
|
||||
let nothing_symbol = Symbol {
|
||||
name: "Nothing".to_string(),
|
||||
type_: Type::Alias(Box::new(Type::Nothing)),
|
||||
uid: nothing_mangle(),
|
||||
initialized: true,
|
||||
mutable: Some(false),
|
||||
global: true,
|
||||
};
|
||||
let mut functions = HashMap::new();
|
||||
functions.insert("$$main".into(), FunctionDef {
|
||||
params: vec![],
|
||||
returns: "Nothing".into(),
|
||||
});
|
||||
scope.push(Definition::Ident(nothing_symbol.clone()));
|
||||
table.insert(nothing_symbol.uid.clone(), nothing_symbol);
|
||||
Self {
|
||||
structs: vec![],
|
||||
functions,
|
||||
scope,
|
||||
table,
|
||||
mangle_num: 0,
|
||||
syms: HashMap::new(),
|
||||
scope: vec![],
|
||||
nesting: 0,
|
||||
mangle_num: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_block(&mut self) {
|
||||
self.scope.push(Definition::BlockStart);
|
||||
}
|
||||
|
||||
pub fn end_block(&mut self) {
|
||||
let mut escaped = vec![];
|
||||
while !self.scope.is_empty() {
|
||||
let top = self.scope.pop();
|
||||
if let Some(Definition::BlockStart) = top {
|
||||
self.scope.append(&mut escaped);
|
||||
return;
|
||||
} else if let Some(Definition::Ident(s)) = top {
|
||||
if !s.initialized {
|
||||
escaped.push(Definition::Ident(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!("Cannot end global scope")
|
||||
}
|
||||
|
||||
pub fn in_global_scope(&self) -> bool {
|
||||
for def in &self.scope {
|
||||
if let Definition::Ident(..) = def {
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn resolve_type(&self, uid: &UID) -> Result<Type> {
|
||||
let symbol = self.table.get(uid).unwrap();
|
||||
if symbol.initialized == false {
|
||||
return error().reason("Symbol was never initialized");
|
||||
} else {
|
||||
Ok(symbol.type_.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_field_no(&self, sid: SID, argname: &str) -> usize {
|
||||
let struct_def = &self.structs[sid].0;
|
||||
for (id, (name, _)) in struct_def.iter().enumerate() {
|
||||
if name == argname {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn start_function(&mut self) {
|
||||
self.nesting += 1;
|
||||
self.scope.push(Definition::FuncStart);
|
||||
}
|
||||
|
||||
pub fn end_function(&mut self) {
|
||||
self.nesting -= 1;
|
||||
let mut escaped = vec![];
|
||||
while !self.scope.is_empty() {
|
||||
let top = self.scope.pop();
|
||||
if let Some(Definition::FuncStart) = top {
|
||||
self.scope.append(&mut escaped);
|
||||
return;
|
||||
} else if let Some(Definition::Ident(s)) = top {
|
||||
if !s.initialized {
|
||||
escaped.push(Definition::Ident(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!("Cannot end global scope")
|
||||
}
|
||||
|
||||
fn generate_uid(&mut self, name: &str) -> UID {
|
||||
let uid = format!("${}${name}", self.mangle_num);
|
||||
self.mangle_num += 1;
|
||||
uid
|
||||
}
|
||||
|
||||
pub fn get_field(&self, struct_id: SID, field_name: &str) -> Result<Type> {
|
||||
let struct_def = &self.structs[struct_id].0;
|
||||
for (name, sid) in struct_def {
|
||||
if name == field_name {
|
||||
return self.resolve_type(sid);
|
||||
}
|
||||
}
|
||||
error().reason(format!("Cannot find field '{field_name}'"))
|
||||
pub fn get(&self, uid: &UID) -> &Symbol {
|
||||
self.syms.get(uid).unwrap()
|
||||
}
|
||||
|
||||
pub fn create_struct(&mut self, params: Vec<Parameter>) -> SID {
|
||||
let sid = self.structs.len();
|
||||
let mut new_params = vec![];
|
||||
for p in params {
|
||||
let s = self.reference_ident(&p.type_str);
|
||||
new_params.push((p.name, s.uid));
|
||||
}
|
||||
self.structs.push(StructureDef(new_params));
|
||||
sid
|
||||
pub fn get_mut(&mut self, uid: &UID) -> &mut Symbol {
|
||||
self.syms.get_mut(uid).unwrap()
|
||||
}
|
||||
|
||||
pub fn create_function(
|
||||
&mut self,
|
||||
params: Vec<Parameter>,
|
||||
returns: Option<String>,
|
||||
) -> Result<UID> {
|
||||
let uid = self.generate_uid("func");
|
||||
let mut symbols = vec![];
|
||||
for p in ¶ms {
|
||||
let symbol = self.reference_ident(&p.type_str);
|
||||
symbols.push(symbol);
|
||||
// Find the definition of a symbol in local and global scope
|
||||
pub fn find(&self, name: &str) -> Result<&Symbol> {
|
||||
let mut nesting = self.nesting;
|
||||
for e in self.scope.iter().rev() {
|
||||
match e {
|
||||
Event::Declared { uid, .. }
|
||||
if nesting == self.nesting || nesting == 0 =>
|
||||
{
|
||||
return Ok(self.get(uid));
|
||||
},
|
||||
Event::Moved { name, span, .. }
|
||||
if nesting == self.nesting || nesting == 0 =>
|
||||
{
|
||||
return error()
|
||||
.reason(format!("Symbol '{name}' moved out of scope here"))
|
||||
.span(&span);
|
||||
},
|
||||
Event::Func { .. } => {
|
||||
nesting -= 1;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
error().reason(format!("Cannot find symbol '{name}'"))
|
||||
}
|
||||
|
||||
// Get all nested members of a struct variable
|
||||
fn get_all_children(&self, uid: &UID) -> Vec<UID> {
|
||||
use SymbolKind as s;
|
||||
match &self.get(uid).kind {
|
||||
s::Variable { children, .. } => {
|
||||
let mut new_children = children.clone();
|
||||
for uid in children {
|
||||
new_children.append(&mut self.get_all_children(uid))
|
||||
}
|
||||
new_children
|
||||
},
|
||||
_ => {
|
||||
vec![]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Move a symbol out of scope
|
||||
pub fn move_symbol(&mut self, move_uid: &UID, span: Span) -> Result<()> {
|
||||
if let SymbolKind::Variable { global, .. } = self.get(move_uid).kind {
|
||||
if global {
|
||||
return error().reason("Cannot move global symbol").span(&span);
|
||||
}
|
||||
let returns = if let Some(s) = returns {
|
||||
self.reference_ident(&s).uid
|
||||
} else {
|
||||
nothing_mangle()
|
||||
};
|
||||
self.start_function();
|
||||
let mut new_params = vec![];
|
||||
for (p, s) in params.iter().zip(symbols.iter()) {
|
||||
let s = self
|
||||
.define_ident(&p.name, s.type_.is_alias()?, false)
|
||||
.trace("While initializing function parameters")?;
|
||||
new_params.push(s.uid);
|
||||
panic!("Moved non-variable {move_uid}");
|
||||
}
|
||||
self.functions.insert(uid.clone(), FunctionDef {
|
||||
params: new_params,
|
||||
returns,
|
||||
let children = self.get_all_children(move_uid);
|
||||
for e in self.scope.iter().rev() {
|
||||
match e {
|
||||
Event::Declared { uid, .. } => {
|
||||
if move_uid == uid {
|
||||
break;
|
||||
}
|
||||
},
|
||||
Event::Moved { uid, span, .. } => {
|
||||
if children.contains(uid) {
|
||||
return error()
|
||||
.reason("Symbol was partially moved here")
|
||||
.span(&span);
|
||||
} else if move_uid == uid {
|
||||
return error()
|
||||
.reason("Symbol was previously moved here")
|
||||
.span(&span);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
self.scope.push(Event::Moved {
|
||||
name: self.get(move_uid).name.clone(),
|
||||
uid: move_uid.clone(),
|
||||
span,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn in_global_scope(&self) -> bool {
|
||||
for e in self.scope.iter().rev() {
|
||||
if let Event::Func { .. } = e {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn define_var(
|
||||
&mut self,
|
||||
name: &str,
|
||||
mutable: bool,
|
||||
type_: &UID,
|
||||
span: Span,
|
||||
) -> UID {
|
||||
let uid = self.generate_uid(name);
|
||||
self.syms.insert(uid.clone(), Symbol {
|
||||
name: name.to_string(),
|
||||
span: Some(span),
|
||||
uid: uid.clone(),
|
||||
kind: SymbolKind::Variable {
|
||||
type_: type_.clone(),
|
||||
mutable,
|
||||
global: self.in_global_scope(),
|
||||
children: vec![],
|
||||
},
|
||||
});
|
||||
self.scope.push(Event::Declared {
|
||||
name: name.to_string(),
|
||||
uid: uid.clone(),
|
||||
});
|
||||
uid
|
||||
}
|
||||
|
||||
pub fn declare_struct(
|
||||
&mut self,
|
||||
struct_name: &str,
|
||||
span: Span,
|
||||
) -> Result<UID> {
|
||||
// Check for multiple definition
|
||||
for e in self.scope.iter().rev() {
|
||||
match e {
|
||||
Event::Declared { name, uid } => {
|
||||
if name == struct_name {
|
||||
let e = error().reason(format!(
|
||||
"Structure '{struct_name}' is defined multiple times"
|
||||
));
|
||||
if let Some(s) = &self.get(uid).span {
|
||||
return e.span(s);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
},
|
||||
Event::Moved { .. } => {},
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
let uid = self.generate_uid(struct_name);
|
||||
self.syms.insert(uid.clone(), Symbol {
|
||||
name: struct_name.to_string(),
|
||||
uid: uid.clone(),
|
||||
span: Some(span),
|
||||
kind: SymbolKind::Struct {
|
||||
params: Parameters::default(),
|
||||
size: 0,
|
||||
align: 0,
|
||||
},
|
||||
});
|
||||
self.scope.push(Event::Declared {
|
||||
name: struct_name.to_string(),
|
||||
uid: uid.clone(),
|
||||
});
|
||||
Ok(uid)
|
||||
}
|
||||
|
||||
pub fn modify_ident(
|
||||
&mut self,
|
||||
uid: UID,
|
||||
name: Option<String>,
|
||||
type_: Option<Type>,
|
||||
mutable: Option<bool>,
|
||||
init: bool,
|
||||
global: bool,
|
||||
) -> Result<()> {
|
||||
let symbol = self.table.get_mut(&uid).unwrap();
|
||||
if let Some(ref type_) = type_ {
|
||||
symbol.type_ = symbol.type_.clone().deduce(type_)?;
|
||||
fn type_params(&mut self, mut params: Parameters) -> Result<Parameters> {
|
||||
for i in 0..params.arity {
|
||||
params.types[i] = self
|
||||
.find(¶ms.type_names[i])
|
||||
.trace(format!(
|
||||
"while resolving type of field '{}'",
|
||||
params.type_names[i]
|
||||
))?
|
||||
.uid
|
||||
.clone();
|
||||
}
|
||||
Ok(params)
|
||||
}
|
||||
symbol.mutable = match (symbol.mutable, mutable) {
|
||||
(Some(true), Some(false)) | (Some(false), Some(true)) => {
|
||||
return error().reason("Cannot mutate immutable symbol");
|
||||
},
|
||||
_ => mutable,
|
||||
};
|
||||
symbol.initialized |= init;
|
||||
symbol.global |= global;
|
||||
|
||||
let mut nesting = self.nesting;
|
||||
for def in &mut self.scope {
|
||||
match def {
|
||||
Definition::Ident(symbol)
|
||||
if (symbol.uid == uid)
|
||||
&& (nesting == 0 || nesting == self.nesting) =>
|
||||
{
|
||||
if let Some(ref name) = name {
|
||||
symbol.name = name.clone();
|
||||
pub fn define_function(
|
||||
&mut self,
|
||||
func_name: &str,
|
||||
params: Parameters,
|
||||
returns_str: Option<&str>,
|
||||
span: Span,
|
||||
) -> Result<UID> {
|
||||
// Check for multiple definition
|
||||
for e in self.scope.iter().rev() {
|
||||
match e {
|
||||
Event::Declared { name, uid } => {
|
||||
if name == func_name {
|
||||
let e = error().reason(format!(
|
||||
"Function '{func_name}' is defined multiple times"
|
||||
));
|
||||
if let Some(s) = &self.get(uid).span {
|
||||
return e.span(s);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
if let Some(ref type_) = type_ {
|
||||
symbol.type_ = symbol.type_.clone().deduce(type_)?;
|
||||
}
|
||||
symbol.mutable = match (symbol.mutable, mutable) {
|
||||
(Some(true), Some(false)) | (Some(false), Some(true)) => {
|
||||
return error().reason("Cannot mutate immutable symbol");
|
||||
},
|
||||
_ => mutable,
|
||||
Event::Moved { .. } => {},
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
let uid = self.generate_uid(func_name);
|
||||
// Check types
|
||||
let params = self.type_params(params)?;
|
||||
let returns = {
|
||||
let sym = self.find(returns_str)?;
|
||||
sym.is_type()?;
|
||||
sym.uid.clone()
|
||||
};
|
||||
symbol.initialized |= init;
|
||||
symbol.global |= global;
|
||||
return Ok(());
|
||||
},
|
||||
Definition::FuncStart => nesting -= 1,
|
||||
_ => {},
|
||||
self.syms.insert(uid.clone(), Symbol {
|
||||
name: func_name.to_string(),
|
||||
uid: uid.clone(),
|
||||
span: Some(span),
|
||||
kind: SymbolKind::Function { params, returns },
|
||||
});
|
||||
self.scope.push(Event::Declared {
|
||||
name: func_name.to_string(),
|
||||
uid: uid.clone(),
|
||||
});
|
||||
Ok(uid)
|
||||
}
|
||||
|
||||
pub fn define_struct(&mut self, uid: &UID, params: Parameters) -> Result<()> {
|
||||
let params = self.type_params(params)?;
|
||||
if let SymbolKind::Struct {
|
||||
params: old_params, ..
|
||||
} = &mut self.get_mut(uid).kind
|
||||
{
|
||||
*old_params = params;
|
||||
} else {
|
||||
unreachable!("Defined non-existent struct")
|
||||
}
|
||||
Ok(())
|
||||
//unreachable!("Symbol {uid} does not exist")
|
||||
}
|
||||
|
||||
pub fn define_ident(
|
||||
&mut self,
|
||||
name: &str,
|
||||
type_: Type,
|
||||
mutable: bool,
|
||||
) -> Result<Symbol> {
|
||||
if let Type::Alias(_) | Type::Function(_) = &type_ {
|
||||
if mutable {
|
||||
return error()
|
||||
.reason("Struct and function definitions cannot be mutable");
|
||||
}
|
||||
}
|
||||
if let Ok(s) = self.lookup_block_scope(name) {
|
||||
// Re-definition error
|
||||
// TODO support name shadowing
|
||||
if s.initialized {
|
||||
return error()
|
||||
.reason(format!("Multiple definitions of '{name}' in this scope"));
|
||||
}
|
||||
// Referenced before init, modify existing entry
|
||||
else {
|
||||
self.modify_ident(
|
||||
s.uid.clone(),
|
||||
None,
|
||||
Some(type_),
|
||||
Some(mutable),
|
||||
true,
|
||||
self.in_global_scope(),
|
||||
)?;
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
// First initialization in this scope
|
||||
let uid = self.generate_uid(name);
|
||||
let sym = Symbol {
|
||||
name: name.into(),
|
||||
type_,
|
||||
uid: uid.clone(),
|
||||
initialized: true,
|
||||
mutable: Some(mutable),
|
||||
global: self.in_global_scope(),
|
||||
};
|
||||
self.scope.push(Definition::Ident(sym.clone()));
|
||||
self.table.insert(uid, sym.clone());
|
||||
Ok(sym)
|
||||
}
|
||||
|
||||
pub fn reference_ident(&mut self, name: &str) -> Symbol {
|
||||
match self.lookup_function_scope(name) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
let uid = self.generate_uid(name);
|
||||
let sym = Symbol {
|
||||
name: name.into(),
|
||||
type_: Type::Ambiguous,
|
||||
uid: uid.clone(),
|
||||
initialized: false,
|
||||
mutable: None,
|
||||
global: false,
|
||||
};
|
||||
self.scope.push(Definition::Ident(sym.clone()));
|
||||
self.table.insert(uid, sym.clone());
|
||||
sym
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_block_scope(&self, name: &str) -> Result<Symbol> {
|
||||
self.lookup_scope(name, Scope::Block)
|
||||
}
|
||||
|
||||
pub fn lookup_function_scope(&self, name: &str) -> Result<Symbol> {
|
||||
self.lookup_scope(name, Scope::Function)
|
||||
}
|
||||
|
||||
fn lookup_scope(&self, name: &str, scope: Scope) -> Result<Symbol> {
|
||||
let mut nesting = self.nesting;
|
||||
for def in self.scope.iter().rev() {
|
||||
match def {
|
||||
Definition::Ident(symbol)
|
||||
if (symbol.name == name)
|
||||
&& ((nesting == 0) || nesting == self.nesting) =>
|
||||
{
|
||||
return Ok(symbol.clone());
|
||||
},
|
||||
Definition::FuncStart | Definition::BlockStart
|
||||
if scope == Scope::Block =>
|
||||
{
|
||||
break;
|
||||
},
|
||||
Definition::FuncStart => nesting -= 1,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
error().reason(format!(
|
||||
"Cannot find the definition of '{}' in the current scope",
|
||||
name
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,234 +0,0 @@
|
|||
use super::Analyzer;
|
||||
use crate::{
|
||||
Expression, ExpressionKind, Immediate, Statement, StatementKind, err::*,
|
||||
semantic::Primitive,
|
||||
};
|
||||
|
||||
use super::Type;
|
||||
|
||||
impl Analyzer {
|
||||
pub fn naming_pass_stmt(
|
||||
&mut self,
|
||||
mut stmt: Box<Statement>,
|
||||
) -> Result<Box<Statement>> {
|
||||
use StatementKind as s;
|
||||
stmt.kind = match stmt.kind {
|
||||
s::Declaration {
|
||||
name,
|
||||
mut type_str,
|
||||
type_actual,
|
||||
mut value,
|
||||
mutable,
|
||||
mut uid,
|
||||
} => {
|
||||
value = *self.naming_pass_expr(value.into())?;
|
||||
let type_ = if let Some(ref s) = type_str {
|
||||
let s = self.table.reference_ident(s);
|
||||
type_str = Some(s.uid.clone());
|
||||
s.type_.is_alias().unwrap_or(Type::Ambiguous)
|
||||
} else {
|
||||
value.type_.clone()
|
||||
};
|
||||
let symbol = self.table.define_ident(&name, type_, mutable)?;
|
||||
uid = symbol.uid;
|
||||
s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
type_actual,
|
||||
value,
|
||||
mutable,
|
||||
uid,
|
||||
}
|
||||
},
|
||||
s::Assignment {
|
||||
name,
|
||||
mut value,
|
||||
mut uid,
|
||||
} => {
|
||||
uid = self.table.reference_ident(&name).uid;
|
||||
value = *self.naming_pass_expr(value.into())?;
|
||||
// Hint ident is rhs type and mutable
|
||||
self.table.modify_ident(
|
||||
uid.clone(),
|
||||
None,
|
||||
Some(value.type_.clone()),
|
||||
Some(true),
|
||||
false,
|
||||
false,
|
||||
)?;
|
||||
s::Assignment { name, value, uid }
|
||||
},
|
||||
s::If {
|
||||
mut predicate,
|
||||
mut block,
|
||||
mut else_,
|
||||
} => {
|
||||
predicate = *self.naming_pass_expr(predicate.into())?;
|
||||
self.table.start_block();
|
||||
for s in &mut block {
|
||||
*s = *self.naming_pass_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_block();
|
||||
else_ = if let Some(else_) = else_ {
|
||||
Some(self.naming_pass_stmt(else_)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
s::If {
|
||||
predicate,
|
||||
block,
|
||||
else_,
|
||||
}
|
||||
},
|
||||
s::While {
|
||||
mut predicate,
|
||||
mut block,
|
||||
} => {
|
||||
predicate = *self.naming_pass_expr(predicate.into())?;
|
||||
self.table.start_block();
|
||||
for s in &mut block {
|
||||
*s = *self.naming_pass_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_block();
|
||||
s::While { predicate, block }
|
||||
},
|
||||
s::Print(mut expression) => {
|
||||
expression = *self.naming_pass_expr(expression.into())?;
|
||||
s::Print(expression)
|
||||
},
|
||||
s::Expression(mut expression) => {
|
||||
expression = *self.naming_pass_expr(expression.into())?;
|
||||
s::Expression(expression)
|
||||
},
|
||||
s::Block(mut block) => {
|
||||
self.table.start_block();
|
||||
for s in &mut block {
|
||||
*s = *self.naming_pass_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_block();
|
||||
s::Block(block)
|
||||
},
|
||||
s::Return(mut expression) => {
|
||||
if let Some(e) = expression {
|
||||
expression = Some(*self.naming_pass_expr(e.into())?);
|
||||
}
|
||||
s::Return(expression)
|
||||
},
|
||||
s::Error(diagnostic) => s::Error(diagnostic),
|
||||
};
|
||||
Ok(stmt)
|
||||
}
|
||||
|
||||
pub fn naming_pass_expr(
|
||||
&mut self,
|
||||
mut expr: Box<Expression>,
|
||||
) -> Result<Box<Expression>> {
|
||||
use ExpressionKind as e;
|
||||
expr.kind = match expr.kind {
|
||||
e::Immediate(immediate) => {
|
||||
expr.type_ = Type::Prim(match &immediate {
|
||||
Immediate::Integer(_, _) => Primitive::integer_ambiguous,
|
||||
Immediate::Real(_) => Primitive::real_ambiguous,
|
||||
Immediate::String(_) => Primitive::string,
|
||||
Immediate::Glyph(_) => Primitive::glyph,
|
||||
Immediate::Boolean(_) => Primitive::boolean,
|
||||
});
|
||||
e::Immediate(immediate)
|
||||
},
|
||||
e::Identifier(name, mut mangle) => {
|
||||
let sym = self.table.reference_ident(&name);
|
||||
mangle = sym.uid;
|
||||
expr.type_ = sym.type_;
|
||||
e::Identifier(name, mangle)
|
||||
},
|
||||
e::Binary {
|
||||
op,
|
||||
mut left,
|
||||
mut right,
|
||||
} => {
|
||||
left = self.naming_pass_expr(left)?;
|
||||
right = self.naming_pass_expr(right)?;
|
||||
e::Binary { op, left, right }
|
||||
},
|
||||
e::Unary { op, mut child } => {
|
||||
child = self.naming_pass_expr(child)?;
|
||||
e::Unary { op, child }
|
||||
},
|
||||
e::Parenthesis(mut expression) => {
|
||||
expression = self.naming_pass_expr(expression)?;
|
||||
e::Parenthesis(expression)
|
||||
},
|
||||
e::FunctionDef {
|
||||
params,
|
||||
returns_str,
|
||||
returns_actual,
|
||||
mut body,
|
||||
mut id,
|
||||
} => {
|
||||
// Starts function
|
||||
id = self
|
||||
.table
|
||||
.create_function(params.clone(), returns_str.clone())
|
||||
.span(&expr.span)?;
|
||||
expr.type_ = Type::Function(id.clone());
|
||||
for s in &mut body {
|
||||
*s = *self.naming_pass_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_function();
|
||||
e::FunctionDef {
|
||||
params,
|
||||
returns_str,
|
||||
returns_actual,
|
||||
body,
|
||||
id,
|
||||
}
|
||||
},
|
||||
e::FunctionCall {
|
||||
mut callee,
|
||||
mut args,
|
||||
is_reference,
|
||||
id,
|
||||
} => {
|
||||
callee = self.naming_pass_expr(callee)?;
|
||||
for a in &mut args {
|
||||
*a = *self.naming_pass_expr(a.clone().into())?;
|
||||
}
|
||||
e::FunctionCall {
|
||||
callee,
|
||||
args,
|
||||
is_reference,
|
||||
id,
|
||||
}
|
||||
},
|
||||
e::StructDef(params, mut sid) => {
|
||||
sid = self.table.create_struct(params.clone());
|
||||
expr.type_ = Type::Alias(Box::new(Type::Struct(sid)));
|
||||
e::StructDef(params, sid)
|
||||
},
|
||||
e::StructLiteral {
|
||||
name,
|
||||
mut args,
|
||||
mut id,
|
||||
} => {
|
||||
id = self.table.reference_ident(&name).uid;
|
||||
for (_, a) in &mut args {
|
||||
*a = *self.naming_pass_expr(a.clone().into())?;
|
||||
}
|
||||
e::StructLiteral { name, args, id }
|
||||
},
|
||||
e::Field {
|
||||
mut namespace,
|
||||
field,
|
||||
uid,
|
||||
} => {
|
||||
namespace = self.naming_pass_expr(namespace)?;
|
||||
e::Field {
|
||||
namespace,
|
||||
field,
|
||||
uid,
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(expr)
|
||||
}
|
||||
}
|
|
@ -40,18 +40,6 @@ macro_rules! primitives {
|
|||
}
|
||||
}
|
||||
}
|
||||
pub fn primitive_symbols() -> Vec<Symbol> {
|
||||
vec![
|
||||
$(Symbol {
|
||||
name: stringify!{$i}.into(),
|
||||
type_: Type::Alias(Box::new(Type::Prim(Primitive::$i))),
|
||||
uid: format!("$${}", stringify!{$i}),
|
||||
initialized: true,
|
||||
mutable: Some(false),
|
||||
global: true,
|
||||
},)*
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
pub enum DataType {
|
||||
i32,
|
||||
i64,
|
||||
f32,
|
||||
f64,
|
||||
}
|
|
@ -1,231 +0,0 @@
|
|||
use super::{Analyzer, Type};
|
||||
use crate::{
|
||||
Expression, ExpressionKind, Statement, StatementKind, err::*,
|
||||
semantic::Primitive,
|
||||
};
|
||||
|
||||
impl Analyzer {
|
||||
pub fn top_down_stmt(
|
||||
&mut self,
|
||||
mut stmt: Box<Statement>,
|
||||
) -> Result<Box<Statement>> {
|
||||
use StatementKind as s;
|
||||
stmt.kind = match stmt.kind {
|
||||
s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
type_actual,
|
||||
mut value,
|
||||
mutable,
|
||||
uid,
|
||||
} => {
|
||||
value = *self.top_down_expr(value.into(), &type_actual)?;
|
||||
s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
type_actual,
|
||||
value,
|
||||
mutable,
|
||||
uid,
|
||||
}
|
||||
},
|
||||
s::Assignment {
|
||||
name,
|
||||
mut value,
|
||||
uid,
|
||||
} => {
|
||||
let type_ = self.table.resolve_type(&uid).span(&stmt.span)?;
|
||||
value = *self.top_down_expr(value.into(), &type_)?;
|
||||
s::Assignment { name, value, uid }
|
||||
},
|
||||
s::If {
|
||||
mut predicate,
|
||||
mut block,
|
||||
mut else_,
|
||||
} => {
|
||||
predicate = *self
|
||||
.top_down_expr(predicate.into(), &Type::Prim(Primitive::boolean))?;
|
||||
for s in &mut block {
|
||||
*s = *self.top_down_stmt(s.clone().into())?;
|
||||
}
|
||||
else_ = if let Some(else_) = else_ {
|
||||
Some(self.top_down_stmt(else_)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
s::If {
|
||||
predicate,
|
||||
block,
|
||||
else_,
|
||||
}
|
||||
},
|
||||
s::While {
|
||||
mut predicate,
|
||||
mut block,
|
||||
} => {
|
||||
predicate = *self
|
||||
.top_down_expr(predicate.into(), &Type::Prim(Primitive::boolean))?;
|
||||
for s in &mut block {
|
||||
*s = *self.top_down_stmt(s.clone().into())?;
|
||||
}
|
||||
s::While { predicate, block }
|
||||
},
|
||||
s::Print(mut expr) => {
|
||||
let expr_t = expr.type_.clone();
|
||||
expr = *self.top_down_expr(expr.into(), &expr_t)?;
|
||||
s::Print(expr)
|
||||
},
|
||||
s::Expression(mut expr) => {
|
||||
let expr_t = expr.type_.clone();
|
||||
expr = *self.top_down_expr(expr.into(), &expr_t)?;
|
||||
s::Expression(expr)
|
||||
},
|
||||
s::Block(mut block) => {
|
||||
for s in &mut block {
|
||||
*s = *self.top_down_stmt(s.clone().into())?;
|
||||
}
|
||||
s::Block(block)
|
||||
},
|
||||
s::Return(expr) => {
|
||||
if let Some(mut expr) = expr {
|
||||
let expr_t = expr.type_.clone();
|
||||
println!("{expr_t}");
|
||||
expr = *self.top_down_expr(expr.into(), &expr_t)?;
|
||||
s::Return(Some(expr))
|
||||
} else {
|
||||
s::Return(None)
|
||||
}
|
||||
},
|
||||
s::Error(diagnostic) => s::Error(diagnostic),
|
||||
};
|
||||
Ok(stmt)
|
||||
}
|
||||
|
||||
pub fn top_down_expr(
|
||||
&mut self,
|
||||
mut expr: Box<Expression>,
|
||||
expect: &Type,
|
||||
) -> Result<Box<Expression>> {
|
||||
use ExpressionKind as e;
|
||||
println!("{} -> {expect}", expr.type_);
|
||||
expr.type_ = expr.type_.coerce(expect).span(&expr.span)?;
|
||||
expr.kind = match expr.kind {
|
||||
e::Immediate(i) => e::Immediate(i),
|
||||
e::Identifier(i, m) => e::Identifier(i, m),
|
||||
e::Binary {
|
||||
op,
|
||||
mut left,
|
||||
mut right,
|
||||
} => {
|
||||
left = self.top_down_expr(left, expect)?;
|
||||
right = self.top_down_expr(right, expect)?;
|
||||
e::Binary { op, left, right }
|
||||
},
|
||||
e::Unary { op, mut child } => {
|
||||
child = self.top_down_expr(child, expect)?;
|
||||
e::Unary { op, child }
|
||||
},
|
||||
e::Parenthesis(mut expression) => {
|
||||
expression = self.top_down_expr(expression, expect)?;
|
||||
e::Parenthesis(expression)
|
||||
},
|
||||
e::FunctionDef {
|
||||
params,
|
||||
returns_str,
|
||||
returns_actual,
|
||||
mut body,
|
||||
id,
|
||||
} => {
|
||||
for s in &mut body {
|
||||
*s = *self.top_down_stmt(s.clone().into())?;
|
||||
}
|
||||
e::FunctionDef {
|
||||
params,
|
||||
returns_str,
|
||||
returns_actual,
|
||||
body,
|
||||
id,
|
||||
}
|
||||
},
|
||||
e::FunctionCall {
|
||||
callee,
|
||||
mut args,
|
||||
is_reference,
|
||||
id,
|
||||
} => {
|
||||
let func_def = if let Type::Function(ref uid) = callee.type_ {
|
||||
self.table.functions.get(uid).unwrap().clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
if func_def.params.len() != args.len() {
|
||||
return error()
|
||||
.reason(format!(
|
||||
"Wrong number of arguments; found {}, expected {}",
|
||||
args.len(),
|
||||
func_def.params.len()
|
||||
))
|
||||
.span(&expr.span);
|
||||
}
|
||||
for (param, arg) in func_def.params.iter().zip(args.iter_mut()) {
|
||||
let type_ = self.table.resolve_type(param).span(&arg.span)?;
|
||||
*arg = *self.top_down_expr(arg.clone().into(), &type_)?;
|
||||
}
|
||||
e::FunctionCall {
|
||||
callee,
|
||||
args,
|
||||
is_reference,
|
||||
id,
|
||||
}
|
||||
},
|
||||
e::StructDef(params, sid) => e::StructDef(params, sid),
|
||||
e::StructLiteral { name, mut args, id } => {
|
||||
let sid = if let Type::Struct(sid) = expr.type_ {
|
||||
sid
|
||||
} else {
|
||||
return error()
|
||||
.reason(format!("Cannot create struct from type {}", expr.type_))
|
||||
.span(&expr.span);
|
||||
};
|
||||
if self.table.structs[sid].0.len() != args.len() {
|
||||
return error()
|
||||
.reason(format!(
|
||||
"Wrong number of parameters; found {}, expected {}",
|
||||
args.len(),
|
||||
self.table.structs[sid].0.len()
|
||||
))
|
||||
.span(&expr.span);
|
||||
}
|
||||
let mut declared_params: Vec<String> = vec![];
|
||||
for (name, arg) in args.iter_mut() {
|
||||
if declared_params.contains(name) {
|
||||
return error()
|
||||
.reason(format!("Field {name} initialized more than once"))
|
||||
.span(&arg.span);
|
||||
}
|
||||
declared_params.push(name.clone());
|
||||
let param_t = self
|
||||
.table
|
||||
.get_field(sid, &name)
|
||||
.span(&arg.span)?
|
||||
.is_alias()
|
||||
.span(&arg.span)?;
|
||||
println!("{param_t}");
|
||||
*arg = *self.top_down_expr(arg.clone().into(), ¶m_t)?;
|
||||
println!("/{param_t}");
|
||||
}
|
||||
e::StructLiteral { name, args, id }
|
||||
},
|
||||
e::Field {
|
||||
namespace,
|
||||
field,
|
||||
uid,
|
||||
} => e::Field {
|
||||
namespace,
|
||||
field,
|
||||
uid,
|
||||
},
|
||||
};
|
||||
Ok(expr)
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
use crate::{BinaryOp, UnaryOp};
|
||||
|
||||
use super::{UID, primitives::*};
|
||||
use crate::err::*;
|
||||
|
||||
/// Struct ID
|
||||
pub type SID = usize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StructureDef(pub Vec<(String, UID)>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionDef {
|
||||
pub params: Vec<UID>,
|
||||
pub returns: UID,
|
||||
}
|
||||
|
||||
pub fn nothing_mangle() -> UID {
|
||||
"$$nothing".into()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Type {
|
||||
Ambiguous,
|
||||
Nothing,
|
||||
Alias(Box<Type>),
|
||||
Prim(Primitive),
|
||||
Struct(SID),
|
||||
Function(UID),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Type::Ambiguous => write!(f, "ambiguous"),
|
||||
Type::Nothing => write!(f, "nothing"),
|
||||
Type::Prim(primitive) => write!(f, "{primitive}"),
|
||||
Type::Struct(vec) => write!(f, "struct {vec:?}"),
|
||||
Type::Alias(t) => write!(f, "type alias ({t})"),
|
||||
Type::Function(fid) => write!(f, "func {fid:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Type {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
use Type::*;
|
||||
match (self, other) {
|
||||
(Ambiguous, Ambiguous) => true,
|
||||
(Prim(p1), Prim(p2)) if p1 == p2 => true,
|
||||
(Struct(p1), Struct(p2)) => p1 == p2,
|
||||
(Function(id1), Function(id2)) => id1 == id2,
|
||||
(Alias(t1), Alias(t2)) => t1 == t2,
|
||||
(Nothing, Nothing) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn is_alias(&self) -> Result<Type> {
|
||||
if let Type::Alias(t) = self {
|
||||
Ok(*t.clone())
|
||||
} else {
|
||||
error().reason(format!("Expected type, found value with type {}", self))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn binary_op(lhs: &Type, op: BinaryOp, rhs: &Type) -> Result<Type> {
|
||||
use Type as t;
|
||||
let e = error().reason(format!(
|
||||
"Binary {op} is not defined for {lhs:?} and {rhs:?}",
|
||||
));
|
||||
match (lhs, rhs) {
|
||||
(t::Prim(a), t::Prim(b)) => {
|
||||
let p = Primitive::binary_op(*a, op, *b)?;
|
||||
Ok(t::Prim(p))
|
||||
},
|
||||
_ => e,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unary_op(op: UnaryOp, child: &Type) -> Result<Type> {
|
||||
use Type as t;
|
||||
if let t::Prim(p) = child {
|
||||
let p = Primitive::unary_op(op, *p)?;
|
||||
Ok(t::Prim(p))
|
||||
} else {
|
||||
error().reason(format!("Unary {op} is not defined for {child:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deduce(self, hint: &Type) -> Result<Self> {
|
||||
use Type::*;
|
||||
match (self, hint) {
|
||||
(Ambiguous, t) => Ok(t.clone()),
|
||||
(t, Ambiguous) => Ok(t),
|
||||
(Prim(mut p1), Prim(p2)) => {
|
||||
p1 = p1.coerce(*p2)?;
|
||||
Ok(Prim(p1))
|
||||
},
|
||||
(t1, t2) if &t1 == t2 => Ok(t1.clone()),
|
||||
(t1, t2) => {
|
||||
error().reason(format!("Cannot coerce type '{t2}' into '{t1}'"))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn coerce(self, expect: &Type) -> Result<Self> {
|
||||
use Type::*;
|
||||
match (self, expect) {
|
||||
(Prim(mut p1), Prim(p2)) => {
|
||||
p1 = p1.coerce(*p2)?;
|
||||
Ok(Prim(p1))
|
||||
},
|
||||
(t1, t2) if &t1 == t2 => Ok(t1.clone()),
|
||||
(t1, t2) => {
|
||||
error().reason(format!("Cannot coerce type '{t2}' into '{t1}'"))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn promote(self) -> Self {
|
||||
use Type::*;
|
||||
match self {
|
||||
Prim(mut p) => {
|
||||
p.promote();
|
||||
Prim(p)
|
||||
},
|
||||
t => t,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
use crate::{Expression, ExpressionKind, Statement, StatementKind};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct Analyzer {
|
||||
table: SymbolTable,
|
||||
}
|
||||
|
||||
impl Analyzer {
|
||||
/// Analyzing a block:
|
||||
/// 1. Name structs
|
||||
/// 2. Type structs
|
||||
/// 3. Name functions
|
||||
/// 4. Name variables (recurse on blocks) (track moves)
|
||||
/// 5. Type variables
|
||||
/// 6. Type assert variables
|
||||
pub fn block(&mut self, mut block: Vec<Statement>) -> Result<Vec<Statement>> {
|
||||
// Name structs
|
||||
for s in &mut block {
|
||||
if let StatementKind::Declaration {
|
||||
name,
|
||||
value:
|
||||
Expression {
|
||||
kind: ExpressionKind::StructDef(params, _),
|
||||
type_,
|
||||
..
|
||||
},
|
||||
..
|
||||
} = &mut s.kind
|
||||
{
|
||||
self.table.declare_struct(name, s.span)?;
|
||||
}
|
||||
}
|
||||
Ok(block)
|
||||
}
|
||||
}
|
|
@ -1,313 +0,0 @@
|
|||
mod analyzer;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{Span, err::*, semantic::Primitive};
|
||||
|
||||
pub type UID = String;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Type {
|
||||
Ambiguous,
|
||||
Prim(Primitive),
|
||||
Nothing,
|
||||
Struct(UID),
|
||||
Function(UID),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Type::Ambiguous => write!(f, "ambiguous"),
|
||||
Type::Prim(primitive) => write!(f, "{primitive}"),
|
||||
Type::Nothing => write!(f, "nothing"),
|
||||
Type::Struct(s) => write!(f, "struct {s}"),
|
||||
Type::Function(func) => write!(f, "func {func}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SymbolKind {
|
||||
Variable {
|
||||
type_: UID,
|
||||
mutable: bool,
|
||||
global: bool,
|
||||
children: Vec<UID>,
|
||||
},
|
||||
Function {
|
||||
arg_names: Vec<String>,
|
||||
arg_types: Vec<UID>,
|
||||
},
|
||||
Struct {
|
||||
field_names: Vec<String>,
|
||||
field_types: Vec<UID>,
|
||||
size: usize,
|
||||
align: usize,
|
||||
},
|
||||
TypeDef {
|
||||
actual: Type,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Symbol {
|
||||
pub name: String,
|
||||
pub span: Option<Span>,
|
||||
pub kind: SymbolKind,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn is_variable(&self) -> Result<()> {
|
||||
let name = &self.name;
|
||||
match &self.kind {
|
||||
SymbolKind::Variable { .. } => Ok(()),
|
||||
SymbolKind::Function { .. } => {
|
||||
error().reason(format!("'{name}' refers to a function, not a value"))
|
||||
},
|
||||
_ => error().reason(format!("'{name}' refers to a type, not a value")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_type(&self) -> Result<Type> {
|
||||
let name = &self.name;
|
||||
match &self.kind {
|
||||
SymbolKind::TypeDef { actual } => Ok(actual.clone()),
|
||||
_ => error().reason(format!("'{name}' refers to a value, not a type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Event {
|
||||
Declared { name: String, uid: UID },
|
||||
Moved { name: String, uid: UID, span: Span },
|
||||
Func { returns: Vec<UID>, uid: UID },
|
||||
Block { returns: UID },
|
||||
}
|
||||
|
||||
pub struct SymbolTable {
|
||||
syms: HashMap<UID, Symbol>,
|
||||
scope: Vec<Event>,
|
||||
nesting: usize,
|
||||
mangle_num: usize,
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
syms: HashMap::new(),
|
||||
scope: vec![],
|
||||
nesting: 0,
|
||||
mangle_num: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_uid(&mut self, name: &str) -> UID {
|
||||
let uid = format!("${}${name}", self.mangle_num);
|
||||
self.mangle_num += 1;
|
||||
uid
|
||||
}
|
||||
|
||||
pub fn get(&self, uid: &UID) -> &Symbol {
|
||||
self.syms.get(uid).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, uid: &UID) -> &mut Symbol {
|
||||
self.syms.get_mut(uid).unwrap()
|
||||
}
|
||||
|
||||
// Find the definition of a symbol in local and global scope
|
||||
pub fn find(&self, name: &str) -> Result<&Symbol> {
|
||||
let mut nesting = self.nesting;
|
||||
for e in self.scope.iter().rev() {
|
||||
match e {
|
||||
Event::Declared { uid, .. }
|
||||
if nesting == self.nesting || nesting == 0 =>
|
||||
{
|
||||
return Ok(self.get(uid));
|
||||
},
|
||||
Event::Moved { name, span, .. }
|
||||
if nesting == self.nesting || nesting == 0 =>
|
||||
{
|
||||
return error()
|
||||
.reason(format!("Symbol '{name}' moved out of scope here"))
|
||||
.span(&span);
|
||||
},
|
||||
Event::Func { .. } => {
|
||||
nesting -= 1;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
error().reason(format!("Cannot find symbol '{name}'"))
|
||||
}
|
||||
|
||||
// Get all nested members of a struct variable
|
||||
fn get_all_children(&self, uid: &UID) -> Vec<UID> {
|
||||
use SymbolKind as s;
|
||||
match &self.get(uid).kind {
|
||||
s::Variable { children, .. } => {
|
||||
let mut new_children = children.clone();
|
||||
for uid in children {
|
||||
new_children.append(&mut self.get_all_children(uid))
|
||||
}
|
||||
new_children
|
||||
},
|
||||
_ => {
|
||||
vec![]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Move a symbol out of scope
|
||||
pub fn move_symbol(&mut self, move_uid: &UID, span: Span) -> Result<()> {
|
||||
let children = self.get_all_children(move_uid);
|
||||
for e in self.scope.iter().rev() {
|
||||
match e {
|
||||
Event::Declared { uid, .. } => {
|
||||
if move_uid == uid {
|
||||
break;
|
||||
}
|
||||
},
|
||||
Event::Moved { uid, span, .. } => {
|
||||
if children.contains(uid) {
|
||||
return error()
|
||||
.reason("Symbol was partially moved here")
|
||||
.span(&span);
|
||||
} else if move_uid == uid {
|
||||
return error()
|
||||
.reason("Symbol was previously moved here")
|
||||
.span(&span);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
self.scope.push(Event::Moved {
|
||||
name: self.get(move_uid).name.clone(),
|
||||
uid: move_uid.clone(),
|
||||
span,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn in_global_scope(&self) -> bool {
|
||||
for e in self.scope.iter().rev() {
|
||||
if let Event::Func { .. } = e {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn define_var(
|
||||
&mut self,
|
||||
name: &str,
|
||||
mutable: bool,
|
||||
type_: &UID,
|
||||
span: Span,
|
||||
) -> UID {
|
||||
let uid = self.generate_uid(name);
|
||||
self.syms.insert(uid.clone(), Symbol {
|
||||
name: name.to_string(),
|
||||
span: Some(span),
|
||||
kind: SymbolKind::Variable {
|
||||
type_: type_.clone(),
|
||||
mutable,
|
||||
global: self.in_global_scope(),
|
||||
children: vec![],
|
||||
},
|
||||
});
|
||||
self.scope.push(Event::Declared {
|
||||
name: name.to_string(),
|
||||
uid: uid.clone(),
|
||||
});
|
||||
uid
|
||||
}
|
||||
|
||||
pub fn declare_struct(
|
||||
&mut self,
|
||||
struct_name: &str,
|
||||
span: Span,
|
||||
) -> Result<UID> {
|
||||
// Check for multiple definition ()
|
||||
for e in self.scope.iter().rev() {
|
||||
match e {
|
||||
Event::Declared { name, uid } => {
|
||||
if name == struct_name {
|
||||
let e = error().reason(format!(
|
||||
"Structure {struct_name} is defined multiple times"
|
||||
));
|
||||
if let Some(s) = &self.get(uid).span {
|
||||
return e.span(s);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
},
|
||||
Event::Moved { .. } => {},
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
let uid = self.generate_uid(struct_name);
|
||||
self.syms.insert(uid.clone(), Symbol {
|
||||
name: struct_name.to_string(),
|
||||
span: Some(span),
|
||||
kind: SymbolKind::Struct {
|
||||
field_names: vec![],
|
||||
field_types: vec![],
|
||||
size: 0,
|
||||
align: 0,
|
||||
},
|
||||
});
|
||||
self.scope.push(Event::Declared {
|
||||
name: struct_name.to_string(),
|
||||
uid: uid.clone(),
|
||||
});
|
||||
Ok(uid)
|
||||
}
|
||||
|
||||
pub fn define_struct(
|
||||
&mut self,
|
||||
uid: &UID,
|
||||
field_names: Vec<String>,
|
||||
field_types: Vec<UID>,
|
||||
) {
|
||||
if let SymbolKind::Struct {
|
||||
field_names: old_names,
|
||||
field_types: old_types,
|
||||
size,
|
||||
align,
|
||||
} = &mut self.get_mut(uid).kind
|
||||
{
|
||||
*old_names = field_names;
|
||||
*old_types = field_types;
|
||||
} else {
|
||||
unreachable!("Defined non-existent struct")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn define_function(
|
||||
&mut self,
|
||||
name: &str,
|
||||
arg_names: Vec<String>,
|
||||
arg_types: Vec<UID>,
|
||||
span: Span,
|
||||
) -> UID {
|
||||
let uid = self.generate_uid(name);
|
||||
self.syms.insert(uid.clone(), Symbol {
|
||||
name: name.to_string(),
|
||||
span: Some(span),
|
||||
kind: SymbolKind::Function {
|
||||
arg_names,
|
||||
arg_types,
|
||||
},
|
||||
});
|
||||
self.scope.push(Event::Declared {
|
||||
name: name.to_string(),
|
||||
uid: uid.clone(),
|
||||
});
|
||||
uid
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue