Minor changes to help text. Add AST checker module
This commit is contained in:
parent
3953751cef
commit
740999bbc2
|
@ -74,7 +74,7 @@ Options
|
|||
--noParse Don't parse the code (i.e. stop at the tokenization stage)
|
||||
--noTC, --noTypeCheck Don't typecheck the code (i.e. stop at the parsing stage)
|
||||
|
||||
--showMismatches Show all mismatches when function dispatching fails (output is really verbose)
|
||||
--showMismatches Show all mismatches when type dispatching fails (output is really verbose)
|
||||
--debugLexer Show the lexer's output
|
||||
--debugParser Show the parser's output
|
||||
--debugTypeChecker Show the typechecker's output
|
||||
|
@ -82,17 +82,17 @@ Options
|
|||
--listWarns Show a list of all warnings
|
||||
-b, --backend Select the compilation backend. Currently only supports 'bytecode' (the default)
|
||||
-c, --compile Compile the code, but do not run the main module
|
||||
-o, --output Rename the output executable to this (a "bc" extension is added for bytecode files,
|
||||
-o, --output Rename the output executable to this (a "pbc" extension is added for bytecode files,
|
||||
if not already present)
|
||||
-s, --string Run the given string as if it were a file (the filename is set to '<string>')
|
||||
--cacheDir Specify a directory where the peon compiler will dump code generation results
|
||||
to speed up subsequent builds. Defaults to ".buildcache"
|
||||
|
||||
The following options are specific to the 'bytecode' backend:
|
||||
-n, --noDump Do not dump bytecode files to the source directory. Note that
|
||||
no files are dumped when using -s/--string
|
||||
--breakpoints Set debugging breakpoints at the given bytecode offsets.
|
||||
-n, --noDump Do not dump bytecode files to the source directory. Redundant
|
||||
when using -s/--string
|
||||
--breakpoints Pause execution and invoke the debugger at the given bytecode offsets.
|
||||
Input should be a comma-separated list of positive integers
|
||||
(spacing is irrelevant). Only works if peon was compiled with
|
||||
-d:debugVM
|
||||
VM debugging enabled
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import frontend/parsing/parser
|
||||
import frontend/parsing/lexer
|
||||
|
||||
|
||||
import std/strformat
|
||||
import std/sequtils
|
||||
import std/strutils
|
||||
|
||||
|
||||
type
|
||||
Comparison* = ref object
|
||||
# The result of a comparison between
|
||||
# AST nodes
|
||||
|
||||
match*: bool # Did the comparison succeed?
|
||||
reason*: string # If it failed, why?
|
||||
exc*: ref Exception # If the comparison crashed with an error, this is the exception
|
||||
inner*: Comparison # A comparison could fail because another, inner comparison failed
|
||||
nodes*: tuple[provided, expected: ASTNode]
|
||||
|
||||
ASTChecker* = ref object
|
||||
## An AST validator. Ensures the
|
||||
## output of the parser matches
|
||||
## the structure we expect
|
||||
|
||||
|
||||
proc newASTChecker*: ASTChecker =
|
||||
## Initializes a new, blank AST checker
|
||||
new(result)
|
||||
|
||||
|
||||
template ensureEqualType(self, other: ASTNode) = doAssert self.kind == other.kind, "cannot compare AST nodes of different types"
|
||||
|
||||
|
||||
proc newComparison(match: bool, expected, provided: ASTNode, reason: string = "", exc: ref Exception = nil, inner: Comparison = nil): Comparison =
|
||||
## Initializes a new comparison object
|
||||
new(result)
|
||||
result.match = match
|
||||
result.nodes.expected = expected
|
||||
result.nodes.provided = provided
|
||||
result.reason = reason
|
||||
result.exc = exc
|
||||
result.inner = inner
|
||||
|
||||
|
||||
proc compare(provided, expected: LiteralExpr): Comparison =
|
||||
## Compares two literal expressions
|
||||
## for structural equality
|
||||
ensureEqualType(provided, expected)
|
||||
result = newComparison(true, expected, provided)
|
||||
case provided.kind:
|
||||
of infExpr, nanExpr, falseExpr, trueExpr:
|
||||
return
|
||||
of strExpr, intExpr, hexExpr, octExpr, binExpr:
|
||||
if provided.literal.kind != expected.literal.kind:
|
||||
result.match = false
|
||||
result.reason = &"expected token of type {expected.literal.kind}, got {provided.literal.kind}"
|
||||
elif provided.literal.lexeme != expected.literal.lexeme:
|
||||
result.match = false
|
||||
result.reason = &"expected lexeme to be {expected.literal.lexeme.escape()}, got {provided.literal.lexeme.escape()}"
|
||||
else:
|
||||
raise newException(ValueError, &"literal comparison attempted with non-literal node of type {provided.kind}")
|
||||
|
||||
|
||||
proc compare(provided, expected: ASTNode): Comparison =
|
||||
## Compares two generic AST nodes for
|
||||
## structural equality
|
||||
ensureEqualType(provided, expected)
|
||||
if provided.isLiteral():
|
||||
return compare(LiteralExpr(provided), LiteralExpr(expected))
|
||||
|
||||
|
||||
proc check*(self: ASTChecker, code: string, structure: ParseTree): Comparison =
|
||||
## Ensures the given source code parses into the given structure
|
||||
try:
|
||||
result = newComparison(true, nil, nil)
|
||||
var
|
||||
tokenizer = newLexer()
|
||||
parser = newParser()
|
||||
tokens = tokenizer.lex(code, "test")
|
||||
tree = parser.parse(tokens, "test", tokenizer.getLines(), tokenizer.getSource())
|
||||
if len(tree) != len(structure):
|
||||
result.match = false
|
||||
result.reason = &"Number of provided nodes ({structure.len()}) does not match number of returned nodes ({tree.len()})"
|
||||
for (provided, expected) in zip(tree, structure):
|
||||
let cmp = compare(provided, expected)
|
||||
if not cmp.match:
|
||||
result.inner = cmp
|
||||
result.reason = "Comparison failed, see the inner attribute for more details"
|
||||
break
|
||||
except LexingError:
|
||||
return Comparison(match: false, reason: "Tokenization failed", exc: getCurrentException())
|
||||
except ParseError:
|
||||
return Comparison(match: false, reason: "Parsing failed", exc: getCurrentException())
|
Loading…
Reference in New Issue