TypeError: expected an object, function, or array
rbecheras opened this issue · 10 comments
I got this error using latest version of snapdragon
/home/remi/app/node_modules/snapdragon/node_modules/define-property/index.js:18
throw new TypeError('expected an object, function, or array');
^
TypeError: expected an object, function, or array
at defineProperty (/home/remi/app/node_modules/snapdragon/node_modules/define-property/index.js:18:11)
at Parser.eos (/home/remi/app/node_modules/snapdragon/lib/parser.js:434:9)
at Parser.parse (/home/remi/app/node_modules/snapdragon/lib/parser.js:536:20)
at Object.<anonymous> (/home/remi/app/src/lib/index.js:15:20)
at Module._compile (module.js:652:30)
at loader (/home/remi/app/node_modules/babel-register/lib/node.js:144:5)
at Object.require.extensions.(anonymous function) [as .js] (/home/remi/app/node_modules/babel-register/lib/node.js:154:7)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
using this parser:
function parser () {
const pos = this.position()
const match = this.match(/^\s*(if)\s*\(\s*(.*?)\s*\)\s*(?=then)/)
const prev = this.prev()
if (!match) return
const ifNode = pos(this.node({
type: 'ConditionnalState.if',
val: match[1],
condition: match[2]
}))
const conditionnalStateNode = pos(this.node({
type: 'ConditionnalState',
nodes: [ifNode]
}))
this.push('ConditionnalState', conditionnalStateNode)
prev.nodes.push(conditionnalStateNode)
}
but if I change it for the following
function parser () {
const pos = this.position()
const match = this.match(/^\s*(if)\s*\(\s*(.*?)\s*\)\s*(?=then)/)
// const prev = this.prev()
if (!match) return
const ifNode = pos(this.node({
type: IfConditionnalStateParser.type,
val: match[1],
condition: match[2]
}))
// const conditionnalStateNode = pos(this.node({
// type: 'ConditionnalState',
// nodes: [ifNode]
// }))
// this.push('ConditionnalState', conditionnalStateNode)
// prev.nodes.push(conditionnalStateNode)
return ifNode
}
Here, there is no error.
I can't understand why that error.
It is the bundled eos()
parser that throw TypeError
because prev.parent
is undefined
.
Line 433 in 82fb20d
But I don't know why/when node should/must have a parent
property.
I see that the condition for that line is hasOpenAndClose(prev)
but I dont know hat to do with that.
I'm trying to create an AST to parse plantuml
files (for activity diagrams only for now).
Here is the actual tree I get (when I use the second version of the mentionned parser, the first make eos() throw
Node {
type: 'root',
errors: [],
nodes:
[ Node { type: 'bos', val: '' },
Node {
type: 'Uml',
title: 'Activité Eolienne',
nodes:
[ Node {
type: 'Activity',
nodes:
[ Node { type: 'StaticState', val: 'Je veux installer une éolienne' },
Node {
type: 'ConditionnalState.if',
val: 'if',
condition: 'Hauteur >= 12m (mât + nacelle)' },
Node { type: 'ConditionnalState.then', val: 'then' },
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.condition.open', val: '(' },
Node { type: 'All', val: 'OUI)' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'StaticState', val: 'CERFA = PC' },
Node { type: 'Space', val: ' ' },
Node { type: 'Activity.stop', val: 'stop' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'ConditionnalState.else', val: 'else' },
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.condition.open', val: '(' },
Node { type: 'All', val: 'NON)' },
Node { type: 'NewLine', val: '\n' },
Node {
type: 'ConditionnalState.if',
val: 'if',
condition: 'Secteur protégé' },
Node { type: 'ConditionnalState.then', val: 'then' },
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.condition.open', val: '(' },
Node { type: 'All', val: 'OUI)' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'StaticState', val: 'CERFA = DP' },
Node { type: 'Space', val: ' ' },
Node { type: 'Activity.stop', val: 'stop' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.else', val: 'else' },
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.condition.open', val: '(' },
Node { type: 'All', val: 'NON)' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'StaticState', val: 'Aucune autorisation nécessaire' },
Node { type: 'StaticState', val: 'Retour au début de l\'assistant' },
Node { type: 'Space', val: ' ' },
Node { type: 'DetachPuml', val: 'detach' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'All', val: 'endif' },
Node { type: 'NewLine', val: '\n' } ],
val: '\\undefined' },
Node { type: 'All', val: '' } ] },
Node { type: 'eos', val: '' } ] }
But I don't know why/when node should/must have a parent property.
Every node can or should have a parent. For example:
const ast = new Node({
type: 'root',
nodes: []
});
const block = new Node({
type: 'block',
nodes: []
});
// instead of doing ast.nodes.push(block), we want to use `ast.push()`
// so that ast is properly set as the parent of the block node
ast.push(block);
const block = new Node({
type: 'block',
nodes: []
});
const otherNode = new Node({
type: 'other',
value: 'foo'
});
// same here
block.push(otherNode);
console.log(otherNode.parent.type) //=> 'block'
console.log(otherNode.parent.parent.type) //=> 'root'
In your example, you're doing prev.nodes.push(conditionnalStateNode)
. Instead, try doing prev.push(conditionnalStateNode)
.
That said, I don't think it is (or should be) required. If it's throwing an error b/c node.parent
is not defined, that might be a bug, or something is else is happening.
If you want I can help you get this figured out.
Hi Jon thank you for your time.
I naturally tried prev.push()
instead of prev.nodes.push()
but Node.prototype.push
doesnt exist.
exemple:
/home/remi/app/src/lib/parsers/uml/EndUmlParser.js:39
parent.push(close);
^
TypeError: parent.push is not a function
at Parser.parse (/home/remi/app/src/lib/parsers/uml/EndUmlParser.js:27:12)
at Parser.getNext (/home/remi/app/node_modules/snapdragon/lib/parser.js:471:36)
at Parser.advance (/home/remi/app/node_modules/snapdragon/lib/parser.js:494:24)
at Parser.parse (/home/remi/app/node_modules/snapdragon/lib/parser.js:529:29)
at Object.<anonymous> (/home/remi/app/src/lib/index.js:15:20)
at Module._compile (module.js:652:30)
at loader (/home/remi/app/node_modules/babel-register/lib/node.js:144:5)
at Object.require.extensions.(anonymous function) [as .js] (/home/remi/app/node_modules/babel-register/lib/node.js:154:7)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
But I saw there is snapdragon-util .pushNode()
to do the same.
I'm investigating this way
but Node.prototype.push doesnt exist.
If .push()
doesn't exist, that means .prev
is not an instance of Node
.
you can also do:
parent.nodes.push(node);
node.parent = parent;
edit: .parent
is useful in an AST when you need to access that object with information about nodes contained in a block.
Is having *.open
and *.close
nodes at first and last position nested in a parent node reaaly important ? required ?
Is having *.open and *.close nodes at first and last position nested in a parent node reaaly important ? reauired ?
No, not required or important. It's just really useful IMHO. It takes much less code to do certain things than other conventions I see.
also, open and close nodes usually represent a character or character sequence. like:
// given the string "${name}"
const block = new Node({
type: 'block'
nodes: []
});
const open = new Node({
type: 'block.open',
value: '${'
});
const close = new Node({
type: 'block.close',
value: '}'
});
block.push(open);
block.push(new Node({ type: 'variable', value: 'name' }));
block.push(close);
The, let's say there is something special about name
. You can then easily set a value on the parent node ("block" in the example), to let other child nodes access that information when compiling.
In fact the latest snapdragon
release (v0.12.0
) depends on snapdragon-node v1.0.6
and that version doesnt implement a #push()
method but a #pushNode()
method.
Line 35 in 82fb20d
Thank you Jon, using Node#push()
or Node#pushNode()
, in fact, defining properly parent
when nesting nodes, solved my issue.
My parser looks like that now
function parser () {
if (!this.isInside('uml')) {
return
}
const pos = this.position()
const match = this.match(IfConditionnalStateParser.pattern)
const prev = this.prev()
if (!match) return
const conditionnalStateNode = pos(this.node({
type: 'conditionnalState'
}))
const ifNode = pos(this.node({
type: 'conditionnalState.if',
val: match[1],
condition: match[2]
}))
this.push('conditionnalState', conditionnalStateNode)
conditionnalStateNode.pushNode(ifNode)
prev.pushNode(conditionnalStateNode)
}
And my ast look like that (it is a work in progress)
'Root AST'
Node {
type: 'root',
errors: [],
nodes:
[ Node { type: 'bos', val: '' },
Node {
type: 'uml',
title: 'Activité Eolienne',
nodes:
[ Node { type: 'uml.open', val: '@startuml' },
Node {
type: 'activity',
nodes:
[ Node { type: 'activity.start' },
Node { type: 'StaticState', val: 'Je veux installer une éolienne' },
Node {
type: 'ConditionnalState',
nodes:
[ Node {
type: 'conditionnalState.if',
val: '\\if',
condition: 'Hauteur >= 12m (mât + nacelle)' },
Node { type: 'ConditionnalState.then', val: 'then' },
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.condition.open', val: '(' },
Node { type: 'All', val: 'OUI)' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'StaticState', val: 'CERFA = PC' },
Node { type: 'Space', val: ' ' },
Node { type: 'Activity.stop', val: 'stop' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'ConditionnalState.else', val: 'else' },
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.condition.open', val: '(' },
Node { type: 'All', val: 'NON)' },
Node { type: 'NewLine', val: '\n' },
Node {
type: 'ConditionnalState',
nodes:
[ Node {
type: 'conditionnalState.if',
val: 'if',
condition: 'Secteur protégé' },
Node { type: 'ConditionnalState.then', val: 'then'},
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.condition.open', val: '(' },
Node { type: 'All', val: 'OUI)' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'StaticState', val: 'CERFA = DP' },
Node { type: 'Space', val: ' ' },
Node { type: 'Activity.stop', val: 'stop' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.else', val: 'else'},
Node { type: 'Space', val: ' ' },
Node { type: 'ConditionnalState.condition.open', val: '(' },
Node { type: 'All', val: 'NON)' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'StaticState', val: 'Aucune autorisation nécessaire' },
Node { type: 'StaticState', val: 'Retour au début de l\'assistant' },
Node { type: 'Space', val: ' ' },
Node { type: 'DetachPuml', val: 'detach' },
Node { type: 'NewLine', val: '\n' },
Node { type: 'All', val: 'endif' },
Node { type: 'NewLine', val: '\n' } ] },
Node { type: 'All', val: '' } ] } ] },
Node { type: 'uml.close', val: '@enduml' } ] } ] }