为 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 命令。这应该不是特别困难,虽然我手边没有例子。
我正在尝试生成一个带有一些附加功能的样式表解析器来试验 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 命令。这应该不是特别困难,虽然我手边没有例子。