Work on sexpr parser

This commit is contained in:
Chad Retz 2017-03-06 14:18:25 -06:00
parent 87b866c600
commit 2761fd761b
8 changed files with 1057 additions and 66 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "src/test/resources/spec"]
path = src/test/resources/spec
url = https://github.com/WebAssembly/spec.git

View File

@ -0,0 +1,558 @@
package asmble.ast
import kotlin.reflect.KClass
sealed class Node {
data class Module(
val types: List<Type.Func>,
val imports: List<Import>,
val tables: List<Type.Table>,
val memories: List<Type.Memory>,
val globals: List<Global>,
val exports: List<Export>,
val startFuncIndex: Int?,
val elems: List<Elem>,
val funcs: List<Func>,
val data: List<Data>,
val customSections: List<CustomSection>
) : Node()
enum class ExternalKind {
FUNCTION, TABLE, MEMORY, GLOBAL
}
enum class ElemType {
ANYFUNC
}
sealed class Type : Node() {
sealed class Value : Type() {
object I32 : Value()
object I64 : Value()
object F32 : Value()
object F64 : Value()
}
data class Func(
val params: List<Value>,
val ret: Value?
) : Type()
data class Global(
val contentType: Value,
val mutable: Boolean
) : Type()
data class Table(
val elemType: ElemType,
val limits: ResizableLimits
) : Type()
data class Memory(
val limits: ResizableLimits
) : Type()
}
data class ResizableLimits(
val initial: Int,
val maximum: Int?
)
data class Import(
val module: String,
val field: String,
val kind: Kind
) : Node() {
sealed class Kind {
data class Func(val typeIndex: Int) : Kind()
data class Table(val type: Type.Table) : Kind()
data class Memory(val type: Type.Memory) : Kind()
data class Global(val type: Type.Global) : Kind()
}
}
data class Global(
val type: Type.Global,
val init: List<Instr>
) : Node()
data class Export(
val field: String,
val kind: ExternalKind,
val index: Int
) : Node()
data class Elem(
val index: Int,
val offset: List<Instr>,
val funcIndices: List<Int>
) : Node()
data class Func(
val type: Type.Func,
val locals: List<Type.Value>,
val instructions: List<Instr>
) : Node()
data class Data(
val index: Int,
val offset: List<Instr>,
val data: Array<Byte>
) : Node()
data class CustomSection(
val sectionIndex: Int,
val name: String,
val payload: Array<Byte>
) : Node()
sealed class Instr : Node() {
// Control flow
object Unreachable : Instr()
object Nop : Instr()
data class Block(val type: Type.Value?) : Instr()
data class Loop(val type: Type.Value?) : Instr()
data class If(val type: Type.Value?) : Instr()
object Else : Instr()
object End : Instr()
data class Br(val relativeDepth: Int) : Instr()
data class BrIf(val relativeDepth: Int) : Instr()
data class BrTable(
val targetTable: List<Int>,
val default: Int
) : Instr()
object Return : Instr()
// Call operators
data class Call(val funcIndex: Int) : Instr()
data class CallIndirect(
val typeIndex: Int,
val reserved: Boolean
) : Instr()
// Parametric operators
object Drop : Instr()
object Select : Instr()
// Variable access
data class GetLocal(val index: Int) : Instr()
data class SetLocal(val index: Int) : Instr()
data class TeeLocal(val index: Int) : Instr()
data class GetGlobal(val index: Int) : Instr()
data class SetGlobal(val index: Int) : Instr()
// Memory operators
data class I32Load(val flags: Int, val offset: Int) : Instr()
data class I64Load(val flags: Int, val offset: Int) : Instr()
data class F32Load(val flags: Int, val offset: Int) : Instr()
data class F64Load(val flags: Int, val offset: Int) : Instr()
data class I32Load8S(val flags: Int, val offset: Int) : Instr()
data class I32Load8U(val flags: Int, val offset: Int) : Instr()
data class I32Load16S(val flags: Int, val offset: Int) : Instr()
data class I32Load16U(val flags: Int, val offset: Int) : Instr()
data class I64Load8S(val flags: Int, val offset: Int) : Instr()
data class I64Load8U(val flags: Int, val offset: Int) : Instr()
data class I64Load16S(val flags: Int, val offset: Int) : Instr()
data class I64Load16U(val flags: Int, val offset: Int) : Instr()
data class I64Load32S(val flags: Int, val offset: Int) : Instr()
data class I64Load32U(val flags: Int, val offset: Int) : Instr()
data class I32Store(val flags: Int, val offset: Int) : Instr()
data class I64Store(val flags: Int, val offset: Int) : Instr()
data class F32Store(val flags: Int, val offset: Int) : Instr()
data class F64Store(val flags: Int, val offset: Int) : Instr()
data class I32Store8(val flags: Int, val offset: Int) : Instr()
data class I32Store16(val flags: Int, val offset: Int) : Instr()
data class I64Store8(val flags: Int, val offset: Int) : Instr()
data class I64Store16(val flags: Int, val offset: Int) : Instr()
data class I64Store32(val flags: Int, val offset: Int) : Instr()
data class CurrentMemory(val reserved: Boolean) : Instr()
data class GrowMemory(val reserved: Boolean) : Instr()
// Constants
data class I32Const(val value: Int) : Instr()
data class I64Const(val value: Long) : Instr()
data class F32Const(val value: Float) : Instr()
data class F64Const(val value: Double) : Instr()
// Comparison operators
object I32Eqz : Instr()
object I32Eq : Instr()
object I32Ne : Instr()
object I32LtS : Instr()
object I32LtU : Instr()
object I32GtS : Instr()
object I32GtU : Instr()
object I32LeS : Instr()
object I32LeU : Instr()
object I32GeS : Instr()
object I32GeU : Instr()
object I64Eqz : Instr()
object I64Eq : Instr()
object I64Ne : Instr()
object I64LtS : Instr()
object I64LtU : Instr()
object I64GtS : Instr()
object I64GtU : Instr()
object I64LeS : Instr()
object I64LeU : Instr()
object I64GeS : Instr()
object I64GeU : Instr()
object F32Eq : Instr()
object F32Ne : Instr()
object F32Lt : Instr()
object F32Gt : Instr()
object F32Le : Instr()
object F32Ge : Instr()
object F64Eq : Instr()
object F64Ne : Instr()
object F64Lt : Instr()
object F64Gt : Instr()
object F64Le : Instr()
object F64Ge : Instr()
// Numeric operators
object I32Clz : Instr()
object I32Ctz : Instr()
object I32Popcnt : Instr()
object I32Add : Instr()
object I32Sub : Instr()
object I32Mul : Instr()
object I32DivS : Instr()
object I32DivU : Instr()
object I32RemS : Instr()
object I32RemU : Instr()
object I32And : Instr()
object I32Or : Instr()
object I32Xor : Instr()
object I32Shl : Instr()
object I32ShrS : Instr()
object I32ShrU : Instr()
object I32Rotl : Instr()
object I32Rotr : Instr()
object I64Clz : Instr()
object I64Ctz : Instr()
object I64Popcnt : Instr()
object I64Add : Instr()
object I64Sub : Instr()
object I64Mul : Instr()
object I64DivS : Instr()
object I64DivU : Instr()
object I64RemS : Instr()
object I64RemU : Instr()
object I64And : Instr()
object I64Or : Instr()
object I64Xor : Instr()
object I64Shl : Instr()
object I64ShrS : Instr()
object I64ShrU : Instr()
object I64Rotl : Instr()
object I64Rotr : Instr()
object F32Abs : Instr()
object F32Neg : Instr()
object F32Ceil : Instr()
object F32Floor : Instr()
object F32Trunc : Instr()
object F32Nearest : Instr()
object F32Sqrt : Instr()
object F32Add : Instr()
object F32Sub : Instr()
object F32Mul : Instr()
object F32Div : Instr()
object F32Min : Instr()
object F32Max : Instr()
object F32CopySign : Instr()
object F64Abs : Instr()
object F64Neg : Instr()
object F64Ceil : Instr()
object F64Floor : Instr()
object F64Trunc : Instr()
object F64Nearest : Instr()
object F64Sqrt : Instr()
object F64Add : Instr()
object F64Sub : Instr()
object F64Mul : Instr()
object F64Div : Instr()
object F64Min : Instr()
object F64Max : Instr()
object F64CopySign : Instr()
// Conversions
object I32WrapI64 : Instr()
object I32TruncSF32 : Instr()
object I32TruncUF32 : Instr()
object I32TruncSF64 : Instr()
object I32TruncUF64 : Instr()
object I64ExtendSI32 : Instr()
object I64ExtendUI32 : Instr()
object I64TruncSF32 : Instr()
object I64TruncUF32 : Instr()
object I64TruncSF64 : Instr()
object I64TruncUF64 : Instr()
object F32ConvertSI32 : Instr()
object F32ConvertUI32 : Instr()
object F32ConvertSI64 : Instr()
object F32ConvertUI64 : Instr()
object F32DemoteF64 : Instr()
object F64ConvertSI32 : Instr()
object F64ConvertUI32 : Instr()
object F64ConvertSI64 : Instr()
object F64ConvertUI64 : Instr()
object F64PromoteF32 : Instr()
// Reinterpretations
object I32ReinterpretF32 : Instr()
object I64ReinterpretF64 : Instr()
object F32ReinterpretI32 : Instr()
object F64ReinterpretI64 : Instr()
}
sealed class InstrOp(name: String) {
sealed class ControlFlowOp(name: String) : InstrOp(name) {
data class NoArg(val name: String, val create: Instr) : ControlFlowOp(name)
data class TypeArg(val name: String, val create: (Type.Value?) -> Instr) : ControlFlowOp(name)
data class DepthArg(val name: String, val create: (Int) -> Instr) : ControlFlowOp(name)
data class TableArg(val name: String, val create: (List<Int>, Int) -> Instr) : ControlFlowOp(name)
}
sealed class CallOp(name: String) : InstrOp(name) {
data class IndexArg(val name: String, val create: (Int) -> Instr) : CallOp(name)
data class IndexReservedArg(val name: String, val create: (Int, Boolean) -> Instr) : CallOp(name)
}
sealed class ParamOp(name: String) : InstrOp(name) {
data class NoArg(val name: String, val create: Instr) : ParamOp(name)
}
sealed class VarOp(name: String) : InstrOp(name) {
data class IndexArg(val name: String, val create: (Int) -> Instr) : VarOp(name)
}
sealed class MemOp(name: String) : InstrOp(name) {
data class FlagsOffsetArg(val name: String, val create: (Int, Int) -> Instr) : MemOp(name)
data class ReservedArg(val name: String, val create: (Boolean) -> Instr) : MemOp(name)
}
sealed class ConstOp(name: String) : InstrOp(name) {
data class IntArg(val name: String, val create: (Int) -> Instr) : ConstOp(name)
data class LongArg(val name: String, val create: (Long) -> Instr) : ConstOp(name)
data class FloatArg(val name: String, val create: (Float) -> Instr) : ConstOp(name)
data class DoubleArg(val name: String, val create: (Double) -> Instr) : ConstOp(name)
}
sealed class CompareOp(name: String) : InstrOp(name) {
data class NoArg(val name: String, val create: Instr) : CompareOp(name)
}
sealed class NumOp(name: String) : InstrOp(name) {
data class NoArg(val name: String, val create: Instr) : NumOp(name)
}
sealed class ConvertOp(name: String) : InstrOp(name) {
data class NoArg(val name: String, val create: Instr) : ConvertOp(name)
}
sealed class ReinterpretOp(name: String) : InstrOp(name) {
data class NoArg(val name: String, val create: Instr) : ReinterpretOp(name)
}
companion object {
// TODO: why can't I set a val in init?
var strToOpMap = emptyMap<String, InstrOp>(); private set
var classToOpMap = emptyMap<KClass<out Instr>, InstrOp>(); private set
init {
// Can't use reification here because inline funcs not allowed in nested context :-(
fun <T> opMapEntry(name: String, newOp: (String, T) -> InstrOp, create: T, clazz: KClass<out Instr>) {
require(!strToOpMap.contains(name) && !classToOpMap.contains(clazz))
val op = newOp(name, create)
strToOpMap += name to op
classToOpMap += clazz to op
}
opMapEntry("unreachable", ControlFlowOp::NoArg, Instr.Unreachable, Instr.Unreachable::class)
opMapEntry("nop", ControlFlowOp::NoArg, Instr.Nop, Instr.Nop::class)
opMapEntry("block", ControlFlowOp::TypeArg, Instr::Block, Instr.Block::class)
opMapEntry("loop", ControlFlowOp::TypeArg, Instr::Loop, Instr.Loop::class)
opMapEntry("if", ControlFlowOp::TypeArg, Instr::If, Instr.If::class)
opMapEntry("else", ControlFlowOp::NoArg, Instr.Else, Instr.Else::class)
opMapEntry("end", ControlFlowOp::NoArg, Instr.End, Instr.End::class)
opMapEntry("br", ControlFlowOp::DepthArg, Instr::Br, Instr.Br::class)
opMapEntry("br_if", ControlFlowOp::DepthArg, Instr::BrIf, Instr.BrIf::class)
opMapEntry("br_if", ControlFlowOp::TableArg, Instr::BrTable, Instr.BrTable::class)
opMapEntry("return", ControlFlowOp::NoArg, Instr.Return, Instr.Return::class)
opMapEntry("call", CallOp::IndexArg, Instr::Call, Instr.Call::class)
opMapEntry("call_indirect", CallOp::IndexReservedArg, Instr::CallIndirect, Instr.CallIndirect::class)
opMapEntry("drop", ParamOp::NoArg, Instr.Drop, Instr.Drop::class)
opMapEntry("select", ParamOp::NoArg, Instr.Select, Instr.Drop::class)
opMapEntry("get_local", VarOp::IndexArg, Instr::GetLocal, Instr.GetLocal::class)
opMapEntry("set_local", VarOp::IndexArg, Instr::SetLocal, Instr.SetLocal::class)
opMapEntry("tee_local", VarOp::IndexArg, Instr::TeeLocal, Instr.TeeLocal::class)
opMapEntry("get_global", VarOp::IndexArg, Instr::GetGlobal, Instr.GetGlobal::class)
opMapEntry("set_global", VarOp::IndexArg, Instr::SetGlobal, Instr.SetGlobal::class)
opMapEntry("i32.load", MemOp::FlagsOffsetArg, Instr::I32Load, Instr.I32Load::class)
opMapEntry("i64.load", MemOp::FlagsOffsetArg, Instr::I64Load, Instr.I64Load::class)
opMapEntry("f32.load", MemOp::FlagsOffsetArg, Instr::F32Load, Instr.F32Load::class)
opMapEntry("f64.load", MemOp::FlagsOffsetArg, Instr::F64Load, Instr.F64Load::class)
opMapEntry("i32.load8_s", MemOp::FlagsOffsetArg, Instr::I32Load8S, Instr.I32Load8S::class)
opMapEntry("i32.load8_u", MemOp::FlagsOffsetArg, Instr::I32Load8U, Instr.I32Load8U::class)
opMapEntry("i32.load16_s", MemOp::FlagsOffsetArg, Instr::I32Load16S, Instr.I32Load16S::class)
opMapEntry("i32.load16_u", MemOp::FlagsOffsetArg, Instr::I32Load16U, Instr.I32Load16U::class)
opMapEntry("i64.load8_s", MemOp::FlagsOffsetArg, Instr::I64Load8S, Instr.I64Load8S::class)
opMapEntry("i64.load8_u", MemOp::FlagsOffsetArg, Instr::I64Load8U, Instr.I64Load8U::class)
opMapEntry("i64.load16_s", MemOp::FlagsOffsetArg, Instr::I64Load16S, Instr.I64Load16S::class)
opMapEntry("i64.load16_u", MemOp::FlagsOffsetArg, Instr::I64Load16U, Instr.I64Load16U::class)
opMapEntry("i64.load32_s", MemOp::FlagsOffsetArg, Instr::I64Load32S, Instr.I64Load32S::class)
opMapEntry("i64.load32_u", MemOp::FlagsOffsetArg, Instr::I64Load32U, Instr.I64Load32U::class)
opMapEntry("i32.store", MemOp::FlagsOffsetArg, Instr::I32Store, Instr.I32Store::class)
opMapEntry("i64.store", MemOp::FlagsOffsetArg, Instr::I64Store, Instr.I64Store::class)
opMapEntry("f32.store", MemOp::FlagsOffsetArg, Instr::F32Store, Instr.F32Store::class)
opMapEntry("f64.store", MemOp::FlagsOffsetArg, Instr::F64Store, Instr.F64Store::class)
opMapEntry("i32.store8", MemOp::FlagsOffsetArg, Instr::I32Store8, Instr.I32Store8::class)
opMapEntry("i32.store16", MemOp::FlagsOffsetArg, Instr::I32Store16, Instr.I32Store16::class)
opMapEntry("i64.store8", MemOp::FlagsOffsetArg, Instr::I64Store8, Instr.I64Store8::class)
opMapEntry("i64.store16", MemOp::FlagsOffsetArg, Instr::I64Store16, Instr.I64Store16::class)
opMapEntry("i64.store32", MemOp::FlagsOffsetArg, Instr::I64Store32, Instr.I64Store32::class)
opMapEntry("current_memory", MemOp::ReservedArg, Instr::CurrentMemory, Instr.CurrentMemory::class)
opMapEntry("grow_memory", MemOp::ReservedArg, Instr::GrowMemory, Instr.GrowMemory::class)
opMapEntry("i32.const", ConstOp::IntArg, Instr::I32Const, Instr.I32Const::class)
opMapEntry("i64.const", ConstOp::LongArg, Instr::I64Const, Instr.I64Const::class)
opMapEntry("f32.const", ConstOp::FloatArg, Instr::F32Const, Instr.F32Const::class)
opMapEntry("f64.const", ConstOp::DoubleArg, Instr::F64Const, Instr.F64Const::class)
opMapEntry("i32.eqz", CompareOp::NoArg, Instr.I32Eqz, Instr.I32Eqz::class)
opMapEntry("i32.eq", CompareOp::NoArg, Instr.I32Eq, Instr.I32Eq::class)
opMapEntry("i32.ne", CompareOp::NoArg, Instr.I32Ne, Instr.I32Ne::class)
opMapEntry("i32.lt_s", CompareOp::NoArg, Instr.I32LtS, Instr.I32LtS::class)
opMapEntry("i32.lt_u", CompareOp::NoArg, Instr.I32LtU, Instr.I32LtU::class)
opMapEntry("i32.gt_s", CompareOp::NoArg, Instr.I32GtS, Instr.I32GtS::class)
opMapEntry("i32.gt_u", CompareOp::NoArg, Instr.I32GtU, Instr.I32GtU::class)
opMapEntry("i32.le_s", CompareOp::NoArg, Instr.I32LeS, Instr.I32LeS::class)
opMapEntry("i32.le_u", CompareOp::NoArg, Instr.I32LeU, Instr.I32LeU::class)
opMapEntry("i32.ge_s", CompareOp::NoArg, Instr.I32GeS, Instr.I32GeS::class)
opMapEntry("i32.ge_u", CompareOp::NoArg, Instr.I32GeU, Instr.I32GeU::class)
opMapEntry("i64.eqz", CompareOp::NoArg, Instr.I64Eqz, Instr.I64Eqz::class)
opMapEntry("i64.eq", CompareOp::NoArg, Instr.I64Eq, Instr.I64Eq::class)
opMapEntry("i64.ne", CompareOp::NoArg, Instr.I64Ne, Instr.I64Ne::class)
opMapEntry("i64.lt_s", CompareOp::NoArg, Instr.I64LtS, Instr.I64LtS::class)
opMapEntry("i64.lt_u", CompareOp::NoArg, Instr.I64LtU, Instr.I64LtU::class)
opMapEntry("i64.gt_s", CompareOp::NoArg, Instr.I64GtS, Instr.I64GtS::class)
opMapEntry("i64.gt_u", CompareOp::NoArg, Instr.I64GtU, Instr.I64GtU::class)
opMapEntry("i64.le_s", CompareOp::NoArg, Instr.I64LeS, Instr.I64LeS::class)
opMapEntry("i64.le_u", CompareOp::NoArg, Instr.I64LeU, Instr.I64LeU::class)
opMapEntry("i64.ge_s", CompareOp::NoArg, Instr.I64GeS, Instr.I64GeS::class)
opMapEntry("i64.ge_u", CompareOp::NoArg, Instr.I64GeU, Instr.I64GeU::class)
opMapEntry("f32.eq", CompareOp::NoArg, Instr.F32Eq, Instr.F32Eq::class)
opMapEntry("f32.ne", CompareOp::NoArg, Instr.F32Ne, Instr.F32Ne::class)
opMapEntry("f32.lt", CompareOp::NoArg, Instr.F32Lt, Instr.F32Lt::class)
opMapEntry("f32.gt", CompareOp::NoArg, Instr.F32Gt, Instr.F32Gt::class)
opMapEntry("f32.le", CompareOp::NoArg, Instr.F32Le, Instr.F32Le::class)
opMapEntry("f32.ge", CompareOp::NoArg, Instr.F32Ge, Instr.F32Ge::class)
opMapEntry("f64.eq", CompareOp::NoArg, Instr.F64Eq, Instr.F64Eq::class)
opMapEntry("f64.ne", CompareOp::NoArg, Instr.F64Ne, Instr.F64Ne::class)
opMapEntry("f64.lt", CompareOp::NoArg, Instr.F64Lt, Instr.F64Lt::class)
opMapEntry("f64.gt", CompareOp::NoArg, Instr.F64Gt, Instr.F64Gt::class)
opMapEntry("f64.le", CompareOp::NoArg, Instr.F64Le, Instr.F64Le::class)
opMapEntry("f64.ge", CompareOp::NoArg, Instr.F64Ge, Instr.F64Ge::class)
opMapEntry("i32.clz", NumOp::NoArg, Instr.I32Clz, Instr.I32Clz::class)
opMapEntry("i32.ctz", NumOp::NoArg, Instr.I32Ctz, Instr.I32Ctz::class)
opMapEntry("i32.popcnt", NumOp::NoArg, Instr.I32Popcnt, Instr.I32Popcnt::class)
opMapEntry("i32.add", NumOp::NoArg, Instr.I32Add, Instr.I32Add::class)
opMapEntry("i32.sub", NumOp::NoArg, Instr.I32Sub, Instr.I32Sub::class)
opMapEntry("i32.mul", NumOp::NoArg, Instr.I32Mul, Instr.I32Mul::class)
opMapEntry("i32.div_s", NumOp::NoArg, Instr.I32DivS, Instr.I32DivS::class)
opMapEntry("i32.div_u", NumOp::NoArg, Instr.I32DivU, Instr.I32DivU::class)
opMapEntry("i32.rem_s", NumOp::NoArg, Instr.I32RemS, Instr.I32RemS::class)
opMapEntry("i32.rem_u", NumOp::NoArg, Instr.I32RemU, Instr.I32RemU::class)
opMapEntry("i32.and", NumOp::NoArg, Instr.I32And, Instr.I32And::class)
opMapEntry("i32.or", NumOp::NoArg, Instr.I32Or, Instr.I32Or::class)
opMapEntry("i32.xor", NumOp::NoArg, Instr.I32Xor, Instr.I32Xor::class)
opMapEntry("i32.shl", NumOp::NoArg, Instr.I32Shl, Instr.I32Shl::class)
opMapEntry("i32.shr_s", NumOp::NoArg, Instr.I32ShrS, Instr.I32ShrS::class)
opMapEntry("i32.shr_u", NumOp::NoArg, Instr.I32ShrU, Instr.I32ShrU::class)
opMapEntry("i32.rotl", NumOp::NoArg, Instr.I32Rotl, Instr.I32Rotl::class)
opMapEntry("i32.rotr", NumOp::NoArg, Instr.I32Rotr, Instr.I32Rotr::class)
opMapEntry("i64.clz", NumOp::NoArg, Instr.I64Clz, Instr.I64Clz::class)
opMapEntry("i64.ctz", NumOp::NoArg, Instr.I64Ctz, Instr.I64Ctz::class)
opMapEntry("i64.popcnt", NumOp::NoArg, Instr.I64Popcnt, Instr.I64Popcnt::class)
opMapEntry("i64.add", NumOp::NoArg, Instr.I64Add, Instr.I64Add::class)
opMapEntry("i64.sub", NumOp::NoArg, Instr.I64Sub, Instr.I64Sub::class)
opMapEntry("i64.mul", NumOp::NoArg, Instr.I64Mul, Instr.I64Mul::class)
opMapEntry("i64.div_s", NumOp::NoArg, Instr.I64DivS, Instr.I64DivS::class)
opMapEntry("i64.div_u", NumOp::NoArg, Instr.I64DivU, Instr.I64DivU::class)
opMapEntry("i64.rem_s", NumOp::NoArg, Instr.I64RemS, Instr.I64RemS::class)
opMapEntry("i64.rem_u", NumOp::NoArg, Instr.I64RemU, Instr.I64RemU::class)
opMapEntry("i64.and", NumOp::NoArg, Instr.I64And, Instr.I64And::class)
opMapEntry("i64.or", NumOp::NoArg, Instr.I64Or, Instr.I64Or::class)
opMapEntry("i64.xor", NumOp::NoArg, Instr.I64Xor, Instr.I64Xor::class)
opMapEntry("i64.shl", NumOp::NoArg, Instr.I64Shl, Instr.I64Shl::class)
opMapEntry("i64.shr_s", NumOp::NoArg, Instr.I64ShrS, Instr.I64ShrS::class)
opMapEntry("i64.shr_u", NumOp::NoArg, Instr.I64ShrU, Instr.I64ShrU::class)
opMapEntry("i64.rotl", NumOp::NoArg, Instr.I64Rotl, Instr.I64Rotl::class)
opMapEntry("i64.rotr", NumOp::NoArg, Instr.I64Rotr, Instr.I64Rotr::class)
opMapEntry("f32.abs", NumOp::NoArg, Instr.F32Abs, Instr.F32Abs::class)
opMapEntry("f32.neg", NumOp::NoArg, Instr.F32Neg, Instr.F32Neg::class)
opMapEntry("f32.ceil", NumOp::NoArg, Instr.F32Ceil, Instr.F32Ceil::class)
opMapEntry("f32.floor", NumOp::NoArg, Instr.F32Floor, Instr.F32Floor::class)
opMapEntry("f32.trunc", NumOp::NoArg, Instr.F32Trunc, Instr.F32Trunc::class)
opMapEntry("f32.nearest", NumOp::NoArg, Instr.F32Nearest, Instr.F32Nearest::class)
opMapEntry("f32.sqrt", NumOp::NoArg, Instr.F32Sqrt, Instr.F32Sqrt::class)
opMapEntry("f32.add", NumOp::NoArg, Instr.F32Add, Instr.F32Add::class)
opMapEntry("f32.sub", NumOp::NoArg, Instr.F32Sub, Instr.F32Sub::class)
opMapEntry("f32.mul", NumOp::NoArg, Instr.F32Mul, Instr.F32Mul::class)
opMapEntry("f32.div", NumOp::NoArg, Instr.F32Div, Instr.F32Div::class)
opMapEntry("f32.min", NumOp::NoArg, Instr.F32Min, Instr.F32Min::class)
opMapEntry("f32.max", NumOp::NoArg, Instr.F32Max, Instr.F32Max::class)
opMapEntry("f32.copysign", NumOp::NoArg, Instr.F32CopySign, Instr.F32CopySign::class)
opMapEntry("f64.abs", NumOp::NoArg, Instr.F64Abs, Instr.F64Abs::class)
opMapEntry("f64.neg", NumOp::NoArg, Instr.F64Neg, Instr.F64Neg::class)
opMapEntry("f64.ceil", NumOp::NoArg, Instr.F64Ceil, Instr.F64Ceil::class)
opMapEntry("f64.floor", NumOp::NoArg, Instr.F64Floor, Instr.F64Floor::class)
opMapEntry("f64.trunc", NumOp::NoArg, Instr.F64Trunc, Instr.F64Trunc::class)
opMapEntry("f64.nearest", NumOp::NoArg, Instr.F64Nearest, Instr.F64Nearest::class)
opMapEntry("f64.sqrt", NumOp::NoArg, Instr.F64Sqrt, Instr.F64Sqrt::class)
opMapEntry("f64.add", NumOp::NoArg, Instr.F64Add, Instr.F64Add::class)
opMapEntry("f64.sub", NumOp::NoArg, Instr.F64Sub, Instr.F64Sub::class)
opMapEntry("f64.mul", NumOp::NoArg, Instr.F64Mul, Instr.F64Mul::class)
opMapEntry("f64.div", NumOp::NoArg, Instr.F64Div, Instr.F64Div::class)
opMapEntry("f64.min", NumOp::NoArg, Instr.F64Min, Instr.F64Min::class)
opMapEntry("f64.max", NumOp::NoArg, Instr.F64Max, Instr.F64Max::class)
opMapEntry("f64.copysign", NumOp::NoArg, Instr.F64CopySign, Instr.F64CopySign::class)
opMapEntry("i32.wrap/i64", ConvertOp::NoArg, Instr.I32WrapI64, Instr.I32WrapI64::class)
opMapEntry("i32.trunc_s/f32", ConvertOp::NoArg, Instr.I32TruncSF32, Instr.I32TruncSF32::class)
opMapEntry("i32.trunc_u/f32", ConvertOp::NoArg, Instr.I32TruncUF32, Instr.I32TruncUF32::class)
opMapEntry("i32.trunc_s/f64", ConvertOp::NoArg, Instr.I32TruncSF64, Instr.I32TruncSF64::class)
opMapEntry("i32.trunc_u/f64", ConvertOp::NoArg, Instr.I32TruncUF64, Instr.I32TruncUF64::class)
opMapEntry("i64.extend_s/i32", ConvertOp::NoArg, Instr.I64ExtendSI32, Instr.I64ExtendSI32::class)
opMapEntry("i64.extend_u/i32", ConvertOp::NoArg, Instr.I64ExtendUI32, Instr.I64ExtendUI32::class)
opMapEntry("i64.trunc_s/f32", ConvertOp::NoArg, Instr.I64TruncSF32, Instr.I64TruncSF32::class)
opMapEntry("i64.trunc_u/f32", ConvertOp::NoArg, Instr.I64TruncUF32, Instr.I64TruncUF32::class)
opMapEntry("i64.trunc_s/f64", ConvertOp::NoArg, Instr.I64TruncSF64, Instr.I64TruncSF64::class)
opMapEntry("i64.trunc_u/f64", ConvertOp::NoArg, Instr.I64TruncUF64, Instr.I64TruncUF64::class)
opMapEntry("f32.convert_s/i32", ConvertOp::NoArg, Instr.F32ConvertSI32, Instr.F32ConvertSI32::class)
opMapEntry("f32.convert_u/i32", ConvertOp::NoArg, Instr.F32ConvertUI32, Instr.F32ConvertUI32::class)
opMapEntry("f32.convert_s/i64", ConvertOp::NoArg, Instr.F32ConvertSI64, Instr.F32ConvertSI64::class)
opMapEntry("f32.convert_u/i64", ConvertOp::NoArg, Instr.F32ConvertUI64, Instr.F32ConvertUI64::class)
opMapEntry("f32.demote/f64", ConvertOp::NoArg, Instr.F32DemoteF64, Instr.F32DemoteF64::class)
opMapEntry("f64.convert_s/i32", ConvertOp::NoArg, Instr.F64ConvertSI32, Instr.F64ConvertSI32::class)
opMapEntry("f64.convert_u/i32", ConvertOp::NoArg, Instr.F64ConvertUI32, Instr.F64ConvertUI32::class)
opMapEntry("f64.convert_s/i64", ConvertOp::NoArg, Instr.F64ConvertSI64, Instr.F64ConvertSI64::class)
opMapEntry("f64.convert_u/i64", ConvertOp::NoArg, Instr.F64ConvertUI64, Instr.F64ConvertUI64::class)
opMapEntry("f64.promote/f32", ConvertOp::NoArg, Instr.F64PromoteF32, Instr.F64PromoteF32::class)
opMapEntry("i32.reinterpret/f32", ReinterpretOp::NoArg, Instr.I32ReinterpretF32, Instr.I32ReinterpretF32::class)
opMapEntry("i64.reinterpret/f64", ReinterpretOp::NoArg, Instr.I64ReinterpretF64, Instr.I64ReinterpretF64::class)
opMapEntry("f32.reinterpret/i32", ReinterpretOp::NoArg, Instr.F32ReinterpretI32, Instr.F32ReinterpretI32::class)
opMapEntry("f64.reinterpret/i64", ReinterpretOp::NoArg, Instr.F64ReinterpretI64, Instr.F64ReinterpretI64::class)
}
}
}
}

