Skip to content

Commit 2eb36f0

Browse files
ahuoguobutterunderflowGuannan Wei
authored
Basic (module bin ...) support (#62)
* basic .bin.wast working --------- Co-authored-by: butterunderflow <azhong.934@gmail.com> Co-authored-by: Guannan Wei <wei220@purdue.edu>
1 parent c286f04 commit 2eb36f0

File tree

8 files changed

+152
-14
lines changed

8 files changed

+152
-14
lines changed

.github/workflows/scala.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ jobs:
6363
make
6464
sudo make install
6565
sudo ldconfig
66+
- name: Install wasmfx-tools
67+
run: |
68+
cd third-party/wasmfx-tools
69+
cargo build --release
6670
- name: Generate models
6771
run: sbt 'runMain gensym.GenerateExternal'
6872
- name: Run tests

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@
2424
path = third-party/lms-clean
2525
url = https://github.com/TiarkRompf/lms-clean
2626
ignore = dirty
27+
[submodule "third-party/wasmfx-tools"]
28+
path = third-party/wasmfx-tools
29+
url = git@github.com:wasmfx/wasmfx-tools.git
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(module binary
2+
"\00\61\73\6d\01\00\00\00\01\85\80\80\80\00\01\60"
3+
"\00\01\7f\03\82\80\80\80\00\01\00\07\87\80\80\80"
4+
"\00\01\03\6f\6e\65\00\00\0a\8a\80\80\80\00\01\84"
5+
"\80\80\80\00\00\41\01\0b"
6+
)
7+
(assert_return (invoke "one") (i32.const 0x1))

src/main/scala/wasm/MiniWasm.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ object Primtives {
6363
(lhs, rhs) match {
6464
case (I32V(v1), I32V(v2)) => I32V(v1 + v2)
6565
case (I64V(v1), I64V(v2)) => I64V(v1 + v2)
66+
case (F32V(v1), F32V(v2)) => F32V(v1 + v2)
67+
case (F64V(v1), F64V(v2)) => F64V(v1 + v2)
6668
case _ => throw new Exception("Invalid types")
6769
}
6870
case Mul(_) =>

src/main/scala/wasm/Parser.scala

Lines changed: 113 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package gensym.wasm.parser
33
import gensym.wasm.ast._
44
import gensym.wasm.source._
55

6+
import scala.util.Try
67
import scala.util.parsing.combinator._
78
import scala.util.parsing.input.Positional
89
import scala.util.matching.Regex
@@ -14,6 +15,9 @@ import scala.collection.JavaConverters._
1415
import collection.mutable.{HashMap, ListBuffer}
1516
import gensym.wasm._
1617

18+
import java.io.OutputStream
19+
20+
1721
import scala.collection.mutable
1822

1923
class GSWasmVisitor extends WatParserBaseVisitor[WIR] {
@@ -188,23 +192,78 @@ class GSWasmVisitor extends WatParserBaseVisitor[WIR] {
188192
???
189193
}
190194

195+
// TODO: This doesn't seems quite correct
196+
def parseHexFloat(text: String): Float = {
197+
if (text.startsWith("0x") || text.startsWith("-0x") || text.startsWith("+0x")) {
198+
// Remove optional sign and "0x" prefix
199+
val cleanText = text.replaceFirst("^[+-]?0x", "")
200+
// why removing the seemling irrelevant following two lines will effect
201+
// the value being parsed?
202+
val value: Float = BigDecimal(text).floatValue
203+
print(f"cleanText = $cleanText, value = $value\n")
204+
205+
val Array(mantissa, exponent) = cleanText.split("p", 2)
206+
207+
// Convert mantissa and exponent
208+
val mantissaValue = java.lang.Float.intBitsToFloat(java.lang.Integer.parseUnsignedInt(mantissa.replace(".", ""), 16))
209+
val exponentValue = Math.pow(2, exponent.toInt).toFloat
210+
// print(s"mantissaValue = $mantissaValue, exponentValue = $exponentValue\n")
211+
mantissaValue * exponentValue
212+
} else {
213+
text.toFloat // Fall back to regular decimal parsing
214+
}
215+
}
216+
217+
191218
def visitLiteralWithType(ctx: LiteralContext, ty: NumType): Num = {
192219
if (ctx.NAT != null) {
193220
ty.kind match {
194-
case I32Type => I32V(ctx.NAT.getText.toInt)
195-
case I64Type => I64V(ctx.NAT.getText.toLong)
221+
case I32Type => {
222+
if (ctx.NAT.getText.startsWith("0x")) {
223+
I32V(Integer.parseInt(ctx.NAT.getText.substring(2), 16))
224+
} else {
225+
I32V(ctx.NAT.getText.toInt)
226+
}
227+
}
228+
case I64Type => {
229+
if (ctx.NAT.getText.startsWith("0x")) {
230+
I64V(java.lang.Long.parseLong(ctx.NAT.getText.substring(2), 16))
231+
} else {
232+
I64V(ctx.NAT.getText.toLong)
233+
}
234+
}
196235
}
197236
} else if (ctx.INT != null) {
198237
ty.kind match {
199-
case I32Type => I32V(ctx.INT.getText.toInt)
200-
case I64Type => I64V(ctx.INT.getText.toLong)
238+
case I32Type => {
239+
if (ctx.INT.getText.startsWith("0x")) {
240+
I32V(Integer.parseInt(ctx.INT.getText.substring(2), 16))
241+
} else {
242+
I32V(ctx.INT.getText.toInt)
243+
}
244+
}
245+
case I64Type => {
246+
if (ctx.INT.getText.startsWith("0x")) {
247+
I64V(java.lang.Long.parseLong(ctx.INT.getText.substring(2), 16))
248+
} else {
249+
I64V(ctx.INT.getText.toLong)
250+
}
251+
}
201252
}
253+
// TODO: parsing support for hex representation for f32/f64 not quite there yet
202254
} else if (ctx.FLOAT != null) {
203255
ty.kind match {
204-
case F32Type => F32V(ctx.FLOAT.getText.toFloat)
205-
case F64Type => F64V(ctx.FLOAT.getText.toDouble)
256+
case F32Type =>
257+
val parsedValue = Try(parseHexFloat(ctx.FLOAT.getText).toFloat).getOrElse(ctx.FLOAT.getText.toFloat)
258+
F32V(parsedValue)
259+
260+
case F64Type =>
261+
// TODO: not processed at all
262+
val parsedValue = ctx.FLOAT.getText.toDouble
263+
F64V(parsedValue)
206264
}
207-
} else error
265+
}
266+
else error
208267
}
209268

210269
override def visitPlainInstr(ctx: PlainInstrContext): Instr = {
@@ -635,10 +694,45 @@ class GSWasmVisitor extends WatParserBaseVisitor[WIR] {
635694
else error
636695
}
637696

638-
override def visitScriptModule(ctx: ScriptModuleContext): Module = {
697+
override def visitScriptModule(ctx: ScriptModuleContext): Module = {
639698
if (ctx.module_ != null) {
640699
visitModule_(ctx.module_).asInstanceOf[Module]
641-
} else {
700+
}
701+
else if (ctx.BIN != null) {
702+
703+
val bin = ctx.STRING_
704+
val hexString = bin.asScala.toList.map(_.getText.substring(1).dropRight(1)).mkString
705+
706+
val byteArray: Array[Byte] = hexStringToByteArray(hexString)
707+
708+
// just for fact checking
709+
// val filePath = "temp.bin"
710+
// Files.write(Paths.get(filePath), byteArray)
711+
712+
// use `wasmfx-tools` to convert the binary file to a text file
713+
val processBuilder = new ProcessBuilder("./third-party/wasmfx-tools/target/release/wasm-tools", "print")
714+
715+
val process = processBuilder.start()
716+
val outputStream: OutputStream = process.getOutputStream
717+
try {
718+
outputStream.write(byteArray)
719+
outputStream.flush()
720+
} finally {
721+
outputStream.close() // Close the stream to signal end of input
722+
}
723+
724+
val output = scala.io.Source.fromInputStream(process.getInputStream).mkString
725+
val errorOutput = scala.io.Source.fromInputStream(process.getErrorStream).mkString
726+
val exitCode = process.waitFor()
727+
728+
// println(s"Exit code: $exitCode")
729+
// println(s"Output:\n$output")
730+
// println(s"Error Output:\n$errorOutput")
731+
732+
val module = Parser.parse(output)
733+
module
734+
}
735+
else {
642736
throw new RuntimeException("Unsupported")
643737
}
644738
}
@@ -688,14 +782,20 @@ class GSWasmVisitor extends WatParserBaseVisitor[WIR] {
688782
Script(cmds.toList)
689783
}
690784

691-
override def visitTag(ctx: TagContext): WIR = {
692-
val name = getVar(ctx.bindVar)
693-
val ty = visitFuncType(ctx.funcType)
694-
Tag(name, ty)
785+
// Function to convert a hex string representation to an Array[Byte]
786+
def hexStringToByteArray(hex: String): Array[Byte] = {
787+
// Split the input string by '\' and filter out empty strings
788+
val byteStrings = hex.split("\\\\").filter(_.nonEmpty)
789+
790+
byteStrings.map { byteStr =>
791+
// Parse the hex value to a byte
792+
Integer.parseInt(byteStr, 16).toByte
793+
}
695794
}
696795

697796
}
698797

798+
699799
object Parser {
700800
private def makeWatVisitor(input: String) = {
701801
val charStream = new ANTLRInputStream(input)

src/test/scala/genwasym/TestScriptRun.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import gensym.wasm.miniwasmscript.ScriptRunner
55

66
import org.scalatest.FunSuite
77

8-
98
class TestScriptRun extends FunSuite {
109
def testFile(filename: String): Unit = {
1110
val script = Parser.parseScriptFile(filename).get
@@ -16,4 +15,9 @@ class TestScriptRun extends FunSuite {
1615
test("simple script") {
1716
testFile("./benchmarks/wasm/script/script_basic.wast")
1817
}
18+
19+
test("simple bin script") {
20+
testFile("./benchmarks/wasm/script/script_basic.bin.wast")
21+
}
22+
1923
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package gensym.wasm
2+
3+
import gensym.wasm.parser.Parser
4+
import org.scalatest.FunSuite
5+
6+
class TestSyntax extends FunSuite {
7+
def testFile(filename: String) = {
8+
val script = Parser.parseScriptFile(filename)
9+
println(s"script = $script")
10+
assert(script != None, "this syntax is not defined in antlr grammar")
11+
}
12+
13+
test("basic script") {
14+
testFile("./benchmarks/wasm/script/script_basic.wabt")
15+
}
16+
}
17+

third-party/wasmfx-tools

Submodule wasmfx-tools added at c9218cf

0 commit comments

Comments
 (0)