readme
This commit is contained in:
parent
38a3a5eab0
commit
c1ae2b4e48
5
demo.hal
5
demo.hal
|
@ -1,3 +1,4 @@
|
||||||
|
/*
|
||||||
S :: struct {
|
S :: struct {
|
||||||
a: integer,
|
a: integer,
|
||||||
b: glyph,
|
b: glyph,
|
||||||
|
@ -9,5 +10,9 @@ s := foo(S{a: 1, b: 'a', c: 1.0}, 2);
|
||||||
foo :: (s: S, a: integer) -> S {
|
foo :: (s: S, a: integer) -> S {
|
||||||
return 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 crate::{Expression, ExpressionKind, Statement, StatementKind};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -247,3 +248,4 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/*
|
||||||
mod generate;
|
mod generate;
|
||||||
mod wasm;
|
mod wasm;
|
||||||
|
|
||||||
|
@ -194,3 +195,4 @@ impl Compiler {
|
||||||
self.ir = result;
|
self.ir = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/*
|
||||||
use crate::{
|
use crate::{
|
||||||
Base, BinaryOp, Immediate,
|
Base, BinaryOp, Immediate,
|
||||||
err::*,
|
err::*,
|
||||||
|
@ -209,3 +210,4 @@ impl Compiler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
43
src/main.rs
43
src/main.rs
|
@ -3,13 +3,11 @@ mod ir;
|
||||||
mod lookahead;
|
mod lookahead;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod semantic;
|
mod semantic;
|
||||||
mod semantic2;
|
|
||||||
mod token;
|
mod token;
|
||||||
mod treewalk;
|
mod treewalk;
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
|
||||||
use err::*;
|
use err::*;
|
||||||
use ir::Compiler;
|
|
||||||
use lookahead::*;
|
use lookahead::*;
|
||||||
use parse::*;
|
use parse::*;
|
||||||
use semantic::Analyzer;
|
use semantic::Analyzer;
|
||||||
|
@ -64,29 +62,6 @@ fn test_expression(expr: &str) {
|
||||||
println!("{:?}", parser.expression(0).unwrap());
|
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> {
|
fn tokenize(input: &'static str) -> impl Iterator<Item = Token> {
|
||||||
Tokenizer::new(input.chars()).filter(|t| t.0.is_meaningful())
|
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))
|
Parser::new(tokenize(input))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typecheck(input: &'static str) -> Vec<Statement> {
|
fn typecheck(input: &'static str) {
|
||||||
Analyzer::new().typecheck(parse(input).collect())
|
let parsed: Vec<_> = parse(input).collect();
|
||||||
}
|
let mut s = semantic::Analyzer::new();
|
||||||
|
s.block(parsed);
|
||||||
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 main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
for p in parse(include_str!("../demo.hal")) {
|
||||||
|
println!("------------------");
|
||||||
|
println!("{p:#?}");
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
for s in typecheck(include_str!("../demo.hal")) {
|
for s in typecheck(include_str!("../demo.hal")) {
|
||||||
println!("------------------");
|
println!("------------------");
|
||||||
println!("{s:#?}");
|
println!("{s:#?}");
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
compile(include_str!("../demo.hal"));
|
|
||||||
//let module = frontend::Module::from_file("./demo.hal")?;
|
//let module = frontend::Module::from_file("./demo.hal")?;
|
||||||
//module.write_to("test");
|
//module.write_to("test");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -2,16 +2,22 @@ use crate::{Base, semantic::*};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Parameter {
|
pub struct Parameters {
|
||||||
pub name: String,
|
pub arity: usize,
|
||||||
pub type_str: String,
|
pub names: Vec<String>,
|
||||||
pub type_actual: Type,
|
pub type_names: Vec<String>,
|
||||||
|
pub types: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Parameter {
|
impl Default for Parameters {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn default() -> Self {
|
||||||
write!(f, "{}: {:?}", self.name, self.type_actual)
|
Self {
|
||||||
|
arity: 0,
|
||||||
|
names: vec![],
|
||||||
|
type_names: vec![],
|
||||||
|
types: vec![],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +45,10 @@ impl std::fmt::Display for Immediate {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ExpressionKind {
|
pub enum ExpressionKind {
|
||||||
Immediate(Immediate),
|
Immediate(Immediate),
|
||||||
Identifier(String, UID),
|
Identifier {
|
||||||
|
name: String,
|
||||||
|
uid: UID,
|
||||||
|
},
|
||||||
Binary {
|
Binary {
|
||||||
op: BinaryOp,
|
op: BinaryOp,
|
||||||
left: Box<Expression>,
|
left: Box<Expression>,
|
||||||
|
@ -51,29 +60,34 @@ pub enum ExpressionKind {
|
||||||
},
|
},
|
||||||
Parenthesis(Box<Expression>),
|
Parenthesis(Box<Expression>),
|
||||||
FunctionDef {
|
FunctionDef {
|
||||||
params: Vec<Parameter>,
|
params: Parameters,
|
||||||
returns_str: Option<String>,
|
returns_str: Option<String>,
|
||||||
returns_actual: Type,
|
returns_actual: Type,
|
||||||
body: Vec<Statement>,
|
body: Vec<Statement>,
|
||||||
id: UID,
|
uid: UID,
|
||||||
},
|
},
|
||||||
FunctionCall {
|
FunctionCall {
|
||||||
callee: Box<Expression>,
|
callee: Box<Expression>,
|
||||||
args: Vec<Expression>,
|
args: Vec<Expression>,
|
||||||
is_reference: bool,
|
uid: UID,
|
||||||
id: UID,
|
|
||||||
},
|
},
|
||||||
StructDef(Vec<Parameter>, usize),
|
StructDef(Parameters, UID),
|
||||||
StructLiteral {
|
StructLiteral {
|
||||||
name: String,
|
name: String,
|
||||||
args: Vec<(String, Expression)>,
|
args: Vec<(String, Expression)>,
|
||||||
id: UID,
|
uid: UID,
|
||||||
},
|
},
|
||||||
Field {
|
Field {
|
||||||
namespace: Box<Expression>,
|
namespace: Box<Expression>,
|
||||||
field: Box<Expression>,
|
field: Box<Expression>,
|
||||||
uid: UID,
|
uid: UID,
|
||||||
},
|
},
|
||||||
|
Block(Vec<Statement>),
|
||||||
|
If {
|
||||||
|
predicate: Box<Expression>,
|
||||||
|
block: Vec<Statement>,
|
||||||
|
else_: Option<Box<Expression>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -105,7 +119,7 @@ impl std::fmt::Display for ExpressionKind {
|
||||||
e::Unary { op: token, child } => {
|
e::Unary { op: token, child } => {
|
||||||
write!(f, "({token} {child})")
|
write!(f, "({token} {child})")
|
||||||
},
|
},
|
||||||
e::Identifier(i, _) => write!(f, "{i}"),
|
e::Identifier { name, .. } => write!(f, "{name}"),
|
||||||
e::FunctionCall { callee, args, .. } => {
|
e::FunctionCall { callee, args, .. } => {
|
||||||
write!(f, "({callee} call {args:?})")
|
write!(f, "({callee} call {args:?})")
|
||||||
},
|
},
|
||||||
|
@ -123,6 +137,28 @@ impl std::fmt::Display for ExpressionKind {
|
||||||
},
|
},
|
||||||
e::StructDef(params, _) => write!(f, "struct {{ {params:?} }}"),
|
e::StructDef(params, _) => write!(f, "struct {{ {params:?} }}"),
|
||||||
e::StructLiteral { name, args, .. } => write!(f, "{name} {{ {args:?} }}"),
|
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,
|
Type::Ambiguous,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Terminal or paren
|
// Primary
|
||||||
else {
|
else {
|
||||||
self.primary().reason("Expected expression")?
|
self.primary().reason("Expected expression")?
|
||||||
};
|
};
|
||||||
|
@ -236,8 +272,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
e::FunctionCall {
|
e::FunctionCall {
|
||||||
callee: current.into(),
|
callee: current.into(),
|
||||||
args,
|
args,
|
||||||
is_reference: false,
|
uid: "".into(),
|
||||||
id: "".into(),
|
|
||||||
},
|
},
|
||||||
span + span2,
|
span + span2,
|
||||||
Type::Ambiguous,
|
Type::Ambiguous,
|
||||||
|
@ -270,6 +305,73 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
Ok(current)
|
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> {
|
fn primary(&mut self) -> Result<Expression> {
|
||||||
use ExpressionKind as e;
|
use ExpressionKind as e;
|
||||||
use Immediate as im;
|
use Immediate as im;
|
||||||
|
@ -301,36 +403,21 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
e::Immediate(im::Boolean(false))
|
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
|
// Function definition
|
||||||
t::LeftParen
|
t::LeftParen
|
||||||
if (self.look(1, t::Identifier("".into())).is_ok()
|
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.look(1, t::RightParen).is_ok() =>
|
||||||
{
|
{
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
let mut params = vec![];
|
let params = self.parameters(span)?;
|
||||||
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 Token(_, span2) = self
|
let Token(_, span2) = self
|
||||||
.eat(t::RightParen)
|
.eat(t::RightParen)
|
||||||
.span(&span)
|
.span(&span)
|
||||||
|
@ -355,38 +442,16 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
returns_str,
|
returns_str,
|
||||||
returns_actual: Type::Ambiguous,
|
returns_actual: Type::Ambiguous,
|
||||||
body,
|
body,
|
||||||
id: "".into(),
|
uid: "".into(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Struct definition
|
// Struct definition
|
||||||
t::Struct => {
|
t::Struct => {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
self.eat(t::LeftBrace)?;
|
self.eat(t::LeftBrace)?;
|
||||||
let mut params = vec![];
|
let params = self.parameters(span)?;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.eat(t::RightBrace)?;
|
self.eat(t::RightBrace)?;
|
||||||
e::StructDef(params, usize::MAX)
|
e::StructDef(params, "".into())
|
||||||
},
|
},
|
||||||
// Struct literal
|
// Struct literal
|
||||||
t::Identifier(name) if self.look(1, t::LeftBrace).is_ok() => {
|
t::Identifier(name) if self.look(1, t::LeftBrace).is_ok() => {
|
||||||
|
@ -414,12 +479,15 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
e::StructLiteral {
|
e::StructLiteral {
|
||||||
name,
|
name,
|
||||||
args,
|
args,
|
||||||
id: "".into(),
|
uid: "".into(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
t::Identifier(i) => {
|
t::Identifier(i) => {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
e::Identifier(i, "".into())
|
e::Identifier {
|
||||||
|
name: i,
|
||||||
|
uid: "".into(),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// Parenthetical
|
// Parenthetical
|
||||||
t::LeftParen => {
|
t::LeftParen => {
|
||||||
|
@ -442,6 +510,41 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
Ok(Expression::new(kind, span, Type::Ambiguous))
|
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)> {
|
fn identifier(&mut self) -> Result<(String, Span)> {
|
||||||
use TokenKind as t;
|
use TokenKind as t;
|
||||||
match self.peek(0) {
|
match self.peek(0) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::semantic::{Type, UID};
|
use crate::semantic::UID;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ pub enum StatementKind {
|
||||||
Declaration {
|
Declaration {
|
||||||
name: String,
|
name: String,
|
||||||
type_str: Option<String>,
|
type_str: Option<String>,
|
||||||
type_actual: Type,
|
type_uid: UID,
|
||||||
value: Expression,
|
value: Expression,
|
||||||
mutable: bool,
|
mutable: bool,
|
||||||
uid: UID,
|
uid: UID,
|
||||||
|
@ -17,18 +17,13 @@ pub enum StatementKind {
|
||||||
value: Expression,
|
value: Expression,
|
||||||
uid: UID,
|
uid: UID,
|
||||||
},
|
},
|
||||||
If {
|
|
||||||
predicate: Expression,
|
|
||||||
block: Vec<Statement>,
|
|
||||||
else_: Option<Box<Statement>>,
|
|
||||||
},
|
|
||||||
While {
|
While {
|
||||||
predicate: Expression,
|
predicate: Expression,
|
||||||
block: Vec<Statement>,
|
block: Vec<Statement>,
|
||||||
},
|
},
|
||||||
Print(Expression),
|
Print(Expression),
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
Block(Vec<Statement>),
|
Remainder(Expression),
|
||||||
Return(Option<Expression>),
|
Return(Option<Expression>),
|
||||||
Error(Diagnostic),
|
Error(Diagnostic),
|
||||||
}
|
}
|
||||||
|
@ -83,7 +78,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
kind: s::Declaration {
|
kind: s::Declaration {
|
||||||
name,
|
name,
|
||||||
type_str,
|
type_str,
|
||||||
type_actual: Type::Ambiguous,
|
type_uid: "".into(),
|
||||||
value,
|
value,
|
||||||
mutable,
|
mutable,
|
||||||
uid: "".into(),
|
uid: "".into(),
|
||||||
|
@ -111,10 +106,6 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// If
|
|
||||||
(Token(t::If, _), _) => {
|
|
||||||
return self.if_else();
|
|
||||||
},
|
|
||||||
// While
|
// While
|
||||||
(Token(t::While, span2), _) => {
|
(Token(t::While, span2), _) => {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
|
@ -149,19 +140,6 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
kind: s::Print(expr),
|
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
|
// Return
|
||||||
(Token(t::Return, span2), _) => {
|
(Token(t::Return, span2), _) => {
|
||||||
span = span + span2;
|
span = span + span2;
|
||||||
|
@ -182,9 +160,26 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
.expression(0)
|
.expression(0)
|
||||||
.trace_span(span, "while parsing expression statement")?;
|
.trace_span(span, "while parsing expression statement")?;
|
||||||
span = span + expr.span;
|
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,
|
span,
|
||||||
kind: s::Expression(expr),
|
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)
|
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 struct Analyzer {
|
||||||
pub table: SymbolTable,
|
table: SymbolTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Analyzer {
|
impl Analyzer {
|
||||||
|
@ -11,25 +13,79 @@ impl Analyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn typecheck(
|
pub fn typecheck() {
|
||||||
&mut self,
|
todo!()
|
||||||
mut statements: Vec<Statement>,
|
|
||||||
) -> Vec<Statement> {
|
|
||||||
for s in &mut statements {
|
|
||||||
*s = *self.naming_pass_stmt(s.clone().into()).unwrap();
|
|
||||||
}
|
}
|
||||||
for s in &mut statements {
|
|
||||||
*s = *self.bottom_up_stmt(s.clone().into()).unwrap();
|
/// Analyzing a block:
|
||||||
}
|
/// 1. Name structs
|
||||||
for s in &mut statements {
|
/// 2. Type structs
|
||||||
*s = *self.top_down_stmt(s.clone().into()).unwrap();
|
/// 3. Name and type functions
|
||||||
}
|
/// 4. Name variables (recurse on blocks) (track moves)
|
||||||
println!("-----TABLE------");
|
/// 5. Type variables
|
||||||
println!("{:#?}", self.table.table);
|
/// 6. Type assert variables
|
||||||
println!("-----FUNCS------");
|
pub fn block(&mut self, mut block: Vec<Statement>) -> Result<Vec<Statement>> {
|
||||||
println!("{:#?}", self.table.functions);
|
// 1. Name structs
|
||||||
println!("-----STRUCTS----");
|
for s in &mut block {
|
||||||
println!("{:#?}", self.table.structs);
|
if let StatementKind::Declaration {
|
||||||
statements
|
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;
|
pub mod analyzer;
|
||||||
mod bottom_up;
|
pub mod primitives;
|
||||||
mod naming;
|
|
||||||
mod primitives;
|
pub use analyzer::*;
|
||||||
mod sizing;
|
pub use primitives::*;
|
||||||
mod top_down;
|
|
||||||
mod types;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{Parameter, err::*};
|
use crate::{Parameters, Span, err::*, semantic::Primitive};
|
||||||
pub use analyzer::*;
|
|
||||||
pub use primitives::*;
|
|
||||||
pub use types::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
pub type UID = String;
|
||||||
enum Scope {
|
|
||||||
Block,
|
#[derive(Debug, Clone)]
|
||||||
Function,
|
pub enum Type {
|
||||||
|
Ambiguous,
|
||||||
|
Prim(Primitive),
|
||||||
|
Nothing,
|
||||||
|
Struct(UID),
|
||||||
|
Function(UID),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mangled name
|
impl std::fmt::Display for Type {
|
||||||
pub type UID = String;
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Symbol {
|
pub struct Symbol {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub type_: Type,
|
|
||||||
pub uid: UID,
|
pub uid: UID,
|
||||||
initialized: bool,
|
pub span: Option<Span>,
|
||||||
pub mutable: Option<bool>,
|
pub kind: SymbolKind,
|
||||||
pub global: bool,
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
enum Definition {
|
pub enum Event {
|
||||||
Ident(Symbol),
|
Declared { name: String, uid: UID },
|
||||||
FuncStart,
|
Moved { name: String, uid: UID, span: Span },
|
||||||
BlockStart,
|
Func { returns: Vec<UID>, uid: UID },
|
||||||
|
Block { returns: UID },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SymbolTable {
|
pub struct SymbolTable {
|
||||||
pub structs: Vec<StructureDef>,
|
syms: HashMap<UID, Symbol>,
|
||||||
pub functions: HashMap<UID, FunctionDef>,
|
scope: Vec<Event>,
|
||||||
scope: Vec<Definition>,
|
|
||||||
pub table: HashMap<UID, Symbol>,
|
|
||||||
mangle_num: usize,
|
|
||||||
nesting: usize,
|
nesting: usize,
|
||||||
|
mangle_num: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolTable {
|
impl SymbolTable {
|
||||||
pub fn new() -> Self {
|
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 {
|
Self {
|
||||||
structs: vec![],
|
syms: HashMap::new(),
|
||||||
functions,
|
scope: vec![],
|
||||||
scope,
|
|
||||||
table,
|
|
||||||
mangle_num: 0,
|
|
||||||
nesting: 0,
|
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 {
|
fn generate_uid(&mut self, name: &str) -> UID {
|
||||||
let uid = format!("${}${name}", self.mangle_num);
|
let uid = format!("${}${name}", self.mangle_num);
|
||||||
self.mangle_num += 1;
|
self.mangle_num += 1;
|
||||||
uid
|
uid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_field(&self, struct_id: SID, field_name: &str) -> Result<Type> {
|
pub fn get(&self, uid: &UID) -> &Symbol {
|
||||||
let struct_def = &self.structs[struct_id].0;
|
self.syms.get(uid).unwrap()
|
||||||
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 create_struct(&mut self, params: Vec<Parameter>) -> SID {
|
pub fn get_mut(&mut self, uid: &UID) -> &mut Symbol {
|
||||||
let sid = self.structs.len();
|
self.syms.get_mut(uid).unwrap()
|
||||||
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 create_function(
|
// Find the definition of a symbol in local and global scope
|
||||||
&mut self,
|
pub fn find(&self, name: &str) -> Result<&Symbol> {
|
||||||
params: Vec<Parameter>,
|
let mut nesting = self.nesting;
|
||||||
returns: Option<String>,
|
for e in self.scope.iter().rev() {
|
||||||
) -> Result<UID> {
|
match e {
|
||||||
let uid = self.generate_uid("func");
|
Event::Declared { uid, .. }
|
||||||
let mut symbols = vec![];
|
if nesting == self.nesting || nesting == 0 =>
|
||||||
for p in ¶ms {
|
{
|
||||||
let symbol = self.reference_ident(&p.type_str);
|
return Ok(self.get(uid));
|
||||||
symbols.push(symbol);
|
},
|
||||||
|
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 {
|
} else {
|
||||||
nothing_mangle()
|
panic!("Moved non-variable {move_uid}");
|
||||||
};
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
self.functions.insert(uid.clone(), FunctionDef {
|
let children = self.get_all_children(move_uid);
|
||||||
params: new_params,
|
for e in self.scope.iter().rev() {
|
||||||
returns,
|
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)
|
Ok(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modify_ident(
|
fn type_params(&mut self, mut params: Parameters) -> Result<Parameters> {
|
||||||
&mut self,
|
for i in 0..params.arity {
|
||||||
uid: UID,
|
params.types[i] = self
|
||||||
name: Option<String>,
|
.find(¶ms.type_names[i])
|
||||||
type_: Option<Type>,
|
.trace(format!(
|
||||||
mutable: Option<bool>,
|
"while resolving type of field '{}'",
|
||||||
init: bool,
|
params.type_names[i]
|
||||||
global: bool,
|
))?
|
||||||
) -> Result<()> {
|
.uid
|
||||||
let symbol = self.table.get_mut(&uid).unwrap();
|
.clone();
|
||||||
if let Some(ref type_) = type_ {
|
}
|
||||||
symbol.type_ = symbol.type_.clone().deduce(type_)?;
|
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;
|
pub fn define_function(
|
||||||
for def in &mut self.scope {
|
&mut self,
|
||||||
match def {
|
func_name: &str,
|
||||||
Definition::Ident(symbol)
|
params: Parameters,
|
||||||
if (symbol.uid == uid)
|
returns_str: Option<&str>,
|
||||||
&& (nesting == 0 || nesting == self.nesting) =>
|
span: Span,
|
||||||
{
|
) -> Result<UID> {
|
||||||
if let Some(ref name) = name {
|
// Check for multiple definition
|
||||||
symbol.name = name.clone();
|
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;
|
self.syms.insert(uid.clone(), Symbol {
|
||||||
symbol.global |= global;
|
name: func_name.to_string(),
|
||||||
return Ok(());
|
uid: uid.clone(),
|
||||||
},
|
span: Some(span),
|
||||||
Definition::FuncStart => nesting -= 1,
|
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(())
|
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