diff --git a/jsgen/gen.wyv b/jsgen/gen.wyv new file mode 100644 index 000000000..09e59db97 --- /dev/null +++ b/jsgen/gen.wyv @@ -0,0 +1,7 @@ +// generates javascript from protobuf-based bytecode +module gen + +// generates javascript in a string from protobuf bytecode (in a buffer) +def generate(bytecodeBuffer:Dyn):String + // placeholder for now + "console.log(\"executing now!\");" diff --git a/native/javascript/stdlib/support/bytecode.js b/native/javascript/stdlib/support/bytecode.js index aafa4a196..8c13faa08 100644 --- a/native/javascript/stdlib/support/bytecode.js +++ b/native/javascript/stdlib/support/bytecode.js @@ -94,6 +94,18 @@ exports.saveBytecode = function(path, bytecodeObject) { fs.writeFileSync(path, bbuffer); } +exports.encodeAndDecode = function(bytecodeObject) { + setRoot(); + const Bytecode = root.lookupType('Bytecode'); + var errMsg = Bytecode.verify(bytecodeObject); + if (errMsg) + throw Error(errMsg); + var bmsg = Bytecode.create(bytecodeObject); + var bbuffer = Bytecode.encode(bmsg).finish(); + var newBytecode = Bytecode.decode(bbuffer); + return wrap(newBytecode); +} + exports.encodeExpr = function(exprObject) { setRoot(); const Expression= root.lookupType('Expression'); diff --git a/native/javascript/stdlib/support/exec.js b/native/javascript/stdlib/support/exec.js new file mode 100644 index 000000000..d8cbc3761 --- /dev/null +++ b/native/javascript/stdlib/support/exec.js @@ -0,0 +1,14 @@ +exports.execute = function(environment, code) { + console.log("executing " + code); + // environment is a linked list; traverse collecting value elements + // each element is a pair + const args = []; + // put the second part into an array + // build a let-binding based on the first part and FUNCTION_ARGS[currentIndex] + // prepend the let bindings to the code + const extendedCode = code; + const encapsulatedCode = new Function('FUNCTION_ARGS', extendedCode); + // TODO: implement all of the above, and then: + // run the extended code + encapsulatedCode(args) +} diff --git a/native/javascript/stdlib/support/stdio.js b/native/javascript/stdlib/support/stdio.js index 02a13111f..5d362fca8 100644 --- a/native/javascript/stdlib/support/stdio.js +++ b/native/javascript/stdlib/support/stdio.js @@ -1,3 +1,5 @@ +const readline = require("readline"); + exports.print = function(s) { process.stdout.write("" + s); } @@ -15,3 +17,16 @@ exports.println = function(s) { exports.flush = function(s) { // NOP, node doesn't have flush } + +exports.onLine = function(cb) { + // invoke cb as a Wyvern function whenever input arrives from standard in + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: false + }); + + rl.on('line', function (line) { + cb.apply(line) + }); +} \ No newline at end of file diff --git a/selfhost/binding.wyv b/selfhost/binding.wyv index 6013e2722..3466e338c 100644 --- a/selfhost/binding.wyv +++ b/selfhost/binding.wyv @@ -1,9 +1,9 @@ -import raw -import error -import lexUtil -import types -import typesUtil -import typecheck +import selfhost.raw +import selfhost.error +import selfhost.lexUtil +import selfhost.types +import selfhost.typesUtil +import selfhost.typecheck import wyvern.collections.llist type List = llist.LinkedList diff --git a/selfhost/bound.wyv b/selfhost/bound.wyv index 8618c4ce4..f08733d99 100644 --- a/selfhost/bound.wyv +++ b/selfhost/bound.wyv @@ -1,6 +1,6 @@ module bound -import types +import selfhost.types import wyvern.collections.llist type List = llist.LinkedList type Counter = types.Counter diff --git a/selfhost/bytecode.wyv b/selfhost/bytecode.wyv index 29f3a73dd..a652d30d2 100644 --- a/selfhost/bytecode.wyv +++ b/selfhost/bytecode.wyv @@ -167,3 +167,5 @@ def encodeExpr(e:Expr):Unit def saveBytecode(path:String, b:Bytecode):Unit bytecode.saveBytecode(path, b) +def encodeAndDecode(b:Bytecode):Unit + bytecode.encodeAndDecode(b) diff --git a/selfhost/error.wyv b/selfhost/error.wyv index 342d5fd98..4e3a38e28 100644 --- a/selfhost/error.wyv +++ b/selfhost/error.wyv @@ -1,7 +1,7 @@ module errors import wyvern.exception -import Location +import selfhost.Location import wyvern.String type ErrorReportingException extends exception.Exception diff --git a/selfhost/exec.wyv b/selfhost/exec.wyv new file mode 100644 index 000000000..a21324f54 --- /dev/null +++ b/selfhost/exec.wyv @@ -0,0 +1,17 @@ +// executes Javascript +module def exec(javascript:JavaScript) + +import javascript:stdlib.support.exec +import wyvern.collections.llist + +type ArgPair + val name:String + val value:Dyn + +def ArgPair(name:String, value:Dyn):ArgPair = new + val name:String = name + val value:Dyn = value + +// executes a string of Javascript in a given environment, returning the result +def execute(environment: llist.LinkedList[ArgPair], code:String):Dyn + exec.execute(environment, code) diff --git a/selfhost/parsing.wyv b/selfhost/parsing.wyv index a0636d86b..4a7a76b61 100644 --- a/selfhost/parsing.wyv +++ b/selfhost/parsing.wyv @@ -4,7 +4,7 @@ import javaMetaDebug import wyvern.ast import metadata wyvern.collections.list import javascript:stdlib.support.parsing -import lexing +import selfhost.lexing import wyvern.util.matching.regexInternal import java:wyvern.stdlib.support.StringHelper.utils diff --git a/selfhost/toBytecode.wyv b/selfhost/toBytecode.wyv index 9de0e20e3..b8cac25cc 100644 --- a/selfhost/toBytecode.wyv +++ b/selfhost/toBytecode.wyv @@ -1,9 +1,9 @@ module def toBytecode(javascript:JavaScript, js:Dyn) -import types -import bytecode +import selfhost.types +import selfhost.bytecode import wyvern.collections.llist -import error +import selfhost.error type List = llist.LinkedList val b = bytecode(javascript, js) @@ -38,15 +38,26 @@ def toBytecodeExpr(e:types.Exp):b.Expr = match e: i:types.Integer => b.IntLit(i.str) u:types.UnitVal => b.IntLit("0") -def writeExpToFile(e:types.Statement, filename:String):Unit +def toFileBytecode(e:types.Statement):b.Bytecode js.log("Converting expression to bytecode...\n") val expBytecode = toBytecode(e) //js.log(d.sequenceExpression.statements.get(0).declaration) js.log("Creating top level bytecode...\n") - val fileBytecode = b.singletonBytecode(b.toplevel(expBytecode)) + b.singletonBytecode(b.toplevel(expBytecode)) + +// takes the same thing as writeExpToFile +// encodes to protobuf bytes and decodes +// returns the same thing as b.loadBytecode +def encodeAndDecode(e:types.Statement): Dyn + val fileBytecode = toFileBytecode(e) + b.encodeAndDecode(fileBytecode) + +def writeExpToFile(e:types.Statement, filename:String):Unit + val fileBytecode = toFileBytecode(e) js.log("Actually saving...\n") b.saveBytecode(filename, fileBytecode) + /* // test code val expr = b.StrLit("Hello") diff --git a/selfhost/typecheck.wyv b/selfhost/typecheck.wyv index 704633bec..182cf9517 100644 --- a/selfhost/typecheck.wyv +++ b/selfhost/typecheck.wyv @@ -1,8 +1,9 @@ module typecheck -import types -import typesUtil -import error +import selfhost.types +import selfhost.typesUtil +import selfhost.error +import debug import wyvern.option import wyvern.collections.llist import wyvern.pair diff --git a/selfhost/types.wyv b/selfhost/types.wyv index 0e3f94253..b609fab01 100644 --- a/selfhost/types.wyv +++ b/selfhost/types.wyv @@ -1,9 +1,9 @@ module types -import raw +import selfhost.raw import wyvern.option import wyvern.collections.llist -import error +import selfhost.error type Option = option.Option type List = llist.LinkedList diff --git a/selfhost/typesUtil.wyv b/selfhost/typesUtil.wyv index 566055fbd..06e46493c 100644 --- a/selfhost/typesUtil.wyv +++ b/selfhost/typesUtil.wyv @@ -3,8 +3,8 @@ module typesUtil import wyvern.option import wyvern.collections.llist import wyvern.pair -import error -import types +import selfhost.error +import selfhost.types type Option = option.Option type List = llist.LinkedList type Binding = types.Binding diff --git a/selfhost/wyvernLexer.wyv b/selfhost/wyvernLexer.wyv index 385c5b5c0..684aaf89f 100644 --- a/selfhost/wyvernLexer.wyv +++ b/selfhost/wyvernLexer.wyv @@ -1,9 +1,9 @@ module def wyvernLexer(js:Dyn, regexUtils:Dyn) -import metadata lexing +import metadata selfhost.lexing import wyvern.collections.llist import wyvern.runtime -import lexUtil +import selfhost.lexUtil type List = llist.LinkedList @@ -26,6 +26,7 @@ def Token(t:String, value:String, line:Int, col:Int):Token = new // note: this lexer will never actually see a logline (\n, \r, or \r\n). But we generate such tokens later on at line breaks +// This is the only state in the lexer. It gets reset on every call to the higher-level lex functions, so this state is mostly innoccuous; it's OK to use those higher-level functions in any single-threaded way, including creating multiple higher-level Lexers and interleaving their use. But note that the current design requires each instance of wyvernLexer to be used in a single-threaded manner. If you want multiple threads create multiple wyvernLexers. val lowLevelLexer : lexing.Lexer = ~ WS: /[ \t]+/, logline: {match: /\\r\\n|\\r|\\n/, lineBreaks: true}, @@ -311,7 +312,6 @@ resource type IncrementalLexer /* // TODO: revise to work with lineCont def incrementalLexer():IncrementalLexer - // TODO: make all the state above local new var tokens : List[Dyn] = llist.Nil[Dyn]() def addLine(input:String):option.Option[lexing.Lexer] diff --git a/selfhost/wyvernParser.wyv b/selfhost/wyvernParser.wyv index cf527b317..ddfb8fbf8 100644 --- a/selfhost/wyvernParser.wyv +++ b/selfhost/wyvernParser.wyv @@ -1,8 +1,8 @@ module wyvernParser -import lexing -import metadata parsing -import raw +import selfhost.lexing +import metadata selfhost.parsing +import selfhost.raw import wyvern.collections.llist type List = llist.LinkedList diff --git a/stdlib/formatstring.wyv b/stdlib/formatstring.wyv index 3f094fa97..301c1338e 100644 --- a/stdlib/formatstring.wyv +++ b/stdlib/formatstring.wyv @@ -71,6 +71,7 @@ def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : if (ast.types.equals(ty, ast.types.boolean(), ctx)) option.Some[AST](ast.call(exp, "ifTrue", {ast.parseExpression("() => \"true\"", ctx), ast.parseExpression("() => \"false\"", ctx)})) else + ast.reportError("Wyvern expressions inside format string must have type Int, String, Float, or Boolean") option.None[AST]() diff --git a/stdlib/platform/javascript/asyncIn.wyv b/stdlib/platform/javascript/asyncIn.wyv new file mode 100644 index 000000000..8e7fc4bd1 --- /dev/null +++ b/stdlib/platform/javascript/asyncIn.wyv @@ -0,0 +1,6 @@ +module def asyncIn(javascript : JavaScript) + +import javascript:stdlib.support.stdio + +def onLine(callback : String -> Unit):Unit + stdio.onLine(callback) diff --git a/stdlib/wyvern/ast.wyv b/stdlib/wyvern/ast.wyv index 799e7452e..ca9fe2dc1 100644 --- a/stdlib/wyvern/ast.wyv +++ b/stdlib/wyvern/ast.wyv @@ -91,6 +91,8 @@ def formalArg(name : String, argType : Type) : FormalArg = ast.formalArg(name, a // Utility Functions def stripLeadingWhitespace(input : String, mustStrip : Boolean) : String = ast.stripLeadingWhitespace(input, mustStrip) +def reportError(msg : String) : Unit = ast.reportError(msg) + def genIdent() : String = ast.genIdent() def getMetadataTypeReceiver(ctx : system.Context) : AST diff --git a/stdlib/wyvern/internal/ast.wyv b/stdlib/wyvern/internal/ast.wyv index 20215ebd4..e6adeed3b 100644 --- a/stdlib/wyvern/internal/ast.wyv +++ b/stdlib/wyvern/internal/ast.wyv @@ -159,6 +159,8 @@ val types = new def stripLeadingWhitespace(input : String, mustStrip : Boolean) : String = utils.stripLeadingWhitespace(input, mustStrip) +def reportError(msg : String) : Unit = utils.reportError(msg) + def genIdent() : String = utils.genIdent() def let(ident : String, bindingType : Type, bindingValue : AST, inExpr : AST) : AST = new diff --git a/tools/src/wyvern/stdlib/support/AST.java b/tools/src/wyvern/stdlib/support/AST.java index 67d8a0b2f..628322a1b 100644 --- a/tools/src/wyvern/stdlib/support/AST.java +++ b/tools/src/wyvern/stdlib/support/AST.java @@ -428,6 +428,10 @@ public String stripLeadingWhitespace(String input, boolean mustStrip) throws IOE return result.toString(); } + public void reportError(String msg) { + ToolError.reportError(ErrorMessage.TSL_ERROR, new FileLocation("TSL", 1, 1), msg); + } + public String genIdent() { return "ASTIDENT$" + Integer.toString(++identNum); } diff --git a/tools/src/wyvern/target/corewyvernIL/effects/EffectSet.java b/tools/src/wyvern/target/corewyvernIL/effects/EffectSet.java index f07ebc918..869d9dde1 100644 --- a/tools/src/wyvern/target/corewyvernIL/effects/EffectSet.java +++ b/tools/src/wyvern/target/corewyvernIL/effects/EffectSet.java @@ -205,7 +205,7 @@ private EffectSet decomposeEffectSet(EffectSet effects, Effect e, DecomposeType @Override public String toString() { - return effectSet == null ? "" : effectSet.toString().replace("[", "{").replace("]", "}"); + return effectSet == null ? "" : effectSet.toString().replace("[", "{").replace("]", "}").replaceAll("MOD\\$", ""); }