1use verilization_compiler::{lang, model, util, for_sep};
2
3use model::Named;
4use lang::{GeneratorError, Language, OutputHandler};
5use std::ffi::OsString;
6use std::collections::{HashMap, HashSet};
7use std::io::Write;
8use std::path::PathBuf;
9use num_bigint::BigUint;
10use lang::generator::*;
11use util::{capitalize_identifier, uncapitalize_identifier};
12use num_traits::ToPrimitive;
13
14type PackageMap = HashMap<model::PackageName, model::PackageName>;
15const RUNTIME_PACKAGE: &str = "dev.argon.verilization.scala_runtime";
16
17
18pub struct ScalaOptionsBuilder {
19 output_dir: Option<OsString>,
20 package_mapping: PackageMap,
21 library_mapping: PackageMap,
22}
23
24pub struct ScalaOptions {
25 pub output_dir: OsString,
26 pub package_mapping: PackageMap,
27 pub library_mapping: PackageMap,
28}
29
30fn make_type_name(name: &str) -> String {
31 let mut name = String::from(name);
32 capitalize_identifier(&mut name);
33 name
34}
35
36fn make_field_name(field_name: &str) -> String {
37 let mut name = String::from(field_name);
38 uncapitalize_identifier(&mut name);
39 name
40}
41
42
43fn scala_package_impl<'a>(options: &'a ScalaOptions, package: &model::PackageName) -> Result<&'a model::PackageName, GeneratorError> {
44 options.package_mapping.get(&package)
45 .or_else(|| options.library_mapping.get(&package))
46 .ok_or_else(|| GeneratorError::UnmappedPackage(package.clone()))
47}
48
49
50
51
52
53fn open_scala_file<'output, Output: OutputHandler<'output>>(options: &ScalaOptions, output: &'output mut Output, name: &model::QualifiedName) -> Result<Output::FileHandle, GeneratorError> {
54 let java_pkg = scala_package_impl(options, &name.package)?;
55 let mut path = PathBuf::from(&options.output_dir);
56 for part in &java_pkg.package {
57 path.push(part);
58 }
59
60 path.push(name.name.clone() + ".scala");
61 Ok(output.create_file(path)?)
62}
63
64
65pub trait ScalaGenerator<'model, 'opt> : Generator<'model> + GeneratorWithFile {
66 fn options(&self) -> &'opt ScalaOptions;
67 fn referenced_types(&self) -> model::ReferencedTypeIterator<'model>;
68
69
70 fn scala_package(&self, package: &model::PackageName) -> Result<&'opt model::PackageName, GeneratorError> {
71 scala_package_impl(&self.options(), package)
72 }
73
74 fn write_package(&mut self, package: &model::PackageName) -> Result<(), GeneratorError> {
75
76 let pkg = self.scala_package(package)?;
77
78 let mut pkg_iter = pkg.package.iter();
79
80 if let Some(part) = pkg_iter.next() {
81 write!(self.file(), "package {}", part)?;
82 while let Some(part) = pkg_iter.next() {
83 write!(self.file(), ".{}", part)?;
84 }
85 writeln!(self.file())?;
86 }
87
88 Ok(())
89 }
90
91 fn write_qual_name(&mut self, name: &model::QualifiedName) -> Result<(), GeneratorError> {
92 let pkg = self.scala_package(&name.package)?;
93 for part in &pkg.package {
94 write!(self.file(), "{}.", part)?;
95 }
96
97 write!(self.file(), "{}", make_type_name(&name.name))?;
98
99 Ok(())
100 }
101
102 fn write_type_args(&mut self, args: &Vec<LangType<'model>>) -> Result<(), GeneratorError> {
103 if !args.is_empty() {
104 write!(self.file(), "[")?;
105 for_sep!(arg, args, { write!(self.file(), ", ")?; }, {
106 self.write_type(arg)?;
107 });
108 write!(self.file(), "]")?;
109 }
110
111 Ok(())
112 }
113
114
115 fn write_type(&mut self, t: &LangType<'model>) -> Result<(), GeneratorError> {
116 Ok(match t {
117 LangType::Versioned(_, name, version, args, _) => {
118 self.write_qual_name(&name)?;
119 write!(self.file(), ".V{}", version)?;
120 self.write_type_args(args)?;
121 },
122
123 LangType::Extern(name, args, _) => {
124 self.write_qual_name(name)?;
125 self.write_type_args(args)?;
126 },
127
128 LangType::TypeParameter(name) => {
129 write!(self.file(), "{}", name)?;
130 },
131
132 LangType::Converter(from, to) => {
133 write!(self.file(), "{}.Converter[", RUNTIME_PACKAGE)?;
134 self.write_type(&*from)?;
135 write!(self.file(), ", ")?;
136 self.write_type(&*to)?;
137 write!(self.file(), "]")?;
138 },
139
140 LangType::Codec(t) => {
141 write!(self.file(), "{}.Codec[", RUNTIME_PACKAGE)?;
142 self.write_type(&*t)?;
143 write!(self.file(), "]")?;
144 },
145 })
146 }
147
148 fn write_args(&mut self, args: &Vec<LangExpr<'model>>) -> Result<(), GeneratorError> {
149 if !args.is_empty() {
150 write!(self.file(), "(")?;
151 for_sep!(arg, args, { write!(self.file(), ", ")?; }, {
152 self.write_expr(&arg)?;
153 });
154 write!(self.file(), ")")?;
155 }
156
157 Ok(())
158 }
159
160 fn write_operation_name(&mut self, op: &Operation) -> Result<(), GeneratorError> {
161 match op {
162 Operation::FromPreviousVersion(prev_ver) => write!(self.file(), "fromV{}", prev_ver)?,
163 Operation::FinalTypeConverter => write!(self.file(), "converter")?,
164 Operation::TypeCodec => write!(self.file(), "codec")?,
165 Operation::FromInteger => write!(self.file(), "fromInteger")?,
166 Operation::FromString => write!(self.file(), "fromString")?,
167 Operation::FromSequence => write!(self.file(), "fromSequence")?,
168 Operation::FromRecord(_) => write!(self.file(), "fromRecord")?,
169 Operation::FromCase(name) => write!(self.file(), "fromCase{}", make_type_name(name))?,
170 }
171
172 Ok(())
173 }
174
175 fn write_expr(&mut self, expr: &LangExpr<'model>) -> Result<(), GeneratorError> {
176 match expr {
177 LangExpr::Identifier(name) => write!(self.file(), "{}", name)?,
178 LangExpr::IntegerLiteral(n) => {
179 if let Some(n) = n.to_i32() {
180 write!(self.file(), "{}", n)?;
181 }
182 else if let Some(n) = n.to_i64() {
183 write!(self.file(), "{}L", n)?;
184 }
185 else {
186 write!(self.file(), "scala.math.BigInt(\"{}\")", n)?;
187 }
188 },
189 LangExpr::StringLiteral(s) => {
190 write!(self.file(), "\"")?;
191 for codepoint in s.chars() {
192 match codepoint {
193 '"' => write!(self.file(), "\\\"")?,
194 '\\' => write!(self.file(), "\\\\")?,
195 '\n' => write!(self.file(), "\\n")?,
196 '\r' => write!(self.file(), "\\r")?,
197 _ => write!(self.file(), "{}", codepoint)?,
198 }
199 }
200 write!(self.file(), "\"")?;
201 },
202 LangExpr::InvokeConverter { converter, value } => {
203 self.write_expr(&*converter)?;
204 write!(self.file(), ".convert(")?;
205 self.write_expr(&*value)?;
206 write!(self.file(), ")")?;
207 },
208 LangExpr::IdentityConverter(t) => {
209 write!(self.file(), "{}.Converter.identity[", RUNTIME_PACKAGE)?;
210 self.write_type(t)?;
211 write!(self.file(), "]")?;
212 },
213 LangExpr::ReadDiscriminator => write!(self.file(), "{}.Nat.codec.read(reader)", RUNTIME_PACKAGE)?,
214 LangExpr::WriteDiscriminator(value) => write!(self.file(), "{}.Nat.codec.write(writer, {})", RUNTIME_PACKAGE, value)?,
215 LangExpr::CodecRead { codec } => {
216 self.write_expr(&*codec)?;
217 write!(self.file(), ".read(reader)")?;
218 },
219 LangExpr::CodecWrite { codec, value } => {
220 self.write_expr(&*codec)?;
221 write!(self.file(), ".write(writer, ")?;
222 self.write_expr(value)?;
223 write!(self.file(), ")")?;
224 },
225 LangExpr::InvokeOperation(op, target, type_args, args) => {
226 match target {
227 OperationTarget::VersionedType(name, version) => {
228 self.write_qual_name(name)?;
229 write!(self.file(), ".V{}.", version)?;
230 },
231 OperationTarget::ExternType(name) => {
232 self.write_qual_name(name)?;
233 write!(self.file(), ".")?;
234 },
235 }
236 self.write_operation_name(op)?;
237 self.write_type_args(type_args)?;
238 match op {
239 Operation::FromRecord(field_names) => {
240 write!(self.file(), "(")?;
241 for_sep!((field_name, arg), field_names.iter().zip(args.iter()), { write!(self.file(), ", ")?; }, {
242 write!(self.file(), "{} = ", make_field_name(field_name))?;
243 self.write_expr(arg)?;
244 });
245 write!(self.file(), ")")?;
246 },
247 _ => self.write_args(args)?,
248 }
249 },
250 LangExpr::InvokeUserConverter { name, prev_ver, version, type_args, args } => {
251 self.write_qual_name(name)?;
252 write!(self.file(), "_Conversions.v{}ToV{}", prev_ver, version)?;
253 self.write_type_args(type_args)?;
254 self.write_args(args)?;
255 },
256 LangExpr::ConstantValue(name, version) => {
257 self.write_qual_name(name)?;
258 write!(self.file(), ".{}", ScalaLanguage::constant_version_name(version))?;
259 },
260 LangExpr::CreateStruct(name, version, type_args, fields) => {
261 write!(self.file(), "new ")?;
262 self.write_qual_name(name)?;
263 write!(self.file(), ".V{}", version)?;
264 self.write_type_args(type_args)?;
265 write!(self.file(), "(")?;
266 for_sep!((_, value), fields, { write!(self.file(), ", ")?; }, {
267 self.write_expr(value)?;
268 });
269 write!(self.file(), ")")?;
270 },
271 LangExpr::CreateEnum(name, version, type_args, field_name, value) => {
272 write!(self.file(), "new ")?;
273 self.write_qual_name(name)?;
274 write!(self.file(), ".V{}.{}", version, make_type_name(field_name))?;
275 self.write_type_args(type_args)?;
276 write!(self.file(), "(")?;
277 self.write_expr(value)?;
278 write!(self.file(), ")")?;
279 },
280 LangExpr::StructField(_, _, field_name, value) => {
281 self.write_expr(value)?;
282 write!(self.file(), ".{}", make_field_name(field_name))?;
283 },
284 }
285
286 Ok(())
287 }
288
289}
290
291impl GeneratorNameMapping for ScalaLanguage {
292 fn convert_prev_type_param(param: &str) -> String {
293 format!("{}_1", param)
294 }
295
296 fn convert_current_type_param(param: &str) -> String {
297 format!("{}_2", param)
298 }
299
300 fn convert_conv_param_name(param: &str) -> String {
301 format!("{}_conv", param)
302 }
303
304 fn convert_prev_param_name() -> &'static str {
305 "prev"
306 }
307
308 fn codec_write_value_name() -> &'static str {
309 "value"
310 }
311
312 fn codec_codec_param_name(param: &str) -> String {
313 format!("{}_codec", param)
314 }
315
316 fn constant_version_name(version: &BigUint) -> String {
317 format!("v{}", version)
318 }
319}
320
321struct ScalaConstGenerator<'model, 'opt, 'output, Output: OutputHandler<'output>> {
322 file: Output::FileHandle,
323 model: &'model model::Verilization,
324 options: &'opt ScalaOptions,
325 constant: Named<'model, model::Constant>,
326 scope: model::Scope<'model>,
327}
328
329impl <'model, 'opt, 'output, Output: OutputHandler<'output>> Generator<'model> for ScalaConstGenerator<'model, 'opt, 'output, Output> {
330 type Lang = ScalaLanguage;
331
332 fn model(&self) -> &'model model::Verilization {
333 self.model
334 }
335
336 fn scope(&self) -> &model::Scope<'model> {
337 &self.scope
338 }
339}
340
341impl <'model, 'opt, 'output, Output: OutputHandler<'output>> GeneratorWithFile for ScalaConstGenerator<'model, 'opt, 'output, Output> {
342 type GeneratorFile = Output::FileHandle;
343 fn file(&mut self) -> &mut Self::GeneratorFile {
344 &mut self.file
345 }
346}
347
348impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ScalaGenerator<'model, 'opt> for ScalaConstGenerator<'model, 'opt, 'output, Output> {
349 fn options(&self) -> &'opt ScalaOptions {
350 self.options
351 }
352
353 fn referenced_types(&self) -> model::ReferencedTypeIterator<'model> {
354 self.constant.referenced_types()
355 }
356}
357
358impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ConstGenerator<'model> for ScalaConstGenerator<'model, 'opt, 'output, Output> {
359 fn constant(&self) -> Named<'model, model::Constant> {
360 self.constant
361 }
362
363 fn write_header(&mut self) -> Result<(), GeneratorError> {
364 self.write_package(&self.constant.name().package)?;
365
366 writeln!(self.file, "object {} {{", make_type_name(&self.constant.name().name))?;
367
368 Ok(())
369 }
370
371 fn write_constant(&mut self, version_name: String, t: LangType<'model>, value: LangExpr<'model>) -> Result<(), GeneratorError> {
372 write!(self.file, "\tval {}: ", version_name)?;
373 self.write_type(&t)?;
374 write!(self.file, " = ")?;
375 self.write_expr(&value)?;
376 writeln!(self.file)?;
377
378 Ok(())
379 }
380
381 fn write_footer(&mut self) -> Result<(), GeneratorError> {
382 writeln!(self.file, "}}")?;
383 Ok(())
384 }
385}
386
387
388impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ScalaConstGenerator<'model, 'opt, 'output, Output> {
389
390 fn open(model: &'model model::Verilization, options: &'opt ScalaOptions, output: &'output mut Output, constant: Named<'model, model::Constant>) -> Result<Self, GeneratorError> {
391 let file = open_scala_file(options, output, constant.name())?;
392 Ok(ScalaConstGenerator {
393 file: file,
394 model: model,
395 options: options,
396 constant: constant,
397 scope: constant.scope(),
398 })
399 }
400
401}
402
403struct ScalaTypeGenerator<'model, 'opt, 'output, Output: OutputHandler<'output>> {
404 options: &'opt ScalaOptions,
405 model: &'model model::Verilization,
406 file: Output::FileHandle,
407 type_def: Named<'model, model::VersionedTypeDefinitionData>,
408 scope: model::Scope<'model>,
409 indentation_level: u32,
410}
411
412impl <'model, 'opt, 'output, Output: OutputHandler<'output>> Generator<'model> for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
413 type Lang = ScalaLanguage;
414
415 fn model(&self) -> &'model model::Verilization {
416 self.model
417 }
418
419 fn scope(&self) -> &model::Scope<'model> {
420 &self.scope
421 }
422}
423
424impl <'model, 'opt, 'output, Output: OutputHandler<'output>> GeneratorWithFile for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
425 type GeneratorFile = Output::FileHandle;
426 fn file(&mut self) -> &mut Self::GeneratorFile {
427 &mut self.file
428 }
429}
430
431impl <'model, 'opt, 'output, Output: OutputHandler<'output>> Indentation for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
432 fn indentation_size(&mut self) -> &mut u32 {
433 &mut self.indentation_level
434 }
435}
436
437impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ScalaGenerator<'model, 'opt> for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
438 fn options(&self) -> &'opt ScalaOptions {
439 self.options
440 }
441
442 fn referenced_types(&self) -> model::ReferencedTypeIterator<'model> {
443 self.type_def.referenced_types()
444 }
445}
446
447impl <'model, 'opt, 'output, Output: OutputHandler<'output>> VersionedTypeGenerator<'model> for ScalaTypeGenerator<'model, 'opt, 'output, Output> {
448 fn type_def(&self) -> Named<'model, model::VersionedTypeDefinitionData> {
449 self.type_def
450 }
451
452 fn write_header(&mut self) -> Result<(), GeneratorError> {
453 self.write_package(&self.type_def.name().package)?;
454 writeln!(self.file, "sealed abstract class {}", make_type_name(&self.type_def.name().name))?;
455 writeln!(self.file, "object {} {{", make_type_name(&self.type_def.name().name))?;
456 self.indent_increase();
457
458 Ok(())
459 }
460
461 fn write_version_header(&mut self, t: LangType<'model>) -> Result<(), GeneratorError> {
462 match t {
463 LangType::Versioned(VersionedTypeKind::Struct, _, version, _, fields) => {
464 self.write_indent()?;
465 write!(self.file, "final case class V{}", version)?;
466 self.write_type_params(&self.type_def().type_params())?;
467 writeln!(self.file, "(")?;
468 self.indent_increase();
469
470 for field in fields.build()? {
471 self.write_indent()?;
472 write!(self.file, "{}: ", make_field_name(field.name))?;
473 self.write_type(&field.field_type)?;
474 writeln!(self.file, ",")?;
475 }
476
477 self.indent_decrease();
478 self.write_indent()?;
479 writeln!(self.file, ") extends {}", self.type_def.name().name)?;
480
481 self.write_indent()?;
482 writeln!(self.file, "object V{} {{", version)?;
483 self.indent_increase();
484 },
485 LangType::Versioned(VersionedTypeKind::Enum, _, version, _, fields) => {
486 self.write_indent()?;
487 write!(self.file, "sealed abstract class V{}", version)?;
488 self.write_type_params(&self.type_def().type_params())?;
489 writeln!(self.file, " extends {}", self.type_def.name().name)?;
490
491 self.write_indent()?;
492 writeln!(self.file, "object V{} {{", version)?;
493 self.indent_increase();
494
495 for field in fields.build()? {
496 self.write_indent()?;
497 write!(self.file, "final case class {}", make_type_name(field.name))?;
498 self.write_type_params(&self.type_def().type_params())?;
499 write!(self.file, "({}: ", make_field_name(field.name))?;
500 self.write_type(&field.field_type)?;
501 write!(self.file, ") extends V{}", version)?;
502 self.write_type_params(&self.type_def().type_params())?;
503 writeln!(self.file)?;
504 }
505 },
506 _ => return Err(GeneratorError::CouldNotGenerateType)
507 }
508
509 Ok(())
510 }
511
512 fn write_operation(&mut self, operation: OperationInfo<'model>) -> Result<(), GeneratorError> {
513 let is_func = !operation.type_params.is_empty() || !operation.params.is_empty();
514
515 self.write_indent()?;
516 if is_func {
517 write!(self.file, "def ")?;
518 }
519 else {
520 write!(self.file, "val ")?;
521 }
522
523 self.write_operation_name(&operation.operation)?;
524 self.write_type_params(&operation.type_params)?;
525 if is_func {
526 write!(self.file, "(")?;
527 for_sep!((param_name, param), operation.params, { write!(self.file, ", ")?; }, {
528 write!(self.file, "{}: ", param_name)?;
529 self.write_type(¶m)?;
530 });
531 write!(self.file, ")")?;
532 }
533 write!(self.file, ": ")?;
534 self.write_type(&operation.result)?;
535 write!(self.file, " = ")?;
536
537 self.write_expr_statement(operation.implementation)?;
538
539
540 Ok(())
541 }
542
543 fn write_version_footer(&mut self) -> Result<(), GeneratorError> {
544 self.indent_decrease();
545 writeln!(self.file, "}}")?;
546
547 Ok(())
548 }
549
550 fn write_footer(&mut self) -> Result<(), GeneratorError> {
551 self.indent_decrease();
552 writeln!(self.file, "}}")?;
553
554 Ok(())
555 }
556
557}
558
559impl <'model, 'opt, 'output, Output: OutputHandler<'output>> ScalaTypeGenerator<'model, 'opt, 'output, Output> {
560
561
562 fn open(model: &'model model::Verilization, options: &'opt ScalaOptions, output: &'output mut Output, type_def: Named<'model, model::VersionedTypeDefinitionData>) -> Result<Self, GeneratorError> {
563 let file = open_scala_file(options, output, type_def.name())?;
564 Ok(ScalaTypeGenerator {
565 file: file,
566 model: model,
567 options: options,
568 type_def: type_def,
569 scope: type_def.scope(),
570 indentation_level: 0,
571 })
572 }
573
574 fn write_expr_statement(&mut self, stmt: LangExprStmt<'model>) -> Result<(), GeneratorError> {
575 match stmt {
576 LangExprStmt::Expr(expr) => {
577 self.write_expr(&expr)?;
578 writeln!(self.file, ";")?;
579 },
580
581 LangExprStmt::CreateCodec { t, read, write } => {
582 write!(self.file, "new {}.Codec[", RUNTIME_PACKAGE)?;
583 self.write_type(&t)?;
584 writeln!(self.file, "] {{")?;
585 self.indent_increase();
586
587
588 self.write_indent()?;
589 write!(self.file, "override def read[R, E](reader: {}.FormatReader[R, E]): zio.ZIO[R, E, ", RUNTIME_PACKAGE)?;
590 self.write_type(&t)?;
591 write!(self.file, "] = ")?;
592
593 self.write_statement(*read, true)?;
594
595 self.write_indent()?;
596 write!(self.file, "override def write[R, E](writer: {}.FormatWriter[R, E], value: ", RUNTIME_PACKAGE)?;
597 self.write_type(&t)?;
598 write!(self.file, "): zio.ZIO[R, E, Unit] = ")?;
599
600 self.write_statement(*write, true)?;
601
602 self.indent_decrease();
603 self.write_indent()?;
604 writeln!(self.file, "}}")?;
605 },
606
607 LangExprStmt::CreateConverter { from_type, to_type, body } => {
608 write!(self.file, "new {}.Converter[", RUNTIME_PACKAGE)?;
609 self.write_type(&from_type)?;
610 write!(self.file, ", ")?;
611 self.write_type(&to_type)?;
612 writeln!(self.file, "] {{")?;
613 self.indent_increase();
614
615
616 self.write_indent()?;
617 write!(self.file, "override def convert({}: ", ScalaLanguage::convert_prev_param_name())?;
618 self.write_type(&from_type)?;
619 write!(self.file, "): ")?;
620 self.write_type(&to_type)?;
621 write!(self.file, " = ")?;
622
623 self.write_statement(*body, false)?;
624
625 self.indent_decrease();
626 self.write_indent()?;
627 writeln!(self.file, "}};")?;
628 },
629 }
630
631 Ok(())
632 }
633
634 fn record_io_expr(&self, expr: &mut LangExpr<'model>, ops: &mut Vec<(String, LangExpr<'model>)>) {
635 let name = format!("value{}", ops.len());
636 let old_expr = std::mem::replace(expr, LangExpr::Identifier(name.clone()));
637 ops.push((name, old_expr));
638 }
639
640 fn gather_io_exprs(&self, expr: &mut LangExpr<'model>, ops: &mut Vec<(String, LangExpr<'model>)>) {
641 match expr {
642 LangExpr::ReadDiscriminator | LangExpr::WriteDiscriminator(_) => self.record_io_expr(expr, ops),
643
644 LangExpr::CodecRead { codec } => {
645 self.gather_io_exprs(codec, ops);
646 self.record_io_expr(expr, ops);
647 },
648 LangExpr::CodecWrite { codec, value } => {
649 self.gather_io_exprs(codec, ops);
650 self.gather_io_exprs(value, ops);
651 self.record_io_expr(expr, ops);
652 },
653
654
655
656 LangExpr::InvokeConverter { converter, value } => {
657 self.gather_io_exprs(converter, ops);
658 self.gather_io_exprs(value, ops);
659 },
660
661 LangExpr::InvokeOperation(_, _, _, args) => {
662 for arg in args {
663 self.gather_io_exprs(arg, ops);
664 }
665 },
666 LangExpr::InvokeUserConverter { args, .. } => {
667 for arg in args {
668 self.gather_io_exprs(arg, ops);
669 }
670 },
671 LangExpr::CreateStruct(_, _, _, fields) => {
672 for (_, value) in fields {
673 self.gather_io_exprs(value, ops);
674 }
675 },
676 LangExpr::CreateEnum(_, _, _, _, value) => {
677 self.gather_io_exprs(value, ops);
678 },
679 LangExpr::StructField(_, _, _, value) => {
680 self.gather_io_exprs(value, ops);
681 },
682 _ => (),
683 }
684 }
685
686 fn write_statement(&mut self, stmt: LangStmt<'model>, use_io: bool) -> Result<(), GeneratorError> {
687 match stmt {
688 LangStmt::Expr(mut exprs, mut result_expr) if use_io => {
689
690 let mut io_ops = Vec::new();
691 let mut ignored_values = HashSet::new();
692
693 for mut expr in &mut exprs {
694 self.gather_io_exprs(&mut expr, &mut io_ops);
695 match expr {
696 LangExpr::Identifier(name) => { ignored_values.insert(name.clone()); },
697 _ => (),
698 };
699 }
700
701 if let Some(result_expr) = &mut result_expr {
702 self.gather_io_exprs(result_expr, &mut io_ops);
703 }
704
705 if io_ops.is_empty() {
706 write!(self.file, "zio.IO.succeed(")?;
707 if let Some(result_expr) = result_expr {
708 self.write_expr(&result_expr)?;
709 }
710 else {
711 write!(self.file, "()")?;
712 }
713 writeln!(self.file, ")")?;
714 }
715 else {
716 writeln!(self.file, "for {{")?;
717 self.indent_increase();
718
719 for (name, expr) in io_ops {
720 self.write_indent()?;
721 if ignored_values.contains(&name) {
722 write!(self.file, "_")?;
723 }
724 else {
725 write!(self.file, "{}", name)?;
726 }
727 write!(self.file, " <- ")?;
728 self.write_expr(&expr)?;
729 writeln!(self.file)?;
730 }
731
732 self.indent_decrease();
733 self.write_indent()?;
734 write!(self.file, "}} yield ")?;
735
736 if let Some(result_expr) = result_expr {
737 self.write_expr(&result_expr)?;
738 }
739 else {
740 write!(self.file, "()")?;
741 }
742
743 writeln!(self.file)?;
744 }
745 },
746
747 LangStmt::Expr(exprs, result_expr) => {
748 writeln!(self.file, "{{")?;
749 self.indent_increase();
750
751 for expr in exprs {
752 self.write_indent()?;
753 self.write_expr(&expr)?;
754 writeln!(self.file)?;
755 }
756
757 if let Some(result_expr) = result_expr {
758 self.write_indent()?;
759 self.write_expr(&result_expr)?;
760 writeln!(self.file)?;
761 }
762
763 self.indent_decrease();
764 self.write_indent()?;
765 writeln!(self.file, "}}")?;
766 },
767
768 LangStmt::MatchEnum { mut value, value_type, cases } => {
769 if cases.is_empty() {
770 self.write_expr(&value)?;
771 writeln!(self.file)?;
772 return Ok(())
773 }
774
775 let mut io_ops = Vec::new();
776 if use_io {
777 self.gather_io_exprs(&mut value, &mut io_ops);
778 }
779
780 for (name, op) in &io_ops {
781 self.write_expr(op)?;
782 writeln!(self.file, ".flatMap {{ {} =>", name)?;
783 self.indent_increase();
784 self.write_indent()?;
785 }
786
787
788 self.write_expr(&value)?;
789 writeln!(self.file, " match {{")?;
790 self.indent_increase();
791
792 for MatchCase { binding_name, case_name, body } in cases {
793 self.write_indent()?;
794 write!(self.file, "case ")?;
795 match &value_type {
796 LangType::Versioned(_, name, version, _, _) => {
797 self.write_qual_name(name)?;
798 write!(self.file, ".V{}.{}({})", version, make_type_name(&case_name), binding_name)?;
799 },
800 _ => panic!("Invalid enum type."),
801 }
802
803 write!(self.file, " => ")?;
804
805 self.write_statement(body, use_io)?;
806 }
807
808 self.indent_decrease();
809 self.write_indent()?;
810 writeln!(self.file, "}}")?;
811
812
813
814 for _ in &io_ops {
815 self.indent_decrease();
816 self.write_indent()?;
817 writeln!(self.file, "}}")?;
818 }
819 },
820
821 LangStmt::MatchDiscriminator { mut value, cases } => {
822 let mut io_ops = Vec::new();
823 if use_io {
824 self.gather_io_exprs(&mut value, &mut io_ops);
825 }
826
827 for (name, op) in &io_ops {
828 self.write_expr(op)?;
829 writeln!(self.file, ".flatMap {{ {} =>", name)?;
830 self.indent_increase();
831 self.write_indent()?;
832 }
833
834 self.write_expr(&value)?;
835 writeln!(self.file, " match {{")?;
836 self.indent_increase();
837
838 for (n, body) in cases {
839 self.write_indent()?;
840 write!(self.file, "case {}.Util.BigIntValue({}) => ", RUNTIME_PACKAGE, n)?;
841
842 self.write_statement(body, use_io)?;
843 }
844
845 self.indent_decrease();
846 self.write_indent()?;
847 writeln!(self.file, "}}")?;
848
849 for _ in &io_ops {
850 self.indent_decrease();
851 self.write_indent()?;
852 writeln!(self.file, "}}")?;
853 }
854 },
855
856
857 }
858
859 Ok(())
860 }
861
862 fn write_type_params(&mut self, params: &Vec<String>) -> Result<(), GeneratorError> {
863 if !params.is_empty() {
864 write!(self.file, "[")?;
865 for_sep!(param, params, { write!(self.file, ", ")?; }, {
866 write!(self.file, "{}", param)?;
867 });
868 write!(self.file, "]")?;
869 }
870
871 Ok(())
872 }
873}
874
875
876pub struct ScalaLanguage {}
877
878impl Language for ScalaLanguage {
879 type OptionsBuilder = ScalaOptionsBuilder;
880 type Options = ScalaOptions;
881
882 fn name() -> &'static str {
883 "scala"
884 }
885
886 fn empty_options() -> ScalaOptionsBuilder {
887 ScalaOptionsBuilder {
888 output_dir: None,
889 package_mapping: HashMap::new(),
890 library_mapping: HashMap::new(),
891 }
892 }
893
894 fn add_option(builder: &mut ScalaOptionsBuilder, name: &str, value: OsString) -> Result<(), GeneratorError> {
895 if name == "out_dir" {
896 if builder.output_dir.is_some() {
897 return Err(GeneratorError::InvalidOptions(String::from("Output directory already specified")))
898 }
899
900 builder.output_dir = Some(value);
901 Ok(())
902 }
903 else if let Some(pkg) = name.strip_prefix("pkg:") {
904 let package = model::PackageName::from_str(pkg);
905
906 let scala_package = model::PackageName::from_str(value.to_str().unwrap());
907
908 if builder.library_mapping.contains_key(&package) || builder.package_mapping.insert(package, scala_package).is_some() {
909 return Err(GeneratorError::InvalidOptions(format!("Package already mapped: {}", pkg)))
910 }
911 Ok(())
912 }
913 else if let Some(pkg) = name.strip_prefix("lib:") {
914 let package = model::PackageName::from_str(pkg);
915
916 let scala_package = model::PackageName::from_str(value.to_str().unwrap());
917
918 if builder.package_mapping.contains_key(&package) || builder.library_mapping.insert(package, scala_package).is_some() {
919 return Err(GeneratorError::InvalidOptions(format!("Package already mapped: {}", pkg)))
920 }
921 Ok(())
922 }
923 else {
924 Err(GeneratorError::InvalidOptions(format!("Unknown option: {}", name)))
925 }
926 }
927
928 fn finalize_options(builder: Self::OptionsBuilder) -> Result<Self::Options, GeneratorError> {
929 Ok(ScalaOptions {
930 output_dir: builder.output_dir.ok_or_else(|| GeneratorError::InvalidOptions(String::from("Output directory not specified")))?,
931 package_mapping: builder.package_mapping,
932 library_mapping: builder.library_mapping,
933 })
934 }
935
936 fn generate<Output: for<'output> OutputHandler<'output>>(model: &model::Verilization, options: Self::Options, output: &mut Output) -> Result<(), GeneratorError> {
937 for constant in model.constants() {
938 let mut const_gen = ScalaConstGenerator::open(model, &options, output, constant)?;
939 const_gen.generate()?;
940 }
941
942 for t in model.types() {
943 match t {
944 model::NamedTypeDefinition::StructType(t) | model::NamedTypeDefinition::EnumType(t) => {
945 let mut type_gen = ScalaTypeGenerator::open(model, &options, output, t)?;
946 type_gen.generate()?;
947 },
948 model::NamedTypeDefinition::ExternType(_) => (),
949 }
950 }
951
952 Ok(())
953 }
954
955}