Initial commit

This commit is contained in:
Max Brunsfeld 2018-09-30 15:59:56 -07:00
commit ad139e0a3a
15 changed files with 3503 additions and 0 deletions

22
.appveyor.yml Normal file
View File

@ -0,0 +1,22 @@
image: Visual Studio 2015
environment:
nodejs_version: "8"
platform:
- x64
install:
- ps: Install-Product node $env:nodejs_version
- node --version
- npm --version
- npm install
test_script:
- npm run test-windows
build: off
branches:
only:
- master

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
build
*.log

23
.travis.yml Normal file
View File

@ -0,0 +1,23 @@
language: node_js
sudo: false
node_js:
- "node"
compiler: clang-3.6
env:
- CXX=clang-3.6
addons:
apt:
sources:
- llvm-toolchain-precise-3.6
- ubuntu-toolchain-r-test
packages:
- clang-3.6
branches:
only:
- master

18
binding.gyp Normal file
View File

@ -0,0 +1,18 @@
{
"targets": [
{
"target_name": "tree_sitter_css_binding",
"include_dirs": [
"<!(node -e \"require('nan')\")",
"src"
],
"sources": [
"src/parser.c",
"src/binding.cc"
],
"cflags_c": [
"-std=c99",
]
}
]
}

7
corpus/declarations.txt Normal file
View File

@ -0,0 +1,7 @@
==========================
Functions
==========================
a {
color: rgba(0, 255, 0, 0.5);
}

98
corpus/selectors.txt Normal file
View File

@ -0,0 +1,98 @@
=========================
Universal selectors
=========================
* {}
---
(stylesheet
(rule_set (selectors (universal_selector)) (block)))
=========================
Type selectors
=========================
div, span {}
---
(stylesheet
(rule_set (selectors (type_selector (identifier)) (type_selector (identifier))) (block)))
=========================
Class selectors
=========================
.class-a {}
.class-b, .class-c {}
---
(stylesheet
(rule_set (selectors (class_selector (identifier))) (block))
(rule_set (selectors (class_selector (identifier)) (class_selector (identifier))) (block)))
=========================
Id selectors
=========================
#some-id {}
---
(stylesheet
(rule_set (selectors (id_selector (identifier))) (block)))
=========================
Attribute selectors
=========================
[a] {}
[b=c] {}
[d~=e] {}
---
(stylesheet
(rule_set (selectors (attribute_selector (property_name (identifier)))) (block))
(rule_set (selectors (attribute_selector (property_name (identifier)) (property_value))) (block))
(rule_set (selectors (attribute_selector (property_name (identifier)) (property_value))) (block)))
=========================
Child selectors
=========================
a > b {}
c > d > e {}
---
(stylesheet
(rule_set
(selectors (child_selector (type_selector (identifier)) (type_selector (identifier))))
(block))
(rule_set
(selectors (child_selector
(child_selector (type_selector (identifier)) (type_selector (identifier)))
(type_selector (identifier))))
(block)))
=========================
Descendant selectors
=========================
a b {}
c d e {}
---
(stylesheet
(rule_set
(selectors (descendant_selector (type_selector (identifier)) (type_selector (identifier))))
(block))
(rule_set
(selectors (descendant_selector
(descendant_selector (type_selector (identifier)) (type_selector (identifier)))
(type_selector (identifier))))
(block)))

15
corpus/stylesheets.txt Normal file
View File

@ -0,0 +1,15 @@
============================
Rule sets
============================
#some-id {
some-property: 5px;
}
---
(stylesheet
(rule_set
(selectors (id_selector (identifier)))
(block
(declaration (property_name (identifier)) (property_value)))))

97
grammar.js Normal file
View File

