mirror of
https://github.com/voltrevo/ValueScript.git
synced 2026-04-18 03:00:27 -04:00
wip
This commit is contained in:
369
valuescript_compiler/src/assembler.rs
Normal file
369
valuescript_compiler/src/assembler.rs
Normal file
@@ -0,0 +1,369 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::assembly_ast::{
|
||||
Array, Assembly, Class, Definition, DefinitionContent, DefinitionRef, Function, Instruction,
|
||||
InstructionOrLabel, Label, LabelRef, Object, Register, Value,
|
||||
};
|
||||
|
||||
pub fn assemble(assembly: &Assembly) -> Vec<u8> {
|
||||
let mut assembler = Assembler {
|
||||
output: Vec::new(),
|
||||
fn_data: Default::default(),
|
||||
definitions_map: LocationMap {
|
||||
references: HashMap::new(),
|
||||
found_locations: HashMap::new(),
|
||||
},
|
||||
};
|
||||
|
||||
assembler.assembly(assembly);
|
||||
|
||||
return assembler.output;
|
||||
}
|
||||
|
||||
struct Assembler {
|
||||
output: Vec<u8>,
|
||||
fn_data: AssemblerFnData,
|
||||
definitions_map: LocationMap,
|
||||
}
|
||||
|
||||
impl Assembler {
|
||||
fn assembly(&mut self, assembly: &Assembly) {
|
||||
for definition in &assembly.definitions {
|
||||
self.definition(definition);
|
||||
}
|
||||
|
||||
self.definitions_map.resolve(&mut self.output);
|
||||
}
|
||||
|
||||
fn definition(&mut self, definition: &Definition) {
|
||||
self.definitions_map.found_locations.insert(
|
||||
LocationRef::DefinitionRef(definition.ref_.clone()),
|
||||
self.output.len(),
|
||||
);
|
||||
|
||||
match &definition.content {
|
||||
DefinitionContent::Function(function) => {
|
||||
self.function(function);
|
||||
}
|
||||
DefinitionContent::Class(class) => {
|
||||
self.class(class);
|
||||
}
|
||||
DefinitionContent::Value(value) => {
|
||||
self.value(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn function(&mut self, function: &Function) {
|
||||
self.output.push(ValueType::Function as u8);
|
||||
|
||||
self.fn_data = Default::default();
|
||||
|
||||
self.fn_data.register_count_pos = self.output.len();
|
||||
self.output.push(0xff); // Placeholder for register count
|
||||
|
||||
self.output.push(function.parameters.len() as u8);
|
||||
|
||||
for parameter in &function.parameters {
|
||||
self.register(parameter);
|
||||
}
|
||||
|
||||
for instruction_or_label in &function.body {
|
||||
match instruction_or_label {
|
||||
InstructionOrLabel::Instruction(instruction) => {
|
||||
self.instruction(instruction);
|
||||
}
|
||||
InstructionOrLabel::Label(label) => {
|
||||
self.label(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.output.push(Instruction::End.byte());
|
||||
|
||||
// TODO: Handle >255 registers
|
||||
self.output[self.fn_data.register_count_pos] = self.fn_data.register_map.len() as u8;
|
||||
|
||||
self.fn_data.labels_map.resolve(&mut self.output);
|
||||
}
|
||||
|
||||
fn class(&mut self, class: &Class) {
|
||||
self.output.push(ValueType::Class as u8);
|
||||
self.value(&class.constructor);
|
||||
self.value(&class.methods);
|
||||
}
|
||||
|
||||
fn label(&mut self, label: &Label) {
|
||||
self
|
||||
.fn_data
|
||||
.labels_map
|
||||
.found_locations
|
||||
.insert(LocationRef::LabelRef(label.ref_()), self.output.len());
|
||||
}
|
||||
|
||||
fn instruction(&mut self, instruction: &Instruction) {
|
||||
use Instruction::*;
|
||||
|
||||
self.output.push(instruction.byte());
|
||||
|
||||
match instruction {
|
||||
End => {}
|
||||
OpInc(dst) | OpDec(dst) => {
|
||||
self.register(dst);
|
||||
}
|
||||
Mov(arg, dst)
|
||||
| OpNot(arg, dst)
|
||||
| OpBitNot(arg, dst)
|
||||
| TypeOf(arg, dst)
|
||||
| UnaryPlus(arg, dst)
|
||||
| UnaryMinus(arg, dst) => {
|
||||
self.value(arg);
|
||||
self.register(dst);
|
||||
}
|
||||
OpPlus(arg1, arg2, dst)
|
||||
| OpMinus(arg1, arg2, dst)
|
||||
| OpMul(arg1, arg2, dst)
|
||||
| OpDiv(arg1, arg2, dst)
|
||||
| OpMod(arg1, arg2, dst)
|
||||
| OpExp(arg1, arg2, dst)
|
||||
| OpEq(arg1, arg2, dst)
|
||||
| OpNe(arg1, arg2, dst)
|
||||
| OpTripleEq(arg1, arg2, dst)
|
||||
| OpTripleNe(arg1, arg2, dst)
|
||||
| OpAnd(arg1, arg2, dst)
|
||||
| OpOr(arg1, arg2, dst)
|
||||
| OpLess(arg1, arg2, dst)
|
||||
| OpLessEq(arg1, arg2, dst)
|
||||
| OpGreater(arg1, arg2, dst)
|
||||
| OpGreaterEq(arg1, arg2, dst)
|
||||
| OpNullishCoalesce(arg1, arg2, dst)
|
||||
| OpOptionalChain(arg1, arg2, dst)
|
||||
| OpBitAnd(arg1, arg2, dst)
|
||||
| OpBitOr(arg1, arg2, dst)
|
||||
| OpBitXor(arg1, arg2, dst)
|
||||
| OpLeftShift(arg1, arg2, dst)
|
||||
| OpRightShift(arg1, arg2, dst)
|
||||
| OpRightShiftUnsigned(arg1, arg2, dst)
|
||||
| InstanceOf(arg1, arg2, dst)
|
||||
| In(arg1, arg2, dst)
|
||||
| Call(arg1, arg2, dst)
|
||||
| Bind(arg1, arg2, dst)
|
||||
| Sub(arg1, arg2, dst)
|
||||
| SubMov(arg1, arg2, dst)
|
||||
| New(arg1, arg2, dst) => {
|
||||
self.value(arg1);
|
||||
self.value(arg2);
|
||||
self.register(dst);
|
||||
}
|
||||
Apply(arg1, arg2, arg3, dst) | SubCall(arg1, arg2, arg3, dst) => {
|
||||
self.value(arg1);
|
||||
self.value(arg2);
|
||||
self.value(arg3);
|
||||
self.register(dst);
|
||||
}
|
||||
Jmp(label_ref) => {
|
||||
self.label_ref(label_ref);
|
||||
}
|
||||
JmpIf(value, label_ref) => {
|
||||
self.value(value);
|
||||
self.label_ref(label_ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&mut self, value: &Value) {
|
||||
match value {
|
||||
Value::Register(register) => {
|
||||
self.output.push(ValueType::Register as u8);
|
||||
self.register(register);
|
||||
}
|
||||
Value::Number(number) => self.number(*number),
|
||||
Value::String(string) => self.string(string),
|
||||
Value::Boolean(boolean) => match boolean {
|
||||
false => self.output.push(ValueType::False as u8),
|
||||
true => self.output.push(ValueType::True as u8),
|
||||
},
|
||||
Value::Null => self.output.push(ValueType::Null as u8),
|
||||
Value::Undefined => self.output.push(ValueType::Undefined as u8),
|
||||
Value::Array(array) => self.array(array),
|
||||
Value::Object(object) => self.object(object),
|
||||
Value::DefinitionRef(definition_ref) => self.definition_ref(definition_ref),
|
||||
}
|
||||
}
|
||||
|
||||
fn label_ref(&mut self, label_ref: &LabelRef) {
|
||||
self
|
||||
.fn_data
|
||||
.labels_map
|
||||
.add_unresolved(LocationRef::LabelRef(label_ref.clone()), &mut self.output);
|
||||
}
|
||||
|
||||
fn register(&mut self, register: &Register) {
|
||||
let reg_index = self.lookup_register(register);
|
||||
self.output.push(reg_index);
|
||||
}
|
||||
|
||||
fn lookup_register(&mut self, register: &Register) -> u8 {
|
||||
match register {
|
||||
Register::Return => 0,
|
||||
Register::This => 1,
|
||||
Register::Named(_) => match self.fn_data.register_map.get(register) {
|
||||
Some(index) => *index,
|
||||
None => {
|
||||
// TODO: Support >255 registers
|
||||
let index = (self.fn_data.register_map.len() as u8) + 2;
|
||||
self.fn_data.register_map.insert(register.clone(), index);
|
||||
index
|
||||
}
|
||||
},
|
||||
Register::Ignore => 0xff,
|
||||
}
|
||||
}
|
||||
|
||||
fn varsize_uint(&mut self, value: usize) {
|
||||
let mut x = value;
|
||||
|
||||
loop {
|
||||
let mut b: u8 = (x % 128) as u8;
|
||||
x /= 128;
|
||||
|
||||
if x != 0 {
|
||||
b += 128;
|
||||
}
|
||||
|
||||
self.output.push(b);
|
||||
|
||||
if x == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn number(&mut self, value: f64) {
|
||||
self.output.push(ValueType::Number as u8);
|
||||
|
||||
if value == (value as i8) as f64 {
|
||||
self.output.push(ValueType::SignedByte as u8);
|
||||
|
||||
for b in (value as i8).to_le_bytes() {
|
||||
self.output.push(b);
|
||||
}
|
||||
} else {
|
||||
self.output.push(ValueType::Number as u8);
|
||||
|
||||
for b in value.to_le_bytes() {
|
||||
self.output.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn string(&mut self, value: &String) {
|
||||
self.output.push(ValueType::String as u8);
|
||||
self.varsize_uint(value.len());
|
||||
|
||||
for b in value.as_bytes() {
|
||||
self.output.push(*b);
|
||||
}
|
||||
}
|
||||
|
||||
fn definition_ref(&mut self, value: &DefinitionRef) {
|
||||
self.output.push(ValueType::Function as u8);
|
||||
self
|
||||
.definitions_map
|
||||
.add_unresolved(LocationRef::DefinitionRef(value.clone()), &mut self.output);
|
||||
}
|
||||
|
||||
fn array(&mut self, array: &Array) {
|
||||
self.output.push(ValueType::Array as u8);
|
||||
self.varsize_uint(array.values.len());
|
||||
|
||||
for value in &array.values {
|
||||
self.value(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn object(&mut self, object: &Object) {
|
||||
self.output.push(ValueType::Object as u8);
|
||||
self.varsize_uint(object.properties.len());
|
||||
|
||||
for (key, value) in &object.properties {
|
||||
self.value(key);
|
||||
self.value(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ValueType {
|
||||
End = 0x00,
|
||||
Void = 0x01,
|
||||
Undefined = 0x02,
|
||||
Null = 0x03,
|
||||
False = 0x04,
|
||||
True = 0x05,
|
||||
SignedByte = 0x06,
|
||||
Number = 0x07,
|
||||
String = 0x08,
|
||||
Array = 0x09,
|
||||
Object = 0x0a,
|
||||
Function = 0x0b,
|
||||
// Instance = 0x0c,
|
||||
Pointer = 0x0d,
|
||||
Register = 0x0e,
|
||||
// External = 0x0f,
|
||||
Builtin = 0x10,
|
||||
Class = 0x11,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Clone)]
|
||||
enum LocationRef {
|
||||
DefinitionRef(DefinitionRef),
|
||||
LabelRef(LabelRef),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LocationRef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
LocationRef::DefinitionRef(def) => write!(f, "{}", def),
|
||||
LocationRef::LabelRef(label) => write!(f, "{}", label),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct LocationMap {
|
||||
references: HashMap<LocationRef, Vec<usize>>,
|
||||
found_locations: HashMap<LocationRef, usize>,
|
||||
}
|
||||
|
||||
impl LocationMap {
|
||||
fn add_unresolved(&mut self, ref_: LocationRef, output: &mut Vec<u8>) {
|
||||
self.references.entry(ref_).or_default().push(output.len());
|
||||
|
||||
output.push(0xff);
|
||||
output.push(0xff); // TODO: Support >65535
|
||||
}
|
||||
|
||||
fn resolve(&self, output: &mut Vec<u8>) {
|
||||
for (name, ref_locations) in &self.references {
|
||||
let location_optional = self.found_locations.get(name);
|
||||
|
||||
if location_optional.is_none() {
|
||||
std::panic!("Unresolved reference to {} at {}", name, ref_locations[0]);
|
||||
}
|
||||
|
||||
let location = location_optional.unwrap();
|
||||
|
||||
for ref_location in ref_locations {
|
||||
output[*ref_location] = (*location % 256) as u8;
|
||||
output[*ref_location + 1] = (*location / 256) as u8; // TODO: Support >65535
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct AssemblerFnData {
|
||||
register_map: HashMap<Register, u8>,
|
||||
register_count_pos: usize,
|
||||
labels_map: LocationMap,
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
// pub fn foo() -> Assembly {}
|
||||
|
||||
struct Assembly {
|
||||
definitions: Vec<Definition>,
|
||||
pub struct Assembly {
|
||||
pub definitions: Vec<Definition>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Assembly {
|
||||
@@ -14,9 +12,9 @@ impl std::fmt::Display for Assembly {
|
||||
}
|
||||
}
|
||||
|
||||
struct Definition {
|
||||
ref_: DefinitionRef,
|
||||
content: DefinitionContent,
|
||||
pub struct Definition {
|
||||
pub ref_: DefinitionRef,
|
||||
pub content: DefinitionContent,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Definition {
|
||||
@@ -25,8 +23,9 @@ impl std::fmt::Display for Definition {
|
||||
}
|
||||
}
|
||||
|
||||
enum DefinitionContent {
|
||||
pub enum DefinitionContent {
|
||||
Function(Function),
|
||||
Class(Class),
|
||||
Value(Value),
|
||||
}
|
||||
|
||||
@@ -36,6 +35,9 @@ impl std::fmt::Display for DefinitionContent {
|
||||
DefinitionContent::Function(function) => {
|
||||
write!(f, "{}", function)
|
||||
}
|
||||
DefinitionContent::Class(class) => {
|
||||
write!(f, "{}", class)
|
||||
}
|
||||
DefinitionContent::Value(value) => {
|
||||
write!(f, "{}", value)
|
||||
}
|
||||
@@ -43,8 +45,9 @@ impl std::fmt::Display for DefinitionContent {
|
||||
}
|
||||
}
|
||||
|
||||
struct DefinitionRef {
|
||||
name: String,
|
||||
#[derive(Hash, PartialEq, Eq, Clone)]
|
||||
pub struct DefinitionRef {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DefinitionRef {
|
||||
@@ -53,9 +56,9 @@ impl std::fmt::Display for DefinitionRef {
|
||||
}
|
||||
}
|
||||
|
||||
struct Function {
|
||||
parameters: Vec<Register>,
|
||||
body: Vec<InstructionOrLabel>,
|
||||
pub struct Function {
|
||||
pub parameters: Vec<Register>,
|
||||
pub body: Vec<InstructionOrLabel>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Function {
|
||||
@@ -82,10 +85,38 @@ impl std::fmt::Display for Function {
|
||||
}
|
||||
}
|
||||
|
||||
enum Register {
|
||||
pub struct Class {
|
||||
pub constructor: Value,
|
||||
pub methods: Value,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Class {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "class({}, ", self.constructor)?;
|
||||
|
||||
match &self.methods {
|
||||
Value::Object(object) => {
|
||||
write!(f, "{{\n")?;
|
||||
for (name, method) in &object.properties {
|
||||
write!(f, " {}: {}\n", name, method)?;
|
||||
}
|
||||
write!(f, "}})\n")?;
|
||||
}
|
||||
_ => {
|
||||
write!(f, "{})\n", self.methods)?;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Clone)]
|
||||
pub enum Register {
|
||||
Return,
|
||||
This,
|
||||
Named(String),
|
||||
Ignore,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Register {
|
||||
@@ -94,11 +125,12 @@ impl std::fmt::Display for Register {
|
||||
Register::Return => write!(f, "%return"),
|
||||
Register::This => write!(f, "%this"),
|
||||
Register::Named(name) => write!(f, "%{}", name),
|
||||
Register::Ignore => write!(f, "%ignore"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum InstructionOrLabel {
|
||||
pub enum InstructionOrLabel {
|
||||
Instruction(Instruction),
|
||||
Label(Label),
|
||||
}
|
||||
@@ -116,8 +148,16 @@ impl std::fmt::Display for InstructionOrLabel {
|
||||
}
|
||||
}
|
||||
|
||||
struct Label {
|
||||
name: String,
|
||||
pub struct Label {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn ref_(&self) -> LabelRef {
|
||||
LabelRef {
|
||||
name: self.name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Label {
|
||||
@@ -126,8 +166,9 @@ impl std::fmt::Display for Label {
|
||||
}
|
||||
}
|
||||
|
||||
struct LabelRef {
|
||||
name: String,
|
||||
#[derive(Hash, PartialEq, Eq, Clone)]
|
||||
pub struct LabelRef {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LabelRef {
|
||||
@@ -176,8 +217,8 @@ pub enum Instruction {
|
||||
Sub(Value, Value, Register),
|
||||
SubMov(Value, Value, Register),
|
||||
SubCall(Value, Value, Value, Register),
|
||||
Jmp(Label),
|
||||
JmpIf(Value, Label),
|
||||
Jmp(LabelRef),
|
||||
JmpIf(Value, LabelRef),
|
||||
UnaryPlus(Value, Register),
|
||||
UnaryMinus(Value, Register),
|
||||
New(Value, Value, Register),
|
||||
@@ -297,9 +338,9 @@ impl std::fmt::Display for Instruction {
|
||||
Instruction::SubCall(obj, subscript, args, register) => {
|
||||
write!(f, "subcall {} {} {} {}", obj, subscript, args, register)
|
||||
}
|
||||
Instruction::Jmp(label) => write!(f, "jmp {}", label),
|
||||
Instruction::JmpIf(value, label) => {
|
||||
write!(f, "jmpif {} {}", value, label)
|
||||
Instruction::Jmp(label_ref) => write!(f, "jmp {}", label_ref),
|
||||
Instruction::JmpIf(value, label_ref) => {
|
||||
write!(f, "jmpif {} {}", value, label_ref)
|
||||
}
|
||||
Instruction::UnaryPlus(value, register) => {
|
||||
write!(f, "unary+ {} {}", value, register)
|
||||
@@ -314,7 +355,61 @@ impl std::fmt::Display for Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
enum Value {
|
||||
impl Instruction {
|
||||
pub fn byte(&self) -> u8 {
|
||||
use Instruction::*;
|
||||
|
||||
// TODO: Define this in one place only
|
||||
match self {
|
||||
End => 0x00,
|
||||
Mov(..) => 0x01,
|
||||
OpInc(..) => 0x02,
|
||||
OpDec(..) => 0x03,
|
||||
OpPlus(..) => 0x04,
|
||||
OpMinus(..) => 0x05,
|
||||
OpMul(..) => 0x06,
|
||||
OpDiv(..) => 0x07,
|
||||
OpMod(..) => 0x08,
|
||||
OpExp(..) => 0x09,
|
||||
OpEq(..) => 0x0a,
|
||||
OpNe(..) => 0x0b,
|
||||
OpTripleEq(..) => 0x0c,
|
||||
OpTripleNe(..) => 0x0d,
|
||||
OpAnd(..) => 0x0e,
|
||||
OpOr(..) => 0x0f,
|
||||
OpNot(..) => 0x10,
|
||||
OpLess(..) => 0x11,
|
||||
OpLessEq(..) => 0x12,
|
||||
OpGreater(..) => 0x13,
|
||||
OpGreaterEq(..) => 0x14,
|
||||
OpNullishCoalesce(..) => 0x15,
|
||||
OpOptionalChain(..) => 0x16,
|
||||
OpBitAnd(..) => 0x17,
|
||||
OpBitOr(..) => 0x18,
|
||||
OpBitNot(..) => 0x19,
|
||||
OpBitXor(..) => 0x1a,
|
||||
OpLeftShift(..) => 0x1b,
|
||||
OpRightShift(..) => 0x1c,
|
||||
OpRightShiftUnsigned(..) => 0x1d,
|
||||
TypeOf(..) => 0x1e,
|
||||
InstanceOf(..) => 0x1f,
|
||||
In(..) => 0x20,
|
||||
Call(..) => 0x21,
|
||||
Apply(..) => 0x22,
|
||||
Bind(..) => 0x23,
|
||||
Sub(..) => 0x24,
|
||||
SubMov(..) => 0x25,
|
||||
SubCall(..) => 0x26,
|
||||
Jmp(..) => 0x27,
|
||||
JmpIf(..) => 0x28,
|
||||
UnaryPlus(..) => 0x29,
|
||||
UnaryMinus(..) => 0x2a,
|
||||
New(..) => 0x2b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Value {
|
||||
Undefined,
|
||||
Null,
|
||||
Boolean(bool),
|
||||
@@ -324,7 +419,6 @@ enum Value {
|
||||
Object(Box<Object>),
|
||||
Register(Register),
|
||||
DefinitionRef(DefinitionRef),
|
||||
LabelRef(LabelRef),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Value {
|
||||
@@ -343,13 +437,12 @@ impl std::fmt::Display for Value {
|
||||
Value::Object(value) => write!(f, "{}", value),
|
||||
Value::Register(value) => write!(f, "{}", value),
|
||||
Value::DefinitionRef(value) => write!(f, "{}", value),
|
||||
Value::LabelRef(value) => write!(f, "{}", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Array {
|
||||
values: Vec<Value>,
|
||||
pub struct Array {
|
||||
pub values: Vec<Value>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Array {
|
||||
@@ -365,8 +458,8 @@ impl std::fmt::Display for Array {
|
||||
}
|
||||
}
|
||||
|
||||
struct Object {
|
||||
properties: Vec<(String, Value)>,
|
||||
pub struct Object {
|
||||
pub properties: Vec<(Value, Value)>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Object {
|
||||
@@ -380,12 +473,7 @@ impl std::fmt::Display for Object {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"{}: {}",
|
||||
serde_json::to_string(&key).expect("Failed json serialization"),
|
||||
value
|
||||
)?;
|
||||
write!(f, "{}: {}", key, value)?;
|
||||
}
|
||||
write!(f, " }}")
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
mod assemble;
|
||||
mod assembler;
|
||||
mod assembly_ast;
|
||||
mod capture_finder;
|
||||
mod compile;
|
||||
|
||||
Reference in New Issue
Block a user