diff --git a/README.markdown b/README.markdown index 1cba64689..ff1ce7534 100644 --- a/README.markdown +++ b/README.markdown @@ -24,7 +24,7 @@ In general, the syntax of Handlebars.js templates is a superset of Mustache templates. For basic syntax, check out the [Mustache manpage](http://mustache.github.com/mustache.5.html). -Once you have a template, use the Handlebars.compile method to compile +Once you have a template, use the `Handlebars.compile` method to compile the template into a function. The generated function takes a context argument, which will be used to render the template. @@ -55,12 +55,12 @@ template. Here's an example, which assumes that your objects have a URL embedded in them, as well as the text for a link: ```js -Handlebars.registerHelper('link_to', function(context) { - return "" + context.body + ""; +Handlebars.registerHelper('link_to', function() { + return "" + this.body + ""; }); var context = { posts: [{url: "/hello-world", body: "Hello World!"}] }; -var source = "" +var source = "" var template = Handlebars.compile(source); template(context); @@ -72,6 +72,14 @@ template(context); // ``` +Helpers take precedence over fields defined on the context. To access a field +that is masked by a helper, a path reference may be used. In the example above +a field named `link_to` on the `context` object would be referenced using: + +``` +{{./link_to}} +``` + Escaping -------- @@ -93,17 +101,17 @@ templates easier and also changes a tiny detail of how partials work. Handlebars.js supports an extended expression syntax that we call paths. Paths are made up of typical expressions and . characters. Expressions allow you to not only display data from the current context, but to -display data from contexts that are descendents and ancestors of the +display data from contexts that are descendants and ancestors of the current context. -To display data from descendent contexts, use the `.` character. So, for +To display data from descendant contexts, use the `.` character. So, for example, if your data were structured like: ```js var data = {"person": { "name": "Alan" }, company: {"name": "Rad, Inc." } }; ``` -you could display the person's name from the top-level context with the +You could display the person's name from the top-level context with the following expression: ``` @@ -130,12 +138,12 @@ When calling a helper, you can pass paths or Strings as parameters. For instance: ```js -Handlebars.registerHelper('link_to', function(title, context) { - return "" + title + "!" +Handlebars.registerHelper('link_to', function(title, options) { + return "" + title + "!" }); var context = { posts: [{url: "/hello-world", body: "Hello World!"}] }; -var source = '' +var source = '' var template = Handlebars.compile(source); template(context); @@ -177,12 +185,18 @@ template(data); // ``` -Whenever the block helper is called it is given two parameters, the -argument that is passed to the helper, or the current context if no -argument is passed and the compiled contents of the block. Inside of -the block helper the value of `this` is the current context, wrapped to -include a method named `__get__` that helps translate paths into values -within the helpers. +Whenever the block helper is called it is given one or more parameters, +any arguments that are passed in the helper in the call and an `options` +object containing the `fn` function which executes the block's child. +The block's current context may be accessed through `this`. + +Block helpers have the same syntax as mustache sections but should not be +confused with one another. Sections are akin to an implicit `each` or +`with` statement depending on the input data and helpers are explicit +pieces of code that are free to implement whatever behavior they like. +The [mustache spec](http://mustache.github.io/mustache.5.html) +defines the exact behavior of sections. In the case of name conflicts, +helpers are given priority. ### Partials @@ -335,15 +349,18 @@ Handlebars in the Wild * [Ember.js](http://www.emberjs.com) makes Handlebars.js the primary way to structure your views, also with automatic data binding support. * Les Hill (@leshill) wrote a Rails Asset Pipeline gem named - handlebars_assets](http://github.com/leshill/handlebars_assets). + [handlebars_assets](http://github.com/leshill/handlebars_assets). * [Gist about Synchronous and asynchronous loading of external handlebars templates](https://gist.github.com/2287070) +* [Lumbar](walmartlabs.github.io/lumbar) provides easy module-based template management for handlebars projects. +* [YUI](http://yuilibrary.com/yui/docs/handlebars/) implements a port of handlebars + +Have a project using Handlebars? Send us a [pull request](https://github.com/wycats/handlebars.js/pull/new/master)! Helping Out ----------- To build Handlebars.js you'll need a few things installed. * Node.js -* Jison, for building the compiler - `npm install jison` * Ruby * therubyracer, for running tests - `gem install therubyracer` * rspec, for running tests - `gem install rspec` @@ -353,11 +370,16 @@ and therubyracer if you've got bundler installed. To build Handlebars.js from scratch, you'll want to run `rake compile` in the root of the project. That will build Handlebars and output the -results to the dist/ folder. To run tests, run `rake spec`. You can also -run our set of benchmarks with `rake bench`. +results to the dist/ folder. To run tests, run `rake test`. You can also +run our set of benchmarks with `rake bench`. Node tests can be run with +`npm test` or `rake npm_test`. The default rake target will compile and +run both test suites. + +Some environments, notably Windows, have issues running therubyracer. Under these +envrionments the `rake compile` and `npm test` should be sufficient to test +most handlebars functionality. -If you notice any problems, please report -them to the GitHub issue tracker at +If you notice any problems, please report them to the GitHub issue tracker at [http://github.com/wycats/handlebars.js/issues](http://github.com/wycats/handlebars.js/issues). Feel free to contact commondream or wycats through GitHub with any other questions or feature requests. To submit changes fork the project and diff --git a/Rakefile b/Rakefile index 6fe5d849e..ac935ddb2 100644 --- a/Rakefile +++ b/Rakefile @@ -90,8 +90,24 @@ task :runtime => [:compile] do |task| Rake::Task["dist/handlebars.runtime.js"].execute end +# Updates the various version numbers. +task :version => [] do |task| + # TODO : Pull from package.json when the version numbers are synced + version = File.read("lib/handlebars/base.js").match(/Handlebars.VERSION = "(.*)";/)[1] + + content = File.read("bower.json") + File.open("bower.json", "w") do |file| + file.puts content.gsub(/"version":.*/, "\"version\": \"#{version}\",") + end + + content = File.read("handlebars.js.nuspec") + File.open("handlebars.js.nuspec", "w") do |file| + file.puts content.gsub(/.*<\/version>/, "#{version}") + end +end + desc "build the build and runtime version of handlebars" -task :release => [:build, :runtime] +task :release => [:version, :build, :runtime] directory "vendor" diff --git a/bin/handlebars b/bin/handlebars index d605e74db..5b1ba89c9 100755 --- a/bin/handlebars +++ b/bin/handlebars @@ -168,10 +168,14 @@ function processTemplate(template, root) { if (argv.simple) { output.push(handlebars.precompile(data, options) + '\n'); } else if (argv.partial) { - if(argv.amd && argv._.length == 1){ output.push('return '); } + if(argv.amd && (argv._.length == 1 && !fs.statSync(argv._[0]).isDirectory())) { + output.push('return '); + } output.push('Handlebars.partials[\'' + template + '\'] = template(' + handlebars.precompile(data, options) + ');\n'); } else { - if(argv.amd && argv._.length == 1){ output.push('return '); } + if(argv.amd && (argv._.length == 1 && !fs.statSync(argv._[0]).isDirectory())) { + output.push('return '); + } output.push('templates[\'' + template + '\'] = template(' + handlebars.precompile(data, options) + ');\n'); } } @@ -184,7 +188,7 @@ argv._.forEach(function(template) { // Output the content if (!argv.simple) { if (argv.amd) { - if(argv._.length > 1){ + if(argv._.length > 1 || (argv._.length == 1 && fs.statSync(argv._[0]).isDirectory())) { if(argv.partial){ output.push('return Handlebars.partials;\n'); } else { diff --git a/bower.json b/bower.json new file mode 100644 index 000000000..a5eb00b98 --- /dev/null +++ b/bower.json @@ -0,0 +1,9 @@ +{ + "name": "handlebars.js", + "version": "1.0.0-rc.4", + "main": "dist/handlebars.js", + "ignore": [ + "node_modules", + "components" + ] +} diff --git a/dist/handlebars.js b/dist/handlebars.js index cf0e7f30c..0b52f734c 100644 --- a/dist/handlebars.js +++ b/dist/handlebars.js @@ -36,7 +36,7 @@ THE SOFTWARE. ; // lib/handlebars/base.js -Handlebars.VERSION = "1.0.0-rc.3"; +Handlebars.VERSION = "1.0.0-rc.4"; Handlebars.COMPILER_REVISION = 3; Handlebars.REVISION_CHANGES = { @@ -159,19 +159,19 @@ Handlebars.registerHelper('each', function(context, options) { return ret; }); -Handlebars.registerHelper('if', function(context, options) { - var type = toString.call(context); - if(type === functionType) { context = context.call(this); } +Handlebars.registerHelper('if', function(conditional, options) { + var type = toString.call(conditional); + if(type === functionType) { conditional = conditional.call(this); } - if(!context || Handlebars.Utils.isEmpty(context)) { + if(!conditional || Handlebars.Utils.isEmpty(conditional)) { return options.inverse(this); } else { return options.fn(this); } }); -Handlebars.registerHelper('unless', function(context, options) { - return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn}); +Handlebars.registerHelper('unless', function(conditional, options) { + return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn}); }); Handlebars.registerHelper('with', function(context, options) { @@ -651,7 +651,7 @@ case 33: return 5; break; } }; -lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$:-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$-\/]+)/,/^(?:$)/]; +lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}\/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$\-\/\.]+)/,/^(?:$)/]; lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,33],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"par":{"rules":[31,32],"inclusive":false},"INITIAL":{"rules":[0,1,2,33],"inclusive":true}}; return lexer;})() parser.lexer = lexer; diff --git a/dist/handlebars.runtime.js b/dist/handlebars.runtime.js index 1cc6962a1..bdf81dc9e 100644 --- a/dist/handlebars.runtime.js +++ b/dist/handlebars.runtime.js @@ -36,7 +36,7 @@ THE SOFTWARE. ; // lib/handlebars/base.js -Handlebars.VERSION = "1.0.0-rc.3"; +Handlebars.VERSION = "1.0.0-rc.4"; Handlebars.COMPILER_REVISION = 3; Handlebars.REVISION_CHANGES = { @@ -159,19 +159,19 @@ Handlebars.registerHelper('each', function(context, options) { return ret; }); -Handlebars.registerHelper('if', function(context, options) { - var type = toString.call(context); - if(type === functionType) { context = context.call(this); } +Handlebars.registerHelper('if', function(conditional, options) { + var type = toString.call(conditional); + if(type === functionType) { conditional = conditional.call(this); } - if(!context || Handlebars.Utils.isEmpty(context)) { + if(!conditional || Handlebars.Utils.isEmpty(conditional)) { return options.inverse(this); } else { return options.fn(this); } }); -Handlebars.registerHelper('unless', function(context, options) { - return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn}); +Handlebars.registerHelper('unless', function(conditional, options) { + return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn}); }); Handlebars.registerHelper('with', function(context, options) { diff --git a/handlebars.js.nuspec b/handlebars.js.nuspec new file mode 100644 index 000000000..9a16cc09d --- /dev/null +++ b/handlebars.js.nuspec @@ -0,0 +1,17 @@ + + + + handlebars.js + 1.0.0-rc.4 + handlebars.js Authors + https://github.com/wycats/handlebars.js/blob/master/LICENSE + https://github.com/wycats/handlebars.js/ + false + Extension of the Mustache logicless template language + + handlebars mustache template html + + + + + diff --git a/lib/handlebars.js b/lib/handlebars.js index 7d85c2610..f82ec3bad 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -21,6 +21,17 @@ Handlebars.create = create; module.exports = Handlebars; // instantiate an instance +// Publish a Node.js require() handler for .handlebars and .hbs files +if (require.extensions) { + var extension = function(module, filename) { + var fs = require("fs"); + var templateString = fs.readFileSync(filename, "utf8"); + module.exports = Handlebars.compile(templateString); + }; + require.extensions[".handlebars"] = extension; + require.extensions[".hbs"] = extension; +} + // BEGIN(BROWSER) // END(BROWSER) diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index 5e7ea0e71..0a9b18cd2 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -6,7 +6,7 @@ var Handlebars = {}; // BEGIN(BROWSER) -Handlebars.VERSION = "1.0.0-rc.3"; +Handlebars.VERSION = "1.0.0-rc.4"; Handlebars.COMPILER_REVISION = 3; Handlebars.REVISION_CHANGES = { @@ -129,19 +129,19 @@ Handlebars.registerHelper('each', function(context, options) { return ret; }); -Handlebars.registerHelper('if', function(context, options) { - var type = toString.call(context); - if(type === functionType) { context = context.call(this); } +Handlebars.registerHelper('if', function(conditional, options) { + var type = toString.call(conditional); + if(type === functionType) { conditional = conditional.call(this); } - if(!context || Handlebars.Utils.isEmpty(context)) { + if(!conditional || Handlebars.Utils.isEmpty(conditional)) { return options.inverse(this); } else { return options.fn(this); } }); -Handlebars.registerHelper('unless', function(context, options) { - return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn}); +Handlebars.registerHelper('unless', function(conditional, options) { + return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn}); }); Handlebars.registerHelper('with', function(context, options) { diff --git a/lib/handlebars/utils.js b/lib/handlebars/utils.js index dffc477b9..1e0e4c902 100644 --- a/lib/handlebars/utils.js +++ b/lib/handlebars/utils.js @@ -1,5 +1,7 @@ exports.attach = function(Handlebars) { +var toString = Object.prototype.toString; + // BEGIN(BROWSER) var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; diff --git a/package.json b/package.json index 7dda35743..ab3f6831e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "handlebars", "description": "Extension of the Mustache logicless template language", - "version": "1.0.10", + "version": "1.0.11", "homepage": "http://www.handlebarsjs.com/", "keywords": [ "handlebars mustache template html" diff --git a/release-notes.md b/release-notes.md new file mode 100644 index 000000000..93dabc2f3 --- /dev/null +++ b/release-notes.md @@ -0,0 +1,61 @@ +# Release Notes + +## Development +- [#515](https://github.com/wycats/handlebars.js/issues/515) - Add node require extensions support ([@jjclark1982](https://github.com/jjclark1982)) +- [#517](https://github.com/wycats/handlebars.js/issues/517) - Fix amd precompiler output with directories ([@blessenm](https://github.com/blessenm)) +- [#519](https://github.com/wycats/handlebars.js/issues/519) - Fix partials with . name ([@jamesgorrie](https://github.com/jamesgorrie)) +- [#433](https://github.com/wycats/handlebars.js/issues/433) - Add support for unicode ids +- [#469](https://github.com/wycats/handlebars.js/issues/469) - Add support for `?` in ids +- Docs updates + +[Commits](https://github.com/wycats/handlebars.js/compare/v1.0.11...master) + +## v1.0.11 / 1.0.0-rc4 - May 13 2013 + +- [#458](https://github.com/wycats/handlebars.js/issues/458) - Fix `./foo` syntax ([@jpfiset](https://github.com/jpfiset)) +- [#460](https://github.com/wycats/handlebars.js/issues/460) - Allow `:` in unescaped identifers ([@jpfiset](https://github.com/jpfiset)) +- [#471](https://github.com/wycats/handlebars.js/issues/471) - Create release notes (These!) +- [#456](https://github.com/wycats/handlebars.js/issues/456) - Allow escaping of `\\` +- [#211](https://github.com/wycats/handlebars.js/issues/211) - Fix exception in `escapeExpression` +- [#375](https://github.com/wycats/handlebars.js/issues/375) - Escape unicode newlines +- [#461](https://github.com/wycats/handlebars.js/issues/461) - Do not fail when compiling `""` +- [#302](https://github.com/wycats/handlebars.js/issues/302) - Fix sanity check in knownHelpersOnly mode +- [#369](https://github.com/wycats/handlebars.js/issues/369) - Allow registration of multiple helpers and partial by passing definition object +- Add bower package declaration ([@DevinClark](https://github.com/DevinClark)) +- Add NuSpec package declaration ([@MikeMayer](https://github.com/MikeMayer)) +- Handle empty context in `with` ([@thejohnfreeman](https://github.com/thejohnfreeman)) +- Support custom template extensions in CLI ([@matteoagosti](https://github.com/matteoagosti)) +- Fix Rhino support ([@broady](https://github.com/broady)) +- Include contexts in string mode ([@leshill](https://github.com/leshill)) +- Return precompiled scripts when compiling to AMD ([@JamesMaroney](https://github.com/JamesMaroney)) +- Docs updates ([@iangreenleaf](https://github.com/iangreenleaf), [@gilesbowkett](https://github.com/gilesbowkett), [@utkarsh2012](https://github.com/utkarsh2012)) +- Fix `toString` handling under IE and browserify ([@tommydudebreaux](https://github.com/tommydudebreaux)) +- Add program metadata + +[Commits](https://github.com/wycats/handlebars.js/compare/v1.0.10...v1.0.11) + +## v1.0.10 - Node - Feb 27 2013 + +- [#428](https://github.com/wycats/handlebars.js/issues/428) - Fix incorrect rendering of nested programs +- Fix exception message ([@tricknotes](https://github.com/tricknotes)) +- Added negative number literal support +- Concert library to single IIFE +- Add handlebars-source gemspec ([@machty](https://github.com/machty)) + +[Commits](https://github.com/wycats/handlebars.js/compare/v1.0.9...v1.0.10) + +## v1.0.9 - Node - Feb 15 2013 + +- Added `Handlebars.create` API in node module for sandboxed instances ([@tommydudebreaux](https://github.com/tommydudebreaux)) + +[Commits](https://github.com/wycats/handlebars.js/compare/1.0.0-rc.3...v1.0.9) + +## 1.0.0-rc3 - Browser - Feb 14 2013 + +- Prevent use of `this` or `..` in illogical place ([@leshill](https://github.com/leshill)) +- Allow AST passing for `parse`/`compile`/`precompile` ([@machty](https://github.com/machty)) +- Optimize generated output by inlining statements where possible +- Check compiler version when evaluating templates +- Package browser dist in npm package + +[Commits](https://github.com/wycats/handlebars.js/compare/v1.0.8...1.0.0-rc.3) diff --git a/spec/example_1.handlebars b/spec/example_1.handlebars new file mode 100644 index 000000000..054e96cb8 --- /dev/null +++ b/spec/example_1.handlebars @@ -0,0 +1 @@ +{{foo}} diff --git a/spec/example_2.hbs b/spec/example_2.hbs new file mode 100644 index 000000000..963eab972 --- /dev/null +++ b/spec/example_2.hbs @@ -0,0 +1 @@ +Hello, {{name}}! diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index 293fe1f74..0b58ce1b3 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -125,6 +125,10 @@ def path(*parts) it "parses simple mustaches" do ast_for("{{foo}}").should == root { mustache id("foo") } + ast_for("{{foo?}}").should == root { mustache id("foo?") } + ast_for("{{foo_}}").should == root { mustache id("foo_") } + ast_for("{{foo-}}").should == root { mustache id("foo-") } + ast_for("{{foo:}}").should == root { mustache id("foo:") } end it "parses simple mustaches with data" do diff --git a/spec/qunit_spec.js b/spec/qunit_spec.js index e2f7d47d0..ad690c60f 100644 --- a/spec/qunit_spec.js +++ b/spec/qunit_spec.js @@ -577,6 +577,13 @@ test("Partials with slash paths", function() { shouldCompileToWithPartials(string, [hash, {}, {'shared/dude':dude}], true, "Dudes: Jeepers", "Partials can use literal paths"); }); +test("Partials with slash and point paths", function() { + var string = "Dudes: {{> shared/dude.thing}}"; + var dude = "{{name}}"; + var hash = {name:"Jeepers", another_dude:"Creepers"}; + shouldCompileToWithPartials(string, [hash, {}, {'shared/dude.thing':dude}], true, "Dudes: Jeepers", "Partials can use literal with points in paths"); +}); + test("Multiple partial registration", function() { Handlebars.registerPartial({ 'shared/dude': '{{name}}', @@ -1502,3 +1509,27 @@ test('isEmpty', function() { equal(Handlebars.Utils.isEmpty('foo'), false); equal(Handlebars.Utils.isEmpty({bar: 1}), false); }); + +if (typeof(require) !== 'undefined') { + suite('Require'); + + test('Load .handlebars files with require()', function() { + var template = require("./example_1"); + assert.deepEqual(template, require("./example_1.handlebars")); + + var expected = 'foo\n'; + var result = template({foo: "foo"}); + + equal(result, expected); + }); + + test('Load .hbs files with require()', function() { + var template = require("./example_2"); + assert.deepEqual(template, require("./example_2.hbs")); + + var expected = 'Hello, World!\n'; + var result = template({name: "World"}); + + equal(result, expected); + }); +} diff --git a/src/handlebars.l b/src/handlebars.l index 9af78ba34..349e78f26 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -32,7 +32,7 @@ "{{" { return 'OPEN'; } "=" { return 'EQUALS'; } -"."/[}/ ] { return 'ID'; } +"."/[}\/ ] { return 'ID'; } ".." { return 'ID'; } [\/.] { return 'SEP'; } \s+ { /*ignore whitespace*/ } @@ -44,11 +44,22 @@ "true"/[}\s] { return 'BOOLEAN'; } "false"/[}\s] { return 'BOOLEAN'; } \-?[0-9]+/[}\s] { return 'INTEGER'; } -[a-zA-Z0-9_$:-]+/[=}\s\/.] { return 'ID'; } + +/* +ID is the inverse of control characters. +Control characters ranges: + [\s] Whitespace + [!"#%-,\./] !, ", #, %, &, ', (, ), *, +, ,, ., /, Exceptions in range: $, - + [;->@] ;, <, =, >, @, Exceptions in range: :, ? + [\[-\^`] [, \, ], ^, `, Exceptions in range: _ + [\{-~] {, |, }, ~ +*/ +[^\s!"#%-,\.\/;->@\[-\^`\{-~]+/[=}\s\/.] { return 'ID'; } + '['[^\]]*']' { yytext = yytext.substr(1, yyleng-2); return 'ID'; } . { return 'INVALID'; } \s+ { /*ignore whitespace*/ } -[a-zA-Z0-9_$-\/]+ { this.popState(); return 'PARTIAL_NAME'; } +[a-zA-Z0-9_$\-\/\.]+ { this.popState(); return 'PARTIAL_NAME'; } <> { return 'EOF'; }