@ -0,0 +1,97 @@
module.exports = grammar({
name: 'css',
extras: $ => [
/\s/,
$.comment,
],
rules: {
stylesheet: $ => repeat(choice(
$.rule_set,
$.import_statement,
)),
// Statements
import_statement: $ => seq(
'@import'
),
// Rule sets
rule_set: $ => seq(
$.selectors,
$.block
),
selectors: $ => commaSep1($._selector),
block: $ => seq('{', repeat($.declaration), '}'),
// Selectors
_selector: $ => choice(
$.universal_selector,
$.type_selector,
$.class_selector,
$.id_selector,
$.attribute_selector,
$.child_selector,
$.descendant_selector
),
universal_selector: $ => '*',
type_selector: $ => $.identifier,
class_selector: $ => seq('.', $.identifier),
id_selector: $ => seq('#', $.identifier),
attribute_selector: $ => seq(
choice(
'[',
seq($._selector, token.immediate('['))
),
$.property_name,
optional(seq(
choice('=', '~=', '^=', '|=', '*=', '$='),
$.property_value
)),
']'
),
child_selector: $ => prec.left(seq($._selector, '>', $._selector)),
descendant_selector: $ => prec.left(seq($._selector, $._selector)),
// Declarations
declaration: $ => seq(
$.property_name,
':',
$.property_value,
';'
),
property_name: $ => $.identifier,
identifier: $ => /[a-zA-Z-_]+/,
property_value: $ => /[^;()\[\]]+/,
comment: $ => token(choice(
seq('//', /.*/),
seq(
'/*',
/[^*]*\*+([^/*][^*]*\*+)*/,
'/'
)
))
}
})
function commaSep1 (rule) {
return seq(rule, repeat(seq(',', rule)))
}

9
index.js Normal file
View File

@ -0,0 +1,9 @@
try {
module.exports = require("./build/Release/tree_sitter_css_binding");
} catch (error) {
try {
module.exports = require("./build/Debug/tree_sitter_css_binding");
} catch (_) {
throw error
}
}

1239
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

21
package.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "tree-sitter-css",
"version": "0.0.1",
"description": "CSS grammar for tree-sitter",
"main": "index.js",
"keywords": [
"parser",
"lexer"
],
"author": "Max Brunsfeld",
"license": "MIT",
"dependencies": {
"nan": "^2.11.1"
},
"devDependencies": {
"tree-sitter-cli": "^0.13.5"
},
"scripts": {
"test": "tree-sitter test"
}
}

28
src/binding.cc Normal file
View File

@ -0,0 +1,28 @@
#include "tree_sitter/parser.h"
#include <node.h>
#include "nan.h"
using namespace v8;
extern "C" TSLanguage * tree_sitter_css();
namespace {
NAN_METHOD(New) {}
void Init(Handle<Object> exports, Handle<Object> module) {
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
tpl->SetClassName(Nan::New("Language").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
Local<Function> constructor = tpl->GetFunction();
Local<Object> instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
Nan::SetInternalFieldPointer(instance, 0, tree_sitter_css());
instance->Set(Nan::New("name").ToLocalChecked(), Nan::New("css").ToLocalChecked());
module->Set(Nan::New("exports").ToLocalChecked(), instance);
}
NODE_MODULE(tree_sitter_css_binding, Init)
} // namespace

361
src/grammar.json Normal file
View File

@ -0,0 +1,361 @@
{
"name": "css",
"rules": {
"stylesheet": {
"type": "REPEAT",
"content": {
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "rule_set"
},
{
"type": "SYMBOL",
"name": "import_statement"
}
]
}
},
"import_statement": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "@import"
}
]
},
"rule_set": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "selectors"
},
{
"type": "SYMBOL",
"name": "block"
}
]
},
"selectors": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_selector"
},
{
"type": "REPEAT",
"content": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": ","
},
{
"type": "SYMBOL",
"name": "_selector"
}
]
}
}
]
},
"block": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "{"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "declaration"
}
},
{
"type": "STRING",
"value": "}"
}
]
},
"_selector": {
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "universal_selector"
},
{
"type": "SYMBOL",
"name": "type_selector"
},
{
"type": "SYMBOL",
"name": "class_selector"
},
{
"type": "SYMBOL",
"name": "id_selector"
},
{
"type": "SYMBOL",
"name": "attribute_selector"
},
{
"type": "SYMBOL",
"name": "child_selector"
},
{
"type": "SYMBOL",
"name": "descendant_selector"
}
]
},
"universal_selector": {
"type": "STRING",
"value": "*"
},
"type_selector": {
"type": "SYMBOL",
"name": "identifier"
},
"class_selector": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "."
},
{
"type": "SYMBOL",
"name": "identifier"
}
]
},
"id_selector": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "#"
},
{
"type": "SYMBOL",
"name": "identifier"
}
]
},
"attribute_selector": {
"type": "SEQ",
"members": [
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "["
},
{
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_selector"
},
{
"type": "IMMEDIATE_TOKEN",
"content": {
"type": "STRING",
"value": "["
}
}
]
}
]
},
{
"type": "SYMBOL",
"name": "property_name"
},
{
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "="
},
{
"type": "STRING",
"value": "~="
},
{
"type": "STRING",
"value": "^="
},
{
"type": "STRING",
"value": "|="
},
{
"type": "STRING",
"value": "*="
},
{
"type": "STRING",
"value": "$="
}
]
},
{
"type": "SYMBOL",
"name": "property_value"
}
]
},
{
"type": "BLANK"
}
]
},
{
"type": "STRING",
"value": "]"
}
]
},
"child_selector": {
"type": "PREC_LEFT",
"value": 0,
"content": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_selector"
},
{
"type": "STRING",
"value": ">"
},
{
"type": "SYMBOL",
"name": "_selector"
}
]
}
},
"descendant_selector": {
"type": "PREC_LEFT",
"value": 0,
"content": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_selector"
},
{
"type": "SYMBOL",
"name": "_selector"
}
]
}
},
"declaration": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "property_name"
},
{
"type": "STRING",
"value": ":"
},
{
"type": "SYMBOL",
"name": "property_value"
},
{
"type": "STRING",
"value": ";"
}
]
},
"property_name": {
"type": "SYMBOL",
"name": "identifier"
},
"identifier": {
"type": "PATTERN",
"value": "[a-zA-Z-_]+"
},
"property_value": {
"type": "PATTERN",
"value": "[^;()\\[\\]]+"
},
"comment": {
"type": "TOKEN",
"content": {
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "//"
},
{
"type": "PATTERN",
"value": ".*"
}
]
},
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "/*"
},
{
"type": "PATTERN",
"value": "[^*]*\\*+([^\\/*][^*]*\\*+)*"
},
{
"type": "STRING",
"value": "/"
}
]
}
]
}
}
},
"extras": [
{
"type": "PATTERN",
"value": "\\s"
},
{
"type": "SYMBOL",
"name": "comment"
}
],
"conflicts": [],
"externals": [],
"inline": []
}

