zeolite-lang-0.24.1.0: example/highlighter/src/zeolite-tokenizing.0rx
/* -----------------------------------------------------------------------------
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()))
}
}
}