Skip to content

Commit

Permalink
Merge pull request #1204 from DanielXMoore/comptime-fixes
Browse files Browse the repository at this point in the history
Comptime fixes
  • Loading branch information
edemaine authored May 2, 2024
2 parents 07c588e + 41f7ff2 commit 160ef0b
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 58 deletions.
5 changes: 3 additions & 2 deletions source/parser.hera
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,9 @@ SingleLineBinaryOpRHS
return [ws1 || [], op, ws2, rhs]

RHS
UnaryExpression
# NOTE: Check for comptime block first, to avoid matching as function call
ExpressionizedStatementWithTrailingCallExpressions
UnaryExpression

# https://262.ecma-international.org/#prod-UnaryExpression
UnaryExpression
Expand Down Expand Up @@ -5733,7 +5734,7 @@ Comma
return { $loc, token: $1 }

Comptime
"comptime" NonIdContinue ->
"comptime" NonIdContinue !":" ->
return { $loc, token: $1 }

ConstructorShorthand
Expand Down
13 changes: 5 additions & 8 deletions source/parser/block.civet
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {
ASTNode
ASTNodeBase
ASTNodeObject
BlockStatement
FunctionNode
StatementTuple
Expand Down Expand Up @@ -108,13 +108,12 @@ function makeBlockFragment(): BlockStatement
* Replace `child` with `replacement` inside the `block`.
* Assumes a `StatementTuple[]` for `block.expressions`
*/
function replaceBlockExpression(node: BlockStatement, child: ASTNodeBase, replacement: ASTNodeBase): void
function replaceBlockExpression(node: BlockStatement, child: ASTNodeObject, replacement: ASTNodeObject): void
found .= false

{ expressions } := node

for (let i = 0, l = expressions.length; i < l; i++)
statement := expressions[i]
for each statement of expressions
[, s,] := statement

if s is child
Expand All @@ -129,19 +128,17 @@ function replaceBlockExpression(node: BlockStatement, child: ASTNodeBase, replac
* Gets the indentation node from a statement. Includes newline,
* excludes comments, strips location info.
*/
function getIndent(statement: StatementTuple) {
function getIndent(statement: StatementTuple)
let indent = statement?.[0]
if (Array.isArray(indent)) {
if Array.isArray(indent)
indent = indent.flat(Infinity)

return indent.filter((n) => n and !(n.type is "Comment")).map((n) => {
if (typeof n is "string") return n
if (n.token != null) return n.token
return ""
})
}
return indent
}

function hoistRefDecs(statements: StatementTuple[]): void
gatherRecursiveAll(statements, (s) => s.hoistDec)
Expand Down
8 changes: 5 additions & 3 deletions source/parser/declaration.civet
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
ASTNodeParent
ASTRef
Binding
BlockStatement
Children
DeclarationStatement
IfStatement
Expand Down Expand Up @@ -113,7 +114,7 @@ function processDeclarations(statements: StatementTuple[]): void
if initializer
prependStatementExpressionBlock initializer, statement

function prependStatementExpressionBlock(initializer: Initializer, statement: {children: Children}): ASTRef?
function prependStatementExpressionBlock(initializer: Initializer, statement: ASTNodeParent): ASTRef?
{expression: exp} .= initializer

// Handle nested statement expression
Expand Down Expand Up @@ -249,8 +250,9 @@ function processDeclarationCondition(condition, rootCondition, parent: ASTNodePa
rootCondition.blockPrefix = getPatternBlockPrefix(pattern, ref, decl, suffix)

function processDeclarationConditions(node: ASTNode): void
gatherRecursiveAll node, (n) =>
n.type is "IfStatement" or n.type is "IterationStatement" or n.type is "SwitchStatement"
gatherRecursiveAll node,
(n): n is IfStatement | IterationStatement | SwitchStatement =>
n.type is "IfStatement" or n.type is "IterationStatement" or n.type is "SwitchStatement"
.forEach (s) =>
processDeclarationConditionStatement s

Expand Down
4 changes: 2 additions & 2 deletions source/parser/op.civet
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ precedenceCustomDefault := precedenceMap.get("custom")!

function getPrecedence(op: BinaryOp): number
// "&&" generated by expandChainedComparisons
if typeof op === "string"
if op <? "string"
precedenceMap.get(op) ??
throw new Error `Unknown operator: ${op}`
else if op.type is "PatternTest"
precedenceRelational
else if typeof op.prec === "number"
else if op.prec <? "number"
op.prec
else
precedenceMap.get(op.prec ?? op.token) ??
Expand Down
42 changes: 19 additions & 23 deletions source/parser/traversal.civet
Original file line number Diff line number Diff line change
Expand Up @@ -56,49 +56,45 @@ function findAncestor(
// without recursing into nested blocks/for loops

function gatherNodes<T extends ASTNodeObject>(node: ASTNode, predicate: Predicate<T>): T[]
if (node == null) return []
return [] if not node? or node <? "string"

if (Array.isArray(node)) {
if Array.isArray(node)
return node.flatMap((n) => gatherNodes(n, predicate))
}

if (predicate(node)) {
if predicate(node)
return [node]
}

switch (node.type) {
case "BlockStatement":
switch node.type
when "BlockStatement"
return []
case "ForStatement":
when "ForStatement"
// Descend into expressions but not into declarations or the body of the for loop
const isDec = node.declaration?.type is "Declaration"
return node.children.flatMap((n) => {
if (isDec and n is node.declaration) return []
return gatherNodes(n, predicate)
})
default:
isDec := node.declaration?.type is "Declaration"
return node.children.flatMap (n) =>
if isDec and n is node.declaration
[]
else
gatherNodes(n, predicate)
else
return gatherNodes(node.children, predicate)
}

// Gather nodes that match a predicate recursing into all unmatched children
// i.e. if the predicate matches a node it is not recursed into further
function gatherRecursive<T extends ASTNodeObject, S extends ASTNodeObject = never>(node: ASTNode, predicate: Predicate<T>, skipPredicate?: Predicate<S>): Exclude<T, S>[]
if (node == null) return []
return [] if not node? or node <? "string"

if (Array.isArray(node)) {
return node.flatMap((n) => gatherRecursive(n, predicate, skipPredicate))
}
if Array.isArray(node)
return node.flatMap gatherRecursive(., predicate, skipPredicate)

if (skipPredicate?.(node)) return []

if (predicate(node)) {
return [node]
}
if predicate(node)
return [node as Exclude<T, S>]

return gatherRecursive(node.children, predicate, skipPredicate)

function gatherRecursiveAll<T extends ASTNodeObject>(node: ASTNode, predicate: Predicate<T>): T[]
if (node == null) return []
return [] if not node? or node <? "string"

if Array.isArray node
return node.flatMap (n) => gatherRecursiveAll n, predicate
Expand Down
34 changes: 14 additions & 20 deletions source/parser/util.civet
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ function addParentPointers(node: ASTNode, parent?: ASTNodeObject): void
return

node = node as ASTNodeObject
node.parent = parent
node.parent = parent if parent?
if node.children
for child of node.children
for each child of node.children
addParentPointers(child, node)

/**
Expand All @@ -62,30 +62,23 @@ function addParentPointers(node: ASTNode, parent?: ASTNodeObject): void
*
* TODO: preserve ref identities
*/
function clone(node: ASTNode) {
function clone(node: ASTNode)
removeParentPointers(node)
return deepCopy(node)
}

function removeParentPointers(node): void {
if (node == null) return
if (typeof node !== "object") return
function removeParentPointers(node: ASTNode): void
return unless node? <? "object"

// NOTE: Arrays are transparent and skipped when traversing via parent
if (Array.isArray(node)) {
for (const child of node) {
removeParentPointers(child)
}
if Array.isArray node
for child of node
removeParentPointers child
return
}

node.parent = null
if (node.children) {
for (const child of node.children) {
removeParentPointers(child)
}
}
}
if node.children
for child of node.children
removeParentPointers child

// If node is not an ASTNodeObject, wrap it in a special "Wrapper" node
// so that it can have a parent (second argument).
Expand All @@ -109,7 +102,7 @@ function maybeUnwrap(node: ASTNode): ASTNode
function isASTNodeObject(node: ASTNode): node is ASTNodeObject
(and)
node <? "object"
node != null
node?
not Array.isArray node

function isParent(node: ASTNode): node is IsParent
Expand All @@ -121,7 +114,8 @@ function isToken(node: ASTNode): node is IsToken
function isEmptyBareBlock(node: ASTNode): boolean
if (node?.type !== "BlockStatement") return false
{ bare, expressions } := node
return bare &&
return bare and
//expressions is like [], [, {type: "EmptyStatement"}]
(expressions.length is 0 ||
(expressions.length is 1 &&
expressions[0][1]?.type is "EmptyStatement"))
Expand Down
16 changes: 16 additions & 0 deletions test/comptime.civet
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ describe "comptime", ->
const value = 25
""", options

testCase """
in expression
---
value := 1 + comptime 2+3
---
const value = 1 + 5
""", options

describe "disabled", ->
testCase """
empty
Expand Down Expand Up @@ -101,6 +109,14 @@ describe "comptime", ->
const x = (()=>{ return 1+2})()
"""

testCase """
as key
---
options := comptime: true
---
const options = {comptime: true}
"""

describe "serialize", ->
it "numbers", =>
assert.equal serialize(1), "1"
Expand Down
1 change: 1 addition & 0 deletions test/object.civet
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,7 @@ describe "object", ->
break
class
catch
comptime
const
continue
declare
Expand Down

0 comments on commit 160ef0b

Please sign in to comment.