1367
src/parser.c Normal file

File diff suppressed because it is too large Load Diff

195
src/tree_sitter/parser.h Normal file
View File

@ -0,0 +1,195 @@
#ifndef TREE_SITTER_PARSER_H_
#define TREE_SITTER_PARSER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define ts_builtin_sym_error ((TSSymbol)-1)
#define ts_builtin_sym_end 0
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
#ifndef TREE_SITTER_RUNTIME_H_
typedef uint16_t TSSymbol;
typedef struct TSLanguage TSLanguage;
#endif
typedef uint16_t TSStateId;
typedef struct {
bool visible : 1;
bool named : 1;
} TSSymbolMetadata;
typedef struct TSLexer TSLexer;
struct TSLexer {
int32_t lookahead;
TSSymbol result_symbol;
void (*advance)(TSLexer *, bool);
void (*mark_end)(TSLexer *);
uint32_t (*get_column)(TSLexer *);
bool (*is_at_included_range_start)(TSLexer *);
};
typedef enum {
TSParseActionTypeShift,
TSParseActionTypeReduce,
TSParseActionTypeAccept,
TSParseActionTypeRecover,
} TSParseActionType;
typedef struct {
union {
struct {
TSStateId state;
bool extra : 1;
bool repetition : 1;
};
struct {
TSSymbol symbol;
int16_t dynamic_precedence;
uint8_t child_count;
uint8_t alias_sequence_id;
};
} params;
TSParseActionType type : 4;
} TSParseAction;
typedef struct {
uint16_t lex_state;
uint16_t external_lex_state;
} TSLexMode;
typedef union {
TSParseAction action;
struct {
uint8_t count;
bool reusable : 1;
};
} TSParseActionEntry;
struct TSLanguage {
uint32_t version;
uint32_t symbol_count;
uint32_t alias_count;
uint32_t token_count;
uint32_t external_token_count;
const char **symbol_names;
const TSSymbolMetadata *symbol_metadata;
const uint16_t *parse_table;
const TSParseActionEntry *parse_actions;
const TSLexMode *lex_modes;
const TSSymbol *alias_sequences;
uint16_t max_alias_sequence_length;
bool (*lex_fn)(TSLexer *, TSStateId);
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
TSSymbol keyword_capture_token;
struct {
const bool *states;
const TSSymbol *symbol_map;
void *(*create)();
void (*destroy)(void *);
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
unsigned (*serialize)(void *, char *);
void (*deserialize)(void *, const char *, unsigned);
} external_scanner;
};
/*
* Lexer Macros
*/
#define START_LEXER() \
bool result = false; \
int32_t lookahead; \
next_state: \
lookahead = lexer->lookahead;
#define ADVANCE(state_value) \
{ \
lexer->advance(lexer, false); \
state = state_value; \
goto next_state; \
}
#define SKIP(state_value) \
{ \
lexer->advance(lexer, true); \
state = state_value; \
goto next_state; \
}
#define ACCEPT_TOKEN(symbol_value) \
result = true; \
lexer->result_symbol = symbol_value; \
lexer->mark_end(lexer);
#define END_STATE() return result;
/*
* Parse Table Macros
*/
#define STATE(id) id
#define ACTIONS(id) id
#define SHIFT(state_value) \
{ \
{ \
.type = TSParseActionTypeShift, \
.params = {.state = state_value}, \
} \
}
#define SHIFT_REPEAT(state_value) \
{ \
{ \
.type = TSParseActionTypeShift, \
.params = { \
.state = state_value, \
.repetition = true \
}, \
} \
}
#define RECOVER() \
{ \
{ .type = TSParseActionTypeRecover } \
}
#define SHIFT_EXTRA() \
{ \
{ \
.type = TSParseActionTypeShift, \
.params = {.extra = true} \
} \
}
#define REDUCE(symbol_val, child_count_val, ...) \
{ \
{ \
.type = TSParseActionTypeReduce, \
.params = { \
.symbol = symbol_val, \
.child_count = child_count_val, \
__VA_ARGS__ \
} \
} \
}
#define ACCEPT_INPUT() \
{ \
{ .type = TSParseActionTypeAccept } \
}
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_PARSER_H_