Compare commits

...

7 Commits

Author SHA1 Message Date
David Nadlinger b7ee07d7f1 core/builtins: Simplify vector of None creation [nfc] 2022-04-22 23:50:00 +01:00
David Nadlinger 188208b959 core/typecheck: Implement unification for scalar indexing of ndarrays 2022-04-22 22:46:24 +01:00
David Nadlinger 164edd266e core/typecheck: Explicitly give errors on "advanced" (subset) indexing 2022-04-22 22:28:59 +01:00
David Nadlinger c74b7992f6 core/typecheck: Basic ndarray indexing support 2022-04-22 21:56:35 +01:00
David Nadlinger 72cb693e2e core/typecheck: First btis of NumPy-like array type inference
For readability of the codebase, I chose ndarray for the name of the
type, while [numpy.]array() is the name of the most commonly used
constructor.
2022-04-22 21:56:35 +01:00
David Nadlinger 8454741f9e cargo fmt the world 2022-04-22 21:56:35 +01:00
David Nadlinger 2e6fc4cfe5 [tmp] nix: Switch to aarch64-darwin 2022-04-22 21:56:21 +01:00
41 changed files with 1527 additions and 1444 deletions

View File

@ -5,9 +5,9 @@
outputs = { self, nixpkgs }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; };
pkgs = import nixpkgs { system = "aarch64-darwin"; };
in rec {
packages.x86_64-linux = rec {
packages.aarch64-darwin = rec {
llvm-nac3 = pkgs.callPackage ./nix/llvm {};
nac3artiq = pkgs.python3Packages.toPythonModule (
pkgs.rustPlatform.buildRustPackage rec {
@ -142,25 +142,26 @@
packages.x86_64-w64-mingw32 = import ./nix/windows { inherit pkgs; };
devShell.x86_64-linux = pkgs.mkShell {
devShell.aarch64-darwin = pkgs.mkShell {
name = "nac3-dev-shell";
buildInputs = with pkgs; [
# build dependencies
packages.x86_64-linux.llvm-nac3
packages.aarch64-darwin.llvm-nac3
llvmPackages_13.clang-unwrapped # IRRT
pkgs.llvmPackages_13.llvm.out # IRRT
libiconv
cargo
rustc
# runtime dependencies
lld_13
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ]))
(packages.aarch64-darwin.python3-mimalloc.withPackages(ps: [ ps.numpy ]))
# development tools
cargo-insta
clippy
rustfmt
];
};
devShells.x86_64-linux.msys2 = pkgs.mkShell {
devShells.aarch64-darwin.msys2 = pkgs.mkShell {
name = "nac3-dev-shell-msys2";
buildInputs = with pkgs; [
curl
@ -171,7 +172,7 @@
};
hydraJobs = {
inherit (packages.x86_64-linux) llvm-nac3 nac3artiq nac3artiq-pgo;
inherit (packages.aarch64-darwin) llvm-nac3 nac3artiq nac3artiq-pgo;
llvm-nac3-msys2 = packages.x86_64-w64-mingw32.llvm-nac3;
nac3artiq-msys2 = packages.x86_64-w64-mingw32.nac3artiq;
nac3artiq-msys2-pkg = packages.x86_64-w64-mingw32.nac3artiq-pkg;

View File

@ -19,7 +19,7 @@ nac3core = { path = "../nac3core" }
git = "https://github.com/nbaksalyar/inkwell.git"
branch = "llvm14"
default-features = false
features = ["llvm14-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
features = ["llvm14-0", "target-aarch64", "target-arm", "target-riscv", "no-libffi-linking"]
[features]
init-llvm-profile = []

View File

@ -6,7 +6,7 @@ use nac3core::{
},
symbol_resolver::ValueEnum,
toplevel::{DefinitionId, GenCall},
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum}
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum},
};
use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef};
@ -15,7 +15,10 @@ use inkwell::{
context::Context, module::Linkage, types::IntType, values::BasicValueEnum, AddressSpace,
};
use pyo3::{PyObject, PyResult, Python, types::{PyDict, PyList}};
use pyo3::{
types::{PyDict, PyList},
PyObject, PyResult, Python,
};
use crate::{symbol_resolver::InnerResolver, timeline::TimeFns};
@ -68,7 +71,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let result = gen_call(self, ctx, obj, fun, params)?;
if let Some(end) = self.end.clone() {
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(ctx, self, end.custom.unwrap())?;
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(
ctx,
self,
end.custom.unwrap(),
)?;
let now = self.timeline.emit_now_mu(ctx);
let smax = ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
let i64 = ctx.ctx.i64_type();
@ -88,7 +95,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
ctx.builder.build_store(end_store, max);
}
if let Some(start) = self.start.clone() {
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(ctx, self, start.custom.unwrap())?;
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(
ctx,
self,
start.custom.unwrap(),
)?;
self.timeline.emit_at_mu(ctx, start_val);
}
Ok(result)
@ -120,7 +131,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let old_start = self.start.take();
let old_end = self.end.take();
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(
ctx,
self,
old_start.custom.unwrap(),
)?
} else {
self.timeline.emit_now_mu(ctx)
};
@ -174,10 +189,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
};
// set duration
let end_expr = self.end.take().unwrap();
let end_val = self
.gen_expr(ctx, &end_expr)?
.unwrap()
.to_basic_value_enum(ctx, self, end_expr.custom.unwrap())?;
let end_val = self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(
ctx,
self,
end_expr.custom.unwrap(),
)?;
// inside a sequential block
if old_start.is_none() {
@ -293,7 +309,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
let int32 = ctx.ctx.i32_type();
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
let service_id = int32.const_int(fun.1.0 as u64, false);
let service_id = int32.const_int(fun.1 .0 as u64, false);
// -- setup rpc tags
let mut tag = Vec::new();
if obj.is_some() {
@ -363,10 +379,8 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
}
// default value handling
for k in keys.into_iter() {
mapping.insert(
k.name,
ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty).into()
);
mapping
.insert(k.name, ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty).into());
}
// reorder the parameters
let mut real_params = fun
@ -507,9 +521,15 @@ pub fn attributes_writeback<'ctx, 'a>(
let mut scratch_buffer = Vec::new();
for (_, val) in globals.iter() {
let val = val.as_ref(py);
let ty = inner_resolver.get_obj_type(py, val, &mut ctx.unifier, &top_levels, &ctx.primitives)?;
let ty = inner_resolver.get_obj_type(
py,
val,
&mut ctx.unifier,
&top_levels,
&ctx.primitives,
)?;
if let Err(ty) = ty {
return Ok(Err(ty))
return Ok(Err(ty));
}
let ty = ty.unwrap();
match &*ctx.unifier.get_ty(ty) {
@ -522,14 +542,18 @@ pub fn attributes_writeback<'ctx, 'a>(
let obj = inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap();
for (name, (field_ty, is_mutable)) in fields.iter() {
if !is_mutable {
continue
continue;
}
if gen_rpc_tag(ctx, *field_ty, &mut scratch_buffer).is_ok() {
attributes.push(name.to_string());
let index = ctx.get_attr_index(ty, *name);
values.push((*field_ty, ctx.build_gep_and_load(
obj.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)])));
values.push((
*field_ty,
ctx.build_gep_and_load(
obj.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)],
),
));
}
}
if !attributes.is_empty() {
@ -538,33 +562,43 @@ pub fn attributes_writeback<'ctx, 'a>(
pydict.set_item("fields", attributes)?;
host_attributes.append(pydict)?;
}
},
}
TypeEnum::TList { ty: elem_ty } => {
if gen_rpc_tag(ctx, *elem_ty, &mut scratch_buffer).is_ok() {
let pydict = PyDict::new(py);
pydict.set_item("obj", val)?;
host_attributes.append(pydict)?;
values.push((ty, inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap()));
values.push((
ty,
inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap(),
));
}
},
}
_ => {}
}
}
let fun = FunSignature {
args: values.iter().enumerate().map(|(i, (ty, _))| FuncArg {
name: i.to_string().into(),
ty: *ty,
default_value: None
}).collect(),
args: values
.iter()
.enumerate()
.map(|(i, (ty, _))| FuncArg {
name: i.to_string().into(),
ty: *ty,
default_value: None,
})
.collect(),
ret: ctx.primitives.none,
vars: Default::default()
vars: Default::default(),
};
let args: Vec<_> = values.into_iter().map(|(_, val)| (None, ValueEnum::Dynamic(val))).collect();
if let Err(e) = rpc_codegen_callback_fn(ctx, None, (&fun, DefinitionId(0)), args, generator) {
let args: Vec<_> =
values.into_iter().map(|(_, val)| (None, ValueEnum::Dynamic(val))).collect();
if let Err(e) = rpc_codegen_callback_fn(ctx, None, (&fun, DefinitionId(0)), args, generator)
{
return Ok(Err(e));
}
Ok(Ok(()))
}).unwrap()?;
})
.unwrap()?;
Ok(())
}

View File

