Socket
Socket
Sign inDemoInstall

@sap/cds-compiler

Package Overview
Dependencies
Maintainers
3
Versions
106
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sap/cds-compiler - npm Package Compare versions

Comparing version 1.1.1 to 1.1.2

21

CHANGELOG.md
# ChangeLog for cdx compiler and backends
## Version 1.1.2
Features:
* Allow reserved names for annotations/properties in assignments.
* Allow final `,` for much more "lists" (e.g. arguments).
* It is now possible to omit the select list in a view definition,
which is the same as writing `select from <name> {*}`.
* Allow `array of` as type spec for a parameter definition.
* SQL generation for sqlite now supports a mode where associations are resolved
to joins.
Changes:
* Improved messages for syntax errors.
* `WHERE` now is a reserved keyword and so cannot be used anymore as name at many places.
Fixes:
* In `toOdata()` with the `hdbcds` naming convention, the value of the `@cds.persistence.name`
annotation now uses `.` rather than `_` as separator for the names of flattened structured
entity elements.
* Numeric values in OData annotations are now correctly mapped to edmx.
## Version 1.1.1

@@ -4,0 +25,0 @@

24

lib/base/messages.js

@@ -5,2 +5,16 @@ // Functions and classes for syntax messages

// For messageIds, where no severity has been provided via code (central def)
const standardSeverities = {
'syntax-anno-after-struct': 'Warning',
'syntax-anno-after-enum': 'Warning',
'syntax-anno-after-params': 'Warning', // very ugly!
}
// For messageIds, where no text has been provided via code (central def)
const standardTexts = {
'syntax-anno-after-struct': 'Avoid annotation assignments after structure definitions',
'syntax-anno-after-enum': 'Avoid annotation assignments after enum definitions',
'syntax-anno-after-params': 'Avoid annotation assignments after parameters',
}
function hasErrors( messages ) {

@@ -83,6 +97,10 @@ return messages && messages.some( m => m.severity === 'Error' );

return function message( id, location, params = {}, severity = undefined, texts = undefined ) {
let s = normalizedSeverity( severity ); // as specified
if (!severity) // TODO: check that they are always eq per messageId
severity = standardSeverities[id];
let s = normalizedSeverity( severity );
if ((s !== 'Error' || severity instanceof Array) && id && id in config )
s = normalizedSeverity( config[id] );
let text = (typeof params === 'string') ? params : messageText( texts, params );
let text = (typeof params === 'string')
? params
: messageText( texts || standardTexts[id], params );
let msg = new CompileMessage( location, text, s, id );

@@ -98,2 +116,4 @@ model.messages.push( msg );

art: transformArg,
code: n => '`' + n + '`',
name: n => msgName(n),
id: n => msgName(n)

@@ -100,0 +120,0 @@ };

11

lib/compiler/assert-consistency.js

@@ -30,2 +30,3 @@ // Consistency checker on model (XSN = augmented CSN)

'version', // TODO: do not set in parser
'@sql_mapping', // TODO: it is time that a 'header' attribute replaces 'version'
],

@@ -147,3 +148,2 @@ },

dbType: { kind: true, test: () => true },
temporary: { kind: true, test: () => true }, // TODO: HANA-CDS - keep?
source: { kind: true, test: () => true }, // TODO: remove in JSON/CDL parser

@@ -366,9 +366,2 @@ projection: { kind: true, test: () => true }, // TODO: remove in JSON/CDL parser

function assertConsistency2(model, stage) {
let sql_mapping = model["@sql_mapping"];
delete model["@sql_mapping"]
assertConsistency(model, stage)
model["@sql_mapping"]=sql_mapping
}
module.exports = assertConsistency2;
module.exports = assertConsistency;

@@ -195,2 +195,4 @@ // Compiler phase "define": transform dictionary of AST-like CSNs into augmented CSN

