use verilization_compiler::{lang, model, util, for_sep};
use model::Named;
use lang::{GeneratorError, Language, OutputHandler};
use std::ffi::OsString;
use std::collections::{HashMap, HashSet};
use std::io::Write;
use std::path::PathBuf;
use num_bigint::BigUint;
use lang::generator::*;
use util::{capitalize_identifier, uncapitalize_identifier};
use num_traits::ToPrimitive;
type PackageMap = HashMap<model::PackageName, model::PackageName>;
const RUNTIME_PACKAGE: &str = "dev.argon.verilization.scala_runtime";
pub struct ScalaOptionsBuilder {
output_dir: Option<OsString>,
package_mapping: PackageMap,
library_mapping: PackageMap,
}
pub struct ScalaOptions {
pub output_dir: OsString,
pub package_mapping: PackageMap,
pub library_mapping: PackageMap,
}
fn make_type_name(name: &str) -> String {
let mut name = String::from(name);
capitalize_identifier(&mut name);
name
}
fn make_field_name(field_name: &str) -> String {
let mut name = String::from(field_name);
uncapitalize_identifier(&mut name);
name
}
fn scala_package_impl<'a>(options: &'a ScalaOptions, package: &model::PackageName) -> Result<&'a model::PackageName, GeneratorError> {
options.package_mapping.get(&package)
.or_else(|| options.library_mapping.get(&package))
.ok_or_else(|| GeneratorError::UnmappedPackage(package.clone()))
}
fn open_scala_file<'output, Output: OutputHandler<'output>>(options: &ScalaOptions, output: &'output mut Output, name: &model::QualifiedName) -> Result<Output::FileHandle, GeneratorError> {
let java_pkg = scala_package_impl(options, &name.package)?;
let mut path = PathBuf::from(&options.output_dir);
for part in &java_pkg.package {
path.push(part);
}
path.push(name.name.clone() + ".scala");
Ok(output.create_file(path)?)
}
pub trait ScalaGenerator<'model, 'opt> : Generator<'model> + GeneratorWithFile {
fn options(&self) -> &'opt ScalaOptions;
fn referenced_types(&self) -> model::ReferencedTypeIterator<'model>;
fn scala_package(&self, package: &model::PackageName) -> Result<&'opt model::PackageName, GeneratorError> {
scala_package_impl(&self.options(), package)
}
fn write_package(&mut self, package: &model::PackageName) -> Result<(), GeneratorError> {
let pkg = self.scala_package(package)?;
let mut pkg_iter = pkg.package.iter();
if let Some(part) = pkg_iter.next() {
write!(self.file(), "package {}", part)?;
while let Some(part) = pkg_iter.next() {
write!(self.file(), ".{}", part)?;
}
writeln!(self.file())?;
}
Ok(())
}
fn write_qual_name(&mut self, name: &model::QualifiedName) -> Result<(), GeneratorError> {
let pkg = self.scala_package(&name.package)?;
for part in &pkg.package {
write!(self.file(), "{}.", part)?;
}
write!(self.file(), "{}", make_type_name(&name.name))?;
Ok(())
}
fn write_type_args(&mut self, args: &Vec<LangType<'model>>) -> Result<(), GeneratorError> {
if !args.is_empty() {
write!(self.file(), "[")?;
for_sep!(arg, args, { write!(self.file(), ", ")?; }, {
self.write_type(arg)?;
});
write!(self.file(), "]")?;
}
Ok(())
}
fn write_type(&mut self, t: &LangType<'model>) -> Result<(), GeneratorError> {
Ok(match t {
LangType::Versioned(_, name, version, args, _) => {
self.write_qual_name(&name)?;
write!(self.file(), ".V{}", version)?;
self.write_type_args(args)?;
},
LangType::Extern(name, args, _) => {
self.write_qual_name(name)?;
self.write_type_args(args)?;
},
LangType::TypeParameter(name) => {
write!(self.file(), "{}", name)?;
},
LangType::Converter(from, to) => {
write!(self.file(), "{}.Converter[", RUNTIME_PACKAGE)?;
self.write_type(&*from)?;
write!(self.file(), ", ")?;
self.write_type(&*to)?;
write!(self.file(), "]")?;
},
LangType::Codec(t) => {
write!(self.file(), "{}.Codec[", RUNTIME_PACKAGE)?;
self.write_type(&*t)?;
write!(self.file(), "]")?;
},
})
}
fn write_args(&mut self, args: &Vec<LangExpr<'model>>) -> Result<(), GeneratorError> {
if !args.is_empty() {
write!(self.file(), "(")?;
for_sep!(arg, args, { write!(self.file(), ", ")?; }, {
self.write_expr(&arg)?;
});
write!(self.file(), ")")?;
}
Ok(())
}
fn write_operation_name(&mut self, op: &Operation) -> Result<(), GeneratorError> {
match op {
Operation::FromPreviousVersion(prev_ver) => write!(self.file(), "fromV{}", prev_ver)?,
Operation::FinalTypeConverter => write!(self.file(), "converter")?,
Operation::TypeCodec => write!(self.file(), "codec")?,
Operation::FromInteger => write!(self.file(), "fromInteger")?,
Operation::FromString => write!(self.file(), "fromString")?,
Operation::FromSequence => write!(self.file(), "fromSequence")?,
Operation::FromRecord(_) => write!(self.file(), "fromRecord")?,
Operation::FromCase(name) => write!(self.file(), "fromCase{}", make_type_name(name))?,
}
Ok(())
}
fn write_expr(&mut self, expr: &LangExpr<'model>) -> Result<(), GeneratorError> {
match expr {
LangExpr::Identifier(name) => write!(self.file(), "{}", name)?,
LangExpr::IntegerLiteral(n) => {
if let Some(n) = n.to_i32() {
write!(self.file(), "{}", n)?;
}
else if let Some(n) = n.to_i64() {
write!(self.file(), "{}L", n)?;
}
else {
write!(self.file(), "scala.math.BigInt(\"{}\")", n)?;
}
},
LangExpr::StringLiteral(s) => {
write!(self.file(), "\"")?;
for codepoint in s.chars() {
match codepoint {
'"' => write!(self.file(), "\\\"")?,
'\\' => write!(self.file(), "\\\\")?,
'\n' => write!(self.file(), "\\n")?,
'\r' => write!(self.file(), "\\r")?,
_ => write!(self.file(), "{}", codepoint)?,
}
}
write!(self.file(), "\"")?;
},
LangExpr::InvokeConverter { converter, value } => {
self.write_expr(&*converter)?;
write!(self.file(), ".convert(")?;
self.write_expr(&*value)?;
write!(self.file(), ")")?;
},
LangExpr::IdentityConverter(t) => {
write!(self.file(), "{}.Converter.identity[", RUNTIME_PACKAGE)?;
self.write_type(t)?;
write!(self.file(), "]")?;
},
LangExpr::ReadDiscriminator => write!(self.file(), "{}.Nat.codec.read(reader)", RUNTIME_PACKAGE)?,
LangExpr::WriteDiscriminator(value) => write!(self.file(), "{}.Nat.codec.write(writer, {})", RUNTIME_PACKAGE, value)?,
LangExpr::CodecRead { codec } => {
self.write_expr(&*codec)?;
write!(self.file(), ".read(reader)")?;
},
LangExpr::CodecWrite { codec, value } => {
self.write_expr(&*codec)?;
write!(self.file(), ".write(writer, ")?;
self.write_expr(value)?;
write!(self.file(), ")")?;
},
LangExpr::InvokeOperation(op, target, type_args, args) => {
match target {
OperationTarget::VersionedType(name, version) => {
self.write_qual_name(name)?;
write!(self.file(), ".V{}.", version)?;
},
OperationTarget::ExternType(name) => {
self.write_qual_name(name)?;
write!(self.file(), ".")?;
},
}
self.write_operation_name(op)?;
self.write_type_args(type_args)?;
match op {
Operation::FromRecord(field_names) => {
write!(self.file(), "(")?;
for_sep!((field_name, arg), field_names.iter().zip(args.iter()), { write!(self.file(), ", ")?; }, {
write!(self.file(), "{} = ", make_field_name(field_name))?;
self.write_expr(arg)?;
});
write!(self.file(), ")")?;
},
_ => self.write_args(args)?,
}
},
LangExpr::InvokeUserConverter { name, prev_ver, version, type_args, args } => {
self.write_qual_name(name)?;
write!(self.file(), "_Conversions.v{}ToV{}", prev_ver, version)?;
self.write_type_args(type_args)?;
self.write_args(args)?;
},
LangExpr::ConstantValue(name, version) => {
self.write_qual_name(name)?;
write!(self.file(), ".{}", ScalaLanguage::constant_version_name(version))?;
},
LangExpr::CreateStruct(name, version, type_args, fields) => {
write!(self.file(), "new ")?;
self.write_qual_name(name)?;
write!(self.file(), ".V{}", version)?;
self.write_type_args(type_args)?;
write!(self.file(), "(")?;
for_sep!((_, value), fields, { write!(self.file(), ", ")?; }, {
self.write_expr(value)?;
});
write!(self.file(), ")")?;
},
LangExpr::CreateEnum(name, version, type_args, field_name, value) => {
write!(self.file(), "new ")?;
self.write_qual_name(name)?;
write!(self.file(), ".V{}.{}", version, make_type_name(field_name))?;
self.write_type_args(type_args)?;
write!(self.file(), "(")?;
self.write_expr(value)?;
write!(self.file(), ")")?;
},
LangExpr::StructField(_, _, field_name, value) => {
self.write_expr(value)?;
write!(self.file(), ".{}", make_field_name(field_name))?;
},
}
Ok(())
}
}
impl GeneratorNameMapping for ScalaLanguage {
fn convert_prev_type_param(param: &str) -> String {
format!("{}_1", param)
}
fn convert_current_type_param(param: &str) -> String {
format!("{}_2", param)
}
fn convert_conv_param_name(param: &str) -> String {
format!("{}_conv", param)
}
fn convert_prev_param_name() -> &'static str {
"prev"
}
fn codec_write_value_name() -> &'static str {
"value"
}
fn codec_codec_param_name(param: &str) -> String {
format!("{}_codec", param)
}
fn constant_version_name(version: &BigUint) -> String {
format!("v{}", version)
}
}
struct ScalaConstGenerator<'model, 'opt, 'output, Output: OutputHandler<'output>> {
file: Output::FileHandle,
model: &'model model::Verilization,
options: &'opt ScalaOptions,
constant: Named<'model, model::Constant>,
scope: model::Scope<'model>,
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> Generator<'model> for ScalaConstGenerator<'model, 'opt, 'output, Output> {
type Lang = ScalaLanguage;
fn model(&self) -> &'model model::Verilization {
self.model
}
fn scope(&self) -> &model::Scope<'model> {
&self.scope
}
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> GeneratorWithFile for ScalaConstGenerator<'model, 'opt, 'output, Output> {
type GeneratorFile = Output::FileHandle;
fn file(&mut self) -> &mut Self::GeneratorFile {
&mut self.file
}
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ScalaGenerator<'model, 'opt> for ScalaConstGenerator<'model, 'opt, 'output, Output> {
fn options(&self) -> &'opt ScalaOptions {
self.options
}
fn referenced_types(&self) -> model::ReferencedTypeIterator<'model> {
self.constant.referenced_types()
}
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ConstGenerator<'model> for ScalaConstGenerator<'model, 'opt, 'output, Output> {
fn constant(&self) -> Named<'model, model::Constant> {
self.constant
}
fn write_header(&mut self) -> Result<(), GeneratorError> {
self.write_package(&self.constant.name().package)?;
writeln!(self.file, "object {} {{", make_type_name(&self.constant.name().name))?;
Ok(())
}
fn write_constant(&mut self, version_name: String, t: LangType<'model>, value: LangExpr<'model>) -> Result<(), GeneratorError> {
write!(self.file, "\tval {}: ", version_name)?;
self.write_type(&t)?;
write!(self.file, " = ")?;
self.write_expr(&value)?;
writeln!(self.file)?;
Ok(())
}
fn write_footer(&mut self) -> Result<(), GeneratorError> {
writeln!(self.file, "}}")?;
Ok(())
}
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ScalaConstGenerator<'model, 'opt, 'output, Output> {
fn open(model: &'model model::Verilization, options: &'opt ScalaOptions, output: &'output mut Output, constant: Named<'model, model::Constant>) -> Result<Self, GeneratorError> {
let file = open_scala_file(options, output, constant.name())?;
Ok(ScalaConstGenerator {
file: file,
model: model,
options: options,
constant: constant,
scope: constant.scope(),
})
}
}
struct ScalaTypeGenerator<'model, 'opt, 'output, Output: OutputHandler<'output>> {
options: &'opt ScalaOptions,
model: &'model model::Verilization,
file: Output::FileHandle,
type_def: Named<'model, model::VersionedTypeDefinitionData>,
scope: model::Scope<'model>,
indentation_level: u32,
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> Generator<'model> for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
type Lang = ScalaLanguage;
fn model(&self) -> &'model model::Verilization {
self.model
}
fn scope(&self) -> &model::Scope<'model> {
&self.scope
}
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> GeneratorWithFile for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
type GeneratorFile = Output::FileHandle;
fn file(&mut self) -> &mut Self::GeneratorFile {
&mut self.file
}
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> Indentation for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
fn indentation_size(&mut self) -> &mut u32 {
&mut self.indentation_level
}
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ScalaGenerator<'model, 'opt> for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
fn options(&self) -> &'opt ScalaOptions {
self.options
}
fn referenced_types(&self) -> model::ReferencedTypeIterator<'model> {
self.type_def.referenced_types()
}
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> VersionedTypeGenerator<'model> for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
fn type_def(&self) -> Named<'model, model::VersionedTypeDefinitionData> {
self.type_def
}
fn write_header(&mut self) -> Result<(), GeneratorError> {
self.write_package(&self.type_def.name().package)?;
writeln!(self.file, "sealed abstract class {}", make_type_name(&self.type_def.name().name))?;
writeln!(self.file, "object {} {{", make_type_name(&self.type_def.name().name))?;
self.indent_increase();
Ok(())
}
fn write_version_header(&mut self, t: LangType<'model>) -> Result<(), GeneratorError> {
match t {
LangType::Versioned(VersionedTypeKind::Struct, _, version, _, fields) => {
self.write_indent()?;
write!(self.file, "final case class V{}", version)?;
self.write_type_params(&self.type_def().type_params())?;
writeln!(self.file, "(")?;
self.indent_increase();
for field in fields.build()? {
self.write_indent()?;
write!(self.file, "{}: ", make_field_name(field.name))?;
self.write_type(&field.field_type)?;
writeln!(self.file, ",")?;
}
self.indent_decrease();
self.write_indent()?;
writeln!(self.file, ") extends {}", self.type_def.name().name)?;
self.write_indent()?;
writeln!(self.file, "object V{} {{", version)?;
self.indent_increase();
},
LangType::Versioned(VersionedTypeKind::Enum, _, version, _, fields) => {
self.write_indent()?;
write!(self.file, "sealed abstract class V{}", version)?;
self.write_type_params(&self.type_def().type_params())?;
writeln!(self.file, " extends {}", self.type_def.name().name)?;
self.write_indent()?;
writeln!(self.file, "object V{} {{", version)?;
self.indent_increase();
for field in fields.build()? {
self.write_indent()?;
write!(self.file, "final case class {}", make_type_name(field.name))?;
self.write_type_params(&self.type_def().type_params())?;
write!(self.file, "({}: ", make_field_name(field.name))?;
self.write_type(&field.field_type)?;
write!(self.file, ") extends V{}", version)?;
self.write_type_params(&self.type_def().type_params())?;
writeln!(self.file)?;
}
},
_ => return Err(GeneratorError::CouldNotGenerateType)
}
Ok(())
}
fn write_operation(&mut self, operation: OperationInfo<'model>) -> Result<(), GeneratorError> {
let is_func = !operation.type_params.is_empty() || !operation.params.is_empty();
self.write_indent()?;
if is_func {
write!(self.file, "def ")?;
}
else {
write!(self.file, "val ")?;
}
self.write_operation_name(&operation.operation)?;
self.write_type_params(&operation.type_params)?;
if is_func {
write!(self.file, "(")?;
for_sep!((param_name, param), operation.params, { write!(self.file, ", ")?; }, {
write!(self.file, "{}: ", param_name)?;
self.write_type(¶m)?;
});
write!(self.file, ")")?;
}
write!(self.file, ": ")?;
self.write_type(&operation.result)?;
write!(self.file, " = ")?;
self.write_expr_statement(operation.implementation)?;
Ok(())
}
fn write_version_footer(&mut self) -> Result<(), GeneratorError> {
self.indent_decrease();
writeln!(self.file, "}}")?;
Ok(())
}
fn write_footer(&mut self) -> Result<(), GeneratorError> {
self.indent_decrease();
writeln!(self.file, "}}")?;
Ok(())
}
}
impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ScalaTypeGenerator<'model, 'opt, 'output, Output> {
fn open(model: &'model model::Verilization, options: &'opt ScalaOptions, output: &'output mut Output, type_def: Named<'model, model::VersionedTypeDefinitionData>) -> Result<Self, GeneratorError> {
let file = open_scala_file(options, output, type_def.name())?;
Ok(ScalaTypeGenerator {
file: file,
model: model,
options: options,
type_def: type_def,
scope: type_def.scope(),
indentation_level: 0,
})
}
fn write_expr_statement(&mut self, stmt: LangExprStmt<'model>) -> Result<(), GeneratorError> {
match stmt {
LangExprStmt::Expr(expr) => {
self.write_expr(&expr)?;
writeln!(self.file, ";")?;
},
LangExprStmt::CreateCodec { t, read, write } => {
write!(self.file, "new {}.Codec[", RUNTIME_PACKAGE)?;
self.write_type(&t)?;
writeln!(self.file, "] {{")?;
self.indent_increase();
self.write_indent()?;
write!(self.file, "override def read[R, E](reader: {}.FormatReader[R, E]): zio.ZIO[R, E, ", RUNTIME_PACKAGE)?;
self.write_type(&t)?;
write!(self.file, "] = ")?;
self.write_statement(*read, true)?;
self.write_indent()?;
write!(self.file, "override def write[R, E](writer: {}.FormatWriter[R, E], value: ", RUNTIME_PACKAGE)?;
self.write_type(&t)?;
write!(self.file, "): zio.ZIO[R, E, Unit] = ")?;
self.write_statement(*write, true)?;
self.indent_decrease();
self.write_indent()?;
writeln!(self.file, "}}")?;
},
LangExprStmt::CreateConverter { from_type, to_type, body } => {
write!(self.file, "new {}.Converter[", RUNTIME_PACKAGE)?;
self.write_type(&from_type)?;
write!(self.file, ", ")?;
self.write_type(&to_type)?;
writeln!(self.file, "] {{")?;
self.indent_increase();
self.write_indent()?;
write!(self.file, "override def convert({}: ", ScalaLanguage::convert_prev_param_name())?;
self.write_type(&from_type)?;
write!(self.file, "): ")?;
self.write_type(&to_type)?;
write!(self.file, " = ")?;
self.write_statement(*body, false)?;
self.indent_decrease();
self.write_indent()?;
writeln!(self.file, "}};")?;
},
}
Ok(())
}
fn record_io_expr(&self, expr: &mut LangExpr<'model>, ops: &mut Vec<(String, LangExpr<'model>)>) {
let name = format!("value{}", ops.len());
let old_expr = std::mem::replace(expr, LangExpr::Identifier(name.clone()));
ops.push((name, old_expr));
}
fn gather_io_exprs(&self, expr: &mut LangExpr<'model>, ops: &mut Vec<(String, LangExpr<'model>)>) {
match expr {
LangExpr::ReadDiscriminator | LangExpr::WriteDiscriminator(_) => self.record_io_expr(expr, ops),
LangExpr::CodecRead { codec } => {
self.gather_io_exprs(codec, ops);
self.record_io_expr(expr, ops);
},
LangExpr::CodecWrite { codec, value } => {
self.gather_io_exprs(codec, ops);
self.gather_io_exprs(value, ops);
self.record_io_expr(expr, ops);
},
LangExpr::InvokeConverter { converter, value } => {
self.gather_io_exprs(converter, ops);
self.gather_io_exprs(value, ops);
},
LangExpr::InvokeOperation(_, _, _, args) => {
for arg in args {
self.gather_io_exprs(arg, ops);
}
},
LangExpr::InvokeUserConverter { args, .. } => {
for arg in args {
self.gather_io_exprs(arg, ops);
}
},
LangExpr::CreateStruct(_, _, _, fields) => {
for (_, value) in fields {
self.gather_io_exprs(value, ops);
}
},
LangExpr::CreateEnum(_, _, _, _, value) => {
self.gather_io_exprs(value, ops);
},
LangExpr::StructField(_, _, _, value) => {
self.gather_io_exprs(value, ops);
},
_ => (),
}
}
fn write_statement(&mut self, stmt: LangStmt<'model>, use_io: bool) -> Result<(), GeneratorError> {
match stmt {
LangStmt::Expr(mut exprs, mut result_expr) if use_io => {
let mut io_ops = Vec::new();
let mut ignored_values = HashSet::new();
for mut expr in &mut exprs {
self.gather_io_exprs(&mut expr, &mut io_ops);
match expr {
LangExpr::Identifier(name) => { ignored_values.insert(name.clone()); },
_ => (),
};
}
if let Some(result_expr) = &mut result_expr {
self.gather_io_exprs(result_expr, &mut io_ops);
}
if io_ops.is_empty() {
write!(self.file, "zio.IO.succeed(")?;
if let Some(result_expr) = result_expr {
self.write_expr(&result_expr)?;
}
else {
write!(self.file, "()")?;
}
writeln!(self.file, ")")?;
}
else {
writeln!(self.file, "for {{")?;
self.indent_increase();
for (name, expr) in io_ops {
self.write_indent()?;
if ignored_values.contains(&name) {
write!(self.file, "_")?;
}
else {
write!(self.file, "{}", name)?;
}
write!(self.file, " <- ")?;
self.write_expr(&expr)?;
writeln!(self.file)?;
}
self.indent_decrease();
self.write_indent()?;
write!(self.file, "}} yield ")?;
if let Some(result_expr) = result_expr {
self.write_expr(&result_expr)?;
}
else {
write!(self.file, "()")?;
}
writeln!(self.file)?;
}
},
LangStmt::Expr(exprs, result_expr) => {
writeln!(self.file, "{{")?;
self.indent_increase();
for expr in exprs {
self.write_indent()?;
self.write_expr(&expr)?;
writeln!(self.file)?;
}
if let Some(result_expr) = result_expr {
self.write_indent()?;
self.write_expr(&result_expr)?;
writeln!(self.file)?;
}
self.indent_decrease();
self.write_indent()?;
writeln!(self.file, "}}")?;
},
LangStmt::MatchEnum { mut value, value_type, cases } => {
if cases.is_empty() {
self.write_expr(&value)?;
writeln!(self.file)?;
return Ok(())
}
let mut io_ops = Vec::new();
if use_io {
self.gather_io_exprs(&mut value, &mut io_ops);
}
for (name, op) in &io_ops {
self.write_expr(op)?;
writeln!(self.file, ".flatMap {{ {} =>", name)?;
self.indent_increase();
self.write_indent()?;
}
self.write_expr(&value)?;
writeln!(self.file, " match {{")?;
self.indent_increase();
for MatchCase { binding_name, case_name, body } in cases {
self.write_indent()?;
write!(self.file, "case ")?;
match &value_type {
LangType::Versioned(_, name, version, _, _) => {
self.write_qual_name(name)?;
write!(self.file, ".V{}.{}({})", version, make_type_name(&case_name), binding_name)?;
},
_ => panic!("Invalid enum type."),
}
write!(self.file, " => ")?;
self.write_statement(body, use_io)?;
}
self.indent_decrease();
self.write_indent()?;
writeln!(self.file, "}}")?;
for _ in &io_ops {
self.indent_decrease();
self.write_indent()?;
writeln!(self.file, "}}")?;
}
},
LangStmt::MatchDiscriminator { mut value, cases } => {
let mut io_ops = Vec::new();
if use_io {
self.gather_io_exprs(&mut value, &mut io_ops);
}
for (name, op) in &io_ops {
self.write_expr(op)?;
writeln!(self.file, ".flatMap {{ {} =>", name)?;
self.indent_increase();
self.write_indent()?;
}
self.write_expr(&value)?;
writeln!(self.file, " match {{")?;
self.indent_increase();
for (n, body) in cases {
self.write_indent()?;
write!(self.file, "case {}.Util.BigIntValue({}) => ", RUNTIME_PACKAGE, n)?;
self.write_statement(body, use_io)?;
}
self.indent_decrease();
self.write_indent()?;
writeln!(self.file, "}}")?;
for _ in &io_ops {
self.indent_decrease();
self.write_indent()?;
writeln!(self.file, "}}")?;
}
},
}
Ok(())
}
fn write_type_params(&mut self, params: &Vec<String>) -> Result<(), GeneratorError> {
if !params.is_empty() {
write!(self.file, "[")?;
for_sep!(param, params, { write!(self.file, ", ")?; }, {
write!(self.file, "{}", param)?;
});
write!(self.file, "]")?;
}
Ok(())
}
}
pub struct ScalaLanguage {}
impl Language for ScalaLanguage {
type OptionsBuilder = ScalaOptionsBuilder;
type Options = ScalaOptions;
fn name() -> &'static str {
"scala"
}
fn empty_options() -> ScalaOptionsBuilder {
ScalaOptionsBuilder {
output_dir: None,
package_mapping: HashMap::new(),
library_mapping: HashMap::new(),
}
}
fn add_option(builder: &mut ScalaOptionsBuilder, name: &str, value: OsString) -> Result<(), GeneratorError> {
if name == "out_dir" {
if builder.output_dir.is_some() {
return Err(GeneratorError::InvalidOptions(String::from("Output directory already specified")))
}
builder.output_dir = Some(value);
Ok(())
}
else if let Some(pkg) = name.strip_prefix("pkg:") {
let package = model::PackageName::from_str(pkg);
let scala_package = model::PackageName::from_str(value.to_str().unwrap());
if builder.library_mapping.contains_key(&package) || builder.package_mapping.insert(package, scala_package).is_some() {
return Err(GeneratorError::InvalidOptions(format!("Package already mapped: {}", pkg)))
}
Ok(())
}
else if let Some(pkg) = name.strip_prefix("lib:") {
let package = model::PackageName::from_str(pkg);
let scala_package = model::PackageName::from_str(value.to_str().unwrap());
if builder.package_mapping.contains_key(&package) || builder.library_mapping.insert(package, scala_package).is_some() {
return Err(GeneratorError::InvalidOptions(format!("Package already mapped: {}", pkg)))
}
Ok(())
}
else {
Err(GeneratorError::InvalidOptions(format!("Unknown option: {}", name)))
}
}
fn finalize_options(builder: Self::OptionsBuilder) -> Result<Self::Options, GeneratorError> {
Ok(ScalaOptions {
output_dir: builder.output_dir.ok_or_else(|| GeneratorError::InvalidOptions(String::from("Output directory not specified")))?,
package_mapping: builder.package_mapping,
library_mapping: builder.library_mapping,
})
}
fn generate<Output: for<'output> OutputHandler<'output>>(model: &model::Verilization, options: Self::Options, output: &mut Output) -> Result<(), GeneratorError> {
for constant in model.constants() {
let mut const_gen = ScalaConstGenerator::open(model, &options, output, constant)?;
const_gen.generate()?;
}
for t in model.types() {
match t {
model::NamedTypeDefinition::StructType(t) | model::NamedTypeDefinition::EnumType(t) => {
let mut type_gen = ScalaTypeGenerator::open(model, &options, output, t)?;
type_gen.generate()?;
},
model::NamedTypeDefinition::ExternType(_) => (),
}
}
Ok(())
}
}