@ -17,9 +17,9 @@ use nac3parser::{
ast::{self, ExprKind, Stmt, StmtKind, StrRef},
parser::{self, parse_program},
};
use pyo3::create_exception;
use pyo3::prelude::*;
use pyo3::{exceptions, types::PyBytes, types::PyDict, types::PySet};
use pyo3::create_exception;
use parking_lot::{Mutex, RwLock};
@ -40,7 +40,7 @@ use tempfile::{self, TempDir};
use crate::codegen::attributes_writeback;
use crate::{
codegen::{rpc_codegen_callback, ArtiqCodeGenerator},
symbol_resolver::{InnerResolver, PythonHelper, Resolver, DeferredEvaluationStore},
symbol_resolver::{DeferredEvaluationStore, InnerResolver, PythonHelper, Resolver},
};
mod codegen;
@ -93,7 +93,7 @@ struct Nac3 {
top_levels: Vec<TopLevelComponent>,
string_store: Arc<RwLock<HashMap<String, i32>>>,
exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
deferred_eval_store: DeferredEvaluationStore
deferred_eval_store: DeferredEvaluationStore,
}
create_exception!(nac3artiq, CompileError, exceptions::PyException);
@ -268,7 +268,7 @@ fn add_exceptions(
composer: &mut TopLevelComposer,
builtin_def: &mut HashMap<StrRef, DefinitionId>,
builtin_ty: &mut HashMap<StrRef, Type>,
error_names: &[&str]
error_names: &[&str],
) -> Vec<Type> {
let mut types = Vec::new();
// note: this is only for builtin exceptions, i.e. the exception name is "0:{exn}"
@ -281,7 +281,7 @@ fn add_exceptions(
// constructor id
def_id + 1,
&mut composer.unifier,
&composer.primitives_ty
&composer.primitives_ty,
);
composer.definition_ast_list.push((Arc::new(RwLock::new(exception_class)), None));
composer.definition_ast_list.push((Arc::new(RwLock::new(exception_fn)), None));
@ -331,7 +331,8 @@ impl Nac3 {
},
Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
let arg =
args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
time_fns.emit_at_mu(ctx, arg);
Ok(None)
}))),
@ -349,7 +350,8 @@ impl Nac3 {
},
Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
let arg =
args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
time_fns.emit_delay_mu(ctx, arg);
Ok(None)
}))),
@ -363,18 +365,19 @@ impl Nac3 {
let types_mod = PyModule::import(py, "types").unwrap();
let get_id = |x| id_fn.call1((x,)).unwrap().extract().unwrap();
let get_attr_id = |obj: &PyModule, attr| id_fn.call1((obj.getattr(attr).unwrap(),))
.unwrap().extract().unwrap();
let get_attr_id = |obj: &PyModule, attr| {
id_fn.call1((obj.getattr(attr).unwrap(),)).unwrap().extract().unwrap()
};
let primitive_ids = PrimitivePythonId {
virtual_id: get_id(
builtins_mod
.getattr("globals")
.unwrap()
.call0()
.unwrap()
.get_item("virtual")
.unwrap(
)),
builtins_mod
.getattr("globals")
.unwrap()
.call0()
.unwrap()
.get_item("virtual")
.unwrap(),
),
generic_alias: (
get_attr_id(typing_mod, "_GenericAlias"),
get_attr_id(types_mod, "GenericAlias"),
@ -519,8 +522,9 @@ impl Nac3 {
let class_obj;
if let StmtKind::ClassDef { name, .. } = &stmt.node {
let class = py_module.getattr(name.to_string()).unwrap();
if issubclass.call1((class, exn_class)).unwrap().extract().unwrap() &&
class.getattr("artiq_builtin").is_err() {
if issubclass.call1((class, exn_class)).unwrap().extract().unwrap()
&& class.getattr("artiq_builtin").is_err()
{
class_obj = Some(class);
} else {
class_obj = None;
@ -566,13 +570,12 @@ impl Nac3 {
let (name, def_id, ty) = composer
.register_top_level(stmt.clone(), Some(resolver.clone()), path.clone())
.map_err(|e| {
CompileError::new_err(format!(
"compilation failed\n----------\n{}",
e
))
CompileError::new_err(format!("compilation failed\n----------\n{}", e))
})?;
if let Some(class_obj) = class_obj {
self.exception_ids.write().insert(def_id.0, store_obj.call1(py, (class_obj, ))?.extract(py)?);
self.exception_ids
.write()
.insert(def_id.0, store_obj.call1(py, (class_obj,))?.extract(py)?);
}
match &stmt.node {
@ -642,7 +645,8 @@ impl Nac3 {
exception_ids: self.exception_ids.clone(),
deferred_eval_store: self.deferred_eval_store.clone(),
});
let resolver = Arc::new(Resolver(inner_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
let resolver =
Arc::new(Resolver(inner_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
let (_, def_id, _) = composer
.register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into())
.unwrap();
@ -651,8 +655,12 @@ impl Nac3 {
FunSignature { args: vec![], ret: self.primitive.none, vars: HashMap::new() };
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature =
store.from_signature(&mut composer.unifier, &self.primitive, &fun_signature, &mut cache);
let signature = store.from_signature(
&mut composer.unifier,
&self.primitive,
&fun_signature,
&mut cache,
);
let signature = store.add_cty(signature);
if let Err(e) = composer.start_analysis(true) {
@ -738,8 +746,12 @@ impl Nac3 {
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature =
store.from_signature(&mut composer.unifier, &self.primitive, &fun_signature, &mut cache);
let signature = store.from_signature(
&mut composer.unifier,
&self.primitive,
&fun_signature,
&mut cache,
);
let signature = store.add_cty(signature);
let attributes_writeback_task = CodeGenTask {
subst: Default::default(),
@ -777,14 +789,23 @@ impl Nac3 {
registry.add_task(task);
registry.wait_tasks_complete(handles);
let mut generator = ArtiqCodeGenerator::new("attributes_writeback".to_string(), size_t, self.time_fns);
let mut generator =
ArtiqCodeGenerator::new("attributes_writeback".to_string(), size_t, self.time_fns);
let context = inkwell::context::Context::create();
let module = context.create_module("attributes_writeback");
let builder = context.create_builder();
let (_, module, _) = gen_func_impl(&context, &mut generator, &registry, builder, module,
attributes_writeback_task, |generator, ctx| {
let (_, module, _) = gen_func_impl(
&context,
&mut generator,
&registry,
builder,
module,
attributes_writeback_task,
|generator, ctx| {
attributes_writeback(ctx, generator, inner_resolver.as_ref(), host_attributes)
}).unwrap();
},
)
.unwrap();
let buffer = module.write_bitcode_to_memory();
let buffer = buffer.as_slice().into();
membuffer.lock().push(buffer);
@ -800,13 +821,22 @@ impl Nac3 {
.create_module_from_ir(MemoryBuffer::create_from_memory_range(buffer, "main"))
.unwrap();
main.link_in_module(other)
.map_err(|err| CompileError::new_err(err.to_string()))?;
main.link_in_module(other).map_err(|err| CompileError::new_err(err.to_string()))?;
}
let builder = context.create_builder();
let modinit_return = main.get_function("__modinit__").unwrap().get_last_basic_block().unwrap().get_terminator().unwrap();
let modinit_return = main
.get_function("__modinit__")
.unwrap()
.get_last_basic_block()
.unwrap()
.get_terminator()
.unwrap();
builder.position_before(&modinit_return);
builder.build_call(main.get_function("attributes_writeback").unwrap(), &[], "attributes_writeback");
builder.build_call(
main.get_function("attributes_writeback").unwrap(),
&[],
"attributes_writeback",
);
main.link_in_module(load_irrt(&context))
.map_err(|err| CompileError::new_err(err.to_string()))?;
@ -880,9 +910,7 @@ impl Nac3 {
return Err(CompileError::new_err("failed to start linker"));
}
} else {
return Err(CompileError::new_err(
"linker returned non-zero status code",
));
return Err(CompileError::new_err("linker returned non-zero status code"));
}
Ok(())

View File

@ -17,9 +17,9 @@ use pyo3::{
use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering::Relaxed},
Arc,
atomic::{AtomicBool, Ordering::Relaxed}
}
},
};
use crate::PrimitivePythonId;
@ -153,7 +153,8 @@ impl StaticValue for PythonValue {
self.resolver
.get_obj_value(py, self.value.as_ref(py), ctx, generator, expected_ty)
.map(Option::unwrap)
}).map_err(|e| e.to_string())
})
.map_err(|e| e.to_string())
}
fn get_field<'ctx, 'a>(
@ -175,9 +176,9 @@ impl StaticValue for PythonValue {
let obj = self.value.getattr(py, &name.to_string())?;
let id = self.resolver.helper.id_fn.call1(py, (&obj,))?.extract(py)?;
if self.id == self.resolver.primitive_ids.none {
return Ok(None)
return Ok(None);
} else {
return Ok(Some((id, obj)))
return Ok(Some((id, obj)));
}
}
let def_id = { *self.resolver.pyid_to_def.read().get(&ty_id).unwrap() };
@ -384,11 +385,11 @@ impl InnerResolver {
}
}
if needs_defer {
self.deferred_eval_store.store.write()
.push((result.clone(),
constraints.extract()?,
pyty.getattr("__name__")?.extract::<String>()?
))
self.deferred_eval_store.store.write().push((
result.clone(),
constraints.extract()?,
pyty.getattr("__name__")?.extract::<String>()?,
))
}
result
};
@ -531,10 +532,7 @@ impl InnerResolver {
let str_fn =
pyo3::types::PyModule::import(py, "builtins").unwrap().getattr("repr").unwrap();
let str_repr: String = str_fn.call1((pyty,)).unwrap().extract().unwrap();
Ok(Err(format!(
"{} is not registered with NAC3 (@nac3 decorator missing?)",
str_repr
)))
Ok(Err(format!("{} is not registered with NAC3 (@nac3 decorator missing?)", str_repr)))
}
}
@ -549,7 +547,7 @@ impl InnerResolver {
let ty = self.helper.type_fn.call1(py, (obj,)).unwrap();
let py_obj_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if let Some(ty) = self.pyid_to_type.read().get(&py_obj_id) {
return Ok(Ok(*ty))
return Ok(Ok(*ty));
}
let (extracted_ty, inst_check) = match self.get_pyty_obj_type(
py,
@ -616,7 +614,7 @@ impl InnerResolver {
let field_data = match obj.getattr("_nac3_option") {
Ok(d) => d,
// we use `none = Option(None)`, so the obj always have attr `_nac3_option`
Err(_) => unreachable!("cannot be None")
Err(_) => unreachable!("cannot be None"),
};
// if is `none`
let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
@ -627,7 +625,9 @@ impl InnerResolver {
let var_map = params
.iter()
.map(|(id_var, ty)| {
if let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(*ty) {
if let TypeEnum::TVar { id, range, name, loc, .. } =
&*unifier.get_ty(*ty)
{
assert_eq!(*id, *id_var);
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
} else {
@ -635,7 +635,7 @@ impl InnerResolver {
}
})
.collect::<HashMap<_, _>>();
return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap()))
return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap()));
} else {
unreachable!("must be tobj")
}
@ -659,9 +659,7 @@ impl InnerResolver {
let var_map = params
.iter()
.map(|(id_var, ty)| {
if let TypeEnum::TVar { id, range, name, loc, .. } =
&*unifier.get_ty(*ty)
{
if let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(*ty) {
assert_eq!(*id, *id_var);
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
} else {
@ -673,7 +671,7 @@ impl InnerResolver {
// loop through non-function fields of the class to get the instantiated value
for field in fields.iter() {
let name: String = (*field.0).into();
if let TypeEnum::TFunc(..) = &*unifier.get_ty(field.1.0) {
if let TypeEnum::TFunc(..) = &*unifier.get_ty(field.1 .0) {
continue;
} else {
let field_data = obj.getattr(&name)?;
@ -689,7 +687,7 @@ impl InnerResolver {
}
};
let field_ty =
unifier.subst(field.1.0, &var_map).unwrap_or(field.1.0);
unifier.subst(field.1 .0, &var_map).unwrap_or(field.1 .0);
if let Err(e) = unifier.unify(ty, field_ty) {
// field type mismatch
return Ok(Err(format!(
@ -706,14 +704,15 @@ impl InnerResolver {
return Ok(Err("object is not of concrete type".into()));
}
}
let extracted_ty = unifier.subst(extracted_ty, &var_map).unwrap_or(extracted_ty);
let extracted_ty =
unifier.subst(extracted_ty, &var_map).unwrap_or(extracted_ty);
Ok(Ok(extracted_ty))
};
let result = instantiate_obj();
// update/remove the cache according to the result
match result {
Ok(Ok(ty)) => self.pyid_to_type.write().insert(py_obj_id, ty),
_ => self.pyid_to_type.write().remove(&py_obj_id)
_ => self.pyid_to_type.write().remove(&py_obj_id),
};
result
}
@ -722,32 +721,32 @@ impl InnerResolver {
if unifier.unioned(extracted_ty, primitives.int32) {
obj.extract::<i32>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of int32", obj))),
|_| Ok(Ok(extracted_ty))
|_| Ok(Ok(extracted_ty)),
)
} else if unifier.unioned(extracted_ty, primitives.int64) {
obj.extract::<i64>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of int64", obj))),
|_| Ok(Ok(extracted_ty))
|_| Ok(Ok(extracted_ty)),
)
} else if unifier.unioned(extracted_ty, primitives.uint32) {
obj.extract::<u32>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of uint32", obj))),
|_| Ok(Ok(extracted_ty))
|_| Ok(Ok(extracted_ty)),
)
} else if unifier.unioned(extracted_ty, primitives.uint64) {
obj.extract::<u64>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of uint64", obj))),
|_| Ok(Ok(extracted_ty))
|_| Ok(Ok(extracted_ty)),
)
} else if unifier.unioned(extracted_ty, primitives.bool) {
obj.extract::<bool>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of bool", obj))),
|_| Ok(Ok(extracted_ty))
|_| Ok(Ok(extracted_ty)),
)
} else if unifier.unioned(extracted_ty, primitives.float) {
obj.extract::<f64>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of float64", obj))),
|_| Ok(Ok(extracted_ty))
|_| Ok(Ok(extracted_ty)),
)
} else {
Ok(Ok(extracted_ty))
@ -799,8 +798,8 @@ impl InnerResolver {
}
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
let elem_ty =
if let TypeEnum::TList { ty } = ctx.unifier.get_ty_immutable(expected_ty).as_ref()
let elem_ty = if let TypeEnum::TList { ty } =
ctx.unifier.get_ty_immutable(expected_ty).as_ref()
{
*ty
} else {
@ -825,13 +824,14 @@ impl InnerResolver {
let arr: Result<Option<Vec<_>>, _> = (0..len)
.map(|i| {
obj
.get_item(i)
.and_then(|elem| self.get_obj_value(py, elem, ctx, generator, elem_ty)
.map_err(
|e| super::CompileError::new_err(
format!("Error getting element {}: {}", i, e))
))
obj.get_item(i).and_then(|elem| {
self.get_obj_value(py, elem, ctx, generator, elem_ty).map_err(|e| {
super::CompileError::new_err(format!(
"Error getting element {}: {}",
i, e
))
})
})
})
.collect();
let arr = arr?.unwrap();
@ -876,18 +876,19 @@ impl InnerResolver {
let tup_tys = ty.iter();
let elements: &PyTuple = obj.cast_as()?;
assert_eq!(elements.len(), tup_tys.len());
let val: Result<Option<Vec<_>>, _> =
elements
.iter()
.enumerate()
.zip(tup_tys)
.map(|((i, elem), ty)| self
.get_obj_value(py, elem, ctx, generator, *ty).map_err(|e|
super::CompileError::new_err(
format!("Error getting element {}: {}", i, e)
)
)
).collect();
let val: Result<Option<Vec<_>>, _> = elements
.iter()
.enumerate()
.zip(tup_tys)
.map(|((i, elem), ty)| {
self.get_obj_value(py, elem, ctx, generator, *ty).map_err(|e| {
super::CompileError::new_err(format!(
"Error getting element {}: {}",
i, e
))
})
})
.collect();
let val = val?.unwrap();
let val = ctx.ctx.const_struct(&val, false);
Ok(Some(val.into()))
@ -901,7 +902,7 @@ impl InnerResolver {
{
*params.iter().next().unwrap().1
}
_ => unreachable!("must be option type")
_ => unreachable!("must be option type"),
};
if id == self.primitive_ids.none {
// for option type, just a null ptr
@ -913,7 +914,13 @@ impl InnerResolver {
))
} else {
match self
.get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator, option_val_ty)
.get_obj_value(
py,
obj.getattr("_nac3_option").unwrap(),
ctx,
generator,
option_val_ty,
)
.map_err(|e| {
super::CompileError::new_err(format!(
"Error getting value of Option object: {}",
@ -924,18 +931,27 @@ impl InnerResolver {
let global_str = format!("{}_option", id);
{
if self.global_value_ids.read().contains_key(&id) {
let global = ctx.module.get_global(&global_str).unwrap_or_else(|| {
ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str)
});
let global =
ctx.module.get_global(&global_str).unwrap_or_else(|| {
ctx.module.add_global(
v.get_type(),
Some(AddressSpace::Generic),
&global_str,
)
});
return Ok(Some(global.as_pointer_value().into()));
} else {
self.global_value_ids.write().insert(id, obj.into());
}
}
let global = ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str);
let global = ctx.module.add_global(
v.get_type(),
Some(AddressSpace::Generic),
&global_str,
);
global.set_initializer(&v);
Ok(Some(global.as_pointer_value().into()))
},
}
None => Ok(None),
}
}
@ -973,7 +989,12 @@ impl InnerResolver {
.iter()
.map(|(name, ty, _)| {
self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator, *ty)
.map_err(|e| super::CompileError::new_err(format!("Error getting field {}: {}", name, e)))
.map_err(|e| {
super::CompileError::new_err(format!(
"Error getting field {}: {}",
name, e
))
})
})
.collect();
let values = values?;
@ -1028,8 +1049,7 @@ impl InnerResolver {
if id == self.primitive_ids.none {
Ok(SymbolValue::OptionNone)
} else {
self
.get_default_param_obj_value(py, obj.getattr("_nac3_option").unwrap())?
self.get_default_param_obj_value(py, obj.getattr("_nac3_option").unwrap())?
.map(|v| SymbolValue::OptionSome(Box::new(v)))
}
} else {
@ -1184,7 +1204,7 @@ impl SymbolResolver for Resolver {
&self,
unifier: &mut Unifier,
defs: &[Arc<RwLock<TopLevelDef>>],
primitives: &PrimitiveStore
primitives: &PrimitiveStore,
) -> Result<(), String> {
// we don't need a lock because this will only be run in a single thread
if self.0.deferred_eval_store.needs_defer.load(Relaxed) {
@ -1214,7 +1234,8 @@ impl SymbolResolver for Resolver {
}
}
Ok(Ok(()))
}).unwrap()?
})
.unwrap()?
}
Ok(())
}

File diff suppressed because it is too large Load Diff

View File

@ -85,33 +85,22 @@ impl<U> crate::fold::Fold<U> for ConstantOptimizer {
fn fold_expr(&mut self, node: crate::Expr<U>) -> Result<crate::Expr<U>, Self::Error> {
match node.node {
crate::ExprKind::Tuple { elts, ctx } => {
let elts = elts
.into_iter()
.map(|x| self.fold_expr(x))
.collect::<Result<Vec<_>, _>>()?;
let expr = if elts
.iter()
.all(|e| matches!(e.node, crate::ExprKind::Constant { .. }))
{
let tuple = elts
.into_iter()
.map(|e| match e.node {
crate::ExprKind::Constant { value, .. } => value,
_ => unreachable!(),
})
.collect();
crate::ExprKind::Constant {
value: Constant::Tuple(tuple),
kind: None,
}
} else {
crate::ExprKind::Tuple { elts, ctx }
};
Ok(crate::Expr {
node: expr,
custom: node.custom,
location: node.location,
})
let elts =
elts.into_iter().map(|x| self.fold_expr(x)).collect::<Result<Vec<_>, _>>()?;
let expr =
if elts.iter().all(|e| matches!(e.node, crate::ExprKind::Constant { .. })) {
let tuple = elts
.into_iter()
.map(|e| match e.node {
crate::ExprKind::Constant { value, .. } => value,
_ => unreachable!(),
})
.collect();
crate::ExprKind::Constant { value: Constant::Tuple(tuple), kind: None }
} else {
crate::ExprKind::Tuple { elts, ctx }
};
Ok(crate::Expr { node: expr, custom: node.custom, location: node.location })
}
_ => crate::fold::fold_expr(self, node),
}
@ -138,18 +127,12 @@ mod tests {
Located {
location,
custom,
node: ExprKind::Constant {
value: 1.into(),
kind: None,
},
node: ExprKind::Constant { value: 1.into(), kind: None },
},
Located {
location,
custom,
node: ExprKind::Constant {
value: 2.into(),
kind: None,
},
node: ExprKind::Constant { value: 2.into(), kind: None },
},
Located {
location,
@ -160,26 +143,17 @@ mod tests {
Located {
location,
custom,
node: ExprKind::Constant {
value: 3.into(),
kind: None,
},
node: ExprKind::Constant { value: 3.into(), kind: None },
},
Located {
location,
custom,
node: ExprKind::Constant {
value: 4.into(),
kind: None,
},
node: ExprKind::Constant { value: 4.into(), kind: None },
},
Located {
location,
custom,
node: ExprKind::Constant {
value: 5.into(),
kind: None,
},
node: ExprKind::Constant { value: 5.into(), kind: None },
},
],
},
@ -187,9 +161,7 @@ mod tests {
],
},
};
let new_ast = ConstantOptimizer::new()
.fold_expr(ast)
.unwrap_or_else(|e| match e {});
let new_ast = ConstantOptimizer::new().fold_expr(ast).unwrap_or_else(|e| match e {});
assert_eq!(
new_ast,
Located {
@ -199,11 +171,7 @@ mod tests {
value: Constant::Tuple(vec![
1.into(),
2.into(),
Constant::Tuple(vec![
3.into(),
4.into(),
5.into(),
])
Constant::Tuple(vec![3.into(), 4.into(), 5.into(),])
]),
kind: None
},

View File

@ -64,11 +64,4 @@ macro_rules! simple_fold {
};
}
simple_fold!(
usize,
String,
bool,
StrRef,
constant::Constant,
constant::ConversionFlag
);
simple_fold!(usize, String, bool, StrRef, constant::Constant, constant::ConversionFlag);

View File

@ -34,10 +34,7 @@ impl<U> ExprKind<U> {
ExprKind::Starred { .. } => "starred",
ExprKind::Slice { .. } => "slice",
ExprKind::JoinedStr { values } => {
if values
.iter()
.any(|e| matches!(e.node, ExprKind::JoinedStr { .. }))
{
if values.iter().any(|e| matches!(e.node, ExprKind::JoinedStr { .. })) {
"f-string expression"
} else {
"literal"

View File

@ -9,6 +9,6 @@ mod impls;
mod location;
pub use ast_gen::*;
pub use location::{Location, FileName};
pub use location::{FileName, Location};
pub type Suite<U = ()> = Vec<Stmt<U>>;

View File

@ -21,7 +21,7 @@ impl From<String> for FileName {
pub struct Location {
pub row: usize,
pub column: usize,
pub file: FileName
pub file: FileName,
}
impl fmt::Display for Location {
@ -53,11 +53,7 @@ impl Location {
)
}
}
Visualize {
loc: *self,
line,
desc,
}
Visualize { loc: *self, line, desc }
}
}

View File

@ -15,7 +15,7 @@ nac3parser = { path = "../nac3parser" }
git = "https://github.com/nbaksalyar/inkwell.git"
branch = "llvm14"
default-features = false
features = ["llvm14-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
features = ["llvm14-0", "target-aarch64", "target-arm", "target-riscv", "no-libffi-linking"]
[dev-dependencies]
test-case = "1.2.0"

View File

@ -18,17 +18,8 @@ fn main() {
* Compiling for WASM32 and filtering the output with regex is the closest we can get.
*/
const FLAG: &[&str] = &[
"--target=wasm32",
FILE,
"-O3",
"-emit-llvm",
"-S",
"-Wall",
"-Wextra",
"-o",
"-",
];
const FLAG: &[&str] =
&["--target=wasm32", FILE, "-O3", "-emit-llvm", "-S", "-Wall", "-Wextra", "-o", "-"];
let output = Command::new("clang")
.args(FLAG)
.output()

View File

@ -11,22 +11,22 @@ use crate::{
symbol_resolver::{SymbolValue, ValueEnum},
toplevel::{DefinitionId, TopLevelDef},
typecheck::{
magic_methods::{binop_assign_name, binop_name},
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
magic_methods::{binop_name, binop_assign_name},
},
};
use inkwell::{
AddressSpace,
attributes::{Attribute, AttributeLoc},
types::{AnyType, BasicType, BasicTypeEnum},
values::{BasicValueEnum, FunctionValue, IntValue, PointerValue}
values::{BasicValueEnum, FunctionValue, IntValue, PointerValue},
AddressSpace,
};
use itertools::{chain, izip, zip, Itertools};
use nac3parser::ast::{
self, Boolop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
};
use super::{CodeGenerator, need_sret};
use super::{need_sret, CodeGenerator};
pub fn get_subst_key(
unifier: &mut Unifier,
@ -241,7 +241,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
op: &Operator,
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
signed: bool
signed: bool,
) -> BasicValueEnum<'ctx> {
let (lhs, rhs) =
if let (BasicValueEnum::IntValue(lhs), BasicValueEnum::IntValue(rhs)) = (lhs, rhs) {
@ -270,9 +270,15 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
(Operator::BitXor, _) => self.builder.build_xor(lhs, rhs, "xor").into(),
(Operator::BitAnd, _) => self.builder.build_and(lhs, rhs, "and").into(),
(Operator::LShift, _) => self.builder.build_left_shift(lhs, rhs, "lshift").into(),
(Operator::RShift, _) => self.builder.build_right_shift(lhs, rhs, true, "rshift").into(),
(Operator::FloorDiv, true) => self.builder.build_int_signed_div(lhs, rhs, "floordiv").into(),
(Operator::FloorDiv, false) => self.builder.build_int_unsigned_div(lhs, rhs, "floordiv").into(),
(Operator::RShift, _) => {
self.builder.build_right_shift(lhs, rhs, true, "rshift").into()
}
(Operator::FloorDiv, true) => {
self.builder.build_int_signed_div(lhs, rhs, "floordiv").into()
}
(Operator::FloorDiv, false) => {
self.builder.build_int_unsigned_div(lhs, rhs, "floordiv").into()
}
(Operator::Pow, s) => integer_power(generator, self, lhs, rhs, s).into(),
// special implementation?
(Operator::MatMult, _) => unreachable!(),
@ -340,18 +346,28 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
let byval_id = Attribute::get_named_enum_kind_id("byval");
let offset = if fun.get_enum_attribute(AttributeLoc::Param(0), sret_id).is_some() {
return_slot = Some(self.builder.build_alloca(fun.get_type().get_param_types()[0]
.into_pointer_type().get_element_type().into_struct_type(), call_name));
return_slot = Some(
self.builder.build_alloca(
fun.get_type().get_param_types()[0]
.into_pointer_type()
.get_element_type()
.into_struct_type(),
call_name,
),
);
loc_params.push((*return_slot.as_ref().unwrap()).into());
1
} else {
0
};
for (i, param) in params.iter().enumerate() {
if fun.get_enum_attribute(AttributeLoc::Param((i + offset) as u32), byval_id).is_some() {
if fun
.get_enum_attribute(AttributeLoc::Param((i + offset) as u32), byval_id)
.is_some()
{
// lazy update
if loc_params.is_empty() {
loc_params.extend(params[0..i+offset].iter().copied());
loc_params.extend(params[0..i + offset].iter().copied());
}
let slot = self.builder.build_alloca(param.get_type(), call_name);
loc_params.push(slot.into());
@ -361,11 +377,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
}
}
}
let params = if loc_params.is_empty() {
params
} else {
&loc_params
};
let params = if loc_params.is_empty() { params } else { &loc_params };
let result = if let Some(target) = self.unwind_target {
let current = self.builder.get_insert_block().unwrap().get_parent().unwrap();
let then_block = self.ctx.append_basic_block(current, &format!("after.{}", call_name));
@ -496,7 +508,10 @@ pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
match def {
TopLevelDef::Class { methods, .. } => {
// TODO: what about other fields that require alloca?
let fun_id = methods.iter().find(|method| method.0 == "__init__".into()).and_then(|method| Some(method.2));
let fun_id = methods
.iter()
.find(|method| method.0 == "__init__".into())
.and_then(|method| Some(method.2));
let ty = ctx.get_llvm_type(generator, signature.ret).into_pointer_type();
let zelf_ty: BasicTypeEnum = ty.get_element_type().try_into().unwrap();
let zelf: BasicValueEnum<'ctx> = ctx.builder.build_alloca(zelf_ty, "alloca").into();
@ -636,8 +651,12 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
);
}
// reorder the parameters
let mut real_params =
fun.0.args.iter().map(|arg| (mapping.remove(&arg.name).unwrap(), arg.ty)).collect_vec();
let mut real_params = fun
.0
.args
.iter()
.map(|arg| (mapping.remove(&arg.name).unwrap(), arg.ty))
.collect_vec();
if let Some(obj) = &obj {
real_params.insert(0, (obj.1.clone(), obj.0));
}
@ -700,32 +719,48 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
};
let has_sret = ret_type.map_or(false, |ret_type| need_sret(ctx.ctx, ret_type));
let mut byvals = Vec::new();
let mut params =
args.iter().enumerate().map(|(i, arg)| match ctx.get_llvm_type(generator, arg.ty) {
BasicTypeEnum::StructType(ty) if is_extern => {
byvals.push((i, ty));
ty.ptr_type(AddressSpace::Generic).into()
},
x => x
}.into()).collect_vec();
let mut params = args
.iter()
.enumerate()
.map(|(i, arg)| {
match ctx.get_llvm_type(generator, arg.ty) {
BasicTypeEnum::StructType(ty) if is_extern => {
byvals.push((i, ty));
ty.ptr_type(AddressSpace::Generic).into()
}
x => x,
}
.into()
})
.collect_vec();
if has_sret {
params.insert(0, ret_type.unwrap().ptr_type(AddressSpace::Generic).into());
}
let fun_ty = match ret_type {
Some(ret_type) if !has_sret => ret_type.fn_type(&params, false),
_ => ctx.ctx.void_type().fn_type(&params, false)
_ => ctx.ctx.void_type().fn_type(&params, false),
};
let fun_val = ctx.module.add_function(&symbol, fun_ty, None);
let offset = if has_sret {
fun_val.add_attribute(AttributeLoc::Param(0),
ctx.ctx.create_type_attribute(Attribute::get_named_enum_kind_id("sret"), ret_type.unwrap().as_any_type_enum()));
fun_val.add_attribute(
AttributeLoc::Param(0),
ctx.ctx.create_type_attribute(
Attribute::get_named_enum_kind_id("sret"),
ret_type.unwrap().as_any_type_enum(),
),
);
1
} else {
0
};
for (i, ty) in byvals {
fun_val.add_attribute(AttributeLoc::Param((i as u32) + offset),
ctx.ctx.create_type_attribute(Attribute::get_named_enum_kind_id("byval"), ty.as_any_type_enum()));
fun_val.add_attribute(
AttributeLoc::Param((i as u32) + offset),
ctx.ctx.create_type_attribute(
Attribute::get_named_enum_kind_id("byval"),
ty.as_any_type_enum(),
),
);
}
fun_val
});
@ -789,7 +824,11 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
let Comprehension { target, iter, ifs, .. } = &generators[0];
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator, iter.custom.unwrap())?;
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(
ctx,
generator,
iter.custom.unwrap(),
)?;
let int32 = ctx.ctx.i32_type();
let size_t = generator.get_size_type(ctx.ctx);
let zero_size_t = size_t.const_zero();
@ -930,14 +969,16 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
) -> Result<Option<ValueEnum<'ctx>>, String> {
let ty1 = ctx.unifier.get_representative(left.custom.unwrap());
let ty2 = ctx.unifier.get_representative(right.custom.unwrap());
let left_val = generator
.gen_expr(ctx, left)?
.unwrap()
.to_basic_value_enum(ctx, generator, left.custom.unwrap())?;
let right_val = generator
.gen_expr(ctx, right)?
.unwrap()
.to_basic_value_enum(ctx, generator, right.custom.unwrap())?;
let left_val = generator.gen_expr(ctx, left)?.unwrap().to_basic_value_enum(
ctx,
generator,
left.custom.unwrap(),
)?;
let right_val = generator.gen_expr(ctx, right)?.unwrap().to_basic_value_enum(
ctx,
generator,
right.custom.unwrap(),
)?;
// we can directly compare the types, because we've got their representatives
// which would be unchanged until further unification, which we would never do
@ -957,7 +998,8 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
let ty = f64_t.fn_type(&[f64_t.into(), i32_t.into()], false);
ctx.module.add_function("llvm.powi.f64.i32", ty, None)
});
let res = ctx.builder
let res = ctx
.builder
.build_call(pow_intr, &[left_val.into(), right_val.into()], "f_pow_i")
.try_as_basic_value()
.unwrap_left();
@ -966,10 +1008,8 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
let (op_name, id) = if let TypeEnum::TObj { fields, obj_id, .. } =
ctx.unifier.get_ty_immutable(left.custom.unwrap()).as_ref()
{
let (binop_name, binop_assign_name) = (
binop_name(op).into(),
binop_assign_name(op).into()
);
let (binop_name, binop_assign_name) =
(binop_name(op).into(), binop_assign_name(op).into());
// if is aug_assign, try aug_assign operator first
if is_aug_assign && fields.contains_key(&binop_assign_name) {
(binop_assign_name, *obj_id)
@ -994,7 +1034,7 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
} else {
unreachable!("must be tobj")
}
},
}
};
let fun_id = {
let defs = ctx.top_level.definitions.read();
@ -1011,7 +1051,8 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
Some((left.custom.unwrap(), left_val.into())),
(&signature, fun_id),
vec![(None, right_val.into())],
).map(|f| f.map(|f| f.into()))
)
.map(|f| f.map(|f| f.into()))
}
}
@ -1033,14 +1074,14 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(),
ctx.unifier.get_ty(ctx.primitives.option).as_ref(),
) {
(
TypeEnum::TObj { obj_id, params, .. },
TypeEnum::TObj { obj_id: opt_id, .. },
) if *obj_id == *opt_id => ctx
.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::Generic)
.const_null()
.into(),
(TypeEnum::TObj { obj_id, params, .. }, TypeEnum::TObj { obj_id: opt_id, .. })
if *obj_id == *opt_id =>
{
ctx.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::Generic)
.const_null()
.into()
}
_ => unreachable!("must be option type"),
}
}
@ -1058,12 +1099,9 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let elements = elts
.iter()
.map(|x| {
generator
.gen_expr(ctx, x)
.map_or_else(
Err,
|v| v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap())
)
generator.gen_expr(ctx, x).map_or_else(Err, |v| {
v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap())
})
})
.collect::<Result<Vec<_>, _>>()?;
let ty = if elements.is_empty() {
@ -1094,9 +1132,9 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let element_val = elts
.iter()
.map(|x| {
generator
.gen_expr(ctx, x)
.map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap()))
generator.gen_expr(ctx, x).map_or_else(Err, |v| {
v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap())
})
})
.collect::<Result<Vec<_>, _>>()?;
let element_ty = element_val.iter().map(BasicValueEnum::get_type).collect_vec();
@ -1117,14 +1155,17 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
ExprKind::Attribute { value, attr, .. } => {
// note that we would handle class methods directly in calls
match generator.gen_expr(ctx, value)?.unwrap() {
ValueEnum::Static(v) => v.get_field(*attr, ctx).map_or_else(|| {
let v = v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?;
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
Ok(ValueEnum::Dynamic(ctx.build_gep_and_load(
v.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)],
))) as Result<_, String>
}, Ok)?,
ValueEnum::Static(v) => v.get_field(*attr, ctx).map_or_else(
|| {
let v = v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?;
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
Ok(ValueEnum::Dynamic(ctx.build_gep_and_load(
v.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)],
))) as Result<_, String>
},
Ok,
)?,
ValueEnum::Dynamic(v) => {
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
ValueEnum::Dynamic(ctx.build_gep_and_load(
@ -1184,10 +1225,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
}
ExprKind::UnaryOp { op, operand } => {
let ty = ctx.unifier.get_representative(operand.custom.unwrap());
let val =
generator.gen_expr(ctx, operand)?
.unwrap()
.to_basic_value_enum(ctx, generator, operand.custom.unwrap())?;
let val = generator.gen_expr(ctx, operand)?.unwrap().to_basic_value_enum(
ctx,
generator,
operand.custom.unwrap(),
)?;
if ty == ctx.primitives.bool {
let val = val.into_int_value();
match op {
@ -1244,14 +1286,16 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
BasicValueEnum::IntValue(lhs),
BasicValueEnum::IntValue(rhs),
) = (
generator
.gen_expr(ctx, lhs)?
.unwrap()
.to_basic_value_enum(ctx, generator, lhs.custom.unwrap())?,
generator
.gen_expr(ctx, rhs)?
.unwrap()
.to_basic_value_enum(ctx, generator, rhs.custom.unwrap())?,
generator.gen_expr(ctx, lhs)?.unwrap().to_basic_value_enum(
ctx,
generator,
lhs.custom.unwrap(),
)?,
generator.gen_expr(ctx, rhs)?.unwrap().to_basic_value_enum(
ctx,
generator,
rhs.custom.unwrap(),
)?,
) {
(lhs, rhs)
} else {
@ -1272,14 +1316,16 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
BasicValueEnum::FloatValue(lhs),
BasicValueEnum::FloatValue(rhs),
) = (
generator
.gen_expr(ctx, lhs)?
.unwrap()
.to_basic_value_enum(ctx, generator, lhs.custom.unwrap())?,
generator
.gen_expr(ctx, rhs)?
.unwrap()
.to_basic_value_enum(ctx, generator, rhs.custom.unwrap())?,
generator.gen_expr(ctx, lhs)?.unwrap().to_basic_value_enum(
ctx,
generator,
lhs.custom.unwrap(),
)?,
generator.gen_expr(ctx, rhs)?.unwrap().to_basic_value_enum(
ctx,
generator,
rhs.custom.unwrap(),
)?,
) {
(lhs, rhs)
} else {
@ -1337,7 +1383,8 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
match result {
None => None,
Some(v) => {
let b = b.unwrap().to_basic_value_enum(ctx, generator, orelse.custom.unwrap())?;
let b =
b.unwrap().to_basic_value_enum(ctx, generator, orelse.custom.unwrap())?;
Some(ctx.builder.build_store(v, b))
}
};
@ -1345,7 +1392,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
ctx.builder.position_at_end(cont_bb);
match result {
None => return Ok(None),
Some(v) => return Ok(Some(ctx.builder.build_load(v, "if_exp_val_load").into()))
Some(v) => return Ok(Some(ctx.builder.build_load(v, "if_exp_val_load").into())),
}
}
ExprKind::Call { func, args, keywords } => {
@ -1419,14 +1466,12 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.unwrap()
.get_parent()
.unwrap();
let unreachable_block = ctx.ctx.append_basic_block(
current_fun,
"unwrap_none_unreachable"
);
let exn_block = ctx.ctx.append_basic_block(
current_fun,
"unwrap_none_exception"
);
let unreachable_block = ctx
.ctx
.append_basic_block(current_fun, "unwrap_none_unreachable");
let exn_block = ctx
.ctx
.append_basic_block(current_fun, "unwrap_none_exception");
ctx.builder.build_unconditional_branch(exn_block);
ctx.builder.position_at_end(exn_block);
ctx.raise_exn(
@ -1434,22 +1479,24 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
"0:UnwrapNoneError",
err_msg,
[None, None, None],
ctx.current_loc
ctx.current_loc,
);
ctx.builder.position_at_end(unreachable_block);
let ptr = ctx
.get_llvm_type(generator, value.custom.unwrap())
.into_pointer_type()
.const_null();
return Ok(Some(ctx.builder.build_load(
ptr,
"unwrap_none_unreachable_load"
).into()));
return Ok(Some(
ctx.builder
.build_load(ptr, "unwrap_none_unreachable_load")
.into(),
));
}
Some(v) => return Ok(Some(v)),
}
},
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
let not_null = ctx.builder.build_is_not_null(ptr, "unwrap_not_null");
let not_null =
ctx.builder.build_is_not_null(ptr, "unwrap_not_null");
ctx.make_assert(
generator,
not_null,
@ -1458,12 +1505,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
[None, None, None],
expr.location,
);
return Ok(Some(ctx.builder.build_load(
ptr,
"unwrap_some_load"
).into()))
return Ok(Some(
ctx.builder.build_load(ptr, "unwrap_some_load").into(),
));
}
_ => unreachable!("option must be static or ptr")
_ => unreachable!("option must be static or ptr"),
}
}
return Ok(generator
@ -1574,30 +1620,26 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
} else {
unreachable!("tuple subscript must be const int after type check");
};
let v = generator
.gen_expr(ctx, value)?
.unwrap();
let v = generator.gen_expr(ctx, value)?.unwrap();
match v {
ValueEnum::Dynamic(v) => {
let v = v.into_struct_value();
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap().into()
}
ValueEnum::Static(v) => {
match v.get_tuple_element(index) {
Some(v) => v,
None => {
let tup = v
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_struct_value();
ctx.builder.build_extract_value(tup, index, "tup_elem").unwrap().into()
}
ValueEnum::Static(v) => match v.get_tuple_element(index) {
Some(v) => v,
None => {
let tup = v
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_struct_value();
ctx.builder.build_extract_value(tup, index, "tup_elem").unwrap().into()
}
}
},
}
} else {
unreachable!("should not be other subscriptable types after type check");
}
},
}
ExprKind::ListComp { .. } => gen_comprehension(generator, ctx, expr)?.into(),
_ => unimplemented!(),
}))

