verilization_lang_scala/
lib.rs

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(&param)?;
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}