├── .common.mk ├── .github └── workflows │ └── test.yml ├── .gitignore ├── Makefile ├── ReadMe.md ├── parser-1.2 ├── Makefile ├── build │ ├── bin │ │ └── generate-yaml-grammar │ ├── lib │ │ ├── generate-yaml-grammar-coffeescript.coffee │ │ ├── generate-yaml-grammar-perl.coffee │ │ └── generate-yaml-grammar.coffee │ ├── yaml-spec-1.2-patched.yaml │ ├── yaml-spec-1.2.patch │ └── yaml-spec-1.2.yaml ├── coffeescript │ ├── Makefile │ ├── bin │ │ └── yaml-parser │ ├── lib │ │ ├── grammar.coffee │ │ ├── parser.coffee │ │ ├── prelude.coffee │ │ ├── receiver.coffee │ │ └── test-receiver.coffee │ └── test │ │ ├── error-suite.tml │ │ ├── parse-block.tml │ │ ├── parse-flow.tml │ │ ├── parse-props.tml │ │ ├── parse-scalar.tml │ │ ├── parse-stream.tml │ │ ├── parse-suite.tml │ │ └── testml-bridge.coffee ├── javascript │ ├── Makefile │ ├── bin │ │ └── yaml-parser │ ├── index.html │ ├── lib │ │ ├── grammar.js │ │ ├── parser.js │ │ ├── prelude.js │ │ ├── receiver.js │ │ └── test-receiver.js │ └── test │ │ ├── diff-trace.tml │ │ ├── error-suite.tml │ │ ├── parse-block.tml │ │ ├── parse-flow.tml │ │ ├── parse-props.tml │ │ ├── parse-scalar.tml │ │ ├── parse-stream.tml │ │ ├── parse-suite.tml │ │ └── testml-bridge.js ├── json-parser-coffeescript │ ├── Makefile │ ├── json-parser.coffee │ ├── parser.coffee │ └── test.coffee └── perl │ ├── .gitignore │ ├── Makefile │ ├── bin │ └── yaml-parser │ ├── lib │ ├── Grammar.pm │ ├── Parser.pm │ ├── Prelude.pm │ ├── Receiver.pm │ └── TestReceiver.pm │ └── test │ ├── TestMLBridge.pm │ ├── diff-trace.tml │ ├── error-suite.tml │ ├── parse-block.tml │ ├── parse-flow.tml │ ├── parse-props.tml │ ├── parse-scalar.tml │ ├── parse-stream.tml │ └── parse-suite.tml ├── parser-1.3 ├── Makefile ├── coffeescript │ ├── Makefile │ ├── bin │ │ └── yaml-parser │ ├── lib │ │ ├── grammar.coffee │ │ ├── parser.coffee │ │ ├── prelude.coffee │ │ ├── receiver.coffee │ │ └── test-receiver.coffee │ └── test │ │ ├── error-suite.tml │ │ ├── parse-block.tml │ │ ├── parse-flow.tml │ │ ├── parse-props.tml │ │ ├── parse-scalar.tml │ │ ├── parse-stream.tml │ │ ├── parse-suite.tml │ │ └── testml-bridge.coffee └── javascript │ ├── Makefile │ ├── bin │ └── yaml-parser │ ├── index.html │ ├── lib │ ├── grammar.js │ ├── parser.js │ ├── prelude.js │ ├── receiver.js │ └── test-receiver.js │ └── test │ ├── diff-trace.tml │ ├── error-suite.tml │ ├── parse-block.tml │ ├── parse-flow.tml │ ├── parse-props.tml │ ├── parse-scalar.tml │ ├── parse-stream.tml │ ├── parse-suite.tml │ └── testml-bridge.js └── test ├── .gitignore ├── Dockerfile ├── Makefile ├── diff-trace.tml ├── error-suite.tml ├── parse-block.tml ├── parse-flow.tml ├── parse-props.tml ├── parse-scalar.tml ├── parse-stream.tml └── parse-suite.tml /.common.mk: -------------------------------------------------------------------------------- 1 | SHELL := bash 2 | 3 | .PHONY: test 4 | 5 | ifeq (,$(ROOT)) 6 | $(error ROOT not defined) 7 | endif 8 | ifeq (,$(BASE)) 9 | $(error BASE not defined) 10 | endif 11 | BASE12 := $(ROOT)/parser-1.2 12 | 13 | LOCAL_MAKE := $(ROOT)/.git/local.mk 14 | ifneq (,$(wildcard $(LOCAL_MAKE))) 15 | $(info ***** USING LOCAL MAKEFILE OVERRIDES *****) 16 | $(info ***** $(LOCAL_MAKE)) 17 | include $(LOCAL_MAKE) 18 | endif 19 | 20 | SPEC_PATCHED_YAML := $(BASE)/build/yaml-spec-1.2-patched.yaml 21 | SPEC_YAML := $(BASE)/build/yaml-spec-1.2.yaml 22 | SPEC_PATCH := $(BASE)/build/yaml-spec-1.2.patch 23 | GENERATOR := $(BASE)/build/bin/generate-yaml-grammar 24 | GENERATOR_LIB := $(BASE)/build/lib/generate-yaml-grammar.coffee 25 | GENERATOR_LANG_LIB := $(BASE)/build/lib/generate-yaml-grammar-$(PARSER_LANG).coffee 26 | NODE_MODULES := $(ROOT)/node_modules 27 | 28 | TESTML_REPO ?= https://github.com/testml-lang/testml 29 | TESTML_COMMIT ?= master 30 | YAML_TEST_SUITE_REPO ?= https://github.com/yaml/yaml-test-suite 31 | YAML_TEST_SUITE_COMMIT ?= main 32 | 33 | PATH := $(NODE_MODULES)/.bin:$(PATH) 34 | PATH := $(ROOT)/test/testml/bin:$(PATH) 35 | export PATH 36 | 37 | export TESTML_RUN := $(BIN)-tap 38 | export TESTML_LIB := $(ROOT)/test:$(ROOT)/test/suite/test:$(TESTML_LIB) 39 | 40 | BUILD_DEPS ?= $(NODE_MODULES) 41 | TEST_DEPS ?= \ 42 | $(ROOT)/test/testml \ 43 | $(ROOT)/test/suite \ 44 | 45 | test := test/*.tml 46 | 47 | .DELETE_ON_ERROR: 48 | 49 | default: 50 | 51 | build:: $(BUILD_DEPS) $(GRAMMAR) 52 | 53 | test:: build $(TEST_DEPS) 54 | TRACE=$(TRACE) TRACE_QUIET=$(TRACE_QUIET) DEBUG=$(DEBUG) \ 55 | prove -v $(test) 56 | 57 | clean:: 58 | 59 | $(GRAMMAR): $(SPEC_PATCHED_YAML) $(GENERATOR) $(GENERATOR_LIB) $(GENERATOR_LANG_LIB) 60 | $(GENERATOR) \ 61 | --from=$< \ 62 | --to=$(PARSER_LANG) \ 63 | --rule=l-yaml-stream \ 64 | > $@ 65 | 66 | $(SPEC_PATCHED_YAML): $(SPEC_YAML) 67 | cp $< $@ 68 | patch $@ < $(SPEC_PATCH) 69 | 70 | $(SPEC_YAML): 71 | cp $(ROOT)/../yaml-grammar/yaml-spec-1.2.yaml $@ || \ 72 | wget https://raw.githubusercontent.com/yaml/yaml-grammar/master/yaml-spec-1.2.yaml || \ 73 | curl -O https://raw.githubusercontent.com/yaml/yaml-grammar/master/yaml-spec-1.2.yaml 74 | 75 | $(ROOT)/test/suite \ 76 | $(ROOT)/test/testml: $(ROOT)/test $(BASE12)/perl/ext-perl 77 | $(eval override export PERL5LIB := $(BASE12)/perl/ext-perl/lib/perl5:$(PERL5LIB)) 78 | $(MAKE) -C $< all 79 | 80 | $(NODE_MODULES): 81 | $(MAKE) -C $(ROOT) $(@:$(ROOT)/%=%) 82 | 83 | $(BASE12)/perl/ext-perl: 84 | $(MAKE) -C $(BASE12)/perl ext-perl 85 | 86 | 87 | define git-clone 88 | mkdir $1 89 | git -C $1 init --quiet 90 | git -C $1 remote add origin $2 91 | git -C $1 fetch origin $3 92 | git -C $1 reset --hard FETCH_HEAD 93 | endef 94 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ '*' ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: 15 | - ubuntu-latest 16 | - macOS-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: actions/setup-node@v1 21 | with: {node-version: 14.x} 22 | 23 | - name: Linux deps 24 | if: ${{ matrix.os == 'macOS-latest' }} 25 | run: | 26 | brew install cpanm 27 | sudo cpanm -n YAML::PP 28 | 29 | - name: Fetch branches 30 | run: | 31 | git config remote.origin.fetch +refs/heads/*:refs/remotes/origin/* 32 | git fetch --unshallow || true 33 | 34 | - run: make test 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /test/yaml-test-suite.tml 2 | /node_modules/ 3 | .testml/ 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := bash 2 | 3 | ROOT := $(shell pwd) 4 | 5 | PARSER_1_3 := parser-1.3 6 | PARSER_1_2 := parser-1.2 7 | 8 | ALL := \ 9 | $(PARSER_1_3) \ 10 | $(PARSER_1_2) \ 11 | 12 | ALL_TEST := $(ALL:parser-%=test-%) 13 | ALL_CLEAN := $(ALL:parser-%=clean-%) 14 | 15 | default: 16 | @echo $(ALL_TEST) 17 | 18 | test: test-1.3 19 | 20 | test-all: $(ALL_TEST) 21 | 22 | $(ALL_TEST): 23 | $(MAKE) -C $(@:test-%=parser-%) test TRACE=$(TRACE) DEBUG=$(DEBUG) 24 | 25 | clean: $(ALL_CLEAN) 26 | rm -fr node_modules 27 | 28 | $(ALL_CLEAN): 29 | $(MAKE) -C $(@:clean-%=parser-%) clean 30 | 31 | node_modules: 32 | git branch --track $@ origin/$@ 2>/dev/null || true 33 | git worktree add -f $@ $@ 34 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | YAML 1.2 Reference Parsers 2 | ========================== 3 | 4 | Generate YAML 1.2 Reference Parsers from the Spec 5 | 6 | [![Travis Test Status]( 7 | https://travis-ci.org/yaml/yaml-reference-parser.svg?branch=master)]( 8 | https://travis-ci.org/yaml/yaml-reference-parser) 9 | [![Actions Status: Test]( 10 | https://github.com/yaml/yaml-reference-parser/workflows/Test/badge.svg)]( 11 | https://github.com/yaml/yaml-reference-parser/actions?query=workflow%3A"Test") 12 | 13 | # Synopsis 14 | 15 | You can see all the generated YAML parsers in action by running: 16 | ``` 17 | make test 18 | ``` 19 | 20 | # Description 21 | 22 | Here we generate 100% compliant YAML 1.2 parsers, in multiple programming 23 | languages, from the YAML 1.2 specification. 24 | 25 | We start with the [YAML 1.2 Spec]( 26 | https://yaml.org/spec/1.2/spec.html#id2770814) converted to the [YAML 1.2 Spec 27 | as YAML]( 28 | https://github.com/yaml/yaml-grammar/blob/master/yaml-spec-1.2-patch.yaml). 29 | Next we apply some minimal local patches for various problems that have been 30 | identified in the spec. 31 | Then we convert that YAML into a machine generated YAML 1.2 parser/grammar 32 | module in every programming language. 33 | 34 | At the moment we have YAML 1.2 reference parsers in these languages: 35 | * [CoffeeScript]( 36 | https://github.com/yaml/yaml-grammar/tree/master/parser/coffeescript/lib/grammar.coffee) 37 | * [JavaScript]( 38 | https://github.com/yaml/yaml-grammar/tree/master/parser/javascript/lib/grammar.js) 39 | * [Perl]( 40 | https://github.com/yaml/yaml-grammar/tree/master/parser/perl/lib/Grammar.pm) 41 | 42 | The parsers pass 100% of the [YAML Test Suite]( 43 | https://github.com/yaml/yaml-test-suite/)! 44 | 45 | # Next Steps 46 | 47 | * Generate equivalent grammar/parsers in as many modern languages as possible 48 | * The logic and primitives are purposefully as simple as possible 49 | * Generating a perfect parser in a new language should take a day or 2 50 | * Contributors welcome! 51 | * Start refactoring the grammar to be simpler 52 | * While always passing the tests 53 | * Generate more optimized / performant parsers 54 | * The goal of simplicity and sticking exactly to the spec made slow parsers 55 | * Create a new YAML 1.3 grammar 56 | * Start with 1.2 grammar 57 | * Apply RFCs as they become accepted 58 | * Generate and test all the language parsers 59 | -------------------------------------------------------------------------------- /parser-1.2/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := bash 2 | 3 | ROOT := $(shell cd ..; pwd) 4 | 5 | ALL := \ 6 | coffeescript \ 7 | javascript \ 8 | perl \ 9 | 10 | ALL_BUILD := $(ALL:%=build-%) 11 | ALL_TEST := $(ALL:%=test-%) 12 | ALL_CLEAN := $(ALL:%=clean-%) 13 | 14 | default: 15 | 16 | build: $(ALL_BUILD) 17 | 18 | build-%: 19 | $(MAKE) -C $(@:build-%=%) build 20 | 21 | test: $(ALL_TEST) 22 | 23 | test-%: 24 | $(MAKE) -C $(@:test-%=%) test TRACE=$(TRACE) DEBUG=$(DEBUG) 25 | 26 | clean: $(ALL_CLEAN) 27 | $(MAKE) -C $(ROOT)/test $@ 28 | 29 | clean-%: 30 | $(MAKE) -C $(@:clean-%=%) clean 31 | -------------------------------------------------------------------------------- /parser-1.2/build/bin/generate-yaml-grammar: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | 3 | VERSION = '0.0.1' 4 | 5 | require 'ingy-prelude' 6 | {ArgumentParser} = require 'argparse' 7 | 8 | ap = new ArgumentParser 9 | add_help: true, 10 | description: 'YAML Grammar Generator' 11 | 12 | ap.add_argument '-f', '--from', 13 | help: "File path of 'yaml-spec-#.#.yaml" 14 | required: true 15 | type: 'str' 16 | 17 | ap.add_argument '-t', '--to', 18 | help: 'Language to generate YAML parser into' 19 | required: true 20 | choices: ['coffeescript', 'perl'] 21 | 22 | ap.add_argument '-r', '--rule', 23 | help: 'Starting rule of grammar' 24 | required: true 25 | 26 | ap.add_argument '--version', 27 | action: 'version' 28 | version: VERSION 29 | 30 | {from, to, rule} = ap.parse_args() 31 | 32 | spec = file_read from 33 | 34 | require "../lib/generate-yaml-grammar-#{to}" 35 | 36 | generator = new generator_class(spec) 37 | 38 | try 39 | generator.gen_grammar(rule) 40 | 41 | catch e 42 | file_write '/tmp/yaml-grammar.coffee', generator.grammar 43 | throw e 44 | 45 | file_write '/tmp/yaml-grammar.coffee', generator.grammar # XXX 46 | 47 | out generator.grammar 48 | -------------------------------------------------------------------------------- /parser-1.2/build/lib/generate-yaml-grammar-coffeescript.coffee: -------------------------------------------------------------------------------- 1 | require './generate-yaml-grammar' 2 | 3 | global.generator_class = \ 4 | class YamlGrammarCoffeeScriptGenerator \ 5 | extends YamlGrammarGenerator 6 | 7 | gen_grammar_head: (top)-> 8 | name = @rule_name top 9 | """\ 10 | #{@gen_grammar_info()} 11 | global.Grammar = class Grammar 12 | 13 | TOP: -> @#{name} 14 | \n\n 15 | """ 16 | 17 | gen_setm: (return_false)-> 18 | setm = "m = @call [@auto_detect_indent, n], 'number'" 19 | if return_false 20 | setm = "\n return false unless " + setm 21 | else 22 | setm = "\n " + setm 23 | return setm 24 | 25 | gen_rule_code: (num, comment, rule_name, debug_args, rule_args, rule_body)-> 26 | 27 | @indent """\ 28 | #{comment} 29 | @::#{rule_name}.num = #{Number @num} 30 | #{rule_name}: #{rule_args}->#{@setm} 31 | debug_rule("#{rule_name}"#{debug_args}) 32 | #{rule_body} 33 | \n\n\n 34 | """ 35 | 36 | gen_rule_args: (name)-> 37 | rule = @spec[name] or XXX name 38 | return '' unless _.isPlainObject rule 39 | @args = rule['(...)'] 40 | return '' unless @args? 41 | delete rule['(...)'] 42 | @args = [ @args ] unless _.isArray @args 43 | "(#{@args.join ', '})" 44 | 45 | gen_debug_args: (name)-> 46 | rule = @spec[name] or XXX name 47 | return '' unless _.isPlainObject rule 48 | args = rule['(...)'] 49 | return '' unless args? 50 | args = [ args ] unless _.isArray args 51 | args = _.map args, (a)-> "#{a}" 52 | str = ',' + args.join(', ') 53 | .replace /\ /g, '' 54 | .replace /^\(\)$/, '' 55 | 56 | gen_hex_code: (hex)-> "\"\\u{#{hex}}\"" 57 | 58 | gen_pair_sep: -> ': ' 59 | 60 | gen_null: -> 'null' 61 | 62 | gen_method_call: (method, args...)-> 63 | [args, sep] = @gen_method_call_args args 64 | "@#{method}(#{sep}#{args}#{sep})" 65 | 66 | gen_method_ref: (name)-> 67 | if name == 'auto_detect_indent' 68 | "[@#{name}, n]" 69 | else 70 | "@#{name}" 71 | 72 | gen_comment_block: (text)-> 73 | text = text 74 | .replace /\n$/, '' 75 | """\ 76 | ### 77 | #{text} 78 | ### 79 | """ 80 | 81 | fix_group: (text)-> 82 | i = 0 83 | text.replace /^ @rep/mg, (m...)-> 84 | m[0] + (if i++ then String(i) else '') 85 | 86 | # vim: set sw=2: 87 | -------------------------------------------------------------------------------- /parser-1.2/build/lib/generate-yaml-grammar-perl.coffee: -------------------------------------------------------------------------------- 1 | require './generate-yaml-grammar' 2 | 3 | global.generator_class = \ 4 | class YamlGrammarPerlGenerator \ 5 | extends YamlGrammarGenerator 6 | 7 | gen_grammar_head: (top)-> 8 | name = @rule_name top 9 | """\ 10 | #{@gen_grammar_info()} 11 | use v5.12; 12 | package Grammar; 13 | use Prelude; 14 | 15 | use constant DEBUG => $ENV{DEBUG}; 16 | 17 | rule '000', TOP => sub { 18 | my ($self) = @_; 19 | $self->func('#{name}'); 20 | }; 21 | \n\n 22 | """ 23 | 24 | gen_grammar_tail: -> 25 | """ 26 | 1; 27 | """ 28 | 29 | gen_setm: (return_false)-> 30 | setm = "\n my $m = $self->call([$self->func('auto_detect_indent'), $n], 'number');" 31 | if return_false 32 | setm = setm.replace /;/, " or return false;" 33 | return setm 34 | 35 | gen_rule_code: (num, comment, rule_name, debug_args, rule_args, rule_body)-> 36 | """\ 37 | #{comment} 38 | rule '#{num}', #{rule_name} => sub { 39 | my #{rule_args} = @_;#{@setm} 40 | debug_rule("#{rule_name}"#{debug_args}) if DEBUG; 41 | #{rule_body}; 42 | }; 43 | \n\n\n 44 | """ 45 | 46 | gen_rep: (rule, min, max)-> 47 | @gen_method_call('rep', @gen_limit(min), @gen_limit(max), @gen(rule)) 48 | .replace /\(\n (-?\d+),\n (undef|-?\d+),/, "($1, $2," 49 | .replace /\)\n\)/, '))' 50 | 51 | gen_rule_args: (name)-> 52 | rule = @spec[name] or XXX name 53 | return '($self)' unless _.isPlainObject rule 54 | args = rule['(...)'] 55 | return '($self)' unless args? 56 | delete rule['(...)'] 57 | @args = args 58 | args = [ args ] unless _.isArray args 59 | args = _.map args, (a)-> if a? then "$#{a}" else 'undef' 60 | args.unshift '$self' 61 | "(#{args.join ', '})" 62 | 63 | gen_debug_args: (name)-> 64 | rule = @spec[name] or XXX name 65 | return '' unless _.isPlainObject rule 66 | args = rule['(...)'] 67 | return '' unless args? 68 | args = [ args ] unless _.isArray args 69 | args = _.map args, (a)-> if a? then "$#{a}" else 'undef' 70 | str = ',' + args.join(', ') 71 | .replace /\ /g, '' 72 | .replace /^\(\)$/, '' 73 | 74 | gen_hex_code: (hex)-> "\"\\x{#{hex}}\"" 75 | 76 | gen_var_value: (var_)-> "$#{var_}" 77 | 78 | gen_limit: (n)-> 79 | if _.isNumber n then n else if n? then "$#{n}" else 'undef' 80 | 81 | gen_pair_sep: -> ' => ' 82 | 83 | gen_null: -> 'undef' 84 | 85 | gen_method_call: (method, args...)-> 86 | [args, sep] = @gen_method_call_args args 87 | "$self->#{method}(#{sep}#{args}#{sep})" 88 | 89 | gen_method_ref: (name)-> 90 | if name == 'auto_detect_indent' 91 | "[$self->func('#{name}'), $n]" 92 | else 93 | "$self->func('#{name}')" 94 | 95 | gen_comment_block: (text)-> 96 | text = text 97 | .replace /^/mg, '# ' 98 | .replace /\n$/, '' 99 | """\ 100 | ### 101 | #{text} 102 | ### 103 | """ 104 | 105 | fix_group: (text)-> 106 | i = 0 107 | text.replace /^ \$self->rep/mg, (m...)-> 108 | m[0] + (if i++ then String(i) else '') 109 | 110 | # vim: set sw=2: 111 | -------------------------------------------------------------------------------- /parser-1.2/build/lib/generate-yaml-grammar.coffee: -------------------------------------------------------------------------------- 1 | require '../../coffeescript/lib/prelude' 2 | 3 | global.YamlGrammarGenerator = class YamlGrammarGenerator 4 | constructor: (spec)-> 5 | @spec = require('yaml').parse spec 6 | @comments = @get_comments spec 7 | @num = 0 8 | @grammar = '' 9 | @arg = false 10 | @group = false 11 | @nums = _.keys(@spec) \ 12 | .filter (k)-> 13 | k[0] == ':' 14 | .reduce (n, k)=> 15 | n[@spec[k]] = k 16 | return n 17 | , {} 18 | 19 | gen_grammar: (top)-> 20 | @out @gen_grammar_head top 21 | 22 | for name in _.keys(@spec) 23 | continue if name[0] == ':' 24 | @out @gen_rule(name) 25 | 26 | @out @gen_grammar_tail() 27 | 28 | @grammar 29 | 30 | gen_grammar_head: -> @not_implemented('gen_grammar_head') 31 | 32 | gen_grammar_info: -> 33 | @gen_comment_block( 34 | """\ 35 | This grammar class was generated from https://yaml.org/spec/1.2/spec.html 36 | """ 37 | ) + "\n" 38 | 39 | gen_grammar_tail: => '' 40 | 41 | gen_rule: (name)-> 42 | @setm = '' 43 | if @spec[name]['(->m)'] 44 | delete @spec[name]['(->m)'] 45 | @setm = @gen_setm false 46 | else if @spec[name]['(m>0)'] 47 | delete @spec[name]['(m>0)'] 48 | @setm = @gen_setm true 49 | num = @num = @nums[name][1..] 50 | comment = @comments[num] 51 | rule_name = @rule_name name 52 | debug_args = @gen_debug_args name 53 | rule_args = @gen_rule_args name 54 | rule_body = @indent(@gen @spec[name]) 55 | @gen_rule_code num, comment, rule_name, debug_args, rule_args, rule_body 56 | 57 | gen_rule_code: -> @not_implemented('gen_rule_code') 58 | 59 | gen_rule_args: -> @not_implemented('gen_rule_args') 60 | 61 | gen_debug_args: -> @not_implemented('gen_debug_args') 62 | 63 | gen: (rule)-> 64 | if isObject rule 65 | return @gen_from_hash rule 66 | 67 | if isArray rule 68 | return @gen_from_array rule 69 | 70 | if isString rule 71 | return @gen_from_string rule 72 | 73 | if isNumber rule 74 | return "#{rule}" 75 | 76 | if rule == null 77 | return @gen_null() 78 | 79 | xxx "[#{@num}] unknown rule type", rule 80 | 81 | gen_from_hash: (rule)-> 82 | set = rule['(set)'] 83 | 84 | keys = _.keys rule 85 | if keys.length > 1 and not set? 86 | XXX ["[#{@num}] unknown keys", keys] 87 | 88 | key = keys[0] 89 | val = rule[key] 90 | 91 | if m = key.match /^\(\{(.)\}\)$/ 92 | return @gen_rep val, m[1], m[1] 93 | 94 | if m = key.match /^(([bcls]|n[bs]|in|seq)[-+][-+a-z]+)$/ 95 | return @gen_call key, val 96 | 97 | if key == 'auto-detect' 98 | return @gen_call key, val 99 | 100 | switch key 101 | when '(any)' then return @gen_group val, 'any' 102 | when '(all)' then return @gen_group val, 'all' 103 | when '(---)' then return @gen_group val, 'but' 104 | 105 | when '(+++)' then return @gen_rep val, 1, null 106 | when '(***)' then return @gen_rep val, 0, null 107 | when '(???)' then return @gen_rep val, 0, 1 108 | 109 | when '(<<<)' then return @gen_may val 110 | when '(===)' then return @gen_chk val, '=' 111 | when '(!==)' then return @gen_chk val, '!' 112 | when '(<==)' then return @gen_chk val, '<=' 113 | 114 | when '(+)' then return @gen_add val 115 | when '(-)' then return @gen_sub val 116 | when '(<)' then return @gen_lt val 117 | when '(<=)' then return @gen_le val 118 | 119 | when '(case)' then return @gen_case val 120 | when '(flip)' then return @gen_flip val 121 | when '(max)' then return @gen_max val 122 | when '(if)' then return @gen_if val, set 123 | when '(set)' then return @gen_set val 124 | when '(ord)' then return @gen_ord val 125 | when '(len)' then return @gen_len val 126 | when '(exclude)' then return @gen_exclude val 127 | 128 | else XXX "[#{@num}] unknown hash rule", rule, @num 129 | 130 | gen_from_array: (rule)-> 131 | if rule.length == 2 and rule[0].match /^x/ 132 | [x, y] = rule 133 | return @gen_method_call 'rng', 134 | @gen_hex_code(x[1..]), 135 | @gen_hex_code(y[1..]) 136 | 137 | xxx "[#{@num}] Unknown array rule type", rule 138 | 139 | 140 | gen_from_string: (rule)-> 141 | if m = rule.match /^x([0-9A-F]+)$/ 142 | return @gen_method_call 'chr', @gen_hex_code m[1] 143 | if rule.match(/^(?:b|c|e|l|nb|ns|s)(?:[-+][a-z0-9]+)+$/) 144 | return @gen_method_ref @rule_name rule 145 | if rule.length == 1 146 | if rule == "'" 147 | return @gen_method_call 'chr', "\"'\"" 148 | if rule == "\\" 149 | return @gen_method_call 'chr', "\"\\\\\"" 150 | if @arg 151 | if rule in ['m', 't'] 152 | if not(rule in @args) and not(@setm) 153 | return @gen_method_call rule 154 | return @gen_var_value rule 155 | return @gen_method_call 'chr', "'#{rule}'" 156 | if rule.match(/^(flow|block)-(in|out|key)$/) or 157 | rule in ['auto-detect', 'strip', 'clip', 'keep'] 158 | return '"' + rule + '"' 159 | if m = rule.match /^<(\w.+)>$/ 160 | return @gen_method_ref @rule_name m[1] 161 | if rule == '(match)' 162 | return @gen_method_ref 'match' 163 | 164 | xxx "[#{@num}] Unknown string rule type", rule 165 | 166 | gen_var_value: (var_)-> var_ 167 | 168 | gen_group: (list, kind)-> 169 | list = list.map (item)=> @gen(item) 170 | @group = true 171 | out = @gen_method_call(kind, list...) 172 | @group = false 173 | @fix_group out 174 | 175 | gen_rep: (rule, min, max)-> 176 | @gen_method_call('rep', @gen_limit(min), @gen_limit(max), @gen(rule)) 177 | .replace /\(\n (-?\d+),\n (null|-?\d+),/, "($1, $2," 178 | .replace /\)\n\)/, '))' 179 | 180 | gen_limit: (n)-> n 181 | 182 | gen_call: (call, args)-> 183 | args = [ args ] unless isArray args 184 | 185 | list = args.map (arg)=> @gen_arg arg 186 | list = list.join ', ' 187 | 188 | ref = @gen_method_ref @rule_name call 189 | 190 | "[ #{ref}, #{list} ]" 191 | 192 | gen_may: (rule)-> 193 | @gen_method_call 'may', @gen(rule) 194 | 195 | gen_chk: (rule, kind)-> 196 | @gen_method_call 'chk', "'#{kind}'", @gen(rule) 197 | 198 | gen_case: (obj)-> 199 | var_ = obj.var 200 | delete obj.var 201 | @gen_method_call 'case', 202 | @gen_var_value(var_), 203 | @gen_hash(obj) 204 | 205 | gen_hash: (obj)-> 206 | out = "{\n" 207 | for key, val of obj 208 | rule = @gen(val) 209 | .replace(/\s+/g, ' ') 210 | .replace(/,\ \)/, ' )') 211 | out += @indent "'#{key}'#{@gen_pair_sep()}#{rule},\n" 212 | out + "}" 213 | 214 | gen_flip: (obj)-> 215 | var_ = obj.var 216 | hash = "{\n" 217 | delete obj.var 218 | for key, val of obj 219 | rule = @gen_arg val 220 | hash += " '#{key}'#{@gen_pair_sep()}#{rule},\n" 221 | hash += "}" 222 | @gen_method_call 'flip', 223 | @gen_var_value(var_), 224 | hash 225 | 226 | gen_max: (max)-> 227 | @gen_method_call 'max', max 228 | 229 | gen_if: (cond, set)-> 230 | @gen_method_call 'if', @gen(cond), @gen_set(set) 231 | 232 | gen_set: ([vari, expr])-> 233 | @gen_method_call 'set', "'#{vari}'", @gen(expr) 234 | 235 | gen_ord: (expr)-> 236 | @gen_method_call 'ord', @gen(expr) 237 | 238 | gen_len: (expr)-> 239 | @gen_method_call 'len', @gen(expr) 240 | 241 | gen_exclude: (rule)-> 242 | @gen_method_call 'exclude', @gen(rule) 243 | 244 | gen_add: ([x, y])-> 245 | @gen_method_call 'add', @gen(x), @gen(y) 246 | 247 | gen_sub: ([x, y])-> 248 | @gen_method_call 'sub', @gen(x), @gen(y) 249 | 250 | gen_lt: ([x, y])-> 251 | @gen_method_call 'lt', @gen_arg(x), @gen_arg(y) 252 | 253 | gen_le: ([x, y])-> 254 | @gen_method_call 'le', @gen_arg(x), @gen_arg(y) 255 | 256 | gen_null: -> @not_implemented('gen_null') 257 | 258 | gen_method_call: -> @not_implemented('gen_method_call') 259 | 260 | gen_method_call_args: (args)-> 261 | multiline = false 262 | sep = '' 263 | 264 | for arg in args 265 | if @group 266 | multiline = true 267 | else if isString(arg) 268 | if arg.match(/\n/) 269 | multiline = true 270 | if multiline 271 | sep = "\n" 272 | args = args.map (a)=> @indent @string a 273 | args = args.join(",#{sep}") 274 | else 275 | args = args.map (a)=> @string a 276 | args = args.join(", ") 277 | 278 | return [args, sep] 279 | 280 | gen_arg: (arg)-> 281 | @arg = true 282 | arg = @gen arg 283 | @arg = false 284 | arg 285 | 286 | out: (text)-> 287 | @grammar += text 288 | 289 | string: (o)-> 290 | if o? 291 | String o 292 | else 293 | @gen_null() 294 | 295 | indent: (text, n=1)-> 296 | text.replace(/^(.)/gm, "#{_.repeat(' ', n)}$1") 297 | 298 | rule_name: (name)-> 299 | name.replace /[-+]/g, '_' 300 | 301 | get_comments: (spec)-> 302 | comments = {} 303 | lines = spec.split "\n" 304 | while (line = lines.shift())? 305 | if m = line.match /^:(\d+):/ 306 | num = m[1] 307 | key = "#{num}" 308 | comments[key] = "# [#{num}]\n" 309 | while (line = lines.shift())? 310 | break unless line.match /^# / 311 | comments[key] += line + '\n' 312 | comments 313 | 314 | not_implemented: (method)-> 315 | class_ = @constructor.name 316 | die "Class '#{class_}' does not implement '#{method}'" 317 | 318 | # vim: set sw=2: 319 | -------------------------------------------------------------------------------- /parser-1.2/build/yaml-spec-1.2.patch: -------------------------------------------------------------------------------- 1 | --- yaml-spec-1.2.yaml 2021-09-05 14:18:57.260078873 -0700 2 | +++ yaml-spec-1.2-patched.yaml 2021-11-26 10:52:23.844142404 -0800 3 | @@ -2183,6 +2183,10 @@ 4 | 5 | 6 | 7 | +#=============================================================================== 8 | +# This rule was modified to add a whitespace lookahead assertion after '?'. 9 | +#=============================================================================== 10 | + 11 | :142: ns-flow-map-entry 12 | # ns-flow-map-entry(n,c) ::= 13 | # ( '?' s-separate(n,c) 14 | @@ -2194,6 +2198,7 @@ 15 | (any): 16 | - (all): 17 | - '?' 18 | + - (===): { (any): [ , s-white, b-break ] } 19 | - s-separate: [ n, c ] 20 | - ns-flow-map-explicit-entry: [ n, c ] 21 | - ns-flow-map-implicit-entry: [ n, c ] 22 | @@ -2320,6 +2325,10 @@ 23 | 24 | 25 | 26 | +#=============================================================================== 27 | +# This rule was modified to add a whitespace lookahead assertion after '?'. 28 | +#=============================================================================== 29 | + 30 | :150: ns-flow-pair 31 | # ns-flow-pair(n,c) ::= 32 | # ( '?' s-separate(n,c) 33 | @@ -2331,6 +2340,7 @@ 34 | (any): 35 | - (all): 36 | - '?' 37 | + - (===): { (any): [ , s-white, b-break ] } 38 | - s-separate: [ n, c ] 39 | - ns-flow-map-explicit-entry: [ n, c ] 40 | - ns-flow-pair-entry: [ n, c ] 41 | @@ -2472,7 +2482,7 @@ 42 | - (any): 43 | - (all): 44 | - s-separate: [ n, c ] 45 | - - ns-flow-yaml-content: [ n, c ] 46 | + - ns-flow-content: [ n, c ] 47 | - e-scalar 48 | 49 | 50 | @@ -2530,6 +2540,23 @@ 51 | # 52 | # m - relative indentation columns or 'auto-detect()' 53 | # t - trailing whitespace indicator 54 | +# 55 | +# This production (162) is misleading. It claims to be called with 2 arguments, 56 | +# `m` and `t`, but it is not called with any arguments. In reality it is 57 | +# supposed to *create* 2 state variables `m` and `t`. To auto-detect `m` it 58 | +# will need the value of `n` (the current indentation). 59 | +# 60 | +# The spec rule 162 should actually be: 61 | +# 62 | +# c-b-block-header(n) ::= 63 | +# ( ( c-indentation-indicator 64 | +# c-chomping-indicator ) 65 | +# | ( c-chomping-indicator 66 | +# c-indentation-indicator(n) ) ) 67 | +# s-b-comment 68 | +# 69 | +# The original from the spec is left intact below, but the generated YAML uses 70 | +# the version above. 71 | #=============================================================================== 72 | 73 | :162: c-b-block-header 74 | @@ -2541,34 +2568,62 @@ 75 | # s-b-comment 76 | 77 | c-b-block-header: 78 | - (...): [ m, t ] 79 | + (...): n 80 | (all): 81 | - (any): 82 | - (all): 83 | - - c-indentation-indicator: m 84 | - - c-chomping-indicator: t 85 | + - c-indentation-indicator: n 86 | + - c-chomping-indicator 87 | + - (===): { (any): [ , s-white, b-break ] } 88 | - (all): 89 | - - c-chomping-indicator: t 90 | - - c-indentation-indicator: m 91 | + - c-chomping-indicator 92 | + - c-indentation-indicator: n 93 | + - (===): { (any): [ , s-white, b-break ] } 94 | - s-b-comment 95 | 96 | 97 | 98 | +#=============================================================================== 99 | +# This production should receive the `n` indentation variable. It creates an 100 | +# `m` state variable that indicates the amount of relative indentation for a 101 | +# literal or folded scalar. 102 | +# 103 | +# It should actually be: 104 | +# 105 | +# c-indentation-indicator(n) ::= 106 | +# ( ns-dec-digit => m = ns-dec-digit - x:30 ) 107 | +# ( => m = auto-detect(n) ) 108 | +# 109 | +# Also change semantic interpretation to look for '1'-'9'. 110 | +#=============================================================================== 111 | + 112 | :163: c-indentation-indicator 113 | # c-indentation-indicator(m) ::= 114 | # ( ns-dec-digit => m = ns-dec-digit - x:30 ) 115 | # ( => m = auto-detect() ) 116 | 117 | c-indentation-indicator: 118 | - (...): m 119 | + (...): n 120 | (any): 121 | - - (if): ns-dec-digit 122 | + - (if): [ 'x31', 'x39' ] 123 | (set): [ m, { (ord): (match) } ] 124 | - (if): 125 | - (set): [ m, "auto-detect" ] 126 | + (set): [ m, { auto-detect: n } ] 127 | 128 | 129 | 130 | +#=============================================================================== 131 | +# This production should not receive any arguments. It creates a variable that 132 | +# indicates how trailing whitespace should be interpreted. 133 | +# 134 | +# It should actually be: 135 | +# 136 | +# c-chomping-indicator ::= 137 | +# ( '-' => t = strip ) 138 | +# ( '+' => t = keep ) 139 | +# ( => t = clip ) 140 | +#=============================================================================== 141 | + 142 | :164: c-chomping-indicator 143 | # c-chomping-indicator(t) ::= 144 | # ( '-' => t = strip ) 145 | @@ -2576,7 +2631,6 @@ 146 | # ( => t = clip ) 147 | 148 | c-chomping-indicator: 149 | - (...): t 150 | (any): 151 | - (if): '-' 152 | (set): [ t, "strip" ] 153 | @@ -2667,6 +2721,16 @@ 154 | ##### 8.1.2. Literal Style 155 | 156 | 157 | +#=============================================================================== 158 | +# This production needs to call c-b-block-header with the variable `n`. 159 | +# 160 | +# It should actually be: 161 | +# 162 | +# c-l+literal(n) ::= 163 | +# '|' c-b-block-header(n) 164 | +# l-literal-content(n+m,t) 165 | +#=============================================================================== 166 | + 167 | :170: c-l+literal 168 | # c-l+literal(n) ::= 169 | # '|' c-b-block-header(m,t) 170 | @@ -2676,7 +2740,7 @@ 171 | (...): n 172 | (all): 173 | - '|' 174 | - - c-b-block-header: [ m, t ] 175 | + - c-b-block-header: n 176 | - l-literal-content: [ { (+): [ n, m ] }, t ] 177 | 178 | 179 | @@ -2730,6 +2794,16 @@ 180 | ##### 8.1.3. Folded Style 181 | 182 | 183 | +#=============================================================================== 184 | +# This production needs to call c-b-block-header with the variable `n`. 185 | +# 186 | +# It should actually be: 187 | +# 188 | +# c-l+folded(n) ::= 189 | +# '>' c-b-block-header(n) 190 | +# l-folded-content(n+m,t) 191 | +#=============================================================================== 192 | + 193 | :174: c-l+folded 194 | # c-l+folded(n) ::= 195 | # '>' c-b-block-header(m,t) 196 | @@ -2739,7 +2813,7 @@ 197 | (...): n 198 | (all): 199 | - '>' 200 | - - c-b-block-header: [ m, t ] 201 | + - c-b-block-header: n 202 | - l-folded-content: [ { (+): [ n, m ] }, t ] 203 | 204 | 205 | @@ -2875,6 +2949,9 @@ 206 | # The rule is a "special" rule that finds the new level of 207 | # indentation and returns the number of columns. The level must be greater than 208 | # 0. It does not consume the indentation. 209 | +# 210 | +# TODO: Rules 183, 185 & 187 have been modified here but should be modified 211 | +# upstream. 212 | #=============================================================================== 213 | 214 | :183: l+block-sequence 215 | @@ -2885,8 +2962,8 @@ 216 | 217 | l+block-sequence: 218 | (...): n 219 | + (m>0): 220 | (all): 221 | - - (set): [ m, ] 222 | - (+++): 223 | (all): 224 | - s-indent: { (+): [ n, m ] } 225 | @@ -2918,6 +2995,7 @@ 226 | 227 | s-l+block-indented: 228 | (...): [ n, c ] 229 | + (->m): 230 | (any): 231 | - (all): 232 | - s-indent: m 233 | @@ -2958,8 +3036,8 @@ 234 | 235 | l+block-mapping: 236 | (...): n 237 | + (m>0): 238 | (all): 239 | - - (set): [ m, ] 240 | - (+++): 241 | (all): 242 | - s-indent: { (+): [ n, m ] } 243 | @@ -2996,6 +3074,10 @@ 244 | 245 | 246 | 247 | +#=============================================================================== 248 | +# This rule was modified to add a whitespace lookahead assertion after '?'. 249 | +#=============================================================================== 250 | + 251 | :190: c-l-block-map-explicit-key 252 | # c-l-block-map-explicit-key(n) ::= 253 | # '?' 254 | @@ -3005,6 +3087,7 @@ 255 | (...): n 256 | (all): 257 | - '?' 258 | + - (===): { (any): [ , s-white, b-break ] } 259 | - s-l+block-indented: [ n, "block-out" ] 260 | 261 | 262 | @@ -3147,6 +3230,27 @@ 263 | 264 | 265 | 266 | +#=============================================================================== 267 | +# This production tries to match collection properties and a collection. It has 268 | +# a problem where it consumes a property that might be for the key of the first 269 | +# pair of a mapping. To make sure the property applies to the collection itself 270 | +# it should check for a newline after the property (in the same scope as the 271 | +# property check). 272 | +# 273 | +# It needs to be: 274 | +# 275 | +# s-l+block-collection(n,c) ::= 276 | +# ( s-separate(n+1,c) 277 | +# ( (c-ns-properties(n+1,c) s-l-comments) | 278 | +# (c-ns-tag-property s-l-comments) | 279 | +# (c-ns-anchor-property s-l-comments) 280 | +# ) 281 | +# )? 282 | +# s-l-comments 283 | +# ( l+block-sequence(seq-spaces(n,c)) 284 | +# | l+block-mapping(n) ) 285 | +#=============================================================================== 286 | + 287 | :200: s-l+block-collection 288 | # s-l+block-collection(n,c) ::= 289 | # ( s-separate(n+1,c) 290 | @@ -3160,8 +3264,17 @@ 291 | (all): 292 | - (???): 293 | (all): 294 | - - s-separate: [ { (+): [ n, 1 ] }, c ] 295 | - - c-ns-properties: [ { (+): [ n, 1 ] }, c ] 296 | + - s-separate: [ { (+): [ n, 1 ] }, c ] 297 | + - (any): 298 | + - (all): 299 | + - c-ns-properties: [ { (+): [ n, 1 ] }, c ] 300 | + - s-l-comments 301 | + - (all): 302 | + - c-ns-tag-property 303 | + - s-l-comments 304 | + - (all): 305 | + - c-ns-anchor-property 306 | + - s-l-comments 307 | - s-l-comments 308 | - (any): 309 | - l+block-sequence: { seq-spaces: [ n, c ] } 310 | @@ -3212,6 +3325,7 @@ 311 | - '-' 312 | - '-' 313 | - '-' 314 | + - (===): { (any): [ , s-white, b-break ] } 315 | 316 | 317 | 318 | @@ -3333,6 +3447,19 @@ 319 | 320 | 321 | 322 | +#=============================================================================== 323 | +# This production has a bug in the rule quantifers for l-document-prefix. It 324 | +# should only be tried once (not multiple times). 325 | +# 326 | +# It should actually be: 327 | +# 328 | +# l-yaml-stream ::= 329 | +# l-document-prefix l-any-document? 330 | +# ( ( l-document-suffix+ l-document-prefix 331 | +# l-any-document? ) 332 | +# | ( l-document-prefix l-explicit-document? ) )* 333 | +#=============================================================================== 334 | + 335 | :211: l-yaml-stream 336 | # l-yaml-stream ::= 337 | # l-document-prefix* l-any-document? 338 | @@ -3342,16 +3469,16 @@ 339 | 340 | l-yaml-stream: 341 | (all): 342 | - - (***): l-document-prefix 343 | + - l-document-prefix 344 | - (???): l-any-document 345 | - (***): 346 | (any): 347 | - (all): 348 | - - (+++): l-document-suffix 349 | + - l-document-suffix 350 | - (***): l-document-prefix 351 | - (???): l-any-document 352 | - (all): 353 | - - (***): l-document-prefix 354 | + - l-document-prefix 355 | - (???): l-explicit-document 356 | 357 | # vim: iskeyword=@,+,- sw=2: 358 | -------------------------------------------------------------------------------- /parser-1.2/coffeescript/Makefile: -------------------------------------------------------------------------------- 1 | ROOT := $(shell cd ../..; pwd) 2 | BASE := $(ROOT)/parser-1.2 3 | PARSER_LANG := coffeescript 4 | BIN := coffee 5 | GRAMMAR := lib/grammar.coffee 6 | 7 | 8 | include $(ROOT)/.common.mk 9 | -------------------------------------------------------------------------------- /parser-1.2/coffeescript/bin/yaml-parser: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | 3 | require '../lib/prelude' 4 | require '../lib/parser' 5 | require '../lib/test-receiver' 6 | 7 | events = false 8 | 9 | main = (yaml=file_read('-'))-> 10 | 11 | parser = new Parser(new TestReceiver) 12 | 13 | pass = true 14 | start = timer() 15 | 16 | try 17 | parser.parse(yaml) 18 | catch e 19 | warn e 20 | pass = false 21 | 22 | time = timer(start) 23 | 24 | if yaml.match /\n./ 25 | n = "\n" 26 | else 27 | n = '' 28 | yaml = yaml.replace /\n$/, '\\n' 29 | 30 | if events 31 | out parser.receiver.output() 32 | return pass 33 | 34 | if pass 35 | say "PASS - '#{n}#{yaml}'" 36 | say parser.receiver.output() 37 | say sprintf "Parse time %.5fs", time 38 | return true 39 | else 40 | say "FAIL - '#{n}#{yaml}'" 41 | say parser.receiver.output() 42 | say sprintf "Parse time %.5fs", time 43 | return false 44 | 45 | argv = process.argv[2..] 46 | 47 | if argv.length and argv[0] == '--events' 48 | events = true 49 | argv.shift() 50 | 51 | if main argv... 52 | exit 0 53 | else 54 | exit 1 55 | 56 | # vim: sw=2: 57 | -------------------------------------------------------------------------------- /parser-1.2/coffeescript/lib/parser.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | This is a parser class. It has a parse() method and parsing primitives for the 3 | grammar. It calls methods in the receiver class, when a rule matches: 4 | ### 5 | 6 | require './prelude' 7 | require './grammar' 8 | 9 | TRACE = Boolean ENV.TRACE 10 | 11 | global.Parser = class Parser extends Grammar 12 | 13 | constructor: (receiver)-> 14 | super() 15 | receiver.parser = @ 16 | @receiver = receiver 17 | @pos = 0 18 | @end = 0 19 | @state = [] 20 | @trace_num = 0 21 | @trace_line = 0 22 | @trace_on = true 23 | @trace_off = 0 24 | @trace_info = ['', '', ''] 25 | 26 | parse: (@input)-> 27 | @input += "\n" unless \ 28 | @input.length == 0 or 29 | @input.endsWith("\n") 30 | 31 | @end = @input.length 32 | 33 | @trace_on = not @trace_start() if TRACE 34 | 35 | try 36 | ok = @call @TOP 37 | @trace_flush() 38 | catch err 39 | @trace_flush() 40 | throw err 41 | 42 | throw "Parser failed" if not ok 43 | throw "Parser finished before end of input" \ 44 | if @pos < @end 45 | 46 | return true 47 | 48 | state_curr: -> 49 | @state[@state.length - 1] || 50 | name: null 51 | doc: false 52 | lvl: 0 53 | beg: 0 54 | end: 0 55 | m: null 56 | t: null 57 | 58 | state_prev: -> 59 | @state[@state.length - 2] 60 | 61 | state_push: (name)-> 62 | curr = @state_curr() 63 | 64 | @state.push 65 | name: name 66 | doc: curr.doc 67 | lvl: curr.lvl + 1 68 | beg: @pos 69 | end: null 70 | m: curr.m 71 | t: curr.t 72 | 73 | state_pop: -> 74 | child = @state.pop() 75 | curr = @state_curr() 76 | return unless curr? 77 | curr.beg = child.beg 78 | curr.end = @pos 79 | 80 | call: (func, type='boolean')-> 81 | args = [] 82 | [func, args...] = func if isArray func 83 | 84 | if isNumber(func) or isString(func) 85 | return func 86 | 87 | FAIL "Bad call type '#{typeof_ func}' for '#{func}'" \ 88 | unless isFunction func 89 | 90 | trace = func.trace ?= func.name 91 | 92 | @state_push(trace) 93 | 94 | @trace_num++ 95 | @trace '?', trace, args if TRACE 96 | 97 | if func.name == 'l_bare_document' 98 | @state_curr().doc = true 99 | 100 | args = args.map (a)=> 101 | if isArray(a) then @call(a, 'any') else \ 102 | if isFunction(a) then a() else \ 103 | a 104 | 105 | pos = @pos 106 | @receive func, 'try', pos 107 | 108 | value = func.apply(@, args) 109 | while isFunction(value) or isArray(value) 110 | value = @call value 111 | 112 | FAIL "Calling '#{trace}' returned '#{typeof_ value}' instead of '#{type}'" \ 113 | if type != 'any' and typeof_(value) != type 114 | 115 | @trace_num++ 116 | if type != 'boolean' 117 | @trace '>', value if TRACE 118 | else 119 | if value 120 | @trace '+', trace if TRACE 121 | @receive func, 'got', pos 122 | else 123 | @trace 'x', trace if TRACE 124 | @receive func, 'not', pos 125 | 126 | @state_pop() 127 | return value 128 | 129 | receive: (func, type, pos)-> 130 | func.receivers ?= @make_receivers() 131 | receiver = func.receivers[type] 132 | return unless receiver 133 | 134 | receiver.call @receiver, 135 | text: @input[pos...@pos] 136 | state: @state_curr() 137 | start: pos 138 | 139 | make_receivers: -> 140 | i = @state.length 141 | names = [] 142 | while i > 0 and not((n = @state[--i].name).match /_/) 143 | if m = n.match /^chr\((.)\)$/ 144 | n = 'x' + hex_char m[1] 145 | else 146 | n = n.replace /\(.*/, '' 147 | names.unshift n 148 | name = [n, names...].join '__' 149 | 150 | return 151 | try: @receiver.constructor.prototype["try__#{name}"] 152 | got: @receiver.constructor.prototype["got__#{name}"] 153 | not: @receiver.constructor.prototype["not__#{name}"] 154 | 155 | 156 | 157 | # Match all subrule methods: 158 | all: (funcs...)-> 159 | all = -> 160 | pos = @pos 161 | for func in funcs 162 | FAIL '*** Missing function in @all group:', funcs \ 163 | unless func? 164 | 165 | if not @call func 166 | @pos = pos 167 | return false 168 | 169 | return true 170 | 171 | # Match any subrule method. Rules are tried in order and stops on first 172 | # match: 173 | any: (funcs...)-> 174 | any = -> 175 | for func in funcs 176 | if @call func 177 | return true 178 | 179 | return false 180 | 181 | may: (func)-> 182 | may = -> 183 | @call func 184 | 185 | # Repeat a rule a certain number of times: 186 | rep: (min, max, func)-> 187 | rep = -> 188 | return false if max? and max < 0 189 | count = 0 190 | pos = @pos 191 | pos_start = pos 192 | while not(max?) or count < max 193 | break unless @call func 194 | break if @pos == pos 195 | count++ 196 | pos = @pos 197 | if count >= min and (not(max?) or count <= max) 198 | return true 199 | @pos = pos_start 200 | return false 201 | name_ 'rep', rep, "rep(#{min},#{max})" 202 | rep2: (min, max, func)-> 203 | rep2 = -> 204 | return false if max? and max < 0 205 | count = 0 206 | pos = @pos 207 | pos_start = pos 208 | while not(max?) or count < max 209 | break unless @call func 210 | break if @pos == pos 211 | count++ 212 | pos = @pos 213 | if count >= min and (not(max?) or count <= max) 214 | return true 215 | @pos = pos_start 216 | return false 217 | name_ 'rep2', rep2, "rep2(#{min},#{max})" 218 | 219 | # Call a rule depending on state value: 220 | case: (var_, map)-> 221 | case_ = -> 222 | rule = map[var_] 223 | rule? or 224 | FAIL "Can't find '#{var_}' in:", map 225 | @call rule 226 | name_ 'case', case_, "case(#{var_},#{stringify map})" 227 | 228 | # Call a rule depending on state value: 229 | flip: (var_, map)-> 230 | value = map[var_] 231 | value? or 232 | FAIL "Can't find '#{var_}' in:", map 233 | return value if isString value 234 | return @call value, 'number' 235 | 236 | the_end: -> 237 | return ( 238 | @pos >= @end or ( 239 | @state_curr().doc and 240 | @start_of_line() and 241 | @input[@pos..].match /^(?:---|\.\.\.)(?=\s|$)/ 242 | ) 243 | ) 244 | 245 | # Match a single char: 246 | chr: (char)-> 247 | chr = -> 248 | return false if @the_end() 249 | if @input[@pos] == char 250 | @pos++ 251 | return true 252 | return false 253 | name_ 'chr', chr, "chr(#{stringify char})" 254 | 255 | # Match a char in a range: 256 | rng: (low, high)-> 257 | rng = -> 258 | return false if @the_end() 259 | if @input[@pos..].match(new RegExp "^[#{low}-#{high}]", 'u') 260 | @pos++ if @input[@pos..].codePointAt(0) > 65535 261 | @pos++ 262 | return true 263 | return false 264 | name_ 'rng', rng, "rng(#{stringify(low)},#{stringify(high)})" 265 | 266 | # Must match first rule but none of others: 267 | but: (funcs...)-> 268 | but = -> 269 | return false if @the_end() 270 | pos1 = @pos 271 | return false unless @call funcs[0] 272 | pos2 = @pos 273 | @pos = pos1 274 | for func in funcs[1..] 275 | if @call func 276 | @pos = pos1 277 | return false 278 | @pos = pos2 279 | return true 280 | 281 | chk: (type, expr)-> 282 | chk = -> 283 | pos = @pos 284 | @pos-- if type == '<=' 285 | ok = @call expr 286 | @pos = pos 287 | return if type == '!' then not(ok) else ok 288 | name_ 'chk', chk, "chk(#{type}, #{stringify expr})" 289 | 290 | set: (var_, expr)-> 291 | set = => 292 | value = @call expr, 'any' 293 | return false if value == -1 294 | value = @auto_detect() if value == 'auto-detect' 295 | state = @state_prev() 296 | state[var_] = value 297 | if state.name != 'all' 298 | size = @state.length 299 | i = 3 300 | while i < size 301 | FAIL "failed to traverse state stack in 'set'" \ 302 | if i > size - 2 303 | state = @state[size - i++ - 1] 304 | state[var_] = value 305 | break if state.name == 's_l_block_scalar' 306 | return true 307 | name_ 'set', set, "set('#{var_}', #{stringify expr})" 308 | 309 | max: (max)-> 310 | max = -> 311 | return true 312 | 313 | exclude: (rule)-> 314 | exclude = -> 315 | return true 316 | 317 | add: (x, y)-> 318 | add = => 319 | y = @call y, 'number' if isFunction y 320 | FAIL "y is '#{stringify y}', not number in 'add'" \ 321 | unless isNumber y 322 | return x + y 323 | name_ 'add', add, "add(#{x},#{stringify y})" 324 | 325 | sub: (x, y)-> 326 | sub = -> 327 | return x - y 328 | 329 | # This method does not need to return a function since it is never 330 | # called in the grammar. 331 | match: -> 332 | state = @state 333 | i = state.length - 1 334 | while i > 0 && not state[i].end? 335 | FAIL "Can't find match" if i == 1 336 | i-- 337 | 338 | {beg, end} = state[i] 339 | return @input[beg...end] 340 | 341 | len: (str)-> 342 | len = -> 343 | str = @call str, 'string' unless isString str 344 | return str.length 345 | 346 | ord: (str)-> 347 | ord = -> 348 | str = @call str, 'string' unless isString str 349 | return str.charCodeAt(0) - 48 350 | 351 | if: (test, do_if_true)-> 352 | if_ = -> 353 | test = @call test, 'boolean' unless isBoolean test 354 | if test 355 | @call do_if_true 356 | return true 357 | return false 358 | name_ 'if', if_ 359 | 360 | lt: (x, y)-> 361 | lt = -> 362 | x = @call x, 'number' unless isNumber x 363 | y = @call y, 'number' unless isNumber y 364 | return x < y 365 | name_ 'lt', lt, "lt(#{stringify x},#{stringify y})" 366 | 367 | le: (x, y)-> 368 | le = -> 369 | x = @call x, 'number' unless isNumber x 370 | y = @call y, 'number' unless isNumber y 371 | return x <= y 372 | name_ 'le', le, "le(#{stringify x},#{stringify y})" 373 | 374 | m: -> 375 | m = => 376 | return @state_curr().m 377 | 378 | t: -> 379 | t = => 380 | return @state_curr().t 381 | 382 | #------------------------------------------------------------------------------ 383 | # Special grammar rules 384 | #------------------------------------------------------------------------------ 385 | start_of_line: -> 386 | return @pos == 0 or 387 | @pos >= @end or 388 | @input[@pos - 1] == "\n" 389 | 390 | end_of_stream: -> 391 | return @pos >= @end 392 | 393 | empty: -> true 394 | 395 | auto_detect_indent: (n)-> 396 | pos = @pos 397 | in_seq = (pos > 0 and @input[pos - 1].match /^[\-\?\:]$/) 398 | match = @input[pos..].match ///^ 399 | ( 400 | (?: 401 | \ * 402 | (?:\#.*)? 403 | \n 404 | )* 405 | ) 406 | (\ *) 407 | /// or FAIL "auto_detect_indent" 408 | pre = match[1] 409 | m = match[2].length 410 | if in_seq and not pre.length 411 | m++ if n == -1 412 | else 413 | m -= n 414 | m = 0 if m < 0 415 | return m 416 | 417 | auto_detect: (n)-> 418 | match = @input[@pos..].match /// 419 | ^.*\n 420 | ( 421 | (?:\ *\n)* 422 | ) 423 | (\ *) 424 | (.?) 425 | /// 426 | pre = match[1] 427 | if match[3].length 428 | m = match[2].length - n 429 | else 430 | m = 0 431 | while pre.match ///\ {#{m}}/// 432 | m++ 433 | m = m - n - 1 434 | die "Spaces found after indent in auto-detect (5LLU)" \ 435 | if m > 0 && pre.match ///^.{#{m + n}}\ ///m 436 | return if m == 0 then 1 else m 437 | 438 | #------------------------------------------------------------------------------ 439 | # Trace debugging 440 | #------------------------------------------------------------------------------ 441 | trace_start: -> 442 | '' || ENV.TRACE_START 443 | 444 | trace_quiet: -> 445 | return [] if ENV.DEBUG 446 | 447 | small = [ 448 | 'b_as_line_feed', 449 | 's_indent', 450 | 'nb_char', 451 | ] 452 | 453 | noisy = [ 454 | 'c_directives_end', 455 | 'c_l_folded', 456 | 'c_l_literal', 457 | 'c_ns_alias_node', 458 | 'c_ns_anchor_property', 459 | 'c_ns_tag_property', 460 | 'l_directive_document', 461 | 'l_document_prefix', 462 | 'ns_flow_content', 463 | 'ns_plain', 464 | 's_l_comments', 465 | 's_separate', 466 | ] 467 | return ((ENV.TRACE_QUIET || '').split ',') 468 | .concat(noisy) 469 | 470 | trace: (type, call, args=[])-> 471 | call = String(call) unless isString call # XXX 472 | call = "'#{call}'" if call.match /^($| |.* $)/ 473 | return unless @trace_on or call == @trace_start() 474 | 475 | level = @state_curr().lvl 476 | indent = _.repeat ' ', level 477 | if level > 0 478 | l = "#{level}".length 479 | indent = "#{level}" + indent[l..] 480 | 481 | input = @input[@pos..] 482 | input = "#{input[0..30]}…" \ 483 | if input.length > 30 484 | input = input \ 485 | .replace(/\t/g, '\\t') 486 | .replace(/\r/g, '\\r') 487 | .replace(/\n/g, '\\n') 488 | 489 | line = sprintf( 490 | "%s%s %-40s %4d '%s'", 491 | indent, 492 | type, 493 | @trace_format_call call, args 494 | @pos, 495 | input, 496 | ) 497 | 498 | if ENV.DEBUG 499 | warn sprintf "%6d %s", 500 | @trace_num, line 501 | return 502 | 503 | trace_info = null 504 | level = "#{level}_#{call}" 505 | if type == '?' and @trace_off == 0 506 | trace_info = [type, level, line, @trace_num] 507 | if call in @trace_quiet() 508 | @trace_off += if type == '?' then 1 else -1 509 | if type != '?' and @trace_off == 0 510 | trace_info = [type, level, line, @trace_num] 511 | 512 | if trace_info? 513 | [prev_type, prev_level, prev_line, trace_num] = 514 | @trace_info 515 | if prev_type == '?' and prev_level == level 516 | trace_info[1] = '' 517 | if line.match /^\d*\ *\+/ 518 | prev_line = prev_line.replace /\?/, '=' 519 | else 520 | prev_line = prev_line.replace /\?/, '!' 521 | if prev_level 522 | warn sprintf "%5d %6d %s", 523 | ++@trace_line, trace_num, prev_line 524 | 525 | @trace_info = trace_info 526 | 527 | if call == @trace_start() 528 | @trace_on = not @trace_on 529 | 530 | trace_format_call: (call, args)-> 531 | return call unless args.length 532 | list = args.map (a)-> 533 | stringify a 534 | list = list.join ',' 535 | return "#{call}(#{list})" 536 | 537 | trace_flush: -> 538 | [type, level, line, count] = @trace_info 539 | if line 540 | warn sprintf "%5d %6d %s", 541 | ++@trace_line, count, line 542 | 543 | # vim: sw=2: 544 | -------------------------------------------------------------------------------- /parser-1.2/coffeescript/lib/prelude.coffee: -------------------------------------------------------------------------------- 1 | require 'ingy-prelude' 2 | 3 | global.ENV = process.env 4 | 5 | global.name_ = (name, func, trace)-> 6 | func.trace = trace || name 7 | if ENV.DEBUGXXX # Not working yet 8 | f = (n, args...)-> 9 | args = args.map (a)-> stringify(a) 10 | args = args.join '' 11 | debug "#{name}(#{args})" 12 | func.apply func 13 | f.name = name 14 | f.trace = trace || name 15 | return f 16 | 17 | func 18 | 19 | global.isNull = _.isNull 20 | global.isBoolean = _.isBoolean 21 | global.isNumber = _.isNumber 22 | global.isString = _.isString 23 | global.isFunction = _.isFunction 24 | global.isArray = _.isArray 25 | global.isObject = _.isPlainObject 26 | 27 | global.typeof_ = (value)-> 28 | return 'null' if _.isNull value 29 | return 'boolean' if _.isBoolean value 30 | return 'number' if _.isNumber value 31 | return 'string' if _.isString value 32 | return 'function' if _.isFunction value 33 | return 'array' if _.isArray value 34 | return 'object' if _.isPlainObject value 35 | xxx [value, typeof(value)] 36 | 37 | global.stringify = (o)-> 38 | if o == "\ufeff" 39 | return "\\uFEFF" 40 | if isFunction o 41 | return "@#{o.trace || o.name}" 42 | if isObject o 43 | return JSON.stringify _.keys(o) 44 | if isArray o 45 | return "[#{(_.map o, (e)-> stringify e).join ','}]" 46 | return JSON.stringify(o).replace /^"(.*)"$/, '$1' 47 | 48 | global.hex_char = (chr)-> 49 | return chr.charCodeAt(0).toString(16) 50 | 51 | global.die_ = (msg)-> 52 | die((new Error().stack) + "\n" + msg) 53 | 54 | global.debug = (msg)-> 55 | warn ">>> #{msg}" 56 | 57 | global.debug_rule = (name, args...)-> 58 | return unless ENV.DEBUG 59 | args = _.join _.map args, (a)-> 60 | stringify(a) 61 | , ',' 62 | debug "#{name}(#{args})" 63 | 64 | global.dump = (o)-> 65 | require('yaml').stringify o 66 | 67 | global.FAIL = (o...)-> 68 | WWW o 69 | die "FAIL '#{o[0] || '???'}'" 70 | 71 | global.timer = (start=null)-> 72 | if start? 73 | time = process.hrtime(start) 74 | time[0] + time[1] / 1000000000 75 | else 76 | process.hrtime() 77 | 78 | # vim: sw=2: 79 | -------------------------------------------------------------------------------- /parser-1.2/coffeescript/lib/receiver.coffee: -------------------------------------------------------------------------------- 1 | require './prelude' 2 | 3 | stream_start_event = -> 4 | event: 'stream_start' 5 | stream_end_event = -> 6 | event: 'stream_end' 7 | document_start_event = (explicit=false)-> 8 | event: 'document_start' 9 | explicit: explicit 10 | version: null 11 | document_end_event = (explicit=false)-> 12 | event: 'document_end' 13 | explicit: explicit 14 | mapping_start_event = (flow=false)-> 15 | event: 'mapping_start' 16 | flow: flow 17 | mapping_end_event = -> 18 | event: 'mapping_end' 19 | sequence_start_event = (flow=false)-> 20 | event: 'sequence_start' 21 | flow: flow 22 | sequence_end_event = -> 23 | event: 'sequence_end' 24 | scalar_event = (style, value)-> 25 | event: 'scalar' 26 | style: style 27 | value: value 28 | alias_event = (name)-> 29 | event: 'alias' 30 | name: name 31 | cache = (text)-> 32 | text: text 33 | 34 | global.Receiver = class Receiver 35 | constructor: -> 36 | @event = [] 37 | @cache = [] 38 | 39 | send: (event)-> 40 | if @callback 41 | @callback event 42 | else 43 | @event.push event 44 | 45 | add: (event)-> 46 | if event.event? 47 | if @anchor? 48 | event.anchor = @anchor 49 | delete @anchor 50 | if @tag? 51 | event.tag = @tag 52 | delete @tag 53 | @push event 54 | return event 55 | 56 | 57 | push: (event)-> 58 | if @cache.length 59 | _.last(@cache).push event 60 | else 61 | if event.event.match /(mapping_start|sequence_start|scalar)/ 62 | @check_document_start() 63 | @send event 64 | 65 | cache_up: (event=null)-> 66 | @cache.push [] 67 | @add event if event? 68 | 69 | cache_down: (event=null)-> 70 | events = @cache.pop() or FAIL 'cache_down' 71 | @push e for e in events 72 | @add event if event? 73 | 74 | cache_drop: -> 75 | events = @cache.pop() or FAIL 'cache_drop' 76 | return events 77 | 78 | cache_get: (type)-> 79 | last = _.last @cache 80 | return \ 81 | last && 82 | last[0] && 83 | last[0].event == type && 84 | last[0] 85 | 86 | check_document_start: -> 87 | return unless @document_start 88 | @send @document_start 89 | delete @document_start 90 | @document_end = document_end_event() 91 | 92 | check_document_end: -> 93 | return unless @document_end 94 | @send @document_end 95 | delete @document_end 96 | @tag_map = {} 97 | @document_start = document_start_event() 98 | 99 | #---------------------------------------------------------------------------- 100 | try__l_yaml_stream: -> 101 | @add stream_start_event() 102 | @tag_map = {} 103 | @document_start = document_start_event() 104 | delete @document_end 105 | got__l_yaml_stream: -> 106 | @check_document_end() 107 | @add stream_end_event() 108 | 109 | got__ns_yaml_version: (o)-> 110 | die "Multiple %YAML directives not allowed" \ 111 | if @document_start.version? 112 | @document_start.version = o.text 113 | 114 | got__c_tag_handle: (o)-> 115 | @tag_handle = o.text 116 | got__ns_tag_prefix: (o)-> 117 | @tag_map[@tag_handle] = o.text 118 | 119 | got__c_directives_end: -> 120 | @check_document_end() 121 | @document_start.explicit = true 122 | got__c_document_end: -> 123 | if @document_end? 124 | @document_end.explicit = true 125 | @check_document_end() 126 | 127 | got__c_flow_mapping__all__x7b: -> @add mapping_start_event true 128 | got__c_flow_mapping__all__x7d: -> @add mapping_end_event() 129 | 130 | got__c_flow_sequence__all__x5b: -> @add sequence_start_event true 131 | got__c_flow_sequence__all__x5d: -> @add sequence_end_event() 132 | 133 | try__l_block_mapping: -> @cache_up mapping_start_event() 134 | got__l_block_mapping: -> @cache_down mapping_end_event() 135 | not__l_block_mapping: -> @cache_drop() 136 | 137 | try__l_block_sequence: -> @cache_up sequence_start_event() 138 | got__l_block_sequence: -> @cache_down sequence_end_event() 139 | not__l_block_sequence: -> 140 | event = @cache_drop()[0] 141 | @anchor = event.anchor 142 | @tag = event.tag 143 | 144 | try__ns_l_compact_mapping: -> @cache_up mapping_start_event() 145 | got__ns_l_compact_mapping: -> @cache_down mapping_end_event() 146 | not__ns_l_compact_mapping: -> @cache_drop() 147 | 148 | try__ns_l_compact_sequence: -> @cache_up sequence_start_event() 149 | got__ns_l_compact_sequence: -> @cache_down sequence_end_event() 150 | not__ns_l_compact_sequence: -> @cache_drop() 151 | 152 | try__ns_flow_pair: -> @cache_up mapping_start_event true 153 | got__ns_flow_pair: -> @cache_down mapping_end_event() 154 | not__ns_flow_pair: -> @cache_drop() 155 | 156 | try__ns_l_block_map_implicit_entry: -> @cache_up() 157 | got__ns_l_block_map_implicit_entry: -> @cache_down() 158 | not__ns_l_block_map_implicit_entry: -> @cache_drop() 159 | 160 | try__c_l_block_map_explicit_entry: -> @cache_up() 161 | got__c_l_block_map_explicit_entry: -> @cache_down() 162 | not__c_l_block_map_explicit_entry: -> @cache_drop() 163 | 164 | try__c_ns_flow_map_empty_key_entry: -> @cache_up() 165 | got__c_ns_flow_map_empty_key_entry: -> @cache_down() 166 | not__c_ns_flow_map_empty_key_entry: -> @cache_drop() 167 | 168 | got__ns_plain: (o)-> 169 | text = o.text 170 | .replace(/(?:[\ \t]*\r?\n[\ \t]*)/g, "\n") 171 | .replace(/(\n)(\n*)/g, (m...)-> if m[2].length then m[2] else ' ') 172 | @add scalar_event 'plain', text 173 | 174 | got__c_single_quoted: (o)-> 175 | text = o.text[1...-1] 176 | .replace(/(?:[\ \t]*\r?\n[\ \t]*)/g, "\n") 177 | .replace(/(\n)(\n*)/g, (m...)-> if m[2].length then m[2] else ' ') 178 | .replace(/''/g, "'") 179 | @add scalar_event 'single', text 180 | 181 | unescapes = 182 | '\\\\': '\\' 183 | '\r\n': '\n' 184 | '\\ ': ' ' 185 | '\\"': '"' 186 | '\\/': '/' 187 | '\\_': '\u{a0}' 188 | '\\0': '\x00' 189 | '\\a': '\x07' 190 | '\\b': '\x08' 191 | '\\e': '\x1b' 192 | '\\f': '\x0c' 193 | '\\n': '\x0a' 194 | '\\r': '\x0d' 195 | '\\t': '\x09' 196 | '\\\t': '\x09' 197 | '\\v': '\x0b' 198 | '\\L': '\u{2028}' 199 | '\\N': '\u{85}' 200 | '\\P': '\u{2029}' 201 | 202 | end1 = String(/// (?: \\ \r?\n[\ \t]* ) ///)[1..-2] 203 | end2 = String(/// (?: [\ \t]*\r?\n[\ \t]* ) ///)[1..-2] 204 | hex = '[0-9a-fA-F]' 205 | hex2 = String(/// (?: \\x ( #{hex}{2} ) ) ///)[1..-2] 206 | hex4 = String(/// (?: \\u ( #{hex}{4} ) ) ///)[1..-2] 207 | hex8 = String(/// (?: \\U ( #{hex}{8} ) ) ///)[1..-2] 208 | 209 | got__c_double_quoted: (o)-> 210 | @add scalar_event('double', o.text[1...-1] 211 | .replace /// 212 | (?: 213 | \r\n 214 | | #{end1} 215 | | #{end2}+ 216 | | #{hex2} 217 | | #{hex4} 218 | | #{hex8} 219 | | \\[\\\ "/_0abefnrt\tvLNP] 220 | ) 221 | ///g, (m)-> 222 | if n = m.match ///^ #{hex2} $/// 223 | return String.fromCharCode(parseInt(n[1], 16)) 224 | if n = m.match ///^ #{hex4} $/// 225 | return String.fromCharCode(parseInt(n[1], 16)) 226 | if n = m.match ///^ #{hex8} $/// 227 | return String.fromCharCode(parseInt(n[1], 16)) 228 | if m.match ///^ #{end1} $/// 229 | return '' 230 | if m.match ///^ #{end2}+ $/// 231 | u = m 232 | .replace(/// #{end2} ///, '') 233 | .replace(/// #{end2} ///g, '\n') 234 | return u || ' ' 235 | if u = unescapes[m] 236 | return u 237 | XXX m 238 | ) 239 | 240 | got__l_empty: -> 241 | @add cache('') if @in_scalar 242 | got__l_nb_literal_text__all__rep2: (o)-> 243 | @add cache(o.text) 244 | try__c_l_literal: -> 245 | @in_scalar = true 246 | @cache_up() 247 | got__c_l_literal: -> 248 | delete @in_scalar 249 | lines = @cache_drop() 250 | lines.pop() if lines.length > 0 and lines[lines.length - 1].text == '' 251 | lines = lines.map (l)-> "#{l.text}\n" 252 | text = lines.join '' 253 | t = @parser.state_curr().t 254 | if t == 'clip' 255 | text = text.replace /\n+$/, "\n" 256 | else if t == 'strip' 257 | text = text.replace /\n+$/, "" 258 | else if not text.match /\S/ 259 | text = text.replace /\n(\n+)$/, "$1" 260 | @add scalar_event 'literal', text 261 | not__c_l_literal: -> 262 | delete @in_scalar 263 | @cache_drop() 264 | 265 | got__ns_char: (o)-> 266 | @first = o.text if @in_scalar 267 | got__s_white: (o)-> 268 | @first = o.text if @in_scalar 269 | got__s_nb_folded_text__all__rep: (o)-> 270 | @add cache "#{@first}#{o.text}" 271 | got__s_nb_spaced_text__all__rep: (o)-> 272 | @add cache "#{@first}#{o.text}" 273 | try__c_l_folded: -> 274 | @in_scalar = true 275 | @first = '' 276 | @cache_up() 277 | got__c_l_folded: -> 278 | delete @in_scalar 279 | lines = @cache_drop().map (l)-> l.text 280 | text = lines.join "\n" 281 | text = text.replace /^(\S.*)\n(?=\S)/gm, "$1 " 282 | text = text.replace /^(\S.*)\n(\n+)/gm, "$1$2" 283 | text = text.replace /^([\ \t]+\S.*)\n(\n+)(?=\S)/gm, "$1$2" 284 | text += "\n" 285 | 286 | t = @parser.state_curr().t 287 | if t == 'clip' 288 | text = text.replace /\n+$/, "\n" 289 | text = '' if text == "\n" 290 | else if t == 'strip' 291 | text = text.replace /\n+$/, "" 292 | @add scalar_event 'folded', text 293 | not__c_l_folded: -> 294 | delete @in_scalar 295 | @cache_drop() 296 | 297 | got__e_scalar: -> @add scalar_event 'plain', '' 298 | 299 | not__s_l_block_collection__all__rep__all__any__all: -> 300 | delete @tag 301 | delete @anchor 302 | 303 | got__c_ns_anchor_property: (o)-> 304 | @anchor = o.text[1..] 305 | 306 | got__c_ns_tag_property: (o)-> 307 | tag = o.text 308 | if m = tag.match /^!<(.*)>$/ 309 | @tag = m[1] 310 | else if m = tag.match /^!!(.*)/ 311 | prefix = @tag_map['!!'] 312 | if prefix? 313 | @tag = prefix + tag[2..] 314 | else 315 | @tag = "tag:yaml.org,2002:#{m[1]}" 316 | else if m = tag.match(/^(!.*?!)/) 317 | prefix = @tag_map[m[1]] 318 | if prefix? 319 | @tag = prefix + tag[(m[1].length)..] 320 | else 321 | die "No %TAG entry for '#{prefix}'" 322 | else if (prefix = @tag_map['!'])? 323 | @tag = prefix + tag[1..] 324 | else 325 | @tag = tag 326 | @tag = @tag.replace /%([0-9a-fA-F]{2})/g, (m...)-> 327 | String.fromCharCode parseInt m[1], 16 328 | 329 | got__c_ns_alias_node: (o)-> @add alias_event o.text[1..] 330 | 331 | # vim: sw=2: 332 | -------------------------------------------------------------------------------- /parser-1.2/coffeescript/lib/test-receiver.coffee: -------------------------------------------------------------------------------- 1 | require './prelude' 2 | require './receiver' 3 | 4 | event_map = 5 | stream_start: '+STR' 6 | stream_end: '-STR' 7 | document_start: '+DOC' 8 | document_end: '-DOC' 9 | mapping_start: '+MAP' 10 | mapping_end: '-MAP' 11 | sequence_start: '+SEQ' 12 | sequence_end: '-SEQ' 13 | scalar: '=VAL' 14 | alias: '=ALI' 15 | 16 | style_map = 17 | plain: ':' 18 | single: "'" 19 | double: '"' 20 | literal: '|' 21 | folded: '>' 22 | 23 | global.TestReceiver = class TestReceiver extends Receiver 24 | 25 | output: -> 26 | list = @event.map (e)-> 27 | type = event_map[e.event] 28 | event = [type] 29 | event.push '---' if type == '+DOC' and e.explicit 30 | event.push '...' if type == '-DOC' and e.explicit 31 | event.push '{}' if type == '+MAP' and e.flow 32 | event.push '[]' if type == '+SEQ' and e.flow 33 | event.push "&#{e.anchor}" if e.anchor 34 | event.push "<#{e.tag}>" if e.tag 35 | event.push "*#{e.name}" if e.name 36 | if e.value? 37 | style = style_map[e.style] 38 | value = e.value 39 | .replace(/\\/g, '\\\\') 40 | .replace(/\x00/g, '\\0') 41 | .replace(/\x07/g, '\\a') 42 | .replace(/\x08/g, '\\b') 43 | .replace(/\x09/g, '\\t') 44 | .replace(/\x0a/g, '\\n') 45 | .replace(/\x0b/g, '\\v') 46 | .replace(/\x0c/g, '\\f') 47 | .replace(/\x0d/g, '\\r') 48 | .replace(/\x1b/g, '\\e') 49 | .replace(/\u{85}/g, '\\N') 50 | .replace(/\u{a0}/g, '\\_') 51 | .replace(/\u{2028}/g, '\\L') 52 | .replace(/\u{2029}/g, '\\P') 53 | event.push "#{style}#{value}" 54 | event.join(' ') + "\n" 55 | list.join '' 56 | 57 | # vim: sw=2: 58 | -------------------------------------------------------------------------------- /parser-1.2/coffeescript/test/error-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/error-suite.tml -------------------------------------------------------------------------------- /parser-1.2/coffeescript/test/parse-block.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-block.tml -------------------------------------------------------------------------------- /parser-1.2/coffeescript/test/parse-flow.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-flow.tml -------------------------------------------------------------------------------- /parser-1.2/coffeescript/test/parse-props.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-props.tml -------------------------------------------------------------------------------- /parser-1.2/coffeescript/test/parse-scalar.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-scalar.tml -------------------------------------------------------------------------------- /parser-1.2/coffeescript/test/parse-stream.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-stream.tml -------------------------------------------------------------------------------- /parser-1.2/coffeescript/test/parse-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-suite.tml -------------------------------------------------------------------------------- /parser-1.2/coffeescript/test/testml-bridge.coffee: -------------------------------------------------------------------------------- 1 | require '../../../test/testml/src/coffee/lib/testml/bridge' 2 | require '../lib/prelude' 3 | require '../lib/parser' 4 | require '../lib/grammar' 5 | require '../lib/test-receiver' 6 | 7 | module.exports = 8 | class TestMLBridge extends TestML.Bridge 9 | 10 | parse: (yaml, expect_error=null)-> 11 | parser = new Parser(new TestReceiver) 12 | 13 | error = '' 14 | try 15 | parser.parse yaml 16 | catch e 17 | error = String e 18 | 19 | if expect_error? 20 | return if error then 1 else 0 21 | 22 | if error 23 | error 24 | else 25 | parser.receiver.output() 26 | 27 | unescape: (text)-> 28 | text = text 29 | .replace(/␣/g, ' ') 30 | .replace(/—*»/g, "\t") 31 | .replace(/⇔/g, "\uFEFF") 32 | .replace(/↵/g, '') 33 | .replace(/∎\n$/, '') 34 | 35 | # text = text.replace(/↓/g, "\r") 36 | 37 | return text 38 | 39 | fix_test_output: (text)-> 40 | return text 41 | -------------------------------------------------------------------------------- /parser-1.2/javascript/Makefile: -------------------------------------------------------------------------------- 1 | ROOT := $(shell cd ../..; pwd) 2 | BASE := $(ROOT)/parser-1.2 3 | PARSER_LANG := javascript 4 | BIN := node 5 | GRAMMAR := 6 | 7 | ALL_JS := \ 8 | bin/yaml-parser \ 9 | lib/grammar.js \ 10 | lib/parser.js \ 11 | lib/prelude.js \ 12 | lib/receiver.js \ 13 | lib/test-receiver.js \ 14 | test/testml-bridge.js \ 15 | 16 | BUILD_DEPS := \ 17 | $(ROOT)/node_modules \ 18 | build-coffee \ 19 | $(ALL_JS) \ 20 | $(ROOT)/test/testml/src/node/lib \ 21 | 22 | 23 | include $(ROOT)/.common.mk 24 | 25 | 26 | build-coffee: 27 | $(MAKE) -C $(BASE)/coffeescript build 28 | 29 | bin/%: $(BASE)/coffeescript/bin/% 30 | echo '#!/usr/bin/env node' > $@ 31 | coffee -cp $< >> $@ 32 | chmod +x $@ 33 | 34 | lib/%.js: $(BASE)/coffeescript/lib/%.coffee 35 | coffee -cp $< > $@ 36 | 37 | test/%.js: $(BASE)/coffeescript/test/%.coffee 38 | coffee -cp $< > $@ 39 | perl -pi -e 's{/coffee/}{/node/}' $@ 40 | 41 | $(ROOT)/test/testml/src/node/lib: $(ROOT)/test/testml 42 | $(MAKE) -C $(@:%/lib=%) js-files 43 | -------------------------------------------------------------------------------- /parser-1.2/javascript/bin/yaml-parser: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // Generated by CoffeeScript 2.5.1 3 | (function() { 4 | //!/usr/bin/env coffee 5 | var argv, events, main; 6 | 7 | require('../lib/prelude'); 8 | 9 | require('../lib/parser'); 10 | 11 | require('../lib/test-receiver'); 12 | 13 | events = false; 14 | 15 | main = function(yaml = file_read('-')) { 16 | var e, n, parser, pass, start, time; 17 | parser = new Parser(new TestReceiver()); 18 | pass = true; 19 | start = timer(); 20 | try { 21 | parser.parse(yaml); 22 | } catch (error) { 23 | e = error; 24 | warn(e); 25 | pass = false; 26 | } 27 | time = timer(start); 28 | if (yaml.match(/\n./)) { 29 | n = "\n"; 30 | } else { 31 | n = ''; 32 | yaml = yaml.replace(/\n$/, '\\n'); 33 | } 34 | if (events) { 35 | out(parser.receiver.output()); 36 | return pass; 37 | } 38 | if (pass) { 39 | say(`PASS - '${n}${yaml}'`); 40 | say(parser.receiver.output()); 41 | say(sprintf("Parse time %.5fs", time)); 42 | return true; 43 | } else { 44 | say(`FAIL - '${n}${yaml}'`); 45 | say(parser.receiver.output()); 46 | say(sprintf("Parse time %.5fs", time)); 47 | return false; 48 | } 49 | }; 50 | 51 | argv = process.argv.slice(2); 52 | 53 | if (argv.length && argv[0] === '--events') { 54 | events = true; 55 | argv.shift(); 56 | } 57 | 58 | if (main(...argv)) { 59 | exit(0); 60 | } else { 61 | exit(1); 62 | } 63 | 64 | // vim: sw=2: 65 | 66 | }).call(this); 67 | -------------------------------------------------------------------------------- /parser-1.2/javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 32 | 33 | -------------------------------------------------------------------------------- /parser-1.2/javascript/lib/prelude.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.5.1 2 | (function() { 3 | require('ingy-prelude'); 4 | 5 | global.ENV = process.env; 6 | 7 | global.name_ = function(name, func, trace) { 8 | var f; 9 | func.trace = trace || name; 10 | if (ENV.DEBUGXXX) { // Not working yet 11 | f = function(n, ...args) { 12 | args = args.map(function(a) { 13 | return stringify(a); 14 | }); 15 | args = args.join(''); 16 | debug(`${name}(${args})`); 17 | return func.apply(func); 18 | }; 19 | f.name = name; 20 | f.trace = trace || name; 21 | return f; 22 | } 23 | return func; 24 | }; 25 | 26 | global.isNull = _.isNull; 27 | 28 | global.isBoolean = _.isBoolean; 29 | 30 | global.isNumber = _.isNumber; 31 | 32 | global.isString = _.isString; 33 | 34 | global.isFunction = _.isFunction; 35 | 36 | global.isArray = _.isArray; 37 | 38 | global.isObject = _.isPlainObject; 39 | 40 | global.typeof_ = function(value) { 41 | if (_.isNull(value)) { 42 | return 'null'; 43 | } 44 | if (_.isBoolean(value)) { 45 | return 'boolean'; 46 | } 47 | if (_.isNumber(value)) { 48 | return 'number'; 49 | } 50 | if (_.isString(value)) { 51 | return 'string'; 52 | } 53 | if (_.isFunction(value)) { 54 | return 'function'; 55 | } 56 | if (_.isArray(value)) { 57 | return 'array'; 58 | } 59 | if (_.isPlainObject(value)) { 60 | return 'object'; 61 | } 62 | return xxx([value, typeof value]); 63 | }; 64 | 65 | global.stringify = function(o) { 66 | if (o === "\ufeff") { 67 | return "\\uFEFF"; 68 | } 69 | if (isFunction(o)) { 70 | return `@${o.trace || o.name}`; 71 | } 72 | if (isObject(o)) { 73 | return JSON.stringify(_.keys(o)); 74 | } 75 | if (isArray(o)) { 76 | return `[${(_.map(o, function(e) { 77 | return stringify(e); 78 | })).join(',')}]`; 79 | } 80 | return JSON.stringify(o).replace(/^"(.*)"$/, '$1'); 81 | }; 82 | 83 | global.hex_char = function(chr) { 84 | return chr.charCodeAt(0).toString(16); 85 | }; 86 | 87 | global.die_ = function(msg) { 88 | return die((new Error().stack) + "\n" + msg); 89 | }; 90 | 91 | global.debug = function(msg) { 92 | return warn(`>>> ${msg}`); 93 | }; 94 | 95 | global.debug_rule = function(name, ...args) { 96 | if (!ENV.DEBUG) { 97 | return; 98 | } 99 | args = _.join(_.map(args, function(a) { 100 | return stringify(a); 101 | }, ',')); 102 | return debug(`${name}(${args})`); 103 | }; 104 | 105 | global.dump = function(o) { 106 | return require('yaml').stringify(o); 107 | }; 108 | 109 | global.FAIL = function(...o) { 110 | WWW(o); 111 | return die(`FAIL '${o[0] || '???'}'`); 112 | }; 113 | 114 | global.timer = function(start = null) { 115 | var time; 116 | if (start != null) { 117 | time = process.hrtime(start); 118 | return time[0] + time[1] / 1000000000; 119 | } else { 120 | return process.hrtime(); 121 | } 122 | }; 123 | 124 | // vim: sw=2: 125 | 126 | }).call(this); 127 | -------------------------------------------------------------------------------- /parser-1.2/javascript/lib/receiver.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.5.1 2 | (function() { 3 | var Receiver, alias_event, cache, document_end_event, document_start_event, mapping_end_event, mapping_start_event, scalar_event, sequence_end_event, sequence_start_event, stream_end_event, stream_start_event; 4 | 5 | require('./prelude'); 6 | 7 | stream_start_event = function() { 8 | return { 9 | event: 'stream_start' 10 | }; 11 | }; 12 | 13 | stream_end_event = function() { 14 | return { 15 | event: 'stream_end' 16 | }; 17 | }; 18 | 19 | document_start_event = function(explicit = false) { 20 | return { 21 | event: 'document_start', 22 | explicit: explicit, 23 | version: null 24 | }; 25 | }; 26 | 27 | document_end_event = function(explicit = false) { 28 | return { 29 | event: 'document_end', 30 | explicit: explicit 31 | }; 32 | }; 33 | 34 | mapping_start_event = function(flow = false) { 35 | return { 36 | event: 'mapping_start', 37 | flow: flow 38 | }; 39 | }; 40 | 41 | mapping_end_event = function() { 42 | return { 43 | event: 'mapping_end' 44 | }; 45 | }; 46 | 47 | sequence_start_event = function(flow = false) { 48 | return { 49 | event: 'sequence_start', 50 | flow: flow 51 | }; 52 | }; 53 | 54 | sequence_end_event = function() { 55 | return { 56 | event: 'sequence_end' 57 | }; 58 | }; 59 | 60 | scalar_event = function(style, value) { 61 | return { 62 | event: 'scalar', 63 | style: style, 64 | value: value 65 | }; 66 | }; 67 | 68 | alias_event = function(name) { 69 | return { 70 | event: 'alias', 71 | name: name 72 | }; 73 | }; 74 | 75 | cache = function(text) { 76 | return { 77 | text: text 78 | }; 79 | }; 80 | 81 | global.Receiver = Receiver = (function() { 82 | var end1, end2, hex, hex2, hex4, hex8, unescapes; 83 | 84 | class Receiver { 85 | constructor() { 86 | this.event = []; 87 | this.cache = []; 88 | } 89 | 90 | send(event) { 91 | if (this.callback) { 92 | return this.callback(event); 93 | } else { 94 | return this.event.push(event); 95 | } 96 | } 97 | 98 | add(event) { 99 | if (event.event != null) { 100 | if (this.anchor != null) { 101 | event.anchor = this.anchor; 102 | delete this.anchor; 103 | } 104 | if (this.tag != null) { 105 | event.tag = this.tag; 106 | delete this.tag; 107 | } 108 | } 109 | this.push(event); 110 | return event; 111 | } 112 | 113 | push(event) { 114 | if (this.cache.length) { 115 | return _.last(this.cache).push(event); 116 | } else { 117 | if (event.event.match(/(mapping_start|sequence_start|scalar)/)) { 118 | this.check_document_start(); 119 | } 120 | return this.send(event); 121 | } 122 | } 123 | 124 | cache_up(event = null) { 125 | this.cache.push([]); 126 | if (event != null) { 127 | return this.add(event); 128 | } 129 | } 130 | 131 | cache_down(event = null) { 132 | var e, events, i, len; 133 | events = this.cache.pop() || FAIL('cache_down'); 134 | for (i = 0, len = events.length; i < len; i++) { 135 | e = events[i]; 136 | this.push(e); 137 | } 138 | if (event != null) { 139 | return this.add(event); 140 | } 141 | } 142 | 143 | cache_drop() { 144 | var events; 145 | events = this.cache.pop() || FAIL('cache_drop'); 146 | return events; 147 | } 148 | 149 | cache_get(type) { 150 | var last; 151 | last = _.last(this.cache); 152 | return last && last[0] && last[0].event === type && last[0]; 153 | } 154 | 155 | check_document_start() { 156 | if (!this.document_start) { 157 | return; 158 | } 159 | this.send(this.document_start); 160 | delete this.document_start; 161 | return this.document_end = document_end_event(); 162 | } 163 | 164 | check_document_end() { 165 | if (!this.document_end) { 166 | return; 167 | } 168 | this.send(this.document_end); 169 | delete this.document_end; 170 | this.tag_map = {}; 171 | return this.document_start = document_start_event(); 172 | } 173 | 174 | //---------------------------------------------------------------------------- 175 | try__l_yaml_stream() { 176 | this.add(stream_start_event()); 177 | this.tag_map = {}; 178 | this.document_start = document_start_event(); 179 | return delete this.document_end; 180 | } 181 | 182 | got__l_yaml_stream() { 183 | this.check_document_end(); 184 | return this.add(stream_end_event()); 185 | } 186 | 187 | got__ns_yaml_version(o) { 188 | if (this.document_start.version != null) { 189 | die("Multiple %YAML directives not allowed"); 190 | } 191 | return this.document_start.version = o.text; 192 | } 193 | 194 | got__c_tag_handle(o) { 195 | return this.tag_handle = o.text; 196 | } 197 | 198 | got__ns_tag_prefix(o) { 199 | return this.tag_map[this.tag_handle] = o.text; 200 | } 201 | 202 | got__c_directives_end() { 203 | this.check_document_end(); 204 | return this.document_start.explicit = true; 205 | } 206 | 207 | got__c_document_end() { 208 | if (this.document_end != null) { 209 | this.document_end.explicit = true; 210 | } 211 | return this.check_document_end(); 212 | } 213 | 214 | got__c_flow_mapping__all__x7b() { 215 | return this.add(mapping_start_event(true)); 216 | } 217 | 218 | got__c_flow_mapping__all__x7d() { 219 | return this.add(mapping_end_event()); 220 | } 221 | 222 | got__c_flow_sequence__all__x5b() { 223 | return this.add(sequence_start_event(true)); 224 | } 225 | 226 | got__c_flow_sequence__all__x5d() { 227 | return this.add(sequence_end_event()); 228 | } 229 | 230 | try__l_block_mapping() { 231 | return this.cache_up(mapping_start_event()); 232 | } 233 | 234 | got__l_block_mapping() { 235 | return this.cache_down(mapping_end_event()); 236 | } 237 | 238 | not__l_block_mapping() { 239 | return this.cache_drop(); 240 | } 241 | 242 | try__l_block_sequence() { 243 | return this.cache_up(sequence_start_event()); 244 | } 245 | 246 | got__l_block_sequence() { 247 | return this.cache_down(sequence_end_event()); 248 | } 249 | 250 | not__l_block_sequence() { 251 | var event; 252 | event = this.cache_drop()[0]; 253 | this.anchor = event.anchor; 254 | return this.tag = event.tag; 255 | } 256 | 257 | try__ns_l_compact_mapping() { 258 | return this.cache_up(mapping_start_event()); 259 | } 260 | 261 | got__ns_l_compact_mapping() { 262 | return this.cache_down(mapping_end_event()); 263 | } 264 | 265 | not__ns_l_compact_mapping() { 266 | return this.cache_drop(); 267 | } 268 | 269 | try__ns_l_compact_sequence() { 270 | return this.cache_up(sequence_start_event()); 271 | } 272 | 273 | got__ns_l_compact_sequence() { 274 | return this.cache_down(sequence_end_event()); 275 | } 276 | 277 | not__ns_l_compact_sequence() { 278 | return this.cache_drop(); 279 | } 280 | 281 | try__ns_flow_pair() { 282 | return this.cache_up(mapping_start_event(true)); 283 | } 284 | 285 | got__ns_flow_pair() { 286 | return this.cache_down(mapping_end_event()); 287 | } 288 | 289 | not__ns_flow_pair() { 290 | return this.cache_drop(); 291 | } 292 | 293 | try__ns_l_block_map_implicit_entry() { 294 | return this.cache_up(); 295 | } 296 | 297 | got__ns_l_block_map_implicit_entry() { 298 | return this.cache_down(); 299 | } 300 | 301 | not__ns_l_block_map_implicit_entry() { 302 | return this.cache_drop(); 303 | } 304 | 305 | try__c_l_block_map_explicit_entry() { 306 | return this.cache_up(); 307 | } 308 | 309 | got__c_l_block_map_explicit_entry() { 310 | return this.cache_down(); 311 | } 312 | 313 | not__c_l_block_map_explicit_entry() { 314 | return this.cache_drop(); 315 | } 316 | 317 | try__c_ns_flow_map_empty_key_entry() { 318 | return this.cache_up(); 319 | } 320 | 321 | got__c_ns_flow_map_empty_key_entry() { 322 | return this.cache_down(); 323 | } 324 | 325 | not__c_ns_flow_map_empty_key_entry() { 326 | return this.cache_drop(); 327 | } 328 | 329 | got__ns_plain(o) { 330 | var text; 331 | text = o.text.replace(/(?:[\ \t]*\r?\n[\ \t]*)/g, "\n").replace(/(\n)(\n*)/g, function(...m) { 332 | if (m[2].length) { 333 | return m[2]; 334 | } else { 335 | return ' '; 336 | } 337 | }); 338 | return this.add(scalar_event('plain', text)); 339 | } 340 | 341 | got__c_single_quoted(o) { 342 | var text; 343 | text = o.text.slice(1, -1).replace(/(?:[\ \t]*\r?\n[\ \t]*)/g, "\n").replace(/(\n)(\n*)/g, function(...m) { 344 | if (m[2].length) { 345 | return m[2]; 346 | } else { 347 | return ' '; 348 | } 349 | }).replace(/''/g, "'"); 350 | return this.add(scalar_event('single', text)); 351 | } 352 | 353 | got__c_double_quoted(o) { 354 | return this.add(scalar_event('double', o.text.slice(1, -1).replace(RegExp(`(?:\\r\\n|${end1}|${end2}+|${hex2}|${hex4}|${hex8}|\\\\[\\\\ "/_0abefnrt\\tvLNP])`, "g"), function(m) { 355 | var n, u; 356 | if (n = m.match(RegExp(`^${hex2}$`))) { 357 | return String.fromCharCode(parseInt(n[1], 16)); 358 | } 359 | if (n = m.match(RegExp(`^${hex4}$`))) { 360 | return String.fromCharCode(parseInt(n[1], 16)); 361 | } 362 | if (n = m.match(RegExp(`^${hex8}$`))) { 363 | return String.fromCharCode(parseInt(n[1], 16)); 364 | } 365 | if (m.match(RegExp(`^${end1}$`))) { 366 | return ''; 367 | } 368 | if (m.match(RegExp(`^${end2}+$`))) { 369 | u = m.replace(RegExp(`${end2}`), '').replace(RegExp(`${end2}`, "g"), '\n'); 370 | return u || ' '; 371 | } 372 | if (u = unescapes[m]) { 373 | return u; 374 | } 375 | return XXX(m); 376 | }))); 377 | } 378 | 379 | got__l_empty() { 380 | if (this.in_scalar) { 381 | return this.add(cache('')); 382 | } 383 | } 384 | 385 | got__l_nb_literal_text__all__rep2(o) { 386 | return this.add(cache(o.text)); 387 | } 388 | 389 | try__c_l_literal() { 390 | this.in_scalar = true; 391 | return this.cache_up(); 392 | } 393 | 394 | got__c_l_literal() { 395 | var lines, t, text; 396 | delete this.in_scalar; 397 | lines = this.cache_drop(); 398 | if (lines.length > 0 && lines[lines.length - 1].text === '') { 399 | lines.pop(); 400 | } 401 | lines = lines.map(function(l) { 402 | return `${l.text}\n`; 403 | }); 404 | text = lines.join(''); 405 | t = this.parser.state_curr().t; 406 | if (t === 'clip') { 407 | text = text.replace(/\n+$/, "\n"); 408 | } else if (t === 'strip') { 409 | text = text.replace(/\n+$/, ""); 410 | } else if (!text.match(/\S/)) { 411 | text = text.replace(/\n(\n+)$/, "$1"); 412 | } 413 | return this.add(scalar_event('literal', text)); 414 | } 415 | 416 | not__c_l_literal() { 417 | delete this.in_scalar; 418 | return this.cache_drop(); 419 | } 420 | 421 | got__ns_char(o) { 422 | if (this.in_scalar) { 423 | return this.first = o.text; 424 | } 425 | } 426 | 427 | got__s_white(o) { 428 | if (this.in_scalar) { 429 | return this.first = o.text; 430 | } 431 | } 432 | 433 | got__s_nb_folded_text__all__rep(o) { 434 | return this.add(cache(`${this.first}${o.text}`)); 435 | } 436 | 437 | got__s_nb_spaced_text__all__rep(o) { 438 | return this.add(cache(`${this.first}${o.text}`)); 439 | } 440 | 441 | try__c_l_folded() { 442 | this.in_scalar = true; 443 | this.first = ''; 444 | return this.cache_up(); 445 | } 446 | 447 | got__c_l_folded() { 448 | var lines, t, text; 449 | delete this.in_scalar; 450 | lines = this.cache_drop().map(function(l) { 451 | return l.text; 452 | }); 453 | text = lines.join("\n"); 454 | text = text.replace(/^(\S.*)\n(?=\S)/gm, "$1 "); 455 | text = text.replace(/^(\S.*)\n(\n+)/gm, "$1$2"); 456 | text = text.replace(/^([\ \t]+\S.*)\n(\n+)(?=\S)/gm, "$1$2"); 457 | text += "\n"; 458 | t = this.parser.state_curr().t; 459 | if (t === 'clip') { 460 | text = text.replace(/\n+$/, "\n"); 461 | if (text === "\n") { 462 | text = ''; 463 | } 464 | } else if (t === 'strip') { 465 | text = text.replace(/\n+$/, ""); 466 | } 467 | return this.add(scalar_event('folded', text)); 468 | } 469 | 470 | not__c_l_folded() { 471 | delete this.in_scalar; 472 | return this.cache_drop(); 473 | } 474 | 475 | got__e_scalar() { 476 | return this.add(scalar_event('plain', '')); 477 | } 478 | 479 | not__s_l_block_collection__all__rep__all__any__all() { 480 | delete this.tag; 481 | return delete this.anchor; 482 | } 483 | 484 | got__c_ns_anchor_property(o) { 485 | return this.anchor = o.text.slice(1); 486 | } 487 | 488 | got__c_ns_tag_property(o) { 489 | var m, prefix, tag; 490 | tag = o.text; 491 | if (m = tag.match(/^!<(.*)>$/)) { 492 | this.tag = m[1]; 493 | } else if (m = tag.match(/^!!(.*)/)) { 494 | prefix = this.tag_map['!!']; 495 | if (prefix != null) { 496 | this.tag = prefix + tag.slice(2); 497 | } else { 498 | this.tag = `tag:yaml.org,2002:${m[1]}`; 499 | } 500 | } else if (m = tag.match(/^(!.*?!)/)) { 501 | prefix = this.tag_map[m[1]]; 502 | if (prefix != null) { 503 | this.tag = prefix + tag.slice((m[1].length)); 504 | } else { 505 | die(`No %TAG entry for '${prefix}'`); 506 | } 507 | } else if ((prefix = this.tag_map['!']) != null) { 508 | this.tag = prefix + tag.slice(1); 509 | } else { 510 | this.tag = tag; 511 | } 512 | return this.tag = this.tag.replace(/%([0-9a-fA-F]{2})/g, function(...m) { 513 | return String.fromCharCode(parseInt(m[1], 16)); 514 | }); 515 | } 516 | 517 | got__c_ns_alias_node(o) { 518 | return this.add(alias_event(o.text.slice(1))); 519 | } 520 | 521 | }; 522 | 523 | unescapes = { 524 | '\\\\': '\\', 525 | '\r\n': '\n', 526 | '\\ ': ' ', 527 | '\\"': '"', 528 | '\\/': '/', 529 | '\\_': '\u{a0}', 530 | '\\0': '\x00', 531 | '\\a': '\x07', 532 | '\\b': '\x08', 533 | '\\e': '\x1b', 534 | '\\f': '\x0c', 535 | '\\n': '\x0a', 536 | '\\r': '\x0d', 537 | '\\t': '\x09', 538 | '\\\t': '\x09', 539 | '\\v': '\x0b', 540 | '\\L': '\u{2028}', 541 | '\\N': '\u{85}', 542 | '\\P': '\u{2029}' 543 | }; 544 | 545 | end1 = String(/(?:\\\r?\n[ \t]*)/).slice(1, -1); 546 | 547 | end2 = String(/(?:[ \t]*\r?\n[ \t]*)/).slice(1, -1); 548 | 549 | hex = '[0-9a-fA-F]'; 550 | 551 | hex2 = String(RegExp(`(?:\\\\x(${hex}{2}))`)).slice(1, -1); 552 | 553 | hex4 = String(RegExp(`(?:\\\\u(${hex}{4}))`)).slice(1, -1); 554 | 555 | hex8 = String(RegExp(`(?:\\\\U(${hex}{8}))`)).slice(1, -1); 556 | 557 | return Receiver; 558 | 559 | }).call(this); 560 | 561 | // vim: sw=2: 562 | 563 | }).call(this); 564 | -------------------------------------------------------------------------------- /parser-1.2/javascript/lib/test-receiver.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.5.1 2 | (function() { 3 | var TestReceiver, event_map, style_map; 4 | 5 | require('./prelude'); 6 | 7 | require('./receiver'); 8 | 9 | event_map = { 10 | stream_start: '+STR', 11 | stream_end: '-STR', 12 | document_start: '+DOC', 13 | document_end: '-DOC', 14 | mapping_start: '+MAP', 15 | mapping_end: '-MAP', 16 | sequence_start: '+SEQ', 17 | sequence_end: '-SEQ', 18 | scalar: '=VAL', 19 | alias: '=ALI' 20 | }; 21 | 22 | style_map = { 23 | plain: ':', 24 | single: "'", 25 | double: '"', 26 | literal: '|', 27 | folded: '>' 28 | }; 29 | 30 | global.TestReceiver = TestReceiver = class TestReceiver extends Receiver { 31 | output() { 32 | var list; 33 | list = this.event.map(function(e) { 34 | var event, style, type, value; 35 | type = event_map[e.event]; 36 | event = [type]; 37 | if (type === '+DOC' && e.explicit) { 38 | event.push('---'); 39 | } 40 | if (type === '-DOC' && e.explicit) { 41 | event.push('...'); 42 | } 43 | if (type === '+MAP' && e.flow) { 44 | event.push('{}'); 45 | } 46 | if (type === '+SEQ' && e.flow) { 47 | event.push('[]'); 48 | } 49 | if (e.anchor) { 50 | event.push(`&${e.anchor}`); 51 | } 52 | if (e.tag) { 53 | event.push(`<${e.tag}>`); 54 | } 55 | if (e.name) { 56 | event.push(`*${e.name}`); 57 | } 58 | if (e.value != null) { 59 | style = style_map[e.style]; 60 | value = e.value.replace(/\\/g, '\\\\').replace(/\x00/g, '\\0').replace(/\x07/g, '\\a').replace(/\x08/g, '\\b').replace(/\x09/g, '\\t').replace(/\x0a/g, '\\n').replace(/\x0b/g, '\\v').replace(/\x0c/g, '\\f').replace(/\x0d/g, '\\r').replace(/\x1b/g, '\\e').replace(/\u0085/g, '\\N').replace(/\u00a0/g, '\\_').replace(/\u2028/g, '\\L').replace(/\u2029/g, '\\P'); 61 | event.push(`${style}${value}`); 62 | } 63 | return event.join(' ') + "\n"; 64 | }); 65 | return list.join(''); 66 | } 67 | 68 | }; 69 | 70 | // vim: sw=2: 71 | 72 | }).call(this); 73 | -------------------------------------------------------------------------------- /parser-1.2/javascript/test/diff-trace.tml: -------------------------------------------------------------------------------- 1 | ../../../test/diff-trace.tml -------------------------------------------------------------------------------- /parser-1.2/javascript/test/error-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/error-suite.tml -------------------------------------------------------------------------------- /parser-1.2/javascript/test/parse-block.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-block.tml -------------------------------------------------------------------------------- /parser-1.2/javascript/test/parse-flow.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-flow.tml -------------------------------------------------------------------------------- /parser-1.2/javascript/test/parse-props.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-props.tml -------------------------------------------------------------------------------- /parser-1.2/javascript/test/parse-scalar.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-scalar.tml -------------------------------------------------------------------------------- /parser-1.2/javascript/test/parse-stream.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-stream.tml -------------------------------------------------------------------------------- /parser-1.2/javascript/test/parse-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-suite.tml -------------------------------------------------------------------------------- /parser-1.2/javascript/test/testml-bridge.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.5.1 2 | (function() { 3 | var TestMLBridge; 4 | 5 | require('../../../test/testml/src/node/lib/testml/bridge'); 6 | 7 | require('../lib/prelude'); 8 | 9 | require('../lib/parser'); 10 | 11 | require('../lib/grammar'); 12 | 13 | require('../lib/test-receiver'); 14 | 15 | module.exports = TestMLBridge = class TestMLBridge extends TestML.Bridge { 16 | parse(yaml, expect_error = null) { 17 | var e, error, parser; 18 | parser = new Parser(new TestReceiver()); 19 | error = ''; 20 | try { 21 | parser.parse(yaml); 22 | } catch (error1) { 23 | e = error1; 24 | error = String(e); 25 | } 26 | if (expect_error != null) { 27 | if (error) { 28 | return 1; 29 | } else { 30 | return 0; 31 | } 32 | } 33 | if (error) { 34 | return error; 35 | } else { 36 | return parser.receiver.output(); 37 | } 38 | } 39 | 40 | unescape(text) { 41 | text = text.replace(/␣/g, ' ').replace(/—*»/g, "\t").replace(/⇔/g, "\uFEFF").replace(/↵/g, '').replace(/∎\n$/, ''); 42 | // text = text.replace(/↓/g, "\r") 43 | return text; 44 | } 45 | 46 | fix_test_output(text) { 47 | return text; 48 | } 49 | 50 | }; 51 | 52 | }).call(this); 53 | -------------------------------------------------------------------------------- /parser-1.2/json-parser-coffeescript/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | coffee test.coffee 3 | -------------------------------------------------------------------------------- /parser-1.2/json-parser-coffeescript/json-parser.coffee: -------------------------------------------------------------------------------- 1 | # This is the JSON grammar. It is very regular and intends to be generated from 2 | # BNF: 3 | class Grammar extends Parser 4 | TOP: -> @json 5 | 6 | json: -> 7 | @all( 8 | @ws, 9 | @node, 10 | @ws, 11 | ) 12 | 13 | node: -> 14 | @any( 15 | @object, 16 | @array, 17 | @scalar, 18 | ) 19 | 20 | scalar: -> 21 | @any( 22 | @string, 23 | @number, 24 | @boolean, 25 | @null, 26 | ) 27 | 28 | object: -> 29 | @all( 30 | @mapBegin, 31 | @ws, 32 | @x01( 33 | @all( 34 | @pair, 35 | @x00( 36 | @all( 37 | @ws, 38 | @comma, 39 | @ws, 40 | @pair 41 | ), 42 | ) 43 | ) 44 | ) 45 | @mapEnd, 46 | ) 47 | 48 | pair: -> 49 | @all( 50 | @string, 51 | @ws, 52 | @colon, 53 | @ws, 54 | @node, 55 | @ws, 56 | ) 57 | 58 | array: -> 59 | @all( 60 | @seqBegin, 61 | @ws, 62 | @x01( 63 | @all( 64 | @node, 65 | @x00( 66 | @all( 67 | @ws, 68 | @comma, 69 | @ws, 70 | @node, 71 | ) 72 | ) 73 | ) 74 | ) 75 | @seqEnd, 76 | @ws, 77 | ) 78 | 79 | string: -> 80 | @all( 81 | @quote, 82 | @x00( 83 | @any( 84 | @char, 85 | @all( 86 | @back, 87 | @any( 88 | @quote, 89 | @back, 90 | @space, 91 | ) 92 | ), 93 | ) 94 | ), 95 | @quote, 96 | ) 97 | 98 | number: -> 99 | @any( 100 | @all( 101 | @x01(@dash), 102 | @first, 103 | @x00(@digit), 104 | @x01( 105 | @all( 106 | @dot, 107 | @x00(@digit), 108 | ) 109 | ) 110 | ), 111 | @zero, 112 | ) 113 | 114 | boolean: -> 115 | @any( 116 | @true, 117 | @false, 118 | ) 119 | 120 | ws: -> 121 | @x00( 122 | @any( 123 | @space, 124 | @tab, 125 | @cr, 126 | @nl, 127 | ) 128 | ) 129 | 130 | space: -> @chr(' ') 131 | tab: -> @chr("\t") 132 | cr: -> @chr("\r") 133 | nl: -> @chr("\n") 134 | 135 | char: -> 136 | @but( 137 | @rng("\u{20}", "\u{10FFFF}"), 138 | @quote, 139 | @all( 140 | @back, 141 | @any( 142 | @quote, 143 | @back, 144 | ) 145 | ) 146 | ) 147 | 148 | quote: -> @chr('"') 149 | back: -> @chr('\\') 150 | 151 | dash: -> @chr('-') 152 | dot: -> @chr('.') 153 | zero: -> @chr('0') 154 | first: -> @rng('1', '9') 155 | digit: -> @rng('0', '9') 156 | 157 | mapBegin: -> @chr('{') 158 | mapEnd: -> @chr('}') 159 | 160 | seqBegin: -> @chr('[') 161 | seqEnd: -> @chr(']') 162 | 163 | colon: -> @chr(':') 164 | comma: -> @chr(',') 165 | 166 | true: -> 167 | @all( 168 | @chr('t'), 169 | @chr('r'), 170 | @chr('u'), 171 | @chr('e'), 172 | ) 173 | 174 | false: -> 175 | @all( 176 | @chr('f'), 177 | @chr('a'), 178 | @chr('l'), 179 | @chr('s'), 180 | @chr('e'), 181 | ) 182 | 183 | null: -> 184 | @all( 185 | @chr('n'), 186 | @chr('u'), 187 | @chr('l'), 188 | @chr('l'), 189 | ) 190 | 191 | global.Grammar = Grammar 192 | -------------------------------------------------------------------------------- /parser-1.2/json-parser-coffeescript/parser.coffee: -------------------------------------------------------------------------------- 1 | ../coffeescript/lib/parser.coffee -------------------------------------------------------------------------------- /parser-1.2/json-parser-coffeescript/test.coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | 3 | require 'ingy-prelude' 4 | require './parser' 5 | require './json-parser' 6 | 7 | 8 | # This is a Receiver class for the Parser. It outputs event lines in the exact 9 | # format of the YAML Test Suite. 10 | class Receiver 11 | mapBegin: (t)-> say "+MAP" 12 | mapEnd: (t)-> say "-MAP" 13 | seqBegin: (t)-> say "+SEQ" 14 | seqEnd: (t)-> say "-SEQ" 15 | string: (t)-> say "=VAL \"#{t[1..-2]}" 16 | number: (t)-> say "=VAL :#{t}" 17 | boolean: (t)-> say "=VAL :#{t}" 18 | null: (t)-> say "=VAL :#{t}" 19 | 20 | 21 | # A parser testing function: 22 | test = (json)-> 23 | say "\nTest JSON: '#{json}'" 24 | 25 | parser = new Grammar(new Receiver) 26 | 27 | try 28 | parser.parse json 29 | say 'OK' 30 | catch error 31 | snip = parser.input[parser.pos..] 32 | .replace(/\t/g, '\\t') 33 | .replace(/\r/g, '\\r') 34 | .replace(/\n/g, '\\n') 35 | say "Error: #{error}\n at -> '#{snip}'" 36 | 37 | 38 | # Tests: 39 | test '[ %1, "two" ]' 40 | test '[ 1, "two" ]' 41 | test '"xyz"' 42 | test '"foo \\"bar\\""' 43 | test 'true' 44 | test 'false' 45 | test 'null' 46 | test '123' 47 | test '123.456' 48 | test '-123.456' 49 | test '123 456' 50 | test ' { "foo": 123 }' 51 | test ' [ { } , [ ] , { "foo" : null, "ba\\"r\\ " : 123.4 } , "" , 0 , -1.23 , true , false , null ] ' 52 | 53 | -------------------------------------------------------------------------------- /parser-1.2/perl/.gitignore: -------------------------------------------------------------------------------- 1 | /ext-perl/ 2 | -------------------------------------------------------------------------------- /parser-1.2/perl/Makefile: -------------------------------------------------------------------------------- 1 | ROOT := $(shell cd ../..; pwd) 2 | BASE := $(ROOT)/parser-1.2 3 | PARSER_LANG := perl 4 | BIN := perl 5 | GRAMMAR := lib/Grammar.pm 6 | 7 | TEST_DEPS := \ 8 | $(ROOT)/test/testml \ 9 | $(ROOT)/test/suite \ 10 | ext-perl \ 11 | 12 | 13 | include $(ROOT)/.common.mk 14 | 15 | 16 | export PERL5LIB := $(ROOT)/perl/ext-perl/lib/perl5 17 | 18 | ext-perl: 19 | git branch --track $@ origin/$@ 2>/dev/null || true 20 | git worktree add -f $@ $@ 21 | 22 | clean:: 23 | rm -fr ext-perl 24 | -------------------------------------------------------------------------------- /parser-1.2/perl/bin/yaml-parser: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use v5.12; 4 | use Encode; 5 | use FindBin; 6 | use lib "$FindBin::Bin/../lib"; 7 | use lib "$FindBin::Bin/../ext/perl/lib/perl5"; 8 | 9 | BEGIN { -d "$FindBin::Bin/../ext" || `cd $FindBin::Bin/.. && make ext/perl 2>&1` } 10 | 11 | use Prelude; 12 | use Parser; 13 | use TestReceiver; 14 | 15 | my $events = false; 16 | 17 | sub main { 18 | my ($yaml) = @_; 19 | $yaml //= file_read '-'; 20 | 21 | my $parser = Parser->new(TestReceiver->new); 22 | 23 | my $pass = true; 24 | my $start = timer; 25 | 26 | eval { 27 | $parser->parse($yaml); 28 | }; 29 | if ($@) { 30 | warn $@; 31 | $pass = false; 32 | } 33 | 34 | my $time = timer($start); 35 | 36 | my $n; 37 | if ($yaml =~ /\n./) { 38 | $n = "\n"; 39 | } 40 | else { 41 | $n = ''; 42 | $yaml =~ s/\n$/\\n/; 43 | } 44 | 45 | if ($events) { 46 | print encode_utf8($parser->{receiver}->output()); 47 | return $pass ? 1 : 0; 48 | } 49 | elsif ($pass) { 50 | say encode_utf8("PASS - '$n$yaml'"); 51 | say encode_utf8($parser->{receiver}->output()); 52 | say sprintf "Parse time %.5fs", $time; 53 | return 1 54 | } 55 | else { 56 | say encode_utf8("FAIL - '$n$yaml'"); 57 | say encode_utf8($parser->{receiver}->output()); 58 | say sprintf "Parse time %.5fs", $time; 59 | return 0; 60 | } 61 | } 62 | 63 | if (@ARGV and $ARGV[0] eq '--events') { 64 | $events = true; 65 | shift @ARGV; 66 | } 67 | 68 | if (main @ARGV) { 69 | exit 0; 70 | } 71 | else { 72 | exit 1; 73 | } 74 | 75 | # vim: sw=2: 76 | -------------------------------------------------------------------------------- /parser-1.2/perl/lib/Parser.pm: -------------------------------------------------------------------------------- 1 | ### 2 | # This is a parser class. It has a parse() method and parsing primitives for 3 | # the grammar. It calls methods in the receiver class, when a rule matches: 4 | ### 5 | 6 | use v5.12; 7 | 8 | package Parser; 9 | 10 | use Encode; 11 | use Prelude; 12 | use Grammar; 13 | 14 | use base 'Grammar'; 15 | 16 | use constant TRACE => $ENV{TRACE}; 17 | 18 | sub receiver { $_[0]->{receiver} } 19 | 20 | sub new { 21 | my ($class, $receiver) = @_; 22 | 23 | my $self = bless { 24 | receiver => $receiver, 25 | pos => 0, 26 | end => 0, 27 | state => [], 28 | trace_num => 0, 29 | trace_line => 0, 30 | trace_on => true, 31 | trace_off => 0, 32 | trace_info => ['', '', ''], 33 | }, $class; 34 | 35 | $receiver->{parser} = $self; 36 | 37 | return $self; 38 | } 39 | 40 | sub parse { 41 | my ($self, $input) = @_; 42 | 43 | $input .= "\n" unless 44 | length($input) == 0 or 45 | $input =~ /\n\z/; 46 | 47 | $input = decode_utf8($input) 48 | unless Encode::is_utf8($input); 49 | $self->{input} = $input; 50 | 51 | $self->{end} = length $self->{input}; 52 | 53 | $self->{trace_on} = not $self->trace_start if TRACE; 54 | 55 | my $ok; 56 | eval { 57 | $ok = $self->call($self->func('TOP')); 58 | $self->trace_flush; 59 | }; 60 | if ($@) { 61 | $self->trace_flush; 62 | die $@; 63 | } 64 | 65 | die "Parser failed" if not $ok; 66 | die "Parser finished before end of input" 67 | if $self->{pos} < $self->{end}; 68 | 69 | return true; 70 | } 71 | 72 | sub state_curr { 73 | my ($self) = @_; 74 | $self->{state}[-1] || { 75 | name => undef, 76 | doc => false, 77 | lvl => 0, 78 | beg => 0, 79 | end => 0, 80 | m => undef, 81 | t => undef, 82 | }; 83 | } 84 | 85 | sub state_prev { 86 | my ($self) = @_; 87 | $self->{state}[-2] 88 | } 89 | 90 | sub state_push { 91 | my ($self, $name) = @_; 92 | 93 | my $curr = $self->state_curr; 94 | 95 | push @{$self->{state}}, { 96 | name => $name, 97 | doc => $curr->{doc}, 98 | lvl => $curr->{lvl} + 1, 99 | beg => $self->{pos}, 100 | end => undef, 101 | m => $curr->{m}, 102 | t => $curr->{t}, 103 | }; 104 | } 105 | 106 | sub state_pop { 107 | my ($self) = @_; 108 | my $child = pop @{$self->{state}}; 109 | my $curr = $self->state_curr; 110 | return unless defined $curr; 111 | $curr->{beg} = $child->{beg}; 112 | $curr->{end} = $self->{pos}; 113 | } 114 | 115 | sub call { 116 | my ($self, $func, $type) = @_; 117 | $type //= 'boolean'; 118 | 119 | my $args = []; 120 | ($func, @$args) = @$func if isArray $func; 121 | 122 | return $func if isNumber $func or isString $func; 123 | 124 | FAIL "Bad call type '${\ typeof $func}' for '$func'" 125 | unless isFunction $func; 126 | 127 | my $trace = $func->{trace} //= $func->{name}; 128 | 129 | $self->state_push($trace); 130 | 131 | $self->{trace_num}++; 132 | $self->trace('?', $trace, $args) if TRACE; 133 | 134 | if ($func->{name} eq 'l_bare_document') { 135 | $self->state_curr->{doc} = true; 136 | } 137 | 138 | @$args = map { 139 | isArray($_) ? $self->call($_, 'any') : 140 | isFunction($_) ? $_->{func}->() : 141 | $_; 142 | } @$args; 143 | 144 | my $pos = $self->{pos}; 145 | $self->receive($func, 'try', $pos); 146 | 147 | my $value = $func->{func}->($self, @$args); 148 | while (isFunction($value) or isArray($value)) { 149 | $value = $self->call($value); 150 | } 151 | 152 | FAIL "Calling '$trace' returned '${\ typeof($value)}' instead of '$type'" 153 | if $type ne 'any' and typeof($value) ne $type; 154 | 155 | $self->{trace_num}++; 156 | if ($type ne 'boolean') { 157 | $self->trace('>', $value) if TRACE; 158 | } 159 | else { 160 | if ($value) { 161 | $self->trace('+', $trace) if TRACE; 162 | $self->receive($func, 'got', $pos); 163 | } 164 | else { 165 | $self->trace('x', $trace) if TRACE; 166 | $self->receive($func, 'not', $pos); 167 | } 168 | } 169 | 170 | $self->state_pop; 171 | return $value; 172 | } 173 | 174 | sub receive { 175 | my ($self, $func, $type, $pos) = @_; 176 | 177 | $func->{receivers} //= $self->make_receivers; 178 | my $receiver = $func->{receivers}{$type}; 179 | return unless $receiver; 180 | 181 | $receiver->($self->{receiver}, { 182 | text => substr($self->{input}, $pos, $self->{pos}-$pos), 183 | state => $self->state_curr, 184 | start => $pos, 185 | }); 186 | } 187 | 188 | sub make_receivers { 189 | my ($self) = @_; 190 | my $i = @{$self->{state}}; 191 | my $names = []; 192 | my $n; 193 | while ($i > 0 and not(($n = $self->{state}[--$i]{name}) =~ /_/)) { 194 | if ($n =~ /^chr\((.)\)$/) { 195 | $n = hex_char $1; 196 | } 197 | else { 198 | $n =~ s/\(.*//; 199 | } 200 | unshift @$names, $n; 201 | } 202 | my $name = join '__', $n, @$names; 203 | 204 | return { 205 | try => $self->{receiver}->can("try__$name"), 206 | got => $self->{receiver}->can("got__$name"), 207 | not => $self->{receiver}->can("not__$name"), 208 | }; 209 | } 210 | 211 | # Match all subrule methods: 212 | sub all { 213 | my ($self, @funcs) = @_; 214 | name all => sub { 215 | my $pos = $self->{pos}; 216 | for my $func (@funcs) { 217 | FAIL '*** Missing function in @all group:', \@funcs 218 | unless defined $func; 219 | 220 | if (not $self->call($func)) { 221 | $self->{pos} = $pos; 222 | return false; 223 | } 224 | } 225 | 226 | return true; 227 | }; 228 | } 229 | 230 | # Match any subrule method. Rules are tried in order and stops on first match: 231 | sub any { 232 | my ($self, @funcs) = @_; 233 | name any => sub { 234 | for my $func (@funcs) { 235 | if ($self->call($func)) { 236 | return true; 237 | } 238 | } 239 | 240 | return false; 241 | }; 242 | } 243 | 244 | sub may { 245 | my ($self, $func) = @_; 246 | name may => sub { 247 | $self->call($func); 248 | }; 249 | } 250 | 251 | # Repeat a rule a certain number of times: 252 | sub rep { 253 | my ($self, $min, $max, $func) = @_; 254 | name rep => sub { 255 | return false if defined $max and $max < 0; 256 | my $count = 0; 257 | my $pos = $self->{pos}; 258 | my $pos_start = $pos; 259 | while (not(defined $max) or $count < $max) { 260 | last unless $self->call($func); 261 | last if $self->{pos} == $pos; 262 | $count++; 263 | $pos = $self->{pos}; 264 | } 265 | if ($count >= $min and (not(defined $max) or $count <= $max)) { 266 | return true; 267 | } 268 | $self->{pos} = $pos_start; 269 | return false; 270 | }, "rep($min,${\ ($max // 'null')})"; 271 | } 272 | sub rep2 { 273 | my ($self, $min, $max, $func) = @_; 274 | name rep2 => sub { 275 | return false if defined $max and $max < 0; 276 | my $count = 0; 277 | my $pos = $self->{pos}; 278 | my $pos_start = $pos; 279 | while (not(defined $max) or $count < $max) { 280 | last unless $self->call($func); 281 | last if $self->{pos} == $pos; 282 | $count++; 283 | $pos = $self->{pos}; 284 | } 285 | if ($count >= $min and (not(defined $max) or $count <= $max)) { 286 | return true; 287 | } 288 | $self->{pos} = $pos_start; 289 | return false; 290 | }, "rep2($min,${\ ($max // 'null')})"; 291 | } 292 | 293 | # Call a rule depending on state value: 294 | sub case { 295 | my ($self, $var, $map) = @_; 296 | name case => sub { 297 | my $rule = $map->{$var}; 298 | defined $rule or 299 | FAIL "Can't find '$var' in:", $map; 300 | $self->call($rule); 301 | }, "case($var,${\ stringify $map})"; 302 | } 303 | 304 | # Call a rule depending on state value: 305 | sub flip { 306 | my ($self, $var, $map) = @_; 307 | my $value = $map->{$var}; 308 | defined $value or 309 | FAIL "Can't find '$var' in:", $map; 310 | return $value if not ref $value; 311 | return $self->call($value, 'number'); 312 | } 313 | name flip => \&flip; 314 | 315 | sub the_end { 316 | my ($self) = @_; 317 | return ( 318 | $self->{pos} >= $self->{end} or ( 319 | $self->state_curr->{doc} and 320 | $self->start_of_line and 321 | substr($self->{input}, $self->{pos}) =~ 322 | /^(?:---|\.\.\.)(?=\s|\z)/ 323 | ) 324 | ); 325 | } 326 | 327 | # Match a single char: 328 | sub chr { 329 | my ($self, $char) = @_; 330 | name chr => sub { 331 | return false if $self->the_end; 332 | if ( 333 | $self->{pos} >= $self->{end} or ( 334 | $self->state_curr->{doc} and 335 | $self->start_of_line and 336 | substr($self->{input}, $self->{pos}) =~ 337 | /^(?:---|\.\.\.)(?=\s|\z)/ 338 | ) 339 | ) { 340 | return false; 341 | } 342 | if (substr($self->{input}, $self->{pos}, 1) eq $char) { 343 | $self->{pos}++; 344 | return true; 345 | } 346 | return false; 347 | }, "chr(${\ stringify($char)})"; 348 | } 349 | 350 | # Match a char in a range: 351 | sub rng { 352 | my ($self, $low, $high) = @_; 353 | name rng => sub { 354 | return false if $self->the_end; 355 | if ( 356 | $low le substr($self->{input}, $self->{pos}, 1) and 357 | substr($self->{input}, $self->{pos}, 1) le $high 358 | ) { 359 | $self->{pos}++; 360 | return true; 361 | } 362 | return false; 363 | }, "rng(${\ stringify($low)},${\ stringify($high)})"; 364 | } 365 | 366 | # Must match first rule but none of others: 367 | sub but { 368 | my ($self, @funcs) = @_; 369 | name but => sub { 370 | return false if $self->the_end; 371 | my $pos1 = $self->{pos}; 372 | return false unless $self->call($funcs[0]); 373 | my $pos2 = $self->{pos}; 374 | $self->{pos} = $pos1; 375 | for my $func (@funcs[1..$#funcs]) { 376 | if ($self->call($func)) { 377 | $self->{pos} = $pos1; 378 | return false; 379 | } 380 | } 381 | $self->{pos} = $pos2; 382 | return true; 383 | } 384 | } 385 | 386 | sub chk { 387 | my ($self, $type, $expr) = @_; 388 | name chk => sub { 389 | my $pos = $self->{pos}; 390 | $self->{pos}-- if $type eq '<='; 391 | my $ok = $self->call($expr); 392 | $self->{pos} = $pos; 393 | return $type eq '!' ? not($ok) : $ok; 394 | }, "chk($type, ${\ stringify $expr})"; 395 | } 396 | 397 | sub set { 398 | my ($self, $var, $expr) = @_; 399 | name set => sub { 400 | my $value = $self->call($expr, 'any'); 401 | return false if $value == -1; 402 | $value = $self->auto_detect if $value eq 'auto-detect'; 403 | my $state = $self->state_prev; 404 | $state->{$var} = $value; 405 | if ($state->{name} ne 'all') { 406 | my $size = @{$self->{state}}; 407 | for (my $i = 3; $i < $size; $i++) { 408 | FAIL "failed to traverse state stack in 'set'" 409 | if $i > $size - 2; 410 | $state = $self->{state}[0 - $i]; 411 | $state->{$var} = $value; 412 | last if $state->{name} eq 's_l_block_scalar'; 413 | } 414 | } 415 | return true; 416 | }, "set('$var', ${\ stringify $expr})"; 417 | } 418 | 419 | sub max { 420 | my ($self, $max) = @_; 421 | name max => sub { 422 | return true; 423 | }; 424 | } 425 | 426 | sub exclude { 427 | my ($self, $rule) = @_; 428 | name exclude => sub { 429 | return true; 430 | }; 431 | } 432 | 433 | sub add { 434 | my ($self, $x, $y) = @_; 435 | name add => sub { 436 | $y = $self->call($y, 'number') if isFunction $y; 437 | FAIL "y is '${\ stringify $y}', not number in 'add'" 438 | unless isNumber $y; 439 | return $x + $y; 440 | }, "add($x,${\ stringify $y})"; 441 | } 442 | 443 | sub sub { 444 | my ($self, $x, $y) = @_; 445 | name sub => sub { 446 | return $x - $y; 447 | }, "sub($x,$y)"; 448 | } 449 | 450 | # This method does not need to return a function since it is never 451 | # called in the grammar. 452 | sub match { 453 | my ($self) = @_; 454 | my $state = $self->{state}; 455 | my $i = @$state - 1; 456 | while ($i > 0 && not defined $state->[$i]{end}) { 457 | FAIL "Can't find match" if $i == 1; 458 | $i--; 459 | } 460 | 461 | my ($beg, $end) = @{$self->{state}[$i]}{qw}; 462 | return substr($self->{input}, $beg, ($end - $beg)); 463 | } 464 | name match => \&match; 465 | 466 | sub len { 467 | my ($self, $str) = @_; 468 | name len => sub { 469 | $str = $self->call($str, 'string') unless isString($str); 470 | return length $str; 471 | }; 472 | } 473 | 474 | sub ord { 475 | my ($self, $str) = @_; 476 | name ord => sub { 477 | # Should be `$self->call($str, 'string')`, but... Perl 478 | $str = $self->call($str, 'number') unless isString($str); 479 | return ord($str) - 48; 480 | }; 481 | } 482 | 483 | sub if { 484 | my ($self, $test, $do_if_true) = @_; 485 | name if => sub { 486 | $test = $self->call($test, 'boolean') unless isBoolean $test; 487 | if ($test) { 488 | $self->call($do_if_true); 489 | return true; 490 | } 491 | return false; 492 | }; 493 | } 494 | 495 | sub lt { 496 | my ($self, $x, $y) = @_; 497 | name lt => sub { 498 | $x = $self->call($x, 'number') unless isNumber($x); 499 | $y = $self->call($y, 'number') unless isNumber($y); 500 | return $x < $y ? true : false; 501 | }, "lt(${\ stringify $x},${\ stringify $y})"; 502 | } 503 | 504 | sub le { 505 | my ($self, $x, $y) = @_; 506 | name le => sub { 507 | $x = $self->call($x, 'number') unless isNumber($x); 508 | $y = $self->call($y, 'number') unless isNumber($y); 509 | return $x <= $y ? true : false; 510 | }, "le(${\ stringify $x},${\ stringify $y})"; 511 | } 512 | 513 | sub m { 514 | my ($self) = @_; 515 | name m => sub { 516 | $self->state_curr->{m}; 517 | }; 518 | } 519 | 520 | sub t { 521 | my ($self) = @_; 522 | name t => sub { 523 | $self->state_curr->{t}; 524 | }; 525 | } 526 | 527 | #------------------------------------------------------------------------------ 528 | # Special grammar rules 529 | #------------------------------------------------------------------------------ 530 | sub start_of_line { 531 | my ($self) = @_; 532 | ( 533 | $self->{pos} == 0 || 534 | $self->{pos} >= $self->{end} || 535 | substr($self->{input}, $self->{pos} - 1, 1) eq "\n" 536 | ) ? true : false; 537 | } 538 | name 'start_of_line', \&start_of_line; 539 | 540 | sub end_of_stream { 541 | my ($self) = @_; 542 | ($self->{pos} >= $self->{end}) ? true : false; 543 | } 544 | name 'end_of_stream', \&end_of_stream; 545 | 546 | sub empty { true } 547 | name 'empty', \∅ 548 | 549 | sub auto_detect_indent { 550 | my ($self, $n) = @_; 551 | my $pos = $self->{pos}; 552 | my $in_seq = ($pos > 0 && substr($self->{input}, $pos - 1, 1) =~ /^[\-\?\:]$/); 553 | substr($self->{input}, $pos) =~ /^ 554 | ( 555 | (?: 556 | \ * 557 | (?:\#.*)? 558 | \n 559 | )* 560 | ) 561 | (\ *) 562 | /x or FAIL "auto_detect_indent"; 563 | my $pre = $1; 564 | my $m = length($2); 565 | if ($in_seq and not length $pre) { 566 | $m++ if $n == -1; 567 | } 568 | else { 569 | $m -= $n; 570 | } 571 | $m = 0 if $m < 0; 572 | return $m; 573 | } 574 | name 'auto_detect_indent', \&auto_detect_indent; 575 | 576 | sub auto_detect { 577 | my ($self, $n) = @_; 578 | substr($self->{input}, $self->{pos}) =~ / 579 | ^.*\n 580 | ( 581 | (?:\ *\n)* 582 | ) 583 | (\ *) 584 | (.?) 585 | /x; 586 | my $pre = $1; 587 | my $m; 588 | if (defined $3 and length($3)) { 589 | $m = length($2) - $n; 590 | } 591 | else { 592 | $m = 0; 593 | while ($pre =~ /\ {$m}/){ 594 | $m++; 595 | } 596 | $m = $m - $n - 1; 597 | } 598 | # XXX change 'die' to 'error' for reporting parse errors 599 | $n += $m; 600 | die "Spaces found after indent in auto-detect (5LLU)" 601 | if $m > 0 and $pre =~ /^.{$n}\ /m; 602 | return($m == 0 ? 1 : $m); 603 | } 604 | name 'auto_detect', \&auto_detect; 605 | 606 | #------------------------------------------------------------------------------ 607 | # Trace debugging 608 | #------------------------------------------------------------------------------ 609 | sub trace_start { 610 | '' || "$ENV{TRACE_START}"; 611 | } 612 | 613 | sub trace_quiet { 614 | return [] if $ENV{DEBUG}; 615 | 616 | my @small = ( 617 | 'b_as_line_feed', 618 | 's_indent', 619 | 'nb_char', 620 | ); 621 | 622 | my @noisy = ( 623 | 'c_directives_end', 624 | 'c_l_folded', 625 | 'c_l_literal', 626 | 'c_ns_alias_node', 627 | 'c_ns_anchor_property', 628 | 'c_ns_tag_property', 629 | 'l_directive_document', 630 | 'l_document_prefix', 631 | 'ns_flow_content', 632 | 'ns_plain', 633 | 's_l_comments', 634 | 's_separate', 635 | ); 636 | 637 | return [ 638 | split(',', ($ENV{TRACE_QUIET} || '')), 639 | @noisy, 640 | ]; 641 | } 642 | 643 | sub trace { 644 | my ($self, $type, $call, $args) = @_; 645 | $args //= []; 646 | 647 | $call = "'$call'" if $call =~ /^($| |.* $)/; 648 | return unless $self->{trace_on} or $call eq $self->trace_start; 649 | 650 | my $level = $self->state_curr->{lvl}; 651 | my $indent = ' ' x $level; 652 | if ($level > 0) { 653 | my $l = length "$level"; 654 | $indent = "$level" . substr($indent, $l); 655 | } 656 | 657 | my $input = substr($self->{input}, $self->{pos}); 658 | $input = substr($input, 0, 30) . '…' 659 | if length($input) > 30; 660 | $input =~ s/\t/\\t/g; 661 | $input =~ s/\r/\\r/g; 662 | $input =~ s/\n/\\n/g; 663 | 664 | my $line = sprintf( 665 | "%s%s %-40s %4d '%s'\n", 666 | $indent, 667 | $type, 668 | $self->trace_format_call($call, $args), 669 | $self->{pos}, 670 | $input, 671 | ); 672 | 673 | if ($ENV{DEBUG}) { 674 | warn sprintf "%6d %s", 675 | $self->{trace_num}, $line; 676 | return; 677 | } 678 | 679 | my $trace_info = undef; 680 | $level = "${level}_$call"; 681 | if ($type eq '?' and not $self->{trace_off}) { 682 | $trace_info = [$type, $level, $line, $self->{trace_num}]; 683 | } 684 | if (grep $_ eq $call, @{$self->trace_quiet}) { 685 | $self->{trace_off} += $type eq '?' ? 1 : -1; 686 | } 687 | if ($type ne '?' and not $self->{trace_off}) { 688 | $trace_info = [$type, $level, $line, $self->{trace_num}]; 689 | } 690 | 691 | if (defined $trace_info) { 692 | my ($prev_type, $prev_level, $prev_line, $trace_num) = 693 | @{$self->{trace_info}}; 694 | if ($prev_type eq '?' and $prev_level eq $level) { 695 | $trace_info->[1] = ''; 696 | if ($line =~ /^\d*\ *\+/) { 697 | $prev_line =~ s/\?/=/; 698 | } 699 | else { 700 | $prev_line =~ s/\?/!/; 701 | } 702 | } 703 | if ($prev_level) { 704 | warn sprintf "%5d %6d %s", 705 | ++$self->{trace_line}, $trace_num, $prev_line; 706 | } 707 | 708 | $self->{trace_info} = $trace_info; 709 | } 710 | 711 | if ($call eq $self->trace_start) { 712 | $self->{trace_on} = not $self->{trace_on}; 713 | } 714 | } 715 | 716 | sub trace_format_call { 717 | my ($self, $call, $args) = @_; 718 | return $call unless @$args; 719 | my $list = join ',', map stringify($_), @$args; 720 | return "$call($list)"; 721 | } 722 | 723 | sub trace_flush { 724 | my ($self) = @_; 725 | my ($type, $level, $line, $count) = @{$self->{trace_info}}; 726 | if (my $line = $self->{trace_info}[2]) { 727 | warn sprintf "%5d %6d %s", 728 | ++$self->{trace_line}, $count, $line; 729 | } 730 | } 731 | 732 | 1; 733 | 734 | # vim: sw=2: 735 | -------------------------------------------------------------------------------- /parser-1.2/perl/lib/Prelude.pm: -------------------------------------------------------------------------------- 1 | use v5.12; 2 | 3 | package Func; 4 | use overload 5 | eq => sub { 0 }, 6 | bool => sub { 1 }; 7 | sub new { 8 | my ($class, $func, $name, $trace, $num) = @_; 9 | bless { 10 | func => $func, 11 | name => $name, 12 | trace => $trace || $name, 13 | num => $num, 14 | receivers => undef, 15 | }, $class; 16 | } 17 | sub TO_JSON { $_[0]->{func} } 18 | 19 | package Prelude; 20 | 21 | use boolean; 22 | use Carp; 23 | use Encode; 24 | use Exporter 'import'; 25 | use JSON::PP; 26 | use Time::HiRes qw< gettimeofday tv_interval >; 27 | use YAML::PP::Perl; 28 | use XXX; 29 | 30 | use constant DEBUG => $ENV{DEBUG}; 31 | 32 | our @EXPORT; 33 | sub export { push @EXPORT, @_ } 34 | 35 | export qw< true false >; 36 | 37 | export 'rule'; 38 | sub rule { 39 | my ($num, $name, $func) = @_; 40 | my ($pkg) = caller; 41 | { 42 | no strict 'refs'; 43 | *{"${pkg}::$name"} = $func; 44 | } 45 | $func = name($name, $func); 46 | $func->{num} = $num; 47 | return $func; 48 | } 49 | 50 | export 'name'; 51 | sub name { 52 | my ($name, $func, $trace) = (@_, ''); 53 | my $f = $func; 54 | if (DEBUG) { 55 | $f = sub { 56 | my ($n, @args) = @_; 57 | my $args = join ',', map stringify($_), @args; 58 | debug("$name($args)"); 59 | goto $func; 60 | } 61 | } 62 | return Func->new($f, $name, $trace); 63 | } 64 | 65 | export qw; 66 | sub isNull { not defined $_[0] } 67 | sub isBoolean { ref($_[0]) eq 'boolean' } 68 | sub isNumber { not(ref $_[0]) and $_[0] =~ /^-?\d+$/ } 69 | sub isString { not(ref $_[0]) and $_[0] !~ /^-?\d+$/ } 70 | sub isFunction { ref($_[0]) eq 'Func' } 71 | sub isArray { ref($_[0]) eq 'ARRAY' } 72 | sub isObject { ref($_[0]) eq 'HASH' } 73 | 74 | export 'typeof'; 75 | sub typeof { 76 | my ($value) = @_; 77 | return 'null' if isNull $value; 78 | return 'boolean' if isBoolean $value; 79 | return 'number' if isNumber $value; 80 | return 'string' if isString $value; 81 | return 'function' if isFunction $value; 82 | return 'array' if isArray $value; 83 | return 'object' if isObject $value; 84 | XXX [$value, ref($value)]; 85 | } 86 | 87 | my $json = JSON::PP->new->canonical->allow_unknown->allow_nonref->convert_blessed; 88 | sub json_stringify { 89 | my $string; 90 | eval { 91 | $string = $json->encode($_[0]); 92 | }; 93 | confess $@ if $@; 94 | return $string; 95 | } 96 | 97 | export 'stringify'; 98 | sub stringify; 99 | sub stringify { 100 | my ($o) = @_; 101 | if ($o eq "\x{feff}") { 102 | return "\\uFEFF"; 103 | } 104 | if (isFunction $o) { 105 | return "\@$o->{trace}"; 106 | } 107 | if (isObject $o) { 108 | return json_stringify [ sort keys %$o ]; 109 | } 110 | if (isArray $o) { 111 | return "[${\ join ',', map stringify($_), @$o}]"; 112 | } 113 | if (isString $o) { 114 | $_ = json_stringify $o; 115 | s/^"(.*)"$/$1/; 116 | return $_; 117 | } 118 | return json_stringify $o; 119 | } 120 | 121 | export 'hex_char'; 122 | sub hex_char { 123 | my ($chr) = @_; 124 | return sprintf "x%x", ord $chr; 125 | } 126 | 127 | export 'func'; 128 | sub func { 129 | my ($self, $name) = @_; 130 | my $func = $self->can($name) or 131 | die "Can't find parser function '$name'"; 132 | Func->new($func, $name); 133 | } 134 | 135 | export 'file_read'; 136 | sub file_read { 137 | decode_utf8(do { local $/; <> }); 138 | } 139 | 140 | export 'debug'; 141 | sub debug { 142 | my ($msg) = @_; 143 | warn ">>> $msg\n"; 144 | } 145 | 146 | export 'debug_rule'; 147 | sub debug_rule { 148 | my ($name, @args) = @_; 149 | my $args = join ',', map stringify($_), @args; 150 | debug "$name($args)"; 151 | } 152 | 153 | export qw< WWW XXX YYY ZZZ www xxx yyy zzz >; 154 | *www = \&www; 155 | *xxx = \&xxx; 156 | *yyy = \&yyy; 157 | *zzz = \&zzz; 158 | 159 | export 'dump'; 160 | sub dump { 161 | YAML::PP::Perl->new->dump(@_); 162 | } 163 | 164 | export 'FAIL'; 165 | sub FAIL { 166 | WWW [@_]; 167 | confess "FAIL '${\ $_[0] // '???'}'"; 168 | } 169 | 170 | export 'timer'; 171 | sub timer { 172 | if (@_) { 173 | tv_interval(shift); 174 | } 175 | else { 176 | [gettimeofday]; 177 | } 178 | } 179 | 180 | 1; 181 | 182 | # vim: sw=2: 183 | -------------------------------------------------------------------------------- /parser-1.2/perl/lib/Receiver.pm: -------------------------------------------------------------------------------- 1 | use v5.12; 2 | package Receiver; 3 | use Prelude; 4 | 5 | sub stream_start_event { 6 | { event => 'stream_start' }; 7 | } 8 | sub stream_end_event { 9 | { event => 'stream_end' }; 10 | } 11 | sub document_start_event { 12 | { event => 'document_start', explicit => (shift || false), version => undef }; 13 | } 14 | sub document_end_event { 15 | { event => 'document_end', explicit => (shift || false) }; 16 | } 17 | sub mapping_start_event { 18 | { event => 'mapping_start', flow => (shift || false) }; 19 | } 20 | sub mapping_end_event { 21 | { event => 'mapping_end' }; 22 | } 23 | sub sequence_start_event { 24 | { event => 'sequence_start', flow => (shift || false) }; 25 | } 26 | sub sequence_end_event { 27 | { event => 'sequence_end' }; 28 | } 29 | sub scalar_event { 30 | my ($style, $value) = @_; 31 | { event => 'scalar', style => $style, value => $value }; 32 | } 33 | sub alias_event { 34 | { event => 'alias', name => (shift) }; 35 | } 36 | sub cache { 37 | { text => (shift) }; 38 | } 39 | 40 | sub new { 41 | my ($class, %self) = @_; 42 | bless { 43 | %self, 44 | event => [], 45 | cache => [], 46 | }, $class; 47 | } 48 | 49 | sub send { 50 | my ($self, $event) = @_; 51 | if (my $callback = $self->{callback}) { 52 | $callback->($event); 53 | } 54 | else { 55 | push @{$self->{event}}, $event; 56 | } 57 | } 58 | 59 | sub add { 60 | my ($self, $event) = @_; 61 | if (defined $event->{event}) { 62 | if (my $anchor = $self->{anchor}) { 63 | $event->{anchor} = delete $self->{anchor}; 64 | } 65 | if (my $tag = $self->{tag}) { 66 | $event->{tag} = delete $self->{tag}; 67 | } 68 | } 69 | $self->push($event); 70 | return $event; 71 | } 72 | 73 | sub push { 74 | my ($self, $event) = @_; 75 | if (@{$self->{cache}}) { 76 | push @{$self->{cache}[-1]}, $event; 77 | } 78 | else { 79 | if ($event->{event} =~ /(mapping_start|sequence_start|scalar)/) { 80 | $self->check_document_start; 81 | } 82 | $self->send($event); 83 | } 84 | } 85 | 86 | sub cache_up { 87 | my ($self, $event) = @_; 88 | CORE::push @{$self->{cache}}, []; 89 | $self->add($event) if $event; 90 | } 91 | 92 | sub cache_down { 93 | my ($self, $event) = @_; 94 | my $events = pop @{$self->{cache}} or FAIL 'cache_down'; 95 | $self->push($_) for @$events; 96 | $self->add($event) if $event; 97 | } 98 | 99 | sub cache_drop { 100 | my ($self) = @_; 101 | my $events = pop @{$self->{cache}} or FAIL 'cache_drop'; 102 | return $events; 103 | } 104 | 105 | sub cache_get { 106 | my ($self, $type) = @_; 107 | return 108 | $self->{cache}[-1] && 109 | $self->{cache}[-1][0] && 110 | $self->{cache}[-1][0]{event} eq $type && 111 | $self->{cache}[-1][0]; 112 | } 113 | 114 | sub check_document_start { 115 | my ($self) = @_; 116 | return unless $self->{document_start}; 117 | $self->send($self->{document_start}); 118 | delete $self->{document_start}; 119 | $self->{document_end} = document_end_event; 120 | } 121 | 122 | sub check_document_end { 123 | my ($self) = @_; 124 | return unless $self->{document_end}; 125 | $self->send($self->{document_end}); 126 | delete $self->{document_end}; 127 | $self->{tag_map} = {}; 128 | $self->{document_start} = document_start_event; 129 | } 130 | 131 | #------------------------------------------------------------------------------ 132 | sub try__l_yaml_stream { 133 | my ($self) = @_; 134 | $self->add(stream_start_event); 135 | $self->{tag_map} = {}; 136 | $self->{document_start} = document_start_event; 137 | delete $self->{document_end}; 138 | } 139 | sub got__l_yaml_stream { 140 | my ($self) = @_; 141 | $self->check_document_end; 142 | $self->add(stream_end_event); 143 | } 144 | 145 | sub got__ns_yaml_version { 146 | my ($self, $o) = @_; 147 | die "Multiple %YAML directives not allowed" 148 | if defined $self->{document_start}{version}; 149 | $self->{document_start}{version} = $o->{text}; 150 | } 151 | 152 | sub got__c_tag_handle { 153 | my ($self, $o) = @_; 154 | $self->{tag_handle} = $o->{text}; 155 | } 156 | sub got__ns_tag_prefix { 157 | my ($self, $o) = @_; 158 | $self->{tag_map}{$self->{tag_handle}} = $o->{text}; 159 | } 160 | 161 | sub got__c_directives_end { 162 | my ($self) = @_; 163 | $self->check_document_end; 164 | $self->{document_start}{explicit} = true; 165 | } 166 | sub got__c_document_end { 167 | my ($self) = @_; 168 | $self->{document_end}{explicit} = true 169 | if defined $self->{document_end}; 170 | $self->check_document_end; 171 | } 172 | 173 | sub got__c_flow_mapping__all__x7b { $_[0]->add(mapping_start_event(true)) } 174 | sub got__c_flow_mapping__all__x7d { $_[0]->add(mapping_end_event) } 175 | 176 | sub got__c_flow_sequence__all__x5b { $_[0]->add(sequence_start_event(true)) } 177 | sub got__c_flow_sequence__all__x5d { $_[0]->add(sequence_end_event) } 178 | 179 | sub try__l_block_mapping { $_[0]->cache_up(mapping_start_event) } 180 | sub got__l_block_mapping { $_[0]->cache_down(mapping_end_event) } 181 | sub not__l_block_mapping { $_[0]->cache_drop } 182 | 183 | sub try__l_block_sequence { $_[0]->cache_up(sequence_start_event) } 184 | sub got__l_block_sequence { $_[0]->cache_down(sequence_end_event) } 185 | sub not__l_block_sequence { 186 | my ($self) = @_; 187 | my $event = $_[0]->cache_drop->[0]; 188 | $self->{anchor} = $event->{anchor}; 189 | $self->{tag} = $event->{tag}; 190 | } 191 | 192 | sub try__ns_l_compact_mapping { $_[0]->cache_up(mapping_start_event) } 193 | sub got__ns_l_compact_mapping { $_[0]->cache_down(mapping_end_event) } 194 | sub not__ns_l_compact_mapping { $_[0]->cache_drop } 195 | 196 | sub try__ns_l_compact_sequence { $_[0]->cache_up(sequence_start_event) } 197 | sub got__ns_l_compact_sequence { $_[0]->cache_down(sequence_end_event) } 198 | sub not__ns_l_compact_sequence { $_[0]->cache_drop } 199 | 200 | sub try__ns_flow_pair { $_[0]->cache_up(mapping_start_event(true)) } 201 | sub got__ns_flow_pair { $_[0]->cache_down(mapping_end_event) } 202 | sub not__ns_flow_pair { $_[0]->cache_drop } 203 | 204 | sub try__ns_l_block_map_implicit_entry { $_[0]->cache_up } 205 | sub got__ns_l_block_map_implicit_entry { $_[0]->cache_down } 206 | sub not__ns_l_block_map_implicit_entry { $_[0]->cache_drop } 207 | 208 | sub try__c_l_block_map_explicit_entry { $_[0]->cache_up } 209 | sub got__c_l_block_map_explicit_entry { $_[0]->cache_down } 210 | sub not__c_l_block_map_explicit_entry { $_[0]->cache_drop } 211 | 212 | sub try__c_ns_flow_map_empty_key_entry { $_[0]->cache_up } 213 | sub got__c_ns_flow_map_empty_key_entry { $_[0]->cache_down } 214 | sub not__c_ns_flow_map_empty_key_entry { $_[0]->cache_drop } 215 | 216 | sub got__ns_plain { 217 | my ($self, $o) = @_; 218 | my $text = $o->{text}; 219 | $text =~ s/(?:[\ \t]*\r?\n[\ \t]*)/\n/g; 220 | $text =~ s/(\n)(\n*)/length($2) ? $2 : ' '/ge; 221 | $self->add(scalar_event(plain => $text)); 222 | } 223 | 224 | sub got__c_single_quoted { 225 | my ($self, $o) = @_; 226 | my $text = substr($o->{text}, 1, -1); 227 | $text =~ s/(?:[\ \t]*\r?\n[\ \t]*)/\n/g; 228 | $text =~ s/(\n)(\n*)/length($2) ? $2 : ' '/ge; 229 | $text =~ s/''/'/g; 230 | $self->add(scalar_event(single => $text)); 231 | } 232 | 233 | sub got__c_double_quoted { 234 | my ($self, $o) = @_; 235 | my $text = substr($o->{text}, 1, -1); 236 | $text =~ s/(?:(?add(scalar_event(double => $text)); 251 | } 252 | 253 | sub got__l_empty { 254 | my ($self) = @_; 255 | $self->add(cache '') if $self->{in_scalar}; 256 | } 257 | sub got__l_nb_literal_text__all__rep2 { 258 | my ($self, $o) = @_; 259 | $self->add(cache $o->{text}); 260 | } 261 | sub try__c_l_literal { 262 | my ($self) = @_; 263 | $self->{in_scalar} = true; 264 | $self->cache_up; 265 | } 266 | sub got__c_l_literal { 267 | my ($self) = @_; 268 | delete $self->{in_scalar}; 269 | my $lines = $self->cache_drop; 270 | pop @$lines if @$lines and $lines->[-1]{text} eq ''; 271 | my $text = join '', map "$_->{text}\n", @$lines; 272 | my $t = $self->{parser}->state_curr->{t}; 273 | if ($t eq 'clip') { 274 | $text =~ s/\n+\z/\n/; 275 | } 276 | elsif ($t eq 'strip') { 277 | $text =~ s/\n+\z//; 278 | } 279 | elsif ($text !~ /\S/) { 280 | $text =~ s/\n(\n+)\z/$1/; 281 | } 282 | $self->add(scalar_event(literal => $text)); 283 | } 284 | sub not__c_l_literal { 285 | my ($self) = @_; 286 | delete $self->{in_scalar}; 287 | $_[0]->cache_drop; 288 | } 289 | 290 | sub got__ns_char { 291 | my ($self, $o) = @_; 292 | $self->{first} = $o->{text} if $self->{in_scalar}; 293 | } 294 | sub got__s_white { 295 | my ($self, $o) = @_; 296 | $self->{first} = $o->{text} if $self->{in_scalar}; 297 | } 298 | sub got__s_nb_folded_text__all__rep { 299 | my ($self, $o) = @_; 300 | $self->add(cache "$self->{first}$o->{text}"); 301 | } 302 | sub got__s_nb_spaced_text__all__rep { 303 | my ($self, $o) = @_; 304 | $self->add(cache "$self->{first}$o->{text}"); 305 | } 306 | sub try__c_l_folded { 307 | my ($self) = @_; 308 | $self->{in_scalar} = true; 309 | $self->{first} = ''; 310 | $self->cache_up; 311 | } 312 | sub got__c_l_folded { 313 | my ($self) = @_; 314 | delete $self->{in_scalar}; 315 | 316 | my @lines = map $_->{text}, @{$self->cache_drop}; 317 | my $text = join "\n", @lines; 318 | $text =~ s/^(\S.*)\n(?=\S)/$1 /gm; 319 | $text =~ s/^(\S.*)\n(\n+)/$1$2/gm; 320 | $text =~ s/^([\ \t]+\S.*)\n(\n+)(?=\S)/$1$2/gm; 321 | $text .= "\n"; 322 | 323 | my $t = $self->{parser}->state_curr->{t}; 324 | if ($t eq 'clip') { 325 | $text =~ s/\n+\z/\n/; 326 | $text = '' if $text eq "\n"; 327 | } 328 | elsif ($t eq 'strip') { 329 | $text =~ s/\n+\z//; 330 | } 331 | $self->add(scalar_event(folded => $text)); 332 | } 333 | sub not__c_l_folded { 334 | my ($self) = @_; 335 | delete $self->{in_scalar}; 336 | $_[0]->cache_drop; 337 | } 338 | 339 | sub got__e_scalar { $_[0]->add(scalar_event(plain => '')) } 340 | 341 | sub not__s_l_block_collection__all__rep__all__any__all { 342 | my ($self) = @_; 343 | delete $self->{anchor}; 344 | delete $self->{tag}; 345 | } 346 | 347 | sub got__c_ns_anchor_property { 348 | my ($self, $o) = @_; 349 | $self->{anchor} = substr($o->{text}, 1); 350 | } 351 | 352 | sub got__c_ns_tag_property { 353 | my ($self, $o) = @_; 354 | my $tag = $o->{text}; 355 | my $prefix; 356 | if ($tag =~ /^!<(.*)>$/) { 357 | $self->{tag} = $1; 358 | } 359 | elsif ($tag =~ /^!!(.*)/) { 360 | if (defined($prefix = $self->{tag_map}{'!!'})) { 361 | $self->{tag} = $prefix . substr($tag, 2); 362 | } 363 | else { 364 | $self->{tag} = "tag:yaml.org,2002:$1"; 365 | } 366 | } 367 | elsif ($tag =~ /^(!.*?!)/) { 368 | $prefix = $self->{tag_map}{$1}; 369 | if (defined $prefix) { 370 | $self->{tag} = $prefix . substr($tag, length($1)); 371 | } 372 | else { 373 | die "No %TAG entry for '$prefix'"; 374 | } 375 | } 376 | elsif (defined($prefix = $self->{tag_map}{'!'})) { 377 | $self->{tag} = $prefix . substr($tag, 1); 378 | } 379 | else { 380 | $self->{tag} = $tag; 381 | } 382 | $self->{tag} =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/eg; 383 | } 384 | 385 | sub got__c_ns_alias_node { 386 | my ($self, $o) = @_; 387 | my $name = $o->{text}; 388 | $name =~ s/^\*//; 389 | $self->add(alias_event($name)); 390 | } 391 | 392 | 1; 393 | 394 | # vim: sw=2: 395 | -------------------------------------------------------------------------------- /parser-1.2/perl/lib/TestReceiver.pm: -------------------------------------------------------------------------------- 1 | use v5.12; 2 | package TestReceiver; 3 | use base 'Receiver'; 4 | 5 | use Prelude; 6 | 7 | my $event_map = { 8 | stream_start => '+STR', 9 | stream_end => '-STR', 10 | document_start => '+DOC', 11 | document_end => '-DOC', 12 | mapping_start => '+MAP', 13 | mapping_end => '-MAP', 14 | sequence_start => '+SEQ', 15 | sequence_end => '-SEQ', 16 | scalar => '=VAL', 17 | alias => '=ALI', 18 | }; 19 | 20 | my $style_map = { 21 | plain => ':', 22 | single => "'", 23 | double => '"', 24 | literal => '|', 25 | folded => '>', 26 | }; 27 | 28 | sub output { 29 | my ($self) = @_; 30 | join '', map { 31 | my $type = $event_map->{$_->{event}}; 32 | my @event = ($type); 33 | push @event, '---' if $type eq '+DOC' and $_->{explicit}; 34 | push @event, '...' if $type eq '-DOC' and $_->{explicit}; 35 | push @event, '{}' if $type eq '+MAP' and $_->{flow}; 36 | push @event, '[]' if $type eq '+SEQ' and $_->{flow}; 37 | push @event, "&$_->{anchor}" if $_->{anchor}; 38 | push @event, "<$_->{tag}>" if $_->{tag}; 39 | push @event, "*$_->{name}" if $_->{name}; 40 | if (exists $_->{value}) { 41 | my $style = $style_map->{$_->{style}}; 42 | my $value = $_->{value}; 43 | $value =~ s/\\/\\\\/g; 44 | $value =~ s/\x08/\\b/g; 45 | $value =~ s/\t/\\t/g; 46 | $value =~ s/\n/\\n/g; 47 | $value =~ s/\r/\\r/g; 48 | push @event, "$style$value"; 49 | } 50 | join(' ', @event) . "\n"; 51 | } @{$self->{event}}; 52 | } 53 | 54 | 1; 55 | 56 | # vim: sw=2: 57 | -------------------------------------------------------------------------------- /parser-1.2/perl/test/TestMLBridge.pm: -------------------------------------------------------------------------------- 1 | use v5.12; 2 | package TestMLBridge; 3 | use base 'TestML::Bridge'; 4 | use utf8; 5 | 6 | use lib 'lib'; 7 | 8 | use Prelude; 9 | use Parser; 10 | use TestReceiver; 11 | 12 | sub parse { 13 | my ($self, $yaml, $expect_error) = @_; 14 | 15 | my $parser = Parser->new(TestReceiver->new); 16 | 17 | eval { $parser->parse($yaml) }; 18 | my $error = $@; 19 | 20 | if (defined $expect_error) { 21 | return $error ? 1 : 0; 22 | } 23 | 24 | return $error 25 | ? do { warn $error; '' } 26 | : $parser->{receiver}->output; 27 | } 28 | 29 | sub unescape { 30 | my ($self, $text) = @_; 31 | 32 | $text =~ s/␣/ /g; 33 | $text =~ s/—*»/\t/g; 34 | $text =~ s/⇔/x{FEFF}/g; 35 | $text =~ s/↵//g; 36 | $text =~ s/∎\n\z//; 37 | 38 | # $text =~ s/↓/\r/g; 39 | 40 | return $text; 41 | } 42 | 43 | sub fix_test_output { 44 | my ($self, $text) = @_; 45 | return $text; 46 | } 47 | 48 | 1; 49 | -------------------------------------------------------------------------------- /parser-1.2/perl/test/diff-trace.tml: -------------------------------------------------------------------------------- 1 | ../../../test/diff-trace.tml -------------------------------------------------------------------------------- /parser-1.2/perl/test/error-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/error-suite.tml -------------------------------------------------------------------------------- /parser-1.2/perl/test/parse-block.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-block.tml -------------------------------------------------------------------------------- /parser-1.2/perl/test/parse-flow.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-flow.tml -------------------------------------------------------------------------------- /parser-1.2/perl/test/parse-props.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-props.tml -------------------------------------------------------------------------------- /parser-1.2/perl/test/parse-scalar.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-scalar.tml -------------------------------------------------------------------------------- /parser-1.2/perl/test/parse-stream.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-stream.tml -------------------------------------------------------------------------------- /parser-1.2/perl/test/parse-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-suite.tml -------------------------------------------------------------------------------- /parser-1.3/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := bash 2 | 3 | ROOT := $(shell cd ..; pwd) 4 | 5 | ALL := \ 6 | javascript \ 7 | 8 | # ALL_BUILD := $(ALL:%=build-%) 9 | ALL_TEST := $(ALL:%=test-%) 10 | ALL_CLEAN := $(ALL:%=clean-%) 11 | 12 | default: 13 | 14 | # build: $(ALL_BUILD) 15 | 16 | # build-%: 17 | # $(MAKE) -C $(@:build-%=%) build 18 | 19 | test: $(ALL_TEST) 20 | 21 | test-%: 22 | $(MAKE) -C $(@:test-%=%) test TRACE=$(TRACE) DEBUG=$(DEBUG) 23 | 24 | clean: $(ALL_CLEAN) 25 | $(MAKE) -C $(ROOT)/test $@ 26 | 27 | clean-%: 28 | $(MAKE) -C $(@:clean-%=%) clean 29 | -------------------------------------------------------------------------------- /parser-1.3/coffeescript/Makefile: -------------------------------------------------------------------------------- 1 | ROOT := $(shell cd ../..; pwd) 2 | BASE := $(ROOT)/parser-1.3 3 | PARSER_LANG := coffeescript 4 | BIN := coffee 5 | GRAMMAR := 6 | 7 | 8 | include $(ROOT)/.common.mk 9 | -------------------------------------------------------------------------------- /parser-1.3/coffeescript/bin/yaml-parser: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | 3 | require '../lib/prelude' 4 | require '../lib/parser' 5 | require '../lib/test-receiver' 6 | 7 | events = false 8 | 9 | main = (yaml=file_read('-'))-> 10 | 11 | parser = new Parser(new TestReceiver) 12 | 13 | pass = true 14 | start = timer() 15 | 16 | try 17 | parser.parse(yaml) 18 | catch e 19 | warn e 20 | pass = false 21 | 22 | time = timer(start) 23 | 24 | if yaml.match /\n./ 25 | n = "\n" 26 | else 27 | n = '' 28 | yaml = yaml.replace /\n$/, '\\n' 29 | 30 | if events 31 | out parser.receiver.output() 32 | return true 33 | 34 | if process.env.STATS 35 | sorted = {} 36 | {calls} = parser.stats 37 | for k in Object.keys(calls).sort((a,b)-> calls[a] - calls[b]) 38 | sorted[k] = calls[k] 39 | WWW sorted 40 | 41 | if pass 42 | say "PASS - '#{n}#{yaml}'" 43 | say parser.receiver.output 44 | say sprintf "Parse time %.5fs", time 45 | return true 46 | else 47 | say "FAIL - '#{n}#{yaml}'" 48 | say parser.receiver.output 49 | say sprintf "Parse time %.5fs", time 50 | return false 51 | 52 | argv = process.argv[2..] 53 | 54 | if argv.length and argv[0] == '--events' 55 | events = true 56 | argv.shift() 57 | 58 | if main argv... 59 | exit 0 60 | else 61 | exit 1 62 | 63 | # vim: sw=2: 64 | -------------------------------------------------------------------------------- /parser-1.3/coffeescript/lib/parser.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | This is a parser class. It has a parse() method and parsing primitives for the 3 | grammar. It calls methods in the receiver class, when a rule matches: 4 | ### 5 | 6 | require './prelude' 7 | require './grammar' 8 | 9 | DEBUG = Boolean ENV.DEBUG 10 | TRACE = Boolean ENV.TRACE 11 | STATS = Boolean ENV.STATS 12 | 13 | global.Parser = class Parser extends Grammar 14 | 15 | stats: 16 | calls: {} 17 | 18 | constructor: (receiver)-> 19 | super() 20 | receiver.parser = @ 21 | @receiver = receiver 22 | @pos = 0 23 | @end = 0 24 | @state = [] 25 | 26 | if DEBUG or TRACE or STATS 27 | @call = @call_debug 28 | @trace_num = 0 29 | @trace_line = 0 30 | @trace_on = true 31 | @trace_off = 0 32 | @trace_info = ['', '', ''] 33 | 34 | make_method = (name, method)-> 35 | (a...)-> 36 | fn1 = method.call(@, a...) 37 | if isFunction fn1 38 | fn2 = -> fn1 39 | Object.defineProperty(fn2, 'name', value: fn1.name) 40 | Object.defineProperty(fn1, 'name', value: name) 41 | return fn2 42 | return fn1 43 | 44 | for method_name in Object.getOwnPropertyNames(Grammar::) 45 | continue if method_name is 'constructor' 46 | if (f = String(Grammar::[method_name])).match(/^\w+\(\)/) 47 | continue 48 | Grammar::[method_name] = make_method(method_name, Grammar::[method_name]) 49 | 50 | parse: (@input)-> 51 | @input += "\n" unless \ 52 | @input.length == 0 or 53 | @input.endsWith("\n") 54 | 55 | @end = @input.length 56 | 57 | if TRACE 58 | @trace_on = not @trace_start() 59 | 60 | try 61 | ok = @call @TOP 62 | if TRACE 63 | @trace_flush() 64 | catch err 65 | if TRACE 66 | @trace_flush() 67 | throw err 68 | 69 | throw "Parser failed" if not ok 70 | throw "Parser finished before end of input" \ 71 | if @pos < @end 72 | 73 | return true 74 | 75 | state_curr: -> 76 | @state[@state.length - 1] || 77 | name: null 78 | doc: false 79 | lvl: 0 80 | beg: 0 81 | end: 0 82 | m: null 83 | t: null 84 | 85 | state_prev: -> 86 | @state[@state.length - 2] 87 | 88 | state_push: (name)-> 89 | curr = @state_curr() 90 | 91 | @state.push 92 | name: name 93 | doc: curr.doc 94 | lvl: curr.lvl + 1 95 | beg: @pos 96 | end: null 97 | m: curr.m 98 | t: curr.t 99 | 100 | state_pop: -> 101 | child = @state.pop() 102 | if (curr = @state_curr())? 103 | curr.beg = child.beg 104 | curr.end = @pos 105 | 106 | # This is the dispatch function that calls all the grammar functions. 107 | # It should be as fast as possible. 108 | call: (func)-> 109 | if func instanceof Array 110 | [func, args...] = func 111 | else 112 | args = [] 113 | 114 | @state_push(func.name) 115 | 116 | args = args.map (a)-> 117 | if typeof(a) == 'function' 118 | a() 119 | else 120 | a 121 | 122 | value = func.apply(@, args) 123 | while value instanceof Object 124 | value = @call value 125 | 126 | @state_pop() 127 | 128 | return value 129 | 130 | # To make the 'call' method as fast as possible, a debugging version of it 131 | # is here. 132 | call_debug: (func)-> 133 | if func instanceof Array 134 | [func, args...] = func 135 | else 136 | args = [] 137 | XXX func if func == Grammar::block_node 138 | 139 | do => 140 | FAIL "Bad call type '#{typeof_ func}' for '#{func}'" \ 141 | unless isFunction func 142 | 143 | @state_push(func.name) 144 | 145 | trace = func.trace ?= func.name 146 | 147 | if STATS 148 | @stats.calls[trace] ?= 0 149 | @stats.calls[trace]++ 150 | 151 | if TRACE 152 | @trace_num++ 153 | @trace '?', trace, args 154 | 155 | args = args.map (a)-> 156 | if typeof(a) == 'function' 157 | a() 158 | else 159 | a 160 | 161 | if DEBUG && func.name.match /_\w/ 162 | debug_rule func.name, args... 163 | 164 | value = func.apply(@, args) 165 | while value instanceof Object 166 | value = @call value 167 | 168 | if TRACE 169 | @trace_num++ 170 | if value 171 | @trace '+', trace 172 | else 173 | @trace 'x', trace 174 | 175 | @state_pop() 176 | 177 | return value 178 | 179 | not: (rule, {name} = {})-> 180 | @got(rule, name: name, not_: true, got_: false) 181 | 182 | got: (rule, {name, try_, got_, not_} = {})-> 183 | name ?= ((new Error().stack).match(/at Parser.(\w+?_\w+) \(/))[1] 184 | try_ ?= false 185 | not_ ?= false 186 | got_ ?= !not_ 187 | 188 | if try_ 189 | try_func = @receiver.constructor.prototype["try_#{name}"] or 190 | die "@receiver.try_#{name} not defined" 191 | if got_ 192 | got_func = @receiver.constructor.prototype["got_#{name}"] or 193 | die "@receiver.got_#{name} not defined" 194 | if not_ 195 | not_func = @receiver.constructor.prototype["not_#{name}"] or 196 | die "@receiver.not_#{name} not defined" 197 | 198 | got = => 199 | pos = @pos 200 | 201 | context = 202 | text: @input[pos...@pos] 203 | state: @state_curr() 204 | start: pos 205 | 206 | if try_ 207 | try_func.call(@receiver, context) 208 | 209 | value = @call(rule) 210 | 211 | context.text = @input[pos...@pos] 212 | 213 | if value 214 | if got_ 215 | got_func.call(@receiver, context) 216 | else 217 | if not_ 218 | not_func.call(@receiver, context) 219 | 220 | return value 221 | 222 | 223 | 224 | # Match all subrule methods: 225 | all: (funcs...)-> 226 | all = -> 227 | pos = @pos 228 | for func in funcs 229 | FAIL "*** Missing function in @all group: #{func}" \ 230 | unless func? 231 | 232 | if not @call func 233 | @pos = pos 234 | return false 235 | 236 | return true 237 | 238 | # Match any subrule method. Rules are tried in order and stops on first 239 | # match: 240 | any: (funcs...)-> 241 | any = -> 242 | for func in funcs 243 | if @call func 244 | return true 245 | 246 | return false 247 | 248 | # Repeat a rule a certain number of times: 249 | rep: (quant, func)-> 250 | switch quant 251 | when '*' then [min, max] = [0, Infinity] 252 | when '?' then [min, max] = [0, 1] 253 | when '+' then [min, max] = [1, Infinity] 254 | 255 | rep = -> 256 | count = 0 257 | pos = @pos 258 | pos_start = pos 259 | while count < max 260 | break unless @call func 261 | break if @pos == pos 262 | count++ 263 | pos = @pos 264 | if count >= min and count <= max 265 | return true 266 | @pos = pos_start 267 | return false 268 | name_ 'rep', rep, "rep(#{quant})" 269 | 270 | # Check for end for doc or stream: 271 | the_end: -> 272 | return ( 273 | @pos >= @end or ( 274 | @state_curr().doc and ( 275 | @pos == 0 or 276 | @input[@pos - 1] == "\n" 277 | ) and 278 | @input[@pos..].match /^(?:---|\.\.\.)(?=\s|$)/ 279 | ) 280 | ) 281 | 282 | make = memoize (regex)-> 283 | on_end = !! regex.match(/\)[\?\*]\/[muy]*$/) 284 | die_ regex if regex.match(/y$/) 285 | regex = regex[0..-2] if regex.endsWith('u') 286 | regex = regex 287 | .replace(/\(([:!=]|<=)/g, '(?$1') 288 | regex = String(regex)[1..-2] 289 | .replace(/\((?!\?)/g, '(?:') 290 | regex = /// (?: #{regex} ) ///yum 291 | return [regex, on_end] 292 | 293 | # Match a regex: 294 | rgx: (regex, name)-> 295 | regex = /// #{regex} ///u unless isRegex regex 296 | regex = String(regex) 297 | [regex, on_end] = make(regex) 298 | 299 | rgx = -> 300 | return on_end if @the_end() 301 | regex.lastIndex = @pos 302 | if m = @input.match(regex) 303 | @pos += m[0].length 304 | return true 305 | return false 306 | if TRACE 307 | name ?= ((new Error().stack).match(/at Parser.(\w+?_\w+) \(/))[1] 308 | name_ 'rgx', rgx, "rgx('#{name}' #{stringify regex})" 309 | 310 | # Match a single char: 311 | chr: (char)-> 312 | chr = -> 313 | return false if @the_end() 314 | if @input[@pos] == char 315 | @pos++ 316 | return true 317 | return false 318 | name_ 'chr', chr, "chr(#{stringify char})" 319 | 320 | # Match a char in a range: 321 | rng: (low, high)-> 322 | rng = -> 323 | return false if @the_end() 324 | if @input[@pos..].match(new RegExp "^[#{low}-#{high}]", 'u') 325 | @pos++ if @input[@pos..].codePointAt(0) > 65535 326 | @pos++ 327 | return true 328 | return false 329 | name_ 'rng', rng, "rng(#{stringify(low)},#{stringify(high)})" 330 | 331 | # Must match first rule but none of others: 332 | but: (funcs...)-> 333 | but = -> 334 | return false if @the_end() 335 | pos1 = @pos 336 | return false unless @call funcs[0] 337 | pos2 = @pos 338 | @pos = pos1 339 | for func in funcs[1..] 340 | if @call func 341 | @pos = pos1 342 | return false 343 | @pos = pos2 344 | return true 345 | 346 | chk: (type, expr)-> 347 | chk = -> 348 | pos = @pos 349 | @pos-- if type == '<=' 350 | ok = @call expr 351 | @pos = pos 352 | return if type == '!' then not(ok) else ok 353 | name_ 'chk', chk, "chk(#{type}, #{stringify expr})" 354 | 355 | set: (var_, expr)-> 356 | set = => 357 | if isString expr 358 | value = expr 359 | else 360 | value = @call expr 361 | return false if value == -1 362 | state = @state_prev() 363 | state[var_] = value 364 | if state.name != 'all' 365 | size = @state.length 366 | i = 3 367 | while i < size 368 | FAIL "failed to traverse state stack in 'set'" \ 369 | if i > size - 1 370 | state = @state[size - i++ - 1] 371 | state[var_] = value 372 | break if state.name == 'block_scalar' 373 | return true 374 | name_ 'set', set, "set('#{var_}', #{stringify expr})" 375 | 376 | # max: (max)-> 377 | # max = -> 378 | # return true 379 | 380 | exclude: (rule)-> 381 | exclude = -> true 382 | 383 | # This method does not need to return a function since it is never 384 | # called in the grammar. 385 | match: -> 386 | state = @state 387 | i = state.length - 1 388 | while i > 0 && not state[i].end? 389 | FAIL "Can't find match" if i == 1 390 | i-- 391 | 392 | {beg, end} = state[i] 393 | return @input[beg...end] 394 | 395 | len: (str)-> 396 | len = -> 397 | str = @call str unless isString str 398 | return str.length 399 | 400 | ord: (str)-> 401 | ord = -> 402 | str = @call str unless isString str 403 | return str.charCodeAt(0) - 48 404 | 405 | if: (test, do_if_true, do_if_false)-> 406 | if_ = -> 407 | test = @call test unless isBoolean test 408 | if test 409 | @call do_if_true 410 | return true 411 | return false 412 | name_ 'if', if_ 413 | 414 | lt: (x, y)-> 415 | lt = -> 416 | x = @call x unless isNumber x 417 | y = @call y unless isNumber y 418 | return x < y 419 | name_ 'lt', lt, "lt(#{stringify x},#{stringify y})" 420 | 421 | le: (x, y)-> 422 | le = -> 423 | x = @call x unless isNumber x 424 | y = @call y unless isNumber y 425 | return x <= y 426 | name_ 'le', le, "le(#{stringify x},#{stringify y})" 427 | 428 | m: (n=0)-> 429 | m = => 430 | return @state_curr().m + n 431 | 432 | t: -> 433 | t = => 434 | return @state_curr().t 435 | 436 | #------------------------------------------------------------------------------ 437 | # Special grammar rules 438 | #------------------------------------------------------------------------------ 439 | empty: -> true 440 | 441 | auto_detect_indent: (n)-> 442 | pos = @pos 443 | in_seq = (pos > 0 and @input[pos - 1].match /^[\-\?\:]$/) 444 | match = @input[pos..].match ///^ 445 | ( 446 | (?: 447 | \ * 448 | (?:\#.*)? 449 | \n 450 | )* 451 | ) 452 | (\ *) 453 | /// or FAIL "auto_detect_indent" 454 | pre = match[1] 455 | m = match[2].length 456 | if in_seq and not pre.length 457 | m++ if n == -1 458 | else 459 | m -= n 460 | m = 0 if m < 0 461 | return m 462 | 463 | auto_detect: (n)-> 464 | match = @input[@pos..].match /// 465 | ^.*\n 466 | ( 467 | (?:\ *\n)* 468 | ) 469 | (\ *) 470 | (.?) 471 | /// 472 | pre = match[1] 473 | if match[3].length 474 | m = match[2].length - n 475 | else 476 | m = 0 477 | while pre.match ///\ {#{m}}/// 478 | m++ 479 | m = m - n - 1 480 | die "Spaces found after indent in auto-detect (5LLU)" \ 481 | if m > 0 && pre.match ///^.{#{m + n}}\ ///m 482 | return if m == 0 then 1 else m 483 | 484 | #------------------------------------------------------------------------------ 485 | # Trace debugging 486 | #------------------------------------------------------------------------------ 487 | trace_start: -> 488 | '' || ENV.TRACE_START 489 | 490 | trace_quiet: -> 491 | return [] 492 | return [] if DEBUG 493 | 494 | small = [ 495 | 'b_as_line_feed', 496 | 's_indent', 497 | 'non_break_character', 498 | ] 499 | 500 | noisy = [ 501 | 'document_start_indicator', 502 | 'block_folded_scalar', 503 | 'block_literal_scalar', 504 | 'c_ns_alias_node', 505 | 'c_ns_anchor_property', 506 | 'c_ns_tag_property', 507 | 'directives_and_document', 508 | 'document_prefix', 509 | 'flow_content', 510 | 'ns_plain', 511 | 'comment_lines', 512 | 'separation_characters', 513 | ] 514 | return ((ENV.TRACE_QUIET || '').split ',') 515 | .concat(noisy) 516 | 517 | trace: (type, call, args=[])-> 518 | call = String(call) unless isString call # XXX 519 | call = "'#{call}'" if call.match /^($| |.* $)/ 520 | 521 | return unless @trace_on or call == @trace_start() 522 | 523 | if call.startsWith 'rgx' 524 | call = call 525 | .replace(/\n/g, "\\n") 526 | .replace(/\r/g, "\\r") 527 | 528 | level = @state_curr().lvl 529 | indent = _.repeat ' ', level 530 | if level > 0 531 | l = "#{level}".length 532 | indent = "#{level}" + indent[l..] 533 | 534 | input = @input[@pos..] 535 | input = "#{input[0..30]}…" \ 536 | if input.length > 30 537 | input = input \ 538 | .replace(/\t/g, '\\t') 539 | .replace(/\r/g, '\\r') 540 | .replace(/\n/g, '\\n') 541 | 542 | line = sprintf( 543 | "%s%s %-40s %4d '%s'", 544 | indent, 545 | type, 546 | @trace_format_call call, args 547 | @pos, 548 | input, 549 | ) 550 | 551 | if DEBUG 552 | warn sprintf "%6d %s", 553 | @trace_num, line 554 | return 555 | 556 | trace_info = null 557 | level = "#{level}_#{call}" 558 | if type == '?' and @trace_off == 0 559 | trace_info = [type, level, line, @trace_num] 560 | if call in @trace_quiet() 561 | @trace_off += if type == '?' then 1 else -1 562 | if type != '?' and @trace_off == 0 563 | trace_info = [type, level, line, @trace_num] 564 | 565 | if trace_info? 566 | [prev_type, prev_level, prev_line, trace_num] = 567 | @trace_info 568 | if prev_type == '?' and prev_level == level 569 | trace_info[1] = '' 570 | if line.match /^\d*\ *\+/ 571 | prev_line = prev_line.replace /\?/, '=' 572 | else 573 | prev_line = prev_line.replace /\?/, '!' 574 | if prev_level 575 | warn sprintf "%5d %6d %s", 576 | ++@trace_line, trace_num, prev_line 577 | 578 | @trace_info = trace_info 579 | 580 | if call == @trace_start() 581 | @trace_on = not @trace_on 582 | 583 | trace_format_call: (call, args)-> 584 | return call unless args.length 585 | list = args.map (a)-> 586 | stringify a 587 | list = list.join ',' 588 | return "#{call}(#{list})" 589 | 590 | trace_flush: -> 591 | [type, level, line, count] = @trace_info 592 | if line 593 | warn sprintf "%5d %6d %s", 594 | ++@trace_line, count, line 595 | 596 | # vim: sw=2: 597 | -------------------------------------------------------------------------------- /parser-1.3/coffeescript/lib/prelude.coffee: -------------------------------------------------------------------------------- 1 | require 'ingy-prelude' 2 | 3 | Function::n = (name)-> 4 | Object.defineProperty(@, 'name', value: name) 5 | return @ 6 | 7 | Function::w = -> 8 | warn 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 9 | return XXX typeof @ 10 | 11 | global.ENV = process.env 12 | 13 | global.memoize = require('moize').maxSize(50) 14 | 15 | #------------------------------------------------------------------------------ 16 | # Grammar helper functions: 17 | #------------------------------------------------------------------------------ 18 | 19 | # Generate required regular expression and string variants: 20 | global.regx = (rgx)-> 21 | str = String(rgx) 22 | 23 | # XXX Can remove when stable: 24 | if str.match(/>>\d+< 46 | func.trace = trace || name 47 | if ENV.DEBUGXXX # Not working yet 48 | f = (n, args...)-> 49 | args = args.map (a)-> stringify(a) 50 | args = args.join '' 51 | debug "#{name}(#{args})" 52 | func.apply func 53 | f.name = name 54 | f.trace = trace || name 55 | return f 56 | 57 | func 58 | 59 | global.isNull = _.isNull 60 | global.isBoolean = _.isBoolean 61 | global.isNumber = _.isNumber 62 | global.isString = _.isString 63 | global.isRegex = _.isRegExp 64 | global.isFunction = _.isFunction 65 | global.isArray = _.isArray 66 | global.isObject = _.isPlainObject 67 | 68 | global.typeof_ = (value)-> 69 | return 'null' if _.isNull value 70 | return 'boolean' if _.isBoolean value 71 | return 'number' if _.isNumber value 72 | return 'string' if _.isString value 73 | return 'regex' if _.isRegex value 74 | return 'function' if _.isFunction value 75 | return 'array' if _.isArray value 76 | return 'object' if _.isPlainObject value 77 | xxx [value, typeof(value)] 78 | 79 | global.stringify = (o)-> 80 | if o == "\ufeff" 81 | return "\\uFEFF" 82 | if isRegex o 83 | return String(o) 84 | if isFunction o 85 | return "@#{o.trace || o.name}" 86 | if isObject o 87 | return JSON.stringify _.keys(o) 88 | if isArray o 89 | return "[#{(_.map o, (e)-> stringify e).join ','}]" 90 | return JSON.stringify(o).replace /^"(.*)"$/, '$1' 91 | 92 | global.hex_char = (chr)-> 93 | return chr.charCodeAt(0).toString(16) 94 | 95 | global.die_ = (msg)-> 96 | die((new Error().stack) + "\n" + msg) 97 | 98 | global.debug = (msg)-> 99 | warn ">>> #{msg}" 100 | 101 | global.debug_rule = (name, args...)-> 102 | return unless ENV.DEBUG 103 | args = _.join _.map args, (a)-> 104 | stringify(a) 105 | , ',' 106 | debug "#{name}(#{args})" 107 | 108 | global.dump = (o)-> 109 | require('yaml').stringify o 110 | 111 | global.FAIL = (o...)-> 112 | WWW o 113 | die_ "FAIL '#{o[0] || '???'}'" 114 | 115 | global.timer = (start=null)-> 116 | if start? 117 | time = process.hrtime(start) 118 | time[0] + time[1] / 1000000000 119 | else 120 | process.hrtime() 121 | 122 | # vim: sw=2: 123 | -------------------------------------------------------------------------------- /parser-1.3/coffeescript/lib/receiver.coffee: -------------------------------------------------------------------------------- 1 | require './prelude' 2 | 3 | stream_start_event = -> 4 | event: 'stream_start' 5 | stream_end_event = -> 6 | event: 'stream_end' 7 | document_start_event = (explicit=false)-> 8 | event: 'document_start' 9 | explicit: explicit 10 | version: null 11 | document_end_event = (explicit=false)-> 12 | event: 'document_end' 13 | explicit: explicit 14 | mapping_start_event = (flow=false)-> 15 | event: 'mapping_start' 16 | flow: flow 17 | mapping_end_event = -> 18 | event: 'mapping_end' 19 | sequence_start_event = (flow=false)-> 20 | event: 'sequence_start' 21 | flow: flow 22 | sequence_end_event = -> 23 | event: 'sequence_end' 24 | scalar_event = (style, value)-> 25 | event: 'scalar' 26 | style: style 27 | value: value 28 | alias_event = (name)-> 29 | event: 'alias' 30 | name: name 31 | cache = (text)-> 32 | text: text 33 | 34 | global.Receiver = class Receiver 35 | constructor: -> 36 | @event = [] 37 | @cache = [] 38 | 39 | send: (event)-> 40 | if @receive 41 | @receive event 42 | else 43 | @event.push event 44 | 45 | add: (event)-> 46 | if event.event? 47 | if @anchor? 48 | event.anchor = @anchor 49 | delete @anchor 50 | if @tag? 51 | event.tag = @tag 52 | delete @tag 53 | @push event 54 | return event 55 | 56 | 57 | push: (event)-> 58 | if @cache.length 59 | _.last(@cache).push event 60 | else 61 | if event.event.match /(mapping_start|sequence_start|scalar)/ 62 | @check_document_start() 63 | @send event 64 | 65 | cache_up: (event=null)-> 66 | @cache.push [] 67 | @add event if event? 68 | 69 | cache_down: (event=null)-> 70 | events = @cache.pop() or FAIL 'cache_down' 71 | @push e for e in events 72 | @add event if event? 73 | 74 | cache_drop: -> 75 | events = @cache.pop() or FAIL 'cache_drop' 76 | return events 77 | 78 | check_document_start: -> 79 | return unless @document_start 80 | @send @document_start 81 | delete @document_start 82 | @document_end = document_end_event() 83 | 84 | check_document_end: -> 85 | return unless @document_end 86 | @send @document_end 87 | delete @document_end 88 | @tag_map = {} 89 | @document_start = document_start_event() 90 | 91 | #---------------------------------------------------------------------------- 92 | try_yaml_stream: -> 93 | @add stream_start_event() 94 | @tag_map = {} 95 | @document_start = document_start_event() 96 | delete @document_end 97 | 98 | got_yaml_stream: -> 99 | @check_document_end() 100 | @add stream_end_event() 101 | 102 | got_yaml_version_number: (o)-> 103 | die "Multiple %YAML directives not allowed" \ 104 | if @document_start.version? 105 | @document_start.version = o.text 106 | 107 | got_tag_handle: (o)-> 108 | @tag_handle = o.text 109 | 110 | got_tag_prefix: (o)-> 111 | @tag_map[@tag_handle] = o.text 112 | 113 | got_document_start_indicator: -> 114 | @check_document_end() 115 | @document_start.explicit = true 116 | 117 | got_document_end_indicator: -> 118 | if @document_end? 119 | @document_end.explicit = true 120 | @check_document_end() 121 | 122 | got_flow_mapping_start: -> @add mapping_start_event true 123 | got_flow_mapping_end: -> @add mapping_end_event() 124 | 125 | got_flow_sequence_start: -> @add sequence_start_event true 126 | got_flow_sequence_end: -> @add sequence_end_event() 127 | 128 | try_block_mapping: -> @cache_up mapping_start_event() 129 | got_block_mapping: -> @cache_down mapping_end_event() 130 | not_block_mapping: -> @cache_drop() 131 | 132 | try_block_sequence_context: -> @cache_up sequence_start_event() 133 | got_block_sequence_context: -> @cache_down sequence_end_event() 134 | not_block_sequence_context: -> 135 | event = @cache_drop()[0] 136 | @anchor = event.anchor 137 | @tag = event.tag 138 | 139 | try_compact_mapping: -> @cache_up mapping_start_event() 140 | got_compact_mapping: -> @cache_down mapping_end_event() 141 | not_compact_mapping: -> @cache_drop() 142 | 143 | try_compact_sequence: -> @cache_up sequence_start_event() 144 | got_compact_sequence: -> @cache_down sequence_end_event() 145 | not_compact_sequence: -> @cache_drop() 146 | 147 | try_flow_pair: -> @cache_up mapping_start_event true 148 | got_flow_pair: -> @cache_down mapping_end_event() 149 | not_flow_pair: -> @cache_drop() 150 | 151 | try_block_mapping_implicit_entry: -> @cache_up() 152 | got_block_mapping_implicit_entry: -> @cache_down() 153 | not_block_mapping_implicit_entry: -> @cache_drop() 154 | 155 | try_block_mapping_explicit_entry: -> @cache_up() 156 | got_block_mapping_explicit_entry: -> @cache_down() 157 | not_block_mapping_explicit_entry: -> @cache_drop() 158 | 159 | try_flow_mapping_empty_key_entry: -> @cache_up() 160 | got_flow_mapping_empty_key_entry: -> @cache_down() 161 | not_flow_mapping_empty_key_entry: -> @cache_drop() 162 | 163 | got_flow_plain_scalar: (o)-> 164 | text = o.text 165 | .replace(/(?:[\ \t]*\r?\n[\ \t]*)/g, "\n") 166 | .replace(/(\n)(\n*)/g, (m...)-> if m[2].length then m[2] else ' ') 167 | @add scalar_event 'plain', text 168 | 169 | got_single_quoted_scalar: (o)-> 170 | text = o.text[1...-1] 171 | .replace(/(?:[\ \t]*\r?\n[\ \t]*)/g, "\n") 172 | .replace(/(\n)(\n*)/g, (m...)-> if m[2].length then m[2] else ' ') 173 | .replace(/''/g, "'") 174 | @add scalar_event 'single', text 175 | 176 | unescapes = 177 | '\\\\': '\\' 178 | '\r\n': '\n' 179 | '\\ ': ' ' 180 | '\\"': '"' 181 | '\\/': '/' 182 | '\\b': '\b' 183 | '\\n': '\n' 184 | '\\r': '\r' 185 | '\\t': '\t' 186 | '\\\t': '\t' 187 | 188 | end1 = String(/// (?: \\ \r?\n[\ \t]* ) ///)[1..-2] 189 | end2 = String(/// (?: [\ \t]*\r?\n[\ \t]* ) ///)[1..-2] 190 | hex = '[0-9a-fA-F]' 191 | hex2 = String(/// (?: \\x ( #{hex}{2} ) ) ///)[1..-2] 192 | hex4 = String(/// (?: \\u ( #{hex}{4} ) ) ///)[1..-2] 193 | hex8 = String(/// (?: \\U ( #{hex}{8} ) ) ///)[1..-2] 194 | 195 | got_double_quoted_scalar: (o)-> 196 | @add scalar_event('double', o.text[1...-1] 197 | .replace /// 198 | (?: 199 | \r\n 200 | | #{end1} 201 | | #{end2}+ 202 | | #{hex2} 203 | | #{hex4} 204 | | #{hex8} 205 | | \\\\ 206 | | \\\t 207 | | \\[\ bnrt"/] 208 | ) 209 | ///g, (m)-> 210 | if n = m.match ///^ #{hex2} $/// 211 | return String.fromCharCode(parseInt(n[1], 16)) 212 | if n = m.match ///^ #{hex4} $/// 213 | return String.fromCharCode(parseInt(n[1], 16)) 214 | if n = m.match ///^ #{hex8} $/// 215 | return String.fromCharCode(parseInt(n[1], 16)) 216 | if m.match ///^ #{end1} $/// 217 | return '' 218 | if m.match ///^ #{end2}+ $/// 219 | u = m 220 | .replace(/// #{end2} ///, '') 221 | .replace(/// #{end2} ///g, '\n') 222 | return u || ' ' 223 | if u = unescapes[m] 224 | return u 225 | XXX m 226 | ) 227 | 228 | got_empty_line: -> 229 | @add cache('') if @in_scalar 230 | got_literal_scalar_line_content: (o)-> 231 | @add cache(o.text) 232 | try_block_literal_scalar: -> 233 | @in_scalar = true 234 | @cache_up() 235 | got_block_literal_scalar: -> 236 | delete @in_scalar 237 | lines = @cache_drop() 238 | lines.pop() if lines.length > 0 and lines[lines.length - 1].text == '' 239 | lines = lines.map (l)-> "#{l.text}\n" 240 | text = lines.join '' 241 | t = @parser.state_curr().t 242 | if t == 'CLIP' 243 | text = text.replace /\n+$/, "\n" 244 | else if t == 'STRIP' 245 | text = text.replace /\n+$/, "" 246 | else if not text.match /\S/ 247 | text = text.replace /\n(\n+)$/, "$1" 248 | @add scalar_event 'literal', text 249 | not_block_literal_scalar: -> 250 | delete @in_scalar 251 | @cache_drop() 252 | 253 | got_folded_scalar_text: (o)-> 254 | @add cache o.text 255 | 256 | got_folded_scalar_spaced_text: (o)-> 257 | @add cache o.text 258 | 259 | try_block_folded_scalar: -> 260 | @in_scalar = true 261 | @cache_up() 262 | 263 | got_block_folded_scalar: -> 264 | delete @in_scalar 265 | lines = @cache_drop().map (l)-> l.text 266 | text = lines.join "\n" 267 | text = text.replace /^(\S.*)\n(?=\S)/gm, "$1 " 268 | text = text.replace /^(\S.*)\n(\n+)/gm, "$1$2" 269 | text = text.replace /^([\ \t]+\S.*)\n(\n+)(?=\S)/gm, "$1$2" 270 | text += "\n" 271 | 272 | t = @parser.state_curr().t 273 | if t == 'CLIP' 274 | text = text.replace /\n+$/, "\n" 275 | text = '' if text == "\n" 276 | else if t == 'STRIP' 277 | text = text.replace /\n+$/, "" 278 | @add scalar_event 'folded', text 279 | 280 | not_block_folded_scalar: -> 281 | delete @in_scalar 282 | @cache_drop() 283 | 284 | got_empty_node: -> @add scalar_event 'plain', '' 285 | 286 | not_block_collection_properties: -> 287 | delete @tag 288 | delete @anchor 289 | 290 | not_block_collection_anchor: -> 291 | delete @anchor 292 | 293 | not_block_collection_tag: -> 294 | delete @tag 295 | 296 | got_anchor_property: (o)-> 297 | @anchor = o.text[1..] 298 | 299 | got_tag_property: (o)-> 300 | tag = o.text 301 | if m = tag.match /^!<(.*)>$/ 302 | @tag = m[1] 303 | else if m = tag.match /^!!(.*)/ 304 | prefix = @tag_map['!!'] 305 | if prefix? 306 | @tag = prefix + tag[2..] 307 | else 308 | @tag = "tag:yaml.org,2002:#{m[1]}" 309 | else if m = tag.match(/^(!.*?!)/) 310 | prefix = @tag_map[m[1]] 311 | if prefix? 312 | @tag = prefix + tag[(m[1].length)..] 313 | else 314 | die "No %TAG entry for '#{prefix}'" 315 | else if (prefix = @tag_map['!'])? 316 | @tag = prefix + tag[1..] 317 | else 318 | @tag = tag 319 | @tag = @tag.replace /%([0-9a-fA-F]{2})/g, (m...)-> 320 | String.fromCharCode parseInt m[1], 16 321 | 322 | got_alias_node: (o)-> @add alias_event o.text[1..] 323 | 324 | # vim: sw=2: 325 | -------------------------------------------------------------------------------- /parser-1.3/coffeescript/lib/test-receiver.coffee: -------------------------------------------------------------------------------- 1 | require './prelude' 2 | require './receiver' 3 | 4 | event_map = 5 | stream_start: '+STR' 6 | stream_end: '-STR' 7 | document_start: '+DOC' 8 | document_end: '-DOC' 9 | mapping_start: '+MAP' 10 | mapping_end: '-MAP' 11 | sequence_start: '+SEQ' 12 | sequence_end: '-SEQ' 13 | scalar: '=VAL' 14 | alias: '=ALI' 15 | 16 | style_map = 17 | plain: ':' 18 | single: "'" 19 | double: '"' 20 | literal: '|' 21 | folded: '>' 22 | 23 | global.TestReceiver = class TestReceiver extends Receiver 24 | 25 | output: '' 26 | 27 | receive: (e)-> 28 | type = event_map[e.event] 29 | 30 | event = [type] 31 | event.push '---' if type == '+DOC' and e.explicit 32 | event.push '...' if type == '-DOC' and e.explicit 33 | event.push '{}' if type == '+MAP' and e.flow 34 | event.push '[]' if type == '+SEQ' and e.flow 35 | event.push "&#{e.anchor}" if e.anchor 36 | event.push "<#{e.tag}>" if e.tag 37 | event.push "*#{e.name}" if e.name 38 | if e.value? 39 | style = style_map[e.style] 40 | value = e.value 41 | .replace(/\\/g, '\\\\') 42 | .replace(/\x08/g, '\\b') 43 | .replace(/\t/g, '\\t') 44 | .replace(/\n/g, '\\n') 45 | .replace(/\r/g, '\\r') 46 | event.push "#{style}#{value}" 47 | 48 | @output += event.join(' ') + "\n" 49 | 50 | # vim: sw=2: 51 | -------------------------------------------------------------------------------- /parser-1.3/coffeescript/test/error-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/error-suite.tml -------------------------------------------------------------------------------- /parser-1.3/coffeescript/test/parse-block.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-block.tml -------------------------------------------------------------------------------- /parser-1.3/coffeescript/test/parse-flow.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-flow.tml -------------------------------------------------------------------------------- /parser-1.3/coffeescript/test/parse-props.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-props.tml -------------------------------------------------------------------------------- /parser-1.3/coffeescript/test/parse-scalar.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-scalar.tml -------------------------------------------------------------------------------- /parser-1.3/coffeescript/test/parse-stream.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-stream.tml -------------------------------------------------------------------------------- /parser-1.3/coffeescript/test/parse-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-suite.tml -------------------------------------------------------------------------------- /parser-1.3/coffeescript/test/testml-bridge.coffee: -------------------------------------------------------------------------------- 1 | require '../../../test/testml/src/coffee/lib/testml/bridge' 2 | require '../lib/prelude' 3 | require '../lib/parser' 4 | require '../lib/grammar' 5 | require '../lib/test-receiver' 6 | 7 | module.exports = 8 | class TestMLBridge extends TestML.Bridge 9 | 10 | parse: (yaml, expect_error=null)-> 11 | parser = new Parser(new TestReceiver) 12 | 13 | error = '' 14 | try 15 | parser.parse yaml 16 | catch e 17 | error = String e 18 | 19 | if expect_error? 20 | return if error then 1 else 0 21 | 22 | if error 23 | error 24 | else 25 | parser.receiver.output 26 | 27 | unescape: (text)-> 28 | text = text 29 | .replace(/␣/g, ' ') 30 | .replace(/—*»/g, "\t") 31 | .replace(/⇔/g, "\uFEFF") 32 | .replace(/↵/g, '') 33 | .replace(/∎\n$/, '') 34 | 35 | # text = text.replace(/↓/g, "\r") 36 | 37 | return text 38 | 39 | fix_test_output: (text)-> 40 | return text 41 | -------------------------------------------------------------------------------- /parser-1.3/javascript/Makefile: -------------------------------------------------------------------------------- 1 | ROOT := $(shell cd ../..; pwd) 2 | BASE := $(ROOT)/parser-1.3 3 | PARSER_LANG := javascript 4 | BIN := node 5 | GRAMMAR := 6 | 7 | ALL_JS := \ 8 | bin/yaml-parser \ 9 | lib/grammar.js \ 10 | lib/parser.js \ 11 | lib/prelude.js \ 12 | lib/receiver.js \ 13 | lib/test-receiver.js \ 14 | test/testml-bridge.js \ 15 | 16 | BUILD_DEPS := \ 17 | $(ROOT)/node_modules \ 18 | $(ALL_JS) \ 19 | $(ROOT)/test/testml/src/node/lib \ 20 | 21 | 22 | include $(ROOT)/.common.mk 23 | 24 | 25 | bin/%: $(BASE)/coffeescript/bin/% 26 | echo '#!/usr/bin/env node' > $@ 27 | coffee -cp $< >> $@ 28 | chmod +x $@ 29 | 30 | lib/%.js: $(BASE)/coffeescript/lib/%.coffee 31 | coffee -cp $< > $@ 32 | 33 | test/%.js: $(BASE)/coffeescript/test/%.coffee 34 | coffee -cp $< > $@ 35 | perl -pi -e 's{/coffee/}{/node/}' $@ 36 | 37 | $(ROOT)/test/testml/src/node/lib: $(ROOT)/test/testml 38 | $(MAKE) -C $(@:%/lib=%) js-files 39 | -------------------------------------------------------------------------------- /parser-1.3/javascript/bin/yaml-parser: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // Generated by CoffeeScript 2.5.1 3 | (function() { 4 | //!/usr/bin/env coffee 5 | var argv, events, main; 6 | 7 | require('../lib/prelude'); 8 | 9 | require('../lib/parser'); 10 | 11 | require('../lib/test-receiver'); 12 | 13 | events = false; 14 | 15 | main = function(yaml = file_read('-')) { 16 | var calls, e, i, k, len, n, parser, pass, ref, sorted, start, time; 17 | parser = new Parser(new TestReceiver()); 18 | pass = true; 19 | start = timer(); 20 | try { 21 | parser.parse(yaml); 22 | } catch (error) { 23 | e = error; 24 | warn(e); 25 | pass = false; 26 | } 27 | time = timer(start); 28 | if (yaml.match(/\n./)) { 29 | n = "\n"; 30 | } else { 31 | n = ''; 32 | yaml = yaml.replace(/\n$/, '\\n'); 33 | } 34 | if (events) { 35 | out(parser.receiver.output()); 36 | return true; 37 | } 38 | if (process.env.STATS) { 39 | sorted = {}; 40 | ({calls} = parser.stats); 41 | ref = Object.keys(calls).sort(function(a, b) { 42 | return calls[a] - calls[b]; 43 | }); 44 | for (i = 0, len = ref.length; i < len; i++) { 45 | k = ref[i]; 46 | sorted[k] = calls[k]; 47 | } 48 | WWW(sorted); 49 | } 50 | if (pass) { 51 | say(`PASS - '${n}${yaml}'`); 52 | say(parser.receiver.output); 53 | say(sprintf("Parse time %.5fs", time)); 54 | return true; 55 | } else { 56 | say(`FAIL - '${n}${yaml}'`); 57 | say(parser.receiver.output); 58 | say(sprintf("Parse time %.5fs", time)); 59 | return false; 60 | } 61 | }; 62 | 63 | argv = process.argv.slice(2); 64 | 65 | if (argv.length && argv[0] === '--events') { 66 | events = true; 67 | argv.shift(); 68 | } 69 | 70 | if (main(...argv)) { 71 | exit(0); 72 | } else { 73 | exit(1); 74 | } 75 | 76 | // vim: sw=2: 77 | 78 | }).call(this); 79 | -------------------------------------------------------------------------------- /parser-1.3/javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 32 | 33 | -------------------------------------------------------------------------------- /parser-1.3/javascript/lib/prelude.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.5.1 2 | (function() { 3 | require('ingy-prelude'); 4 | 5 | Function.prototype.n = function(name) { 6 | Object.defineProperty(this, 'name', { 7 | value: name 8 | }); 9 | return this; 10 | }; 11 | 12 | Function.prototype.w = function() { 13 | warn('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'); 14 | return XXX(typeof this); 15 | }; 16 | 17 | global.ENV = process.env; 18 | 19 | global.memoize = require('moize').maxSize(50); 20 | 21 | //------------------------------------------------------------------------------ 22 | // Grammar helper functions: 23 | //------------------------------------------------------------------------------ 24 | 25 | // Generate required regular expression and string variants: 26 | global.regx = function(rgx) { 27 | var chars, str; 28 | str = String(rgx); 29 | // XXX Can remove when stable: 30 | if (str.match(/>>\d+<>> ${msg}`); 152 | }; 153 | 154 | global.debug_rule = function(name, ...args) { 155 | if (!ENV.DEBUG) { 156 | return; 157 | } 158 | args = _.join(_.map(args, function(a) { 159 | return stringify(a); 160 | }, ',')); 161 | return debug(`${name}(${args})`); 162 | }; 163 | 164 | global.dump = function(o) { 165 | return require('yaml').stringify(o); 166 | }; 167 | 168 | global.FAIL = function(...o) { 169 | WWW(o); 170 | return die_(`FAIL '${o[0] || '???'}'`); 171 | }; 172 | 173 | global.timer = function(start = null) { 174 | var time; 175 | if (start != null) { 176 | time = process.hrtime(start); 177 | return time[0] + time[1] / 1000000000; 178 | } else { 179 | return process.hrtime(); 180 | } 181 | }; 182 | 183 | // vim: sw=2: 184 | 185 | }).call(this); 186 | -------------------------------------------------------------------------------- /parser-1.3/javascript/lib/receiver.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.5.1 2 | (function() { 3 | var Receiver, alias_event, cache, document_end_event, document_start_event, mapping_end_event, mapping_start_event, scalar_event, sequence_end_event, sequence_start_event, stream_end_event, stream_start_event; 4 | 5 | require('./prelude'); 6 | 7 | stream_start_event = function() { 8 | return { 9 | event: 'stream_start' 10 | }; 11 | }; 12 | 13 | stream_end_event = function() { 14 | return { 15 | event: 'stream_end' 16 | }; 17 | }; 18 | 19 | document_start_event = function(explicit = false) { 20 | return { 21 | event: 'document_start', 22 | explicit: explicit, 23 | version: null 24 | }; 25 | }; 26 | 27 | document_end_event = function(explicit = false) { 28 | return { 29 | event: 'document_end', 30 | explicit: explicit 31 | }; 32 | }; 33 | 34 | mapping_start_event = function(flow = false) { 35 | return { 36 | event: 'mapping_start', 37 | flow: flow 38 | }; 39 | }; 40 | 41 | mapping_end_event = function() { 42 | return { 43 | event: 'mapping_end' 44 | }; 45 | }; 46 | 47 | sequence_start_event = function(flow = false) { 48 | return { 49 | event: 'sequence_start', 50 | flow: flow 51 | }; 52 | }; 53 | 54 | sequence_end_event = function() { 55 | return { 56 | event: 'sequence_end' 57 | }; 58 | }; 59 | 60 | scalar_event = function(style, value) { 61 | return { 62 | event: 'scalar', 63 | style: style, 64 | value: value 65 | }; 66 | }; 67 | 68 | alias_event = function(name) { 69 | return { 70 | event: 'alias', 71 | name: name 72 | }; 73 | }; 74 | 75 | cache = function(text) { 76 | return { 77 | text: text 78 | }; 79 | }; 80 | 81 | global.Receiver = Receiver = (function() { 82 | var end1, end2, hex, hex2, hex4, hex8, unescapes; 83 | 84 | class Receiver { 85 | constructor() { 86 | this.event = []; 87 | this.cache = []; 88 | } 89 | 90 | send(event) { 91 | if (this.receive) { 92 | return this.receive(event); 93 | } else { 94 | return this.event.push(event); 95 | } 96 | } 97 | 98 | add(event) { 99 | if (event.event != null) { 100 | if (this.anchor != null) { 101 | event.anchor = this.anchor; 102 | delete this.anchor; 103 | } 104 | if (this.tag != null) { 105 | event.tag = this.tag; 106 | delete this.tag; 107 | } 108 | } 109 | this.push(event); 110 | return event; 111 | } 112 | 113 | push(event) { 114 | if (this.cache.length) { 115 | return _.last(this.cache).push(event); 116 | } else { 117 | if (event.event.match(/(mapping_start|sequence_start|scalar)/)) { 118 | this.check_document_start(); 119 | } 120 | return this.send(event); 121 | } 122 | } 123 | 124 | cache_up(event = null) { 125 | this.cache.push([]); 126 | if (event != null) { 127 | return this.add(event); 128 | } 129 | } 130 | 131 | cache_down(event = null) { 132 | var e, events, i, len; 133 | events = this.cache.pop() || FAIL('cache_down'); 134 | for (i = 0, len = events.length; i < len; i++) { 135 | e = events[i]; 136 | this.push(e); 137 | } 138 | if (event != null) { 139 | return this.add(event); 140 | } 141 | } 142 | 143 | cache_drop() { 144 | var events; 145 | events = this.cache.pop() || FAIL('cache_drop'); 146 | return events; 147 | } 148 | 149 | check_document_start() { 150 | if (!this.document_start) { 151 | return; 152 | } 153 | this.send(this.document_start); 154 | delete this.document_start; 155 | return this.document_end = document_end_event(); 156 | } 157 | 158 | check_document_end() { 159 | if (!this.document_end) { 160 | return; 161 | } 162 | this.send(this.document_end); 163 | delete this.document_end; 164 | this.tag_map = {}; 165 | return this.document_start = document_start_event(); 166 | } 167 | 168 | //---------------------------------------------------------------------------- 169 | try_yaml_stream() { 170 | this.add(stream_start_event()); 171 | this.tag_map = {}; 172 | this.document_start = document_start_event(); 173 | return delete this.document_end; 174 | } 175 | 176 | got_yaml_stream() { 177 | this.check_document_end(); 178 | return this.add(stream_end_event()); 179 | } 180 | 181 | got_yaml_version_number(o) { 182 | if (this.document_start.version != null) { 183 | die("Multiple %YAML directives not allowed"); 184 | } 185 | return this.document_start.version = o.text; 186 | } 187 | 188 | got_tag_handle(o) { 189 | return this.tag_handle = o.text; 190 | } 191 | 192 | got_tag_prefix(o) { 193 | return this.tag_map[this.tag_handle] = o.text; 194 | } 195 | 196 | got_document_start_indicator() { 197 | this.check_document_end(); 198 | return this.document_start.explicit = true; 199 | } 200 | 201 | got_document_end_indicator() { 202 | if (this.document_end != null) { 203 | this.document_end.explicit = true; 204 | } 205 | return this.check_document_end(); 206 | } 207 | 208 | got_flow_mapping_start() { 209 | return this.add(mapping_start_event(true)); 210 | } 211 | 212 | got_flow_mapping_end() { 213 | return this.add(mapping_end_event()); 214 | } 215 | 216 | got_flow_sequence_start() { 217 | return this.add(sequence_start_event(true)); 218 | } 219 | 220 | got_flow_sequence_end() { 221 | return this.add(sequence_end_event()); 222 | } 223 | 224 | try_block_mapping() { 225 | return this.cache_up(mapping_start_event()); 226 | } 227 | 228 | got_block_mapping() { 229 | return this.cache_down(mapping_end_event()); 230 | } 231 | 232 | not_block_mapping() { 233 | return this.cache_drop(); 234 | } 235 | 236 | try_block_sequence_context() { 237 | return this.cache_up(sequence_start_event()); 238 | } 239 | 240 | got_block_sequence_context() { 241 | return this.cache_down(sequence_end_event()); 242 | } 243 | 244 | not_block_sequence_context() { 245 | var event; 246 | event = this.cache_drop()[0]; 247 | this.anchor = event.anchor; 248 | return this.tag = event.tag; 249 | } 250 | 251 | try_compact_mapping() { 252 | return this.cache_up(mapping_start_event()); 253 | } 254 | 255 | got_compact_mapping() { 256 | return this.cache_down(mapping_end_event()); 257 | } 258 | 259 | not_compact_mapping() { 260 | return this.cache_drop(); 261 | } 262 | 263 | try_compact_sequence() { 264 | return this.cache_up(sequence_start_event()); 265 | } 266 | 267 | got_compact_sequence() { 268 | return this.cache_down(sequence_end_event()); 269 | } 270 | 271 | not_compact_sequence() { 272 | return this.cache_drop(); 273 | } 274 | 275 | try_flow_pair() { 276 | return this.cache_up(mapping_start_event(true)); 277 | } 278 | 279 | got_flow_pair() { 280 | return this.cache_down(mapping_end_event()); 281 | } 282 | 283 | not_flow_pair() { 284 | return this.cache_drop(); 285 | } 286 | 287 | try_block_mapping_implicit_entry() { 288 | return this.cache_up(); 289 | } 290 | 291 | got_block_mapping_implicit_entry() { 292 | return this.cache_down(); 293 | } 294 | 295 | not_block_mapping_implicit_entry() { 296 | return this.cache_drop(); 297 | } 298 | 299 | try_block_mapping_explicit_entry() { 300 | return this.cache_up(); 301 | } 302 | 303 | got_block_mapping_explicit_entry() { 304 | return this.cache_down(); 305 | } 306 | 307 | not_block_mapping_explicit_entry() { 308 | return this.cache_drop(); 309 | } 310 | 311 | try_flow_mapping_empty_key_entry() { 312 | return this.cache_up(); 313 | } 314 | 315 | got_flow_mapping_empty_key_entry() { 316 | return this.cache_down(); 317 | } 318 | 319 | not_flow_mapping_empty_key_entry() { 320 | return this.cache_drop(); 321 | } 322 | 323 | got_flow_plain_scalar(o) { 324 | var text; 325 | text = o.text.replace(/(?:[\ \t]*\r?\n[\ \t]*)/g, "\n").replace(/(\n)(\n*)/g, function(...m) { 326 | if (m[2].length) { 327 | return m[2]; 328 | } else { 329 | return ' '; 330 | } 331 | }); 332 | return this.add(scalar_event('plain', text)); 333 | } 334 | 335 | got_single_quoted_scalar(o) { 336 | var text; 337 | text = o.text.slice(1, -1).replace(/(?:[\ \t]*\r?\n[\ \t]*)/g, "\n").replace(/(\n)(\n*)/g, function(...m) { 338 | if (m[2].length) { 339 | return m[2]; 340 | } else { 341 | return ' '; 342 | } 343 | }).replace(/''/g, "'"); 344 | return this.add(scalar_event('single', text)); 345 | } 346 | 347 | got_double_quoted_scalar(o) { 348 | return this.add(scalar_event('double', o.text.slice(1, -1).replace(RegExp(`(?:\\r\\n|${end1}|${end2}+|${hex2}|${hex4}|${hex8}|\\\\\\\\|\\\\\\t|\\\\[ bnrt"/])`, "g"), function(m) { 349 | var n, u; 350 | if (n = m.match(RegExp(`^${hex2}$`))) { 351 | return String.fromCharCode(parseInt(n[1], 16)); 352 | } 353 | if (n = m.match(RegExp(`^${hex4}$`))) { 354 | return String.fromCharCode(parseInt(n[1], 16)); 355 | } 356 | if (n = m.match(RegExp(`^${hex8}$`))) { 357 | return String.fromCharCode(parseInt(n[1], 16)); 358 | } 359 | if (m.match(RegExp(`^${end1}$`))) { 360 | return ''; 361 | } 362 | if (m.match(RegExp(`^${end2}+$`))) { 363 | u = m.replace(RegExp(`${end2}`), '').replace(RegExp(`${end2}`, "g"), '\n'); 364 | return u || ' '; 365 | } 366 | if (u = unescapes[m]) { 367 | return u; 368 | } 369 | return XXX(m); 370 | }))); 371 | } 372 | 373 | got_empty_line() { 374 | if (this.in_scalar) { 375 | return this.add(cache('')); 376 | } 377 | } 378 | 379 | got_literal_scalar_line_content(o) { 380 | return this.add(cache(o.text)); 381 | } 382 | 383 | try_block_literal_scalar() { 384 | this.in_scalar = true; 385 | return this.cache_up(); 386 | } 387 | 388 | got_block_literal_scalar() { 389 | var lines, t, text; 390 | delete this.in_scalar; 391 | lines = this.cache_drop(); 392 | if (lines.length > 0 && lines[lines.length - 1].text === '') { 393 | lines.pop(); 394 | } 395 | lines = lines.map(function(l) { 396 | return `${l.text}\n`; 397 | }); 398 | text = lines.join(''); 399 | t = this.parser.state_curr().t; 400 | if (t === 'CLIP') { 401 | text = text.replace(/\n+$/, "\n"); 402 | } else if (t === 'STRIP') { 403 | text = text.replace(/\n+$/, ""); 404 | } else if (!text.match(/\S/)) { 405 | text = text.replace(/\n(\n+)$/, "$1"); 406 | } 407 | return this.add(scalar_event('literal', text)); 408 | } 409 | 410 | not_block_literal_scalar() { 411 | delete this.in_scalar; 412 | return this.cache_drop(); 413 | } 414 | 415 | got_folded_scalar_text(o) { 416 | return this.add(cache(o.text)); 417 | } 418 | 419 | got_folded_scalar_spaced_text(o) { 420 | return this.add(cache(o.text)); 421 | } 422 | 423 | try_block_folded_scalar() { 424 | this.in_scalar = true; 425 | return this.cache_up(); 426 | } 427 | 428 | got_block_folded_scalar() { 429 | var lines, t, text; 430 | delete this.in_scalar; 431 | lines = this.cache_drop().map(function(l) { 432 | return l.text; 433 | }); 434 | text = lines.join("\n"); 435 | text = text.replace(/^(\S.*)\n(?=\S)/gm, "$1 "); 436 | text = text.replace(/^(\S.*)\n(\n+)/gm, "$1$2"); 437 | text = text.replace(/^([\ \t]+\S.*)\n(\n+)(?=\S)/gm, "$1$2"); 438 | text += "\n"; 439 | t = this.parser.state_curr().t; 440 | if (t === 'CLIP') { 441 | text = text.replace(/\n+$/, "\n"); 442 | if (text === "\n") { 443 | text = ''; 444 | } 445 | } else if (t === 'STRIP') { 446 | text = text.replace(/\n+$/, ""); 447 | } 448 | return this.add(scalar_event('folded', text)); 449 | } 450 | 451 | not_block_folded_scalar() { 452 | delete this.in_scalar; 453 | return this.cache_drop(); 454 | } 455 | 456 | got_empty_node() { 457 | return this.add(scalar_event('plain', '')); 458 | } 459 | 460 | not_block_collection_properties() { 461 | delete this.tag; 462 | return delete this.anchor; 463 | } 464 | 465 | not_block_collection_anchor() { 466 | return delete this.anchor; 467 | } 468 | 469 | not_block_collection_tag() { 470 | return delete this.tag; 471 | } 472 | 473 | got_anchor_property(o) { 474 | return this.anchor = o.text.slice(1); 475 | } 476 | 477 | got_tag_property(o) { 478 | var m, prefix, tag; 479 | tag = o.text; 480 | if (m = tag.match(/^!<(.*)>$/)) { 481 | this.tag = m[1]; 482 | } else if (m = tag.match(/^!!(.*)/)) { 483 | prefix = this.tag_map['!!']; 484 | if (prefix != null) { 485 | this.tag = prefix + tag.slice(2); 486 | } else { 487 | this.tag = `tag:yaml.org,2002:${m[1]}`; 488 | } 489 | } else if (m = tag.match(/^(!.*?!)/)) { 490 | prefix = this.tag_map[m[1]]; 491 | if (prefix != null) { 492 | this.tag = prefix + tag.slice((m[1].length)); 493 | } else { 494 | die(`No %TAG entry for '${prefix}'`); 495 | } 496 | } else if ((prefix = this.tag_map['!']) != null) { 497 | this.tag = prefix + tag.slice(1); 498 | } else { 499 | this.tag = tag; 500 | } 501 | return this.tag = this.tag.replace(/%([0-9a-fA-F]{2})/g, function(...m) { 502 | return String.fromCharCode(parseInt(m[1], 16)); 503 | }); 504 | } 505 | 506 | got_alias_node(o) { 507 | return this.add(alias_event(o.text.slice(1))); 508 | } 509 | 510 | }; 511 | 512 | unescapes = { 513 | '\\\\': '\\', 514 | '\r\n': '\n', 515 | '\\ ': ' ', 516 | '\\"': '"', 517 | '\\/': '/', 518 | '\\b': '\b', 519 | '\\n': '\n', 520 | '\\r': '\r', 521 | '\\t': '\t', 522 | '\\\t': '\t' 523 | }; 524 | 525 | end1 = String(/(?:\\\r?\n[ \t]*)/).slice(1, -1); 526 | 527 | end2 = String(/(?:[ \t]*\r?\n[ \t]*)/).slice(1, -1); 528 | 529 | hex = '[0-9a-fA-F]'; 530 | 531 | hex2 = String(RegExp(`(?:\\\\x(${hex}{2}))`)).slice(1, -1); 532 | 533 | hex4 = String(RegExp(`(?:\\\\u(${hex}{4}))`)).slice(1, -1); 534 | 535 | hex8 = String(RegExp(`(?:\\\\U(${hex}{8}))`)).slice(1, -1); 536 | 537 | return Receiver; 538 | 539 | }).call(this); 540 | 541 | // vim: sw=2: 542 | 543 | }).call(this); 544 | -------------------------------------------------------------------------------- /parser-1.3/javascript/lib/test-receiver.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.5.1 2 | (function() { 3 | var TestReceiver, event_map, style_map; 4 | 5 | require('./prelude'); 6 | 7 | require('./receiver'); 8 | 9 | event_map = { 10 | stream_start: '+STR', 11 | stream_end: '-STR', 12 | document_start: '+DOC', 13 | document_end: '-DOC', 14 | mapping_start: '+MAP', 15 | mapping_end: '-MAP', 16 | sequence_start: '+SEQ', 17 | sequence_end: '-SEQ', 18 | scalar: '=VAL', 19 | alias: '=ALI' 20 | }; 21 | 22 | style_map = { 23 | plain: ':', 24 | single: "'", 25 | double: '"', 26 | literal: '|', 27 | folded: '>' 28 | }; 29 | 30 | global.TestReceiver = TestReceiver = (function() { 31 | class TestReceiver extends Receiver { 32 | receive(e) { 33 | var event, style, type, value; 34 | type = event_map[e.event]; 35 | event = [type]; 36 | if (type === '+DOC' && e.explicit) { 37 | event.push('---'); 38 | } 39 | if (type === '-DOC' && e.explicit) { 40 | event.push('...'); 41 | } 42 | if (type === '+MAP' && e.flow) { 43 | event.push('{}'); 44 | } 45 | if (type === '+SEQ' && e.flow) { 46 | event.push('[]'); 47 | } 48 | if (e.anchor) { 49 | event.push(`&${e.anchor}`); 50 | } 51 | if (e.tag) { 52 | event.push(`<${e.tag}>`); 53 | } 54 | if (e.name) { 55 | event.push(`*${e.name}`); 56 | } 57 | if (e.value != null) { 58 | style = style_map[e.style]; 59 | value = e.value.replace(/\\/g, '\\\\').replace(/\x08/g, '\\b').replace(/\t/g, '\\t').replace(/\n/g, '\\n').replace(/\r/g, '\\r'); 60 | event.push(`${style}${value}`); 61 | } 62 | return this.output += event.join(' ') + "\n"; 63 | } 64 | 65 | }; 66 | 67 | TestReceiver.prototype.output = ''; 68 | 69 | return TestReceiver; 70 | 71 | }).call(this); 72 | 73 | // vim: sw=2: 74 | 75 | }).call(this); 76 | -------------------------------------------------------------------------------- /parser-1.3/javascript/test/diff-trace.tml: -------------------------------------------------------------------------------- 1 | ../../../test/diff-trace.tml -------------------------------------------------------------------------------- /parser-1.3/javascript/test/error-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/error-suite.tml -------------------------------------------------------------------------------- /parser-1.3/javascript/test/parse-block.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-block.tml -------------------------------------------------------------------------------- /parser-1.3/javascript/test/parse-flow.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-flow.tml -------------------------------------------------------------------------------- /parser-1.3/javascript/test/parse-props.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-props.tml -------------------------------------------------------------------------------- /parser-1.3/javascript/test/parse-scalar.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-scalar.tml -------------------------------------------------------------------------------- /parser-1.3/javascript/test/parse-stream.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-stream.tml -------------------------------------------------------------------------------- /parser-1.3/javascript/test/parse-suite.tml: -------------------------------------------------------------------------------- 1 | ../../../test/parse-suite.tml -------------------------------------------------------------------------------- /parser-1.3/javascript/test/testml-bridge.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.5.1 2 | (function() { 3 | var TestMLBridge; 4 | 5 | require('../../../test/testml/src/node/lib/testml/bridge'); 6 | 7 | require('../lib/prelude'); 8 | 9 | require('../lib/parser'); 10 | 11 | require('../lib/grammar'); 12 | 13 | require('../lib/test-receiver'); 14 | 15 | module.exports = TestMLBridge = class TestMLBridge extends TestML.Bridge { 16 | parse(yaml, expect_error = null) { 17 | var e, error, parser; 18 | parser = new Parser(new TestReceiver()); 19 | error = ''; 20 | try { 21 | parser.parse(yaml); 22 | } catch (error1) { 23 | e = error1; 24 | error = String(e); 25 | } 26 | if (expect_error != null) { 27 | if (error) { 28 | return 1; 29 | } else { 30 | return 0; 31 | } 32 | } 33 | if (error) { 34 | return error; 35 | } else { 36 | return parser.receiver.output; 37 | } 38 | } 39 | 40 | unescape(text) { 41 | text = text.replace(/␣/g, ' ').replace(/—*»/g, "\t").replace(/⇔/g, "\uFEFF").replace(/↵/g, '').replace(/∎\n$/, ''); 42 | // text = text.replace(/↓/g, "\r") 43 | return text; 44 | } 45 | 46 | fix_test_output(text) { 47 | return text; 48 | } 49 | 50 | }; 51 | 52 | }).call(this); 53 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | /suite/ 2 | /testml/ 3 | -------------------------------------------------------------------------------- /test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | RUN apt update 4 | RUN apt install -y \ 5 | build-essential \ 6 | git \ 7 | nodejs \ 8 | perl \ 9 | vim \ 10 | && true 11 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | ROOT := $(shell cd ..; pwd) 2 | BASE := $(ROOT)/test 3 | 4 | include $(ROOT)/.common.mk 5 | 6 | ALL := \ 7 | testml \ 8 | yaml-test-suite.tml \ 9 | 10 | all: $(ALL) 11 | 12 | clean:: 13 | rm -fr $(ALL) 14 | rm -fr .testml 15 | rm -fr suite 16 | 17 | testml: 18 | $(call git-clone,$@,$(TESTML_REPO),$(TESTML_COMMIT)) 19 | git -C $@ fetch origin ext/perl node_modules 20 | $(MAKE) -C $@ ext/perl 21 | $(MAKE) -C $@ src/node_modules 22 | 23 | yaml-test-suite.tml: suite 24 | for file in $ $@ 30 | 31 | suite: 32 | $(call git-clone,$@,$(YAML_TEST_SUITE_REPO),$(YAML_TEST_SUITE_COMMIT)) 33 | $(MAKE) -C $@ testml 34 | ln -s testml $@/test 35 | -------------------------------------------------------------------------------- /test/diff-trace.tml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env testml-bash 2 | 3 | my-trace(*yaml) == coffee-trace(*yaml) 4 | :"+ -- TRACE output matches coffeescript's TRACE output -- '{*yaml}'" 5 | 6 | %Bridge bash 7 | 8 | my-trace() { 9 | TRACE=1 ./bin/yaml-parser "$1" 2>&1 | grep -v '^Parse time' 10 | } 11 | 12 | coffee-trace() { 13 | TRACE=1 ../coffeescript/bin/yaml-parser "$1" 2>&1 | grep -v '^Parse time' 14 | } 15 | 16 | === Flow sequence 17 | --- yaml: [ 1,22 , 333,] 18 | 19 | # vim: ft=sh: 20 | -------------------------------------------------------------------------------- /test/error-suite.tml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env testml 2 | 3 | 4 | <*error> => 5 | "({*Name}) {*Label} -- '{*in-yaml}'": 6 | *in-yaml.unescape.parse('expect-error') == 1 7 | 8 | 9 | %Import yaml-test-suite 10 | -------------------------------------------------------------------------------- /test/parse-block.tml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env testml 2 | 3 | 4 | parse(*yaml) == *events 5 | :"+ -- '{*yaml}'" 6 | 7 | 8 | 9 | === Block mapping 10 | --- yaml 11 | foo: bar 12 | --- events 13 | +STR 14 | +DOC 15 | +MAP 16 | =VAL :foo 17 | =VAL :bar 18 | -MAP 19 | -DOC 20 | -STR 21 | 22 | 23 | === Mapping value on next line 24 | --- yaml 25 | foo: 26 | bar 27 | --- ^events 28 | 29 | 30 | === Mapping value quoted 31 | --- yaml 32 | foo: "bar" 33 | --- events 34 | +STR 35 | +DOC 36 | +MAP 37 | =VAL :foo 38 | =VAL "bar 39 | -MAP 40 | -DOC 41 | -STR 42 | 43 | 44 | === Mapping flow seq value 45 | --- yaml 46 | foo: [1,2] 47 | --- events 48 | +STR 49 | +DOC 50 | +MAP 51 | =VAL :foo 52 | +SEQ [] 53 | =VAL :1 54 | =VAL :2 55 | -SEQ 56 | -MAP 57 | -DOC 58 | -STR 59 | 60 | 61 | === Mapping flow seq value multiline 62 | --- yaml 63 | foo: [1, 64 | 2] 65 | --- ^events 66 | 67 | 68 | === Block sequence 69 | --- yaml 70 | - foo 71 | - bar 72 | --- events 73 | +STR 74 | +DOC 75 | +SEQ 76 | =VAL :foo 77 | =VAL :bar 78 | -SEQ 79 | -DOC 80 | -STR 81 | 82 | 83 | === Block mapping, two pair 84 | --- yaml 85 | foo: bar 86 | baz: 42 87 | --- events 88 | +STR 89 | +DOC 90 | +MAP 91 | =VAL :foo 92 | =VAL :bar 93 | =VAL :baz 94 | =VAL :42 95 | -MAP 96 | -DOC 97 | -STR 98 | 99 | 100 | === Flow mapping in block mapping 101 | --- yaml 102 | foo: {bar: 42} 103 | --- events 104 | +STR 105 | +DOC 106 | +MAP 107 | =VAL :foo 108 | +MAP {} 109 | =VAL :bar 110 | =VAL :42 111 | -MAP 112 | -MAP 113 | -DOC 114 | -STR 115 | 116 | 117 | === Flow mapping in block sequence 118 | --- yaml 119 | - {foo: 42} 120 | --- events 121 | +STR 122 | +DOC 123 | +SEQ 124 | +MAP {} 125 | =VAL :foo 126 | =VAL :42 127 | -MAP 128 | -SEQ 129 | -DOC 130 | -STR 131 | 132 | 133 | === Flow mapping multiline in block sequence 134 | --- yaml 135 | - {foo: 136 | 42 137 | } 138 | --- events 139 | +STR 140 | +DOC 141 | +SEQ 142 | +MAP {} 143 | =VAL :foo 144 | =VAL :42 145 | -MAP 146 | -SEQ 147 | -DOC 148 | -STR 149 | 150 | 151 | === Map in block seq 152 | --- yaml 153 | - foo: bar 154 | --- events 155 | +STR 156 | +DOC 157 | +SEQ 158 | +MAP 159 | =VAL :foo 160 | =VAL :bar 161 | -MAP 162 | -SEQ 163 | -DOC 164 | -STR 165 | 166 | 167 | === Map in block seq, new line 168 | --- yaml 169 | - 170 | foo: bar 171 | --- ^events 172 | 173 | 174 | === Block seq in seq in seq 175 | --- yaml 176 | - - - foo 177 | --- events 178 | +STR 179 | +DOC 180 | +SEQ 181 | +SEQ 182 | +SEQ 183 | =VAL :foo 184 | -SEQ 185 | -SEQ 186 | -SEQ 187 | -DOC 188 | -STR 189 | 190 | 191 | === Seq tab after - 192 | --- yaml 193 | - a 194 | --- events 195 | +STR 196 | +DOC 197 | +SEQ 198 | =VAL :a 199 | -SEQ 200 | -DOC 201 | -STR 202 | 203 | 204 | 205 | === Block seq in seq in seq 206 | --- yaml 207 | - - x 208 | --- events 209 | +STR 210 | +DOC 211 | +SEQ 212 | +SEQ 213 | =VAL :x 214 | -SEQ 215 | -SEQ 216 | -DOC 217 | -STR 218 | 219 | 220 | === Block mapping in block mapping 221 | --- yaml 222 | foo: 223 | bar: 42 224 | --- events 225 | +STR 226 | +DOC 227 | +MAP 228 | =VAL :foo 229 | +MAP 230 | =VAL :bar 231 | =VAL :42 232 | -MAP 233 | -MAP 234 | -DOC 235 | -STR 236 | 237 | 238 | === Block map in map in map 239 | --- yaml 240 | foo: 241 | bar: 242 | baz: 42 243 | --- events 244 | +STR 245 | +DOC 246 | +MAP 247 | =VAL :foo 248 | +MAP 249 | =VAL :bar 250 | +MAP 251 | =VAL :baz 252 | =VAL :42 253 | -MAP 254 | -MAP 255 | -MAP 256 | -DOC 257 | -STR 258 | 259 | 260 | === Anchor on map and tag on key 261 | --- yaml 262 | &a1 263 | !t1 a: b 264 | --- events 265 | +STR 266 | +DOC 267 | +MAP &a1 268 | =VAL :a 269 | =VAL :b 270 | -MAP 271 | -DOC 272 | -STR 273 | 274 | 275 | === Tag on map and anchor on key 276 | --- yaml 277 | &a1 278 | !t1 &a2 a: b 279 | --- events 280 | +STR 281 | +DOC 282 | +MAP &a1 283 | =VAL &a2 :a 284 | =VAL :b 285 | -MAP 286 | -DOC 287 | -STR 288 | 289 | 290 | === Anchor on map and tag on key; not top 291 | --- yaml 292 | foo: &a1 293 | !t1 a: b 294 | --- events 295 | +STR 296 | +DOC 297 | +MAP 298 | =VAL :foo 299 | +MAP &a1 300 | =VAL :a 301 | =VAL :b 302 | -MAP 303 | -MAP 304 | -DOC 305 | -STR 306 | 307 | 308 | === XXX 309 | --- SKIP 310 | --- yaml 311 | - ? c: d 312 | : e: f 313 | --- events 314 | $$$ 315 | -------------------------------------------------------------------------------- /test/parse-flow.tml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env testml 2 | 3 | 4 | parse(*yaml) == *events 5 | :"+ -- '{*yaml}'" 6 | 7 | 8 | 9 | === Empty sequence 10 | --- yaml: [] 11 | --- events 12 | +STR 13 | +DOC 14 | +SEQ [] 15 | -SEQ 16 | -DOC 17 | -STR 18 | 19 | 20 | === Flow sequence 21 | --- yaml: [1,2,3] 22 | --- events 23 | +STR 24 | +DOC 25 | +SEQ [] 26 | =VAL :1 27 | =VAL :2 28 | =VAL :3 29 | -SEQ 30 | -DOC 31 | -STR 32 | 33 | 34 | === Flow sequence trailing comma 35 | --- yaml: [1,2,3,] 36 | --- ^events 37 | 38 | 39 | === Flow sequence with ws 40 | --- yaml: [ 1, 2 , 3 ,] 41 | --- ^events 42 | 43 | 44 | === Flow sequence with surrounding ws 45 | --- yaml: " [1, 2, 3] " 46 | --- ^events 47 | 48 | 49 | === Flow sequence multiline 50 | --- yaml 51 | [ 1, 52 | 2, 3] 53 | --- ^events 54 | 55 | 56 | === Flow sequence multiline 57 | --- yaml 58 | [ 1, 59 | 2, 3] 60 | --- ^events 61 | 62 | 63 | === Flow sequence with different styles 64 | --- yaml: [1,two,'three',"four"] 65 | --- events 66 | +STR 67 | +DOC 68 | +SEQ [] 69 | =VAL :1 70 | =VAL :two 71 | =VAL 'three 72 | =VAL "four 73 | -SEQ 74 | -DOC 75 | -STR 76 | 77 | 78 | === Sequence in flow sequence 79 | --- yaml: [[]] 80 | --- events 81 | +STR 82 | +DOC 83 | +SEQ [] 84 | +SEQ [] 85 | -SEQ 86 | -SEQ 87 | -DOC 88 | -STR 89 | 90 | 91 | === Empty flow mapping 92 | --- yaml: {} 93 | --- events 94 | +STR 95 | +DOC 96 | +MAP {} 97 | -MAP 98 | -DOC 99 | -STR 100 | 101 | 102 | === Flow mapping 103 | --- yaml: {foo: bar} 104 | --- events 105 | +STR 106 | +DOC 107 | +MAP {} 108 | =VAL :foo 109 | =VAL :bar 110 | -MAP 111 | -DOC 112 | -STR 113 | 114 | 115 | === Flow mapping 116 | --- yaml: {foo: 'bar', baz: 42} 117 | --- events 118 | +STR 119 | +DOC 120 | +MAP {} 121 | =VAL :foo 122 | =VAL 'bar 123 | =VAL :baz 124 | =VAL :42 125 | -MAP 126 | -DOC 127 | -STR 128 | 129 | 130 | === Flow mapping with empty value 131 | --- yaml: {foo:} 132 | --- events 133 | +STR 134 | +DOC 135 | +MAP {} 136 | =VAL :foo 137 | =VAL : 138 | -MAP 139 | -DOC 140 | -STR 141 | 142 | 143 | === Flow mapping explicit key 144 | --- yaml: {? foo: bar} 145 | --- events 146 | +STR 147 | +DOC 148 | +MAP {} 149 | =VAL :foo 150 | =VAL :bar 151 | -MAP 152 | -DOC 153 | -STR 154 | 155 | 156 | === Leading ? in flow key 157 | --- yaml: {?foo:} 158 | --- events 159 | +STR 160 | +DOC 161 | +MAP {} 162 | =VAL :?foo 163 | =VAL : 164 | -MAP 165 | -DOC 166 | -STR 167 | 168 | 169 | === Key with : 170 | --- yaml: {http://example.com: url} 171 | --- events 172 | +STR 173 | +DOC 174 | +MAP {} 175 | =VAL :http://example.com 176 | =VAL :url 177 | -MAP 178 | -DOC 179 | -STR 180 | 181 | 182 | === Double quoted key no space after : 183 | --- yaml: {"http"://example.com} 184 | --- events 185 | +STR 186 | +DOC 187 | +MAP {} 188 | =VAL "http 189 | =VAL ://example.com 190 | -MAP 191 | -DOC 192 | -STR 193 | 194 | 195 | -------------------------------------------------------------------------------- /test/parse-props.tml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env testml 2 | 3 | 4 | parse(*yaml) == *events 5 | :"+ -- '{*yaml}'" 6 | 7 | 8 | 9 | === Anchor and alias 10 | --- yaml(<) 11 | --- &a1 [*a1] 12 | --- events 13 | +STR 14 | +DOC --- 15 | +SEQ [] &a1 16 | =ALI *a1 17 | -SEQ 18 | -DOC 19 | -STR 20 | 21 | 22 | 23 | === Tag 24 | --- yaml 25 | - !t1 foo 26 | --- events 27 | +STR 28 | +DOC 29 | +SEQ 30 | =VAL :foo 31 | -SEQ 32 | -DOC 33 | -STR 34 | 35 | 36 | 37 | === Tag and Anchor 38 | --- yaml 39 | - !t1 &a1 foo 40 | --- events 41 | +STR 42 | +DOC 43 | +SEQ 44 | =VAL &a1 :foo 45 | -SEQ 46 | -DOC 47 | -STR 48 | 49 | 50 | 51 | === Anchor and Tag 52 | --- yaml 53 | - &a1 !t1 foo 54 | --- ^events 55 | 56 | 57 | 58 | === Anchors on map and elements 59 | --- yaml 60 | &a1 61 | &a2 foo: &a3 bar 62 | --- events 63 | +STR 64 | +DOC 65 | +MAP &a1 66 | =VAL &a2 :foo 67 | =VAL &a3 :bar 68 | -MAP 69 | -DOC 70 | -STR 71 | 72 | 73 | 74 | === Anchors on seq and elements 75 | --- yaml 76 | &a1 77 | - &a2 foo 78 | - &a3 bar 79 | --- events 80 | +STR 81 | +DOC 82 | +SEQ &a1 83 | =VAL &a2 :foo 84 | =VAL &a3 :bar 85 | -SEQ 86 | -DOC 87 | -STR 88 | 89 | 90 | 91 | === Anchors on flow map and elements 92 | --- yaml 93 | &a1 { &a2 foo: &a3 bar } 94 | --- events 95 | +STR 96 | +DOC 97 | +MAP {} &a1 98 | =VAL &a2 :foo 99 | =VAL &a3 :bar 100 | -MAP 101 | -DOC 102 | -STR 103 | 104 | 105 | 106 | === Anchors on flow seq and elements 107 | --- yaml 108 | &a1 [ &a2 foo, &a3 bar ] 109 | --- events 110 | +STR 111 | +DOC 112 | +SEQ [] &a1 113 | =VAL &a2 :foo 114 | =VAL &a3 :bar 115 | -SEQ 116 | -DOC 117 | -STR 118 | 119 | 120 | 121 | === Anchors on map key 122 | --- yaml 123 | &a1 foo: bar 124 | --- events 125 | +STR 126 | +DOC 127 | +MAP 128 | =VAL &a1 :foo 129 | =VAL :bar 130 | -MAP 131 | -DOC 132 | -STR 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /test/parse-scalar.tml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env testml 2 | 3 | 4 | parse(*yaml) == *events 5 | :"+ -- '{*yaml}'" 6 | 7 | 8 | 9 | === Top plain scalar 10 | --- yaml: foo 11 | --- events 12 | +STR 13 | +DOC 14 | =VAL :foo 15 | -DOC 16 | -STR 17 | 18 | 19 | === Top plain with newline 20 | --- yaml 21 | foo 22 | --- ^events 23 | 24 | 25 | === Top plain with extra ws 26 | --- yaml: " foo " 27 | --- ^events 28 | 29 | 30 | === Comment before node 31 | --- yaml(<) 32 | # comment 33 | 34 | foo 35 | --- ^events 36 | 37 | 38 | === Top level scalar with header 39 | --- yaml(<) 40 | --- foo 41 | --- events 42 | +STR 43 | +DOC --- 44 | =VAL :foo 45 | -DOC 46 | -STR 47 | 48 | 49 | === Top level double quoted scalar 50 | --- yaml(-) 51 | "foo" 52 | --- events 53 | +STR 54 | +DOC 55 | =VAL "foo 56 | -DOC 57 | -STR 58 | 59 | 60 | === Top level single quoted sclaar 61 | --- yaml: "'foo'" 62 | --- events 63 | +STR 64 | +DOC 65 | =VAL 'foo 66 | -DOC 67 | -STR 68 | 69 | 70 | === Top level literal scalar 71 | --- yaml(<) 72 | --- | 73 | foo 74 | bar 75 | --- events 76 | +STR 77 | +DOC --- 78 | =VAL |foo\nbar\n 79 | -DOC 80 | -STR 81 | 82 | 83 | === Literal scalar with indent 84 | --- yaml(<) 85 | - |2 86 | foo 87 | bar 88 | --- events 89 | +STR 90 | +DOC 91 | +SEQ 92 | =VAL | foo\n bar\n 93 | -SEQ 94 | -DOC 95 | -STR 96 | 97 | 98 | -------------------------------------------------------------------------------- /test/parse-stream.tml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env testml 2 | 3 | 4 | parse(*yaml) == *events 5 | :"+ -- '{*yaml}'" 6 | 7 | 8 | 9 | === Empty stream 10 | --- yaml: "" 11 | --- events 12 | +STR 13 | -STR 14 | 15 | 16 | === Empty stream with comments 17 | --- yaml(<) 18 | # comment 19 | 20 | # blank line above 21 | --- ^events 22 | 23 | 24 | === Block node with header and footer 25 | --- yaml(<) 26 | --- 27 | foo: 28 | - bar 29 | ... 30 | --- events 31 | +STR 32 | +DOC --- 33 | +MAP 34 | =VAL :foo 35 | +SEQ 36 | =VAL :bar 37 | -SEQ 38 | -MAP 39 | -DOC ... 40 | -STR 41 | 42 | 43 | 44 | === Two flow nodes with header 45 | --- yaml(<) 46 | --- [foo, bar] 47 | --- {foo: bar} 48 | --- events 49 | +STR 50 | +DOC --- 51 | +SEQ [] 52 | =VAL :foo 53 | =VAL :bar 54 | -SEQ 55 | -DOC 56 | +DOC --- 57 | +MAP {} 58 | =VAL :foo 59 | =VAL :bar 60 | -MAP 61 | -DOC 62 | -STR 63 | 64 | 65 | 66 | === Two flow nodes with header and footer 67 | --- yaml(<) 68 | --- [foo, bar] 69 | ... 70 | --- {foo: bar} 71 | ... 72 | --- events 73 | +STR 74 | +DOC --- 75 | +SEQ [] 76 | =VAL :foo 77 | =VAL :bar 78 | -SEQ 79 | -DOC ... 80 | +DOC --- 81 | +MAP {} 82 | =VAL :foo 83 | =VAL :bar 84 | -MAP 85 | -DOC ... 86 | -STR 87 | 88 | 89 | 90 | === Two flow nodes with header and footer w/ comment 91 | --- yaml(<) 92 | --- # comment 93 | [foo, bar] 94 | ... # comment 95 | --- # comment 96 | {foo: bar} 97 | ... # comment 98 | --- events 99 | +STR 100 | +DOC --- 101 | +SEQ [] 102 | =VAL :foo 103 | =VAL :bar 104 | -SEQ 105 | -DOC ... 106 | +DOC --- 107 | +MAP {} 108 | =VAL :foo 109 | =VAL :bar 110 | -MAP 111 | -DOC ... 112 | -STR 113 | 114 | 115 | 116 | === Two block nodes with header and footer 117 | --- yaml(<) 118 | --- 119 | - foo 120 | - bar 121 | ... 122 | --- 123 | foo: bar 124 | ... 125 | --- events 126 | +STR 127 | +DOC --- 128 | +SEQ 129 | =VAL :foo 130 | =VAL :bar 131 | -SEQ 132 | -DOC ... 133 | +DOC --- 134 | +MAP 135 | =VAL :foo 136 | =VAL :bar 137 | -MAP 138 | -DOC ... 139 | -STR 140 | 141 | 142 | 143 | === Two scalar nodes with header and footer 144 | --- yaml(<) 145 | --- foo 146 | bar 147 | ... 148 | --- baz 149 | ... 150 | --- events 151 | +STR 152 | +DOC --- 153 | =VAL :foo bar 154 | -DOC ... 155 | +DOC --- 156 | =VAL :baz 157 | -DOC ... 158 | -STR 159 | 160 | 161 | 162 | === Empty stream with footer 163 | --- yaml 164 | ... 165 | --- events 166 | +STR 167 | -STR 168 | 169 | 170 | 171 | === Empty stream with footer; no final newline 172 | --- yaml(-) 173 | ... 174 | --- ^events 175 | 176 | 177 | 178 | === Stream with just header 179 | --- yaml(<) 180 | --- 181 | --- events 182 | +STR 183 | +DOC --- 184 | =VAL : 185 | -DOC 186 | -STR 187 | 188 | 189 | 190 | === Stream with just header; no final newline 191 | --- yaml(<-) 192 | --- 193 | --- ^events 194 | -------------------------------------------------------------------------------- /test/parse-suite.tml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env testml 2 | 3 | 4 | "({*Name}) {*Label} -- '{*in-yaml}'": 5 | *in-yaml.unescape.parse.fix-test-output == *test-event.unescape 6 | 7 | 8 | %Import yaml-test-suite 9 | --------------------------------------------------------------------------------