View File

@ -1,9 +1,4 @@
typedef _ExtInt(8) int8_t;
typedef unsigned _ExtInt(8) uint8_t;
typedef _ExtInt(32) int32_t;
typedef unsigned _ExtInt(32) uint32_t;
typedef _ExtInt(64) int64_t;
typedef unsigned _ExtInt(64) uint64_t;
#include "stdint.h"
# define MAX(a, b) (a > b ? a : b)
# define MIN(a, b) (a > b ? b : a)

View File

@ -261,7 +261,11 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
ctx.module.add_function(SYMBOL, fn_t, None)
});
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(ctx, generator, i.custom.unwrap())?;
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(
ctx,
generator,
i.custom.unwrap(),
)?;
Ok(ctx
.builder
.build_call(func, &[i.into(), length.into()], "bounded_ind")
@ -329,27 +333,19 @@ pub fn list_slice_assignment<'ctx, 'a>(
// index in bound and positive should be done
// assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
// throw exception if not satisfied
let src_end = ctx.builder
let src_end = ctx
.builder
.build_select(
ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
src_idx.2,
zero,
"is_neg",
),
ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, src_idx.2, zero, "is_neg"),
ctx.builder.build_int_sub(src_idx.1, one, "e_min_one"),
ctx.builder.build_int_add(src_idx.1, one, "e_add_one"),
"final_e",
)
.into_int_value();
let dest_end = ctx.builder
let dest_end = ctx
.builder
.build_select(
ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
dest_idx.2,
zero,
"is_neg",
),
ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, dest_idx.2, zero, "is_neg"),
ctx.builder.build_int_sub(dest_idx.1, one, "e_min_one"),
ctx.builder.build_int_add(dest_idx.1, one, "e_add_one"),
"final_e",

View File

@ -8,8 +8,6 @@ use crate::{
};
use crossbeam::channel::{unbounded, Receiver, Sender};
use inkwell::{
AddressSpace,
OptimizationLevel,
attributes::{Attribute, AttributeLoc},
basic_block::BasicBlock,
builder::Builder,
@ -17,10 +15,11 @@ use inkwell::{
module::Module,
passes::{PassManager, PassManagerBuilder},
types::{AnyType, BasicType, BasicTypeEnum},
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue}
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue},
AddressSpace, OptimizationLevel,
};
use itertools::Itertools;
use nac3parser::ast::{Stmt, StrRef, Location};
use nac3parser::ast::{Location, Stmt, StrRef};
use parking_lot::{Condvar, Mutex};
use std::collections::{HashMap, HashSet};
use std::sync::{
@ -300,7 +299,10 @@ fn get_llvm_type<'ctx>(
&*definition.read()
{
let struct_type = ctx.opaque_struct_type(&name.to_string());
type_cache.insert(unifier.get_representative(ty), struct_type.ptr_type(AddressSpace::Generic).into());
type_cache.insert(
unifier.get_representative(ty),
struct_type.ptr_type(AddressSpace::Generic).into(),
);
let fields = fields_list
.iter()
.map(|f| {
@ -326,7 +328,11 @@ fn get_llvm_type<'ctx>(
// a struct with fields in the order present in the tuple
let fields = ty
.iter()
.map(|ty| get_llvm_type(ctx, generator, unifier, top_level, type_cache, primitives, *ty))
.map(|ty| {
get_llvm_type(
ctx, generator, unifier, top_level, type_cache, primitives, *ty,
)
})
.collect_vec();
ctx.struct_type(&fields, false).into()
}
@ -349,26 +355,35 @@ fn get_llvm_type<'ctx>(
}
fn need_sret<'ctx>(ctx: &'ctx Context, ty: BasicTypeEnum<'ctx>) -> bool {
fn need_sret_impl<'ctx>(ctx: &'ctx Context, ty: BasicTypeEnum<'ctx>, maybe_large: bool) -> bool {
fn need_sret_impl<'ctx>(
ctx: &'ctx Context,
ty: BasicTypeEnum<'ctx>,
maybe_large: bool,
) -> bool {
match ty {
BasicTypeEnum::IntType(_) | BasicTypeEnum::PointerType(_) => false,
BasicTypeEnum::FloatType(_) if maybe_large => false,
BasicTypeEnum::StructType(ty) if maybe_large && ty.count_fields() <= 2 =>
ty.get_field_types().iter().any(|ty| need_sret_impl(ctx, *ty, false)),
BasicTypeEnum::StructType(ty) if maybe_large && ty.count_fields() <= 2 => {
ty.get_field_types().iter().any(|ty| need_sret_impl(ctx, *ty, false))
}
_ => true,
}
}
need_sret_impl(ctx, ty, true)
}
pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenContext) -> Result<(), String>> (
pub fn gen_func_impl<
'ctx,
G: CodeGenerator,
F: FnOnce(&mut G, &mut CodeGenContext) -> Result<(), String>,
>(
context: &'ctx Context,
generator: &mut G,
registry: &WorkerRegistry,
builder: Builder<'ctx>,
module: Module<'ctx>,
task: CodeGenTask,
codegen_function: F
codegen_function: F,
) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> {
let top_level_ctx = registry.top_level_ctx.clone();
let static_value_store = registry.static_value_store.clone();
@ -463,7 +478,15 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
let ret_type = if unifier.unioned(ret, primitives.none) {
None
} else {
Some(get_llvm_type(context, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, &primitives, ret))
Some(get_llvm_type(
context,
generator,
&mut unifier,
top_level_ctx.as_ref(),
&mut type_cache,
&primitives,
ret,
))
};
let has_sret = ret_type.map_or(false, |ty| need_sret(context, ty));
@ -489,7 +512,7 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
let fn_type = match ret_type {
Some(ret_type) if !has_sret => ret_type.fn_type(&params, false),
_ => context.void_type().fn_type(&params, false)
_ => context.void_type().fn_type(&params, false),
};
let symbol = &task.symbol_name;
@ -504,9 +527,13 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
fn_val.set_personality_function(personality);
}
if has_sret {
fn_val.add_attribute(AttributeLoc::Param(0),
context.create_type_attribute(Attribute::get_named_enum_kind_id("sret"),
ret_type.unwrap().as_any_type_enum()));
fn_val.add_attribute(
AttributeLoc::Param(0),
context.create_type_attribute(
Attribute::get_named_enum_kind_id("sret"),
ret_type.unwrap().as_any_type_enum(),
),
);
}
let init_bb = context.append_basic_block(fn_val, "init");

View File

@ -87,19 +87,15 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
.unwrap()
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_pointer_value();
let len = ctx
.build_gep_and_load(v, &[zero, i32_type.const_int(1, false)])
.into_int_value();
let len =
ctx.build_gep_and_load(v, &[zero, i32_type.const_int(1, false)]).into_int_value();
let raw_index = generator
.gen_expr(ctx, slice)?
.unwrap()
.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
.into_int_value();
let raw_index = ctx.builder.build_int_s_extend(
raw_index,
generator.get_size_type(ctx.ctx),
"sext",
);
let raw_index =
ctx.builder.build_int_s_extend(raw_index, generator.get_size_type(ctx.ctx), "sext");
// handle negative index
let is_negative = ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
@ -114,12 +110,8 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
.into_int_value();
// unsigned less than is enough, because negative index after adjustment is
// bigger than the length (for unsigned cmp)
let bound_check = ctx.builder.build_int_compare(
inkwell::IntPredicate::ULT,
index,
len,
"inbound",
);
let bound_check =
ctx.builder.build_int_compare(inkwell::IntPredicate::ULT, index, len, "inbound");
ctx.make_assert(
generator,
bound_check,
@ -696,12 +688,15 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
&mut ctx.unifier,
type_.custom.unwrap(),
);
let obj_id = if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(type_.custom.unwrap()) {
let obj_id = if let TypeEnum::TObj { obj_id, .. } =
&*ctx.unifier.get_ty(type_.custom.unwrap())
{
*obj_id
} else {
unreachable!()
};
let exception_name = format!("{}:{}", ctx.resolver.get_exception_id(obj_id.0), exn_name);
let exception_name =
format!("{}:{}", ctx.resolver.get_exception_id(obj_id.0), exn_name);
let exn_id = ctx.resolver.get_string_id(&exception_name);
let exn_id_global =
ctx.module.add_global(ctx.ctx.i32_type(), None, &format!("exn.{}", exn_id));
@ -751,7 +746,9 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
let mut final_proxy_lambda =
|ctx: &mut CodeGenContext<'ctx, 'a>,
target: BasicBlock<'ctx>,
block: BasicBlock<'ctx>| final_proxy(ctx, target, block, final_data.as_mut().unwrap());
block: BasicBlock<'ctx>| {
final_proxy(ctx, target, block, final_data.as_mut().unwrap())
};
let mut redirect_lambda = |ctx: &mut CodeGenContext<'ctx, 'a>,
target: BasicBlock<'ctx>,
block: BasicBlock<'ctx>| {
@ -1059,7 +1056,7 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
stmt.location,
)
}
_ => unimplemented!()
_ => unimplemented!(),
};
Ok(())
}