let absolute;
if (path.broken)
return parent;
for (let item of path) {

@@ -415,2 +417,4 @@ let id = item.id;

addToDefinitions( art, undefined, prefix, parent );
if (art.dbType && !options.hanaFlavor)
message( `TABLE TYPE is not supported yet`, art.dbType.location );
defineAnnotations( art, art, block );

@@ -417,0 +421,0 @@ initMembers( art, art, block );

@@ -190,2 +190,6 @@ // Compiler phase "resolve": resolve all references

return;
if (!options.hanaFlavor && !options.betaMode && tc.location) {
message( null, tc.location, {}, 'Error',
'TECHNICAL CONFIGURATION is not supported yet' );
}

@@ -1003,4 +1007,8 @@ // secondary and fulltext indexes

break;
if (step.namedArgs)
if (step.namedArgs) {
if (!options.betaMode && !options.hanaFlavor)
message( null, dictLocation( step.namedArgs ), {},
'Error', 'View arguments are not supported yet' );
resolveParams( step.namedArgs, step._artifact, query._main._block ); // block for future :const
}
if (step.where) // TODO: support for $projection, $parameters, ...?

@@ -1007,0 +1015,0 @@ resolveExpr( step.where, 'element', null, {},

@@ -434,3 +434,3 @@ // Compiler functions and utilities shared across all phases

function pathName (path) {
return path.map( id => id.id ).join('.');
return (path.broken) ? '' : path.map( id => id.id ).join('.');
}

@@ -437,0 +437,0 @@

@@ -255,4 +255,6 @@ 'use strict';

Bool : val => val == "true",
Byte : val => parseInt(val),
Int : val => parseInt(val),
Decimal : val => parseFloat(val),
Float : val => parseFloat(val),
EnumMember : val => handleEnumValue(val),

@@ -259,0 +261,0 @@ Path : val => ({ "=": val.replace(/\//g, '.') }),

@@ -145,2 +145,8 @@ const parseXml = require('./xmlParserWithLocations');

},
Float: val => {
return {
literal: 'decimal',
val: Number(val)
}
},
Int: val => {

@@ -147,0 +153,0 @@ return {

@@ -37,2 +37,4 @@ const edmxDict = require('./vocabularies/Dictionary.json');

case 'Edm.Double':
checkIfProp('Float');
break;
case 'Edm.Decimal':

@@ -39,0 +41,0 @@ checkIfProp('Decimal');

@@ -43,8 +43,13 @@ 'use strict';

// handle the annotations directly tied to the object
handleAnnotations(objName, object);
// handle the annotations of the object's elements
handleElements(objName, object);
// handle the annotations of the object's actions
handleActions(objName, object);
if (object.kind == "action" || object.kind == "function") {
handleAction(objName, object);
}
else { // service, entity, anything else?
// handle the annotations directly tied to the object
handleAnnotations(objName, object);
// handle the annotations of the object's elements
handleElements(objName, object);
// handle the annotations of the object's actions
handleBoundActions(objName, object);
}
}

@@ -128,9 +133,6 @@

// handle the annotations of actions and action parameters
// handle the annotations of cObject's (an entity) bound actions and action parameters
// in: cObjectname : name of the object that holds the actions
// cObject : the object itself
function handleActions(cObjectname, cObject) {
// edm target name is "<serviceName>.EntityContainer/<actionName>" or
// "<serviceName>.EntityContainer/<actionName>/<parameterName>"
function handleBoundActions(cObjectname, cObject) {
// TODO the respective entity names do not appear in the target name

@@ -148,13 +150,19 @@ // => in edm all actions are in the same namespace

let action = cObject.actions[actionName];
let edmTargetName = serviceName + ".EntityContainer/" + actionName;
handleAnnotations(edmTargetName, action);
for (let paramName in cObject.actions[actionName].params) {
let param = cObject.actions[actionName].params[paramName];
edmTargetName = serviceName + ".EntityContainer/" + actionName + "/" + paramName;
handleAnnotations(edmTargetName, param);
}
handleAction(serviceName + "." + actionName, action);
}
}
// handle the annotations of an action and its parameters
// in: cActionName : name of the action, full name
// cAction : the action object
function handleAction(cActionName, cAction) {
handleAnnotations(cActionName, cAction);
for (let paramName in cAction.params) {
let param = cAction.params[paramName];
let edmTargetName = cActionName + "/" + paramName;
handleAnnotations(edmTargetName, param);
}
}
// note: in csn, all annotations are flattened out

@@ -213,2 +221,7 @@ // => values can be

}
else if (carrier.kind === 'action' || carrier.kind === 'function' || carrier.kind === 'param') {
// annotated object is an action/function:
// find last . in name and insert "EntityContainer/"
stdName = edmCarrierName.replace(/\.(?=[^.]*$)/, '.EntityContainer/');
}

@@ -387,2 +400,7 @@ // result objects that holds all the annotation objects to be created

// expected type is dTypeName
// mappping rule for values:
// if expected type is ... the expression to be generated is ...
// floating point type except Edm.Decimal -> Float
// Edm.Decimal -> Decimal
// integer tpye -> Int
function handleSimpleValue(val, dTypeName, context) {

@@ -412,3 +430,3 @@ // caller already made sure that val is neither object nor array

}
else if (dTypeName == "Edm.Double") {
else if (dTypeName == "Edm.Decimal") {
if (isNaN(val) || isNaN(parseFloat(val))) {

@@ -421,2 +439,10 @@ errorMessage(context, "found non-numeric string, but expected type " + dTypeName);

}
else if (dTypeName == "Edm.Double") {
if (isNaN(val) || isNaN(parseFloat(val))) {
errorMessage(context, "found non-numeric string, but expected type " + dTypeName);
}
else {
typeName = "Float";
}
}
else if (isComplexType(dTypeName)) {

@@ -455,10 +481,13 @@ errorMessage(context, "found String, but expected complex type " + dTypeName);

}
else if (dTypeName == "Edm.Double") {
typeName = "Decimal";
}
else if (dTypeName == "Edm.Boolean") {
errorMessage(context, "found number, but expected type " + dTypeName);
}
else if (dTypeName == "Edm.Decimal") {
typeName = "Decimal";
}
else if (dTypeName == "Edm.Double") {
typeName = "Float";
}
else {
typeName = Number.isInteger(val) ? 'Int' : 'Decimal';
typeName = Number.isInteger(val) ? 'Int' : 'Float';
}

@@ -465,0 +494,0 @@ }

@@ -31,3 +31,8 @@ 'use strict';

// helper function
// helper functions
function getTargetOfAssoc(assoc) {
// assoc.target can be the name of the target or the object itself
return (typeof assoc.target === 'object') ? assoc.target : csn.definitions[assoc.target];
}
function getKeyOfTargetOfManagedAssoc(assoc) {

@@ -160,8 +165,16 @@ // assoc.target can be the name of the target or the object itself

let path = ae["="];
if (carrier.kind == 'entity') {
let element = carrier.elements[path];
// FIXME: Deal with multi-step paths properly
if (element && glue.isAssociation(element) && !element.onCond) {
ae["="] = path + fkSeparator + getKeyOfTargetOfManagedAssoc(element);
let steps = path.split('.');
let ent = carrier;
for (let i in steps) {
if (!ent || ent.kind != 'entity') return;
let el = ent.elements[steps[i]];
if (el && glue.isAssociation(el)) {
if (i < steps.length-1) {
ent = getTargetOfAssoc(el);
}
else { //last step
if (!el.onCond) { // only for managed
ae["="] += fkSeparator + getKeyOfTargetOfManagedAssoc(el);
}
}
}

@@ -326,9 +339,8 @@ }

if (options && options.tntFlavor && !options.tntFlavor.skipAnnosSubstitutingFKeysForAssocs) {
// replace association by fk field
if (aNameWithoutQualifier == "@UI.LineItem" ||
aNameWithoutQualifier == "@UI.Identification" ||
aNameWithoutQualifier == "@UI.FieldGroup") {
for (let i in a) {
let ae = a[i];
if (ae["Value"] != undefined && ae["Value"]["="] != undefined) {
// replace association by fk field, mind nested annotatinos
if (aNameWithoutQualifier == "@UI.LineItem" || aNameWithoutQualifier == "@UI.LineItem.$value" ||
aNameWithoutQualifier == "@UI.Identification" || aNameWithoutQualifier == "@UI.Identification.$value" ||
aNameWithoutQualifier == "@UI.FieldGroup" || aNameWithoutQualifier == "@UI.FieldGroup.$value") {
for (let ae of a) {
if (ae["Value"] && ae["Value"]["="]) {
replaceManagedAssocByFK(ae["Value"]);

@@ -341,4 +353,3 @@ }

if (aNameWithoutQualifier == "@UI.SelectionFields") {
for (let i in a) {
let ae = a[i];
for (let ae of a) {
if ("=" in ae) {

@@ -345,0 +356,0 @@ replaceManagedAssocByFK(ae);

@@ -302,7 +302,9 @@ // Transform augmented CSN into compact "official" CSN

let model = compact( ...args );
let result = Object.create(null);
let definitions = model.definitions;
for (let k of Object.keys( definitions ).sort())
result[k] = normalizeNode( definitions[k] );
model.definitions = result;
if (model.definitions) {
let result = Object.create(null);
let definitions = model.definitions;
for (let k of Object.keys( definitions ).sort())
result[k] = normalizeNode( definitions[k] );
model.definitions = result;
}
return model;

@@ -309,0 +311,0 @@ }

@@ -9,3 +9,3 @@ // Wrapper around generated ANTLR parser

var { CompileMessage } = require('../base/messages');
var { getMessageFunction, CompileMessage } = require('../base/messages');
var errorStrategy = require('./errorStrategy');

@@ -20,17 +20,8 @@

super(...args);
this.messages = [];
}
// Push message `msg` with location `loc` to array of errors:
message( msg, loc, severity ) {
this.messages.push( new CompileMessage( loc, msg, severity ) );
}
// method which is called by generated parser:
// method which is called by generated parser with --trace-parser[-amg]:
syntaxError( recognizer, offendingSymbol, line, column, msg, e ) {
var loc = recognizer.tokenLocation( offendingSymbol );
//console.log(e); throw new CompileMessage( loc, msg );
var err = new CompileMessage( loc, msg[0].toUpperCase() + msg.slice(1) );
if (e && e.expectedTokens)
err.expectedTokens = e.expectedTokens;
this.messages.push( err );
if (!(e instanceof CompileMessage)) // not already reported
recognizer.message( null, offendingSymbol, msg );
}

@@ -103,5 +94,7 @@ }

parser.filename = filename;
parser.options = options;
parser.$message = getMessageFunction( parser ); // sets parser.messages
initTokenRewrite( parser, tokenStream );
parser.options = options;
parser.filename = filename;
// comment the following 2 lines if you want to output the parser errors directly:

@@ -133,4 +126,5 @@ parser.messageErrorListener = errorListener;

parser.removeErrorListeners();
parser.addErrorListener( errorListener );
parser.avoidErrorListeners = true;
}
parser.addErrorListener( errorListener );

@@ -147,3 +141,3 @@ if (options.parseListener) {

ast.messages = errorListener.messages;
ast.messages = parser.messages;
if (options.attachTokens === true || options.attachTokens === filename)

@@ -150,0 +144,0 @@ ast.tokenStream = tokenStream;

@@ -39,2 +39,4 @@ // Error strategy with special handling for (non-reserved) keywords

var SEMI = null;
// Remember context for potential error message

@@ -97,2 +99,3 @@ function epsilon() {

sync,
singleTokenInsertion,
reportNoViableAlternative,

@@ -107,2 +110,3 @@ reportInputMismatch,

getExpectedTokensForMessage,
getTokenDisplay,
constructor: KeywordErrorStrategy

@@ -134,2 +138,4 @@ });

}
// TODO: expected token is identifier, current is KEYWORD
if (nextTokens.contains(antlr4.Token.EPSILON)) {

@@ -154,3 +160,4 @@ if (recognizer.$nextTokensToken !== token) {

// report error and recover if possible
if( this.singleTokenDeletion(recognizer) !== null) {
if( token.text !== '}' &&
this.singleTokenDeletion(recognizer) !== null) { // also calls reportUnwantedToken
return;

@@ -172,2 +179,26 @@ } else {

}
function singleTokenInsertion( recognizer ) {
var currentSymbol = recognizer.getTokenStream().LT(1);
// if current token is consistent with what could come after current
// ATN state, then we know we're missing a token; error recovery
// is free to conjure up and insert the missing token
var atn = recognizer._interp.atn;
var currentState = atn.states[recognizer.state];
var next = currentState.transitions[0].target;
var expectingAtLL2 = atn.nextTokens(next, recognizer._ctx);
if (expectingAtLL2.contains(currentSymbol.type) ) {
if (currentSymbol.text === '}') {
if (SEMI == null)
SEMI = recognizer.literalNames.indexOf( "';'" );
if (atn.nextTokens( currentState ).contains(SEMI))
return true;
}
this.reportMissingToken(recognizer);
return true;
} else {
return false;
}
}
// Report `NoViableAltException e` signalled by parser `recognizer`

@@ -177,2 +208,14 @@ function reportNoViableAlternative( recognizer, e ) {

if (e.startToken === e.offendingToken) { // mismatch at LA(1)
if (e.offendingToken.text === '}') {
// Do it generally, or even in reportError()? - the catch clause does not work in Antlr4.Js
if (SEMI == null)
SEMI = recognizer.literalNames.indexOf( "';'" );
let s = recognizer._interp.atn.states[recognizer.state];
let nextTokens = recognizer.atn.nextTokens(s);
if (nextTokens.contains(SEMI))
return;
// Remarks: this does not work if it fails in a subrule, i.e. if
// nextTokens.contains(EPSILON) - more serious ANTLR hacking would be
// required to get the nextTokens of the calling rule...
}
this.reportInputMismatch( recognizer, e );

@@ -194,8 +237,16 @@ }

this.getExpectedTokensForMessage( recognizer, e.offendingToken, deadEnds );
var msg = "Mismatched input " + this.getTokenErrorDisplay(e.offendingToken);
if (expecting) {
msg += " expecting " + expecting.toString(recognizer.literalNames, recognizer.symbolicNames);
e.expectedTokens = intervalSetToArray( recognizer, expecting );
let offending = this.getTokenDisplay( e.offendingToken, recognizer );
let err;
if (expecting && expecting.length) {
err = recognizer.message( 'syntax-mismatched-token', e.offendingToken,
{ offending, expecting: expecting.join(', ') },
'Error', 'Mismatched $(OFFENDING), expecting $(EXPECTING)' );
err.expectedTokens = expecting;
}
recognizer.notifyErrorListeners(msg, e.offendingToken, e);
else { // should not really happen anymore... -> no messageId !
err = recognizer.message( null, e.offendingToken, { offending },
'Error', 'Mismatched $(OFFENDING)' );
}
if (!recognizer.avoidErrorListeners) // with --trace-parser or --trace-parser-ambig
recognizer.notifyErrorListeners( err.message, e.offendingToken, err );
}

@@ -205,14 +256,15 @@

function reportUnwantedToken( recognizer ) {
if (this.inErrorRecoveryMode(recognizer)) {
if (this.inErrorRecoveryMode(recognizer))
return;
}
this.beginErrorCondition(recognizer);
var t = recognizer.getCurrentToken();
var tokenName = this.getTokenErrorDisplay(t);
var expecting = this.getExpectedTokensForMessage( recognizer, t );
var e = new antlr4.error.InputMismatchException( recognizer );
e.expectedTokens = intervalSetToArray( recognizer, expecting );
var msg = "Extraneous input " + tokenName + " expecting " +
expecting.toString(recognizer.literalNames, recognizer.symbolicNames);
recognizer.notifyErrorListeners(msg, t, e);
var token = recognizer.getCurrentToken();
var expecting = this.getExpectedTokensForMessage( recognizer, token );
var offending = this.getTokenDisplay( token, recognizer );
let err = recognizer.message( 'syntax-extraneous-token', token,
{ offending, expecting: expecting.join(', ') },
'Error', 'Extraneous $(OFFENDING), expecting $(EXPECTING)' );
err.expectedTokens = expecting;
if (!recognizer.avoidErrorListeners) // with --trace-parser or --trace-parser-ambig
recognizer.notifyErrorListeners( err.message, token, err );
}

@@ -222,17 +274,18 @@

function reportMissingToken( recognizer ) {
if ( this.inErrorRecoveryMode(recognizer)) {
if ( this.inErrorRecoveryMode(recognizer))
return;
}
this.beginErrorCondition(recognizer);
var t = recognizer.getCurrentToken();
var expecting = this.getExpectedTokensForMessage( recognizer, t );
var e = new antlr4.error.InputMismatchException( recognizer );
e.expectedTokens = intervalSetToArray( recognizer, expecting );
var msg = "Missing " + expecting.toString(recognizer.literalNames, recognizer.symbolicNames) +
" at " + this.getTokenErrorDisplay(t);
recognizer.notifyErrorListeners(msg, t, e);
var token = recognizer.getCurrentToken();
var expecting = this.getExpectedTokensForMessage( recognizer, token );
var offending = this.getTokenDisplay( token, recognizer );
// TODO: if non-reserved keyword will not been parsed as keyword, use Identifier for offending
let err = recognizer.message( 'syntax-missing-token', token,
{ offending, expecting: expecting.join(', ') },
'Error', 'Missing $(EXPECTING) before $(OFFENDING)' );
err.expectedTokens = expecting;
if (!recognizer.avoidErrorListeners) // with --trace-parser or --trace-parser-ambig
recognizer.notifyErrorListeners( err.message, token, err );
}
var SEMI = null;
function consumeUntil( recognizer, set ) {

@@ -260,2 +313,4 @@ if (SEMI == null)

// We now also allow keywords if the Identifier is expected.
// Called by match() and in generated parser in "else part" before consume()
// for ( TOKEN1 | TOKEN2 )
function recoverInline( recognizer ) {

@@ -300,6 +355,42 @@ var identType = recognizer.constructor.Identifier;

}
names.sort( (a, b) => tokenPrecedence(a) < tokenPrecedence(b) ? -1 : 1 );
return names;
}
const token1sort = {
// 0: Identifier, Number, ...
// 1: separators:
',': 1, '.': 1, ':': 1, ';': 1,
// 2: parentheses:
'(': 2, ')': 2, '[': 2, ']':2, '{': 2, '}': 2,
// 3: special:
'!': 3, '#': 3, '$': 3, '?': 3, '@': 3,
// 4: operators:
'*': 4, '+': 4, '-': 4, '/': 4, '<': 4, '=': 4, '>': 4, '|': 4,
// 8: KEYWORD
// 9: <EOF>
}
function tokenPrecedence( name ) {
if (name.length < 2 || name === '<EOF>')
return '9' + name;
let prec = token1sort[ name.charAt(1) ];
if (prec)
return '' + prec + name;
else
return (name.charAt(1) < 'a' ? '8' : '0') + name;
}
function getTokenDisplay( token, recognizer ) {
if (!token)
return '<EOF>';
let t = token.type;
if (t === antlr4.Token.EOF || t === antlr4.Token.EPSILON )
return '<EOF>';
else if (token.text === '.') // also for DOTbeforeBRACE
return '\'.\'';
else
return recognizer.literalNames[t] || recognizer.symbolicNames[t];
}
// Return an IntervalSet of token types which the parser had expected. Do not

@@ -316,3 +407,3 @@ // include non-reserved keywords if not mentioned explicitly (i.e. other than

if (recognizer.state < 0)
return null;
return [];
if (recognizer.state >= atn.states.length)

@@ -325,3 +416,3 @@ throw( 'Invalid state number ' + recognizer.state + ' for ' +

if (!identType || !beforeUnreserved || beforeUnreserved + 2 > identType)
return super1.getExpectedTokens.call( this, recognizer );
return intervalSetToArray( recognizer, super1.getExpectedTokens.call( this, recognizer ) );

@@ -355,3 +446,3 @@ var ll1 = new antlr4_LL1Analyzer(atn);

// console.log(state, recognizer.$nextTokensState, expected.toString(recognizer.literalNames, recognizer.symbolicNames));
return expected;
return intervalSetToArray( recognizer, expected );

@@ -384,4 +475,2 @@ // Add an interval `v` to the IntervalSet `this`. If `v` contains the token

// probably overwrite getTokenErrorDisplay() - use token text
module.exports = {

@@ -388,0 +477,0 @@ epsilon,

@@ -46,4 +46,6 @@ // Generic ANTLR parser class with AST-building functions

setOnce,
notYet,
hanaFlavorOnly,
noAssignmentInSameLine,
noSemicolonHere,
isStraightBefore,
constructor: GenericAntlrParser // keep this last

@@ -88,23 +90,22 @@ });

// Push message `msg` with location `loc` to array of errors:
function message( msg, loc, severity ) {
if (!this.options.parseOnly) // TODO: remove this test
this.messageErrorListener.message( msg, loc, severity );
function message( id, loc, ...args ) {
return this.$message( id, // function $message is set in antlrParser.js
(loc instanceof antlr4.CommonToken) ? this.tokenLocation(loc) : loc,
...args );
}
// Push a message to the array of errors complaining that language construct
// 'feature' is not yet supported (using the location `loc`, which can also be a token for its locatio'), unless
// option 'parseOnly' or any of the options from 'optionsArray' are set.
function notYet( feature, loc, optionsArray=[] ) {
if (this.options.parseOnly) {
// Generally ignore if only parsing
// Use the following functions for language constructs which we (currently)
// just being able to parse, in able to run tests from HANA CDS. As soon as we
// create ASTs for the language construct and put it into a CSN, a
// corresponding check should actually be inside the compiler, because the same
// language construct can come from a CSN as source.
// TODO: this is not completely done this way
function hanaFlavorOnly( text, ...tokens ) {
if (!text || this.options.hanaFlavor)
return;
if (typeof text !== 'string') {
tokens = [ text, ...tokens ];
text = tokens.map( t => t.text.toUpperCase() ).join(' ') + ' is not supported';
}
for (let option of optionsArray) {
// Grammar says to ignore for this option
if (this.options[option]) {
return;
}
}
this.messageErrorListener.message( `${feature} not supported yet`,
(loc instanceof antlr4.CommonToken) ? this.tokenLocation(loc) : loc );
this.message( null, this.tokenLocation( tokens[0], tokens[ tokens.length-1 ] ), text );
}

@@ -115,7 +116,24 @@

if (t.text === ';')
this.messageErrorListener.message( `Unexpected ';' - previous keyword 'with' is ignored`,
this.tokenLocation(t), 'Warning' );
this.message( 'syntax-ignored-with', t, {},
'Warning', `Unexpected ';' after WITH - ignored WITH` );
// TODO remove ';' from set of expected tokens
}
function noAssignmentInSameLine() {
var t = this.getCurrentToken();
if (t.text === '@' && t.line <= this._input.LT(-1).line)
this.message( 'syntax-anno-same-line', t, {},
'Warning', `Annotation assignment belongs to next statement` );
}
// Use after matching ',' to allow ',' in front of the closing paren. Be sure
// that you know what to do if successful - break/return/... = check the
// generated grammar; inside loops, you can use `break`. This function is
// still the preferred way to express an optional ',' at the end, because it
// does not influence the error reporting. It might also allow to match
// reserved keywords, because there is no ANTLR generated decision in front of it.
function isStraightBefore( closing ) {
return this.getCurrentToken().text === closing;
}
// Attach location matched by current rule to node `art`. If a location is

@@ -186,4 +204,4 @@ // already provided, only set the end location. Use this function only

if (!id) {
this.message( "Quoted identifier must contain at least one character",
this.tokenLocation( token ) );
this.message( 'syntax-empty-ident', token, {},
'Error', 'Quoted identifier must contain at least one character' );
}

@@ -222,4 +240,6 @@ return { id, quoted: true, location: this.tokenLocation( token ) };

if (p.test_fn && !p.test_fn(val) || p.test_re && !p.test_re.test(val))
this.message( p.test_msg, location );
// TODO: make tests available for CSN parser
if ((p.test_fn && !p.test_fn(val) || p.test_re && !p.test_re.test(val)) &&
!this.options.parseOnly)
this.message( null, location, p.test_msg ); // TODO: message id

@@ -230,7 +250,7 @@ if (p.unexpected_char)

if (~idx) {
this.message( p.unexpected_msg, {
this.message( null, { // TODO: message id
filename: location.filename,
start: atChar( idx ),
end: atChar( idx + (val[idx] == '\'' ? 2 : 1) )
} );
}, p.unexpected_msg );
}

@@ -291,3 +311,3 @@ }

}
else if (kind) {
else if (kind || this.options.parseOnly) {
addToDictWithIndexNo( parent, env, art.name.id, art );

@@ -299,7 +319,10 @@ }

if (kind === 0)
this.message( `Duplicate value for view parameter "${name}"`, loc );
this.message( 'duplicate-argument', loc, { name },
'Error', 'Duplicate value for parameter $(NAME)' );
else if (kind === '')
this.message( `Duplicate EXCLUDING for source element "${name}"`, loc );
this.message( 'duplicate-excluding', loc, { name },
'Error', 'Duplicate EXCLUDING for source element $(NAME)' );
else
this.message( `Duplicate assignment for structure property "${name}"`, loc );
this.message( 'duplicate-prop', loc, { name },
'Error', 'Duplicate value for structure property $(NAME)' );
} );

@@ -372,3 +395,4 @@ }

if (prev) {
this.message( `Option ${prev.option} has already been specified`, loc );
this.message( 'syntax-repeated-option', loc, { option: prev.option },
'Error', 'Option $(OPTION) has already been specified' );
}

@@ -375,0 +399,0 @@ if (typeof value === 'boolean') {

@@ -84,4 +84,5 @@ // Main entry point for the Research Vanilla CDS Compiler

model.$frontend = 'json';
if(model.version && model.version.csn === "0.1.99")
options.newCsn=true; // force new version TODO optimize?
// TODO: I (CW) do not think that the following is a good idea...
if (model.version && model.version.csn === "0.1.99")
options.newCsn = true; // force new version TODO optimize?
return model;

@@ -88,0 +89,0 @@ } else if (options.fallbackParser || ['.cds', '.hdbcds', '.hdbdd'].includes(ext))

@@ -781,3 +781,5 @@

// date'2017-11-02'
return result + v.literal + "'" + v.val + "'";
// date('2017-11-02') if sqlite
return result + v.literal + `${options.toSql.dialect === 'sqlite' ?
"('" : "'"}` + v.val + `${options.toSql.dialect === 'sqlite' ? "')" : "'"}`;
} else if (v.literal == 'struct') {

@@ -784,0 +786,0 @@ // { foo: 1 }

@@ -134,3 +134,7 @@ "use strict";

// Perform implicit redirection of non-exposed association targets
addImplicitRedirections(model);
// Don't do it when producing SQL for sqlite, as there are no assocs to redirect any more
// (FIXME : ... and implicit redirection fails when translation assoc-to-join has already run)
if (!(options.toSql && options.toSql.dialect === 'sqlite')) {
addImplicitRedirections(model);
}

@@ -137,0 +141,0 @@ // Process all artifacts (pass 1)

@@ -277,2 +277,6 @@ 'use strict';

if (['element', 'key', 'param'].includes(member.kind)) {
// If we have a 'preserved dotted name' (i.e. we are a result of flattening), use that for the @cds.persistence.name annotation
if (member._flatElementNameWithDots) {
memberName = member._flatElementNameWithDots;
}
addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.toOdata.names), member);

@@ -279,0 +283,0 @@ }

@@ -21,3 +21,3 @@ 'use strict';

skipPropagatingFromInclude: false, // if true: Do not propagate properties from (first) included artifact
skipPropagatingActions: false, // if true: Do not propagate bound actions (and functions)
skipPropagatingActions: true, // if true: Do not propagate bound actions (and functions)
skipPropagatingIncludes: false, // if true: Do not propagate the list of included artifacts

@@ -24,0 +24,0 @@ skipNotPropagatingIndexableAnno: false, // if true: Do not make an exception for the propagation of the '@Indexable' annotation

@@ -188,4 +188,14 @@ 'use strict';

let valueForeignKeyElementName = assoc.value.element.replace(/\./g, pathDelimiter) + fkSeparator + foreignKey.name.id;
// For the foreign key element, take the same path as for the assoc, just without the last step
let valueForeignKeyElementPath = [];
if (assoc.value.path) {
valueForeignKeyElementPath = cloneWithTransformations(assoc.value.path, {}).slice(0, -1);
// Take over _artifact links from original path, because flattenStructStepsInPath requires them
for (let i = 0; i < valueForeignKeyElementPath.length; i++) {
valueForeignKeyElementPath[i]._artifact = assoc.value.path[i]._artifact;
}
}
valueForeignKeyElementPath.push({ id: valueForeignKeyElementName });
foreignKeyElement.value = {
path: [{ id: valueForeignKeyElementName }],
path: valueForeignKeyElementPath,
absolute: assoc.value.absolute,

@@ -283,2 +293,4 @@ element: valueForeignKeyElementName,

}
// Preserve the generated element name as it would have been with 'hdbcds' names
setProp(flatElem, '_flatElementNameWithDots', elem.name.id + '.' + grandChildName);
result[flatElemName] = flatElem;

@@ -316,2 +328,4 @@ }

}
// Preserve the generated element name as it would have been with 'hdbcds' names
setProp(flatElem, '_flatElementNameWithDots', elem.name.id + '.' + childName);
result[flatElemName] = flatElem;

@@ -318,0 +332,0 @@ }

@@ -18,5 +18,2 @@ 'use strict'

if(!options.betaMode)
signal(error`Conversion of associations into JOINs is not supported yet`);
// Note: This is called from the 'forHana' transformations, so it is controlled by its options)

@@ -590,9 +587,16 @@ const pathDelimiter = (options.forHana && options.forHana.names == 'hdbcds') ? '.' : '_';

replace the content of the old node with the new one
If newNode is a join tree (rewritten from path),
oldPath must be cleared first.
If newNode is a path => oldNode._artifact === newNode._artifact,
no need to exchange _artifact (as non-iterable property it is
not assigned).
*/
function replaceNodeContent(oldNode, newNode)
{
Object.keys(oldNode).forEach(k => {
delete oldNode[k] });
delete oldNode._artifact;
delete oldNode._status;
// if the newNode is a join expression, throw away old path content
if(newNode.op) {
Object.keys(oldNode).forEach(k => {
delete oldNode[k] });
delete oldNode._artifact;
}
Object.assign(oldNode, newNode);

@@ -599,0 +603,0 @@ }

{
"name": "@sap/cds-compiler",
"version": "1.1.1",
"version": "1.1.2",
"dependencies": {

@@ -5,0 +5,0 @@ "ajv": {

@@ -1,1 +0,1 @@

{"bin":{"cdsc":"bin/cdsc.js"},"bundleDependencies":false,"dependencies":{"ajv":"6.1.1","antlr4":"4.7.1","commander":"2.14.0","fs-extra":"5.0.0","resolve":"1.5.0","sax":"1.2.4"},"deprecated":false,"description":"Standard-Feature-Set Vanilla-CDS in Product Quality","keywords":["CDS"],"main":"lib/main.js","name":"@sap/cds-compiler","repository":{"type":"git","url":"git@github.wdf.sap.corp/CDS/cds-compiler.git"},"version":"1.1.1","license":"SEE LICENSE IN developer-license-3.1.txt"}
{"bin":{"cdsc":"bin/cdsc.js"},"bundleDependencies":false,"dependencies":{"ajv":"6.1.1","antlr4":"4.7.1","commander":"2.14.0","fs-extra":"5.0.0","resolve":"1.5.0","sax":"1.2.4"},"deprecated":false,"description":"Standard-Feature-Set Vanilla-CDS in Product Quality","keywords":["CDS"],"main":"lib/main.js","name":"@sap/cds-compiler","repository":{"type":"git","url":"git@github.wdf.sap.corp/CDS/cds-compiler.git"},"version":"1.1.2","license":"SEE LICENSE IN developer-license-3.1.txt"}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc