Implement enums

This commit is contained in:
Andrew Morris
2023-07-07 10:49:21 +10:00
parent 9552c71109
commit d763e0dd13
7 changed files with 189 additions and 15 deletions

View File

@@ -0,0 +1,73 @@
use swc_common::Spanned;
use crate::{
asm::{Number, Object, Value},
static_eval_expr::static_eval_expr,
Diagnostic, DiagnosticLevel,
};
pub fn compile_enum_value(
ts_enum: &swc_ecma_ast::TsEnumDecl,
diagnostics: &mut Vec<Diagnostic>,
) -> Value {
let mut properties = Vec::<(Value, Value)>::new();
let mut next_default_id: Option<f64> = Some(0.0);
for member in &ts_enum.members {
let key = match &member.id {
swc_ecma_ast::TsEnumMemberId::Ident(ident) => ident.sym.to_string(),
swc_ecma_ast::TsEnumMemberId::Str(str) => str.value.to_string(),
};
let init_value = match &member.init {
Some(init) => match static_eval_expr(&init) {
Some(init_value) => match init_value {
Value::Number(Number(n)) => {
next_default_id = Some(n + 1.0);
Some(Value::Number(Number(n)))
}
Value::String(_) => Some(init_value),
_ => None,
},
None => {
diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: "TODO: Static eval failed".to_string(),
span: init.span(),
});
None
}
},
None => None,
};
let value = match init_value {
Some(value) => value,
None => {
let id = match next_default_id {
Some(id) => id,
None => {
diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
message: format!("Missing required initializer"),
span: member.span,
});
0.0
}
};
let value = Value::Number(Number(id));
next_default_id = Some(id + 1.0);
value
}
};
properties.push((Value::String(key.clone()), value.clone()));
properties.push((value, Value::String(key)));
}
Value::Object(Box::new(Object { properties }))
}

View File

@@ -10,6 +10,7 @@ use crate::asm::{
Array, Builtin, Definition, DefinitionContent, FnLine, Function, Instruction, Label, Pointer,
Register, Value,
};
use crate::compile_enum_value::compile_enum_value;
use crate::diagnostic::{Diagnostic, DiagnosticLevel};
use crate::expression_compiler::CompiledExpression;
use crate::expression_compiler::ExpressionCompiler;
@@ -1279,7 +1280,30 @@ impl FunctionCompiler {
Var(var_decl) => self.var_declaration(var_decl),
TsInterface(interface_decl) => self.todo(interface_decl.span, "TsInterface declaration"),
TsTypeAlias(_) => {}
TsEnum(ts_enum) => self.todo(ts_enum.span, "TsEnum declaration"),
TsEnum(ts_enum) => {
let pointer = match self
.scope_analysis
.lookup_value(&OwnerId::Module, &ts_enum.id)
{
Some(Value::Pointer(p)) => p,
_ => {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: format!("Pointer for {} should have been in scope", ts_enum.id.sym),
span: ts_enum.id.span,
});
return;
}
};
let enum_value = compile_enum_value(ts_enum, &mut self.diagnostics);
self.definitions.push(Definition {
pointer,
content: DefinitionContent::Value(enum_value),
});
}
TsModule(ts_module) => self.todo(ts_module.span, "TsModule declaration"),
};
}

View File

@@ -2,6 +2,7 @@ pub mod asm;
mod assembler;
pub mod assembly_parser;
mod compile;
mod compile_enum_value;
mod constants;
mod diagnostic;
mod expression_compiler;

View File

