Pegjs: سلوك غير متسق للمحلل اللغوي الذي تم إنشاؤه

تم إنشاؤها على ٨ مارس ٢٠١٨  ·  15تعليقات  ·  مصدر: pegjs/pegjs

نوع القضية

  • تقرير الشوائب:

المتطلبات الأساسية

  • هل يمكنك إعادة إظهار
  • هل بحثت في مشكلات المستودع ؟: _نعم_
  • هل قمت بفحص المنتديات ؟: _لا_
  • هل أجريت بحثًا على الويب (google ، yahoo ، إلخ) ؟: _نعم_

وصف

في الوقت الحالي ، أستخدم JS API لإنشاء المحلل اللغوي في وقت التشغيل. هذا يعمل بشكل جيد.

ثم حاولت إنشاء المحلل اللغوي باستخدام CLI لتجنب إنشائه أثناء وقت التشغيل. عندما أستخدمه على الرغم من أنني أحصل على أخطاء (حوالي نصف اختباراتي لتحليل أخطاء رمي السلسلة).

خطوات التكاثر

  1. انقل القواعد النحوية إلى ملفها الخاص grammar.pegjs
  2. قم بإنشاء المحلل اللغوي باستخدام CLI
pegjs -o parser.js grammar.pegjs
  1. قم بإزالة peg.generate('...') واستبدله بالمحلل اللغوي الجديد
const parser = require('./parser');
parser.parse('...');
  1. قم بإجراء الاختبارات

سلوك متوقع:
أتوقع أن المحلل اللغوي الذي تم إنشاؤه من CLI يعمل مثل المحلل اللغوي الذي تم إنشاؤه من JS API.

السلوك الفعلي:
باستخدام JS API ، عندما أمرر هذه السلسلة ( 'foo = "bar"' ) إلى المحلل اللغوي أحصل على AST التالي:

{
  kind: 'condition',
  target: 'foo',
  operator: '=',
  value: 'bar',
  valueType: 'string',
  attributeType: undefined
}

ومع ذلك ، عند استخدام المحلل اللغوي "الذي تم إنشاؤه" باستخدام CLI ، وتمرير نفس السلسلة ( 'foo = "bar"' ) أحصل على الخطأ التالي:

SyntaxError: Expected "(", boolean, date, datetime, number, string, or time but "\"" found.
    at peg$buildStructuredError (/Users/emmenko/xxx/parser.js:446:12)
    at Object.peg$parse [as parse] (/Users/emmenko/xxx/parser.js:2865:11)
    at repl:1:7
    at ContextifyScript.Script.runInThisContext (vm.js:50:33)
    at REPLServer.defaultEval (repl.js:240:29)
    at bound (domain.js:301:14)
    at REPLServer.runBound [as eval] (domain.js:314:12)
    at REPLServer.onLine (repl.js:441:10)
    at emitOne (events.js:121:20)
    at REPLServer.emit (events.js:211:7) 

برمجة

  • PEG.js: 0.10.0
  • Node.js: 8.9.1
  • NPM أو الغزل: [email protected]
  • المتصفح: Chrome
  • نظام التشغيل: OSX
  • المحرر: VSCode
question

ال 15 كومينتر

جميل ، لقد ملأتها بشكل صحيح 👍 ، الآن نحتاج فقط إلى القواعد ويمكنني مساعدتك 😄

ها أنت ذا:

// GRAMMAR
const parser = peg.generate(`
{
  function getFlattenedValue (value) {
    if (!value) return undefined
    return Array.isArray(value)
      ? value.map(function(v){return v.value})
      : value.value
  }
  function getValueType (value) {
    if (!value) return undefined
    var rawType = value.type
    if (Array.isArray(value))
      rawType = value[0].type
    switch (rawType) {
      case 'string':
      case 'number':
      case 'boolean':
        return rawType
      default:
        return 'string'
    }
  }
  function getAttributeType (target, op, val) {
    if (typeof target === 'string' && target.indexOf('attributes.') === 0) {
      if (!val)
        return undefined
      switch (op) {
        case 'in':
        case 'not in':
          return val[0].type;
        case 'contains':
          return 'set-' + val.type
        default:
          return Array.isArray(val) ? 'set-' + val[0].type : val.type;
      }
    }
  }
  function transformToCondition (target, op, val) {
    return {
      kind: "condition",
      target: target,
      operator: op,
      value: getFlattenedValue(val),
      valueType: getValueType(val),
      attributeType: getAttributeType(target, op, val),
    }
  }

  function createIdentifier (body) {
    return body
      .map(identifiers => identifiers.filter(identifier => (identifier && identifier !== '.'))) // gets raw_identifiers without dots and empty identifiers
      .filter(identifiers => identifiers.length > 0) // filter out empty identifiers arrays
      .map(identifiers => identifiers.join('.'))
      .join('.') // join back to construct the path
  }
}

// ----- DSL Grammar -----
predicate
  = ws exp:expression ws { return exp; }

expression
  = head:term tail:("or" term)*
    {
      if (tail.length === 0) {
        return head;
      }

      return {
        kind: "logical",
        logical: "or",
        conditions: [head].concat(tail.map(function(el){return el[1];})),
      };
    }

term
  = head:factor tail:("and" factor)*
    {
      if (tail.length === 0) {
        return head;
      }

      return {
        kind: "logical",
        logical: "and",
        conditions: [head].concat(tail.map(function(el){return el[1];})),
      };
    }

factor
  = ws negation:"not" ws primary:primary ws
    {
      return {
        kind: "negation",
        condition: primary,
      };
    }
  / ws primary:primary ws { return primary; }

primary
  = basic_comparison
  / list_comparison
  / empty_comparison
  / parens

// ----- Comparators -----
basic_comparison
  = target:val_expression ws op:single_operators ws val:value
    { return transformToCondition(target, op, val); }

list_comparison
  = target:val_expression ws op:list_operators ws val:list_of_values
    { return transformToCondition(target, op, val); }

empty_comparison
  = target:val_expression ws op:empty_operators
    { return transformToCondition(target, op); }

// ----- Operators -----
single_operators
  = "!="
  / "="
  / "<>"
  / ">="
  / ">"
  / "<="
  / "<"
  / "contains"

list_operators
  = "!="
  / "="
  / "<>"
  / "not in"
  / "in"
  / "contains all"
  / "contains any"

empty_operators
  = "is not empty"
  / "is empty"
  / "is not defined"
  / "is defined"

list_of_values
  = ws "(" ws head:value tail:(ws "," ws value)* ws ")" ws
    {
      if (tail.length === 0) {
        return [head];
      }
      return [head].concat(tail.map(function(el){ return el[el.length -1];}));
    }

// ----- Expressions -----
val_expression
  = application_expression
  / constant_expression
  / field_expression

application_expression
  = identifier ws "(" ws function_argument (ws "," ws function_argument)* ws ")"
constant_expression = ws val:value ws { return val; }
field_expression = ws i:identifier ws { return i; }

function_argument
  = expression
  / constant_expression
  / field_expression

value
  = v:boolean { return { type: 'boolean', value: v }; }
  / v:datetime { return { type: 'datetime', value: v }; }
  / v:date { return { type: 'date', value: v }; }
  / v:time { return { type: 'time', value: v }; }
  / v:number { return { type: 'number', value: v }; }
  / v:string { return { type: 'string', value: v }; }

// ----- Common rules -----
parens
  = ws "(" ws ex:expression ws ")" ws { return ex; }

identifier
  = body:((raw_identifier "." escaped_identifier)+ / (raw_identifier "." raw_identifier)+)
    { 
      return createIdentifier(body)
    }
    / i:raw_identifier { return i; }

escaped_identifier
  = "\`" head:raw_identifier tail:("-" raw_identifier)* "\`"
    { return [head].concat(tail.map(function(el){return el.join('');})).join(''); }

raw_identifier = i:[a-zA-Z0-9_]* { return i.join(''); }

ws "whitespace" = [ \\t\\n\\r]*

// ----- Types: booleans -----
boolean "boolean"
  = "false" { return false; }
  / "true" { return true; }

// ----- Types: datetime -----
datetime "datetime"
  =  quotation_mark datetime:datetime_format quotation_mark
    { return datetime.map(function(el){return Array.isArray(el) ? el.join('') : el;}).join(''); }

datetime_format = date_format time_mark time_format zulu_mark
time_mark = "T"
zulu_mark = "Z"

// ----- Types: date -----
date "date"
  =  quotation_mark date:date_format quotation_mark { return date.join("");}

date_format = [0-9][0-9][0-9][0-9] minus [0-9][0-9] minus [0-9][0-9]

// ----- Types: time -----
time "time"
  =  quotation_mark time:time_format quotation_mark { return time.join("");}

time_format = [0-2][0-9] colon [0-5][0-9] colon [0-5][0-9] decimal_point [0-9][0-9][0-9]
colon = ":"

// ----- Types: numbers -----
number "number"
  = minus? int frac? exp? { return parseFloat(text()); }

decimal_point = "."
digit1_9 = [1-9]
e = [eE]
exp = e (minus / plus)? DIGIT+
frac = decimal_point DIGIT+
int = zero / (digit1_9 DIGIT*)
minus = "-"
plus = "+"
zero = "0"

// ----- Types: strings -----
string "string"
  = quotation_mark chars:char* quotation_mark { return chars.join(""); }

char
  = unescaped
  / escape
    sequence:(
        '"'
      / "\\\\"
      / "/"
      / "b" { return "\\b"; }
      / "f" { return "\\f"; }
      / "n" { return "\\n"; }
      / "r" { return "\\r"; }
      / "t" { return "\\t"; }
      / "u" digits:$(HEXDIG HEXDIG HEXDIG HEXDIG)
        { return String.fromCharCode(parseInt(digits, 16)); }
    )
    { return sequence; }

escape = "\\\\"
quotation_mark = '"'
unescaped = [^\\0-\\x1F\\x22\\x5C]
// See RFC 4234, Appendix B (http://tools.ietf.org/html/rfc4234).
DIGIT  = [0-9]
HEXDIG = [0-9a-f]i

إضافة صغيرة ذات صلة إلى الخطأ. قمت بإعداد pegjs عبر أداة تحميل pegjs . إنه يعمل على JS API تحت الغطاء الذي يستدعي parser.generate كما أنه يؤدي أيضًا إلى نفس الخطأ.

بالمناسبة شكرا جزيلا للمشروع!

emmenko لا أعرف سبب عمل القواعد الخاصة بك مع واجهة برمجة التطبيقات (سأستمر في محاولة معرفة السبب) ، ولكن قواعدك كانت غير صحيحة ، يجب أن تكون قاعدة unescaped يلي:

unescaped = !'"' [^\\0-\\x1F\\x22\\x5C]

أخبرني إذا كان هذا سيؤدي إلى حل المشكلة من جانبك

tdeekens إذا كان الخطأ نفسه (على سبيل المثال Expected ... but "\"" found. ) ، فتحقق مما إذا كانت القواعد النحوية صحيحة أو انشرها هنا

futagoza me و tdeekens في نفس الفريق ، لذا فهي نفس المشكلة 😅

سنبقيك على اطلاع! شكرا لدعمكم حتى الآن 🙏

لا أعرف لماذا كانت قواعدك تعمل مع API

لم نواجه مشكلة في ذلك لنكون صادقين. شكرا لتوضيح ذلك على أي حال!

وأنها تعمل الآن؟

للأسف لم يساعد ☹️

باستخدام القواعد النحوية ، PEG.js 0.10 ، Node 8.9.0 والإدخال foo = "bar" ، جربت ذلك عبر 3 طرق:

  1. https://pegjs.org/online
  2. واجهة برمجة تطبيقات PEG.js
  3. pegjs CLI

أظهر الثلاثة الخطأ نفسه: Line 1, column 7: Expected "(", boolean, date, datetime, number, string, or time but "\"" found.

إذا قمت بتغيير القواعد النحوية الخاصة بك ، فسيتم إصلاح هذا الخطأ لجميع المسارات الثلاثة:

// orignal
unescaped = [^\\0-\\x1F\\x22\\x5C]

// fixed
unescaped = !'"' [^\\0-\\x1F\\x22\\x5C]

بعد تطبيق القاعدة الثابتة ، هل يمكنك التحقق مما يلي:

  • أنت تتلقى نفس رسالة الخطأ أو خطأ مختلف
  • أنت تفعل شيئًا مختلفًا (أو خطوات إضافية) عما ذكرته
  • هل تستخدم الخيارات عند استخدام PEG.js API

أيضًا ، بعد التغيير والتبديل في الإدخال قليلاً ، أدركت أن قواعدك لا تأخذ في الاعتبار الأسطر الجديدة مثل المسافات البيضاء بشكل صحيح ، وهذا على الأرجح بسبب قاعدة ws .

تحرير: هنا نص الاختبار الخاص بي:

/* eslint node/no-unsupported-features: 0 */

"use strict";

const { exec } = require( "child_process" );
const { readFileSync } = require( "fs" );
const { join } = require( "path" );
const { generate } = require( "pegjs" );

function test( parser ) {

    try {

        console.log( parser.parse( `foo = "bar"` ) );

    } catch ( error ) {

        if ( error.name !== "SyntaxError" ) throw error;

        const loc = error.location.start;

        console.log( `Line ${ loc.line }, column ${ loc.column }: ${ error.message }` );

    }

}

const COMMAND = process.argv[ 2 ];
switch ( COMMAND ) {

    case "api":
        test( generate( readFileSync( join( __dirname, "grammar.pegjs" ), "utf8" ) ) );
        break;

    case "cli":
        exec( "node node_modules/pegjs/bin/pegjs -o parser.js grammar.pegjs", error => {

            if ( error ) console.error( error ), process.exit( 1 );

            test( require( "./parser" ) );

        } );
        break;

    default:
        console.error( `Invalid command "${ COMMAND }" passed to test script.` );
        process.exit( 1 );

}

شكرا جزيلا لردود الفعل! سنحاول غدًا اقتراحك وسنخبرك في أقرب وقت ممكن إذا كان ذلك مفيدًا. 🙏

شكرا على ملاحظاتك. نعتذر أولاً عن اللبس. أردت فقط أن أشير إلى أن المشكلة كانت موجودة أيضًا في أداة تحميل حزمة الويب. آسف أنه تسبب في ارتباك حول هذه المسألة.

لقد جربنا التحسين. إنه يعمل على إصلاح المحلل اللغوي بشكل عام ولكننا نواجه مشكلة جديدة الآن نواجه صعوبة في فهم سبب ذلك.

مثال على الاختبار هو (المزيد أدناه)

Object {
+   "attributeType": undefined,
    "kind": "condition",
    "operator": "=",
    "target": "foo",
-   "value": "bar",
+   "value": ",b,a,r",
    "valueType": "string",
}

نعتقد أن الخطأ قد يكون على الأرجح من جانبنا ، فنحن لسنا متأكدين من مكانه بعد. يحدث هذا على سبيل المثال مع المدخلات التالية

categories.id != ("b33f8e3a-f8d1-476f-a595-2615c4b57556")

الذي يصبح

categories.id != (",b,3,3,f,8,e,3,a,-,f,8,d,1,-,4,7,6,f,-,a,5,9,5,-,2,6,1,5,c,4,b,5,7,5,5,6")

عند التحليل.

من الواضح أننا سنكون ممتنين للغاية للحصول على دليل ولكننا نفهم أيضًا ما إذا كان بإمكاننا دعمنا هناك.

عفوًا ، خطئي 😨 ، هذا يجب أن يصلح ذلك

unescaped = !'"' value:[^\\0-\\x1F\\x22\\x5C] { return value; }

شكرا للاستجابة الفائقة السرعة. إنه يساعد ولكن ليس عند استخدام CLI أو أداة تحميل webpack التي غالبًا ما تُرجع الخطأ الأولي SyntaxError: Expected "(", boolean, date, datetime, number, string, or time but "\"" found. . شيء يحدث على سبيل المثال مع not(sku = "123") أو مثال أكثر تعقيدًا lineItemTotal(sku = "SKU1" or list contains all (1,2,3), field.name, "third arg") = "10 EUR" . هل لا يزال هناك شيء يجب القيام به مع الهارب؟

نعم ، اتضح أنه بسبب الهروب المزدوج. فيما يلي القواعد الثابتة:

ws "whitespace" = [ \t\n\r]*

char
  = unescaped
  / escape
    sequence:(
        '"'
      / "\\"
      / "/"
      / "b" { return "\b"; }
      / "f" { return "\f"; }
      / "n" { return "\n"; }
      / "r" { return "\r"; }
      / "t" { return "\t"; }
      / "u" digits:$(HEXDIG HEXDIG HEXDIG HEXDIG)
        { return String.fromCharCode(parseInt(digits, 16)); }
    )
    { return sequence; }

escape = "\\"

unescaped = !'"' value:[^\0-\x1F\x22\x5C] { return value; }

تحرير: يبدو أنك قد ترغب في العمل على القواعد التي تحلل المثال المعقد: lineItemTotal(sku = "SKU1" or list contains all (1,2,3), field.name, "third arg") = "10 EUR" ، تقوم حاليًا بإخراج عقدة غريبة "kind":"condition"

شكرا جزيلا للمساعدة والمشورة. يبدو أنه يحل المشاكل التي كانت لدينا. سننظر في النصيحة المتعلقة بالعقدة "الشرطية".

على الرحب والسعة 😄

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات