minigrace
Version:
A compiler for the Grace programming language
848 lines • 1.07 MB
JavaScript
let gracecode_parser_sourceFile = "/Users/black/Development/mg/gracelang/minigrace/parser.grace";
let gracecode_parser_sha256 = "a7b683f7eebbe09be105d7c8702dd1ebeee9da02f695a1e579475d61ee66613a";
let gracecode_parser_minigraceRevision = "b57591d29fc5ee5270d114920bf671367b8c3ecb";
let gracecode_parser_minigraceGeneration = "5171";
if (typeof gctCache !== "undefined")
gctCache["parser"] = "dialect:\n standard\nfreshScopes:\n $102\n $103\n $104\n $106\nmethodTypes:$101:\n blockParameter(params:Unknown) \u2192 Boolean\nmethodTypes:$102:\n end \u2192 Position\n start \u2192 Position\nmodules:\n ast\n basic\n errormessages\n io\n standard\n util\npath:\n /Users/black/Development/mg/gracelang/minigrace/parser.grace\nscope:$101:\n AstNode type\n CompilerError Unknown (mth) $scope_done\n GatheringError Unknown (mth) $scope_done\n Position type\n Range type\n Scope type\n Variable type\n accept(1) Unknown (mth) $scope_done\n acceptArgument Unknown (mth) $scope_done\n acceptDotOuter Unknown (mth) $scope_done\n acceptKeyword(1) Unknown (mth) $scope_done\n acceptKeyword(1)or(1) Unknown (mth) $scope_done\n acceptKeyword(1)or(1)or(1) Unknown (mth) $scope_done\n asDebugString String (go) $scope_string\n asString String (go) $scope_string\n basicAsString String (go) $scope_string\n block Unknown (mth) $scope_done\n blockBody(1)beginningWith(1) Unknown (mth) $scope_done\n blockParameter(1) Boolean (mth) $scope_done\n blockParameters Unknown (mth) $scope_done\n callrest(1) Unknown (mth) $scope_done\n canonicalName(1) Unknown (mth) $scope_done\n checkAnnotation(1) Unknown (mth) $scope_done\n checkBadInterfaceLiteral Unknown (mth) $scope_done\n checkBadOperators Unknown (mth) $scope_done\n checkForSeparatorInInterface Unknown (mth) $scope_done\n checkUnexpectedTokenAfterStatement Unknown (mth) $scope_done\n checkWhereCondition Unknown (mth) $scope_done\n classOrTrait(1) Unknown (mth) $scope_done\n defdec Unknown (mth) $scope_done\n doannotation Unknown (mth) $scope_done\n dodialect Unknown (mth) $scope_done\n doif Unknown (mth) $scope_done\n doimport Unknown (mth) $scope_done\n doobject Unknown (mth) $scope_done\n doouter Unknown (mth) $scope_done\n doreturn Unknown (mth) $scope_done\n doself Unknown (mth) $scope_done\n dotrest(1) Unknown (mth) $scope_done\n emptyRange Unknown (mth) $scope_done\n errorDefMissingRhs(1) Unknown (mth) $scope_done\n errorDefNoExpression Unknown (mth) $scope_done\n errorDefNoName Unknown (mth) $scope_done\n errorDefUsesAssign(1) Unknown (mth) $scope_done\n errorMissingAnnotation Unknown (mth) $scope_done\n errorMissingComma Unknown (mth) $scope_done\n errorMissingIdentifier Unknown (mth) $scope_done\n errorNoVarArgs Unknown (mth) $scope_done\n errorUnexpectedKeyword Unknown (mth) $scope_done\n expression(1) Unknown (mth) $scope_done\n expressionrest(1)recursingWith(1)blocks(1) Unknown (mth) $scope_done\n fakeSymbolTable Unknown (mth) $103\n findClosingBrace(2) Unknown (mth) $scope_done\n findNextToken(1) Unknown (mth) $scope_done\n findNextTokenIndentedAt(1) Unknown (mth) $scope_done\n findNextValidToken(1) Unknown (mth) $scope_done\n generic Unknown (mth) $scope_done\n identifier Unknown (mth) $scope_done\n inheritModifier(1) Unknown (mth) $scope_done\n inheritOrUse Unknown (mth) $scope_done\n interfaceLiteral Unknown (mth) $scope_done\n isMe(1) Boolean (go) $scope_boolean\n isOnAContinationLine Unknown (mth) $scope_done\n line(1)column(1) Position (mth) $104\n matchcase Unknown (mth) $scope_done\n methodClassOrTrait Unknown (mth) $scope_done\n methodDecRest(1) Unknown (mth) $scope_done\n methodDeclaration(1) Unknown (mth) $scope_done\n methodHeader Unknown (mth) $scope_done\n methodInInterface Unknown (mth) $scope_done\n methodInInterface(1) Unknown (mth) $scope_done\n methodSignature Unknown (mth) $scope_done\n myIdentityHash Number (go) $scope_number\n newIf(3) Unknown (mth) $scope_done\n next Unknown (mth) $scope_done\n nextToken Unknown (mth) $scope_done confidential\n noPosition Unknown (mth) $scope_done\n nullNode AstNode (mth) $105\n numericName(1) Unknown (mth) $scope_done\n operator(1) Unknown (mth) $102\n optionalTypeAnnotation Unknown (mth) $scope_done\n parameterList(1) Unknown (mth) $scope_done\n parenthesizedArgs(1)startingWith(1) Unknown (mth) $scope_done\n parse(1) Unknown (mth) $scope_done\n parseAlias(1) Unknown (mth) $scope_done\n parseArgumentsFor(1)into(1)acceptBlocks(1) Unknown (mth) $scope_done\n parseExclude(1) Unknown (mth) $scope_done\n parseObjectConstructorBody(1)startingWith(1)after(1) Unknown (mth) $scope_done\n prefixop Unknown (mth) $scope_done\n pushComments Unknown (mth) $scope_done\n pushIdentifier Unknown (mth) $scope_done\n pushNum Unknown (mth) $scope_done\n pushString Unknown (mth) $scope_done\n reconcileComments Unknown (mth) $scope_done\n reportBadRhs Unknown (mth) $scope_done\n reportMissingArrow Unknown (mth) $scope_done\n reportSyntaxError(1)before(1) Unknown (mth) $scope_done\n restoreParsePosition(1) Unknown (mth) $scope_done\n saveParsePosition Unknown (mth) $scope_done\n separator Unknown (mth) $scope_done\n sequenceConstructor Unknown (mth) $scope_done\n skipSeparators Unknown (mth) $scope_done\n start(1)end(1) Range (mth) $106\n startsStatement Unknown (mth) $scope_done\n statement Unknown (mth) $scope_done\n successfulParse(1) Unknown (mth) $scope_done\n term Unknown (mth) $scope_done\n toprec(1) Unknown (mth) $scope_done\n trycatch Unknown (mth) $scope_done\n typeArgs Unknown (mth) $scope_done\n typedec Unknown (mth) $scope_done\n typedec(1) Unknown (mth) $scope_done\n typeexpression Unknown (mth) $scope_done\n typeexpressionrest Unknown (mth) $scope_done\n typeparameters Unknown (mth) $scope_done\n typeterm Unknown (mth) $scope_done\n unsuccessfulParse(1) Unknown (mth) $scope_done\n valueexpressionrest Unknown (mth) $scope_done\n vardec Unknown (mth) $scope_done\nscope:$102:\n asDebugString String (go) $scope_string\n asString String (go) $scope_string\n basicAsString String (go) $scope_string\n end Position (def) $scopeEmpty public\n isArithmetic Unknown (mth) $scope_done\n isMe(1) Boolean (go) $scope_boolean\n myIdentityHash Number (go) $scope_number\n op Unknown (def) $scopeEmpty public\n precedence Unknown (mth) $scope_done\n start Position (def) $scopeEmpty public\n symbol Unknown (def) $scopeEmpty\n typeArgs Unknown (var) $scopeEmpty public\n typeArgs:=(1) Unknown (var) $scopeEmpty public\nscope:$103:\n ::(1) Unknown (mth) $scope_done\n ==(1) Unknown (mth) $scope_done\n areReusedNamesCompleted Unknown (mth) $scope_done\n asDebugString String (go) $scope_string\n asString Unknown (mth) $scope_done\n basicAsString String (go) $scope_string\n hash Unknown (mth) $scope_done\n isFresh Unknown (mth) $scope_done\n isMe(1) Boolean (go) $scope_boolean\n localNames Unknown (mth) $scope_done\n myIdentityHash Number (go) $scope_number\n node Unknown (var) $scopeEmpty\n node:=(1) Unknown (var) $scopeEmpty\n prefix== Unknown (mth) $scope_done\n prefix\u2260 Unknown (mth) $scope_done\n variety Unknown (mth) $scope_done\n varsAreMethods Unknown (mth) $scope_done\n \u2260(1) Unknown (mth) $scope_done\nscope:$104:\n ::(1) Unknown (mth) $scope_done\n <(1) Boolean (mth) $scope_done\n ==(1) Boolean (mth) $scope_done\n >(1) Boolean (mth) $scope_done\n addColumn(1) Unknown (mth) $scope_done\n addLine(1) Unknown (mth) $scope_done\n asDebugString String (go) $scope_string\n asString Unknown (mth) $scope_done\n basicAsString String (go) $scope_string\n column Unknown (def) $scopeEmpty\n hash Number (mth) $scope_done\n isMe(1) Boolean (go) $scope_boolean\n line Unknown (def) $scopeEmpty\n myIdentityHash Number (go) $scope_number\n prefix== Unknown (mth) $scope_done\n prefix\u2260 Unknown (mth) $scope_done\n \u2260(1) Unknown (mth) $scope_done\n \u2264(1) Boolean (mth) $scope_done\n \u2265(1) Boolean (mth) $scope_done\nscope:$105:\n ::(1) Unknown (mth) $scope_done\n ==(1) Unknown (mth) $scope_done\n accept(1)from(1) Unknown (mth) $scope_done\n annotations Unknown (mth) $scope_done\n asDebugString String (go) $scope_string\n asString Unknown (mth) $scope_done\n basicAsString String (go) $scope_string\n childrenDo(1) Unknown (mth) $scope_done\n childrenMap(1) Unknown (mth) $scope_done\n column Unknown (def) $scope_number\n column:=(1) Unknown (mth) $scope_done\n comments Unknown (mth) $scope_done\n decType Unknown (mth) $scope_done\n end Position (mth) $scope_done\n endCol Unknown (mth) $scope_done\n hasAnnotation(1) Unknown (mth) $scope_done\n hash Unknown (mth) $scope_done\n inClass Unknown (mth) $scope_done\n inTrait Unknown (mth) $scope_done\n isAssignment Unknown (mth) $scope_done\n isBlock Unknown (mth) $scope_done\n isBuiltIn Unknown (mth) $scope_done\n isCall Unknown (mth) $scope_done\n isClass Unknown (mth) $scope_done\n isComment Unknown (mth) $scope_done\n isConstant Unknown (mth) $scope_done\n isDeclaredByParent Unknown (mth) $scope_done\n isDialect Unknown (mth) $scope_done\n isExecutable Unknown (mth) $scope_done\n isExternal Unknown (mth) $scope_done\n isFresh Unknown (mth) $scope_done\n isIdentifier Unknown (mth) $scope_done\n isImport Unknown (mth) $scope_done\n isMarkerDeclaration Unknown (mth) $scope_done\n isMe(1) Boolean (go) $scope_boolean\n isMember Unknown (mth) $scope_done\n isMethod Unknown (mth) $scope_done\n isModule Unknown (mth) $scope_done\n isNull Unknown (mth) $scope_done\n isObject Unknown (mth) $scope_done\n isOuter Unknown (mth) $scope_done\n isReturn Unknown (mth) $scope_done\n isSelf Unknown (mth) $scope_done\n isSelfOrOuter Unknown (mth) $scope_done\n isSequenceConstructor Unknown (mth) $scope_done\n isSuper Unknown (mth) $scope_done\n isTrait Unknown (mth) $scope_done\n isTypeDec Unknown (mth) $scope_done\n kind Unknown (def) $scope_string\n line Unknown (def) $scope_number\n line:=(1) Unknown (mth) $scope_done\n map(1)ancestors(1) Unknown (mth) $scope_done\n myIdentityHash Number (go) $scope_number\n nameString Unknown (mth) $scope_done\n newAccept(1) Unknown (mth) $scope_done\n prefix== Unknown (mth) $scope_done\n prefix\u2260 Unknown (mth) $scope_done\n pretty(1) Unknown (mth) $scope_done\n range Unknown (mth) $scope_done\n register Unknown (def) $scope_string\n scope Unknown (def) $103\n shallowCopy Unknown (mth) $scope_done\n start Unknown (mth) $scope_done\n toGrace(1) Unknown (mth) $scope_done\n \u2260(1) Unknown (mth) $scope_done\nscope:$106:\n ::(1) Unknown (mth) $scope_done\n <(1) Unknown (mth) $scope_done\n ==(1) Unknown (mth) $scope_done\n >(1) Unknown (mth) $scope_done\n arrow Unknown (mth) $scope_done\n asDebugString String (go) $scope_string\n asString Unknown (mth) $scope_done\n basicAsString String (go) $scope_string\n column Unknown (mth) $scope_done\n end Unknown (def) $scopeEmpty\n hash Number (mth) $scope_done\n isMe(1) Boolean (go) $scope_boolean\n line Unknown (mth) $scope_done\n lineRangeString Unknown (mth) $scope_done\n myIdentityHash Number (go) $scope_number\n prefix== Unknown (mth) $scope_done\n prefix\u2260 Unknown (mth) $scope_done\n rangeLongString Unknown (mth) $scope_done\n rangeString Unknown (mth) $scope_done\n start Unknown (def) $scopeEmpty\n sugg Unknown (mth) $scope_done\n \u2260(1) Unknown (mth) $scope_done\n \u2264(1) Unknown (mth) $scope_done\n \u2265(1) Unknown (mth) $scope_done\nself:\n $101\ntypes:\n";
if (typeof originalSourceLines !== "undefined") {
originalSourceLines["parser"] = [
"dialect \"standard\"",
"import \"io\" as io",
"import \"ast\" as ast",
"import \"util\" as util",
"import \"errormessages\" as errormessages",
"import \"basic\" as basic",
"",
"use basic.open",
"",
"var tokens := false",
"var moduleObject",
"def comments = list [] // so we can request `removeAt`",
"",
"var auto_count := 0",
"def noBlocks = false",
"def blocksOK = true",
"",
"",
"var values := list [ ]",
"",
"// The alternative definition below allows pushes and pops of `values`",
"// to be traced. It can be useful for debugging the parser.",
"// def values = object {",
"// def realValues = list [ ]",
"// var tracing is public := false",
"// method push(v) {",
"// if (tracing) then {",
"// print \"pushed {v.toGrace 0} (line {v.line})\"",
"// }",
"// realValues.push(v)",
"// }",
"// method pop {",
"// def res = realValues.pop",
"// if (tracing) then {",
"// print \"popped {res.toGrace 0} (line {res.line})\"",
"// }",
"// res",
"// }",
"// method size { realValues.size }",
"// method filter(b) { realValues.filter(b) }",
"// method isEmpty { realValues.isEmpty }",
"// method last { realValues.last }",
"// method do(b) { realValues.do(b) }",
"// }",
"",
"// sym is a module-level field referring to the current token",
"var sym := object {",
" def kind is public = \"start\"",
" def line is public = 0",
" def column is public = 0",
" def indent is public = 0",
" def value is public = \"\"",
" def size is public = 0",
" method ==(other) {",
" if (other == false) then {",
" false",
" } else {",
" (other.line == line) && (other.column == column)",
" }",
" }",
"}",
"",
"var lastToken := sym",
"var statementToken := lastToken // the token starting the current statement",
"var comment := false",
"",
"method next {",
" // Advance to the next token in the stream, assigning it to sym.",
" // Put the position in the input into util module variables.",
"",
" if (tokens.size > 0) then {",
" nextToken",
" pushComments",
" } else {",
" errormessages.syntaxError(\"unexpectedly found the end of the input. \"",
" ++ \"This is often caused by a missing '\\}'\")",
" atPosition(sym.line, sym.column)",
" }",
"}",
"",
"method nextToken is confidential {",
" lastToken := sym",
" sym := tokens.poll",
" if (util.verbosity > 110) then { io.error.write \"{sym}\\n\" }",
" util.setPosition(sym.line, sym.column)",
"}",
"",
"method isOnAContinationLine {",
" // is current token on a line that is a continuation of the previous line?",
" def currentLine = sym.line",
" var s := sym",
" while {s.line == currentLine} do {",
" if (s.hasPrev.not) then { return false }",
" s := s.prev",
" if (s.isSeparator) then { return false }",
" }",
" return true",
"}",
"",
"method saveParsePosition {",
" def lexerState = tokens.savePosition",
" return [lexerState, values.size]",
"}",
"",
"method restoreParsePosition(saved) {",
" tokens.restorePosition(saved.first)",
" sym := tokens.first.prev",
" lastToken := sym.prev",
" def oldValueStackSize = saved.second",
" if (oldValueStackSize > values.size) then {",
" ProgrammingError.raise \"can't restore parse position; the values stack has been popped\"",
" }",
" while {oldValueStackSize ≠ values.size} do { values.pop }",
"}",
"",
"method findNextToken(tokenMatcher) {",
" // Search for the next token for which the given block returns true.",
" // Used for generating suggestions.",
"",
" if (tokenMatcher.apply(sym)) then {",
" return sym",
" }",
" var n := sym",
" while {n.isEof.not && { n.indent >= lastToken.indent }} do {",
" if (tokenMatcher.apply(n)) then {",
" return n",
" }",
" n := n.next",
" }",
" n",
"}",
"",
"method findNextTokenIndentedAt(tok) {",
" // Search for the next token for whose indent does not exceed that of tok.",
" // Used for generating suggestions.",
" if (((sym.line > tok.line) && (sym.indent <= tok.indent)) || (sym.isEof)) then {",
" return sym",
" }",
" var n := sym",
" while {n.isEof.not} do {",
" if ((n.line > tok.line) && (n.indent <= tok.indent)) then {",
" return n",
" }",
" n := n.next",
" }",
" n",
"}",
"",
"method findNextValidToken(validFollowTokens) {",
" // returns the next token in validFollowTokens, or the next token",
" // that could start an expression, or eof",
"",
" def invalidTokens = set.withAll [\"dot\", \"comma\", \"colon\", \"rparen\", \"rbrace\",",
" \"rsquare\", \"arrow\", \"assignment\"]; // Tokens that cannot start an expression",
"",
" var candidate := sym",
" while {candidate.isEof.not} do {",
" if (validFollowTokens.contains(candidate.kind)) then {",
" return candidate // candidate is a valid follow token, so return it",
" }",
" if (!invalidTokens.contains(candidate.kind)) then {",
" return candidate // candidate could start an expression, so return it",
" }",
" candidate := candidate.next // candidate is invalid, so go to the next one",
" }",
" return candidate // candidate is eof",
"}",
"",
"method findClosingBrace(opening, inserted) {",
" // Finds the closing brace correspondng to opening (that is the beginning of a control",
" // structure) -- an opening brace. Returns an object with two fields: found",
" // and tok. If a closing brace is found, found is true, and tok is",
" // the closing brace. Otherwise, found is false, and tok is the",
" // token after which the closing brace should appear.",
"",
" var n := sym",
" var numOpening := if (inserted) then {1} else {0}",
" var numClosing := 0",
" def result = object {",
" var found is public",
" var tok is public",
" }",
" // Count all tokens on the same line first.",
" while {(n.isEof.not) && (n.line == opening.line)} do {",
" if (n.isLBrace) then {",
" numOpening := numOpening + 1",
" } elseif { n.isRBrace } then {",
" numClosing := numClosing + 1",
" }",
" n := n.next",
" }",
" // Count all tokens that have greater indent than the opening brace.",
" while {(n.isEof.not) && (n.indent > opening.indent)} do {",
" if (n.isLBrace) then {",
" numOpening := numOpening + 1",
" } elseif { n.isRBrace } then {",
" numClosing := numClosing + 1",
" }",
" n := n.next",
" }",
" if (n.isRBrace) then {",
" result.found := true",
" result.tok := n",
" } else {",
" result.found := false",
" result.tok := n.prev",
" }",
" result",
"}",
"",
"",
"method accept(t) {",
" // True if the current token has kind t, where",
" // t is \"num\", \"string\", \"keyword\", etc.",
" sym.kind == t",
"}",
"",
"method acceptKeyword (kw) {",
" if (sym.isKeyword.not) then { return false }",
" def v = sym.value",
" if (v == kw) then { return true }",
" return false",
"}",
"",
"method acceptKeyword (kw1) or (kw2) {",
" if (sym.isKeyword.not) then { return false }",
" def v = sym.value",
" if (v == kw1) then { return true }",
" if (v == kw2) then { return true }",
" return false",
"}",
"",
"method acceptKeyword (kw1) or (kw2) or (kw3) {",
" if (sym.isKeyword.not) then { return false }",
" def v = sym.value",
" if (v == kw1) then { return true }",
" if (v == kw2) then { return true }",
" if (v == kw3) then { return true }",
" return false",
"}",
"",
"method skipSeparators {",
" while { sym.isSeparator } do { next }",
"}",
"",
"method acceptArgument {",
" // True if the current token can start an argument to a request",
" if (sym.isString) then { return true }",
" if (sym.isNum) then { return true }",
" if (sym.isLBrace) then { return true }",
" if (sym.isLSquare) then { return true }",
" if (sym.isIdentifier) then {",
" def symValue = sym.value",
" return (symValue == \"true\") || (symValue == \"false\")",
" }",
" return false",
"}",
"",
"method successfulParse (aParsingBlock) {",
" // returns true if executing aParsingBlock parses at least one value.",
" def sz = values.size",
" aParsingBlock.apply",
" values.size != sz",
"}",
"method unsuccessfulParse (aParsingBlock) {",
" // returns true if executing aParsingBlock fails to parse any values.",
" def sz = values.size",
" aParsingBlock.apply",
" values.size == sz",
"}",
"method pushNum {",
" // Push the current token onto the output stack as a number",
" var o := ast.numeral(sym.value)",
" values.push(o)",
" next",
" return o",
"}",
"",
"method pushString {",
" // Push the current token onto the output stack as a string",
" var o := ast.stringLiteral(sym.value)",
" o.end := line (sym.line) column (sym.endCol)",
" values.push(o)",
" next",
" return o",
"}",
"",
"method pushIdentifier {",
" // Push the current token onto the output stack as an identifier.",
" // false means that this identifier has not yet been annotated with a dtype.",
" util.setPosition(sym.line, sym.column)",
" def o = if (sym.value == \"_\") then {",
" ast.wildcardIdentifier(false)",
" } else {",
" ast.identifier(sym.value, false)",
" }",
" values.push(o)",
" next",
" return o",
"}",
"",
"method checkAnnotation(ann) {",
" if (ann.isCall) then {",
" for (ann.parts) do {p->",
" for (p.args) do {a->",
" if ((a.isIdentifier) && {false != a.dtype}) then {",
" var tok := sym",
" // Look back from the current token to try and find the tokens that cause this error.",
" while {tok.value != \":\"} do { tok := tok.prev }",
" def suggestion = errormessages.suggestion.new",
" suggestion.deleteTokenRange(tok, tok.next)leading(true)trailing(false)",
" errormessages.syntaxError(\"an argument to an annotation cannot have a type.\")",
" atRange(tok.line, tok.column, tok.next.endCol)",
" withSuggestion(suggestion)",
" }",
" }",
" }",
" }",
" ann",
"}",
"method doannotation {",
" if (acceptKeyword \"is\" .not) then {",
" return false",
" }",
" next",
" def anns = ast.noAnnotations",
" if (unsuccessfulParse {expression(noBlocks)}) then {",
" errorMissingAnnotation",
" }",
" while {sym.isComma} do {",
" anns.add(checkAnnotation(values.pop))",
" next",
" if (unsuccessfulParse {expression(noBlocks)}) then {",
" errorMissingAnnotation",
" }",
" }",
" anns.add(checkAnnotation(values.pop))",
" anns",
"}",
"",
"method errorMissingAnnotation {",
" def suggestions = list [ ]",
" var suggestion := errormessages.suggestion.new",
" def nextTok = findNextValidToken [\"assignment\"]",
" if (nextTok == sym) then {",
" suggestion.insert(\" «annotation»\")afterToken(lastToken)",
" } else {",
" suggestion.replaceTokenRange(sym, nextTok.prev)leading(true)trailing(false)with(\" «annotation»\")",
" }",
" suggestions.push(suggestion)",
" suggestion := errormessages.suggestion.new",
" suggestion.deleteTokenRange(lastToken, nextTok.prev)leading(true)trailing(false)",
" suggestions.push(suggestion)",
" errormessages.syntaxError(\"one or more annotations separated by commas must follow 'is'.\")",
" atRange(lastToken.line, lastToken.column, lastToken.endCol)",
" withSuggestions(suggestions)",
"}",
"",
"method typeterm {",
" if (sym.isIdentifier) then {",
" pushIdentifier",
" generic",
" dotrest(noBlocks)",
" } elseif {acceptKeyword \"outer\"} then {",
" doouter",
" skipSeparators",
" if (! sym.isDot) then {",
" values.pop",
" // `outer` without a following `.method` is not a valid type",
" } else {",
" dotrest(noBlocks)",
" }",
" } elseif {acceptKeyword \"self\"} then {",
" doself",
" if (! sym.isDot) then {",
" values.pop",
" // `outer` without a following `.method` is not a valid type",
" } else {",
" dotrest(noBlocks)",
" }",
" } elseif {acceptKeyword \"interface\"} then {",
" interfaceLiteral",
" } elseif {acceptKeyword \"Unknown\"} then {",
" values.push(ast.unknownLiteral)",
" next",
" } elseif {acceptKeyword \"Self\"} then {",
" values.push(ast.selfTypeLiteral)",
" next",
" } elseif {acceptKeyword \"...\"} then {",
" values.push(ast.ellipsis)",
" next",
" }",
"}",
"",
"method typeexpression {",
" var sz := values.size",
" if (sym.isLParen) then {",
" def prevStatementToken = statementToken",
" statementToken := sym",
" next",
" if (unsuccessfulParse {typeexpression}) then {",
" def suggestion = errormessages.suggestion.new",
" def nextTok = findNextValidToken [\"rparen\"]",
" if (nextTok == sym) then {",
" suggestion.insert(\"«type expression»\")afterToken(lastToken)",
" } else {",
" suggestion.replaceTokenRange(sym, nextTok.prev) leading (true)",
" trailing(false) with \"«type expression»\"",
" }",
" errormessages.syntaxError \"parentheses must contain a valid type expression.\"",
" atPosition(sym.line, sym.column) withSuggestion(suggestion)",
" }",
" if (sym.kind != \"rparen\") then {",
" checkBadOperators",
" def suggestion = errormessages.suggestion.new",
" suggestion.insert(\")\")afterToken(lastToken)",
" errormessages.syntaxError \"a type expression beginning with a '(' must end with a ')'.\"",
" atRange(lastToken.line, lastToken.column, lastToken.endCol)",
" withSuggestion(suggestion)",
" }",
" statementToken := prevStatementToken",
" next",
" } else {",
" typeterm",
" }",
" if (values.size > sz) then {",
" dotrest(noBlocks)",
" typeexpressionrest",
" }",
" // TODO: check that the expression doesn't contain arbitrary requests or var references.",
" // This has to happen in the identifier resolution phase.",
"}",
"",
"method newIf(cond, thenList, elseList) {",
" def thenBlock = ast.block(sequence.empty, thenList)",
" def elseBlock = ast.block(sequence.empty, elseList)",
" ast.ifExpr(cond, thenBlock, elseBlock)",
"}",
"",
"method reportSyntaxError(message) before (expectedTokens) {",
" def suggestions = list [ ]",
" var suggestion := errormessages.suggestion.new",
" def nextTok = findNextValidToken (expectedTokens)",
" if (nextTok == sym) then {",
" suggestion.insert(\" «expression»\")afterToken(lastToken)",
" } else {",
" suggestion.replaceTokenRange(sym, nextTok.prev)leading(true)trailing(false)with(\" «expression»\")",
" }",
" suggestions.push(suggestion)",
" suggestion := errormessages.suggestion.new",
" suggestion.deleteTokenRange(lastToken, nextTok.prev)leading(true)trailing(false)",
" suggestions.push(suggestion)",
" errormessages.syntaxError(message)",
" atPosition(sym.line, sym.column)",
" withSuggestions(suggestions)",
"}",
"",
"method reportMissingArrow {",
" def suggestion = errormessages.suggestion.new",
" if ((sym.isAssignment) || (sym.value == \"=\")) then {",
" suggestion.replaceToken(sym)with(\"->\")",
" } else {",
" suggestion.insert(\" ->\")afterToken(lastToken)",
" }",
" errormessages.syntaxError(\"in a block with parameters, the parameters must be followed by '->'\")",
" atPosition(sym.line, sym.column) withSuggestion(suggestion)",
"}",
"",
"method reportBadRhs {",
" // a bind symbol := was not followed by a valid expression.",
"",
" def suggestions = list [ ]",
" var suggestion := errormessages.suggestion.new",
" def nextTok = findNextValidToken [\"rbrace\"]",
" if (nextTok == sym) then {",
" suggestion.insert(\" «expression»\")afterToken(lastToken)",
" } else {",
" suggestion.replaceTokenRange(sym, nextTok.prev)leading(true)trailing(false)with(\" «expression»\")",
" }",
" suggestions.push(suggestion)",
" suggestion := errormessages.suggestion.new",
" suggestion.deleteTokenRange(lastToken, nextTok.prev)leading(true)trailing(false)",
" suggestions.push(suggestion)",
" errormessages.syntaxError(\"a valid expression must follow ':='.\")",
" atPosition(sym.line, sym.column) withSuggestions(suggestions)",
"}",
"",
"method block {",
" // Parses a block. Since a block is (a) treated as a statement, and",
" // (b) may have statements inside, we save and restore the setting of the",
" // global variables relevant to the statement context.",
"",
" if (sym.isLBrace) then {",
" def btok = sym",
" next",
" skipSeparators",
" def oldStatementToken = statementToken",
" statementToken := sym",
" var isMatchingBlock := false",
"",
" // a block may start with or without parameters. We assume that",
" // parameters are present, parse the first expression, and then check.",
" // If it wasn't a parameter, we back-up the parse position.",
" def savedPosition = saveParsePosition",
" def params = blockParameters",
" if (params.isEmpty) then {",
" restoreParsePosition(savedPosition)",
" }",
"",
" def blockNode = blockBody (params) beginningWith (btok)",
" values.push(blockNode)",
" statementToken := oldStatementToken",
" }",
"}",
"",
"method blockParameters {",
" // parse all the parameters of this block, and return them as a",
" // collection of identifier nodes.",
" def params = list [ ]",
" while {blockParameter(params)} do {",
" if (sym.isArrow) then {",
" next",
" skipSeparators",
" return params",
" }",
" if (sym.isComma) then {",
" next",
" } else {",
" reportMissingArrow",
" }",
" }",
" skipSeparators",
" return params",
"}",
"",
"method blockParameter(params) -> Boolean {",
" // parse one parameter, if possible, push it onto params, and",
" // return true. If the next expression is not a parameter, return false.",
"",
" var paramIsPattern := sym.isLParen",
" // Parsing the expression ‹(a)› will return an identifierNode‹a› .",
" // Checking for a paren lets us distinguish parameter from pattern.",
" if (successfulParse {expression(blocksOK)}) then {",
" if (sym.isComma || sym.isArrow || sym.isColon) then {",
" // we have found a parameter",
" var thisParam := values.pop",
" if (paramIsPattern || thisParam.isIdentifier.not) then {",
" paramIsPattern := true",
" thisParam := ast.wildcardIdentifier(thisParam)",
" // put the pattern in the type field",
" }",
" thisParam.isBindingOccurrence := true",
" thisParam.isParameter := true",
" if (paramIsPattern && sym.isColon) then {",
" reportSyntaxError(\"a block parameter that's an expression is assumed to mean \" ++",
" \"_:‹expression›, and so cannot be followed by a colon\")",
" before [\"arrow\", \"comma\"]",
" }",
" if (sym.isColon) then {",
" // We allow an expression for v: <PatternExpression>",
" next",
" if (successfulParse {expression(blocksOK)} .not) then {",
" reportSyntaxError \"a block parameter must have a pattern or type expression after the ':'.\" before [\"arrow\", \"rbrace\"]",
" }",
" thisParam.dtype := values.pop",
" }",
" params.push(thisParam)",
" return true",
" } else {",
" // we just parsed the first expression in the block",
" return false",
" }",
" } else {",
" return false // we didn't parse anything",
" }",
"}",
"",
"method blockBody(params) beginningWith (btok) {",
" // returns a block AST node. params is the list of parameters,",
" // which may be empty, and btok the lbrace that started the block.",
" // The module variable sym is the first token in the body, and lastToken",
" // is the preceeding lbrace that started the block, or the arrow that",
" // terminated the parameter list (if there was one),",
" // or the comment that appears after the lbrace, if there was one.",
"",
" def originalValues = values",
" values := list []",
" while {sym.isRBrace.not} do {",
" // Take the body of the block",
" if (unsuccessfulParse {statement}) then {",
" def suggestion = errormessages.suggestion.new",
" suggestion.insert \"}\" afterToken (lastToken)",
" errormessages.syntaxError \"a block must end with a '}'.\"",
" atPosition(sym.line, sym.column) withSuggestion(suggestion)",
" }",
" separator",
" }",
" def etok = sym // the closing rbrace",
" next",
" def body = values",
" values := originalValues",
" return ast.block(params, body).setPositionFrom(btok)",
"}",
"",
"",
"// Accept an \"if\" statement. This is a special syntactic case, rather",
"// than just a call with a multi-part method name - it might be possible",
"// to change that and compensate later on.",
"method doif {",
" if (sym.isIdentifier && (sym.value == \"if\")) then {",
" def btok = sym",
" next",
" def opener = if (sym.isLParen || {sym.isLBrace})",
" then { sym.value } else { \"-missing-\" }",
" def closer = if (opener == \"(\") then { \")\" }",
" elseif { opener == \"\\{\" } then { \"\\}\" }",
" else { \"-nothing-\" }",
" if (opener == \"-missing-\") then {",
" def suggestion = errormessages.suggestion.new",
" // Look ahead for a rparen or then.",
" def nextTok = findNextToken { t ->",
" (t.line == btok.line) && ((t.isRParen) ||",
" (t.isRBrace) || (t.isLBrace) ||",
" ((t.isIdentifier) && (t.value == \"then\")))",
" }",
" if (false == nextTok) then {",
" suggestion.insert(\" («condition») then \\{\")afterToken(btok)",
" } elseif { nextTok.isRParen } then {",
" if (nextTok == sym) then {",
" suggestion.insert(\"(«condition»\")beforeToken(sym)",
" } else {",
" suggestion.insert(\"(\")beforeToken(sym)",
" }",
" } elseif { nextTok.isLBrace } then {",
" if (nextTok == sym) then {",
" suggestion.insert(\" («condition») then\")afterToken(btok)",
" } else {",
" suggestion.insert(\"(\")beforeToken(sym)",
" suggestion.insert(\") then\")afterToken(nextTok.prev)andTrailingSpace(true)",
" }",
" } elseif { nextTok.isIdentifier } then {",
" if (nextTok == sym) then {",
" suggestion.insert(\"(«condition») \")beforeToken(sym)",
" } else {",
" suggestion.insert(\"(\")beforeToken(sym)",
" suggestion.insert(\")\")afterToken(nextTok.prev)andTrailingSpace(true)",
" }",
" }",
" errormessages.syntaxError(\"an if statement must have a condition \" ++",
" \"in parentheses or braces after the 'if'.\")",
" atPosition(sym.line, sym.column) withSuggestion(suggestion)",
" }",
" next",
" if (unsuccessfulParse {expression(blocksOK)}) then {",
" def suggestion = errormessages.suggestion.new",
" // Look ahead for a rparen.",
" var nextTok := findNextToken { t -> (t.line == lastToken.line) && (t.isRParen) }",
" if (false == nextTok) then {",
" nextTok := findNextValidToken [\"rparen\"]",
" if (nextTok == sym) then {",
" suggestion.insert(\"«expression») then \\{\")afterToken(lastToken)",
" } else {",
" suggestion.replaceTokenRange(sym, nextTok.prev)",
" leading(true)trailing(false)with(\"«expression») then \\{\")",
" }",
" errormessages.syntaxError(\"an if statement must have a \" ++",
" \"condition in parentheses or braces after the 'if'.\")",
" atPosition(sym.line, sym.column)",
" withSuggestion(suggestion)",
" } else {",
" if (nextTok == sym) then {",
" suggestion.insert(\"«expression»\")afterToken(lastToken)",
" errormessages.syntaxError(\"an if statement must have a \" ++",
" \"condition in parentheses or braces after the 'if'.\")",
" atPosition(sym.line, sym.column)",
" withSuggestion(suggestion)",
" } else {",
" suggestion.replaceTokenRange(sym, nextTok.prev)",
" leading(false)trailing(true)with(\"«expression»\")",
" errormessages.syntaxError(\"an if statement must have a \" ++",
" \"condition in parentheses or braces after the 'if'.\")",
" atRange(sym.line, sym.column, nextTok.column - 1)",
" withSuggestion(suggestion)",
" }",
" }",
" }",
" if (sym.value != closer) then {",
" checkBadOperators",
" def suggestion = errormessages.suggestion.new",
" suggestion.insert(\")\")afterToken(lastToken)",
" errormessages.syntaxError(\"an expression beginning with a \"++",
" \"'{opener}' must end with a '{closer}'.\")",
" atPosition(lastToken.line, lastToken.column + lastToken.size)",
" withSuggestion(suggestion)",
" }",
" next",
" var cond := values.pop",
" var body := list []",
"",
" var elseblock := list []",
" var curelse := elseblock",
" // These two variables are for else/elseif handling. An 'elseif' is",
" // turned into nested 'if' statements for the AST; `curelse` points",
" // to the most deeply-nested of those (where any eventual \"else\"",
" // blocks will go). `elseblock` contains the statements of the",
" // top-level 'else' block --- if there are any 'elseif's, that top-",
" // level 'else' will comprise just one statement: another if.",
" var v",
" if (sym.isIdentifier && (sym.value == \"then\")) then {",
" next",
" if (sym.kind != \"lbrace\") then {",
" def suggestion = errormessages.suggestion.new",
" def closingBrace = findClosingBrace(btok, true)",
" if (closingBrace.found.not) then {",
" if (closingBrace.tok == lastToken) then {",
" suggestion.replaceToken(lastToken)leading(false)trailing(true)with(\"then \\{}\")",
" } else {",
" suggestion.addLine(closingBrace.tok.line + 0.1, \"}\")",
" suggestion.replaceToken(lastToken)leading(false)trailing(true)with(\"then \\{\")",
" }",
" } else {",
" suggestion.replaceToken(lastToken)leading(false)trailing(true)with(\"then \\{\")",
" }",
" errormessages.syntaxError(\"an if statement must have a '\\{' after the 'then'.\")atPosition(",
" lastToken.line, lastToken.column + lastToken.size)withSuggestion(suggestion)",
" }",
" next",
" while {sym.isRBrace.not} do {",
" if (unsuccessfulParse {statement}) then {",
" def suggestion = errormessages.suggestion.new",
" def closingBrace = findClosingBrace(btok, false)",
" if (closingBrace.found.not) then {",
" if (closingBrace.tok == lastToken) then {",
" suggestion.insert(\"}\")afterToken(lastToken)",
" } else {",
" suggestion.addLine(closingBrace.tok.line + 0.1, \"}\")",
" }",
" }",
" suggestion.deleteToken(sym)",
" errormessages.syntaxError \"the then clause of an if statement must end with a '}'\"",
" atPosition(sym.line, sym.column)",
" withSuggestion(suggestion)",
" }",
" separator",
" body.push(values.pop)",
" }",
" next",
" var econd",
" var eif",
" var newelse",
" var ebody",
" while {sym.isIdentifier && (sym.value == \"elseif\")} do {",
" // Currently, the parser just accepts arbitrarily many",
" // \"elseifs\", turning them into ifs inside the else.",
" // TODO: allow blocks after elseif to contain a sequence of expressions.",
" statementToken := sym",
" next",
" if (sym.isLBrace.not) then {",
" def suggestion = errormessages.suggestion.new",
" // Look ahead for a rbrace or then.",
" def nextTok = findNextToken { t ->",
" (t.line == statementToken.line)",
" && ((t.isRBrace || t.isLBrace)",
" || (t.isIdentifier && (t.value == \"then\"))) }",
" if (false == nextTok) then {",
" suggestion.insert(\" \\{ «expression» \\} then \\{\")afterToken(statementToken)",
" } elseif { nextTok.isRBrace } then {",
" if (nextTok == sym) then {",
" suggestion.insert(\"\\{ «expression» \\}\")beforeToken(sym)",
" } else {",
" suggestion.insert(\"\\{ \")beforeToken(sym)",
" }",
" } elseif { nextTok.isLBrace } then {",
" if (nextTok == sym) then {",
" suggestion.insert(\" \\{ «expression» \\} then\")afterToken(statementToken)",
" } else {",
" suggestion.insert(\"\\{ \")beforeToken(sym)",
" suggestion.insert(\" \\} then\")afterToken(nextTok.prev)andTrailingSpace(true)",
" }",
" } elseif { nextTok.isIdentifier } then {",
" if (nextTok == sym) then {",
" suggestion.insert(\"\\{ «expression» \\} \")beforeToken(sym)",
" } else {",
" suggestion.insert(\"\\{ \")beforeToken(sym)",
" suggestion.insert(\" \\}\")afterToken(nextTok.prev)andTrailingSpace(true)",
" }",
" }",
" errormessages.syntaxError(\"an elseif statement must have a \" ++",
" \"condition in braces after the 'elseif'.\")",
" atPosition(sym.line, sym.column)",
" withSuggestion(suggestion)",
" }",
" next",
" if (unsuccessfulParse {expression(blocksOK)}) then {",
" def suggestion = errormessages.suggestion.new",
" // Look ahead for a rbrace or then.",
" var nextTok := findNextToken { t ->",
" (t.line == lastToken.line) && (t.isRBrace)}",
" if (false == nextTok) then {",
" nextTok := findNextValidToken [\"rbrace\"]",
" if (nextTok == sym) then {",
" suggestion.insert(\"«expression» \\} then \\{\")afterToken(lastToken)",
" } else {",
" suggestion.replaceTokenRange(sym, nextTok.prev)leading(true)trailing(false)with(\"«expression» \\} then \\{\")",
" }",
" errormessages.syntaxError(\"an elseif clause must have an expression in braces after the 'elseif'.\")atPosition(",
" sym.line, sym.column)withSuggestion(suggestion)",
" } else {",
" if (nextTok == sym) then {",
" suggestion.insert(\"«expression»\")afterToken(lastToken)",
" errormessages.syntaxError(\"an elseif clause must have an expression in braces after the 'elseif'.\")atPosition(",
" sym.line, sym.column)withSuggestion(suggestion)",
" } else {",
" //checkInvalidExpression",
" suggestion.replaceTokenRange(sym, nextTok.prev)leading(false)trailing(true)with(\"«expression»\")",
" errormessages.syntaxError(\"(4) an elseif statement must have an expression in braces after the 'elseif'.\")atRange(",
" sym.line, sym.column, nextTok.column - 1)withSuggestion(suggestion)",
" }",
" }",
" }",
" if (sym.value != \"\\}\") then {",
" checkBadOperators",
" def suggestion = errormessages.suggestion.new",
" suggestion.insert \"}\" afterToken (lastToken)",
" errormessages.syntaxError(\"a condition beginning with a \" ++",
" \"'\\{' must end with a '\\}'.\")",
" atPosition(lastToken.line, lastToken.column + lastToken.size)",
" withSuggestion(suggestion)",
" }",
" next",
" econd := values.pop",
" if (sym.isIdentifier && (sym.value == \"then\")) then {",
" next",
" ebody := list []",
" } else {",
" def suggestion = errormessages.suggestion.new",
" if (sym.isLBrace) then {",
" def closingBrace = findClosingBrace(statementToken, false)",
" if (closingBrace.found.not) then {",
"