View File

@ -160,7 +160,7 @@ pub trait SymbolResolver {
&self,
_unifier: &mut Unifier,
_top_level_defs: &[Arc<RwLock<TopLevelDef>>],
_primitives: &PrimitiveStore
_primitives: &PrimitiveStore,
) -> Result<(), String> {
Ok(())
}

View File

@ -14,8 +14,8 @@ pub fn get_exn_constructor(
class_id: usize,
cons_id: usize,
unifier: &mut Unifier,
primitives: &PrimitiveStore
)-> (TopLevelDef, TopLevelDef, Type, Type) {
primitives: &PrimitiveStore,
) -> (TopLevelDef, TopLevelDef, Type, Type) {
let int32 = primitives.int32;
let int64 = primitives.int64;
let string = primitives.str;
@ -225,11 +225,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let expect_ty = obj.clone().unwrap().0;
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
ctx,
generator,
expect_ty,
)?;
let obj_val =
obj.unwrap().1.clone().to_basic_value_enum(ctx, generator, expect_ty)?;
if let BasicValueEnum::PointerValue(ptr) = obj_val {
Ok(Some(ctx.builder.build_is_not_null(ptr, "is_some").into()))
} else {
@ -250,11 +247,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let expect_ty = obj.clone().unwrap().0;
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
ctx,
generator,
expect_ty,
)?;
let obj_val =
obj.unwrap().1.clone().to_basic_value_enum(ctx, generator, expect_ty)?;
if let BasicValueEnum::PointerValue(ptr) = obj_val {
Ok(Some(ctx.builder.build_is_null(ptr, "is_none").into()))
} else {
@ -272,11 +266,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|_, _, _, _, _| {
unreachable!("handled in gen_expr")
},
)))),
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|_, _, _, _, _| {
unreachable!("handled in gen_expr")
})))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
@ -567,7 +559,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let arg = args[0].1.clone().to_basic_value_enum(
ctx,
generator,
ctx.primitives.float,
)?;
let round_intrinsic =
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -607,7 +603,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let arg = args[0].1.clone().to_basic_value_enum(
ctx,
generator,
ctx.primitives.float,
)?;
let round_intrinsic =
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -668,13 +668,15 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let ty_i32 = ctx.primitives.int32;
for (i, arg) in args.iter().enumerate() {
if arg.0 == Some("start".into()) {
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
start =
Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if arg.0 == Some("stop".into()) {
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if arg.0 == Some("step".into()) {
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 0 {
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
start =
Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 1 {
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 2 {
@ -829,7 +831,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let arg = args[0].1.clone().to_basic_value_enum(
ctx,
generator,
ctx.primitives.float,
)?;
let floor_intrinsic =
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -869,7 +875,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let arg = args[0].1.clone().to_basic_value_enum(
ctx,
generator,
ctx.primitives.float,
)?;
let floor_intrinsic =
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -909,7 +919,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let arg = args[0].1.clone().to_basic_value_enum(
ctx,
generator,
ctx.primitives.float,
)?;
let ceil_intrinsic =
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -949,7 +963,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let arg = args[0].1.clone().to_basic_value_enum(
ctx,
generator,
ctx.primitives.float,
)?;
let ceil_intrinsic =
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -1005,7 +1023,10 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
let arg = arg.into_pointer_value();
let (start, end, step) = destructure_range(ctx, arg);
Some(calculate_len_for_slice_range(generator, ctx, start, end, step).into())
Some(
calculate_len_for_slice_range(generator, ctx, start, end, step)
.into(),
)
} else {
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
@ -1244,30 +1265,12 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
})),
];
let ast_list: Vec<Option<ast::Stmt<()>>> =
(0..top_level_def_list.len()).map(|_| None).collect();
let ast_list: Vec<Option<ast::Stmt<()>>> = vec![None; top_level_def_list.len()];
(
izip!(top_level_def_list, ast_list).collect_vec(),
&[
"int32",
"int64",
"uint32",
"uint64",
"float",
"round",
"round64",
"range",
"str",
"bool",
"floor",
"floor64",
"ceil",
"ceil64",
"len",
"min",
"max",
"abs",
"Some",
"int32", "int64", "uint32", "uint64", "float", "round", "round64", "range", "str",
"bool", "floor", "floor64", "ceil", "ceil64", "len", "min", "max", "abs", "Some",
],
)
}

View File

@ -91,8 +91,7 @@ impl TopLevelComposer {
assert!(name == *simple_name);
builtin_ty.insert(name, *signature);
builtin_id.insert(name, DefinitionId(id));
} else if let TopLevelDef::Class { name, constructor, object_id, .. } = &*def
{
} else if let TopLevelDef::Class { name, constructor, object_id, .. } = &*def {
assert!(id == object_id.0);
if let Some(constructor) = constructor {
builtin_ty.insert(*name, *constructor);
@ -739,8 +738,13 @@ impl TopLevelComposer {
let mut subst_list = Some(Vec::new());
// unification of previously assigned typevar
let mut unification_helper = |ty, def| {
let target_ty =
get_type_from_type_annotation_kinds(&temp_def_list, unifier, primitives, &def, &mut subst_list)?;
let target_ty = get_type_from_type_annotation_kinds(
&temp_def_list,
unifier,
primitives,
&def,
&mut subst_list,
)?;
unifier.unify(ty, target_ty).map_err(|e| e.to_display(unifier).to_string())?;
Ok(()) as Result<(), String>
};
@ -780,7 +784,9 @@ impl TopLevelComposer {
match &*def.read() {
TopLevelDef::Class { resolver: Some(resolver), .. }
| TopLevelDef::Function { resolver: Some(resolver), .. } => {
if let Err(e) = resolver.handle_deferred_eval(unifier, &temp_def_list, primitives) {
if let Err(e) =
resolver.handle_deferred_eval(unifier, &temp_def_list, primitives)
{
errors.insert(e);
}
}
@ -904,7 +910,7 @@ impl TopLevelComposer {
unifier,
primitives_store,
&type_annotation,
&mut None
&mut None,
)?;
Ok(FuncArg {
@ -972,7 +978,7 @@ impl TopLevelComposer {
unifier,
primitives_store,
&return_ty_annotation,
&mut None
&mut None,
)?
} else {
primitives_store.none
@ -1334,7 +1340,7 @@ impl TopLevelComposer {
));
}
}
ast::StmtKind::Assign { .. } => {}, // we don't class attributes
ast::StmtKind::Assign { .. } => {} // we don't class attributes
ast::StmtKind::Pass { .. } => {}
ast::StmtKind::Expr { value: _, .. } => {} // typically a docstring; ignoring all expressions matches CPython behavior
_ => {
@ -1483,24 +1489,30 @@ impl TopLevelComposer {
// first, fix function typevar ids
// they may be changed with our use of placeholders
for (def, _) in definition_ast_list.iter().skip(self.builtin_num) {
if let TopLevelDef::Function {
signature,
var_id,
..
} = &mut *def.write() {
if let TopLevelDef::Function { signature, var_id, .. } = &mut *def.write() {
if let TypeEnum::TFunc(FunSignature { args, ret, vars }) =
unifier.get_ty(*signature).as_ref() {
let new_var_ids = vars.values().map(|v| match &*unifier.get_ty(*v) {
TypeEnum::TVar{id, ..} => *id,
_ => unreachable!(),
}).collect_vec();
unifier.get_ty(*signature).as_ref()
{
let new_var_ids = vars
.values()
.map(|v| match &*unifier.get_ty(*v) {
TypeEnum::TVar { id, .. } => *id,
_ => unreachable!(),
})
.collect_vec();
if new_var_ids != *var_id {
let new_signature = FunSignature {
args: args.clone(),
ret: *ret,
vars: new_var_ids.iter().zip(vars.values()).map(|(id, v)| (*id, *v)).collect(),
vars: new_var_ids
.iter()
.zip(vars.values())
.map(|(id, v)| (*id, *v))
.collect(),
};
unifier.unification_table.set_value(*signature, Rc::new(TypeEnum::TFunc(new_signature)));
unifier
.unification_table
.set_value(*signature, Rc::new(TypeEnum::TFunc(new_signature)));
*var_id = new_var_ids;
}
}
@ -1527,7 +1539,7 @@ impl TopLevelComposer {
unifier,
primitives_ty,
&make_self_type_annotation(type_vars, *object_id),
&mut None
&mut None,
)?;
if ancestors
.iter()
@ -1696,14 +1708,15 @@ impl TopLevelComposer {
unifier,
primitives_ty,
&ty_ann,
&mut None
&mut None,
)?;
vars.extend(type_vars.iter().map(|ty|
vars.extend(type_vars.iter().map(|ty| {
if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*ty) {
(*id, *ty)
} else {
unreachable!()
}));
}
}));
Some((self_ty, type_vars.clone()))
} else {
unreachable!("must be class def")
@ -1918,7 +1931,12 @@ impl TopLevelComposer {
}
instance_to_stmt.insert(
get_subst_key(unifier, self_type, &subst, Some(&vars.keys().cloned().collect())),
get_subst_key(
unifier,
self_type,
&subst,
Some(&vars.keys().cloned().collect()),
),
FunInstance {
body: Arc::new(fun_body),
unifier_id: 0,

View File

@ -521,65 +521,66 @@ pub fn parse_parameter_default_value(
}
match &default.node {
ast::ExprKind::Constant { value, .. } => handle_constant(value, &default.location),
ast::ExprKind::Call { func, args, .. } if args.len() == 1 => {
match &func.node {
ast::ExprKind::Name { id, .. } if *id == "int64".into() => match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
let v: Result<i64, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::I64(v)),
_ => Err(format!("default param value out of range at {}", default.location)),
ast::ExprKind::Call { func, args, .. } if args.len() == 1 => match &func.node {
ast::ExprKind::Name { id, .. } if *id == "int64".into() => match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
let v: Result<i64, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::I64(v)),
_ => {
Err(format!("default param value out of range at {}", default.location))
}
}
_ => Err(format!("only allow constant integer here at {}", default.location))
}
ast::ExprKind::Name { id, .. } if *id == "uint32".into() => match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
let v: Result<u32, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U32(v)),
_ => Err(format!("default param value out of range at {}", default.location)),
_ => Err(format!("only allow constant integer here at {}", default.location)),
},
ast::ExprKind::Name { id, .. } if *id == "uint32".into() => match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
let v: Result<u32, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U32(v)),
_ => {
Err(format!("default param value out of range at {}", default.location))
}
}
_ => Err(format!("only allow constant integer here at {}", default.location))
}
ast::ExprKind::Name { id, .. } if *id == "uint64".into() => match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
let v: Result<u64, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U64(v)),
_ => Err(format!("default param value out of range at {}", default.location)),
_ => Err(format!("only allow constant integer here at {}", default.location)),
},
ast::ExprKind::Name { id, .. } if *id == "uint64".into() => match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
let v: Result<u64, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U64(v)),
_ => {
Err(format!("default param value out of range at {}", default.location))
}
}
_ => Err(format!("only allow constant integer here at {}", default.location))
}
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok(
SymbolValue::OptionSome(
Box::new(parse_parameter_default_value(&args[0], resolver)?)
)
),
_ => Err(format!("unsupported default parameter at {}", default.location)),
}
}
ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts
.iter()
.map(|x| parse_parameter_default_value(x, resolver))
.collect::<Result<Vec<_>, _>>()?
_ => Err(format!("only allow constant integer here at {}", default.location)),
},
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok(SymbolValue::OptionSome(
Box::new(parse_parameter_default_value(&args[0], resolver)?),
)),
_ => Err(format!("unsupported default parameter at {}", default.location)),
},
ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(
elts.iter()
.map(|x| parse_parameter_default_value(x, resolver))
.collect::<Result<Vec<_>, _>>()?,
)),
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
ast::ExprKind::Name { id, .. } => {
resolver.get_default_param_value(default).ok_or_else(
|| format!(
resolver.get_default_param_value(default).ok_or_else(|| {
format!(
"`{}` cannot be used as a default parameter at {} \
(not primitive type, option or tuple / not defined?)",
id,
default.location
id, default.location
)
)
})
}
_ => Err(format!(
"unsupported default parameter (not primitive type, option or tuple) at {}",
default.location
))
)),
}
}

View File

