为 jison 生成的导入语句定义语法 css präprozessor 语言

Defining Grammar for import statements for a jison generated css präprozessor languages

我正在尝试生成一个带有一些附加功能的样式表解析器来试验 jison。 如何实现导入指令以将其他文件加载到主文件中?我有点困惑。有没有办法在语法文件中使用词法分析器?我可以读取文件然后对其进行标记化吗?

grammar.jison

%{
  var nodes = require('./nodes')
%}

%%

// Parsing starts here.
stylesheet:
  statements EOF                   { return new nodes.StyleSheet() }          
;

statements:
  /* empty */                      { $$ = [] }
| statementGroup                   { $$ =  }
| statements ';' statementGroup    { $$ = .concat() }
| statements ';'                   { $$ =  }
;

statementGroup:
  statement                        { $$ = [  ] }
| rules
| rules statement                  { $$ = .concat() }
;

statement:
  variableDeclaration
;


rules:
  rule                             { $$ = [  ] }
| rules rule                       { $$ = .concat() }
;

rule:
  selector '{' declarations '}'    { $$ = new nodes.Rule(, ) }
;

selector:
  IDENTIFIER
| SELECTOR
;

declarations:
  /* empty */                       { $$ = [] }
| declarationGroup                  { $$ =  }
| declarations ';' declarationGroup { $$ = .concat() }
| declarations ';'                  { $$ =  }
;

declarationGroup:
  declaration                       { $$ = [  ] }
| rules
| rules declaration                 { $$ = .concat() }
;

declaration:
  property
| variableDeclaration
;

property:
  IDENTIFIER ':' values            { $$ = new nodes.Property(, ) }
;

variableDeclaration:
  VARIABLE ':' values              { $$ = new nodes.Assign(, ) }
;

values:
  value                            { $$ = [  ] }
| values value                     { $$ = .concat() }
;

value:
  IDENTIFIER                       { $$ = new nodes.Literal() }
| COLOR                            { $$ = new nodes.Literal() }
| NUMBER                           { $$ = new nodes.Literal() }
| DIMENSION                        { $$ = new nodes.Literal() }
| VARIABLE                         { $$ = new nodes.Variable() }
;

tokens.jisonlex

// Order is important. Rules are matches from top to bottom.

//// Macros
DIGIT                 [0-9]
NUMBER                {DIGIT}+(\.{DIGIT}+)? // matches: 10 and 3.14
NAME                  [a-zA-Z][\w\-]*       // matches: body, background-color and myClassName
SELECTOR              (\.|\#|\:\:|\:){NAME} // matches: #id, .class, :hover and ::before
PATH                  (.+)/([^/]+)          // matches ./bla/bla/nested.sss

%%

//// Rules
\s+                   // ignore spaces, line breaks

// Numbers
{NUMBER}(px|em|\%)    return 'DIMENSION' // 10px, 1em, 50%
{NUMBER}              return 'NUMBER' // 0
\#[0-9A-Fa-f]{3,6}    return 'COLOR' // #fff, #f0f0f0

// Selectors
{SELECTOR}            return 'SELECTOR' // .class, #id
{NAME}{SELECTOR}      return 'SELECTOR' // div.class, body#id

\@{NAME}              return 'VARIABLE' // @variable


{NAME}                return 'IDENTIFIER' // body, font-size

.                     return yytext // {, }, +, :, ;

<<EOF>>               return 'EOF'

nodes.js

var Context = require('./context').Context

var compressed

function StyleSheet(rules, ss) {
  this.rules = rules
  this.ss = ss ? ss : []
}
exports.StyleSheet = StyleSheet

StyleSheet.prototype.toCSS = function(output) {
  compressed = output || false

  var context = new Context()

  var ret = this.rules.map(function (rule) { 
return rule.toCSS(context) }).filter(function (value) { return typeof value !== 'undefined' }).join('\n')

  return compressed ? ret.replace(/\s+/g, '') : ret
}

function Rule(selector, declarations) {
  this.selector = selector
  this.declarations = declarations
}
exports.Rule = Rule

Rule.prototype.toCSS = function(parentContext) {
  var propertiesCSS = [],
      nestedRulesCSS = [],
      context = new Context(this, parentContext)

  this.declarations.forEach(function(declaration) {
    var css = declaration.toCSS(context)

    if (declaration instanceof Property) {
      propertiesCSS.push(css)
    } else if (declaration instanceof Rule) {
      nestedRulesCSS.push(css)
    }
  })

  return [ context.selector() + ' { ' + propertiesCSS.join(' ') +  ' }' ].
         concat(nestedRulesCSS).
         join('\n')
}


function Property(name, values) {
  this.name = name
  this.values = values
}
exports.Property = Property

Property.prototype.toCSS = function(context) {
  var valuesCSS = this.values.map(function(value) { return value.toCSS(context) })
  return this.name + ': ' + valuesCSS.join(' ') + ';'
}


function Literal(value) {
  this.value = value
}
exports.Literal = Literal


Literal.prototype.toCSS = function() {
  return this.value
}


function Variable(name) {
  this.name = name
}
exports.Variable = Variable

Variable.prototype.toCSS = function(context) {
  return context.get(this.name)
}


function Assign(name, values) {
  this.name = name
  this.values = values
}
exports.Assign = Assign

Assign.prototype.toCSS = function(context) {
  var valuesCSS = this.values.map(function(value) { return value.toCSS(context) })
  context.set(this.name, valuesCSS.join(' '))
}

据我所知,基本的jison词法分析器不允许增量输入;你需要给它一个它标记化的字符串。

但是,您可以使用自己的自定义词法分析器(包括调用 jison 词法分析器)来进行增量词法分析。因此,您的自定义词法分析器需要实现输入堆栈以实现 include 命令。这应该不是特别困难,虽然我手边没有例子。