View File

@ -1,72 +1,6 @@
package asmble.ast package asmble.ast
import asmble.util.Either
sealed class SExpr { sealed class SExpr {
data class Multi(val vals: List<SExpr> = emptyList()) : SExpr() data class Multi(val vals: List<SExpr> = emptyList()) : SExpr()
data class Symbol(val contents: String = "", val quoted: Boolean = false) : SExpr() data class Symbol(val contents: String = "", val quoted: Boolean = false) : SExpr()
companion object {
data class ParseError(val charOffset: Int, val msg: String)
fun parse(str: CharSequence): Either<Multi, ParseError> {
val state = ParseState(str)
var ret = emptyList<SExpr>()
while (!state.isEof) {
ret += state.nextSExpr()
if (state.err != null) return Either.Right(ParseError(state.offset, state.err!!))
}
if (ret.size == 1 && ret[0] is Multi) return Either.Left(ret[0] as Multi)
else return Either.Left(Multi(ret))
}
private class ParseState(val str: CharSequence, var offset: Int = 0, var err: String? = null) {
fun nextSExpr(): SExpr {
skipWhitespace()
if (isEof) return Multi()
// What type of expr do we have here?
when (str[offset]) {
'(' -> {
offset++
var ret = Multi()
while (err == null && !isEof && str[offset] != ')') {
ret = ret.copy(ret.vals + nextSExpr())
}
if (err == null) {
if (str[offset] == ')') offset++ else err = "EOF when expected ')'"
}
return ret
}
'"' -> {
offset++
// Anything can be escaped (for now)
var retStr = ""
while (!isEof && str[offset] != '"') {
if (str[offset] == '\\') {
++offset
if (isEof) {
err = "EOF when expected char to unescape"
break
}
}
retStr += str[offset]
offset++
}
if (err == null && str[offset] != '"') err = "EOF when expected '\"'"
return Symbol(retStr, true)
}
else -> {
// Go until next quote or whitespace or parens
val origOffset = offset
while (!isEof && str[offset] != '(' && str[offset] != ')' &&
str[offset] != '"' && !str[offset].isWhitespace()) offset++
return Symbol(str.substring(origOffset, offset))
}
}
}
fun skipWhitespace() { while (!isEof && str[offset].isWhitespace()) offset++ }
val isEof: Boolean inline get() = offset >= str.length
}
}
} }

View File

@ -0,0 +1,394 @@
package asmble.io
import asmble.ast.Node
import asmble.ast.Node.InstrOp
import asmble.ast.SExpr
import asmble.util.takeUntilNull
class SExprToAst {
fun toBlockSigMaybe(exp: SExpr.Multi, offset: Int): List<Node.Type.Value> {
val types = exp.vals.drop(offset).asSequence().map {
if (it is SExpr.Symbol) toTypeMaybe(it) else null
}.takeUntilNull().toList()
// We can only handle one type for now
require(types.size <= 1)
return types
}
fun toElemType(exp: SExpr.Multi, offset: Int): Node.ElemType {
exp.vals[offset].requireSymbol("anyfunc")
return Node.ElemType.ANYFUNC
}
fun toExport(exp: SExpr.Multi): Node.Export {
exp.vals.first().requireSymbol("export")
val field = (exp.vals[1] as SExpr.Symbol).contents
val kind = exp.vals[2] as SExpr.Multi
val kindIndex = toVar(kind.vals[1] as SExpr.Symbol)
val extKind = when((kind.vals[0] as SExpr.Symbol).contents) {
"func" -> Node.ExternalKind.FUNCTION
"global" -> Node.ExternalKind.GLOBAL
"table" -> Node.ExternalKind.TABLE
"memory" -> Node.ExternalKind.MEMORY
else -> throw Exception("Unrecognized kind: ${kind.vals[0]}")
}
return Node.Export(field, extKind, kindIndex)
}
fun toExprMaybe(exp: SExpr.Multi): List<Node.Instr> {
// <op> or <op> <expr>+
val maybeOpAndOffset = toOpMaybe(exp, 0)
if (maybeOpAndOffset != null) {
// Everything left in the multi should be a a multi expression
return exp.vals.drop(maybeOpAndOffset.second).flatMap {
toExprMaybe(it as SExpr.Multi)
} + maybeOpAndOffset.first
}
// Other blocks take up the rest (ignore names)
val blockName = (exp.vals.first() as SExpr.Symbol).contents
var opOffset = 1
if (exp.maybeName(opOffset) != null) opOffset++
val sigs = toBlockSigMaybe(exp, opOffset)
opOffset += sigs.size
when(blockName) {
"block" ->
return listOf(Node.Instr.Block(sigs.firstOrNull())) +
toInstrs(exp, opOffset).first + Node.Instr.End
"loop" ->
return listOf(Node.Instr.Loop(sigs.firstOrNull())) +
toInstrs(exp, opOffset).first + Node.Instr.End
"if" -> {
if (opOffset >= exp.vals.size) return emptyList()
var ret = emptyList<Node.Instr>()
// Try expressions
var exprMulti = exp.vals[opOffset] as SExpr.Multi
var exprs = toExprMaybe(exprMulti)
// Conditional?
if (exprs.isNotEmpty()) {
// First expression means it's the conditional, so push on stack
ret += exprs
opOffset++
exprMulti = exp.vals[opOffset] as SExpr.Multi
}
ret += Node.Instr.If(sigs.firstOrNull())
// Is it a "then"?
if (exprMulti.vals[0] is SExpr.Symbol && (exprMulti.vals[0] as SExpr.Symbol).contents == "then") {
ret += toInstrs(exprMulti, 1).first
} else ret += toExprMaybe(exprMulti)
// Now check for "else"
opOffset++
if (opOffset < exp.vals.size) {
ret += Node.Instr.Else
exprMulti = exp.vals[opOffset] as SExpr.Multi
if (exprMulti.vals[0] is SExpr.Symbol && (exprMulti.vals[0] as SExpr.Symbol).contents == "else") {
ret += toInstrs(exprMulti, 1).first
} else ret += toExprMaybe(exprMulti)
}
return ret + Node.Instr.End
}
else -> return emptyList()
}
}
fun toFunc(exp: SExpr.Multi): Pair<String?, Node.Func> {
exp.vals.first().requireSymbol("func")
// TODO: export/import
var currentIndex = 1
val name = exp.maybeName(currentIndex)
if (name != null) currentIndex++
val sig = toFuncSig(exp.vals[currentIndex] as SExpr.Multi)
currentIndex++
val locals = exp.repeated("local", currentIndex, { toLocals(it).second }).flatten()
currentIndex += locals.size
val (instrs, _) = toInstrs(exp, currentIndex)
return Pair(name, Node.Func(sig, locals, instrs))
}
fun toFuncSig(exp: SExpr.Multi): Node.Type.Func {
// TODO: type <var> form?
val params = exp.repeated("param", 0, { toParams(it).second }).flatten()
val results = exp.vals.drop(params.size).map { toResult(it as SExpr.Multi) }
require(results.size <= 1)
return Node.Type.Func(params, results.firstOrNull())
}
fun toGlobalSig(exp: SExpr): Node.Type.Global = when(exp) {
is SExpr.Symbol -> Node.Type.Global(toType(exp), false)
is SExpr.Multi -> {
exp.vals.first().requireSymbol("mut")
Node.Type.Global(toType(exp.vals[1] as SExpr.Symbol), true)
}
}
fun toImport(exp: SExpr.Multi): Triple<String, String, Node.Type> {
exp.vals.first().requireSymbol("import")
val module = (exp.vals[1] as SExpr.Symbol).contents
val field = (exp.vals[2] as SExpr.Symbol).contents
val kind = exp.vals[3] as SExpr.Multi
val kindName = (kind.vals[0] as SExpr.Symbol).contents
val kindSubOffset = if (kind.maybeName(1) == null) 1 else 2
return Triple(module, field, when(kindName) {
"func" -> toFuncSig(kind.vals[kindSubOffset] as SExpr.Multi)
"global" -> toGlobalSig(kind.vals[kindSubOffset])
"table" -> toTableSig(kind, kindSubOffset)
"memory" -> toMemorySig(kind, kindSubOffset)
else -> throw Exception("Unrecognized type: $kindName")
})
}
fun toInstrs(exp: SExpr.Multi, offset: Int): Pair<List<Node.Instr>, Int> {
var runningOffset = 0
var ret = emptyList<Node.Instr>()
while (offset + runningOffset < exp.vals.size) {
val maybeInstrAndOffset = toInstrMaybe(exp, offset + runningOffset)
if (maybeInstrAndOffset.first.isEmpty()) break
ret += maybeInstrAndOffset.first
runningOffset += maybeInstrAndOffset.second
}
return Pair(ret, runningOffset)
}
fun toInstrMaybe(exp: SExpr.Multi, offset: Int): Pair<List<Node.Instr>, Int> {
// <expr>
if (exp.vals[offset] is SExpr.Multi) {
val exprs = toExprMaybe(exp)
return Pair(exprs, if (exprs.isEmpty()) 0 else 1)
}
// <op>
val maybeOpAndOffset = toOpMaybe(exp, offset)
if (maybeOpAndOffset != null) {
return Pair(listOf(maybeOpAndOffset.first), maybeOpAndOffset.second)
}
// Other blocks (ignore names)
if (exp.vals[offset] !is SExpr.Symbol) return Pair(emptyList(), 0)
val blockName = (exp.vals[offset] as SExpr.Symbol).contents
var opOffset = 1
if (exp.maybeName(offset + opOffset) != null) opOffset++
val sigs = toBlockSigMaybe(exp, offset + opOffset)
opOffset += sigs.size
var ret = emptyList<Node.Instr>()
when(blockName) {
"block" -> {
ret += Node.Instr.Block(sigs.firstOrNull())
toInstrs(exp, offset + opOffset).also {
ret += it.first
opOffset += it.second
}
}
"loop" -> {
ret += Node.Instr.Loop(sigs.firstOrNull())
toInstrs(exp, offset + opOffset).also {
ret += it.first
opOffset += it.second
}
}
"if" -> {
ret += Node.Instr.Loop(sigs.firstOrNull())
toInstrs(exp, offset + opOffset).also {
ret += it.first
opOffset += it.second
}
// Else?
if (offset + opOffset < exp.vals.size) {
if ((exp.vals[offset + opOffset] as? SExpr.Symbol)?.contents == "else") {
ret += Node.Instr.Else
opOffset++
if (exp.maybeName(offset + opOffset) != null) opOffset++
toInstrs(exp, offset + opOffset).also {
ret += it.first
opOffset += it.second
}
}
}
}
else -> return Pair(emptyList(), 0)
}
require((exp.vals[offset + opOffset] as? SExpr.Symbol)?.contents == "end")
opOffset++
if (exp.maybeName(offset + opOffset) != null) opOffset++
return Pair(ret, opOffset)
}
fun toLocals(exp: SExpr.Multi): Pair<String?, List<Node.Type.Value>> {
exp.vals.first().requireSymbol("local")
val name = exp.maybeName(1)
if (name != null) return Pair(name, listOf(toType(exp.vals[2] as SExpr.Symbol)))
return Pair(null, exp.vals.drop(1).map { toType(it as SExpr.Symbol) })
}
fun toMemorySig(exp: SExpr.Multi, offset: Int): Node.Type.Memory {
return Node.Type.Memory(toResizeableLimits(exp, offset))
}
fun toModule(exp: SExpr.Multi): Pair<String?, Node.Module> {
exp.vals.first().requireSymbol("module")
var currIndex = 1
val name = exp.maybeName(currIndex)
if (name != null) currIndex++
var types = exp.repeated("type", currIndex, { toTypeDef(it).second })
currIndex += types.size
val funcs = exp.repeated("func", currIndex, { toFunc(it).second })
currIndex += funcs.size
val imports = exp.repeated("import", currIndex, this::toImport).map {
when(it.third) {
is Node.Type.Func -> {
// TODO: why does smart cast not work here?
types += it.third as Node.Type.Func
Node.Import(it.first, it.second, Node.Import.Kind.Func(types.lastIndex))
}
is Node.Type.Table ->
Node.Import(it.first, it.second, Node.Import.Kind.Table(it.third as Node.Type.Table))
is Node.Type.Memory ->
Node.Import(it.first, it.second, Node.Import.Kind.Memory(it.third as Node.Type.Memory))
is Node.Type.Global ->
Node.Import(it.first, it.second, Node.Import.Kind.Global(it.third as Node.Type.Global))
else ->
throw Exception("Unrecognized import kind: ${it.third}")
}
}
currIndex += imports.size
val exports = exp.repeated("export", currIndex, this::toExport)
currIndex += exports.size
}
fun toOpMaybe(exp: SExpr.Multi, offset: Int): Pair<Node.Instr, Int>? {
if (offset >= exp.vals.size) return null
val head = exp.vals[offset] as SExpr.Symbol
fun oneVar() = toVar(exp.vals[offset + 1] as SExpr.Symbol)
val op = InstrOp.strToOpMap[head.contents]
return when(op) {
null -> null
is InstrOp.ControlFlowOp.NoArg -> Pair(op.create, 1)
is InstrOp.ControlFlowOp.TypeArg -> return null // Type not handled here
is InstrOp.ControlFlowOp.DepthArg -> Pair(op.create(oneVar()), 2)
is InstrOp.ControlFlowOp.TableArg -> {
val vars = exp.vals.drop(offset + 1).asSequence().map(this::toVarMaybe).takeUntilNull().toList()
Pair(op.create(vars.drop(1), vars.first()), offset + vars.size)
}
is InstrOp.CallOp.IndexArg -> Pair(op.create(oneVar()), 2)
is InstrOp.CallOp.IndexReservedArg -> Pair(op.create(oneVar(), false), 2)
is InstrOp.ParamOp.NoArg -> Pair(op.create, 1)
is InstrOp.VarOp.IndexArg -> Pair(op.create(oneVar()), 2)
is InstrOp.MemOp.FlagsOffsetArg -> {
var count = 1
var instrOffset = 0
var instrAlign = 0
if (exp.vals.size > offset + count) {
val maybeSym = exp.vals[offset + count] as? SExpr.Symbol
if (maybeSym != null && maybeSym.contents.startsWith("offset=")) {
instrOffset = maybeSym.contents.substring(7).toInt()
count++
}
}
if (exp.vals.size > offset + count) {
val maybeSym = exp.vals[offset + count] as? SExpr.Symbol
if (maybeSym != null && maybeSym.contents.startsWith("align=")) {
instrAlign = maybeSym.contents.substring(6).toInt()
count++
}
}
Pair(op.create(instrAlign, instrOffset), count)
}
is InstrOp.MemOp.ReservedArg -> Pair(op.create(false), 1)
is InstrOp.ConstOp.IntArg -> Pair(op.create((exp.vals[offset + 1] as SExpr.Symbol).contents.toInt()), 2)
is InstrOp.ConstOp.LongArg -> Pair(op.create((exp.vals[offset + 1] as SExpr.Symbol).contents.toLong()), 2)
is InstrOp.ConstOp.FloatArg -> Pair(op.create((exp.vals[offset + 1] as SExpr.Symbol).contents.toFloat()), 2)
is InstrOp.ConstOp.DoubleArg -> Pair(op.create((exp.vals[offset + 1] as SExpr.Symbol).contents.toDouble()), 2)
is InstrOp.CompareOp.NoArg -> Pair(op.create, 1)
is InstrOp.NumOp.NoArg -> Pair(op.create, 1)
is InstrOp.ConvertOp.NoArg -> Pair(op.create, 1)
is InstrOp.ReinterpretOp.NoArg -> Pair(op.create, 1)
}
}
fun toParams(exp: SExpr.Multi): Pair<String?, List<Node.Type.Value>> {
exp.vals.first().requireSymbol("param")
val name = exp.maybeName(1)
if (name != null) return Pair(name, listOf(toType(exp.vals[2] as SExpr.Symbol)))
return Pair(null, exp.vals.drop(1).map { toType(it as SExpr.Symbol) })
}
fun toResizeableLimits(exp: SExpr.Multi, offset: Int): Node.ResizableLimits {
var max: Int? = null
if (offset + 1 < exp.vals.size && exp.vals[offset + 1] is SExpr.Symbol) {
max = (exp.vals[offset + 1] as SExpr.Symbol).contents.toIntOrNull()
}
return Node.ResizableLimits((exp.vals[offset] as SExpr.Symbol).contents.toInt(), max)
}
fun toResult(exp: SExpr.Multi): Node.Type.Value {
exp.vals.first().requireSymbol("result")
return toType(exp.vals[1] as SExpr.Symbol)
}
fun toTableSig(exp: SExpr.Multi, offset: Int): Node.Type.Table {
val limits = toResizeableLimits(exp, offset)
return Node.Type.Table(toElemType(exp, offset + if (limits.maximum == null) 0 else 1), limits)
}
fun toType(exp: SExpr.Symbol): Node.Type.Value {
return toTypeMaybe(exp) ?: throw Exception("Unknown value type: ${exp.contents}")
}
fun toTypeMaybe(exp: SExpr.Symbol): Node.Type.Value? = when(exp.contents) {
"i32" -> Node.Type.Value.I32
"i64" -> Node.Type.Value.I64
"f32" -> Node.Type.Value.F32
"f64" -> Node.Type.Value.F64
else -> null
}
fun toTypeDef(exp: SExpr.Multi): Pair<String?, Node.Type.Func> {
exp.vals.first().requireSymbol("typedef")
var currIndex = 1
val name = exp.maybeName(currIndex)
if (name != null) currIndex++
val funcSigExp = exp.vals[currIndex] as SExpr.Multi
funcSigExp.vals[0].requireSymbol("func")
return Pair(name, toFuncSig(funcSigExp.vals[1] as SExpr.Multi))
}
fun toVar(exp: SExpr.Symbol): Int {
// TODO: what about name?
return exp.contents.toInt()
}
fun toVarMaybe(exp: SExpr): Int? {
// TODO: what about name?
if (exp !is SExpr.Symbol) return null
return exp.contents.toIntOrNull()
}
private fun SExpr.requireSymbol(contents: String, quotedCheck: Boolean? = null) {
if (this is SExpr.Symbol && this.contents == contents &&
(quotedCheck == null || this.quoted == quotedCheck)) {
return
}
throw Exception("Expected symbol of $contents, got $this")
}
private fun SExpr.Multi.maybeName(index: Int): String? {
if (this.vals.size > index && this.vals[index] is SExpr.Symbol) {
val sym = this.vals[index] as SExpr.Symbol
if (!sym.quoted && sym.contents[0] == '$') return sym.contents
}
return null
}
private fun <T> SExpr.Multi.repeated(name: String, startOffset: Int, fn: (SExpr.Multi) -> T): List<T> {
var offset = startOffset
var ret = emptyList<T>()
while (this.vals.size > offset) {
if (this.vals[offset] !is SExpr.Multi) break
val expMulti = this.vals[offset] as SExpr.Multi
if (expMulti.vals[0] !is SExpr.Symbol) break
val expName = expMulti.vals[0] as SExpr.Symbol
if (expName.quoted || expName.contents != name) break
ret += fn(expMulti)
offset++
}
return ret
}
}