@ -34,23 +34,23 @@ impl TypeAnnotation {
}
None => format!("class_def_{}", id.0),
};
format!(
"{}{}",
class_name,
{
let param_list = params.iter().map(|p| p.stringify(unifier)).collect_vec().join(", ");
if param_list.is_empty() {
"".into()
} else {
format!("[{}]", param_list)
}
format!("{}{}", class_name, {
let param_list =
params.iter().map(|p| p.stringify(unifier)).collect_vec().join(", ");
if param_list.is_empty() {
"".into()
} else {
format!("[{}]", param_list)
}
)
})
}
Virtual(ty) => format!("virtual[{}]", ty.stringify(unifier)),
List(ty) => format!("list[{}]", ty.stringify(unifier)),
Tuple(types) => {
format!("tuple[{}]", types.iter().map(|p| p.stringify(unifier)).collect_vec().join(", "))
format!(
"tuple[{}]",
types.iter().map(|p| p.stringify(unifier)).collect_vec().join(", ")
)
}
}
}
@ -302,7 +302,7 @@ pub fn get_type_from_type_annotation_kinds(
unifier: &mut Unifier,
primitives: &PrimitiveStore,
ann: &TypeAnnotation,
subst_list: &mut Option<Vec<Type>>
subst_list: &mut Option<Vec<Type>>,
) -> Result<Type, String> {
match ann {
TypeAnnotation::CustomClass { id: obj_id, params } => {
@ -324,7 +324,7 @@ pub fn get_type_from_type_annotation_kinds(
unifier,
primitives,
x,
subst_list
subst_list,
)
})
.collect::<Result<Vec<_>, _>>()?;
@ -402,7 +402,7 @@ pub fn get_type_from_type_annotation_kinds(
unifier,
primitives,
ty.as_ref(),
subst_list
subst_list,
)?;
Ok(unifier.add_ty(TypeEnum::TVirtual { ty }))
}
@ -412,7 +412,7 @@ pub fn get_type_from_type_annotation_kinds(
unifier,
primitives,
ty.as_ref(),
subst_list
subst_list,
)?;
Ok(unifier.add_ty(TypeEnum::TList { ty }))
}
@ -420,7 +420,13 @@ pub fn get_type_from_type_annotation_kinds(
let tys = tys
.iter()
.map(|x| {
get_type_from_type_annotation_kinds(top_level_defs, unifier, primitives, x, subst_list)
get_type_from_type_annotation_kinds(
top_level_defs,
unifier,
primitives,
x,
subst_list,
)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(unifier.add_ty(TypeEnum::TTuple { ty: tys }))

View File

@ -20,8 +20,9 @@ impl<'a> Inferencer<'a> {
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<(), String> {
match &pattern.node {
ast::ExprKind::Name { id, .. } if id == &"none".into() =>
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
ast::ExprKind::Name { id, .. } if id == &"none".into() => {
Err(format!("cannot assign to a `none` (at {})", pattern.location))
}
ExprKind::Name { id, .. } => {
if !defined_identifiers.contains(id) {
defined_identifiers.insert(*id);

View File

@ -430,7 +430,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?;
match msg {
Some(m) => self.unify(m.custom.unwrap(), self.primitives.str, &m.location)?,
None => ()
None => (),
}
}
_ => return report_error("Unsupported statement type", stmt.location),
@ -464,9 +464,14 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
let var_map = params
.iter()
.map(|(id_var, ty)| {
if let TypeEnum::TVar { id, range, name, loc, .. } = &*self.unifier.get_ty(*ty) {
if let TypeEnum::TVar { id, range, name, loc, .. } =
&*self.unifier.get_ty(*ty)
{
assert_eq!(*id, *id_var);
(*id, self.unifier.get_fresh_var_with_range(range, *name, *loc).0)
(
*id,
self.unifier.get_fresh_var_with_range(range, *name, *loc).0,
)
} else {
unreachable!()
}
@ -822,7 +827,7 @@ impl<'a> Inferencer<'a> {
},
});
} else {
return report_error("Integer out of bound", args[0].location)
return report_error("Integer out of bound", args[0].location);
}
}
}
@ -842,13 +847,13 @@ impl<'a> Inferencer<'a> {
},
});
} else {
return report_error("Integer out of bound", args[0].location)
return report_error("Integer out of bound", args[0].location);
}
}
}
if id == "uint64".into() && args.len() == 1 {
if let ExprKind::Constant { value: ast::Constant::Int(val), kind } =
&args[0].node
&args[0].node
{
let custom = Some(self.primitives.uint64);
let v: Result<u64, _> = (*val).try_into();
@ -862,10 +867,62 @@ impl<'a> Inferencer<'a> {
},
});
} else {
return report_error("Integer out of bound", args[0].location)
return report_error("Integer out of bound", args[0].location);
}
}
}
// array() is a "magic" function call that determines the number of
// dimensions in the result from the nesting of the array argument type,
// to match the host Python NumPy API.
if id == "array".into() {
if args.is_empty() {
return report_error(
"`array()` expects at least one argument (contents in list form)",
func_location,
);
}
if args.len() > 2 || !keywords.is_empty() {
return report_error(
"Additional `array()` arguments not yet implemented",
func_location,
);
// TODO: Implement `dtype=` kwarg.
}
let list_arg = self.fold_expr(args.remove(0))?;
// TODO: Implement special case for emtpy arrays (e.g. `array([[]]))`)
// to match NumPy.
let mut num_dims = 0;
let mut elem_type = list_arg.custom.unwrap();
while let TypeEnum::TList { ty } = &*self.unifier.get_ty(elem_type) {
elem_type = *ty;
num_dims += 1;
}
if num_dims == 0 {
return report_error(
"expected list argument to array(), not xxx",
func_location,
);
}
let custom =
Some(self.unifier.add_ty(TypeEnum::TNDArray { ty: elem_type, num_dims }));
return Ok(Located {
location,
custom,
node: ExprKind::Call {
func: Box::new(Located {
custom: None,
location: func.location,
node: ExprKind::Name { id, ctx },
}),
args: vec![list_arg],
keywords: vec![],
},
});
}
Located { location: func_location, custom, node: ExprKind::Name { id, ctx } }
} else {
func
@ -962,8 +1019,9 @@ impl<'a> Inferencer<'a> {
Ok(self.unifier.add_ty(TypeEnum::TTuple { ty: ty? }))
}
ast::Constant::Str(_) => Ok(self.primitives.str),
ast::Constant::None
=> report_error("CPython `None` not supported (nac3 uses `none` instead)", *loc),
ast::Constant::None => {
report_error("CPython `None` not supported (nac3 uses `none` instead)", *loc)
}
_ => report_error("not supported", *loc),
}
}
@ -998,8 +1056,11 @@ impl<'a> Inferencer<'a> {
}
(None, _) => {
let t = self.unifier.stringify(ty);
report_error(&format!("`{}::{}` field/method does not exist", t, attr), value.location)
},
report_error(
&format!("`{}::{}` field/method does not exist", t, attr),
value.location,
)
}
}
} else {
let attr_ty = self.unifier.get_dummy_var().0;
@ -1033,10 +1094,8 @@ impl<'a> Inferencer<'a> {
let method = if let TypeEnum::TObj { fields, .. } =
self.unifier.get_ty_immutable(left.custom.unwrap()).as_ref()
{
let (binop_name, binop_assign_name) = (
binop_name(op).into(),
binop_assign_name(op).into()
);
let (binop_name, binop_assign_name) =
(binop_name(op).into(), binop_assign_name(op).into());
// if is aug_assign, try aug_assign operator first
if is_aug_assign && fields.contains_key(&binop_assign_name) {
binop_assign_name
@ -1097,6 +1156,7 @@ impl<'a> Inferencer<'a> {
for v in [lower.as_ref(), upper.as_ref(), step.as_ref()].iter().flatten() {
self.constrain(v.custom.unwrap(), self.primitives.int32, &v.location)?;
}
// xxx: Support TNDArray.
let list = self.unifier.add_ty(TypeEnum::TList { ty });
self.constrain(value.custom.unwrap(), list, &value.location)?;
Ok(list)
@ -1115,15 +1175,76 @@ impl<'a> Inferencer<'a> {
Ok(ty)
}
_ => {
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap())
{
return report_error("Tuple index must be a constant (KernelInvariant is also not supported)", slice.location)
match &*self.unifier.get_ty(value.custom.unwrap()) {
TypeEnum::TTuple { .. } => return report_error(
"Tuple index must be a constant (KernelInvariant is also not supported)",
slice.location,
),
TypeEnum::TNDArray { ty: elem_ty, num_dims } => {
let num_idxs = match &*self.unifier.get_ty(slice.custom.unwrap()) {
TypeEnum::TTuple { ty: idx_tys } => {
for idx_ty in idx_tys.iter() {
// xxx: NumPy supports a tuple of tuples for "advanced indexing"
// of multidimensional arrays (sequence index -> subset). We
// don't support this, but could give a better error message.
self.constrain(
*idx_ty,
self.primitives.int32,
&slice.location,
)?;
}
idx_tys.len()
}
TypeEnum::TList { .. } | TypeEnum::TNDArray { .. } => {
return report_error(
concat!(
"ndarray index is list/array, but NumPy advanced (subset)",
"indexing is not supported yet"
),
slice.location,
);
}
_ => {
// xxx: Could lead to suboptimal error message, as higher-dimensional indexing is not mentioned?!
self.constrain(
slice.custom.unwrap(),
self.primitives.int32,
&slice.location,
)?;
1
}
};
if *num_dims < num_idxs {
report_error(
&format!(
"ndarray has dimension {}, but {} indices supplied",
num_dims, num_idxs
),
slice.location,
)
} else if *num_dims == num_idxs {
Ok(*elem_ty)
} else {
Ok(self.unifier.add_ty(TypeEnum::TNDArray {
ty: *elem_ty,
num_dims: *num_dims - num_idxs,
}))
}
}
_ => {
// the index is not a constant, so value can only be a list
// xxx: Or an ndarray now, so remove the constraint?
self.constrain(
slice.custom.unwrap(),
self.primitives.int32,
&slice.location,
)?;
let list = self.unifier.add_ty(TypeEnum::TList { ty });
self.constrain(value.custom.unwrap(), list, &value.location)?;
Ok(ty)
}
}
// the index is not a constant, so value can only be a list
self.constrain(slice.custom.unwrap(), self.primitives.int32, &slice.location)?;
let list = self.unifier.add_ty(TypeEnum::TList { ty });
self.constrain(value.custom.unwrap(), list, &value.location)?;
Ok(ty)
}
}
}

View File

@ -513,6 +513,20 @@ impl TestEnvironment {
[("a", "list[int32]"), ("b", "list[int32]")].iter().cloned().collect(),
&[]
; "listcomp test")]
#[test_case(
indoc! {"
a = array([1, 2])
a0 = a[0]
b = array([[1, 2], [3, 4]])
b0 = b[0]
b00 = b[0, 0]
c = 1
ac = a[c]
"},
[("a", "ndarray[int32, 1]"), ("a0", "int32"), ("b", "ndarray[int32, 2]"),
("b0", "ndarray[int32, 1]"), ("b00", "int32"), ("ac", "int32")].iter().cloned().collect(),
&[]
; "array test")]
#[test_case(indoc! {"
a = virtual(Bar(), Bar)
b = a.b()
@ -533,6 +547,7 @@ fn test_basic(source: &str, mapping: HashMap<&str, &str>, virtuals: &[(&str, &st
let mut env = TestEnvironment::new();
let id_to_name = std::mem::take(&mut env.id_to_name);
let mut defined_identifiers: HashSet<_> = env.identifier_mapping.keys().cloned().collect();
defined_identifiers.insert("array".into());
defined_identifiers.insert("virtual".into());
let mut inferencer = env.get_inferencer();
inferencer.defined_identifiers = defined_identifiers.clone();

View File

@ -137,6 +137,13 @@ pub enum TypeEnum {
TList {
ty: Type,
},
TNDArray {
ty: Type,
// We could introduce a more sensible limit for the number of dimensions
// and make this e.g. u8; usize for now to avoid some casts.
num_dims: usize,
},
TObj {
obj_id: DefinitionId,
fields: Mapping<StrRef, (Type, bool)>,
@ -156,6 +163,7 @@ impl TypeEnum {
TypeEnum::TVar { .. } => "TVar",
TypeEnum::TTuple { .. } => "TTuple",
TypeEnum::TList { .. } => "TList",
TypeEnum::TNDArray { .. } => "TNDArray",
TypeEnum::TObj { .. } => "TObj",
TypeEnum::TVirtual { .. } => "TVirtual",
TypeEnum::TCall { .. } => "TCall",
@ -173,7 +181,7 @@ pub struct Unifier {
pub(crate) calls: Vec<Rc<Call>>,
var_id: u32,
unify_cache: HashSet<(Type, Type)>,
snapshot: Option<(usize, u32)>
snapshot: Option<(usize, u32)>,
}
impl Default for Unifier {
@ -387,6 +395,7 @@ impl Unifier {
TVar { .. } => allowed_typevars.iter().any(|b| self.unification_table.unioned(a, *b)),
TCall { .. } => false,
TList { ty } => self.is_concrete(*ty, allowed_typevars),
TNDArray { ty, .. } => self.is_concrete(*ty, allowed_typevars),
TTuple { ty } => ty.iter().all(|ty| self.is_concrete(*ty, allowed_typevars)),
TObj { params: vars, .. } => {
vars.values().all(|ty| self.is_concrete(*ty, allowed_typevars))
@ -459,13 +468,10 @@ impl Unifier {
if let Some(i) = required.iter().position(|v| v == k) {
required.remove(i);
}
let i = all_names
.iter()
.position(|v| &v.0 == k)
.ok_or_else(|| {
self.restore_snapshot();
TypeError::new(TypeErrorKind::UnknownArgName(*k), *loc)
})?;
let i = all_names.iter().position(|v| &v.0 == k).ok_or_else(|| {
self.restore_snapshot();
TypeError::new(TypeErrorKind::UnknownArgName(*k), *loc)
})?;
let (name, expected) = all_names.remove(i);
self.unify_impl(expected, *t, false).map_err(|_| {
self.restore_snapshot();
@ -652,6 +658,28 @@ impl Unifier {
self.unify_impl(x, b, false)?;
self.set_a_to_b(a, x);
}
(TVar { fields: Some(fields), range, .. }, TNDArray { ty, num_dims }) => {
for (k, v) in fields.iter() {
match *k {
RecordKey::Int(_) => {
// .<n> is generated during generic scalar indexing lowering.
let indexed_ty = if *num_dims == 1 {
*ty
} else {
self.add_ty(TNDArray { ty: *ty, num_dims: *num_dims - 1 })
};
self.unify_impl(v.ty, indexed_ty, false).map_err(|e| e.at(v.loc))?
}
RecordKey::Str(_) => {
// xxx: Implement .shape here?
return Err(TypeError::new(TypeErrorKind::NoSuchField(*k, b), v.loc));
}
}
}
let x = self.check_var_compatibility(b, range)?.unwrap_or(b);
self.unify_impl(x, b, false)?;
self.set_a_to_b(a, x);
}
(TTuple { ty: ty1 }, TTuple { ty: ty2 }) => {
if ty1.len() != ty2.len() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
@ -907,6 +935,13 @@ impl Unifier {
TypeEnum::TList { ty } => {
format!("list[{}]", self.internal_stringify(*ty, obj_to_name, var_to_name, notes))
}
TypeEnum::TNDArray { ty, num_dims } => {
format!(
"ndarray[{}, {}]",
self.internal_stringify(*ty, obj_to_name, var_to_name, notes),
num_dims
)
}
TypeEnum::TVirtual { ty } => {
format!(
"virtual[{}]",

View File

@ -479,7 +479,8 @@ fn test_typevar_range() {
assert_eq!(
env.unify(a_list, int_list),
Err("Incompatible types: list[typevar22] and list[0]\
\n\nNotes:\n typevar22 {1}".into())
\n\nNotes:\n typevar22 {1}"
.into())
);
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
@ -507,7 +508,10 @@ fn test_rigid_var() {
assert_eq!(env.unify(a, b), Err("Incompatible types: typevar3 and typevar2".to_string()));
env.unifier.unify(list_a, list_x).unwrap();
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: list[typevar2] and list[0]".to_string()));
assert_eq!(
env.unify(list_x, list_int),
Err("Incompatible types: list[typevar2] and list[0]".to_string())
);
env.unifier.replace_rigid_var(a, int);
env.unifier.unify(list_x, list_int).unwrap();

View File

@ -16,21 +16,10 @@ pub struct UnificationTable<V> {
#[derive(Clone, Debug)]
enum Action<V> {
Parent {
key: usize,
original_parent: usize,
},
Value {
key: usize,
original_value: Option<V>,
},
Rank {
key: usize,
original_rank: u32,
},
Marker {
generation: u32,
}
Parent { key: usize, original_parent: usize },
Value { key: usize, original_value: Option<V> },
Rank { key: usize, original_rank: u32 },
Marker { generation: u32 },
}
impl<V> Default for UnificationTable<V> {
@ -41,7 +30,13 @@ impl<V> Default for UnificationTable<V> {
impl<V> UnificationTable<V> {
pub fn new() -> UnificationTable<V> {
UnificationTable { parents: Vec::new(), ranks: Vec::new(), values: Vec::new(), log: Vec::new(), generation: 0 }
UnificationTable {
parents: Vec::new(),
ranks: Vec::new(),
values: Vec::new(),
log: Vec::new(),
generation: 0,
}
}
pub fn new_key(&mut self, v: V) -> UnificationKey {
@ -125,7 +120,10 @@ impl<V> UnificationTable<V> {
pub fn restore_snapshot(&mut self, snapshot: (usize, u32)) {
let (log_len, generation) = snapshot;
assert!(self.log.len() >= log_len, "snapshot restoration error");
assert!(matches!(self.log[log_len - 1], Action::Marker { generation: gen } if gen == generation), "snapshot restoration error");
assert!(
matches!(self.log[log_len - 1], Action::Marker { generation: gen } if gen == generation),
"snapshot restoration error"
);
for action in self.log.drain(log_len - 1..).rev() {
match action {
Action::Parent { key, original_parent } => {
@ -145,7 +143,10 @@ impl<V> UnificationTable<V> {
pub fn discard_snapshot(&mut self, snapshot: (usize, u32)) {
let (log_len, generation) = snapshot;
assert!(self.log.len() >= log_len, "snapshot discard error");
assert!(matches!(self.log[log_len - 1], Action::Marker { generation: gen } if gen == generation), "snapshot discard error");
assert!(
matches!(self.log[log_len - 1], Action::Marker { generation: gen } if gen == generation),
"snapshot discard error"
);
self.log.clear();
}
}
@ -159,11 +160,23 @@ where
.enumerate()
.map(|(i, (v, p))| if *p == i { v.as_ref().map(|v| v.as_ref().clone()) } else { None })
.collect();
UnificationTable { parents: self.parents.clone(), ranks: self.ranks.clone(), values, log: Vec::new(), generation: 0 }
UnificationTable {
parents: self.parents.clone(),
ranks: self.ranks.clone(),
values,
log: Vec::new(),
generation: 0,
}
}
pub fn from_send(table: &UnificationTable<V>) -> UnificationTable<Rc<V>> {
let values = table.values.iter().cloned().map(|v| v.map(Rc::new)).collect();
UnificationTable { parents: table.parents.clone(), ranks: table.ranks.clone(), values, log: Vec::new(), generation: 0 }
UnificationTable {
parents: table.parents.clone(),
ranks: table.ranks.clone(),
values,
log: Vec::new(),
generation: 0,
}
}
}

View File

@ -1,15 +1,15 @@
use lalrpop_util::ParseError;
use nac3ast::*;
use crate::ast::Ident;
use crate::ast::Location;
use crate::token::Tok;
use crate::error::*;
use crate::token::Tok;
use lalrpop_util::ParseError;
use nac3ast::*;
pub fn make_config_comment(
com_loc: Location,
stmt_loc: Location,
nac3com_above: Vec<(Ident, Tok)>,
nac3com_end: Option<Ident>
nac3com_end: Option<Ident>,
) -> Result<Vec<Ident>, ParseError<Location, Tok, LexicalError>> {
if com_loc.column() != stmt_loc.column() && !nac3com_above.is_empty() {
return Err(ParseError::User {
@ -23,18 +23,21 @@ pub fn make_config_comment(
)
)
}
})
});
};
Ok(
nac3com_above
.into_iter()
.map(|(com, _)| com)
.chain(nac3com_end.map_or_else(|| vec![].into_iter(), |com| vec![com].into_iter()))
.collect()
)
Ok(nac3com_above
.into_iter()
.map(|(com, _)| com)
.chain(nac3com_end.map_or_else(|| vec![].into_iter(), |com| vec![com].into_iter()))
.collect())
}
pub fn handle_small_stmt<U>(stmts: &mut [Stmt<U>], nac3com_above: Vec<(Ident, Tok)>, nac3com_end: Option<Ident>, com_above_loc: Location) -> Result<(), ParseError<Location, Tok, LexicalError>> {
pub fn handle_small_stmt<U>(
stmts: &mut [Stmt<U>],
nac3com_above: Vec<(Ident, Tok)>,
nac3com_end: Option<Ident>,
com_above_loc: Location,
) -> Result<(), ParseError<Location, Tok, LexicalError>> {
if com_above_loc.column() != stmts[0].location.column() && !nac3com_above.is_empty() {
return Err(ParseError::User {
error: LexicalError {
@ -47,17 +50,12 @@ pub fn handle_small_stmt<U>(stmts: &mut [Stmt<U>], nac3com_above: Vec<(Ident, To
)
)
}
})
});
}
apply_config_comments(
&mut stmts[0],
nac3com_above
.into_iter()
.map(|(com, _)| com).collect()
);
apply_config_comments(&mut stmts[0], nac3com_above.into_iter().map(|(com, _)| com).collect());
apply_config_comments(
stmts.last_mut().unwrap(),
nac3com_end.map_or_else(Vec::new, |com| vec![com])
nac3com_end.map_or_else(Vec::new, |com| vec![com]),
);
Ok(())
}
@ -72,7 +70,7 @@ fn apply_config_comments<U>(stmt: &mut Stmt<U>, comments: Vec<Ident>) {
| StmtKind::AnnAssign { config_comment, .. }
| StmtKind::Break { config_comment, .. }
| StmtKind::Continue { config_comment, .. }
| StmtKind::Return { config_comment, .. }
| StmtKind::Return { config_comment, .. }
| StmtKind::Raise { config_comment, .. }
| StmtKind::Import { config_comment, .. }
| StmtKind::ImportFrom { config_comment, .. }
@ -80,6 +78,8 @@ fn apply_config_comments<U>(stmt: &mut Stmt<U>, comments: Vec<Ident>) {
| StmtKind::Nonlocal { config_comment, .. }
| StmtKind::Assert { config_comment, .. } => config_comment.extend(comments),
_ => { unreachable!("only small statements should call this function") }
_ => {
unreachable!("only small statements should call this function")
}
}
}

View File

@ -145,35 +145,27 @@ impl From<LalrpopError<Location, Tok, LexicalError>> for ParseError {
fn from(err: LalrpopError<Location, Tok, LexicalError>) -> Self {
match err {
// TODO: Are there cases where this isn't an EOF?
LalrpopError::InvalidToken { location } => ParseError {
error: ParseErrorType::Eof,
location,
},
LalrpopError::ExtraToken { token } => ParseError {
error: ParseErrorType::ExtraToken(token.1),
location: token.0,
},
LalrpopError::User { error } => ParseError {
error: ParseErrorType::Lexical(error.error),
location: error.location,
},
LalrpopError::InvalidToken { location } => {
ParseError { error: ParseErrorType::Eof, location }
}
LalrpopError::ExtraToken { token } => {
ParseError { error: ParseErrorType::ExtraToken(token.1), location: token.0 }
}
LalrpopError::User { error } => {
ParseError { error: ParseErrorType::Lexical(error.error), location: error.location }
}
LalrpopError::UnrecognizedToken { token, expected } => {
// Hacky, but it's how CPython does it. See PyParser_AddToken,
// in particular "Only one possible expected token" comment.
let expected = if expected.len() == 1 {
Some(expected[0].clone())
} else {
None
};
let expected = if expected.len() == 1 { Some(expected[0].clone()) } else { None };
ParseError {
error: ParseErrorType::UnrecognizedToken(token.1, expected),
location: token.0,
}
}
LalrpopError::UnrecognizedEOF { location, .. } => ParseError {
error: ParseErrorType::Eof,
location,
},
LalrpopError::UnrecognizedEOF { location, .. } => {
ParseError { error: ParseErrorType::Eof, location }
}
}
}
}

View File

@ -15,10 +15,7 @@ struct FStringParser<'a> {
impl<'a> FStringParser<'a> {
fn new(source: &'a str, str_location: Location) -> Self {
Self {
chars: source.chars().peekable(),
str_location,
}
Self { chars: source.chars().peekable(), str_location }
}
#[inline]
@ -251,17 +248,11 @@ impl<'a> FStringParser<'a> {
}
if !content.is_empty() {
values.push(self.expr(ExprKind::Constant {
value: content.into(),
kind: None,
}))
values.push(self.expr(ExprKind::Constant { value: content.into(), kind: None }))
}
let s = match values.len() {
0 => self.expr(ExprKind::Constant {
value: String::new().into(),
kind: None,
}),
0 => self.expr(ExprKind::Constant { value: String::new().into(), kind: None }),
1 => values.into_iter().next().unwrap(),
_ => self.expr(ExprKind::JoinedStr { values }),
};
@ -277,9 +268,7 @@ fn parse_fstring_expr(source: &str) -> Result<Expr, ParseError> {
/// Parse an fstring from a string, located at a certain position in the sourcecode.
/// In case of errors, we will get the location and the error returned.
pub fn parse_located_fstring(source: &str, location: Location) -> Result<Expr, FStringError> {
FStringParser::new(source, location)
.parse()
.map_err(|error| FStringError { error, location })
FStringParser::new(source, location).parse().map_err(|error| FStringError { error, location })
}
#[cfg(test)]

View File

@ -69,10 +69,7 @@ pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ArgumentList, Lexi
keywords.push(ast::Keyword::new(
location,
ast::KeywordData {
arg: name.map(|name| name.into()),
value: Box::new(value),
},
ast::KeywordData { arg: name.map(|name| name.into()), value: Box::new(value) },
));
}
None => {

View File

@ -3,12 +3,12 @@
//! This means source code is translated into separate tokens.
pub use super::token::Tok;
use crate::ast::{Location, FileName};
use crate::ast::{FileName, Location};
use crate::error::{LexicalError, LexicalErrorType};
use std::char;
use std::cmp::Ordering;
use std::str::FromStr;
use std::num::IntErrorKind;
use std::str::FromStr;
use unic_emoji_char::is_emoji_presentation;
use unic_ucd_ident::{is_xid_continue, is_xid_start};
@ -32,20 +32,14 @@ impl IndentationLevel {
if self.spaces <= other.spaces {
Ok(Ordering::Less)
} else {
Err(LexicalError {
location,
error: LexicalErrorType::TabError,
})
Err(LexicalError { location, error: LexicalErrorType::TabError })
}
}
Ordering::Greater => {
if self.spaces >= other.spaces {
Ok(Ordering::Greater)
} else {
Err(LexicalError {
location,
error: LexicalErrorType::TabError,
})
Err(LexicalError { location, error: LexicalErrorType::TabError })
}
}
Ordering::Equal => Ok(self.spaces.cmp(&other.spaces)),
@ -63,7 +57,7 @@ pub struct Lexer<T: Iterator<Item = char>> {
chr1: Option<char>,
chr2: Option<char>,
location: Location,
config_comment_prefix: Option<&'static str>
config_comment_prefix: Option<&'static str>,
}
pub static KEYWORDS: phf::Map<&'static str, Tok> = phf::phf_map! {
@ -136,11 +130,7 @@ where
T: Iterator<Item = char>,
{
pub fn new(source: T) -> Self {
let mut nlh = NewlineHandler {
source,
chr0: None,
chr1: None,
};
let mut nlh = NewlineHandler { source, chr0: None, chr1: None };
nlh.shift();
nlh.shift();
nlh
@ -195,7 +185,7 @@ where
location: start,
chr1: None,
chr2: None,
config_comment_prefix: Some(" nac3:")
config_comment_prefix: Some(" nac3:"),
};
lxr.next_char();
lxr.next_char();
@ -287,15 +277,15 @@ where
let end_pos = self.get_pos();
let value = match i128::from_str_radix(&value_text, radix) {
Ok(value) => value,
Err(e) => {
match e.kind() {
IntErrorKind::PosOverflow | IntErrorKind::NegOverflow => i128::MAX,
_ => return Err(LexicalError {
Err(e) => match e.kind() {
IntErrorKind::PosOverflow | IntErrorKind::NegOverflow => i128::MAX,
_ => {
return Err(LexicalError {
error: LexicalErrorType::OtherError(format!("{:?}", e)),
location: start_pos,
}),
})
}
}
},
};
Ok((start_pos, Tok::Int { value }, end_pos))
}
@ -338,14 +328,7 @@ where
if self.chr0 == Some('j') || self.chr0 == Some('J') {
self.next_char();
let end_pos = self.get_pos();
Ok((
start_pos,
Tok::Complex {
real: 0.0,
imag: value,
},
end_pos,
))
Ok((start_pos, Tok::Complex { real: 0.0, imag: value }, end_pos))
} else {
let end_pos = self.get_pos();
Ok((start_pos, Tok::Float { value }, end_pos))
@ -364,7 +347,7 @@ where
let value = value_text.parse::<i128>().ok();
let nonzero = match value {
Some(value) => value != 0i128,
None => true
None => true,
};
if start_is_zero && nonzero {
return Err(LexicalError {
@ -433,9 +416,8 @@ where
fn lex_comment(&mut self) -> Option<Spanned> {
self.next_char();
// if possibly nac3 pseudocomment, special handling for `# nac3:`
let (mut prefix, mut is_comment) = self
.config_comment_prefix
.map_or_else(|| ("".chars(), false), |v| (v.chars(), true));
let (mut prefix, mut is_comment) =
self.config_comment_prefix.map_or_else(|| ("".chars(), false), |v| (v.chars(), true));
// for the correct location of config comment
let mut start_loc = self.location;
start_loc.go_left();
@ -460,22 +442,20 @@ where
return Some((
start_loc,
Tok::ConfigComment { content: content.trim().into() },
self.location
self.location,
));
}
}
}
}
self.next_char();
};
}
}
fn unicode_literal(&mut self, literal_number: usize) -> Result<char, LexicalError> {
let mut p: u32 = 0u32;
let unicode_error = LexicalError {
error: LexicalErrorType::UnicodeError,
location: self.get_pos(),
};
let unicode_error =
LexicalError { error: LexicalErrorType::UnicodeError, location: self.get_pos() };
for i in 1..=literal_number {
match self.next_char() {
Some(c) => match c.to_digit(16) {
@ -530,10 +510,8 @@ where
}
}
}
unicode_names2::character(&name).ok_or(LexicalError {
error: LexicalErrorType::UnicodeError,
location: start_pos,
})
unicode_names2::character(&name)
.ok_or(LexicalError { error: LexicalErrorType::UnicodeError, location: start_pos })
}
fn lex_string(
@ -650,14 +628,9 @@ where
let end_pos = self.get_pos();
let tok = if is_bytes {
Tok::Bytes {
value: string_content.chars().map(|c| c as u8).collect(),
}
Tok::Bytes { value: string_content.chars().map(|c| c as u8).collect() }
} else {
Tok::String {
value: string_content,
is_fstring,
}
Tok::String { value: string_content, is_fstring }
};
Ok((start_pos, tok, end_pos))
@ -842,11 +815,7 @@ where
let tok_start = self.get_pos();
self.next_char();
let tok_end = self.get_pos();
self.emit((
tok_start,
Tok::Name { name: c.to_string().into() },
tok_end,
));
self.emit((tok_start, Tok::Name { name: c.to_string().into() }, tok_end));
} else {
self.consume_character(c)?;
}
@ -1439,14 +1408,8 @@ class Foo(A, B):
assert_eq!(
tokens,
vec![
Tok::String {
value: "\\\\".to_owned(),
is_fstring: false,
},
Tok::String {
value: "\\".to_owned(),
is_fstring: false,
},
Tok::String { value: "\\\\".to_owned(), is_fstring: false },
Tok::String { value: "\\".to_owned(), is_fstring: false },
Tok::Newline,
]
);
@ -1459,27 +1422,13 @@ class Foo(A, B):
assert_eq!(
tokens,
vec![
Tok::Int {
value: 47i128,
},
Tok::Int {
value: 13i128,
},
Tok::Int {
value: 0i128,
},
Tok::Int {
value: 123i128,
},
Tok::Int { value: 47i128 },
Tok::Int { value: 13i128 },
Tok::Int { value: 0i128 },
Tok::Int { value: 123i128 },
Tok::Float { value: 0.2 },
Tok::Complex {
real: 0.0,
imag: 2.0,
},
Tok::Complex {
real: 0.0,
imag: 2.2,
},
Tok::Complex { real: 0.0, imag: 2.0 },
Tok::Complex { real: 0.0, imag: 2.2 },
Tok::Newline,
]
);
@ -1539,21 +1488,13 @@ class Foo(A, B):
assert_eq!(
tokens,
vec![
Tok::Name {
name: String::from("avariable").into(),
},
Tok::Name { name: String::from("avariable").into() },
Tok::Equal,
Tok::Int {
value: 99i128
},
Tok::Int { value: 99i128 },
Tok::Plus,
Tok::Int {
value: 2i128
},
Tok::Int { value: 2i128 },
Tok::Minus,
Tok::Int {
value: 0i128
},
Tok::Int { value: 0i128 },
Tok::Newline,
]
);
@ -1740,42 +1681,15 @@ class Foo(A, B):
assert_eq!(
tokens,
vec![
Tok::String {
value: String::from("double"),
is_fstring: false,
},
Tok::String {
value: String::from("single"),
is_fstring: false,
},
Tok::String {
value: String::from("can't"),
is_fstring: false,
},
Tok::String {
value: String::from("\\\""),
is_fstring: false,
},
Tok::String {
value: String::from("\t\r\n"),
is_fstring: false,
},
Tok::String {
value: String::from("\\g"),
is_fstring: false,
},
Tok::String {
value: String::from("raw\\'"),
is_fstring: false,
},
Tok::String {
value: String::from("Đ"),
is_fstring: false,
},
Tok::String {
value: String::from("\u{80}\u{0}a"),
is_fstring: false,
},
Tok::String { value: String::from("double"), is_fstring: false },
Tok::String { value: String::from("single"), is_fstring: false },
Tok::String { value: String::from("can't"), is_fstring: false },
Tok::String { value: String::from("\\\""), is_fstring: false },
Tok::String { value: String::from("\t\r\n"), is_fstring: false },
Tok::String { value: String::from("\\g"), is_fstring: false },
Tok::String { value: String::from("raw\\'"), is_fstring: false },
Tok::String { value: String::from("Đ"), is_fstring: false },
Tok::String { value: String::from("\u{80}\u{0}a"), is_fstring: false },
Tok::Newline,
]
);
@ -1840,41 +1754,17 @@ class Foo(A, B):
fn test_raw_byte_literal() {
let source = r"rb'\x1z'";
let tokens = lex_source(source);
assert_eq!(
tokens,
vec![
Tok::Bytes {
value: b"\\x1z".to_vec()
},
Tok::Newline
]
);
assert_eq!(tokens, vec![Tok::Bytes { value: b"\\x1z".to_vec() }, Tok::Newline]);
let source = r"rb'\\'";
let tokens = lex_source(source);
assert_eq!(
tokens,
vec![
Tok::Bytes {
value: b"\\\\".to_vec()
},
Tok::Newline
]
)
assert_eq!(tokens, vec![Tok::Bytes { value: b"\\\\".to_vec() }, Tok::Newline])
}
#[test]
fn test_escape_octet() {
let source = r##"b'\43a\4\1234'"##;
let tokens = lex_source(source);
assert_eq!(
tokens,
vec![
Tok::Bytes {
value: b"#a\x04S4".to_vec()
},
Tok::Newline
]
)
assert_eq!(tokens, vec![Tok::Bytes { value: b"#a\x04S4".to_vec() }, Tok::Newline])
}
#[test]
@ -1883,13 +1773,7 @@ class Foo(A, B):
let tokens = lex_source(source);
assert_eq!(
tokens,
vec![
Tok::String {
value: "\u{2002}".to_owned(),
is_fstring: false,
},
Tok::Newline
]
vec![Tok::String { value: "\u{2002}".to_owned(), is_fstring: false }, Tok::Newline]
)
}
}

View File

@ -31,5 +31,5 @@ lalrpop_mod!(
#[allow(unused)]
python
);
pub mod token;
pub mod config_comment_helper;
pub mod token;

View File

@ -75,9 +75,7 @@ pub fn parse(source: &str, mode: Mode, file: FileName) -> Result<ast::Mod, Parse
let marker_token = (Default::default(), mode.to_marker(), Default::default());
let tokenizer = iter::once(Ok(marker_token)).chain(lxr);
python::TopParser::new()
.parse(tokenizer)
.map_err(ParseError::from)
python::TopParser::new().parse(tokenizer).map_err(ParseError::from)
}
#[cfg(test)]
@ -163,7 +161,7 @@ class Foo(A, B):
let parse_ast = parse_expression(&source).unwrap();
insta::assert_debug_snapshot!(parse_ast);
}
#[test]
fn test_more_comment() {
let source = "\
@ -185,7 +183,7 @@ while i < 2: # nac3: 4
3";
insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
}
#[test]
fn test_sample_comment() {
let source = "\

View File

@ -1,7 +1,7 @@
//! Different token definitions.
//! Loosely based on token.h from CPython source:
use std::fmt::{self, Write};
use crate::ast;
use std::fmt::{self, Write};
/// Python source code can be tokenized in a sequence of these tokens.
#[derive(Clone, Debug, PartialEq)]
@ -111,8 +111,16 @@ impl fmt::Display for Tok {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Tok::*;
match self {
Name { name } => write!(f, "'{}'", ast::get_str_from_ref(&ast::get_str_ref_lock(), *name)),
Int { value } => if *value != i128::MAX { write!(f, "'{}'", value) } else { write!(f, "'#OFL#'") },
Name { name } => {
write!(f, "'{}'", ast::get_str_from_ref(&ast::get_str_ref_lock(), *name))
}
Int { value } => {
if *value != i128::MAX {
write!(f, "'{}'", value)
} else {
write!(f, "'#OFL#'")
}
}
Float { value } => write!(f, "'{}'", value),
Complex { real, imag } => write!(f, "{}j{}", real, imag),
String { value, is_fstring } => {
@ -134,7 +142,11 @@ impl fmt::Display for Tok {
}
f.write_str("\"")
}
ConfigComment { content } => write!(f, "ConfigComment: '{}'", ast::get_str_from_ref(&ast::get_str_ref_lock(), *content)),
ConfigComment { content } => write!(
f,
"ConfigComment: '{}'",
ast::get_str_from_ref(&ast::get_str_ref_lock(), *content)
),
Newline => f.write_str("Newline"),
Indent => f.write_str("Indent"),
Dedent => f.write_str("Dedent"),

View File

@ -13,4 +13,4 @@ nac3core = { path = "../nac3core" }
git = "https://github.com/nbaksalyar/inkwell.git"
branch = "llvm14"
default-features = false
features = ["llvm14-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
features = ["llvm14-0", "target-aarch64", "target-arm", "target-riscv", "no-libffi-linking"]

View File

@ -52,22 +52,16 @@ fn handle_typevar_definition(
Default::default(),
)?;
get_type_from_type_annotation_kinds(
def_list, unifier, primitives, &ty, &mut None
def_list, unifier, primitives, &ty, &mut None,
)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(unifier.get_fresh_var_with_range(&constraints, None, None).0)
} else {
Err(format!(
"expression {:?} cannot be handled as a TypeVar in global scope",
var
))
Err(format!("expression {:?} cannot be handled as a TypeVar in global scope", var))
}
} else {
Err(format!(
"expression {:?} cannot be handled as a TypeVar in global scope",
var
))
Err(format!("expression {:?} cannot be handled as a TypeVar in global scope", var))
}
}
@ -92,15 +86,13 @@ fn handle_assignment_pattern(
) {
internal_resolver.add_id_type(*id, var);
Ok(())
} else if let Ok(val) =
parse_parameter_default_value(value.borrow(), resolver)
{
} else if let Ok(val) = parse_parameter_default_value(value.borrow(), resolver) {
internal_resolver.add_module_global(*id, val);
Ok(())
} else {
Err(format!("fails to evaluate this expression `{:?}` as a constant or TypeVar at {}",
targets[0].node,
targets[0].location,
Err(format!(
"fails to evaluate this expression `{:?}` as a constant or TypeVar at {}",
targets[0].node, targets[0].location,
))
}
}
@ -146,10 +138,7 @@ fn handle_assignment_pattern(
Ok(())
}
}
_ => Err(format!(
"unpack of this expression is not supported at {}",
value.location
)),
_ => Err(format!("unpack of this expression is not supported at {}", value.location)),
}
}
}
@ -178,7 +167,8 @@ fn main() {
class_names: Default::default(),
module_globals: Default::default(),
str_store: Default::default(),
}.into();
}
.into();
let resolver =
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
@ -202,13 +192,19 @@ fn main() {
eprintln!("{}", err);
return;
}
},
}
// allow (and ignore) "from __future__ import annotations"
StmtKind::ImportFrom { module, names, .. }
if module == &Some("__future__".into()) && names.len() == 1 && names[0].name == "annotations".into() => (),
if module == &Some("__future__".into())
&& names.len() == 1
&& names[0].name == "annotations".into() =>
{
()
}
_ => {
let (name, def_id, ty) =
composer.register_top_level(stmt, Some(resolver.clone()), "__main__".into()).unwrap();
let (name, def_id, ty) = composer
.register_top_level(stmt, Some(resolver.clone()), "__main__".into())
.unwrap();
internal_resolver.add_id_def(name, def_id);
if let Some(ty) = ty {
internal_resolver.add_id_type(name, ty);

View File

@ -115,7 +115,7 @@ in stdenv.mkDerivation (rec {
"-DLLVM_ENABLE_THREADS=OFF"
"-DLLVM_INCLUDE_BENCHMARKS=OFF"
"-DLLVM_BUILD_TOOLS=OFF"
"-DLLVM_TARGETS_TO_BUILD=X86;ARM;RISCV"
"-DLLVM_TARGETS_TO_BUILD=host;ARM;RISCV"
] ++ optionals enableSharedLibraries [
"-DLLVM_LINK_LLVM_DYLIB=ON"
] ++ optionals enableManpages [

View File

@ -47,12 +47,11 @@ pub extern "C" fn __nac3_personality(_state: u32, _exception_object: u32, _conte
unimplemented!();
}
fn main() {
let filename = env::args().nth(1).unwrap();
unsafe {
let lib = libloading::Library::new(filename).unwrap();
let func: libloading::Symbol<unsafe extern fn()> = lib.get(b"__modinit__").unwrap();
let func: libloading::Symbol<unsafe extern "C" fn()> = lib.get(b"__modinit__").unwrap();
func()
}
}