handle variable scopes correctly, add option to write name section

This commit is contained in:
2021-12-11 23:25:50 +01:00
parent f02d1de8a7
commit 196719b35e
8 changed files with 348 additions and 205 deletions

7
Cargo.lock generated
View File

@@ -76,6 +76,7 @@ dependencies = [
"anyhow", "anyhow",
"ariadne", "ariadne",
"chumsky", "chumsky",
"pico-args",
"wasm-encoder", "wasm-encoder",
"wasmparser", "wasmparser",
] ]
@@ -109,6 +110,12 @@ version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013"
[[package]]
name = "pico-args"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
version = "0.5.19" version = "0.5.19"

View File

@@ -12,3 +12,4 @@ wasm-encoder = "0.8"
anyhow = "1" anyhow = "1"
chumsky = "0.5" chumsky = "0.5"
ariadne = "0.1" ariadne = "0.1"
pico-args = "0.4"

View File

@@ -58,6 +58,69 @@ pub struct Function {
pub params: Vec<(String, Type)>, pub params: Vec<(String, Type)>,
pub type_: Option<Type>, pub type_: Option<Type>,
pub body: Expression, pub body: Expression,
pub locals: Locals,
}
#[derive(Debug, Default)]
pub struct Locals {
pub params: Vec<Local>,
pub locals: Vec<Local>,
}
impl Locals {
pub fn add_param(&mut self, span: Span, name: String, type_: Type) -> u32 {
assert!(self.locals.is_empty());
let id = self.params.len() as u32;
self.params.push(Local {
span,
name,
type_,
index: Some(id),
});
id
}
pub fn add_local(&mut self, span: Span, name: String, type_: Type, store: bool) -> u32 {
let id = (self.params.len() + self.locals.len()) as u32;
self.locals.push(Local {
span,
name,
type_,
index: store.then(|| id),
});
id
}
}
impl std::ops::Index<u32> for Locals {
type Output = Local;
fn index(&self, id: u32) -> &Local {
let id = id as usize;
if id < self.params.len() {
&self.params[id]
} else {
&self.locals[id - self.params.len()]
}
}
}
impl std::ops::IndexMut<u32> for Locals {
fn index_mut(&mut self, id: u32) -> &mut Local {
let id = id as usize;
if id < self.params.len() {
&mut self.params[id]
} else {
&mut self.locals[id - self.params.len()]
}
}
}
#[derive(Debug)]
pub struct Local {
pub span: Span,
pub name: String,
pub type_: Type,
pub index: Option<u32>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -75,8 +138,8 @@ pub enum DataValues {
String(String), String(String),
File { File {
path: PathBuf, path: PathBuf,
data: Vec<u8> data: Vec<u8>,
} },
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -108,28 +171,28 @@ impl Expression {
pub fn const_i32(&self) -> i32 { pub fn const_i32(&self) -> i32 {
match self.expr { match self.expr {
Expr::I32Const(v) => v, Expr::I32Const(v) => v,
_ => panic!("Expected I32Const") _ => panic!("Expected I32Const"),
} }
} }
pub fn const_i64(&self) -> i64 { pub fn const_i64(&self) -> i64 {
match self.expr { match self.expr {
Expr::I64Const(v) => v, Expr::I64Const(v) => v,
_ => panic!("Expected I64Const") _ => panic!("Expected I64Const"),
} }
} }
pub fn const_f32(&self) -> f32 { pub fn const_f32(&self) -> f32 {
match self.expr { match self.expr {
Expr::F32Const(v) => v, Expr::F32Const(v) => v,
_ => panic!("Expected F32Const") _ => panic!("Expected F32Const"),
} }
} }
pub fn const_f64(&self) -> f64 { pub fn const_f64(&self) -> f64 {
match self.expr { match self.expr {
Expr::F64Const(v) => v, Expr::F64Const(v) => v,
_ => panic!("Expected F64Const") _ => panic!("Expected F64Const"),
} }
} }
} }
@@ -144,12 +207,16 @@ pub enum Expr {
I64Const(i64), I64Const(i64),
F32Const(f32), F32Const(f32),
F64Const(f64), F64Const(f64),
Variable(String), Variable {
name: String,
local_id: Option<u32>,
},
Let { Let {
name: String, name: String,
type_: Option<Type>, type_: Option<Type>,
value: Option<Box<Expression>>, value: Option<Box<Expression>>,
let_type: LetType, let_type: LetType,
local_id: Option<u32>,
}, },
Poke { Poke {
mem_location: MemoryLocation, mem_location: MemoryLocation,
@@ -181,10 +248,12 @@ pub enum Expr {
Assign { Assign {
name: String, name: String,
value: Box<Expression>, value: Box<Expression>,
local_id: Option<u32>,
}, },
LocalTee { LocalTee {
name: String, name: String,
value: Box<Expression>, value: Box<Expression>,
local_id: Option<u32>,
}, },
Cast { Cast {
value: Box<Expression>, value: Box<Expression>,
@@ -224,7 +293,7 @@ impl Expr {
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LetType { pub enum LetType {
Normal, Normal,
Lazy, Lazy,

View File

@@ -2,13 +2,13 @@ use std::collections::HashMap;
use wasm_encoder::{ use wasm_encoder::{
BlockType, CodeSection, DataSection, EntityType, Export, ExportSection, Function, BlockType, CodeSection, DataSection, EntityType, Export, ExportSection, Function,
FunctionSection, GlobalSection, GlobalType, ImportSection, Instruction, MemArg, MemoryType, FunctionSection, GlobalSection, GlobalType, ImportSection, IndirectNameMap, Instruction,
Module, StartSection, TypeSection, ValType, MemArg, MemoryType, Module, NameMap, NameSection, StartSection, TypeSection, ValType,
}; };
use crate::{ast, intrinsics::Intrinsics}; use crate::{ast, intrinsics::Intrinsics, Options};
pub fn emit(script: &ast::Script) -> Vec<u8> { pub fn emit(script: &ast::Script, module_name: &str, options: &Options) -> Vec<u8> {
let mut module = Module::new(); let mut module = Module::new();
let function_types = collect_function_types(script); let function_types = collect_function_types(script);
@@ -188,6 +188,56 @@ pub fn emit(script: &ast::Script) -> Vec<u8> {
module.section(&data_section); module.section(&data_section);
} }
if options.debug {
let mut names = NameSection::new();
names.module(module_name);
let mut functions = HashMap::new();
for (name, index) in &function_map {
functions.insert(*index, name);
}
let mut keys: Vec<_> = functions.keys().collect();
keys.sort();
let mut function_names = NameMap::new();
for i in keys {
function_names.append(*i, functions[i]);
}
names.functions(&function_names);
let mut functions = HashMap::new();
for function in &script.functions {
let mut local_names = NameMap::new();
for param in &function.locals.params {
local_names.append(param.index.unwrap(), &param.name);
}
let mut locals = HashMap::new();
for local in &function.locals.locals {
if let Some(index) = local.index {
locals.insert(index, &local.name);
}
}
let mut keys: Vec<_> = locals.keys().collect();
keys.sort();
for i in keys {
local_names.append(*i, locals[i]);
}
functions.insert(*function_map.get(&function.name).unwrap(), local_names);
}
let mut keys: Vec<_> = functions.keys().collect();
keys.sort();
let mut locals = IndirectNameMap::new();
for i in keys {
locals.append(*i, &functions[i]);
}
names.locals(&locals);
module.section(&names);
}
module.finish() module.finish()
} }
@@ -237,9 +287,9 @@ struct FunctionContext<'a> {
function: &'a mut Function, function: &'a mut Function,
globals: &'a HashMap<&'a str, u32>, globals: &'a HashMap<&'a str, u32>,
functions: &'a HashMap<String, u32>, functions: &'a HashMap<String, u32>,
locals: &'a HashMap<String, u32>, locals: &'a ast::Locals,
labels: Vec<String>, labels: Vec<String>,
let_values: HashMap<&'a str, (&'a ast::Expression, ast::LetType)>, let_values: HashMap<u32, (&'a ast::Expression, ast::LetType)>,
intrinsics: &'a Intrinsics, intrinsics: &'a Intrinsics,
} }
@@ -249,27 +299,22 @@ fn emit_function(
functions: &HashMap<String, u32>, functions: &HashMap<String, u32>,
intrinsics: &Intrinsics, intrinsics: &Intrinsics,
) -> Function { ) -> Function {
let mut locals = Vec::new(); let mut function = Function::new_with_locals_types({
collect_locals_expr(&func.body, &mut locals); let mut locals: Vec<(u32, ast::Type)> = func
locals.sort_by_key(|(_, t)| *t); .locals
.locals
let mut function = Function::new_with_locals_types(locals.iter().map(|(_, t)| map_type(*t))); .iter()
.filter_map(|local| local.index.map(|i| (i, local.type_)))
let mut local_map: HashMap<String, u32> = HashMap::new(); .collect();
locals.sort();
for (ref name, _) in func.params.iter() { locals.into_iter().map(|(_, t)| map_type(t))
local_map.insert(name.clone(), local_map.len() as u32); });
}
for (name, _) in locals {
local_map.insert(name, local_map.len() as u32);
}
let mut context = FunctionContext { let mut context = FunctionContext {
function: &mut function, function: &mut function,
globals, globals,
functions, functions,
locals: &local_map, locals: &func.locals,
labels: vec![], labels: vec![],
let_values: HashMap::new(), let_values: HashMap::new(),
intrinsics, intrinsics,
@@ -284,89 +329,6 @@ fn emit_function(
function function
} }
fn collect_locals_expr<'a>(expr: &ast::Expression, locals: &mut Vec<(String, ast::Type)>) {
match &expr.expr {
ast::Expr::Block {
statements,
final_expression,
} => {
for stmt in statements {
collect_locals_expr(stmt, locals);
}
if let Some(ref expr) = final_expression {
collect_locals_expr(expr, locals);
}
}
ast::Expr::Let {
name, type_, value, ..
} => {
locals.push((name.clone(), type_.unwrap()));
if let Some(ref value) = value {
collect_locals_expr(value, locals);
}
}
ast::Expr::Peek(mem_location) => collect_locals_expr(&mem_location.left, locals),
ast::Expr::Poke {
mem_location,
value,
..
} => {
collect_locals_expr(&mem_location.left, locals);
collect_locals_expr(value, locals);
}
ast::Expr::Variable { .. }
| ast::Expr::I32Const(_)
| ast::Expr::I64Const(_)
| ast::Expr::F32Const(_)
| ast::Expr::F64Const(_) => (),
ast::Expr::UnaryOp { value, .. } => collect_locals_expr(value, locals),
ast::Expr::BinOp { left, right, .. } => {
collect_locals_expr(left, locals);
collect_locals_expr(right, locals);
}
ast::Expr::Branch(_) => (),
ast::Expr::BranchIf { condition, .. } => collect_locals_expr(condition, locals),
ast::Expr::Assign { value, .. } => collect_locals_expr(value, locals),
ast::Expr::LocalTee { value, .. } => collect_locals_expr(value, locals),
ast::Expr::Loop { block, .. } => collect_locals_expr(block, locals),
ast::Expr::LabelBlock { block, .. } => collect_locals_expr(block, locals),
ast::Expr::Cast { value, .. } => collect_locals_expr(value, locals),
ast::Expr::FuncCall { params, .. } => {
for param in params {
collect_locals_expr(param, locals);
}
}
ast::Expr::Select {
condition,
if_true,
if_false,
..
} => {
collect_locals_expr(condition, locals);
collect_locals_expr(if_true, locals);
collect_locals_expr(if_false, locals);
}
ast::Expr::If {
condition,
if_true,
if_false,
} => {
collect_locals_expr(condition, locals);
collect_locals_expr(if_true, locals);
if let Some(if_false) = if_false {
collect_locals_expr(if_false, locals);
}
}
ast::Expr::Return { value: Some(value) } => collect_locals_expr(value, locals),
ast::Expr::Return { value: None } => (),
ast::Expr::First { value, drop } => {
collect_locals_expr(value, locals);
collect_locals_expr(drop, locals);
}
ast::Expr::Error => unreachable!(),
}
}
fn mem_arg_for_location(mem_location: &ast::MemoryLocation) -> MemArg { fn mem_arg_for_location(mem_location: &ast::MemoryLocation) -> MemArg {
let offset = if let ast::Expr::I32Const(v) = mem_location.right.expr { let offset = if let ast::Expr::I32Const(v) = mem_location.right.expr {
v as u32 as u64 v as u32 as u64
@@ -405,19 +367,20 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
} }
ast::Expr::Let { ast::Expr::Let {
value, value,
name,
let_type, let_type,
local_id,
.. ..
} => { } => {
let local = &ctx.locals[local_id.unwrap()];
if let Some(ref value) = value { if let Some(ref value) = value {
match let_type { match let_type {
ast::LetType::Normal => { ast::LetType::Normal => {
emit_expression(ctx, value); emit_expression(ctx, value);
ctx.function ctx.function
.instruction(&Instruction::LocalSet(*ctx.locals.get(name).unwrap())); .instruction(&Instruction::LocalSet(local.index.unwrap()));
} }
ast::LetType::Lazy | ast::LetType::Inline => { ast::LetType::Lazy | ast::LetType::Inline => {
ctx.let_values.insert(name, (value, *let_type)); ctx.let_values.insert(local_id.unwrap(), (value, *let_type));
} }
} }
} }
@@ -599,11 +562,16 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
ast::Expr::F64Const(v) => { ast::Expr::F64Const(v) => {
ctx.function.instruction(&Instruction::F64Const(*v)); ctx.function.instruction(&Instruction::F64Const(*v));
} }
ast::Expr::Assign { name, value, .. } => { ast::Expr::Assign {
name,
value,
local_id,
..
} => {
emit_expression(ctx, value); emit_expression(ctx, value);
if let Some(local_index) = ctx.locals.get(name) { if let &Some(id) = local_id {
ctx.function ctx.function
.instruction(&Instruction::LocalSet(*local_index)); .instruction(&Instruction::LocalSet(ctx.locals[id].index.unwrap()));
} else if let Some(global_index) = ctx.globals.get(name.as_str()) { } else if let Some(global_index) = ctx.globals.get(name.as_str()) {
ctx.function ctx.function
.instruction(&Instruction::GlobalSet(*global_index)); .instruction(&Instruction::GlobalSet(*global_index));
@@ -611,10 +579,13 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
unreachable!(); unreachable!();
} }
} }
ast::Expr::LocalTee { name, value, .. } => { ast::Expr::LocalTee {
value, local_id, ..
} => {
emit_expression(ctx, value); emit_expression(ctx, value);
let index = ctx.locals.get(name).unwrap(); ctx.function.instruction(&Instruction::LocalTee(
ctx.function.instruction(&Instruction::LocalTee(*index)); ctx.locals[local_id.unwrap()].index.unwrap(),
));
} }
ast::Expr::Loop { label, block, .. } => { ast::Expr::Loop { label, block, .. } => {
ctx.labels.push(label.to_string()); ctx.labels.push(label.to_string());
@@ -632,14 +603,15 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
ctx.labels.pop(); ctx.labels.pop();
ctx.function.instruction(&Instruction::End); ctx.function.instruction(&Instruction::End);
} }
ast::Expr::Variable(name) => { ast::Expr::Variable { name, local_id } => {
if let Some(index) = ctx.locals.get(name) { if let &Some(id) = local_id {
if let Some((expr, let_type)) = ctx.let_values.get(name.as_str()) { if let Some((expr, let_type)) = ctx.let_values.get(&id) {
match let_type { match let_type {
ast::LetType::Lazy => { ast::LetType::Lazy => {
let expr = ctx.let_values.remove(name.as_str()).unwrap().0; let expr = ctx.let_values.remove(&id).unwrap().0;
emit_expression(ctx, expr); emit_expression(ctx, expr);
ctx.function.instruction(&Instruction::LocalTee(*index)); ctx.function
.instruction(&Instruction::LocalTee(ctx.locals[id].index.unwrap()));
} }
ast::LetType::Inline => { ast::LetType::Inline => {
let expr = *expr; let expr = *expr;
@@ -648,7 +620,8 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
_ => unreachable!(), _ => unreachable!(),
} }
} else { } else {
ctx.function.instruction(&Instruction::LocalGet(*index)); ctx.function
.instruction(&Instruction::LocalGet(ctx.locals[id].index.unwrap()));
} }
} else if let Some(index) = ctx.globals.get(name.as_str()) { } else if let Some(index) = ctx.globals.get(name.as_str()) {
ctx.function.instruction(&Instruction::GlobalGet(*index)); ctx.function.instruction(&Instruction::GlobalGet(*index));

View File

@@ -1,26 +1,41 @@
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use std::ffi::OsStr;
use std::io::prelude::*; use std::io::prelude::*;
use std::{fs::File, path::Path}; use std::{fs::File, path::Path};
mod ast; mod ast;
mod constfold; mod constfold;
mod emit; mod emit;
mod includes;
mod intrinsics; mod intrinsics;
mod parser; mod parser;
mod typecheck; mod typecheck;
mod includes;
type Span = std::ops::Range<usize>; type Span = std::ops::Range<usize>;
pub fn compile_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> { #[derive(Default)]
pub struct Options {
pub(crate) debug: bool,
}
impl Options {
pub fn with_debug(self) -> Self {
Options {
debug: true,
..self
}
}
}
pub fn compile_file<P: AsRef<Path>>(path: P, options: Options) -> Result<Vec<u8>> {
let path = path.as_ref(); let path = path.as_ref();
let mut input = String::new(); let mut input = String::new();
File::open(path)?.read_to_string(&mut input)?; File::open(path)?.read_to_string(&mut input)?;
compile_str(&input, path) compile_str(&input, path, options)
} }
pub fn compile_str(input: &str, path: &Path) -> Result<Vec<u8>> { pub fn compile_str(input: &str, path: &Path, options: Options) -> Result<Vec<u8>> {
let mut script = match parser::parse(&input) { let mut script = match parser::parse(&input) {
Ok(script) => script, Ok(script) => script,
Err(_) => bail!("Parse failed"), Err(_) => bail!("Parse failed"),
@@ -32,6 +47,13 @@ pub fn compile_str(input: &str, path: &Path) -> Result<Vec<u8>> {
if let Err(_) = typecheck::tc_script(&mut script, &input) { if let Err(_) = typecheck::tc_script(&mut script, &input) {
bail!("Type check failed"); bail!("Type check failed");
} }
let wasm = emit::emit(&script); let wasm = emit::emit(
&script,
&path
.file_stem()
.unwrap_or_else(|| OsStr::new("unknown"))
.to_string_lossy(),
&options,
);
Ok(wasm) Ok(wasm)
} }

View File

@@ -1,17 +1,21 @@
use anyhow::{anyhow, Result}; use anyhow::Result;
use std::io::prelude::*; use std::io::prelude::*;
use std::{fs::File, path::PathBuf}; use std::{fs::File, path::PathBuf};
use curlywas::compile_file; use curlywas::{compile_file, Options};
fn main() -> Result<()> { fn main() -> Result<()> {
let mut filename = PathBuf::from( let mut args = pico_args::Arguments::from_env();
std::env::args()
.nth(1)
.ok_or_else(|| anyhow!("Path to .hw file missing"))?,
);
let wasm = compile_file(&filename)?; let mut options = Options::default();
if args.contains(["-d", "--debug"]) {
options = options.with_debug();
}
let mut filename = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
let wasm = compile_file(&filename, options)?;
wasmparser::validate(&wasm)?; wasmparser::validate(&wasm)?;

View File

@@ -309,7 +309,10 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.labelled("value"); .labelled("value");
let variable = filter_map(|span, tok| match tok { let variable = filter_map(|span, tok| match tok {
Token::Ident(id) => Ok(ast::Expr::Variable(id)), Token::Ident(id) => Ok(ast::Expr::Variable {
name: id,
local_id: None,
}),
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))), _ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))),
}) })
.labelled("variable"); .labelled("variable");
@@ -319,6 +322,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.map(|(name, expr)| ast::Expr::LocalTee { .map(|(name, expr)| ast::Expr::LocalTee {
name, name,
value: Box::new(expr), value: Box::new(expr),
local_id: None,
}) })
.boxed(); .boxed();
@@ -385,6 +389,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
type_, type_,
value: value.map(Box::new), value: value.map(Box::new),
let_type: let_type.unwrap_or(ast::LetType::Normal), let_type: let_type.unwrap_or(ast::LetType::Normal),
local_id: None,
}) })
.boxed(); .boxed();
@@ -395,6 +400,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.map(|(name, value)| ast::Expr::Assign { .map(|(name, value)| ast::Expr::Assign {
name, name,
value: Box::new(value), value: Box::new(value),
local_id: None,
}) })
.boxed(); .boxed();
@@ -807,6 +813,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
name, name,
type_, type_,
body, body,
locals: ast::Locals::default(),
}) })
}) })
.boxed(); .boxed();

View File

@@ -20,7 +20,8 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
source, source,
global_vars: HashMap::new(), global_vars: HashMap::new(),
functions: HashMap::new(), functions: HashMap::new(),
local_vars: HashMap::new(), locals: ast::Locals::default(),
local_vars: LocalVars::new(),
block_stack: Vec::new(), block_stack: Vec::new(),
return_type: None, return_type: None,
intrinsics: Intrinsics::new(), intrinsics: Intrinsics::new(),
@@ -122,22 +123,22 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
for f in &mut script.functions { for f in &mut script.functions {
context.local_vars.clear(); context.local_vars.clear();
context.local_vars.push_scope();
for (name, type_) in &f.params { for (name, type_) in &f.params {
if let Some(Var { span, .. }) = context if let Some(span) = context
.local_vars .local_vars
.get(name) .get(name)
.or_else(|| context.global_vars.get(name)) .map(|id| &context.locals[id].span)
.or_else(|| context.global_vars.get(name).map(|v| &v.span))
{ {
result = result =
report_duplicate_definition("Variable already defined", &f.span, span, source); report_duplicate_definition("Variable already defined", &f.span, span, source);
} else { } else {
context.local_vars.insert( context.local_vars.insert(
name.clone(), name.clone(),
Var { context
type_: *type_, .locals
span: f.span.clone(), .add_param(f.span.clone(), name.clone(), *type_),
mutable: true,
},
); );
} }
} }
@@ -145,6 +146,22 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
tc_expression(&mut context, &mut f.body)?; tc_expression(&mut context, &mut f.body)?;
let mut local_mapping: Vec<(ast::Type, usize)> = context
.locals
.locals
.iter()
.enumerate()
.filter(|(_, local)| local.index.is_some())
.map(|(index, local)| (local.type_, index))
.collect();
local_mapping.sort_by_key(|&(t, _)| t);
let locals_start = context.locals.params.len();
for (id, (_, index)) in local_mapping.into_iter().enumerate() {
context.locals.locals[index].index = Some((locals_start + id) as u32);
}
f.locals = std::mem::replace(&mut context.locals, ast::Locals::default());
if f.body.type_ != f.type_ { if f.body.type_ != f.type_ {
result = type_mismatch(f.type_, &f.span, f.body.type_, &f.body.span, source); result = type_mismatch(f.type_, &f.span, f.body.type_, &f.body.span, source);
} }
@@ -233,12 +250,50 @@ struct Context<'a> {
source: &'a str, source: &'a str,
global_vars: Vars, global_vars: Vars,
functions: HashMap<String, FunctionType>, functions: HashMap<String, FunctionType>,
local_vars: Vars, locals: ast::Locals,
local_vars: LocalVars,
block_stack: Vec<String>, block_stack: Vec<String>,
return_type: Option<ast::Type>, return_type: Option<ast::Type>,
intrinsics: Intrinsics, intrinsics: Intrinsics,
} }
struct LocalVars(Vec<HashMap<String, u32>>);
impl LocalVars {
fn new() -> LocalVars {
LocalVars(Vec::new())
}
fn get(&self, name: &str) -> Option<u32> {
self.0
.iter()
.rev()
.filter_map(|scope| scope.get(name))
.next()
.copied()
}
fn get_in_current(&self, name: &str) -> Option<u32> {
self.0.last().unwrap().get(name).copied()
}
fn clear(&mut self) {
self.0.clear();
}
fn push_scope(&mut self) {
self.0.push(HashMap::new());
}
fn pop_scope(&mut self) {
self.0.pop();
}
fn insert(&mut self, name: String, id: u32) {
self.0.last_mut().unwrap().insert(name, id);
}
}
fn report_duplicate_definition( fn report_duplicate_definition(
msg: &str, msg: &str,
span: &Span, span: &Span,
@@ -360,20 +415,25 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
ref mut statements, ref mut statements,
ref mut final_expression, ref mut final_expression,
} => { } => {
context.local_vars.push_scope();
for stmt in statements { for stmt in statements {
tc_expression(context, stmt)?; tc_expression(context, stmt)?;
} }
if let Some(final_expression) = final_expression { let type_ = if let Some(final_expression) = final_expression {
tc_expression(context, final_expression)?; tc_expression(context, final_expression)?;
final_expression.type_ final_expression.type_
} else { } else {
None None
} };
context.local_vars.pop_scope();
type_
} }
ast::Expr::Let { ast::Expr::Let {
ref mut value, ref mut value,
ref mut type_, ref mut type_,
ref name, ref name,
let_type,
ref mut local_id,
.. ..
} => { } => {
if let Some(ref mut value) = value { if let Some(ref mut value) = value {
@@ -395,26 +455,21 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
} }
} }
if let Some(type_) = type_ { if let Some(type_) = type_ {
if let Some(Var { span, .. }) = context let store = let_type != ast::LetType::Inline;
let id = context
.local_vars .local_vars
.get(name) .get_in_current(name)
.or_else(|| context.global_vars.get(name)) .filter(|id| {
{ let local = &context.locals[*id];
return report_duplicate_definition( local.type_ == *type_ && store == local.index.is_some()
"Variable already defined", })
&expr.span, .unwrap_or_else(|| {
span, context
context.source, .locals
); .add_local(expr.span.clone(), name.clone(), *type_, store)
} });
context.local_vars.insert( *local_id = Some(id);
name.clone(), context.local_vars.insert(name.clone(), id);
Var {
type_: *type_,
span: expr.span.clone(),
mutable: true,
},
);
} else { } else {
Report::build(ReportKind::Error, (), expr.span.start) Report::build(ReportKind::Error, (), expr.span.start)
.with_message("Type missing") .with_message("Type missing")
@@ -528,12 +583,14 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
} }
} }
} }
ast::Expr::Variable(ref name) => { ast::Expr::Variable {
if let Some(&Var { type_, .. }) = context ref name,
.global_vars ref mut local_id,
.get(name) } => {
.or_else(|| context.local_vars.get(name)) if let Some(id) = context.local_vars.get(name) {
{ *local_id = Some(id);
Some(context.locals[id].type_)
} else if let Some(&Var { type_, .. }) = context.global_vars.get(name) {
Some(type_) Some(type_)
} else { } else {
return unknown_variable(&expr.span, context.source); return unknown_variable(&expr.span, context.source);
@@ -542,58 +599,61 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
ast::Expr::Assign { ast::Expr::Assign {
ref name, ref name,
ref mut value, ref mut value,
ref mut local_id,
} => { } => {
tc_expression(context, value)?; tc_expression(context, value)?;
if let Some(&Var {
let (type_, span) = if let Some(id) = context.local_vars.get(name) {
*local_id = Some(id);
let local = &context.locals[id];
if local.index.is_none() {
return immutable_assign(&expr.span, context.source);
}
(local.type_, &local.span)
} else if let Some(&Var {
type_, type_,
ref span, ref span,
mutable, mutable,
}) = context }) = context.global_vars.get(name)
.local_vars
.get(name)
.or_else(|| context.global_vars.get(name))
{ {
if value.type_ != Some(type_) {
return type_mismatch(
Some(type_),
span,
value.type_,
&value.span,
context.source,
);
}
if !mutable { if !mutable {
return immutable_assign(&expr.span, context.source); return immutable_assign(&expr.span, context.source);
} }
(type_, span)
} else { } else {
return unknown_variable(&expr.span, context.source); return unknown_variable(&expr.span, context.source);
};
if value.type_ != Some(type_) {
return type_mismatch(Some(type_), span, value.type_, &value.span, context.source);
} }
None None
} }
ast::Expr::LocalTee { ast::Expr::LocalTee {
ref name, ref name,
ref mut value, ref mut value,
ref mut local_id,
} => { } => {
tc_expression(context, value)?; tc_expression(context, value)?;
if let Some(&Var { if let Some(id) = context.local_vars.get(name) {
type_, *local_id = Some(id);
ref span, let local = &context.locals[id];
mutable,
}) = context.local_vars.get(name) if local.index.is_none() {
{ return immutable_assign(&expr.span, context.source);
if value.type_ != Some(type_) { }
if value.type_ != Some(local.type_) {
return type_mismatch( return type_mismatch(
Some(type_), Some(local.type_),
span, &local.span,
value.type_, value.type_,
&value.span, &value.span,
context.source, context.source,
); );
} }
if !mutable {
return immutable_assign(&expr.span, context.source); Some(local.type_)
}
Some(type_)
} else { } else {
return unknown_variable(&expr.span, context.source); return unknown_variable(&expr.span, context.source);
} }