zeolite-lang 0.24.0.1 → 0.24.1.0
raw patch · 31 files changed
+2801/−450 lines, 31 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- CompilerCxx.Procedure: compileLazyInit :: (Ord c, Show c, CollectErrorsM m, CompilerContext c m [String] a) => DefinedMember c -> CompilerState a m ()
+ CompilerCxx.Procedure: compileLazyInit :: (Ord c, Show c, CollectErrorsM m, CompilerContext c m [String] a) => CategoryName -> DefinedMember c -> CompilerState a m ()
Files
- ChangeLog.md +36/−0
- base/category-source.hpp +6/−1
- example/highlighter/README.md +11/−3
- example/highlighter/main.0rx +129/−2
- example/highlighter/src/html.0rp +34/−0
- example/highlighter/src/html.0rx +206/−0
- example/highlighter/src/tokenizer.0rp +46/−32
- example/highlighter/src/tokenizer.0rx +79/−60
- example/highlighter/src/zeolite-parsing.0rp +94/−0
- example/highlighter/src/zeolite-parsing.0rx +467/−0
- example/highlighter/src/zeolite-tokenizing.0rp +94/−0
- example/highlighter/src/zeolite-tokenizing.0rx +688/−0
- example/highlighter/src/zeolite.0rp +0/−72
- example/highlighter/src/zeolite.0rx +0/−214
- example/highlighter/test/tokenizer.0rt +67/−0
- example/highlighter/test/zeolite-tokenizing.0rt +568/−0
- example/highlighter/test/zeolite.0rt +0/−49
- lib/testing/src/check-sequence.0rx +7/−1
- lib/testing/test/check-sequence.0rt +7/−1
- lib/util/helpers.0rp +9/−1
- lib/util/src/helpers.0rx +10/−1
- lib/util/test/helpers.0rt +6/−1
- src/CompilerCxx/Code.hs +3/−0
- src/CompilerCxx/CxxFiles.hs +1/−1
- src/CompilerCxx/Procedure.hs +23/−4
- src/Parser/Procedure.hs +19/−4
- src/Test/Procedure.hs +13/−0
- tests/conversions.0rt +9/−1
- tests/delegation.0rt +59/−0
- tests/modified-storage.0rt +108/−0
- zeolite-lang.cabal +2/−2
ChangeLog.md view
@@ -1,5 +1,41 @@ # Revision history for zeolite-lang +## 0.24.1.0 -- 2024-01-07++### Language++* **[fix]** Fixes parsing of `<` expressions when the left side is an explicit+ type conversion, e.g., `"123"?String < "456"`. Previously, the `<` was assumed+ to be the start of a param substitution.++ Note that this shouldn't ever be needed because `<` only supports built-in+ `concrete` types. (Any value with a union/intersection type convertible to one+ of those types already simplifies to that type, making the union/intersection+ superfluous.)++* **[new]** Adds the `<||` operator to use the left value unless it's `empty`.++```text+optional Int value <- empty+// value is preferred, but 123 is used because value is empty.+Int other <- value <|| 123+```++* **[behavior]** Adds source context to traces for crashes during initialization+ of `@category` members.++### Libraries++* **[fix]** Fixes `CheckSequence:using` (in `lib/testing`) when using an empty+ list.++* **[new]** Adds `AppendH` helpers to `lib/util`.++### Examples++* Adds `example/highlighter`, as both a code example and a highlighted-HTML+ generator for Zeolite source files.+ ## 0.24.0.1 -- 2023-12-10 ### Language
base/category-source.hpp view
@@ -190,7 +190,12 @@ BoxedValue::Present(result) \ ? TypeValue::Call(result, func, args) \ : ReturnTuple(count); \- })+ })++#define TYPE_VALUE_LEFT_UNLESS_EMPTY(left, right) ({ \+ const BoxedValue result = left; \+ BoxedValue::Present(result) ? result : (right); \+ }) class TypeValue { public:
example/highlighter/README.md view
@@ -1,10 +1,12 @@ # Zeolite Code Highlighter -This example is a work in progress and doesn't do anything yet. Eventually it-will generate syntax-highlighted HTML from Zeolite source files.+This example is a fully-functional syntax highlighter for Zeolite source files. -The module should still build and the tests should pass, however:+**You can see the highlighting of the code in this example+[here](https://ta0kira.github.io/zeolite/example/highlighter/).** +To build and test the example code:+ ```shell # This is just to locate the example code. Not for normal use! ZEOLITE_PATH=$(zeolite --get-path)@@ -14,4 +16,10 @@ # Run the unit tests. zeolite -p "$ZEOLITE_PATH" -t example/highlighter+```++To generate an HTML file for Zeolite source files (after building the example):++```shell+example/highlighter/ZeoliteHighlight "Your Page Title" your-source-file.0rx ```
example/highlighter/main.0rx view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2023 Kevin P. Barry+Copyright 2023-2024 Kevin P. Barry Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -21,7 +21,134 @@ } define ZeoliteHighlight {+ $ReadOnlyExcept[]$++ @category ZeoliteParseContext zeoliteSourceContext <- ZeoliteParseContext.new()+ .include<ZeoliteWhitespace>()+ .include<ZeoliteLineComment>()+ .include<ZeoliteBlockComment>()+ .include<ZeoliteUpperSymbol>()+ // Must be before ZeoliteLowerSymbol.+ .include<ZeoliteTestcase>()+ // Must be before ZeoliteOperator. (Due to : in labels.)+ .include<ZeoliteLowerSymbol>()+ .include<ZeoliteScopeQualifier>()+ // Must be before ZeoliteExtras. (Due to \ in escapes.)+ .include<ZeoliteNumber>()+ .include<ZeoliteOperator>()+ .include<ZeoliteParamName>()+ .include<ZeoliteStringLiteral>()+ .include<ZeoliteCharLiteral>()+ .include<ZeoliteBraceSection>()+ .include<ZeoliteParenSection>()+ .include<ZeoliteSquareSection>()+ .include<ZeolitePragma>()+ .include<ZeoliteExtras>()+ run () {- // TODO+ if (Argv.global().size() < 3) {+ \ BasicOutput.stderr()+ .append(Argv.global().readAt(0))+ .append(" [page title] [.0r? input files]\n")+ exit(1)+ }+ \ writeHtmlStart(title: Argv.global().readAt(1))+ traverse (Counter.builder().start(2).limit(Argv.global().size()).done() -> Int i) {+ String filename <- Argv.global().readAt(i)+ $ReadOnly[filename]$+ scoped {+ optional String content <- loadFile(filename: filename)+ } in if (`present` content) {+ \ processFile(filename: filename, content: `require` content)+ }+ }+ \ writeHtmlEnd()+ }++ @type writeHtmlStart (String title:) -> ()+ writeHtmlStart (title) {+ Formatted escapedTitle <- HtmlContent.escape(content: title)+ // TODO: Use HtmlContent to format this stuff?+ \ BasicOutput.stdout()+ .append("<!DOCTYPE html>")+ .append("<html>")+ .append("<head>")+ .append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />")+ .append("<meta name=\"Generator\" content=\"ZeoliteHighlighter\" />")+ .append("<title>").append(escapedTitle).append("</title>")+ .append("</head>")+ .append("<body>")+ .append("<h1>").append(escapedTitle).append("</h1>\n")+ \ writeCommand()+ \ BasicOutput.stdout().append("<hr>")+ }++ @type writeCommand () -> ()+ writeCommand () {+ \ BasicOutput.stdout()+ .append("<p>This file was generated by <a href=\"https://github.com/ta0kira/zeolite/tree/master/example/highlighter\" style=\"font-family:monospace;\">ZeoliteHighlight</a>.</p>")+ }++ @type writeHtmlEnd () -> ()+ writeHtmlEnd () {+ \ BasicOutput.stdout()+ .append("</body>")+ .append("</html>")+ }++ @type processFile (String filename:, String content:) -> ()+ processFile (filename, content) {+ TextStream input <- TextStream.new(content)+ $Hidden[content]$+ DefaultOrder<ZeoliteParsed> output <- defer+ \ StreamTokenizer:new(context: zeoliteSourceContext, tokenizer: zeoliteSourceContext.defaultTokenizer())+ .tokenizeAll(input, (output <- Vector<ZeoliteParsed>.new()))++ if (input.atEnd()) {+ \ BasicOutput.stderr()+ .append("Successfully parsed file ")+ .append(filename)+ .append(":\n")+ } else {+ \ BasicOutput.stderr()+ .append("Incomplete parse of file ")+ .append(filename)+ .append(" at line ")+ .append(input.currentLine())+ .append(" char ")+ .append(input.currentChar())+ .append(":\n")+ }++ ZeoliteHtmlFormatter formatter <- ZeoliteHtmlFormatter.new()+ \ BasicOutput.stdout().append("<h2>").append(HtmlContent.escape(content: filename)).append("</h2>\n")+ \ BasicOutput.stdout().append("<pre style=\"background:#fafffa;\">\n")+ traverse (output.defaultOrder() -> ZeoliteParsed parsed) {+ \ BasicOutput.stdout().append(parsed.formatWith(formatter))+ }+ \ BasicOutput.stdout().append("</pre>\n")+ }++ @type loadFile (String filename:) -> (optional String)+ loadFile (filename) {+ \ BasicOutput.stderr()+ .append("Reading file ")+ .append(filename)+ .append("...\n")+ scoped {+ RawFileReader input <- RawFileReader.open(filename)+ } cleanup {+ \ input.freeResource()+ } in if (`present` input.getFileError()) {+ \ BasicOutput.stderr()+ .append("Error reading file ")+ .append(filename)+ .append(": ")+ .append(`require` input.getFileError())+ .append("\n")+ return empty+ } else {+ return TextReader.readAll(input)+ } } }
+ example/highlighter/src/html.0rp view
@@ -0,0 +1,34 @@+/* -----------------------------------------------------------------------------+Copyright 2024 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$++concrete HtmlContent {+ refines Formatted++ @type section (String open:, String close:) -> ([Append<HtmlContent> & Build<HtmlContent>])+ @type tag (String name:) -> (HtmlContent)+ @type escape (String content:) -> (HtmlContent)+ @type raw (String verified:) -> (HtmlContent)+}++concrete ZeoliteHtmlFormatter {+ refines TokenFormatter<String, String, HtmlContent>++ @type new () -> (ZeoliteHtmlFormatter)+}
+ example/highlighter/src/html.0rx view
@@ -0,0 +1,206 @@+/* -----------------------------------------------------------------------------+Copyright 2024 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define HtmlContent {+ $ReadOnlyExcept[]$++ @category KVReader<Char, String> charEscapes <- HashedMap<Char, String>.new()+ .set('"', """)+ .set('#', "#")+ .set('$', "$")+ .set('%', "%")+ .set('&', "&")+ .set('\'', "'")+ .set('<', "<")+ .set('>', ">")++ @value String content++ formatted () {+ return content+ }++ section (open, close) {+ return delegate -> `HtmlSectionBuilder.new`+ }++ tag (name) {+ return #self{ String.builder()+ .append("<")+ .append(name)+ .append(" />")+ .build() }+ }++ escape (content) {+ [Append<Formatted> & Build<String>] builder <- String.builder()+ traverse (content.defaultOrder() -> Char c) {+ scoped {+ optional String replacement <- charEscapes.get(c)+ } in if (`present` replacement) {+ \ builder.append(`require` replacement)+ } else {+ \ builder.append(c)+ }+ }+ return #self{ builder.build() }+ }++ raw (verified) {+ return #self{ verified }+ }+}++define ZeoliteHtmlFormatter {+ $ReadOnlyExcept[]$++ @category KVReader<String, String> labelToStyle <- HashedMap<String, String>.new()+ .set("ZeoliteError", "color:red; font-weight:bold; text-decoration:underline;")++ .set("ZeoliteBlockComment", "color:DarkGray; font-style:italic;")+ .set("ZeoliteLineComment", "color:DarkGray; font-style:italic;")+ .set("ZeolitePragma", "color:DarkSalmon; font-style:italic;")+ .set("ZeolitePragmaName", "color:DarkSalmon; font-style:italic; font-weight:bold;")+ .set("ZeolitePragmaArg", "color:DarkSalmon; font-style:normal;")++ .set("ZeoliteArgLabel", "color:green;")+ .set("ZeoliteAssignment", "color:blue;")+ .set("ZeoliteDiscard", "color:blue;")+ .set("ZeoliteFunctionCall", "color:blue;")+ .set("ZeoliteTick", "color:blue;")++ .set("ZeoliteBuiltinCategory", "color:DarkBlue; font-weight:bold;")+ .set("ZeoliteBuiltinConstant", "color:DarkCyan; font-weight:bold;")+ .set("ZeoliteIgnore", "color:DarkCyan; font-weight:bold;")+ .set("ZeoliteBuiltinFunction", "color:DarkGreen; font-weight:bold;")+ .set("ZeoliteBuiltinParam", "color:DarkBlue; font-weight:bold;")+ .set("ZeoliteCategoryName", "color:DarkBlue;")+ .set("ZeoliteParamName", "color:DarkBlue;")++ .set("ZeoliteCharLiteral", "color:DarkViolet;")+ .set("ZeoliteStringLiteral", "color:DeepPink;")+ .set("ZeoliteEscapedChar", "color:DarkMagenta;")+ .set("ZeoliteOperator", "color:BlueViolet;")++ .set("ZeoliteNumber", "color:GoldenRod;")+ .set("ZeoliteEscapedNumber", "color:DarkMagenta;")++ .set("ZeoliteContainKeyword", "color:Indigo; font-weight:bold;")+ .set("ZeoliteScopeQualifier", "color:Indigo; font-weight:bold;")+ .set("ZeoliteTypeKeyword", "color:Crimson;")+ .set("ZeoliteControlKeyword", "font-weight:bold;")+ .set("ZeoliteStorageKeyword", "font-style:italic;")++ .set("ZeoliteTestcase", "background:PaleTurquoise;")+ .set("ZeoliteTestcaseKeyword", "color:green;")++ @category SetReader<String> labelsUsingDefault <- HashedSet<String>.new()+ .append("ZeoliteBraceSection")+ .append("ZeoliteCloseDelim")+ .append("ZeoliteComma")+ .append("ZeoliteDoubleQuote")+ .append("ZeoliteFunctionOrVariableName")+ .append("ZeoliteOpenDelim")+ .append("ZeoliteParenSection")+ .append("ZeolitePragmaArgClose")+ .append("ZeolitePragmaArgOpen")+ .append("ZeolitePragmaDelim")+ .append("ZeoliteQuotedChars")+ .append("ZeoliteSingleQuote")+ .append("ZeoliteSquareSection")+ .append("ZeoliteTestcaseSection")+ .append("ZeoliteWhitespace")++ new () {+ return delegate -> #self+ }++ formatLeaf (label, value) {+ scoped {+ optional String style <- getStyle(label)+ } in if (`present` style) {+ return HtmlContent+ .section(open: buildSpanTag(`require` style), close: "</span>")+ .append(HtmlContent.escape(content: value))+ .build()+ } else {+ return HtmlContent.escape(content: value)+ }+ }++ createSection (label) {+ scoped {+ optional String style <- getStyle(label)+ } in if (`present` style) {+ return HtmlContent.section(open: buildSpanTag(`require` style), close: "</span>")+ } else {+ return HtmlContent.section(open: "", close: "")+ }+ }++ @type getStyle (String) -> (optional String)+ getStyle (label) (style) {+ style <- labelToStyle.get(label)+ if (! `present` style && ! labelsUsingDefault.member(label)) {+ \ BasicOutput.stderr()+ .append("HTML style for label \"")+ .append(label)+ .append("\" not found!\n")+ }+ }++ @type buildSpanTag (String) -> (String)+ buildSpanTag (style) {+ return String.builder()+ .append("<span style=\"")+ .append(style)+ .append("\">")+ .build()+ }+}++concrete HtmlSectionBuilder {+ refines Append<HtmlContent>+ refines Build<HtmlContent>++ @type new (String open:, String close:) -> (HtmlSectionBuilder)+}++define HtmlSectionBuilder {+ $ReadOnlyExcept[]$++ @value [Append<Formatted> & Build<String>] content+ @value String close++ new (open, close) {+ return #self{ String.builder().append(open), close }+ }++ append (newContent) {+ \ delegate -> `content.append`+ return self+ }++ build () {+ // We need a new builder so that we don't append close multiple times.+ return HtmlContent.raw(verified: String.builder()+ .append(content.build())+ .append(close)+ .build())+ }+}
example/highlighter/src/tokenizer.0rp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2023 Kevin P. Barry+Copyright 2023-2024 Kevin P. Barry Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -18,55 +18,69 @@ $ModuleOnly$ -@value interface TextToken<|#l> {- immutable+// Tokenizes with context #c and generates values of type #v.+@value interface Tokenizer<#c|#v> {+ tokenize (TextStream, #c) -> (optional #v)+} - exportWith<#f> (TextFormatter<#l, #f>) -> (#f)+// Supports generic formatting based on label type #l and content type #x.+@value interface FormattedToken<|#l, #x> {+ formatWith<#f> (TokenFormatter<#l, #x, #f>) -> (#f) } -@value interface TextFormatter<#l|#f> {- format (String content:, #l label:) -> (#f)+// Generates #f values by formatting with label type #l and content type #x.+@value interface TokenFormatter<#l, #x|#f|> {+ formatLeaf (#l label:, #x value:) -> (#f)+ createSection (#l label:) -> ([Append<#f> & Build<#f>]) } +// Input adapter for parsing text input. concrete TextStream { @type new (String) -> (TextStream) - @value current () -> (optional Char)- @value forward () -> (TextStream)- @value reset () -> (TextStream)- @value take () -> (String)- @value preview () -> (String)- @value atEnd () -> (Bool)-}+ @value current () -> (optional Char)+ @value tokenSize () -> (Int)+ @value preview () -> (String)+ @value currentLine () -> (Int)+ @value currentChar () -> (Int)+ @value atEnd () -> (Bool) -@value interface Tokenizer<#t, #v> {- tokenize (TextStream, KVReader<#t, Tokenizer<#t, #v>>) -> (optional #v, optional #t, Int)+ @value forward () -> (TextStream)+ @value reset () -> (TextStream)+ @value take () -> (String) } -concrete MultiTokenizer<#t, #v> {- refines Tokenizer<#t, #v>-- visibility Tokenizer<#t, #v>-- @type new () -> (#self)+// Tokenizes all content in a TextStream.+concrete StreamTokenizer<#c|#v> {+ @category new<#c, #v> (#c context:, Tokenizer<#c, #v> tokenizer:) -> (StreamTokenizer<#c, #v>) - @value include (#t) -> (#self)+ @value tokenizeAll (TextStream, Append<#v>) -> (#self) } -concrete StreamTokenizer<#t, #v> {- @category new<#t, #v>- #v requires TextToken<any>- (KVReader<#t, Tokenizer<#t, #v>> tokenizers:, Tokenizer<#t, #v> default:) -> (StreamTokenizer<#t, #v>)+// Serializes parsed data without formatting.+concrete UnformattedFormatter {+ refines TokenFormatter<any, Formatted, String> - @value tokenizeAll (TextStream, Append<#v>) -> (#self)+ @type new () -> (#self) } +// Helpers for reasoning about Char values. concrete CharType {- @type lower (Char) -> (Bool)- @type upper (Char) -> (Bool)- @type digit (Char) -> (Bool)- @type alphaNum (Char) -> (Bool)+ @type lower (Char) -> (Bool)+ @type upper (Char) -> (Bool)+ @type digit (Char) -> (Bool)+ @type binDigit (Char) -> (Bool)+ @type octDigit (Char) -> (Bool)+ @type hexDigit (Char) -> (Bool)+ @type alphaNum (Char) -> (Bool) @type whitespace (Char) -> (Bool)- @type oneOf (Char, String) -> (Bool)++ @type binChars () -> (SetReader<Char>)+ @type octChars () -> (SetReader<Char>)+ @type decChars () -> (SetReader<Char>)+ @type hexChars () -> (SetReader<Char>)++ @type oneOf (Char, DefaultOrder<Char>) -> (Bool)+ @type escapeBreaks (DefaultOrder<Char>) -> (String) }
example/highlighter/src/tokenizer.0rx view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2023 Kevin P. Barry+Copyright 2023-2024 Kevin P. Barry Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -22,9 +22,11 @@ @value String source @value Int tokenStart @value Int tokenEnd+ @value Int currentLine+ @value Int currentChar new (source) {- return TextStream{ source, 0, 0 }+ return TextStream{ source, 0, 0, 1, 1 } } current () (char) {@@ -34,6 +36,10 @@ } } + tokenSize () {+ return tokenEnd-tokenStart+ }+ forward () { if (tokenEnd < source.size()) { tokenEnd <- tokenEnd+1@@ -48,72 +54,66 @@ take () { cleanup {+ while (tokenStart < tokenEnd) {+ if (source.readAt(tokenStart) == '\n') {+ currentChar <- 1+ currentLine <- currentLine+1+ } else {+ currentChar <- currentChar+1+ }+ } update {+ tokenStart <- tokenStart+1+ } tokenStart <- tokenEnd } in return preview() } preview () {- return source.subSequence(tokenStart, tokenEnd-tokenStart)+ return source.subSequence(tokenStart, tokenSize()) } + currentLine () {+ return currentLine+ }++ currentChar () {+ return currentChar+ }+ atEnd () { return tokenStart >= source.size() } } -define MultiTokenizer {- $ReadOnlyExcept[]$-- @value [DefaultOrder<#t> & Append<#t>] types-+define UnformattedFormatter { new () {- return #self{ Vector<#t>.new() }+ return delegate -> #self } - include (type) {- \ types.append(type)- return self+ formatLeaf (_, value) {+ return value.formatted() } - tokenize (input, tokenizers) (token, nextType, popCount) {- token <- empty- nextType <- empty- popCount <- 0- traverse (types.defaultOrder() -> #t type) {- scoped {- optional Tokenizer<#t, #v> tokenizer <- tokenizers.get(type)- } in if (`present` tokenizer) {- token, nextType, popCount <- require(tokenizer).tokenize(input, tokenizers)- }- } update {- if (`present` token) {- break- }- }+ createSection (_) {+ return String.builder() } } define StreamTokenizer { $ReadOnlyExcept[]$ - @value KVReader<#t, Tokenizer<#t, #v>> tokenizers- @value Tokenizer<#t, #v> defaultTokenizer+ @value #c context+ @value Tokenizer<#c, #v> tokenizer - new (tokenizers, defaultTokenizer) {- return delegate -> StreamTokenizer<#t, #v>+ new (context, tokenizer) {+ return delegate -> StreamTokenizer<#c, #v> } tokenizeAll (input, output) { scoped {- Stack<Tokenizer<#t, #v>> tokenizerStack <- Vector<Tokenizer<#t, #v>>.new().push(defaultTokenizer)- Tokenizer<#t, #v> tokenizer <- defaultTokenizer optional #v token <- empty- optional #t nextType <- empty- Int popCount <- 0 } in while (!input.atEnd()) {- $Hidden[tokenizerStack,defaultTokenizer]$- $ReadOnly[tokenizer]$- token, nextType, popCount <- tokenizer.tokenize(input, tokenizers)+ token <- input `tokenizer.tokenize` context } update { // Process new token. if (`present` token) {@@ -122,26 +122,6 @@ // Tokenizing failed => let the caller deal with the rest of the input. break }- // Pop tokenizers.- while (popCount > 0 && tokenizerStack.size() > 0) {- tokenizer <- tokenizerStack.pop()- } update {- popCount <- popCount-1- }- if (popCount > 0) {- tokenizer <- defaultTokenizer- popCount <- 0- }- // Set new tokenizer.- scoped {- optional Tokenizer<#t, #v> nextTokenizer <- empty- if (`present` nextType) {- nextTokenizer <- tokenizers.get(`require` nextType)- }- } in if (`present` nextTokenizer) {- \ tokenizerStack.push(tokenizer)- tokenizer <- `require` nextTokenizer- } } return self }@@ -155,7 +135,18 @@ .set('\n', "\\n") .set('\r', "\\r") .set('\"', "\\\"")+ .set('\\', "\\\\") + @category SetReader<Char> binChars <- HashedSet<Char>.new() `AppendH:from` "01".defaultOrder()++ @category SetReader<Char> octChars <- HashedSet<Char>.new() `AppendH:from` "01234567".defaultOrder()++ @category SetReader<Char> decChars <- HashedSet<Char>.new() `AppendH:from` "0123456789".defaultOrder()++ @category SetReader<Char> hexChars <- HashedSet<Char>.new() `AppendH:from` "0123456789abcdefABCDEF".defaultOrder()++ @category SetReader<Char> whitespaceChars <- HashedSet<Char>.new() `AppendH:from` "\n\t\r ".defaultOrder()+ lower (c) { return c >= 'a' && c <= 'z' }@@ -165,15 +156,43 @@ } digit (c) {- return c >= '0' && c <= '9'+ return `decChars.member` c } + binDigit (c) {+ return `binChars.member` c+ }++ octDigit (c) {+ return `octChars.member` c+ }++ hexDigit (c) {+ return `hexChars.member` c+ }+ alphaNum (c) { return lower(c) || upper(c) || digit(c) } whitespace (c) {- return c `oneOf` "\n\t\r "+ return `whitespaceChars.member` c+ }++ binChars () {+ return binChars+ }++ octChars () {+ return octChars+ }++ decChars () {+ return decChars+ }++ hexChars () {+ return hexChars } oneOf (c, allowed) (match) {
+ example/highlighter/src/zeolite-parsing.0rp view
@@ -0,0 +1,94 @@+/* -----------------------------------------------------------------------------+Copyright 2023 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$++// Hierarchical parsed data from Zeolite source files.+concrete ZeoliteParsed {+ refines Formatted+ refines FormattedToken<String, String>+ refines TestCompare<ZeoliteParsed>++ visibility Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ @type leaf (String label:, String content:) -> (ZeoliteParsed)+ @type section (String label:, DefaultOrder<ZeoliteParsed>) -> (ZeoliteParsed)+}++// Singleton tokenizer with a name.+@type interface NamedTokenizer {+ tokenizer () -> (Tokenizer<ZeoliteParseContext, ZeoliteParsed>)+ tokenizerName () -> (String)+}++// Context used when parsing Zeolite source files.+concrete ZeoliteParseContext {+ refines KVReader<String, Tokenizer<ZeoliteParseContext, ZeoliteParsed>>++ @type new () -> (ZeoliteParseContext)++ @value defaultTokenizer () -> (Tokenizer<ZeoliteParseContext, ZeoliteParsed>)++ @value include<#t>+ #t defines NamedTokenizer+ () -> (ZeoliteParseContext)+}++// Delegate to a tokenizer by name.+concrete UseNamedTokenizer {+ @type new<#t>+ #t defines NamedTokenizer+ () -> (Tokenizer<ZeoliteParseContext, ZeoliteParsed>)+}++// Attempts multiple tokenizers in order.+concrete TokenAlternatives {+ refines Append<Tokenizer<ZeoliteParseContext, ZeoliteParsed>>+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ @type new () -> (TokenAlternatives)+}++// Zeolite symbol parsing.+concrete ZeoliteSymbol {+ // Parses symbol and returns the stream forwarded to the end of the symbol.++ @type parseUpperSymbol (TextStream) -> (optional TextStream)+ @type parseLowerSymbol (TextStream) -> (optional TextStream)+ @type parseParamName (TextStream) -> (optional TextStream)+ @type parseScopeQualifier (TextStream) -> (optional TextStream)+ @type parseOperatorSymbols (TextStream) -> (optional TextStream)++ // Caller must check for \ first. Stream isn't reset!+ @type parseEscapedChar (TextStream) -> (optional TextStream)++ // Returns a type label if the token is in the given set.++ @type tryControlKeyword (String) -> (optional String)+ @type tryTypeKeyword (String) -> (optional String)+ @type tryContainKeyword (String) -> (optional String)+ @type tryStorageKeyword (String) -> (optional String)+ @type tryScopeQualifier (String) -> (optional String)+ @type tryTestcaseKeyword (String) -> (optional String)+ @type tryBuiltinCategory (String) -> (optional String)+ @type tryBuiltinFunction (String) -> (optional String)+ @type tryBuiltinConstant (String) -> (optional String)+ @type tryBuiltinParam (String) -> (optional String)+ @type tryAssignment (String) -> (optional String)+ @type tryFunctionCall (String) -> (optional String)+}
+ example/highlighter/src/zeolite-parsing.0rx view
@@ -0,0 +1,467 @@+/* -----------------------------------------------------------------------------+Copyright 2023-2024 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define ZeoliteParsed {+ $ReadOnlyExcept[]$++ @value String label+ @value optional String content+ @value DefaultOrder<ZeoliteParsed> subsections++ leaf (label, content) {+ return #self{ label, content, AlwaysEmpty.default() }+ }++ section (label, subsections) {+ return #self{ label, empty, subsections }+ }++ testCompare (actual, report) {+ \ MultiChecker.new(report)+ .check(+ title: "label",+ actual.label(),+ CheckValue:equals(label))+ &.tryCheck(+ title: "content",+ actual.content(),+ CheckValue:equals(content))+ &.tryCheck(+ title: "subsections",+ actual.subsections(),+ CheckSequence:matches(CheckSequence:using(subsections)))+ }++ formatted () {+ [Append<Formatted> & Build<String>] builder <- String.builder()+ .append("ZeoliteParsed{label:")+ .append(label)+ if (`present` content) {+ \ builder+ .append(",content:\"")+ .append(CharType.escapeBreaks(`require` content))+ .append("\"")+ } else {+ traverse (subsections.defaultOrder() -> Formatted subsection) {+ \ builder+ .append(",")+ .append(subsection)+ }+ }+ \ builder.append("}")+ return builder.build()+ }++ formatWith (formatter) {+ if (`present` content) {+ return formatter.formatLeaf(label: label, value: `require` content)+ } else {+ [Append<#f> & Build<#f>] builder <- formatter.createSection(label: label)+ traverse (subsections.defaultOrder() -> ZeoliteParsed subsection) {+ \ builder.append(subsection.formatWith(formatter))+ }+ return builder.build()+ }+ }++ @value label () -> (String)+ label () {+ return label+ }++ @value content () -> (optional String)+ content () {+ return content+ }++ @value subsections () -> (DefaultOrder<ZeoliteParsed>)+ subsections () {+ return subsections+ }+}++define UseNamedTokenizer {+ $ReadOnlyExcept[]$++ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ @value String name++ new () {+ return #self{ #t.tokenizerName() }+ }++ tokenize (input, context) {+ return delegate -> `context.get(name)&.tokenize`+ }+}++define TokenAlternatives {+ $ReadOnlyExcept[]$++ @value Vector<Tokenizer<ZeoliteParseContext, ZeoliteParsed>> tokenizers++ new () {+ return #self{ Vector<Tokenizer<ZeoliteParseContext, ZeoliteParsed>>.new() }+ }++ append (tokenizer) {+ \ delegate -> `tokenizers.append`+ return self+ }++ tokenize (input, context) (token) {+ token <- empty+ traverse (tokenizers.defaultOrder() -> Tokenizer<ZeoliteParseContext, ZeoliteParsed> tokenizer) {+ token <- tokenizer.tokenize(input, context)+ } update {+ if (`present` token) {+ break+ }+ }+ }+}++define ZeoliteParseContext {+ $ReadOnlyExcept[]$++ @value TokenAlternatives defaultTokenizer+ @value HashedMap<String, Tokenizer<ZeoliteParseContext, ZeoliteParsed>> tokenizers++ new () {+ return #self{ TokenAlternatives.new(), HashedMap<String, Tokenizer<ZeoliteParseContext, ZeoliteParsed>>.new() }+ }++ defaultTokenizer () {+ return defaultTokenizer+ }++ include () {+ \ #t.tokenizerName() `tokenizers.set` #t.tokenizer()+ \ defaultTokenizer.append(UseNamedTokenizer.new<#t>())+ return self+ }++ get (k) {+ return delegate -> `tokenizers.get`+ }+}++define ZeoliteSymbol {+ $ReadOnlyExcept[]$++ @category SetReader<String> controlKeywords <- HashedSet<String>.new()+ .append("break")+ .append("cleanup")+ .append("continue")+ .append("defer")+ .append("delegate")+ .append("elif")+ .append("else")+ .append("if")+ .append("in")+ .append("return")+ .append("scoped")+ .append("traverse")+ .append("update")+ .append("while")++ @category SetReader<String> typeKeywords <- HashedSet<String>.new()+ .append("allows")+ .append("defines")+ .append("immutable")+ .append("refines")+ .append("requires")+ .append("visibility")++ @category SetReader<String> containKeywords <- HashedSet<String>.new()+ .append("concrete")+ .append("interface")+ .append("define")+ .append("testcase")++ @category SetReader<String> storageKeywords <- HashedSet<String>.new()+ .append("optional")+ .append("weak")++ @category SetReader<String> scopeQualifiers <- HashedSet<String>.new()+ .append("@category")+ .append("@type")+ .append("@value")+ .append("unittest")++ @category SetReader<String> testcaseKeywords <- HashedSet<String>.new()+ .append("args")+ .append("compiler")+ .append("compiles")+ .append("error")+ .append("exclude")+ .append("failure")+ .append("require")+ .append("stderr")+ .append("stdout")+ .append("success")+ .append("timeout")++ @category SetReader<String> builtinCategries <- HashedSet<String>.new()+ .append("all")+ .append("any")+ .append("Append")+ .append("AsBool")+ .append("AsChar")+ .append("AsFloat")+ .append("AsInt")+ .append("Bool")+ .append("Bounded")+ .append("Build")+ .append("Char")+ .append("CharBuffer")+ .append("Container")+ .append("Default")+ .append("Duplicate")+ .append("Equals")+ .append("Float")+ .append("Formatted")+ .append("Hashed")+ .append("Identifier")+ .append("Int")+ .append("LessThan")+ .append("Order")+ .append("Pointer")+ .append("ReadAt")+ .append("String")+ .append("SubSequence")+ .append("Testcase")+ .append("WriteAt")++ @category SetReader<String> builtinFunctions <- HashedSet<String>.new()+ .append("exit")+ .append("fail")+ .append("identify")+ .append("present")+ .append("reduce")+ .append("require")+ .append("strong")+ .append("typename")++ @category SetReader<String> builtinConstants <- HashedSet<String>.new()+ .append("empty")+ .append("false")+ .append("true")+ .append("self")++ @category SetReader<String> builtinParams <- HashedSet<String>.new()+ .append("#self")++ @category SetReader<String> assignmentOperators <- HashedSet<String>.new()+ .append("<-")+ .append("<-|")+ .append("<->")+ .append("->")++ @category SetReader<String> functionCallOperators <- HashedSet<String>.new()+ .append(":")+ .append(".")+ .append("&.")+ .append("?")++ @category SetReader<Char> operatorChars <- HashedSet<Char>.new() `AppendH:from` ":.!%^&*-+|<>?=".defaultOrder()++ @category SetReader<Char> escapedChars <- HashedSet<Char>.new() `AppendH:from` "'\"?\\abfnrtv".defaultOrder()++ parseUpperSymbol (input) {+ \ input.reset()+ optional Char current <- input.current()+ if (! `present` current || !CharType.upper(`require` current)) {+ return empty+ }+ while (`present` current && CharType.alphaNum(`require` current)) {+ current <- input.forward().current()+ }+ return input+ }++ parseLowerSymbol (input) {+ \ input.reset()+ optional Char current <- input.current()+ if (! `present` current || !CharType.lower(`require` current)) {+ return empty+ }+ while (`present` current && CharType.alphaNum(`require` current)) {+ current <- input.forward().current()+ }+ return input+ }++ parseParamName (input) {+ \ input.reset()+ optional Char current <- input.current()+ if (! `present` current || `require` current != '#') {+ return empty+ }+ current <- input.forward().current()+ if (! `present` current || !CharType.lower(`require` current)) {+ return empty+ }+ while (`present` current && CharType.alphaNum(`require` current)) {+ current <- input.forward().current()+ }+ return input+ }++ parseScopeQualifier (input) {+ \ input.reset()+ optional Char current <- input.current()+ if (! `present` current || `require` current != '@') {+ return empty+ }+ current <- input.forward().current()+ while (`present` current && CharType.alphaNum(`require` current)) {+ current <- input.forward().current()+ }+ return input+ }++ parseOperatorSymbols (input) (output) {+ \ input.reset()+ output <- empty+ optional Char current <- input.current()+ while (`present` current && `operatorChars.member` `require` current) {+ current <- input.forward().current()+ output <- input+ }+ }++ parseEscapedChar (input) {+ scoped {+ optional Char current <- input.current()+ } in if (! `present` current) {+ return empty+ } elif (`require` current == 'x') {+ // Hex escaped char.+ traverse (Counter.zeroIndexed(2) -> _) {+ current <- input.forward().current()+ if (! `present` current || !CharType.hexDigit(`require` current)) {+ return empty+ }+ }+ \ input.forward()+ } elif (CharType.octDigit(`require` current)) {+ // Oct escaped char.+ traverse (Counter.zeroIndexed(2) -> _) {+ current <- input.forward().current()+ if (! `present` current || !CharType.octDigit(`require` current)) {+ return empty+ }+ }+ \ input.forward()+ } elif (`escapedChars.member` `require` current) {+ // Single escaped char.+ \ input.forward()+ } else {+ // Invalid escaped char.+ \ input.forward()+ return empty+ }+ return input+ }++ tryControlKeyword (value) (type) {+ type <- empty+ if (`controlKeywords.member` value) {+ type <- "ZeoliteControlKeyword"+ }+ }++ tryTypeKeyword (value) (type) {+ type <- empty+ if (`typeKeywords.member` value) {+ type <- "ZeoliteTypeKeyword"+ }+ }++ tryContainKeyword (value) (type) {+ type <- empty+ if (`containKeywords.member` value) {+ type <- "ZeoliteContainKeyword"+ }+ }++ tryStorageKeyword (value) (type) {+ type <- empty+ if (`storageKeywords.member` value) {+ type <- "ZeoliteStorageKeyword"+ }+ }++ tryScopeQualifier (value) (type) {+ type <- empty+ if (`scopeQualifiers.member` value) {+ type <- "ZeoliteScopeQualifier"+ }+ }++ tryTestcaseKeyword (value) (type) {+ type <- empty+ if (`testcaseKeywords.member` value) {+ type <- "ZeoliteTestcaseKeyword"+ }+ }++ tryBuiltinCategory (value) (type) {+ type <- empty+ if (`builtinCategries.member` value) {+ type <- "ZeoliteBuiltinCategory"+ }+ }++ tryBuiltinFunction (value) (type) {+ type <- empty+ if (`builtinFunctions.member` value) {+ type <- "ZeoliteBuiltinFunction"+ }+ }++ tryBuiltinConstant (value) (type) {+ type <- empty+ if (`builtinConstants.member` value) {+ type <- "ZeoliteBuiltinConstant"+ }+ }++ tryAssignment (value) (type) {+ type <- empty+ if (`assignmentOperators.member` value) {+ type <- "ZeoliteAssignment"+ }+ }++ tryBuiltinParam (value) (type) {+ type <- empty+ if (`builtinParams.member` value) {+ type <- "ZeoliteBuiltinParam"+ }+ }++ tryFunctionCall (value) (type) {+ type <- empty+ if (`functionCallOperators.member` value) {+ type <- "ZeoliteFunctionCall"+ }+ }+}
+ example/highlighter/src/zeolite-tokenizing.0rp view
@@ -0,0 +1,94 @@+/* -----------------------------------------------------------------------------+Copyright 2023-2024 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$++concrete ZeoliteWhitespace {+ defines NamedTokenizer+}++concrete ZeoliteLineComment {+ defines NamedTokenizer+}++concrete ZeoliteBlockComment {+ defines NamedTokenizer+}++concrete ZeoliteUpperSymbol {+ defines NamedTokenizer+}++concrete ZeoliteLowerSymbol {+ defines NamedTokenizer+}++concrete ZeoliteScopeQualifier {+ defines NamedTokenizer+}++concrete ZeoliteOperator {+ defines NamedTokenizer+}++concrete ZeoliteParamName {+ defines NamedTokenizer+}++concrete ZeoliteStringLiteral {+ defines NamedTokenizer+}++concrete ZeoliteCharLiteral {+ defines NamedTokenizer+}++concrete ZeoliteNumber {+ defines NamedTokenizer+}++concrete ZeoliteBraceSection {+ defines NamedTokenizer+}++concrete ZeoliteParenSection {+ defines NamedTokenizer+}++concrete ZeoliteSquareSection {+ defines NamedTokenizer+}++concrete ZeolitePragma {+ defines NamedTokenizer+}++concrete ZeoliteExtras {+ defines NamedTokenizer+}++// NOTE: Uses the following if available:+// - ZeoliteStringLiteral (required)+// - ZeoliteLowerSymbol (required)+// - ZeoliteSquareSection+// - ZeoliteUpperSymbol+// - ZeoliteNumber+// - ZeoliteOperator+concrete ZeoliteTestcase {+ defines NamedTokenizer+}
+ example/highlighter/src/zeolite-tokenizing.0rx view
@@ -0,0 +1,688 @@+/* -----------------------------------------------------------------------------+Copyright 2023-2024 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define ZeoliteWhitespace {+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) (token) {+ \ input.reset()+ token <- empty+ optional Char current <- input.current()+ while (`present` current && CharType.whitespace(`require` current)) {+ current <- input.forward().current()+ }+ scoped {+ String content <- input.take()+ } in if (content.size() > 0) {+ token <- ZeoliteParsed.leaf(label: tokenizerName(), content: content)+ }+ }+}++define ZeoliteLineComment {+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) (token) {+ \ input.reset()+ token <- empty+ \ input.forward().forward()+ if (input.preview() != "//") {+ return _+ }+ optional Char current <- input.current()+ while (`present` current && !CharType.oneOf(`require` current, "\r\n")) {+ current <- input.forward().current()+ }+ token <- ZeoliteParsed.leaf(label: tokenizerName(), content: input.take())+ }+}++define ZeoliteBlockComment {+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) (token) {+ \ input.reset()+ token <- empty+ \ input.forward().forward()+ if (input.preview() != "/*") {+ return _+ }+ scoped {+ optional Char current <- empty+ Bool starFound <- false+ } in while (`present` (current <- input.current())) {+ if (starFound && `require` current == '/') {+ \ input.forward()+ break+ } else {+ starFound <- `require` current == '*'+ }+ } update {+ \ input.forward()+ }+ token <- ZeoliteParsed.leaf(label: tokenizerName(), content: input.take())+ }+}++define ZeoliteUpperSymbol {+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, _) {+ optional String content <- ZeoliteSymbol.parseUpperSymbol(input)&.take()+ if (! `present` content) {+ return empty+ } else {+ String type <- ZeoliteSymbol.tryBuiltinCategory(`require` content)+ <|| "ZeoliteCategoryName"+ return ZeoliteParsed.leaf(label: type, content: `require` content)+ }+ }+}++define ZeoliteLowerSymbol {+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, _) {+ optional String content <- ZeoliteSymbol.parseLowerSymbol(input)&.preview()+ if (! `present` content) {+ return empty+ } else {+ String type <- ZeoliteSymbol.tryControlKeyword(`require` content)+ <|| ZeoliteSymbol.tryTypeKeyword(`require` content)+ <|| ZeoliteSymbol.tryContainKeyword(`require` content)+ <|| ZeoliteSymbol.tryStorageKeyword(`require` content)+ <|| ZeoliteSymbol.tryScopeQualifier(`require` content)+ <|| ZeoliteSymbol.tryBuiltinCategory(`require` content)+ <|| ZeoliteSymbol.tryBuiltinFunction(`require` content)+ <|| ZeoliteSymbol.tryBuiltinConstant(`require` content)+ <|| tryLabel(input)+ <|| "ZeoliteFunctionOrVariableName"+ $Hidden[content]$+ return ZeoliteParsed.leaf(label: type, content: input.take())+ }+ }++ @type tryLabel (TextStream) -> (optional String)+ tryLabel (input) {+ if (`present` input.current() && `require` input.current() == ':') {+ \ input.forward()+ return "ZeoliteArgLabel"+ } else {+ return empty+ }+ }+}++define ZeoliteScopeQualifier {+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, _) {+ optional String content <- ZeoliteSymbol.parseScopeQualifier(input)&.take()+ if (! `present` content) {+ return empty+ } else {+ String type <- ZeoliteSymbol.tryScopeQualifier(`require` content)+ <|| "ZeoliteError"+ return ZeoliteParsed.leaf(label: type, content: `require` content)+ }+ }+}++define ZeoliteOperator {+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, _) {+ optional String content <- ZeoliteSymbol.parseOperatorSymbols(input)&.take()+ if (! `present` content) {+ return empty+ } else {+ String type <- ZeoliteSymbol.tryAssignment(`require` content)+ <|| ZeoliteSymbol.tryFunctionCall(`require` content)+ <|| "ZeoliteOperator"+ return ZeoliteParsed.leaf(label: type, content: `require` content)+ }+ }+}++define ZeoliteParamName {+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) {+ optional String content <- ZeoliteSymbol.parseParamName(input)&.take()+ if (! `present` content) {+ return empty+ } else {+ String type <- ZeoliteSymbol.tryBuiltinParam(`require` content)+ <|| "ZeoliteParamName"+ return ZeoliteParsed.leaf(label: type, content: `require` content)+ }+ }+}++concrete ZeoliteTestcaseKeyword {+ defines Default+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>+}++define ZeoliteTestcaseKeyword {+ default () {+ return delegate -> #self+ }++ tokenize (input, context) (token) {+ token <- empty+ scoped {+ optional String content <- ZeoliteSymbol.parseLowerSymbol(input)&.preview()+ } in if (! `present` content) {+ return empty+ } else {+ optional String type <- ZeoliteSymbol.tryTestcaseKeyword(`require` content)+ $Hidden[content]$+ if (`present` type) {+ token <- ZeoliteParsed.leaf(label: `require` type, content: input.take())+ }+ }+ }+}++define ZeoliteStringLiteral {+ $ReadOnlyExcept[]$++ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) (token) {+ \ input.reset()+ token <- empty+ if (input.atEnd() || `require` input.current() != '"') {+ return _+ }+ Vector<ZeoliteParsed> subsections <- Vector<ZeoliteParsed>.new()+ // Setting this early still allows building it incrementally while also+ // allowing an early return if something is missing.+ token <- ZeoliteParsed.section(label: tokenizerName(), subsections)+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: input.forward().take()))++ scoped {+ optional Char current <- empty+ } in while (!input.atEnd() && `present` (current <- input.current())) {+ if (`require` current == '\"') {+ \ addCurrentChars(input, subsections)+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: input.forward().take()))+ return _+ } elif (`require` current == '\\') {+ \ addCurrentChars(input, subsections)+ if (`present` ZeoliteSymbol.parseEscapedChar(input.forward())) {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: input.take()))+ } else {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteError", content: input.take()))+ }+ } else {+ \ input.forward()+ }+ }+ \ addCurrentChars(input, subsections)+ }++ @type addCurrentChars (TextStream, Append<ZeoliteParsed>) -> ()+ addCurrentChars (input, output) {+ if (input.tokenSize() > 0) {+ \ output.append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: input.take()))+ }+ }+}++define ZeoliteCharLiteral {+ $ReadOnlyExcept[]$++ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) (token) {+ \ input.reset()+ token <- empty+ if (input.atEnd() || `require` input.current() != '\'') {+ return _+ }+ Vector<ZeoliteParsed> subsections <- Vector<ZeoliteParsed>.new()+ // Setting this early still allows building it incrementally while also+ // allowing an early return if something is missing.+ token <- ZeoliteParsed.section(label: tokenizerName(), subsections)+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: input.forward().take()))++ optional Char current <- input.current()+ if (! `present` current) {+ return _+ } elif (`require` current == '\\') {+ if (`present` ZeoliteSymbol.parseEscapedChar(input.forward())) {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: input.take()))+ } else {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteError", content: input.take()))+ }+ } elif (`require` current == '\'') {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteError", content: input.forward().take()))+ } else {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: input.forward().take()))+ }+ if (`present` (current <- input.current())) {+ if (`require` current == '\'') {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: input.forward().take()))+ } else {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteError", content: input.forward().take()))+ }+ }+ }+}++define ZeoliteNumber {+ $ReadOnlyExcept[]$++ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) (token) {+ \ input.reset()+ token <- empty+ optional Char current <- input.current()+ if (! `present` current) {+ return _+ } elif (CharType.digit(`require` current)) {+ token <- parseNumber(input, escaped: false, CharType.decChars())+ } elif (`require` current `CharType.oneOf` "-+") {+ \ input.forward()+ token <- parseNumber(input, escaped: false, CharType.decChars())+ } elif (`require` current != '\\') {+ return _+ } else {+ current <- input.forward().current()+ if (! `present` current) {+ return _+ } elif (`require` current `CharType.oneOf` "bB") {+ \ input.forward()+ token <- parseNumber(input, escaped: true, CharType.binChars())+ } elif (`require` current `CharType.oneOf` "oO") {+ \ input.forward()+ token <- parseNumber(input, escaped: true, CharType.octChars())+ } elif (`require` current `CharType.oneOf` "dD") {+ \ input.forward()+ token <- parseNumber(input, escaped: true, CharType.decChars())+ } elif (`require` current `CharType.oneOf` "xX") {+ \ input.forward()+ token <- parseNumber(input, escaped: true, CharType.hexChars())+ } else {+ return _+ }+ }+ }++ @type parseNumber (TextStream, Bool escaped:, SetReader<Char>) -> (optional ZeoliteParsed)+ parseNumber (input, escaped, allowed) {+ Bool isEmpty <- true+ optional Char current <- empty+ while (`present` (current <- input.current()) && `allowed.member` `require` current) {+ \ input.forward()+ isEmpty <- false+ }+ if (isEmpty) {+ return empty+ } elif (`present` (current <- input.current()) && `require` current == '.') {+ \ input.forward()+ while (`present` (current <- input.current()) && `allowed.member` `require` current) {+ \ input.forward()+ }+ }+ if (escaped) {+ return ZeoliteParsed.leaf(label: "ZeoliteEscapedNumber", content: input.take())+ } elif (`present` current && `require` current `CharType.oneOf` "eE") {+ \ input.forward()+ if (`present` (current <- input.current()) && `require` current `CharType.oneOf` "-+") {+ \ input.forward()+ }+ while (`present` (current <- input.current()) && `allowed.member` `require` current) {+ \ input.forward()+ }+ }+ return ZeoliteParsed.leaf(label: "ZeoliteNumber", content: input.take())+ }+}++define ZeoliteBraceSection {+ tokenizer () {+ return ZeoliteDelimSection.new(name: tokenizerName(), open: '{', close: '}', empty)+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }+}++define ZeoliteParenSection {+ tokenizer () {+ return ZeoliteDelimSection.new(name: tokenizerName(), open: '(', close: ')', empty)+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }+}++define ZeoliteSquareSection {+ tokenizer () {+ return ZeoliteDelimSection.new(name: tokenizerName(), open: '[', close: ']', empty)+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }+}++define ZeolitePragma {+ $ReadOnlyExcept[]$++ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) (token) {+ \ input.reset()+ token <- empty+ if (input.atEnd() || `require` input.current() != '$') {+ return _+ }+ Vector<ZeoliteParsed> subsections <- Vector<ZeoliteParsed>.new()+ token <- ZeoliteParsed.section(label: tokenizerName(), subsections)+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: input.forward().take()))++ if (! `present` ZeoliteSymbol.parseUpperSymbol(input)) {+ return _+ } else {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeolitePragmaName", content: input.take()))+ }++ optional Char current <- input.current()+ if (! `present` current) {+ return _+ } elif (`require` current == '[') {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeolitePragmaArgOpen", content: input.forward().take()))+ while (`present` (current <- input.current()) && `require` current != ']') {+ \ input.forward()+ }+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeolitePragmaArg", content: input.take()))+ if (`present` (current <- input.current())) {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeolitePragmaArgClose", content: input.forward().take()))+ } else {+ return _+ }+ }++ if (`present` (current <- input.current())) {+ if (`require` current == '$') {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: input.forward().take()))+ } else {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteError", content: input.forward().take()))+ }+ }+ }+}++define ZeoliteExtras {+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) (token) {+ \ input.reset()+ optional Char current <- input.current()+ if (! `present` current) {+ return empty+ } elif (`require` current == '_') {+ \ input.forward()+ return ZeoliteParsed.leaf(label: "ZeoliteIgnore", content: input.take())+ } elif (`require` current == '\\') {+ \ input.forward()+ return ZeoliteParsed.leaf(label: "ZeoliteDiscard", content: input.take())+ } elif (`require` current == '`') {+ \ input.forward()+ return ZeoliteParsed.leaf(label: "ZeoliteTick", content: input.take())+ } elif (`require` current == ',') {+ \ input.forward()+ return ZeoliteParsed.leaf(label: "ZeoliteComma", content: input.take())+ } elif (`require` current `CharType.oneOf` ";#@") {+ \ input.forward()+ return ZeoliteParsed.leaf(label: "ZeoliteError", content: input.take())+ } else {+ return empty+ }+ }+}++define ZeoliteTestcase {+ $ReadOnlyExcept[]$++ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ @category Tokenizer<ZeoliteParseContext, ZeoliteParsed> testcaseSpecs <- TokenAlternatives.new()+ .append(ZeoliteOptionalSeparator.default())+ .append(UseNamedTokenizer.new<ZeoliteStringLiteral>())+ .append(UseNamedTokenizer.new<ZeoliteSquareSection>())+ .append(ZeoliteTestcaseKeyword.default())+ .append(UseNamedTokenizer.new<ZeoliteUpperSymbol>())+ .append(UseNamedTokenizer.new<ZeoliteLowerSymbol>())+ .append(UseNamedTokenizer.new<ZeoliteNumber>())+ .append(UseNamedTokenizer.new<ZeoliteOperator>())++ tokenizer () {+ return delegate -> #self+ }++ tokenizerName () {+ return typename<#self>().formatted()+ }++ tokenize (input, context) (token) {+ scoped {+ optional String content <- ZeoliteSymbol.parseLowerSymbol(input)&.preview()+ } in if (! `present` content || require(content) != "testcase") {+ return empty+ }++ Vector<ZeoliteParsed> subsections <- Vector<ZeoliteParsed>.new()+ // Setting this early still allows building it incrementally while also+ // allowing an early return if something is missing.+ token <- ZeoliteParsed.section(label: tokenizerName(), subsections)+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteContainKeyword", content: input.take()))++ \ ZeoliteOptionalSeparator.parseAny(input, context, subsections)+ // Testcase description string.+ scoped {+ optional ZeoliteParsed name <- delegate -> `ZeoliteStringLiteral.tokenizer().tokenize`+ } in if (`present` name) {+ \ subsections.append(`require` name)+ } else {+ return _+ }+ \ ZeoliteOptionalSeparator.parseAny(input, context, subsections)++ // Testcase specs.+ \ StreamTokenizer:new(context: context, tokenizer: ZeoliteDelimSection.new(name: "ZeoliteTestcaseSection", open: '{', close: '}', testcaseSpecs))+ .tokenizeAll(input, subsections)+ }+}++concrete ZeoliteOptionalSeparator {+ defines Default+ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ visibility Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ @type parseAny (TextStream, ZeoliteParseContext, Append<ZeoliteParsed>) -> ()+}++define ZeoliteOptionalSeparator {+ $ReadOnlyExcept[]$++ @category Tokenizer<ZeoliteParseContext, ZeoliteParsed> tokenizer <- TokenAlternatives.new()+ .append(UseNamedTokenizer.new<ZeoliteLineComment>())+ .append(UseNamedTokenizer.new<ZeoliteBlockComment>())+ .append(UseNamedTokenizer.new<ZeoliteWhitespace>())++ parseAny (input, context, output) {+ \ StreamTokenizer:new(context: context, tokenizer: default()).tokenizeAll(input, output)+ }++ default () {+ return delegate -> #self+ }++ tokenize (input, output) {+ return delegate -> `tokenizer.tokenize`+ }+}++concrete ZeoliteDelimSection {+ @type new (String name:, Char open:, Char close:, optional Tokenizer<ZeoliteParseContext, ZeoliteParsed>) -> (Tokenizer<ZeoliteParseContext, ZeoliteParsed>)+}++define ZeoliteDelimSection {+ $ReadOnlyExcept[]$++ refines Tokenizer<ZeoliteParseContext, ZeoliteParsed>++ @value String name+ @value Char open+ @value Char close+ @value optional Tokenizer<ZeoliteParseContext, ZeoliteParsed> tokenizer++ new (name, open, close, tokenizer) {+ return delegate -> #self+ }++ tokenize (input, context) (token) {+ \ input.reset()+ token <- empty+ if (input.atEnd() || `require` input.current() != open) {+ return _+ }+ Vector<ZeoliteParsed> subsections <- Vector<ZeoliteParsed>.new()+ token <- ZeoliteParsed.section(label: name, subsections)+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: input.forward().take()))++ \ StreamTokenizer:new(context: context, tokenizer: tokenizer <|| context.defaultTokenizer()).tokenizeAll(input, subsections)+ \ input.reset()+ if (!input.atEnd() && `require` input.current() == close) {+ \ subsections.append(ZeoliteParsed.leaf(label: "ZeoliteCloseDelim", content: input.forward().take()))+ }+ }+}
− example/highlighter/src/zeolite.0rp
@@ -1,72 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2023 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at-- http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$ModuleOnly$--concrete ZeoliteToken {- refines Formatted- refines TestCompare<ZeoliteToken>- refines TextToken<String>-- visibility Tokenizer<String, ZeoliteToken>-- @type new (String content:, String label:) -> (optional ZeoliteToken)-}--@type interface NamedTokenizer {- tokenizer () -> (Tokenizer<String, ZeoliteToken>)- tokenizerName () -> (String)-}--concrete ZeoliteTokenizers {- refines KVReader<String, Tokenizer<String, ZeoliteToken>>-- @type new () -> (ZeoliteTokenizers)-- @value include<#t>- #t defines NamedTokenizer- () -> (ZeoliteTokenizers)-}--concrete ZeoliteSelectFrom {- refines Tokenizer<String, ZeoliteToken>-- visibility Tokenizer<String, ZeoliteToken>-- @type new () -> (ZeoliteSelectFrom)-- @value include<#t>- #t defines NamedTokenizer- () -> (ZeoliteSelectFrom)-}--concrete ZeoliteWhitespace {- defines NamedTokenizer-}--concrete ZeoliteLineComment {- defines NamedTokenizer-}--concrete ZeoliteCategoryName {- defines NamedTokenizer-}--concrete ZeoliteParamName {- defines NamedTokenizer-}
− example/highlighter/src/zeolite.0rx
@@ -1,214 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2023 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at-- http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define ZeoliteToken {- @value String content- @value String label-- new (content, label) {- if (content.size() < 1) {- return empty- } else {- return delegate -> #self- }- }-- exportWith (formatter) {- return formatter.format(content: content, label: label)- }-- testCompare (actual, report) {- \ MultiChecker.new(report)- .tryCheck(- title: "content",- actual.content(),- CheckValue:equals(content))- .tryCheck(- title: "label",- actual.label(),- CheckValue:equals(label))- }-- formatted () {- return String.builder()- .append("ZeoliteToken{\"")- .append(CharType.escapeBreaks(content))- .append("\",")- .append(label)- .append("}")- .build()- }-- @value content () -> (String)- content () {- return content- }-- @value label () -> (String)- label () {- return label- }-}--define ZeoliteTokenizers {- $ReadOnlyExcept[]$-- @value HashedMap<String, Tokenizer<String, ZeoliteToken>> tokenizers-- new () {- return #self{ HashedMap<String, Tokenizer<String, ZeoliteToken>>.new() }- }-- include () {- \ #t.tokenizerName() `tokenizers.set` #t.tokenizer()- return self- }-- get (k) {- return delegate -> `tokenizers.get`- }-}--define ZeoliteSelectFrom {- $ReadOnlyExcept[]$-- @value MultiTokenizer<String, ZeoliteToken> selector-- new () {- return #self{ MultiTokenizer<String, ZeoliteToken>.new() }- }-- include () {- \ selector.include(#t.tokenizerName())- return self- }-- tokenize (input, tokenizers) {- return delegate -> `selector.tokenize`- }-}--define ZeoliteWhitespace {- refines Tokenizer<String, ZeoliteToken>-- tokenizer () {- return #self{ }- }-- tokenizerName () {- return typename<#self>().formatted()- }-- tokenize (input, tokenizers) (token, nextType, popCount) {- \ input.reset()- token <- empty- nextType <- empty- popCount <- 0- optional Char current <- input.current()- while (`present` current && CharType.whitespace(`require` current)) {- current <- input.forward().current()- }- token <- ZeoliteToken.new(content: input.take(), label: tokenizerName())- }-}--define ZeoliteLineComment {- refines Tokenizer<String, ZeoliteToken>-- tokenizer () {- return #self{ }- }-- tokenizerName () {- return typename<#self>().formatted()- }-- tokenize (input, tokenizers) (token, nextType, popCount) {- \ input.reset()- token <- empty- nextType <- empty- popCount <- 0- \ input.forward().forward()- if (input.preview() != "//") {- return _- }- optional Char current <- input.current()- while (`present` current && !CharType.oneOf(`require` current, "\r\n")) {- current <- input.forward().current()- }- token <- ZeoliteToken.new(content: input.take(), label: tokenizerName())- }-}--define ZeoliteCategoryName {- refines Tokenizer<String, ZeoliteToken>-- tokenizer () {- return #self{ }- }-- tokenizerName () {- return typename<#self>().formatted()- }-- tokenize (input, tokenizers) (token, nextType, popCount) {- \ input.reset()- token <- empty- nextType <- empty- popCount <- 0- optional Char current <- input.current()- if (! `present` current || !CharType.upper(`require` current)) {- return _- }- while (`present` current && CharType.alphaNum(`require` current)) {- current <- input.forward().current()- }- token <- ZeoliteToken.new(content: input.take(), label: tokenizerName())- }-}--define ZeoliteParamName {- refines Tokenizer<String, ZeoliteToken>-- tokenizer () {- return #self{ }- }-- tokenizerName () {- return typename<#self>().formatted()- }-- tokenize (input, tokenizers) (token, nextType, popCount) {- \ input.reset()- token <- empty- nextType <- empty- popCount <- 0- optional Char current <- input.current()- if (! `present` current || `require` current != '#') {- return _- }- current <- input.forward().current()- if (! `present` current || !CharType.lower(`require` current)) {- return _- }- while (`present` current && CharType.alphaNum(`require` current)) {- current <- input.forward().current()- }- token <- ZeoliteToken.new(content: input.take(), label: tokenizerName())- }-}
+ example/highlighter/test/tokenizer.0rt view
@@ -0,0 +1,67 @@+/* -----------------------------------------------------------------------------+Copyright 2024 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "input tests" {+ success TestChecker+}++unittest integration {+ TextStream input <- TextStream.new("123\n4")++ \ input.atEnd() `Matches:with` CheckValue:equals(false)+ \ input.current() `Matches:with` CheckValue:equals('1')+ \ input.preview() `Matches:with` CheckValue:equals("")+ \ input.currentLine() `Matches:with` CheckValue:equals(1)+ \ input.currentChar() `Matches:with` CheckValue:equals(1)++ \ input.forward().forward()+ \ input.atEnd() `Matches:with` CheckValue:equals(false)+ \ input.current() `Matches:with` CheckValue:equals('3')+ \ input.preview() `Matches:with` CheckValue:equals("12")+ \ input.currentLine() `Matches:with` CheckValue:equals(1)+ \ input.currentChar() `Matches:with` CheckValue:equals(1)++ \ input.take() `Matches:with` CheckValue:equals("12")+ \ input.atEnd() `Matches:with` CheckValue:equals(false)+ \ input.current() `Matches:with` CheckValue:equals('3')+ \ input.preview() `Matches:with` CheckValue:equals("")+ \ input.currentLine() `Matches:with` CheckValue:equals(1)+ \ input.currentChar() `Matches:with` CheckValue:equals(3)++ \ input.forward().forward()+ \ input.atEnd() `Matches:with` CheckValue:equals(false)+ \ input.current() `Matches:with` CheckValue:equals('4')+ \ input.preview() `Matches:with` CheckValue:equals("3\n")++ \ input.reset()+ \ input.atEnd() `Matches:with` CheckValue:equals(false)+ \ input.current() `Matches:with` CheckValue:equals('3')+ \ input.preview() `Matches:with` CheckValue:equals("")+ \ input.currentLine() `Matches:with` CheckValue:equals(1)+ \ input.currentChar() `Matches:with` CheckValue:equals(3)++ \ input.forward().forward().forward()+ \ input.atEnd() `Matches:with` CheckValue:equals(false)+ \ input.current() `Matches:with` CheckValue:equals(empty?Char)+ \ input.preview() `Matches:with` CheckValue:equals("3\n4")++ \ input.take() `Matches:with` CheckValue:equals("3\n4")+ \ input.atEnd() `Matches:with` CheckValue:equals(true)+ \ input.currentLine() `Matches:with` CheckValue:equals(2)+ \ input.currentChar() `Matches:with` CheckValue:equals(2)+}
+ example/highlighter/test/zeolite-tokenizing.0rt view
@@ -0,0 +1,568 @@+/* -----------------------------------------------------------------------------+Copyright 2023-2024 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "parsing tests" {+ success TestChecker+}++concrete ParsingTester {+ @type new (ZeoliteParseContext) -> (ParsingTester)++ @value checkParsesAs (String, DefaultOrder<ZeoliteParsed>) -> ()+}++unittest parseZeoliteWhitespace {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteWhitespace>())++ \ " \t \r\n" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " \t \r\n"))++ \ " test" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+}++unittest parseZeoliteLineComment {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteLineComment>()+ .include<ZeoliteWhitespace>())++ \ "//12345\n//54321" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteLineComment", content: "//12345"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: "\n"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteLineComment", content: "//54321"))++ \ "/ /" `tester.checkParsesAs` AlwaysEmpty.default()+}++unittest parseZeoliteBlockComment {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteBlockComment>()+ .include<ZeoliteWhitespace>())++ \ "/*/12345\n54321* */" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*/12345\n54321* */"))++ \ "/*comment*/ " `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*comment*/"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ \ "/*forever " `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*forever "))++ \ "/ *" `tester.checkParsesAs` AlwaysEmpty.default()+}++unittest parseZeoliteUpperSymbol {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteUpperSymbol>())++ \ "Hello123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteCategoryName", content: "Hello123"))+ \ "String" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteBuiltinCategory", content: "String"))++ \ "Hello_123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteCategoryName", content: "Hello"))++ \ "hello" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "123Hello" `tester.checkParsesAs` AlwaysEmpty.default()+}++unittest parseZeoliteLowerSymbol {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteLowerSymbol>())++ \ "hello123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteFunctionOrVariableName", content: "hello123"))+ \ "hello123:" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteArgLabel", content: "hello123:"))+ \ "cleanup" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteControlKeyword", content: "cleanup"))+ \ "immutable" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteTypeKeyword", content: "immutable"))+ \ "concrete" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteContainKeyword", content: "concrete"))+ \ "optional" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteStorageKeyword", content: "optional"))+ \ "unittest" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteScopeQualifier", content: "unittest"))+ \ "any" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteBuiltinCategory", content: "any"))+ \ "identify" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteBuiltinFunction", content: "identify"))+ \ "empty" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteBuiltinConstant", content: "empty"))++ \ "hello_123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteFunctionOrVariableName", content: "hello"))+ // Only has a special label in testcase.+ \ "compiler" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteFunctionOrVariableName", content: "compiler"))+ \ "empty123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteFunctionOrVariableName", content: "empty123"))+ \ "identify:" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteBuiltinFunction", content: "identify"))++ \ "Hello" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "123hello" `tester.checkParsesAs` AlwaysEmpty.default()+}++unittest parseZeoliteScopeQualifier {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteScopeQualifier>())++ \ "@value" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteScopeQualifier", content: "@value"))+ \ "@type" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteScopeQualifier", content: "@type"))+ \ "@category" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteScopeQualifier", content: "@category"))++ \ "@value something" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteScopeQualifier", content: "@value"))+ \ "@local" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: "@local"))+}++unittest parseZeoliteOperator {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteOperator>())++ \ "<-" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteAssignment", content: "<-"))+ \ "<-|" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteAssignment", content: "<-|"))+ \ "<->" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteAssignment", content: "<->"))+ \ "->" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteAssignment", content: "->"))+ \ ":" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteFunctionCall", content: ":"))+ \ "." `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteFunctionCall", content: "."))+ \ "&." `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteFunctionCall", content: "&."))+ \ "?" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteFunctionCall", content: "?"))+ \ "<<" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOperator", content: "<<"))+ \ ":.!%^&*-+|<>?=" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOperator", content: ":.!%^&*-+|<>?="))++ \ "<-||" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOperator", content: "<-||"))+ \ "&.call()" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteFunctionCall", content: "&."))+}++unittest parseZeoliteParamName {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteParamName>())++ \ "#param123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteParamName", content: "#param123"))+ \ "#self" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteBuiltinParam", content: "#self"))++ \ "#param_123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteParamName", content: "#param"))+ \ "#self123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteParamName", content: "#self123"))++ \ "param" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "#Param" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "#123param" `tester.checkParsesAs` AlwaysEmpty.default()+}++unittest parseZeoliteStringLiteral {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteStringLiteral>())++ \ "\"hello\\n\\t\\0123\\xABC\\Q \\09c goodbye\"" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteStringLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: "\""))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "hello"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: "\\n"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: "\\t"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: "\\012"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "3"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: "\\xAB"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "C"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: "\\Q"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: "\\0"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "9c goodbye"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: "\""))))++ \ "\"forever" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteStringLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: "\""))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "forever"))))+}++unittest parseZeoliteCharLiteral {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteCharLiteral>())++ \ "'a'" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteCharLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "a"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))))+ \ "'\\''" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteCharLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: "\\'"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))))+ \ "'\\n'" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteCharLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: "\\n"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))))+ \ "'\\xAB'" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteCharLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: "\\xAB"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))))+ \ "'\\Q'" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteCharLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: "\\Q"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))))++ \ "''" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteCharLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: "'"))))+ \ "'forever" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteCharLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteSingleQuote", content: "'"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "f"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: "o"))))+}++unittest parseZeoliteNumber {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteNumber>())++ \ "123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "123"))+ \ "123.456" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "123.456"))+ \ "123.456E123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "123.456E123"))+ \ "+123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "+123"))+ \ "-123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "-123"))+ \ "123.456E+123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "123.456E+123"))+ \ "123.456E-123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "123.456E-123"))++ \ "\\b101.010" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedNumber", content: "\\b101.010"))+ \ "\\o123.456" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedNumber", content: "\\o123.456"))+ \ "\\d123.456" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedNumber", content: "\\d123.456"))+ \ "\\x123.abc" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedNumber", content: "\\x123.abc"))++ \ "123A" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "123"))+ \ "\\b0123" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedNumber", content: "\\b01"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "23"))+ \ "\\o12389" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedNumber", content: "\\o123"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "89"))+ \ "\\d123A" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedNumber", content: "\\d123"))+ \ "\\xABCqq" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedNumber", content: "\\xABC"))++ \ "-" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "+" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "\\" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "\\b" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "\\o" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "\\d" `tester.checkParsesAs` AlwaysEmpty.default()+ \ "\\h" `tester.checkParsesAs` AlwaysEmpty.default()+}++unittest parseZeoliteBraceSection {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteBlockComment>()+ .include<ZeoliteBraceSection>()+ .include<ZeoliteWhitespace>())++ \ "{ /*hello*/ }" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteBraceSection", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: "{"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*hello*/"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCloseDelim", content: "}"))))++ \ "{ /*forever*/" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteBraceSection", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: "{"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*forever*/"))))+}++unittest parseZeoliteParenSection {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteBlockComment>()+ .include<ZeoliteParenSection>()+ .include<ZeoliteWhitespace>())++ \ "( /*hello*/ )" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteParenSection", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: "("))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*hello*/"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCloseDelim", content: ")"))))++ \ "( /*forever*/" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteParenSection", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: "("))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*forever*/"))))+}++unittest parseZeoliteSquareSection {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteBlockComment>()+ .include<ZeoliteSquareSection>()+ .include<ZeoliteWhitespace>())++ \ "[ /*hello*/ ]" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteSquareSection", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: "["))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*hello*/"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCloseDelim", content: "]"))))++ \ "[ /*forever*/" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteSquareSection", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: "["))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*forever*/"))))+}++unittest parseZeoliteTestcase {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteWhitespace>()+ .include<ZeoliteLineComment>()+ .include<ZeoliteBlockComment>()+ .include<ZeoliteStringLiteral>()+ .include<ZeoliteSquareSection>()+ .include<ZeoliteNumber>()+ .include<ZeoliteOperator>()+ // ZeoliteTestcase also uses the above if available.+ .include<ZeoliteTestcase>()+ // These should be after ZeoliteTestcase.+ .include<ZeoliteUpperSymbol>()+ .include<ZeoliteLowerSymbol>())++ String data <- "testcase \"my test\" {+ success Checker<[A&B]> // <- enables lib/testing support+ require compiler \"something\"+ timeout 30+ }"++ \ data `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteTestcase", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteContainKeyword", content: "testcase"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.section(label: "ZeoliteStringLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: "\""))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "my test"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: "\""))))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.section(label: "ZeoliteTestcaseSection", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: "{"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: "\n "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteTestcaseKeyword", content: "success"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCategoryName", content: "Checker"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteOperator", content: "<"))+ .append(ZeoliteParsed.section(label: "ZeoliteSquareSection", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: "["))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCategoryName", content: "A"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteOperator", content: "&"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCategoryName", content: "B"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCloseDelim", content: "]"))))+ .append(ZeoliteParsed.leaf(label: "ZeoliteOperator", content: ">"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteLineComment", content: "// <- enables lib/testing support"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: "\n "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteTestcaseKeyword", content: "require"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteTestcaseKeyword", content: "compiler"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.section(label: "ZeoliteStringLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: "\""))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "something"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: "\""))))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: "\n "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteTestcaseKeyword", content: "timeout"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteNumber", content: "30"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: "\n "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCloseDelim", content: "}"))))))++ \ "testcase { }" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteTestcase", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteContainKeyword", content: "testcase"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))))+}++unittest parseZeolitePragma {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeolitePragma>())++ \ "$NoArgs$" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeolitePragma", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: "$"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaName", content: "NoArgs"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: "$"))))+ \ "$SomeArgs[a, b, 123]$" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeolitePragma", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: "$"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaName", content: "SomeArgs"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaArgOpen", content: "["))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaArg", content: "a, b, 123"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaArgClose", content: "]"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: "$"))))++ \ "$NoArgs" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeolitePragma", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: "$"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaName", content: "NoArgs"))))+ \ "$foo$" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeolitePragma", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: "$"))))+ \ "$NoArgs#" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeolitePragma", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: "$"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaName", content: "NoArgs"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: "#"))))+ \ "$SomeArgs[args" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeolitePragma", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaDelim", content: "$"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaName", content: "SomeArgs"))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaArgOpen", content: "["))+ .append(ZeoliteParsed.leaf(label: "ZeolitePragmaArg", content: "args"))))+}++unittest parseZeoliteExtras {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteExtras>())++ \ "_" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteIgnore", content: "_"))+ \ "\\" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteDiscard", content: "\\"))+ \ "`" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteTick", content: "`"))+ \ "," `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteComma", content: ","))+ \ ";" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: ";"))+ \ "#" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: "#"))+ \ "@" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteError", content: "@"))+}++unittest integration {+ ParsingTester tester <- ParsingTester.new(ZeoliteParseContext.new()+ .include<ZeoliteWhitespace>()+ .include<ZeoliteLineComment>()+ .include<ZeoliteBlockComment>()+ .include<ZeolitePragma>()+ .include<ZeoliteStringLiteral>()+ .include<ZeoliteCharLiteral>()+ .include<ZeoliteTestcase>()+ .include<ZeoliteUpperSymbol>()+ .include<ZeoliteLowerSymbol>()+ .include<ZeoliteParamName>()+ .include<ZeoliteBraceSection>())++ \ "{Category #param}\"string\\n\" /*/ done*/\n" `tester.checkParsesAs` Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.section(label: "ZeoliteBraceSection", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteOpenDelim", content: "{"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCategoryName", content: "Category"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteParamName", content: "#param"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteCloseDelim", content: "}"))))+ .append(ZeoliteParsed.section(label: "ZeoliteStringLiteral", Vector<ZeoliteParsed>.new()+ .append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: "\""))+ .append(ZeoliteParsed.leaf(label: "ZeoliteQuotedChars", content: "string"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteEscapedChar", content: "\\n"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteDoubleQuote", content: "\""))))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: " "))+ .append(ZeoliteParsed.leaf(label: "ZeoliteBlockComment", content: "/*/ done*/"))+ .append(ZeoliteParsed.leaf(label: "ZeoliteWhitespace", content: "\n"))+}++define ParsingTester {+ $ReadOnlyExcept[]$++ @value ZeoliteParseContext context++ new (context) {+ return delegate -> #self+ }++ checkParsesAs (originalData, expected) {+ TextStream input <- TextStream.new(originalData)+ DefaultOrder<ZeoliteParsed> output <- defer+ \ StreamTokenizer:new(context: context, tokenizer: context.defaultTokenizer())+ .tokenizeAll(input, (output <- Vector<ZeoliteParsed>.new()))++ // Match output.+ \ output `Matches:tryWith` CheckSequence:matches(CheckSequence:using(expected))++ // Serialize output.+ [Append<Formatted> & Build<String>] builder <- String.builder()+ UnformattedFormatter formatter <- UnformattedFormatter.new()+ traverse (output.defaultOrder() -> ZeoliteParsed parsed) {+ \ builder.append(parsed.formatWith(formatter))+ }++ // Append unparsed remainder.+ while (`present` input.current()) {+ \ input.forward()+ }+ // This will be included in the output of failing tests.+ \ BasicOutput.stderr()+ .append("Remaining content: \"")+ .append(CharType.escapeBreaks(input.preview()))+ .append("\"\n")+ \ builder.append(input.take())++ // Check that all data is accounted for.+ \ builder.build() `Matches:tryWith` CheckValue:equals(originalData)+ }+}
− example/highlighter/test/zeolite.0rt
@@ -1,49 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2023 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at-- http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "working tests" {- success TestChecker-}--unittest test {- ZeoliteTokenizers allTokenizers <- ZeoliteTokenizers.new()- .include<ZeoliteWhitespace>()- .include<ZeoliteLineComment>()- .include<ZeoliteCategoryName>()- .include<ZeoliteParamName>()- ZeoliteSelectFrom categoryOrParam <- ZeoliteSelectFrom.new()- .include<ZeoliteWhitespace>()- .include<ZeoliteLineComment>()- .include<ZeoliteCategoryName>()- .include<ZeoliteParamName>()-- TextStream input <- TextStream.new("Category #param // done")- Vector<ZeoliteToken> output <- Vector<ZeoliteToken>.new()-- \ StreamTokenizer:new(tokenizers: allTokenizers, default: categoryOrParam).tokenizeAll(input, output)-- DefaultOrder<ZeoliteToken> expected <- Vector<ZeoliteToken>.new()- .append(`require` ZeoliteToken.new(content: "Category", label: "ZeoliteCategoryName"))- .append(`require` ZeoliteToken.new(content: " ", label: "ZeoliteWhitespace"))- .append(`require` ZeoliteToken.new(content: "#param", label: "ZeoliteParamName"))- .append(`require` ZeoliteToken.new(content: " ", label: "ZeoliteWhitespace"))- .append(`require` ZeoliteToken.new(content: "// done", label: "ZeoliteLineComment"))-- \ input.atEnd() `Matches:with` CheckValue:equals(true)- \ output `Matches:with` CheckSequence:matches(CheckSequence:using(expected))-}
lib/testing/src/check-sequence.0rx view
@@ -54,7 +54,13 @@ } defaultOrder () {- return #self{ original, original.defaultOrder() }+ scoped {+ optional Order<#x> order <- original.defaultOrder()+ } in if (`present` order) {+ return #self{ original, order }+ } else {+ return empty+ } } get () {
lib/testing/test/check-sequence.0rt view
@@ -176,13 +176,19 @@ success TestChecker } -unittest test {+unittest matchingValues { ValueList<TestValue> actual <- ValueList<TestValue>.new() .append(TestValue.default()) .append(TestValue.new(456, "something", empty)) ValueList<TestValue> expected <- ValueList<TestValue>.new() .append(TestValue.default()) .append(TestValue.new(456, "something", empty))+ \ actual `Matches:with` CheckSequence:matches(CheckSequence:using(expected))+}++unittest bothEmpty {+ ValueList<TestValue> actual <- ValueList<TestValue>.new()+ ValueList<TestValue> expected <- ValueList<TestValue>.new() \ actual `Matches:with` CheckSequence:matches(CheckSequence:using(expected)) }
lib/util/helpers.0rp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021,2024 Kevin P. Barry Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -239,4 +239,12 @@ #xx defines LessThan<#x> @type new () -> (LessThan2<#x>)+}++// Helpers for Append<#x>.+concrete AppendH {+ // Appends all items in order.+ @category from<#x, #a>+ #a requires Append<#x>+ (#a, optional Order<#x>) -> (#a) }
lib/util/src/helpers.0rx view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021,2024 Kevin P. Barry Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -232,5 +232,14 @@ lessThan2 (x, y) { return x `#xx.lessThan` y+ }+}++define AppendH {+ from (output, input) {+ traverse (input -> #x value) {+ \ output.append(value)+ }+ return output } }
lib/util/test/helpers.0rt view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021,2023 Kevin P. Barry+Copyright 2021,2023-2024 Kevin P. Barry Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -212,6 +212,11 @@ expected <- expected+1 } \ expected `Matches:with` CheckValue:equals(5)+}++unittest appendHFrom {+ [Append<Formatted> & Build<String>] output <- String.builder() `AppendH:from` "123".defaultOrder() `AppendH:from` "abc".defaultOrder()+ \ output.build() `Matches:with` CheckValue:equals("123abc") } concrete ValueOrder {
src/CompilerCxx/Code.hs view
@@ -102,6 +102,9 @@ startInitTracing :: CategoryName -> SymbolScope -> String startInitTracing t s = "TRACE_FUNCTION(" ++ show (show t ++ " " ++ show s ++ " init") ++ ")" +startLazyTracing :: CategoryName -> SymbolScope -> String+startLazyTracing t s = "TRACE_FUNCTION(" ++ show (show t ++ " " ++ show s ++ " init") ++ ")"+ startTestTracing :: FunctionName -> String startTestTracing f = "TRACE_FUNCTION(" ++ show ("unittest " ++ show f) ++ ")"
src/CompilerCxx/CxxFiles.hs view
@@ -593,7 +593,7 @@ inlineCategoryConstructor t d tm em = do ctx <- getContextForInit testing tm em t d CategoryScope- initMembers <- runDataCompiler (sequence $ map compileLazyInit members) ctx+ initMembers <- runDataCompiler (sequence $ map (compileLazyInit $ getCategoryName t) members) ctx let initMembersStr = intercalate ", " $ cdOutput initMembers let initColon = if null initMembersStr then "" else " : " return $ mconcat [
src/CompilerCxx/Procedure.hs view
@@ -46,6 +46,7 @@ import Base.CompilerError import Base.GeneralType+import Base.Mergeable (mergeAny) import Base.MergeTree import Base.Positional import Compilation.CompilerState@@ -413,9 +414,9 @@ compileLazyInit :: (Ord c, Show c, CollectErrorsM m, CompilerContext c m [String] a) =>- DefinedMember c -> CompilerState a m ()-compileLazyInit (DefinedMember _ _ _ _ Nothing) = return ()-compileLazyInit (DefinedMember c _ t1 n (Just e)) = resetBackgroundM $ do+ CategoryName -> DefinedMember c -> CompilerState a m ()+compileLazyInit _ (DefinedMember _ _ _ _ Nothing) = return ()+compileLazyInit t0 (DefinedMember c _ t1 n (Just e)) = resetBackgroundM $ do (ts,e') <- compileExpression e when (length (pValues ts) /= 1) $ compilerErrorM $ "Expected single return in initializer" ++ formatFullContextBrace (getExpressionContext e)@@ -424,7 +425,14 @@ let Positional [t2] = ts lift $ (checkValueAssignment r fa t2 t1) <?? "In initialization of " ++ show n ++ " at " ++ formatFullContext c- csWrite [variableName n ++ "([this]() { return " ++ writeStoredVariable t1 e' ++ "; })"]+ let maybeTrace = setTraceContext c+ let trace = case maybeTrace of+ [v] -> " " ++ v+ _ -> ""+ -- NOTE: This needs to be on one line, due to how multiple member inits are concatenated.+ csWrite [variableName n ++ "([this]() { " ++ startInitTracing t0 CategoryScope ++ trace +++ " return " ++ writeStoredVariable t1 e' ++ "; })"+ ] compileVoidExpression :: (Ord c, Show c, CollectErrorsM m, CompilerContext c m [String] a) =>@@ -706,6 +714,12 @@ bind t1' t2' where bind t1 t2+ | o == "<||" = do+ when (not $ isOptionalValue t1) $ do+ compilerErrorM $ "<|| requires the left expression to be optional but got " ++ show t1+ when (isWeakValue t2) $ do+ compilerErrorM $ "<|| requires the right expression to be not be weak but got " ++ show t2+ compileOptionalOr (t1,e1) (t2,e2) | o `Set.member` comparison && isIdentifierRequiredValue t1 && isIdentifierRequiredValue t2 = do return (Positional [boolRequiredValue],glueInfix PrimIdentifier PrimBool e1 o e2) | t1 /= t2 =@@ -772,6 +786,11 @@ compilerErrorM $ "Function call requires one return but got " ++ formatTypes ts ++ formatFullContextBrace c2 formatTypes [] = "none" formatTypes ts = intercalate ", " (map show ts)+ compileOptionalOr (t1,e1) (t2,e2) = do+ let t' = combineTypes t1 t2+ let code = WrappedSingle $ "TYPE_VALUE_LEFT_UNLESS_EMPTY(" ++ useAsUnwrapped e1 ++ ", " ++ useAsUnwrapped e2 ++ ")"+ return (Positional [t'], code)+ combineTypes (ValueType _ t1) (ValueType s t2) = ValueType s (mergeAny [t1,t2]) forceOptionalReturns :: [c] -> ScopedFunction c -> ScopedFunction c forceOptionalReturns c0 (ScopedFunction c n t s v as rs ps fs ms) =
src/Parser/Procedure.hs view
@@ -35,6 +35,7 @@ import qualified Data.Set as Set import Base.CompilerError+import Base.GeneralType (singleType) import Base.Positional import Parser.Common import Parser.Pragma@@ -43,6 +44,7 @@ import Parser.TypeInstance () import Types.Procedure import Types.TypeCategory+import Types.TypeInstance (TypeInstanceOrParam(..), TypeInstance(..)) instance ParseFromSource (ExecutableProcedure SourceContext) where@@ -339,7 +341,7 @@ o <- op return $ NamedOperator [c] o where op = labeled "binary operator" $ foldr (<|>) empty $ map (try . operator) ops- ops = compareInfix ++ logicalInfix ++ addInfix ++ subInfix ++ multInfix ++ divInfix ++ bitwiseInfix ++ bitshiftInfix+ ops = compareInfix ++ logicalInfix ++ addInfix ++ subInfix ++ multInfix ++ divInfix ++ bitwiseInfix ++ bitshiftInfix ++ optionalOrInfix compareInfix :: [String] compareInfix = ["==","!=","<","<=",">",">="]@@ -365,11 +367,14 @@ bitshiftInfix :: [String] bitshiftInfix = [">>","<<"] +optionalOrInfix :: [String]+optionalOrInfix = ["<||"]+ leftAssocInfix :: [String] leftAssocInfix = addInfix ++ subInfix ++ multInfix ++ divInfix ++ bitwiseInfix ++ bitshiftInfix rightAssocInfix :: [String]-rightAssocInfix = logicalInfix+rightAssocInfix = logicalInfix ++ optionalOrInfix nonAssocInfix :: [String] nonAssocInfix = compareInfix@@ -729,7 +734,7 @@ return $ IntegerLiteral [c] unsigned d instance ParseFromSource (ValueOperation SourceContext) where- sourceParser = valueCall <|> conversion <|> selectReturn where+ sourceParser = conversion <|> valueCall <|> selectReturn where valueCall = labeled "function call" $ do c <- getSourceContext o <- (valueSymbolGet >> return AlwaysCall) <|> (valueSymbolMaybeGet >> return CallUnlessEmpty)@@ -739,8 +744,18 @@ conversion = labeled "type conversion" $ do c <- getSourceContext inferredParam- t <- sourceParser -- NOTE: Should not need try here.+ t <- typeInstance <|> sourceParser return $ TypeConversion [c] t+ -- This is the same as the TypeInstance ParseFromSource implementation+ -- except that it uses try for params. This is necessary for < and <|| when+ -- the left side is converted to a category without params.+ typeInstance = labeled "type instance" $ do+ n <- sourceParser+ as <- labeled "type args" $ try args <|> return []+ return $ singleType $ JustTypeInstance $ TypeInstance n (Positional as)+ args = between (sepAfter $ string "<")+ (sepAfter $ string ">")+ (sepBy sourceParser (sepAfter $ string ",")) selectReturn = labeled "return selection" $ do c <- getSourceContext sepAfter_ (string_ "{")
src/Test/Procedure.hs view
@@ -284,9 +284,22 @@ checkShortParseFail "x <- \\o123.", checkShortParseFail "x <- \\o123.456E1", + checkShortParseSuccess "x <-| 123", checkShortParseFail "Int x <-| 123", checkShortParseFail "_ <-| 123", checkShortParseFail "x, y <-| 123",++ checkShortParseSuccess "\\ x <|| y",+ checkShortParseSuccess "\\ empty?Int <|| y",+ checkShortParseSuccess "\\ 123 <|| 456",+ checkShortParseSuccess "\\ empty <|| 'a'",+ checkShortParseFail "Int x <|| 'a'",+ checkShortParseFail "x, y <|| 'a'",+ checkShortParseFail "\\ _ <|| 'a'",++ checkShortParseSuccess "\\ \"123\"?String < \"456\"",+ checkShortParseSuccess "\\ \"123\"?ReadAt < Char>",+ checkShortParseSuccess "\\ \"123\"?String < String.default()", checkParsesAs "'\"'" (\e -> case e of
tests/conversions.0rt view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2020-2021 Kevin P. Barry+Copyright 2020-2021,2023 Kevin P. Barry Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -53,6 +53,14 @@ unittest convertedCall { Value value <- Value.create() \ value?Base1.call()+}++unittest convertWithLessThan {+ \ Testing.checkEquals("123"?String < "456", true)+}++unittest convertWithParamSubstitution {+ \ Testing.checkEquals("123"?ReadAt<Char>.readAt(1), '2') } concrete Helper {
tests/delegation.0rt view
@@ -292,3 +292,62 @@ return delegate -> Value } }+++testcase "delegate handles optional with &." {+ success+}++concrete Helper {+ @type testEmpty (Int) -> ()+ @type testNonEmpty (Int) -> ()+ @type call (Bool fake:) -> (optional String)+}++define Helper {+ testEmpty (position) {+ \ Testing.checkEquals(delegate -> `Helper.call(fake: true)&.readAt`, empty)+ }++ testNonEmpty (position) {+ \ Testing.checkEquals(delegate -> `Helper.call(fake: false)&.readAt`, "123".readAt(position))+ }++ call (fake) {+ if (fake) {+ return empty+ } else {+ return "123"+ }+ }+}++unittest testEmpty {+ \ Helper.testEmpty(1)+}++unittest testNonEmpty {+ \ Helper.testNonEmpty(1)+}+++testcase "delegate applies optional with &." {+ error+ require compiler "value"+ require compiler "modifier"+}++concrete Helper {+ @type test (Int) -> ()+ @type call () -> (optional String)+}++define Helper {+ test (position) {+ Char value <- delegate -> `Helper.call()&.readAt`+ }++ call () {+ return empty+ }+}
tests/modified-storage.0rt view
@@ -395,6 +395,114 @@ } +testcase "optional or" {+ success+}++concrete Helper {+ @type notCalled () -> (all)+ @type getOnce () -> (optional Int)+}++define Helper {+ @category Bool used <- false++ notCalled () {+ fail("should not be called")+ }++ getOnce () {+ if (used) {+ fail("already called")+ }+ used <- true+ return 123+ }+}++unittest bothBoxedEmpty {+ optional String value <- empty+ \ Testing.checkEquals(value <|| "message", "message")+}++unittest bothBoxedNotEmpty {+ optional String value <- "other"+ \ Testing.checkEquals(value <|| "message", "other")+}++unittest leftUnboxedEmpty {+ optional Int value <- empty+ \ Testing.checkEquals((value <|| "message").formatted(), "message")+}++unittest leftUnboxedNotEmpty {+ optional Int value <- 123+ \ Testing.checkEquals((value <|| "message").formatted(), "123")+}++unittest rightUnboxedEmpty {+ optional String value <- empty+ \ Testing.checkEquals((value <|| 123).formatted(), "123")+}++unittest rightUnboxedNotEmpty {+ optional String value <- "message"+ \ Testing.checkEquals((value <|| 123).formatted(), "message")+}++unittest leftEvaluatedOnce {+ \ Testing.checkEquals(Helper.getOnce() <|| 456, 123)+}++unittest shortCircuit {+ optional String value <- "other"+ \ Testing.checkEquals(value <|| Helper.notCalled(), "other")+}+++testcase "optional-or required return type" {+ error+ require "\[Int\|String\]"+ exclude "optional"+}++unittest test {+ Int value <- empty?Int <|| "message"+}+++testcase "optional-or optional return type" {+ error+ require "modifier"+ exclude "\[Int\|String\]"+}++unittest test {+ Formatted value <- empty?Int <|| empty?String+}+++testcase "optional-or not allowed with left required" {+ error+ require "optional.+Int"+}++unittest test {+ \ 123 <|| 456+}+++testcase "optional-or not allowed with right weak" {+ error+ require "weak Int"+}++unittest test {+ weak Int value <- empty+ \ empty?Int <|| value+}++ testcase "conditional assignment" { success }
zeolite-lang.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.2 name: zeolite-lang-version: 0.24.0.1+version: 0.24.1.0 synopsis: Zeolite is a statically-typed, general-purpose programming language. description:@@ -59,7 +59,7 @@ license-file: LICENSE author: Kevin P. Barry maintainer: Kevin P. Barry <ta0kira@gmail.com>-copyright: (c) Kevin P. Barry 2019-2023+copyright: (c) Kevin P. Barry 2019-2024 category: Compiler build-type: Simple