View File

@ -0,0 +1,87 @@
package asmble.io
import asmble.ast.SExpr
import asmble.util.Either
open class StrToSExpr {
data class ParseError(val charOffset: Int, val msg: String)
fun parse(str: CharSequence): Either<SExpr.Multi, ParseError> {
val state = ParseState(str)
var ret = emptyList<SExpr>()
while (!state.isEof) {
ret += state.nextSExpr()
if (state.err != null) return Either.Right(ParseError(state.offset, state.err!!))
}
if (ret.size == 1 && ret[0] is SExpr.Multi) return Either.Left(ret[0] as SExpr.Multi)
else return Either.Left(SExpr.Multi(ret))
}
private class ParseState(val str: CharSequence, var offset: Int = 0, var err: String? = null) {
fun nextSExpr(): SExpr {
skipWhitespace()
if (isEof) return SExpr.Multi()
// What type of expr do we have here?
when (str[offset]) {
'(' -> {
offset++
var ret = SExpr.Multi()
while (err == null && !isEof && str[offset] != ')') {
ret = ret.copy(ret.vals + nextSExpr())
}
if (err == null) {
if (str[offset] == ')') offset++ else err = "EOF when expected ')'"
}
return ret
}
'"' -> {
offset++
// Check escapes
var retStr = ""
while (err == null && !isEof && str[offset] != '"') {
if (str[offset] == '\\') {
offset++
if (isEof) err = "EOF when expected char to unescape" else when (str[offset]) {
'n' -> retStr += '\n'
't' -> retStr += '\t'
'\\' -> retStr += '\\'
'\'' -> retStr += '\''
'"' -> retStr += '"'
else -> {
// Try to parse hex if there is enough, otherwise just gripe
if (offset + 4 >= str.length) err = "Unknown escape" else {
try {
retStr += str.substring(offset, offset + 4).toByte(16).toChar()
offset += 4
} catch (e: NumberFormatException) {
err = "Unknown escape"
}
}
}
}
} else {
retStr += str[offset]
offset++
}
}
if (err == null && str[offset] != '"') err = "EOF when expected '\"'"
else if (err == null) offset++
return SExpr.Symbol(retStr, true)
}
else -> {
// Go until next quote or whitespace or parens
val origOffset = offset
while (!isEof && str[offset] != '(' && str[offset] != ')' &&
str[offset] != '"' && !str[offset].isWhitespace()) offset++
return SExpr.Symbol(str.substring(origOffset, offset))
}
}
}
fun skipWhitespace() { while (!isEof && str[offset].isWhitespace()) offset++ }
val isEof: Boolean inline get() = offset >= str.length
}
companion object : StrToSExpr()
}

View File

@ -0,0 +1,7 @@
package asmble.util
fun <T : Any> Collection<T?>.takeUntilNull(): Collection<T> {
return this.asSequence().takeUntilNull().toList()
}

View File

@ -0,0 +1,7 @@
package asmble.util
fun <T : Any> Sequence<T?>.takeUntilNull(): Sequence<T> {
// Unchecked cast, oh well, it's erased
return this.takeWhile({ it != null }) as Sequence<T>
}

@ -0,0 +1 @@
Subproject commit 0b9b9878619aec23980fe713b3a7abf5a991e152