Skip to content

Commit

Permalink
Implement value plucking
Browse files Browse the repository at this point in the history
Resolves #235, #427, #545
  • Loading branch information
futagoza committed Sep 17, 2018
1 parent 2696947 commit 460f0cc
Show file tree
Hide file tree
Showing 14 changed files with 770 additions and 316 deletions.
25 changes: 23 additions & 2 deletions docs/grammar/parsing-expression-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ There are several types of parsing expressions, some of them containing subexpre
* [! { predicate }](#--predicate--1)
* [$ expression](#-expression-2)
* [label : expression](#label--expression)
* [expression1 expression2 ... expressionN](#expression1-expression2---expressionn)
* [expression<sub>1</sub> expression<sub>2</sub> ... expression<sub>n</sub>](#expression1-expression2---expressionn)
* [expression { action }](#expression--action-)
* [expression1 / expression2 / ... / expressionN](#expression1--expression2----expressionn)
* [expression<sub>1</sub> / expression<sub>2</sub> / ... / expression<sub>n</sub>](#expression1--expression2----expressionn)
* [expression<sub>1</sub> @expression<sub>2</sub> ... expression<sub>n</sub>](#expression1--expression2---expressionn)

#### "*literal*"<br>'*literal*'

Expand Down Expand Up @@ -113,3 +114,23 @@ The action has access to all variables and functions in the [Action Execution En
#### *expression<sub>1</sub>* / *expression<sub>2</sub>* / ... / *expression<sub>n</sub>*

Try to match the first expression, if it does not succeed, try the second one, etc. Return the match result of the first successfully matched expression. If no expression matches, consider the match failed.

#### *expression<sub>1</sub>* @*expression<sub>2</sub>* ... *expression<sub>n</sub>*

Only returns the expression(s) following `@`

> WARNING: You cannot use this on predicate's, and cannot use it alongside an action.
```js
start = MultiPluck
/ SinglePluck

SinglePluck = "0"? @integer
MultiPluck = @integer "." @integer

integer = $[0-9]+
```
When `SinglePluck` finds `011`, it returns `"11"`
When `MultiPluck` finds `0.11`, it returns `["0", "11"]`
4 changes: 3 additions & 1 deletion packages/pegjs/lib/compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const reportInfiniteRecursion = require( "./passes/report-infinite-recursion" );
const reportInfiniteRepetition = require( "./passes/report-infinite-repetition" );
const reportUndefinedRules = require( "./passes/report-undefined-rules" );
const inferenceMatchResult = require( "./passes/inference-match-result" );
const reportIncorrectPlucking = require( "./passes/report-incorrect-plucking" );
const Session = require( "./session" );
const util = require( "../util" );

Expand All @@ -30,7 +31,8 @@ const compiler = {
reportUnusedRules: reportUnusedRules,
reportDuplicateLabels: reportDuplicateLabels,
reportInfiniteRecursion: reportInfiniteRecursion,
reportInfiniteRepetition: reportInfiniteRepetition
reportInfiniteRepetition: reportInfiniteRepetition,
reportIncorrectPlucking: reportIncorrectPlucking,
},
transform: {
removeProxyRules: removeProxyRules
Expand Down
1 change: 1 addition & 0 deletions packages/pegjs/lib/compiler/opcodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const opcodes = {
APPEND: 10, // APPEND
WRAP: 11, // WRAP n
TEXT: 12, // TEXT
PLUCK: 41, // PLUCK n, k, p1, ..., pK

// Conditions and Loops

Expand Down
48 changes: 35 additions & 13 deletions packages/pegjs/lib/compiler/passes/generate-bytecode.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,16 +441,19 @@ function generateBytecode( ast, session ) {

sequence( node, context ) {

const TOTAL_ELEMENTS = node.elements.length;

function buildElementsCode( elements, context ) {

if ( elements.length > 0 ) {

const processedCount = node.elements.length - elements.slice( 1 ).length;
const processedCount = TOTAL_ELEMENTS - elements.slice( 1 ).length;

return buildSequence(
generate( elements[ 0 ], {
sp: context.sp,
env: context.env,
pluck: context.pluck,
action: null,
reportFailures: context.reportFailures
} ),
Expand All @@ -460,6 +463,7 @@ function generateBytecode( ast, session ) {
buildElementsCode( elements.slice( 1 ), {
sp: context.sp + 1,
env: context.env,
pluck: context.pluck,
action: context.action,
reportFailures: context.reportFailures
} ),
Expand All @@ -471,26 +475,32 @@ function generateBytecode( ast, session ) {
)
);

} else if ( context.action ) {
}

if ( context.pluck.length > 0 )

const functionIndex = addFunctionConst(
false,
Object.keys( context.env ),
context.action.code
return buildSequence(
[ op.PLUCK, TOTAL_ELEMENTS + 1, context.pluck.length ],
context.pluck.map( eSP => context.sp - eSP )
);

if ( context.action )

return buildSequence(
[ op.LOAD_SAVED_POS, node.elements.length ],
[ op.LOAD_SAVED_POS, TOTAL_ELEMENTS ],
buildCall(
functionIndex,
node.elements.length + 1,
addFunctionConst( // functionIndex
false,
Object.keys( context.env ),
context.action.code
),
TOTAL_ELEMENTS + 1,
context.env,
context.sp
)
);

}
return buildSequence( [ op.WRAP, node.elements.length ], [ op.NIP ] );
return buildSequence( [ op.WRAP, TOTAL_ELEMENTS ], [ op.NIP ] );

}

Expand All @@ -499,6 +509,7 @@ function generateBytecode( ast, session ) {
buildElementsCode( node.elements, {
sp: context.sp + 1,
env: context.env,
pluck: [],
action: context.action,
reportFailures: context.reportFailures
} )
Expand All @@ -508,9 +519,20 @@ function generateBytecode( ast, session ) {

labeled( node, context ) {

const env = util.clone( context.env );
let env = context.env;
const label = node.label;
const sp = context.sp + 1;

if ( label !== null ) {

env = util.clone( context.env );
context.env[ label ] = sp;

}

if ( context.pluck && node.pick )

context.env[ node.label ] = context.sp + 1;
context.pluck.push( sp );

return generate( node.expression, {
sp: context.sp,
Expand Down
38 changes: 36 additions & 2 deletions packages/pegjs/lib/compiler/passes/generate-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ function generateJS( ast, session, options ) {
" var ends = [];",
" var stack = [];",
" var startPos = peg$currPos;",
" var params;"
" var params, paramsLength, paramsN;"
].join( "\n" ) );

} else {
Expand All @@ -391,7 +391,7 @@ function generateJS( ast, session, options ) {
" var end = bc.length;",
" var ends = [];",
" var stack = [];",
" var params;"
" var params, paramsLength, paramsN;"
].join( "\n" ) );

}
Expand Down Expand Up @@ -472,6 +472,24 @@ function generateJS( ast, session, options ) {
" ip++;",
" break;",
"",
" case " + op.PLUCK + ":", // PLUCK n, k, p1, ..., pK
" paramsLength = bc[ip + 2];",
" paramsN = 3 + paramsLength",
"",
" params = bc.slice(ip + 3, ip + paramsN);",
" params = paramsLength === 1",
" ? stack[stack.length - 1 - params[ 0 ]]",
" : params.map(function(p) { return stack[stack.length - 1 - p]; });",
"",
" stack.splice(",
" stack.length - bc[ip + 1],",
" bc[ip + 1],",
" params",
" );",
"",
" ip += paramsN;",
" break;",
"",
" case " + op.IF + ":", // IF t, f
indent10( generateCondition( "stack[stack.length - 1]", 0 ) ),
"",
Expand Down Expand Up @@ -825,6 +843,22 @@ function generateJS( ast, session, options ) {
ip++;
break;

case op.PLUCK: // PLUCK n, k, p1, ..., pK
const baseLength = 3;
const paramsLength = bc[ ip + baseLength - 1 ];
const n = baseLength + paramsLength;
value = bc.slice( ip + baseLength, ip + n );
value = paramsLength === 1
? stack.index( value[ 0 ] )
: `[ ${
value.map( p => stack.index( p ) )
.join( ", " )
} ]`;
stack.pop( bc[ ip + 1 ] );
parts.push( stack.push( value ) );
ip += n;
break;

case op.IF: // IF t, f
compileCondition( stack.top(), 0 );
break;
Expand Down
5 changes: 3 additions & 2 deletions packages/pegjs/lib/compiler/passes/report-duplicate-labels.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function reportDuplicateLabels( ast, session ) {

const label = node.label;

if ( __hasOwnProperty.call( env, label ) ) {
if ( label && __hasOwnProperty.call( env, label ) ) {

const start = env[ label ].start;

Expand All @@ -49,7 +49,8 @@ function reportDuplicateLabels( ast, session ) {
}

check( node.expression, env );
env[ label ] = node.location;

if ( label ) env[ label ] = node.location;

},

Expand Down
53 changes: 53 additions & 0 deletions packages/pegjs/lib/compiler/passes/report-incorrect-plucking.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use strict";

//
// Check if the given element's expression is of type `semantic_*`
//
function isSemanticPredicate( element ) {

const type = element.expression.type;

if ( type === "semantic_and" ) return true;
if ( type === "semantic_not" ) return true;

return false;

}

//
// Compiler pass to ensure the following are enforced:
//
// - plucking can not be done with an action block
// - cannot pluck a semantic predicate
//
function reportIncorrectPlucking( ast, session ) {

session.buildVisitor( {

action( node ) {

this.visit( node.expression, true );

},

labeled( node, action ) {

if ( node.pick !== true ) return void 0;

if ( action === true )

session.error( `"@" cannot be used with an action block.`, node.location );

if ( isSemanticPredicate( node ) )

session.error( `"@" cannot be used on a semantic predicate.`, node.location );

this.visit( node.expression );

},

} )( ast );

}

module.exports = reportIncorrectPlucking;

0 comments on commit 460f0cc

Please sign in to comment.