@@ -11,6 +11,7 @@ use crate::asm::{
Class, Definition, DefinitionContent, FnLine, Function, Instruction, Lazy, Module, Object,
Pointer, Register, Value,
};
use crate::compile_enum_value::compile_enum_value;
use crate::diagnostic::{Diagnostic, DiagnosticLevel};
use crate::expression_compiler::{CompiledExpression, ExpressionCompiler};
use crate::function_compiler::{FunctionCompiler, Functionish};
@@ -199,7 +200,27 @@ impl ModuleCompiler {
ExportDecl(ed) => self.compile_export_decl(ed),
ExportNamed(en) => self.compile_named_export(en),
ExportDefaultDecl(edd) => self.compile_export_default_decl(edd),
ExportDefaultExpr(_) => self.todo(module_decl.span(), "ExportDefaultExpr declaration"),
ExportDefaultExpr(ede) => {
let value = match &*ede.expr {
swc_ecma_ast::Expr::Ident(ident) => match self.scope_analysis.lookup(ident) {
Some(name) => Some(name.value.clone()),
None => None,
},
expr => static_eval_expr(expr),
};
match value {
Some(value) => {
self.module.export_default = value;
}
None => {
self.todo(
module_decl.span(),
"Failed to evaluate export default expression",
);
}
};
}
ExportAll(_) => self.todo(module_decl.span(), "ExportAll declaration"),
TsImportEquals(_) => self.not_supported(module_decl.span(), "TsImportEquals declaration"),
TsExportAssignment(_) => {
@@ -326,7 +347,7 @@ impl ModuleCompiler {
}
TsInterface(_) => {}
TsTypeAlias(_) => {}
TsEnum(ts_enum) => self.todo(ts_enum.span, "TsEnum declaration"),
TsEnum(ts_enum) => self.compile_enum_decl(false, ts_enum),
TsModule(ts_module) => self.todo(ts_module.span, "TsModule declaration"),
};
}
@@ -366,6 +387,38 @@ impl ModuleCompiler {
self.module.definitions.append(&mut fn_defns);
}
fn compile_enum_decl(&mut self, export: bool, ts_enum: &swc_ecma_ast::TsEnumDecl) {
let pointer = match self
.scope_analysis
.lookup_value(&OwnerId::Module, &ts_enum.id)
{
Some(Value::Pointer(p)) => p,
_ => {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: format!("Pointer for {} should have been in scope", ts_enum.id.sym),
span: ts_enum.id.span,
});
return;
}
};
if export {
self.module.export_star.properties.push((
Value::String(ts_enum.id.sym.to_string()),
Value::Pointer(pointer.clone()),
));
}
let enum_value = compile_enum_value(ts_enum, &mut self.diagnostics);
self.module.definitions.push(Definition {
pointer,
content: DefinitionContent::Value(enum_value),
});
}
fn compile_export_default_decl(&mut self, edd: &swc_ecma_ast::ExportDefaultDecl) {
use swc_ecma_ast::DefaultDecl;
@@ -432,7 +485,7 @@ impl ModuleCompiler {
}
Decl::TsInterface(_) => {}
Decl::TsTypeAlias(_) => {}
Decl::TsEnum(ts_enum) => self.todo(ts_enum.span, "TsEnum declaration in export"),
Decl::TsEnum(ts_enum) => self.compile_enum_decl(true, ts_enum),
Decl::TsModule(ts_module) => self.todo(ts_module.span, "TsModule declaration in export"),
};
}

View File

@@ -33,6 +33,7 @@ pub enum NameType {
Param,
Function,
Class,
Enum,
Import,
Builtin,
Constant,
@@ -187,6 +188,7 @@ impl ScopeAnalysis {
effectively_const: match type_ {
NameType::Var | NameType::Let | NameType::Param => false,
NameType::Const
| NameType::Enum
| NameType::Function
| NameType::Class
| NameType::Import
@@ -434,12 +436,16 @@ impl ScopeAnalysis {
}
Decl::TsInterface(_) => {}
Decl::TsTypeAlias(_) => {}
Decl::TsEnum(ts_enum) => {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: "TODO: Implement TsEnum declarations".to_string(),
span: ts_enum.span,
});
Decl::TsEnum(ts_enum) => 'b: {
if ts_enum.declare {
break 'b;
}
for member in &ts_enum.members {
if let Some(init) = &member.init {
self.expr(scope, &init);
}
}
}
Decl::TsModule(ts_module) => {
self.diagnostics.push(Diagnostic {
@@ -510,8 +516,8 @@ impl ScopeAnalysis {
}
swc_ecma_ast::Decl::TsInterface(_) => {}
swc_ecma_ast::Decl::TsTypeAlias(_) => {}
swc_ecma_ast::Decl::TsEnum(_) => {
// Diagnostic emitted after hoist processing
swc_ecma_ast::Decl::TsEnum(ts_enum) => {
self.insert_pointer_name(scope, NameType::Enum, &ts_enum.id);
}
swc_ecma_ast::Decl::TsModule(_) => {
// Diagnostic emitted after hoist processing
@@ -571,6 +577,9 @@ impl ScopeAnalysis {
}
}
}
Decl::TsEnum(ts_enum) => {
self.insert_pointer_name(scope, NameType::Enum, &ts_enum.id);
}
_ => {}
},
Stmt::Block(block_stmt) => {
@@ -687,9 +696,7 @@ impl ScopeAnalysis {
}
Decl::TsInterface(_) => {}
Decl::TsTypeAlias(_) => {}
Decl::TsEnum(_) => {
// Diagnostic emitted after hoist processing
}
Decl::TsEnum(_) => {}
Decl::TsModule(_) => {
// Diagnostic emitted after hoist processing
}