From 48a0d847a1643882271a7b7a8aee99104da8d25f Mon Sep 17 00:00:00 2001 From: jennyafish Date: Thu, 13 Jun 2019 13:10:54 -0400 Subject: [PATCH 01/27] started on formatstring TSL design --- examples/io-lib-redesign/stdioTest.wyv | 4 +- examples/tsls/formatstring.wyv | 87 ++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 examples/tsls/formatstring.wyv diff --git a/examples/io-lib-redesign/stdioTest.wyv b/examples/io-lib-redesign/stdioTest.wyv index 30247e9e4..9f9d031bd 100644 --- a/examples/io-lib-redesign/stdioTest.wyv +++ b/examples/io-lib-redesign/stdioTest.wyv @@ -3,8 +3,8 @@ require java import stdout import stdin import wyvern.option -type Option = option.Option -val filesys = fileSystem(java) +//type Option = option.Option +//val filesys = fileSystem(java) val out = stdout(java) val in = stdin(java) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv new file mode 100644 index 000000000..92deb35fb --- /dev/null +++ b/examples/tsls/formatstring.wyv @@ -0,0 +1,87 @@ +module formatstring + +import wyvern.internal.ast +import wyvern.option +import wyvern.util.matching.regex + +type AST = ast.AST + +type FormatString + metadata new + def parseTSL(input : String, ctx : system.Context) : option.Option[AST] + + + + + + + + + + + + + + +/** OUTLINE + +pull off individual string characters until you reach a % (account for esc later) +then look for either a number, ., or { (regexp matching) +continue parsing and appending strings recursively + + +*/ + + +def parseString(input:String):option.Option[AST] + //start by pulling the string before the next % character + val prefixRegex : regex.Regex = regex("[^%]+") + val prefixString : option.Option[String] = prefixRegex.findPrefixMatchOf(input).map[String]( + (m:regex.Match) => m.after()) + val appendString : String = prefixString.getOrElse(() => input) + //append appendString to the result of parseExpr() + + +def parseExpr(input:String):option.Option[AST] + val exp = + + ast.cast(exp, ast.types.string()) + +//%\{.*\} +// shouldn't need to account for {} in primitive Wyvern expressions?? but maybe list methods... +// ^maybe account for this later +// still need to account for escape characters + + + + + + + + + + + + + + + + +/* +EXAMPLES +printf(~) + some sort of dollar amt $%{5} -> some sort of dollar amt $5 + floating %.2{6.0} -> floating 6.00 + + (val str = "bar") + foo%{str} + foo%{"bar"} + +not really much use for quotes +use %__ identifiers to detail how to print the data +formatting specs based on C + + +*/ + + From 783a754808fa428a3c0837fc0f2f129d74221931 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Thu, 13 Jun 2019 15:37:58 -0400 Subject: [PATCH 02/27] made formatstring TSL client; import list metadata error --- examples/tsls/formatstring.wyv | 21 ++++++++++++++++++--- examples/tsls/formatstringClient.wyv | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 examples/tsls/formatstringClient.wyv diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 92deb35fb..9c502d42b 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -3,12 +3,26 @@ module formatstring import wyvern.internal.ast import wyvern.option import wyvern.util.matching.regex +import metadata wyvern.collections.list type AST = ast.AST -type FormatString +val prefixRegex : regex.Regex = regex("[^%]+") +def parseExpr(input : String, ctx: system.Context) : option.Option[AST] + option.Some[AST](ast.string(input)) //temporary placeholder + +type FormatString = String metadata new def parseTSL(input : String, ctx : system.Context) : option.Option[AST] + val regxMatch : option.Option[regex.Match] = prefixRegex.findPrefixMatchOf(input) + val prefixString : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.matched()) + val rest : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.after()) + + (prefixString.isDefined).ifTrue( + () => ast.call(ast.string(prefixString.get()), "+", {parseExpr(rest.getOrElse(() => ""), ctx)}), + () => parseExpr(rest.getOrElse(() => ""), ctx) + ) + @@ -32,20 +46,21 @@ continue parsing and appending strings recursively */ - +/* def parseString(input:String):option.Option[AST] //start by pulling the string before the next % character val prefixRegex : regex.Regex = regex("[^%]+") val prefixString : option.Option[String] = prefixRegex.findPrefixMatchOf(input).map[String]( (m:regex.Match) => m.after()) val appendString : String = prefixString.getOrElse(() => input) - //append appendString to the result of parseExpr() + //append appendString to the result of parseExpr() def parseExpr(input:String):option.Option[AST] val exp = ast.cast(exp, ast.types.string()) +*/ //%\{.*\} // shouldn't need to account for {} in primitive Wyvern expressions?? but maybe list methods... diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv new file mode 100644 index 000000000..1d4815732 --- /dev/null +++ b/examples/tsls/formatstringClient.wyv @@ -0,0 +1,14 @@ +require stdout + +import metadata tsls.formatstring + +def printf(str:formatstring.FormatString):Unit + stdout.print(str) + stdout.println() + +//just a plain string +printf(~) + Hello World! + +//need to deal with leading whitespace for parsing; maybe use ` to indicate +//beginning of format string or something? \ No newline at end of file From 8fe0c810c2006e382cccf6565664843963b46f38 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Mon, 17 Jun 2019 21:06:42 -0400 Subject: [PATCH 03/27] working on formatstring TSL, about to merge fixes for stdioTest imports --- examples/tsls/formatstring.wyv | 6 +++--- stdlib/platform/java/stdin.wyv | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 9c502d42b..f075a2f85 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -1,6 +1,6 @@ module formatstring -import wyvern.internal.ast +import wyvern.ast import wyvern.option import wyvern.util.matching.regex import metadata wyvern.collections.list @@ -19,11 +19,11 @@ type FormatString = String val rest : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.after()) (prefixString.isDefined).ifTrue( - () => ast.call(ast.string(prefixString.get()), "+", {parseExpr(rest.getOrElse(() => ""), ctx)}), + () => ast.call(ast.string(prefixString.get()), "+", {(parseExpr(rest.getOrElse(() => ""), ctx)}).getOrElse(() => ast.string(""))), () => parseExpr(rest.getOrElse(() => ""), ctx) ) - + //option.Some[AST](ast.string(input)) diff --git a/stdlib/platform/java/stdin.wyv b/stdlib/platform/java/stdin.wyv index 41f8a70b4..78ba91e4a 100644 --- a/stdlib/platform/java/stdin.wyv +++ b/stdlib/platform/java/stdin.wyv @@ -13,10 +13,10 @@ effect Read = {fileEffects.Read} val sc = stdio.initStdin() -def read() : {this.Read} Int +def read() : {Read} Int stdio.read(sc) -def readLine() : {this.Read} Option[String] +def readLine() : {Read} Option[String] val line = stdio.readLine(sc) if (stdio.isNull(line)) option.None[String]() From da5381ed67656a837dcc9415c8cde05ef42224fc Mon Sep 17 00:00:00 2001 From: jennyafish Date: Mon, 17 Jun 2019 22:21:19 -0400 Subject: [PATCH 04/27] formatstring TSL working for unformatted strings --- examples/tsls/formatstring.wyv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index f075a2f85..32f3281e6 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -19,10 +19,11 @@ type FormatString = String val rest : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.after()) (prefixString.isDefined).ifTrue( - () => ast.call(ast.string(prefixString.get()), "+", {(parseExpr(rest.getOrElse(() => ""), ctx)}).getOrElse(() => ast.string(""))), + () => option.Some[AST](ast.call(ast.string(prefixString.get()), "+", {(parseExpr(rest.getOrElse(() => ""), ctx)).getOrElse(() => ast.string(""))})), () => parseExpr(rest.getOrElse(() => ""), ctx) ) + //option.Some[AST](ast.call(ast.string("hello "), "+", {ast.string("world!")})) //option.Some[AST](ast.string(input)) From f1ee6bab125236259c991c7e02c69cdd7da77c5d Mon Sep 17 00:00:00 2001 From: jennyafish Date: Wed, 19 Jun 2019 13:49:53 -0400 Subject: [PATCH 05/27] printformatting working on Wyvern string literals --- examples/tsls/formatstring.wyv | 34 +++++++++++++++++++++++++++- examples/tsls/formatstringClient.wyv | 5 +++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 32f3281e6..7c0ac8865 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -8,8 +8,40 @@ import metadata wyvern.collections.list type AST = ast.AST val prefixRegex : regex.Regex = regex("[^%]+") + +// recursively determines position of the rbrace ending the expression, -1 if not found +def getExprPos(input : String, lbraceCt : Int, pos : Int) : Int + (input.length() == pos).ifTrue( + () => -1, + () => (input.charAt(pos) == #"}").ifTrue( + () => (lbraceCt == 0).ifTrue( + () => pos, + () => getExprPos(input, lbraceCt - 1, pos+1) + ), + () => getExprPos(input, lbraceCt, pos+1) + ) + ) + def parseExpr(input : String, ctx: system.Context) : option.Option[AST] - option.Some[AST](ast.string(input)) //temporary placeholder + //if %{...}, we want to get the contents of the braces + val normal : option.Option[String] = regex("%\\{").findPrefixMatchOf(input).map[String]((m:regex.Match) => m.after()) + //val end = getExprPos(normal.get(), 0, 0) + (normal.isDefined).ifTrue( + () => (getExprPos(normal.get(), 0, 0) == -1).ifTrue( + () => option.Some[AST](ast.string("parsing error")),//continue parsing or throw error + () => option.Some[AST](ast.parseExpression(normal.get().substring(0,getExprPos(normal.get(), 0, 0)), ctx)) + ), + //if nothing found then default to whole string? + () => option.Some[AST](ast.string("the other one")) + ) + + //regex("[^\\}]*(\\{.*\\})*[^\\}]*") + //if %/d./d, ensure formatted float + + // %/d + + // if none of the above, just append % and continue parsing + //option.Some[AST](ast.string(input)) type FormatString = String metadata new diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 1d4815732..5b5f57f49 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -11,4 +11,7 @@ printf(~) Hello World! //need to deal with leading whitespace for parsing; maybe use ` to indicate -//beginning of format string or something? \ No newline at end of file +//beginning of format string or something? + +printf(~) + Hello %{"string!"} \ No newline at end of file From 429c09ef12e5545cd352d0a48f36b8f351922ee0 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Thu, 20 Jun 2019 13:47:07 -0400 Subject: [PATCH 06/27] added some stdin tests --- examples/io-lib-redesign/stdioTest.wyv | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/examples/io-lib-redesign/stdioTest.wyv b/examples/io-lib-redesign/stdioTest.wyv index 9f9d031bd..e5e3ea0ad 100644 --- a/examples/io-lib-redesign/stdioTest.wyv +++ b/examples/io-lib-redesign/stdioTest.wyv @@ -3,9 +3,22 @@ require java import stdout import stdin import wyvern.option -//type Option = option.Option +type Option = option.Option //val filesys = fileSystem(java) val out = stdout(java) val in = stdin(java) -in.read() +val prompt : Int = in.read() +out.print("the int ") +out.printInt(prompt) +out.println() + +val promptStr : Option[String] = in.readLine() +out.print("hello " + promptStr.getOrElse(() => "none")) +out.println() + +in.close() + +//in.read() //this throws an error after closing stdin + +//just reads in int from stdin without waiting for \n \ No newline at end of file From 93ec83275c95b46c0876f0d346dd454bfaaa5c05 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Thu, 20 Jun 2019 14:07:41 -0400 Subject: [PATCH 07/27] printformatting works for multiple string args --- examples/tsls/formatstring.wyv | 68 ++++++++++------------------ examples/tsls/formatstringClient.wyv | 2 +- 2 files changed, 24 insertions(+), 46 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 7c0ac8865..73c725a9f 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -7,6 +7,14 @@ import metadata wyvern.collections.list type AST = ast.AST +type ParsedExpr + def expr():option.Option[AST] + def rest():String + +def makeParsedExpr(e:option.Option[AST],r:String):ParsedExpr = new + def expr():option.Option[AST] = e + def rest():String = r + val prefixRegex : regex.Regex = regex("[^%]+") // recursively determines position of the rbrace ending the expression, -1 if not found @@ -22,20 +30,21 @@ def getExprPos(input : String, lbraceCt : Int, pos : Int) : Int ) ) -def parseExpr(input : String, ctx: system.Context) : option.Option[AST] +def parseExpr(input : String, ctx: system.Context) : ParsedExpr //if %{...}, we want to get the contents of the braces val normal : option.Option[String] = regex("%\\{").findPrefixMatchOf(input).map[String]((m:regex.Match) => m.after()) //val end = getExprPos(normal.get(), 0, 0) (normal.isDefined).ifTrue( () => (getExprPos(normal.get(), 0, 0) == -1).ifTrue( - () => option.Some[AST](ast.string("parsing error")),//continue parsing or throw error - () => option.Some[AST](ast.parseExpression(normal.get().substring(0,getExprPos(normal.get(), 0, 0)), ctx)) + () => makeParsedExpr(option.None[AST](), normal.get()), //error? + //option.Some[AST](ast.string("parsing error")),//continue parsing or throw error + () => makeParsedExpr(option.Some[AST](ast.parseExpression(normal.get().substring(0,getExprPos(normal.get(), 0, 0)), ctx)), normal.get().substring(getExprPos(normal.get(), 0, 0)+1, normal.get().length())) + //option.Some[AST](ast.parseExpression(normal.get().substring(0,getExprPos(normal.get(), 0, 0)), ctx)) ), //if nothing found then default to whole string? - () => option.Some[AST](ast.string("the other one")) + () => makeParsedExpr(option.None[AST](), input) //WARNING probably loops //option.Some[AST](ast.string("nonstandard formatstring")) ) - //regex("[^\\}]*(\\{.*\\})*[^\\}]*") //if %/d./d, ensure formatted float // %/d @@ -49,12 +58,15 @@ type FormatString = String val regxMatch : option.Option[regex.Match] = prefixRegex.findPrefixMatchOf(input) val prefixString : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.matched()) val rest : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.after()) - - (prefixString.isDefined).ifTrue( - () => option.Some[AST](ast.call(ast.string(prefixString.get()), "+", {(parseExpr(rest.getOrElse(() => ""), ctx)).getOrElse(() => ast.string(""))})), - () => parseExpr(rest.getOrElse(() => ""), ctx) + (input == "").ifTrue( + () => option.Some[AST](ast.string("")), + () => (prefixString.isDefined).ifTrue( + () => option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {(parseExpr(rest.getOrElse(() => ""), ctx)).expr().getOrElse(() => ast.string(""))}), "+", {this.parseTSL(parseExpr(rest.getOrElse(() => ""), ctx).rest(), ctx).getOrElse(() => ast.string(""))})), + () => option.Some[AST](ast.call(parseExpr(rest.getOrElse(() => input), ctx).expr().getOrElse(() => ast.string("")), "+", {this.parseTSL(parseExpr(rest.getOrElse(() => input), ctx).rest(), ctx).getOrElse(() => ast.string(""))})) + //g = parseExpr(rest.getOrElse(() => ""), ctx) + //option.Some[AST](ast.call(g.expr().getOrElse(() => ast.string("")), "+", {this.parseTSL(g.rest(), ctx).getOrElse(() => ast.string(""))})) + ) ) - //option.Some[AST](ast.call(ast.string("hello "), "+", {ast.string("world!")})) //option.Some[AST](ast.string(input)) @@ -98,38 +110,4 @@ def parseExpr(input:String):option.Option[AST] //%\{.*\} // shouldn't need to account for {} in primitive Wyvern expressions?? but maybe list methods... // ^maybe account for this later -// still need to account for escape characters - - - - - - - - - - - - - - - - -/* -EXAMPLES -printf(~) - some sort of dollar amt $%{5} -> some sort of dollar amt $5 - floating %.2{6.0} -> floating 6.00 - - (val str = "bar") - foo%{str} - foo%{"bar"} - -not really much use for quotes -use %__ identifiers to detail how to print the data -formatting specs based on C - - -*/ - - +// still need to account for escape characters \ No newline at end of file diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 5b5f57f49..a49c2d6e2 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -14,4 +14,4 @@ printf(~) //beginning of format string or something? printf(~) - Hello %{"string!"} \ No newline at end of file + Hello %{"string!"}%{" mm"} and %{ "another string!" } \ No newline at end of file From 40dabadec78aa202b2c96c216b3681d225ab6d11 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Thu, 20 Jun 2019 17:31:19 -0400 Subject: [PATCH 08/27] added method for getType in AST; not yet working properly --- stdlib/wyvern/ast.wyv | 3 +++ stdlib/wyvern/internal/ast.wyv | 4 ++++ tools/src/wyvern/stdlib/support/AST.java | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/stdlib/wyvern/ast.wyv b/stdlib/wyvern/ast.wyv index 865532e7d..c32861dbe 100644 --- a/stdlib/wyvern/ast.wyv +++ b/stdlib/wyvern/ast.wyv @@ -92,3 +92,6 @@ def formalArg(name : String, argType : Type) : FormalArg = ast.formalArg(name, a def stripLeadingWhitespace(input : String, mustStrip : Boolean) : String = ast.stripLeadingWhitespace(input, mustStrip) def genIdent() : String = ast.genIdent() + +def getType(e : AST, ctx : system.Context) : Type + ast.getType(e, ctx) \ No newline at end of file diff --git a/stdlib/wyvern/internal/ast.wyv b/stdlib/wyvern/internal/ast.wyv index b510e27ef..8bea7a960 100644 --- a/stdlib/wyvern/internal/ast.wyv +++ b/stdlib/wyvern/internal/ast.wyv @@ -156,3 +156,7 @@ def genIdent() : String = utils.genIdent() def let(ident : String, bindingType : Type, bindingValue : AST, inExpr : AST) : AST = new val ast:Unit = utils.let(ident, bindingType, bindingValue, inExpr) + +def getType(e : AST, ctx : system.Context) : Type + val ctxDyn:Dyn = ctx + utils.getObjectType(e, ctxDyn) \ No newline at end of file diff --git a/tools/src/wyvern/stdlib/support/AST.java b/tools/src/wyvern/stdlib/support/AST.java index 4bb1205f0..939aaa025 100644 --- a/tools/src/wyvern/stdlib/support/AST.java +++ b/tools/src/wyvern/stdlib/support/AST.java @@ -437,5 +437,10 @@ public int charCount(String leading) { } return count; } + + public ValueType getObjectType(ObjectValue o, GenContext ctx) { + IExpr expr = getExpr(o); + return expr.typeCheck(ctx, null); + } } From 16f3990084d397f2ad19e52049ba08f8fe393943 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Fri, 21 Jun 2019 11:01:46 -0400 Subject: [PATCH 09/27] getType in AST compiles --- stdlib/wyvern/internal/ast.wyv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/wyvern/internal/ast.wyv b/stdlib/wyvern/internal/ast.wyv index 8bea7a960..92537c0b8 100644 --- a/stdlib/wyvern/internal/ast.wyv +++ b/stdlib/wyvern/internal/ast.wyv @@ -159,4 +159,5 @@ def let(ident : String, bindingType : Type, bindingValue : AST, inExpr : AST) : def getType(e : AST, ctx : system.Context) : Type val ctxDyn:Dyn = ctx - utils.getObjectType(e, ctxDyn) \ No newline at end of file + new + val typ:Unit = utils.getObjectType(e, ctxDyn) \ No newline at end of file From be9bc5e4c8b83887823038bb2676f5f7236c9e8f Mon Sep 17 00:00:00 2001 From: jennyafish Date: Fri, 21 Jun 2019 11:27:48 -0400 Subject: [PATCH 10/27] cleaned up formatstring parser for fewer inefficiencies --- examples/tsls/formatstring.wyv | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 73c725a9f..6aeabbc39 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -18,6 +18,7 @@ def makeParsedExpr(e:option.Option[AST],r:String):ParsedExpr = new val prefixRegex : regex.Regex = regex("[^%]+") // recursively determines position of the rbrace ending the expression, -1 if not found +// TODO: could also be part of a string def getExprPos(input : String, lbraceCt : Int, pos : Int) : Int (input.length() == pos).ifTrue( () => -1, @@ -33,17 +34,16 @@ def getExprPos(input : String, lbraceCt : Int, pos : Int) : Int def parseExpr(input : String, ctx: system.Context) : ParsedExpr //if %{...}, we want to get the contents of the braces val normal : option.Option[String] = regex("%\\{").findPrefixMatchOf(input).map[String]((m:regex.Match) => m.after()) - //val end = getExprPos(normal.get(), 0, 0) - (normal.isDefined).ifTrue( - () => (getExprPos(normal.get(), 0, 0) == -1).ifTrue( - () => makeParsedExpr(option.None[AST](), normal.get()), //error? - //option.Some[AST](ast.string("parsing error")),//continue parsing or throw error - () => makeParsedExpr(option.Some[AST](ast.parseExpression(normal.get().substring(0,getExprPos(normal.get(), 0, 0)), ctx)), normal.get().substring(getExprPos(normal.get(), 0, 0)+1, normal.get().length())) - //option.Some[AST](ast.parseExpression(normal.get().substring(0,getExprPos(normal.get(), 0, 0)), ctx)) - ), - //if nothing found then default to whole string? - () => makeParsedExpr(option.None[AST](), input) //WARNING probably loops //option.Some[AST](ast.string("nonstandard formatstring")) - ) + if (normal.isDefined) + val eoparse = getExprPos(normal.get(), 0, 0) + (eoparse == -1).ifTrue( + () => makeParsedExpr(option.None[AST](), normal.get()), + () => makeParsedExpr(option.Some[AST](ast.parseExpression(normal.get().substring(0,eoparse), ctx)), normal.get().substring(eoparse+1, normal.get().length())) + ) + else + //if nothing found then default to whole string + makeParsedExpr(option.None[AST](), input) //WARNING probably loops + //if %/d./d, ensure formatted float @@ -58,17 +58,17 @@ type FormatString = String val regxMatch : option.Option[regex.Match] = prefixRegex.findPrefixMatchOf(input) val prefixString : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.matched()) val rest : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.after()) - (input == "").ifTrue( - () => option.Some[AST](ast.string("")), - () => (prefixString.isDefined).ifTrue( - () => option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {(parseExpr(rest.getOrElse(() => ""), ctx)).expr().getOrElse(() => ast.string(""))}), "+", {this.parseTSL(parseExpr(rest.getOrElse(() => ""), ctx).rest(), ctx).getOrElse(() => ast.string(""))})), - () => option.Some[AST](ast.call(parseExpr(rest.getOrElse(() => input), ctx).expr().getOrElse(() => ast.string("")), "+", {this.parseTSL(parseExpr(rest.getOrElse(() => input), ctx).rest(), ctx).getOrElse(() => ast.string(""))})) - //g = parseExpr(rest.getOrElse(() => ""), ctx) - //option.Some[AST](ast.call(g.expr().getOrElse(() => ast.string("")), "+", {this.parseTSL(g.rest(), ctx).getOrElse(() => ast.string(""))})) - ) - ) - //option.Some[AST](ast.call(ast.string("hello "), "+", {ast.string("world!")})) - //option.Some[AST](ast.string(input)) + if (input == "") + option.Some[AST](ast.string("")) + else + if (prefixString.isDefined) + val g = parseExpr(rest.getOrElse(() => ""), ctx) + option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().getOrElse(() => ast.string(""))}), "+", {this.parseTSL(g.rest(), ctx).getOrElse(() => ast.string(""))})) + else + val g = parseExpr(rest.getOrElse(() => input), ctx) + option.Some[AST](ast.call(g.expr().getOrElse(() => ast.string("")), "+", {this.parseTSL(g.rest(), ctx).getOrElse(() => ast.string(""))})) + + From cb3a06f91d2757b2f81a363da9a4147a61e317b0 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Fri, 21 Jun 2019 15:39:08 -0400 Subject: [PATCH 11/27] set up to handle float specs and typecasting ASTs for appends --- examples/tsls/formatstring.wyv | 46 ++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 6aeabbc39..c7a3a18d0 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -4,6 +4,8 @@ import wyvern.ast import wyvern.option import wyvern.util.matching.regex import metadata wyvern.collections.list +import wyvern.Int +import wyvern.String type AST = ast.AST @@ -18,7 +20,8 @@ def makeParsedExpr(e:option.Option[AST],r:String):ParsedExpr = new val prefixRegex : regex.Regex = regex("[^%]+") // recursively determines position of the rbrace ending the expression, -1 if not found -// TODO: could also be part of a string +// TODO: could also be part of a string, use inString : bool? +// TODO: ignore escape characters (\{, \}) def getExprPos(input : String, lbraceCt : Int, pos : Int) : Int (input.length() == pos).ifTrue( () => -1, @@ -30,19 +33,43 @@ def getExprPos(input : String, lbraceCt : Int, pos : Int) : Int () => getExprPos(input, lbraceCt, pos+1) ) ) + +// takes in an expression as a string and returns a string wrapped in an AST +def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : option.Option[AST] + val exp : AST = ast.parseExpression(input, ctx) + val ty = ast.getType(exp, ctx) + //if (ty == ast.types.string()) + option.Some[AST](exp) + //String.ofInt(x) + //String.ofFormattedFloat(format: String, f: Float) wait might need to work around this + //else + //option.None[AST]() -def parseExpr(input : String, ctx: system.Context) : ParsedExpr - //if %{...}, we want to get the contents of the braces + +def parseExpr(input : String, ctx : system.Context) : ParsedExpr + // if %{...}, we want to get the contents of the braces val normal : option.Option[String] = regex("%\\{").findPrefixMatchOf(input).map[String]((m:regex.Match) => m.after()) if (normal.isDefined) - val eoparse = getExprPos(normal.get(), 0, 0) + val eoparse : Int = getExprPos(normal.get(), 0, 0) (eoparse == -1).ifTrue( - () => makeParsedExpr(option.None[AST](), normal.get()), - () => makeParsedExpr(option.Some[AST](ast.parseExpression(normal.get().substring(0,eoparse), ctx)), normal.get().substring(eoparse+1, normal.get().length())) + () => makeParsedExpr(option.None[AST](), normal.get()), //technically don't need normal + () => makeParsedExpr(convStringAST(normal.get().substring(0,eoparse), ctx, -1), normal.get().substring(eoparse+1, normal.get().length())) ) else - //if nothing found then default to whole string - makeParsedExpr(option.None[AST](), input) //WARNING probably loops + val floatspec : option.Option[regex.Match] = regex("%\\.\\d+\\{").findPrefixMatchOf(input) + val formatspec : option.Option[String] = floatspec.map[String]((m:regex.Match) => m.matched()) + if (floatspec.isDefined) + val precision : Int = Int.from(formatspec.get().substring(2, formatspec.get().length()-1)) + val floatstring : String = floatspec.get().after() + val floatstringend : Int = getExprPos(floatstring, 0, 0) + (floatstringend == -1).ifTrue( + () => makeParsedExpr(option.None[AST](), floatstring), + () => makeParsedExpr(convStringAST(floatstring.substring(0, floatstringend), ctx, precision), + floatstring.substring(floatstringend+1, floatstring.length())) + ) + else + // if nothing found then default to whole string + makeParsedExpr(option.None[AST](), input) //WARNING might loop //if %/d./d, ensure formatted float @@ -52,6 +79,9 @@ def parseExpr(input : String, ctx: system.Context) : ParsedExpr // if none of the above, just append % and continue parsing //option.Some[AST](ast.string(input)) + +//TODO: if parseExpr.expr() == option.None[AST], then return option.None[AST] +//TODO: ignore escape character (\%) type FormatString = String metadata new def parseTSL(input : String, ctx : system.Context) : option.Option[AST] From 99fdc4289917bc6b3b1d708290c1aa62d3745d5e Mon Sep 17 00:00:00 2001 From: jennyafish Date: Fri, 21 Jun 2019 17:11:16 -0400 Subject: [PATCH 12/27] handled returning option.None[AST] for improper use of % in printformatting --- examples/tsls/formatstring.wyv | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index c7a3a18d0..1903deb11 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -37,7 +37,7 @@ def getExprPos(input : String, lbraceCt : Int, pos : Int) : Int // takes in an expression as a string and returns a string wrapped in an AST def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : option.Option[AST] val exp : AST = ast.parseExpression(input, ctx) - val ty = ast.getType(exp, ctx) + val ty : ast.Type = ast.getType(exp, ctx) //if (ty == ast.types.string()) option.Some[AST](exp) //String.ofInt(x) @@ -68,16 +68,15 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr floatstring.substring(floatstringend+1, floatstring.length())) ) else - // if nothing found then default to whole string - makeParsedExpr(option.None[AST](), input) //WARNING might loop + // if nothing found then 5 should've been escaped, so throw error + makeParsedExpr(option.None[AST](), input) //if %/d./d, ensure formatted float // %/d - // if none of the above, just append % and continue parsing - //option.Some[AST](ast.string(input)) + // if none of the above, return option.None[AST] //TODO: if parseExpr.expr() == option.None[AST], then return option.None[AST] @@ -92,11 +91,28 @@ type FormatString = String option.Some[AST](ast.string("")) else if (prefixString.isDefined) - val g = parseExpr(rest.getOrElse(() => ""), ctx) - option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().getOrElse(() => ast.string(""))}), "+", {this.parseTSL(g.rest(), ctx).getOrElse(() => ast.string(""))})) + if (rest.get() == "") + option.Some[AST](ast.string(prefixString.get())) + else + val g : ParsedExpr = parseExpr(rest.getOrElse(() => ""), ctx) + if (g.expr().isDefined) + val recursiveCall = this.parseTSL(g.rest(), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().get()}), "+", {recursiveCall.get()})) + else + option.None[AST]() //recursive call gives None + else + option.None[AST]() //cannot parse expression else - val g = parseExpr(rest.getOrElse(() => input), ctx) - option.Some[AST](ast.call(g.expr().getOrElse(() => ast.string("")), "+", {this.parseTSL(g.rest(), ctx).getOrElse(() => ast.string(""))})) + val g : ParsedExpr = parseExpr(rest.getOrElse(() => input), ctx) + if (g.expr().isDefined) + val recursiveCall = this.parseTSL(g.rest(), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(g.expr().get(), "+", {recursiveCall.get()})) + else + option.None[AST]() //recursive call gives None + else + option.None[AST]() //cannot parse expression From f66a5c849d1b918d99e4043a9d03e752f1ace019 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Mon, 24 Jun 2019 15:42:23 -0400 Subject: [PATCH 13/27] parser ignores curly braces inside strings for wyvern expressions --- examples/tsls/formatstring.wyv | 31 ++++++++++++++++++---------- examples/tsls/formatstringClient.wyv | 8 +++---- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 1903deb11..f11214631 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -17,20 +17,26 @@ def makeParsedExpr(e:option.Option[AST],r:String):ParsedExpr = new def expr():option.Option[AST] = e def rest():String = r -val prefixRegex : regex.Regex = regex("[^%]+") - // recursively determines position of the rbrace ending the expression, -1 if not found -// TODO: could also be part of a string, use inString : bool? // TODO: ignore escape characters (\{, \}) -def getExprPos(input : String, lbraceCt : Int, pos : Int) : Int +def getExprPos(input : String, lbraceCt : Int, inString : Boolean, pos : Int) : Int (input.length() == pos).ifTrue( () => -1, () => (input.charAt(pos) == #"}").ifTrue( - () => (lbraceCt == 0).ifTrue( + () => (lbraceCt == 0 && !inString).ifTrue( () => pos, - () => getExprPos(input, lbraceCt - 1, pos+1) + () => inString.ifTrue( + () => getExprPos(input, lbraceCt, inString, pos+1), + () => getExprPos(input, lbraceCt - 1, inString, pos+1) + ) ), - () => getExprPos(input, lbraceCt, pos+1) + () => (input.charAt(pos) == #"\"").ifTrue( + () => getExprPos(input, lbraceCt, !inString, pos+1), + () => (input.charAt(pos) == #"{" && !inString).ifTrue( + () => getExprPos(input, lbraceCt+1, inString, pos+1), + () => getExprPos(input, lbraceCt, inString, pos+1) + ) + ) ) ) @@ -50,9 +56,9 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr // if %{...}, we want to get the contents of the braces val normal : option.Option[String] = regex("%\\{").findPrefixMatchOf(input).map[String]((m:regex.Match) => m.after()) if (normal.isDefined) - val eoparse : Int = getExprPos(normal.get(), 0, 0) + val eoparse : Int = getExprPos(normal.get(), 0, false, 0) (eoparse == -1).ifTrue( - () => makeParsedExpr(option.None[AST](), normal.get()), //technically don't need normal + () => makeParsedExpr(option.None[AST](), normal.get()), () => makeParsedExpr(convStringAST(normal.get().substring(0,eoparse), ctx, -1), normal.get().substring(eoparse+1, normal.get().length())) ) else @@ -61,7 +67,7 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr if (floatspec.isDefined) val precision : Int = Int.from(formatspec.get().substring(2, formatspec.get().length()-1)) val floatstring : String = floatspec.get().after() - val floatstringend : Int = getExprPos(floatstring, 0, 0) + val floatstringend : Int = getExprPos(floatstring, 0, false, 0) (floatstringend == -1).ifTrue( () => makeParsedExpr(option.None[AST](), floatstring), () => makeParsedExpr(convStringAST(floatstring.substring(0, floatstringend), ctx, precision), @@ -78,8 +84,11 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr // if none of the above, return option.None[AST] +//val prefixRegex : regex.Regex = regex("((\\%)|[^%])+") +//alternative approach: if last char of prefixString is \ and next matches %, replace it with % and append to parseTSL +val prefixRegex : regex.Regex = regex("[^%]+") -//TODO: if parseExpr.expr() == option.None[AST], then return option.None[AST] +//include \% + other characters, except pure % //TODO: ignore escape character (\%) type FormatString = String metadata new diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index a49c2d6e2..56c91013a 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -2,6 +2,9 @@ require stdout import metadata tsls.formatstring +//need to deal with leading whitespace for parsing; maybe use ` to indicate +//beginning of format string or something? + def printf(str:formatstring.FormatString):Unit stdout.print(str) stdout.println() @@ -10,8 +13,5 @@ def printf(str:formatstring.FormatString):Unit printf(~) Hello World! -//need to deal with leading whitespace for parsing; maybe use ` to indicate -//beginning of format string or something? - printf(~) - Hello %{"string!"}%{" mm"} and %{ "another string!" } \ No newline at end of file + Hello %{"string!"}%{" {mm}"} and %{ "another string!" } \ No newline at end of file From ec637c8b25ca808b2687915b6a1ac0ba5afcc08e Mon Sep 17 00:00:00 2001 From: jennyafish Date: Wed, 26 Jun 2019 12:04:23 -0400 Subject: [PATCH 14/27] hopefully faster parsing on average; ignores escape characters within spliced wyvern expressions --- examples/tsls/formatstring.wyv | 49 ++++++++++++++++------------ examples/tsls/formatstringClient.wyv | 6 +++- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index f11214631..2c013b612 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -18,32 +18,40 @@ def makeParsedExpr(e:option.Option[AST],r:String):ParsedExpr = new def rest():String = r // recursively determines position of the rbrace ending the expression, -1 if not found -// TODO: ignore escape characters (\{, \}) +// TODO: ignore escape characters (\{, \}) (do we actually need this??) def getExprPos(input : String, lbraceCt : Int, inString : Boolean, pos : Int) : Int - (input.length() == pos).ifTrue( - () => -1, - () => (input.charAt(pos) == #"}").ifTrue( - () => (lbraceCt == 0 && !inString).ifTrue( - () => pos, - () => inString.ifTrue( - () => getExprPos(input, lbraceCt, inString, pos+1), - () => getExprPos(input, lbraceCt - 1, inString, pos+1) - ) - ), - () => (input.charAt(pos) == #"\"").ifTrue( - () => getExprPos(input, lbraceCt, !inString, pos+1), - () => (input.charAt(pos) == #"{" && !inString).ifTrue( - () => getExprPos(input, lbraceCt+1, inString, pos+1), - () => getExprPos(input, lbraceCt, inString, pos+1) - ) - ) - ) - ) + if (input.length() == pos) + -1 + else + val c = input.charAt(pos) + if (c == #"}" || c == #"{" || c == #"\"") + (c == #"}").ifTrue( + () => (lbraceCt == 0 && !inString).ifTrue( + () => pos, + () => inString.ifTrue( + () => getExprPos(input, lbraceCt, inString, pos+1), + () => (input.charAt(pos-1) == #"\\").ifTrue( + () => getExprPos(input, lbraceCt, inString, pos+1), + () => getExprPos(input, lbraceCt - 1, inString, pos+1) + ) + ) + ), + () => (c == #"\"").ifTrue( + () => getExprPos(input, lbraceCt, !inString, pos+1), + () => (c == #"{" && !inString).ifTrue( + () => getExprPos(input, lbraceCt+1, inString, pos+1), + () => getExprPos(input, lbraceCt, inString, pos+1) + ) + ) + ) + else + getExprPos(input, lbraceCt, inString, pos+1) // takes in an expression as a string and returns a string wrapped in an AST def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : option.Option[AST] val exp : AST = ast.parseExpression(input, ctx) val ty : ast.Type = ast.getType(exp, ctx) + //layout: if floatPrecision != -1, check type float and format, otws format as string //if (ty == ast.types.string()) option.Some[AST](exp) //String.ofInt(x) @@ -86,6 +94,7 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr //val prefixRegex : regex.Regex = regex("((\\%)|[^%])+") //alternative approach: if last char of prefixString is \ and next matches %, replace it with % and append to parseTSL +//would need to replace \ in string even if using regex val prefixRegex : regex.Regex = regex("[^%]+") //include \% + other characters, except pure % diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 56c91013a..ebff613bc 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -14,4 +14,8 @@ printf(~) Hello World! printf(~) - Hello %{"string!"}%{" {mm}"} and %{ "another string!" } \ No newline at end of file + Hello %{"string!"}%{" {mm}"} and %{ "another string!" } + +val x = "xvalue" +printf(~) + %{x} \ No newline at end of file From 997c62f4837bc55dc6e21d35b7b125bcf3958788 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Wed, 26 Jun 2019 15:52:15 -0400 Subject: [PATCH 15/27] parsing accounts for escaped double quotes --- examples/tsls/formatstring.wyv | 26 ++++++++++++++++++-------- examples/tsls/formatstringClient.wyv | 4 ++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 2c013b612..7a8fb2d6a 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -18,7 +18,7 @@ def makeParsedExpr(e:option.Option[AST],r:String):ParsedExpr = new def rest():String = r // recursively determines position of the rbrace ending the expression, -1 if not found -// TODO: ignore escape characters (\{, \}) (do we actually need this??) +// TODO: ignore escape characters (\") def getExprPos(input : String, lbraceCt : Int, inString : Boolean, pos : Int) : Int if (input.length() == pos) -1 @@ -30,14 +30,17 @@ def getExprPos(input : String, lbraceCt : Int, inString : Boolean, pos : Int) : () => pos, () => inString.ifTrue( () => getExprPos(input, lbraceCt, inString, pos+1), - () => (input.charAt(pos-1) == #"\\").ifTrue( - () => getExprPos(input, lbraceCt, inString, pos+1), - () => getExprPos(input, lbraceCt - 1, inString, pos+1) - ) + () => getExprPos(input, lbraceCt - 1, inString, pos+1) ) ), () => (c == #"\"").ifTrue( - () => getExprPos(input, lbraceCt, !inString, pos+1), + () => (pos == 0).ifTrue( + () => getExprPos(input, lbraceCt, !inString, pos+1), + () => (input.charAt(pos-1) == #"\\").ifTrue( + () => getExprPos(input, lbraceCt, inString, pos+1), + () => getExprPos(input, lbraceCt, !inString, pos+1) + ) + ), () => (c == #"{" && !inString).ifTrue( () => getExprPos(input, lbraceCt+1, inString, pos+1), () => getExprPos(input, lbraceCt, inString, pos+1) @@ -70,6 +73,7 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr () => makeParsedExpr(convStringAST(normal.get().substring(0,eoparse), ctx, -1), normal.get().substring(eoparse+1, normal.get().length())) ) else + // we check if %.x{...}, for float precision to x decimal places val floatspec : option.Option[regex.Match] = regex("%\\.\\d+\\{").findPrefixMatchOf(input) val formatspec : option.Option[String] = floatspec.map[String]((m:regex.Match) => m.matched()) if (floatspec.isDefined) @@ -82,7 +86,7 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr floatstring.substring(floatstringend+1, floatstring.length())) ) else - // if nothing found then 5 should've been escaped, so throw error + // if nothing found then % should've been escaped, so throw error makeParsedExpr(option.None[AST](), input) @@ -99,6 +103,7 @@ val prefixRegex : regex.Regex = regex("[^%]+") //include \% + other characters, except pure % //TODO: ignore escape character (\%) +//TODO: parse leading whitespace type FormatString = String metadata new def parseTSL(input : String, ctx : system.Context) : option.Option[AST] @@ -135,7 +140,12 @@ type FormatString = String - +/* +parsing whitespace +put parseTSL in a helper function +do some preprocessing on the string +find smallest indent and remove that much from each whitespace gap following \n +*/ diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index ebff613bc..730410ae5 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -14,8 +14,8 @@ printf(~) Hello World! printf(~) - Hello %{"string!"}%{" {mm}"} and %{ "another string!" } - + Hello %{"string!"}%{" \"}{mm}"} and %{ "another string!" } + val x = "xvalue" printf(~) %{x} \ No newline at end of file From 4c8c2f8782aca379b5d0f0b46f60468ce592fc77 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Thu, 27 Jun 2019 15:14:33 -0400 Subject: [PATCH 16/27] added support for equality checking types in ast.types --- examples/tsls/formatstring.wyv | 19 +++++++++++++++++-- stdlib/wyvern/internal/ast.wyv | 22 +++++++++++++--------- tools/src/wyvern/stdlib/support/AST.java | 5 +++++ 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 7a8fb2d6a..fe28aa28e 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -55,8 +55,11 @@ def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : val exp : AST = ast.parseExpression(input, ctx) val ty : ast.Type = ast.getType(exp, ctx) //layout: if floatPrecision != -1, check type float and format, otws format as string - //if (ty == ast.types.string()) - option.Some[AST](exp) + if (ast.types.equals(ty, ast.types.string(), ctx)) + option.Some[AST](exp) + else + //if (ast.types.equals(ty, ast.types.int(), ctx)) + option.None[AST]() //String.ofInt(x) //String.ofFormattedFloat(format: String, f: Float) wait might need to work around this //else @@ -96,6 +99,18 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr // if none of the above, return option.None[AST] +/* TO DO LATER +val whitespaceRX : regex.Regex = regex("[ \t]+") +val lineRX : regex.Regex = regex("[^\n]*\n") +def getLeastIndented(input : String, acc : Int) : Int + val leadingWS : option.Option[regex.Match] = whitespaceRX.findPrefixMatchOf(input) + val + +// strips leading whitespace for each line up to least indented line +def parseWhitespace(input : String) : String + +*/ + //val prefixRegex : regex.Regex = regex("((\\%)|[^%])+") //alternative approach: if last char of prefixString is \ and next matches %, replace it with % and append to parseTSL //would need to replace \ in string even if using regex diff --git a/stdlib/wyvern/internal/ast.wyv b/stdlib/wyvern/internal/ast.wyv index 92537c0b8..3be70e13a 100644 --- a/stdlib/wyvern/internal/ast.wyv +++ b/stdlib/wyvern/internal/ast.wyv @@ -4,13 +4,13 @@ import wyvern.internal.list import java:wyvern.stdlib.support.AST.utils type AST - val ast:Unit + val ast:Unit type Decl - val decl:Unit + val decl:Unit type Type - val typ:Unit + val typ:Unit type DeclType val declType:Unit @@ -37,10 +37,10 @@ def bind(bindings : list.List, inExpr : AST) : AST = new val ast:Unit = utils.bind(bindings, inExpr) def object(decls : list.List) : AST = new - val ast:Unit = utils.object(decls) + val ast:Unit = utils.object(decls) def defDeclaration(name : String, formalArgs : list.List[FormalArg], returnType : Type, body : AST):Decl = new - val decl:Unit = utils.defDeclaration(name, formalArgs, returnType, body) + val decl:Unit = utils.defDeclaration(name, formalArgs, returnType, body) def delegateDeclaration(delegateType : Type, fieldName : String) : Decl = new val decl:Unit = utils.delegateDeclaration(delegateType, fieldName) @@ -58,19 +58,19 @@ def varDeclaration(fieldName : String, fieldType : Type, value : AST) : Decl = n val decl:Unit = utils.varDeclaration(fieldName, fieldType, value) def int(i:Int):AST = new - val ast:Unit = utils.intLiteral(i) + val ast:Unit = utils.intLiteral(i) def boolean(b:Boolean):AST = new val ast:Unit = utils.booleanLiteral(b) def string(s:String):AST = new - val ast:Unit = utils.stringLiteral(s) + val ast:Unit = utils.stringLiteral(s) def variable(s:String):AST = new - val ast:Unit = utils.variable(s) + val ast:Unit = utils.variable(s) def call(receiver:AST, methodName:String, arguments:list.List):AST = new - val ast:Unit = utils.methodCall(receiver,methodName, arguments) + val ast:Unit = utils.methodCall(receiver,methodName, arguments) def cast(toCastExpr : AST, exprType : Type) : AST = new val ast:Unit = utils.cast(toCastExpr, exprType) @@ -149,6 +149,10 @@ val types = new def refinement(typeParams : list.List[Type], base : Type):Type = new val typ:Unit = utils.refinementType(typeParams, base) + + def equals(t1 : Type, t2 : Type, ctx : system.Context):Boolean + val ctxDyn:Dyn = ctx + utils.checkTypeEquality(t1, t2, ctxDyn) def stripLeadingWhitespace(input : String, mustStrip : Boolean) : String = utils.stripLeadingWhitespace(input, mustStrip) diff --git a/tools/src/wyvern/stdlib/support/AST.java b/tools/src/wyvern/stdlib/support/AST.java index 939aaa025..f39fe681d 100644 --- a/tools/src/wyvern/stdlib/support/AST.java +++ b/tools/src/wyvern/stdlib/support/AST.java @@ -46,6 +46,7 @@ import wyvern.target.corewyvernIL.expression.Value; import wyvern.target.corewyvernIL.generics.GenericArgument; import wyvern.target.corewyvernIL.modules.TypedModuleSpec; +import wyvern.target.corewyvernIL.support.FailureReason; import wyvern.target.corewyvernIL.support.GenContext; import wyvern.target.corewyvernIL.support.ILFactory; import wyvern.target.corewyvernIL.support.InterpreterState; @@ -442,5 +443,9 @@ public ValueType getObjectType(ObjectValue o, GenContext ctx) { IExpr expr = getExpr(o); return expr.typeCheck(ctx, null); } + + public boolean checkTypeEquality(ObjectValue t1, ObjectValue t2, GenContext ctx) { + return getType(t1).equalsInContext(getType(t2), ctx, null); + } } From acfd9f42f73800e253ef4c7bb99d68f28c4ecf82 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Thu, 27 Jun 2019 15:20:32 -0400 Subject: [PATCH 17/27] added support for float types in AST --- stdlib/wyvern/internal/ast.wyv | 3 +++ tools/src/wyvern/stdlib/support/AST.java | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/stdlib/wyvern/internal/ast.wyv b/stdlib/wyvern/internal/ast.wyv index 3be70e13a..7eb659ae7 100644 --- a/stdlib/wyvern/internal/ast.wyv +++ b/stdlib/wyvern/internal/ast.wyv @@ -140,6 +140,9 @@ val types = new def string():Type = new val typ:Unit = utils.stringType() + + def float():Type = new + val typ:Unit = utils.floatType() def nominal(pathVariable : String, typeMember : String):Type = new val typ:Unit = utils.nominalType(pathVariable, typeMember) diff --git a/tools/src/wyvern/stdlib/support/AST.java b/tools/src/wyvern/stdlib/support/AST.java index f39fe681d..77c59bebd 100644 --- a/tools/src/wyvern/stdlib/support/AST.java +++ b/tools/src/wyvern/stdlib/support/AST.java @@ -99,6 +99,10 @@ public ValueType booleanType() { public ValueType stringType() { return Util.stringType(); } + + public ValueType floatType() { + return Util.floatType(); + } public ValueType nominalType(String pathVariable, String typeMember) { return new NominalType(pathVariable, typeMember); From eaca73fbf381acb5dc055efe796584b3376f9dfd Mon Sep 17 00:00:00 2001 From: jennyafish Date: Thu, 27 Jun 2019 16:48:47 -0400 Subject: [PATCH 18/27] removed an import statement --- tools/src/wyvern/stdlib/support/AST.java | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/src/wyvern/stdlib/support/AST.java b/tools/src/wyvern/stdlib/support/AST.java index 77c59bebd..ac93293e2 100644 --- a/tools/src/wyvern/stdlib/support/AST.java +++ b/tools/src/wyvern/stdlib/support/AST.java @@ -46,7 +46,6 @@ import wyvern.target.corewyvernIL.expression.Value; import wyvern.target.corewyvernIL.generics.GenericArgument; import wyvern.target.corewyvernIL.modules.TypedModuleSpec; -import wyvern.target.corewyvernIL.support.FailureReason; import wyvern.target.corewyvernIL.support.GenContext; import wyvern.target.corewyvernIL.support.ILFactory; import wyvern.target.corewyvernIL.support.InterpreterState; From b890924875adcf3d26cb0c0bebd8f4f6598771ba Mon Sep 17 00:00:00 2001 From: jennyafish Date: Fri, 28 Jun 2019 16:33:17 -0400 Subject: [PATCH 19/27] printformatting works with integers --- examples/tsls/formatstring.wyv | 31 +++++++++++------------- examples/tsls/formatstringClient.wyv | 10 +++++--- tools/src/wyvern/stdlib/support/AST.java | 3 ++- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index fe28aa28e..cd1ce6de8 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -18,7 +18,6 @@ def makeParsedExpr(e:option.Option[AST],r:String):ParsedExpr = new def rest():String = r // recursively determines position of the rbrace ending the expression, -1 if not found -// TODO: ignore escape characters (\") def getExprPos(input : String, lbraceCt : Int, inString : Boolean, pos : Int) : Int if (input.length() == pos) -1 @@ -50,16 +49,25 @@ def getExprPos(input : String, lbraceCt : Int, inString : Boolean, pos : Int) : else getExprPos(input, lbraceCt, inString, pos+1) -// takes in an expression as a string and returns a string wrapped in an AST +// takes in a Wyvern expression as a string and returns a string wrapped in an AST def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : option.Option[AST] val exp : AST = ast.parseExpression(input, ctx) val ty : ast.Type = ast.getType(exp, ctx) - //layout: if floatPrecision != -1, check type float and format, otws format as string if (ast.types.equals(ty, ast.types.string(), ctx)) option.Some[AST](exp) else - //if (ast.types.equals(ty, ast.types.int(), ctx)) - option.None[AST]() + if (ast.types.equals(ty, ast.types.int(), ctx)) + option.Some[AST](ast.call(ast.parseExpression("String", ctx), "ofInt", {exp})) + else + option.None[AST]() + /* + if (ast.types.equals(ty, ast.types.float())) + if (floatPrecision == -1) + + else + val format : String = "%." + String.ofInt(floatPrecision) + "f" + option.Some[AST](ast.call(, "", {ast.string(format), exp})) + */ //String.ofInt(x) //String.ofFormattedFloat(format: String, f: Float) wait might need to work around this //else @@ -99,17 +107,6 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr // if none of the above, return option.None[AST] -/* TO DO LATER -val whitespaceRX : regex.Regex = regex("[ \t]+") -val lineRX : regex.Regex = regex("[^\n]*\n") -def getLeastIndented(input : String, acc : Int) : Int - val leadingWS : option.Option[regex.Match] = whitespaceRX.findPrefixMatchOf(input) - val - -// strips leading whitespace for each line up to least indented line -def parseWhitespace(input : String) : String - -*/ //val prefixRegex : regex.Regex = regex("((\\%)|[^%])+") //alternative approach: if last char of prefixString is \ and next matches %, replace it with % and append to parseTSL @@ -178,7 +175,7 @@ then look for either a number, ., or { (regexp matching) continue parsing and appending strings recursively -*/ +*/ /* def parseString(input:String):option.Option[AST] diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 730410ae5..597df7cc4 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -1,9 +1,9 @@ require stdout import metadata tsls.formatstring +import wyvern.String -//need to deal with leading whitespace for parsing; maybe use ` to indicate -//beginning of format string or something? +//need to deal with leading whitespace for parsing def printf(str:formatstring.FormatString):Unit stdout.print(str) @@ -16,6 +16,8 @@ printf(~) printf(~) Hello %{"string!"}%{" \"}{mm}"} and %{ "another string!" } -val x = "xvalue" +val x = 3 printf(~) - %{x} \ No newline at end of file + %{x} + +stdout.print(String.ofFormattedFloat("%.2f", 1.5)) \ No newline at end of file diff --git a/tools/src/wyvern/stdlib/support/AST.java b/tools/src/wyvern/stdlib/support/AST.java index ac93293e2..e148569de 100644 --- a/tools/src/wyvern/stdlib/support/AST.java +++ b/tools/src/wyvern/stdlib/support/AST.java @@ -46,6 +46,7 @@ import wyvern.target.corewyvernIL.expression.Value; import wyvern.target.corewyvernIL.generics.GenericArgument; import wyvern.target.corewyvernIL.modules.TypedModuleSpec; +import wyvern.target.corewyvernIL.support.FailureReason; import wyvern.target.corewyvernIL.support.GenContext; import wyvern.target.corewyvernIL.support.ILFactory; import wyvern.target.corewyvernIL.support.InterpreterState; @@ -448,7 +449,7 @@ public ValueType getObjectType(ObjectValue o, GenContext ctx) { } public boolean checkTypeEquality(ObjectValue t1, ObjectValue t2, GenContext ctx) { - return getType(t1).equalsInContext(getType(t2), ctx, null); + return getType(t1).equalsInContext(getType(t2), ctx, new FailureReason()); } } From 22e380118d76e3e12cfe34757908caac58e9b0bc Mon Sep 17 00:00:00 2001 From: jennyafish Date: Fri, 28 Jun 2019 16:46:34 -0400 Subject: [PATCH 20/27] support for floats and formatted floats --- examples/tsls/formatstring.wyv | 30 ++++++++++------------------ examples/tsls/formatstringClient.wyv | 2 +- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index cd1ce6de8..5958b8509 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -50,28 +50,25 @@ def getExprPos(input : String, lbraceCt : Int, inString : Boolean, pos : Int) : getExprPos(input, lbraceCt, inString, pos+1) // takes in a Wyvern expression as a string and returns a string wrapped in an AST +// TODO: handle booleans def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : option.Option[AST] val exp : AST = ast.parseExpression(input, ctx) val ty : ast.Type = ast.getType(exp, ctx) if (ast.types.equals(ty, ast.types.string(), ctx)) option.Some[AST](exp) else + val str = ast.parseExpression("String", ctx) //wyvern.String if (ast.types.equals(ty, ast.types.int(), ctx)) - option.Some[AST](ast.call(ast.parseExpression("String", ctx), "ofInt", {exp})) + option.Some[AST](ast.call(str, "ofInt", {exp})) else - option.None[AST]() - /* - if (ast.types.equals(ty, ast.types.float())) - if (floatPrecision == -1) - + if (ast.types.equals(ty, ast.types.float(), ctx)) + if (floatPrecision == -1) + option.Some[AST](ast.call(str, "ofFloat", {exp})) + else + val format : String = "%." + String.ofInt(floatPrecision) + "f" + option.Some[AST](ast.call(str, "ofFormattedFloat", {ast.string(format), exp})) else - val format : String = "%." + String.ofInt(floatPrecision) + "f" - option.Some[AST](ast.call(, "", {ast.string(format), exp})) - */ - //String.ofInt(x) - //String.ofFormattedFloat(format: String, f: Float) wait might need to work around this - //else - //option.None[AST]() + option.None[AST]() def parseExpr(input : String, ctx : system.Context) : ParsedExpr @@ -100,13 +97,6 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr // if nothing found then % should've been escaped, so throw error makeParsedExpr(option.None[AST](), input) - - //if %/d./d, ensure formatted float - - // %/d - - // if none of the above, return option.None[AST] - //val prefixRegex : regex.Regex = regex("((\\%)|[^%])+") //alternative approach: if last char of prefixString is \ and next matches %, replace it with % and append to parseTSL diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 597df7cc4..79523f1f1 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -18,6 +18,6 @@ printf(~) val x = 3 printf(~) - %{x} + %{x} %{4} %.5{1.6} %{1.7} stdout.print(String.ofFormattedFloat("%.2f", 1.5)) \ No newline at end of file From 287d2db78aeefc55c55eedca7692f37bab4a6ee2 Mon Sep 17 00:00:00 2001 From: jennyafish Date: Fri, 28 Jun 2019 17:15:16 -0400 Subject: [PATCH 21/27] strips leading whitespace for printformatting --- examples/tsls/formatstring.wyv | 110 ++++++++++----------------------- 1 file changed, 34 insertions(+), 76 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 5958b8509..4d91480da 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -103,87 +103,45 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr //would need to replace \ in string even if using regex val prefixRegex : regex.Regex = regex("[^%]+") +// recursively parses the input, returning option.None[AST] if parsing error +def parse(input : String, ctx : system.Context) : option.Option[AST] + val regxMatch : option.Option[regex.Match] = prefixRegex.findPrefixMatchOf(input) + val prefixString : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.matched()) + val rest : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.after()) + if (input == "") + option.Some[AST](ast.string("")) + else + if (prefixString.isDefined) + if (rest.get() == "") + option.Some[AST](ast.string(prefixString.get())) + else + val g : ParsedExpr = parseExpr(rest.getOrElse(() => ""), ctx) + if (g.expr().isDefined) + val recursiveCall = parse(g.rest(), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().get()}), "+", {recursiveCall.get()})) + else + option.None[AST]() //recursive call gives None + else + option.None[AST]() //cannot parse expression + else + val g : ParsedExpr = parseExpr(rest.getOrElse(() => input), ctx) + if (g.expr().isDefined) + val recursiveCall = parse(g.rest(), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(g.expr().get(), "+", {recursiveCall.get()})) + else + option.None[AST]() //recursive call gives None + else + option.None[AST]() //cannot parse expression + //include \% + other characters, except pure % //TODO: ignore escape character (\%) -//TODO: parse leading whitespace type FormatString = String metadata new def parseTSL(input : String, ctx : system.Context) : option.Option[AST] - val regxMatch : option.Option[regex.Match] = prefixRegex.findPrefixMatchOf(input) - val prefixString : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.matched()) - val rest : option.Option[String] = regxMatch.map[String]((m:regex.Match) => m.after()) - if (input == "") - option.Some[AST](ast.string("")) - else - if (prefixString.isDefined) - if (rest.get() == "") - option.Some[AST](ast.string(prefixString.get())) - else - val g : ParsedExpr = parseExpr(rest.getOrElse(() => ""), ctx) - if (g.expr().isDefined) - val recursiveCall = this.parseTSL(g.rest(), ctx) - if (recursiveCall.isDefined) - option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().get()}), "+", {recursiveCall.get()})) - else - option.None[AST]() //recursive call gives None - else - option.None[AST]() //cannot parse expression - else - val g : ParsedExpr = parseExpr(rest.getOrElse(() => input), ctx) - if (g.expr().isDefined) - val recursiveCall = this.parseTSL(g.rest(), ctx) - if (recursiveCall.isDefined) - option.Some[AST](ast.call(g.expr().get(), "+", {recursiveCall.get()})) - else - option.None[AST]() //recursive call gives None - else - option.None[AST]() //cannot parse expression - + //TODO: only strip leading whitespace for ~ referencing, not {} TSL syntax + parse(ast.stripLeadingWhitespace(input, false), ctx) -/* -parsing whitespace -put parseTSL in a helper function -do some preprocessing on the string -find smallest indent and remove that much from each whitespace gap following \n -*/ - - - - - - - - - - -/** OUTLINE - -pull off individual string characters until you reach a % (account for esc later) -then look for either a number, ., or { (regexp matching) -continue parsing and appending strings recursively - - -*/ - -/* -def parseString(input:String):option.Option[AST] - //start by pulling the string before the next % character - val prefixRegex : regex.Regex = regex("[^%]+") - val prefixString : option.Option[String] = prefixRegex.findPrefixMatchOf(input).map[String]( - (m:regex.Match) => m.after()) - val appendString : String = prefixString.getOrElse(() => input) - //append appendString to the result of parseExpr() - - -def parseExpr(input:String):option.Option[AST] - val exp = - - ast.cast(exp, ast.types.string()) -*/ - -//%\{.*\} -// shouldn't need to account for {} in primitive Wyvern expressions?? but maybe list methods... -// ^maybe account for this later -// still need to account for escape characters \ No newline at end of file From 59764abeeb279ffe4f287c29b72c504edd3c73be Mon Sep 17 00:00:00 2001 From: Jenny Fish Date: Mon, 1 Jul 2019 14:12:21 -0400 Subject: [PATCH 22/27] adding recent progress --- examples/tsls/formatstring.wyv | 8 +++++++- examples/tsls/formatstringClient.wyv | 4 +--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 4d91480da..00e2b85f3 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -50,6 +50,7 @@ def getExprPos(input : String, lbraceCt : Int, inString : Boolean, pos : Int) : getExprPos(input, lbraceCt, inString, pos+1) // takes in a Wyvern expression as a string and returns a string wrapped in an AST +// floatPrecision -1 indicates that no float precision was specified // TODO: handle booleans def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : option.Option[AST] val exp : AST = ast.parseExpression(input, ctx) @@ -98,7 +99,7 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr makeParsedExpr(option.None[AST](), input) -//val prefixRegex : regex.Regex = regex("((\\%)|[^%])+") +//val prefixRegex : regex.Regex = regex("(\\\\|\\%|[^%])+") //alternative approach: if last char of prefixString is \ and next matches %, replace it with % and append to parseTSL //would need to replace \ in string even if using regex val prefixRegex : regex.Regex = regex("[^%]+") @@ -115,6 +116,11 @@ def parse(input : String, ctx : system.Context) : option.Option[AST] if (rest.get() == "") option.Some[AST](ast.string(prefixString.get())) else + // check here for prefix length, if ending in \ sub for % and recall parse + // but also account for \\% == \% + /* + use regex [^\\]+, once you encounter an escape, if next is \ or % do the sub + */ val g : ParsedExpr = parseExpr(rest.getOrElse(() => ""), ctx) if (g.expr().isDefined) val recursiveCall = parse(g.rest(), ctx) diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 79523f1f1..1217b287d 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -18,6 +18,4 @@ printf(~) val x = 3 printf(~) - %{x} %{4} %.5{1.6} %{1.7} - -stdout.print(String.ofFormattedFloat("%.2f", 1.5)) \ No newline at end of file + %{x} %{4} %.5{1.6} %{1.7} \ No newline at end of file From 79318701ef9b43c1f6317fffd689dda271ad86db Mon Sep 17 00:00:00 2001 From: Jenny Fish Date: Tue, 2 Jul 2019 14:56:38 -0400 Subject: [PATCH 23/27] parsing escaped % symbols working --- examples/tsls/formatstring.wyv | 57 ++++++++++++++++++++++++---- examples/tsls/formatstringClient.wyv | 6 +-- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 00e2b85f3..d2e58bf12 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -102,7 +102,8 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr //val prefixRegex : regex.Regex = regex("(\\\\|\\%|[^%])+") //alternative approach: if last char of prefixString is \ and next matches %, replace it with % and append to parseTSL //would need to replace \ in string even if using regex -val prefixRegex : regex.Regex = regex("[^%]+") +val prefixRegex : regex.Regex = regex("[^%]+(\\$)?") +//val prefixRegex : regex.Regex = regex("[^%]+") // recursively parses the input, returning option.None[AST] if parsing error def parse(input : String, ctx : system.Context) : option.Option[AST] @@ -121,15 +122,55 @@ def parse(input : String, ctx : system.Context) : option.Option[AST] /* use regex [^\\]+, once you encounter an escape, if next is \ or % do the sub */ - val g : ParsedExpr = parseExpr(rest.getOrElse(() => ""), ctx) - if (g.expr().isDefined) - val recursiveCall = parse(g.rest(), ctx) - if (recursiveCall.isDefined) - option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().get()}), "+", {recursiveCall.get()})) + val prefix : String = prefixString.get() + val suffix : String = rest.get() + if (prefix != "") + // checking for escaped characters + if (prefix.charAt(prefix.length()-1) == #"\\") + if (suffix.charAt(0) == #"%") + val recursiveCall : option.Option[AST] = parse(suffix.substring(1, suffix.length()), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.string(prefix.substring(0,prefix.length()-1) + "%"), "+", {recursiveCall.get()})) + else + option.None[AST]() + else + if (suffix.charAt(0) == #"\\") + val recursiveCall : option.Option[AST] = parse(suffix.substring(1, suffix.length()), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.string(prefix.substring(0,prefix.length()-1) + "%"), "+", {recursiveCall.get()})) + else + option.None[AST]() + else + val g : ParsedExpr = parseExpr(suffix, ctx) + if (g.expr().isDefined) + val recursiveCall : option.Option[AST] = parse(g.rest(), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().get()}), "+", {recursiveCall.get()})) + else + option.None[AST]() //recursive call gives None + else + option.None[AST]() //cannot parse expression else - option.None[AST]() //recursive call gives None + val g : ParsedExpr = parseExpr(suffix, ctx) + if (g.expr().isDefined) + val recursiveCall : option.Option[AST] = parse(g.rest(), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().get()}), "+", {recursiveCall.get()})) + else + option.None[AST]() //recursive call gives None + else + option.None[AST]() //cannot parse expression else - option.None[AST]() //cannot parse expression + // no need to append prefix + val g : ParsedExpr = parseExpr(rest.getOrElse(() => ""), ctx) + if (g.expr().isDefined) + val recursiveCall = parse(g.rest(), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(g.expr().get(), "+", {recursiveCall.get()})) + else + option.None[AST]() //recursive call gives None + else + option.None[AST]() //cannot parse expression else val g : ParsedExpr = parseExpr(rest.getOrElse(() => input), ctx) if (g.expr().isDefined) diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 1217b287d..114a9c0b2 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -3,19 +3,17 @@ require stdout import metadata tsls.formatstring import wyvern.String -//need to deal with leading whitespace for parsing - def printf(str:formatstring.FormatString):Unit stdout.print(str) stdout.println() //just a plain string printf(~) - Hello World! + Hello \\ \%World!\\n printf(~) Hello %{"string!"}%{" \"}{mm}"} and %{ "another string!" } val x = 3 printf(~) - %{x} %{4} %.5{1.6} %{1.7} \ No newline at end of file + %{x} %{4} %.5{1.6} %{1.7} From d727c6c1ac345380de179039436607f01ca7d1cb Mon Sep 17 00:00:00 2001 From: Jenny Fish Date: Wed, 3 Jul 2019 11:19:39 -0400 Subject: [PATCH 24/27] support for lone escaped \ characters --- examples/tsls/formatstring.wyv | 56 +++++++++++++--------------- examples/tsls/formatstringClient.wyv | 6 ++- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index d2e58bf12..a39a5ee0f 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -102,7 +102,7 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr //val prefixRegex : regex.Regex = regex("(\\\\|\\%|[^%])+") //alternative approach: if last char of prefixString is \ and next matches %, replace it with % and append to parseTSL //would need to replace \ in string even if using regex -val prefixRegex : regex.Regex = regex("[^%]+(\\$)?") +val prefixRegex : regex.Regex = regex("[^\\\\%]+") //val prefixRegex : regex.Regex = regex("[^%]+") // recursively parses the input, returning option.None[AST] if parsing error @@ -117,39 +117,31 @@ def parse(input : String, ctx : system.Context) : option.Option[AST] if (rest.get() == "") option.Some[AST](ast.string(prefixString.get())) else - // check here for prefix length, if ending in \ sub for % and recall parse - // but also account for \\% == \% - /* - use regex [^\\]+, once you encounter an escape, if next is \ or % do the sub - */ val prefix : String = prefixString.get() val suffix : String = rest.get() - if (prefix != "") + if (suffix.length() >= 2) // checking for escaped characters - if (prefix.charAt(prefix.length()-1) == #"\\") - if (suffix.charAt(0) == #"%") - val recursiveCall : option.Option[AST] = parse(suffix.substring(1, suffix.length()), ctx) + if (suffix.charAt(0) == #"\\") + if (suffix.charAt(1) == #"%") + val recursiveCall : option.Option[AST] = parse(suffix.substring(2, suffix.length()), ctx) if (recursiveCall.isDefined) - option.Some[AST](ast.call(ast.string(prefix.substring(0,prefix.length()-1) + "%"), "+", {recursiveCall.get()})) + option.Some[AST](ast.call(ast.string(prefix + "%"), "+", {recursiveCall.get()})) else option.None[AST]() else - if (suffix.charAt(0) == #"\\") - val recursiveCall : option.Option[AST] = parse(suffix.substring(1, suffix.length()), ctx) + if (suffix.charAt(1) == #"\\") + val recursiveCall : option.Option[AST] = parse(suffix.substring(2, suffix.length()), ctx) if (recursiveCall.isDefined) - option.Some[AST](ast.call(ast.string(prefix.substring(0,prefix.length()-1) + "%"), "+", {recursiveCall.get()})) + option.Some[AST](ast.call(ast.string(prefix + "\\"), "+", {recursiveCall.get()})) else option.None[AST]() else - val g : ParsedExpr = parseExpr(suffix, ctx) - if (g.expr().isDefined) - val recursiveCall : option.Option[AST] = parse(g.rest(), ctx) - if (recursiveCall.isDefined) - option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().get()}), "+", {recursiveCall.get()})) - else - option.None[AST]() //recursive call gives None + //would throw error but this could also be part of an escaped string sequence + val recursiveCall : option.Option[AST] = parse(suffix.substring(1, suffix.length()), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.string(prefix + "\\"), "+", {recursiveCall.get()})) else - option.None[AST]() //cannot parse expression + option.None[AST]() else val g : ParsedExpr = parseExpr(suffix, ctx) if (g.expr().isDefined) @@ -161,16 +153,18 @@ def parse(input : String, ctx : system.Context) : option.Option[AST] else option.None[AST]() //cannot parse expression else - // no need to append prefix - val g : ParsedExpr = parseExpr(rest.getOrElse(() => ""), ctx) - if (g.expr().isDefined) - val recursiveCall = parse(g.rest(), ctx) - if (recursiveCall.isDefined) - option.Some[AST](ast.call(g.expr().get(), "+", {recursiveCall.get()})) - else - option.None[AST]() //recursive call gives None + if (suffix == "") + option.Some[AST](ast.string(prefix)) else - option.None[AST]() //cannot parse expression + val g : ParsedExpr = parseExpr(rest.getOrElse(() => ""), ctx) + if (g.expr().isDefined) + val recursiveCall = parse(g.rest(), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.call(ast.string(prefixString.get()), "+", {g.expr().get()}), "+", {recursiveCall.get()})) + else + option.None[AST]() //recursive call gives None + else + option.None[AST]() //cannot parse expression else val g : ParsedExpr = parseExpr(rest.getOrElse(() => input), ctx) if (g.expr().isDefined) diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 114a9c0b2..5964891c9 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -9,11 +9,13 @@ def printf(str:formatstring.FormatString):Unit //just a plain string printf(~) - Hello \\ \%World!\\n - + Hello \\ \%World! + +/* printf(~) Hello %{"string!"}%{" \"}{mm}"} and %{ "another string!" } val x = 3 printf(~) %{x} %{4} %.5{1.6} %{1.7} +*/ \ No newline at end of file From 503925f54c2a5e716c8bec7b15b59c46a528425e Mon Sep 17 00:00:00 2001 From: Jenny Fish Date: Wed, 3 Jul 2019 12:31:04 -0400 Subject: [PATCH 25/27] ignores consecutive escaped characters --- examples/tsls/formatstring.wyv | 34 +++++++++++++++++++++++----- examples/tsls/formatstringClient.wyv | 6 ++--- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index a39a5ee0f..2e4a1a37d 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -99,9 +99,7 @@ def parseExpr(input : String, ctx : system.Context) : ParsedExpr makeParsedExpr(option.None[AST](), input) -//val prefixRegex : regex.Regex = regex("(\\\\|\\%|[^%])+") -//alternative approach: if last char of prefixString is \ and next matches %, replace it with % and append to parseTSL -//would need to replace \ in string even if using regex +//val prefixRegex : regex.Regex = regex("(\\\\\\\\|\\\\%|[^%])+") val prefixRegex : regex.Regex = regex("[^\\\\%]+") //val prefixRegex : regex.Regex = regex("[^%]+") @@ -174,10 +172,34 @@ def parse(input : String, ctx : system.Context) : option.Option[AST] else option.None[AST]() //recursive call gives None else - option.None[AST]() //cannot parse expression + // can only reach here if first character starts with \ + if (input.charAt(0) == #"\\") + if (input.length() >= 2) + if (input.charAt(1) == #"%") + val recursiveCall : option.Option[AST] = parse(input.substring(2,input.length()), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.string("%"), "+", {recursiveCall.get()})) + else + option.None[AST]() + else + if (input.charAt(1) == #"\\") + val recursiveCall : option.Option[AST] = parse(input.substring(2,input.length()), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.string("\\"), "+", {recursiveCall.get()})) + else + option.None[AST]() + else + val recursiveCall : option.Option[AST] = parse(input.substring(1,input.length()), ctx) + if (recursiveCall.isDefined) + option.Some[AST](ast.call(ast.string("\\"), "+", {recursiveCall.get()})) + else + option.None[AST]() + else + option.Some[AST](ast.string(input)) + else + option.None[AST]() //cannot parse expression + -//include \% + other characters, except pure % -//TODO: ignore escape character (\%) type FormatString = String metadata new def parseTSL(input : String, ctx : system.Context) : option.Option[AST] diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 5964891c9..54a10e4e6 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -9,13 +9,11 @@ def printf(str:formatstring.FormatString):Unit //just a plain string printf(~) - Hello \\ \%World! - -/* + Hello \\\%World!\n + printf(~) Hello %{"string!"}%{" \"}{mm}"} and %{ "another string!" } val x = 3 printf(~) %{x} %{4} %.5{1.6} %{1.7} -*/ \ No newline at end of file From 93464607b17f6d0092f9a5d3160d0bdfda38d1bf Mon Sep 17 00:00:00 2001 From: Jenny Fish Date: Wed, 3 Jul 2019 12:43:53 -0400 Subject: [PATCH 26/27] now supports printformatted booleans --- examples/tsls/formatstring.wyv | 5 ++++- examples/tsls/formatstringClient.wyv | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 2e4a1a37d..2038bed4f 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -69,7 +69,10 @@ def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : val format : String = "%." + String.ofInt(floatPrecision) + "f" option.Some[AST](ast.call(str, "ofFormattedFloat", {ast.string(format), exp})) else - option.None[AST]() + 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 + option.None[AST]() def parseExpr(input : String, ctx : system.Context) : ParsedExpr diff --git a/examples/tsls/formatstringClient.wyv b/examples/tsls/formatstringClient.wyv index 54a10e4e6..fcb4d12df 100644 --- a/examples/tsls/formatstringClient.wyv +++ b/examples/tsls/formatstringClient.wyv @@ -17,3 +17,5 @@ printf(~) val x = 3 printf(~) %{x} %{4} %.5{1.6} %{1.7} + +printf({%{1 == 2}%{true}}) \ No newline at end of file From 17d2b84f2fd98f74bc4a4adf315ec396399efe28 Mon Sep 17 00:00:00 2001 From: Jenny Fish Date: Wed, 3 Jul 2019 12:45:16 -0400 Subject: [PATCH 27/27] removed todo comment --- examples/tsls/formatstring.wyv | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/tsls/formatstring.wyv b/examples/tsls/formatstring.wyv index 2038bed4f..3f094fa97 100644 --- a/examples/tsls/formatstring.wyv +++ b/examples/tsls/formatstring.wyv @@ -51,7 +51,6 @@ def getExprPos(input : String, lbraceCt : Int, inString : Boolean, pos : Int) : // takes in a Wyvern expression as a string and returns a string wrapped in an AST // floatPrecision -1 indicates that no float precision was specified -// TODO: handle booleans def convStringAST(input : String, ctx : system.Context, floatPrecision : Int) : option.Option[AST] val exp : AST = ast.parseExpression(input, ctx) val ty : ast.Type = ast.getType(exp, ctx)