diff --git a/Tmain/flags-fielddef-datatype.d/stdout-expected.txt b/Tmain/flags-fielddef-datatype.d/stdout-expected.txt index 9bbd7f209c..c62077a2ad 100644 --- a/Tmain/flags-fielddef-datatype.d/stdout-expected.txt +++ b/Tmain/flags-fielddef-datatype.d/stdout-expected.txt @@ -1,6 +1,6 @@ #LETTER NAME ENABLED LANGUAGE JSTYPE FIXED OP DESCRIPTION -- boolfield no FIELDTEST --b no -- a field having boolean value +- boolfield no FIELDTEST --b no rw a field having boolean value - deffield no FIELDTEST s-- no -- a field that type is not specified -- intfield no FIELDTEST -i- no -- a field having integer value -- strboolfield no FIELDTEST s-b no -- a field having string value or false -- strfield no FIELDTEST s-- no -- a field having string value +- intfield no FIELDTEST -i- no rw a field having integer value +- strboolfield no FIELDTEST s-b no rw a field having string value or false +- strfield no FIELDTEST s-- no rw a field having string value diff --git a/Tmain/list-fielddef-flags.d/stdout-expected.txt b/Tmain/list-fielddef-flags.d/stdout-expected.txt index 64d73e8ccc..3049b06970 100644 --- a/Tmain/list-fielddef-flags.d/stdout-expected.txt +++ b/Tmain/list-fielddef-flags.d/stdout-expected.txt @@ -1,2 +1,2 @@ #LETTER NAME DESCRIPTION -- datatype=TYPE acceaptable datatype of the field ([str]|bool|int|str+bool) +- datatype=TYPE acceaptable datatype of the field (str|bool|int|str+bool) diff --git a/Tmain/list-fields-with-prefix.d/stdout-expected.txt b/Tmain/list-fields-with-prefix.d/stdout-expected.txt index 838c1d1be8..1d953ac929 100644 --- a/Tmain/list-fields-with-prefix.d/stdout-expected.txt +++ b/Tmain/list-fields-with-prefix.d/stdout-expected.txt @@ -31,7 +31,7 @@ x UCTAGSxpath no NONE s-- no -- xpath for - UCTAGShowImported no Go s-- no -- how the package is imported ("inline" for `.' or "init" for `_') - UCTAGSpackage yes Go s-- no -- the real package specified by the package name - UCTAGSpackageName yes Go s-- no -- the name for referring the package -- UCTAGSimplements yes Inko s-- no -- Trait being implemented +- UCTAGSimplements yes Inko s-- no rw Trait being implemented - UCTAGSassignment yes LdScript s-- no -- how a value is assigned to the symbol - UCTAGSdefiner yes Lisp s-- no -- the name of the function or macro that defines the unknown/Y-kind object - UCTAGSsectionMarker no Markdown s-- no -- character used for declaring section(#, ##, =, or -) @@ -39,8 +39,8 @@ x UCTAGSxpath no NONE s-- no -- xpath for - UCTAGSlangid yes NSIS s-- no -- language identifier specified in (License)LangString commands - UCTAGScategory yes ObjectiveC s-- no -- category attached to the class - UCTAGSprotocols yes ObjectiveC s-- no -- protocols that the class (or category) confirms to -- UCTAGShome yes Passwd s-- no -- home directory -- UCTAGSshell yes Passwd s-- no -- login shell +- UCTAGShome yes Passwd s-- no rw home directory +- UCTAGSshell yes Passwd s-- no rw login shell - UCTAGSdecorators no Python s-- no -- decorators on functions and classes - UCTAGSnameref yes Python s-- no -- the original name for the tag - UCTAGSassignmentop no R s-- no -- operator for assignment @@ -48,6 +48,7 @@ x UCTAGSxpath no NONE s-- no -- xpath for - UCTAGSoverline no ReStructuredText --b no -- whether using overline & underline for declaring section - UCTAGSsectionMarker no ReStructuredText s-- no -- character used for declaring section - UCTAGSmixin yes Ruby s-- no -- how the class or module is mixed in (mixin:HOW:MODULE) +- UCTAGSmodule yes SCSS s-- no rw the name of module behind the namespace - UCTAGSdefiner yes Scheme s-- no -- the name of the function or macro that defines the unknown/Y-kind object - UCTAGSparameter no SystemVerilog --b no -- parameter whose value can be overridden. - UCTAGStarget yes Thrift s-- no -- the target language specified at "namespace" diff --git a/Tmain/list-fields.d/stdout-expected.txt b/Tmain/list-fields.d/stdout-expected.txt index fb4587a956..04d1c45cf6 100644 --- a/Tmain/list-fields.d/stdout-expected.txt +++ b/Tmain/list-fields.d/stdout-expected.txt @@ -49,7 +49,7 @@ z kind no NONE s-- no r- [tags output] prepend "kind:" to k/ (or K/) field outpu - howImported no Go s-- no -- how the package is imported ("inline" for `.' or "init" for `_') - package yes Go s-- no -- the real package specified by the package name - packageName yes Go s-- no -- the name for referring the package -- implements yes Inko s-- no -- Trait being implemented +- implements yes Inko s-- no rw Trait being implemented - assignment yes LdScript s-- no -- how a value is assigned to the symbol - definer yes Lisp s-- no -- the name of the function or macro that defines the unknown/Y-kind object - sectionMarker no Markdown s-- no -- character used for declaring section(#, ##, =, or -) @@ -57,8 +57,8 @@ z kind no NONE s-- no r- [tags output] prepend "kind:" to k/ (or K/) field outpu - langid yes NSIS s-- no -- language identifier specified in (License)LangString commands - category yes ObjectiveC s-- no -- category attached to the class - protocols yes ObjectiveC s-- no -- protocols that the class (or category) confirms to -- home yes Passwd s-- no -- home directory -- shell yes Passwd s-- no -- login shell +- home yes Passwd s-- no rw home directory +- shell yes Passwd s-- no rw login shell - decorators no Python s-- no -- decorators on functions and classes - nameref yes Python s-- no -- the original name for the tag - assignmentop no R s-- no -- operator for assignment @@ -66,6 +66,7 @@ z kind no NONE s-- no r- [tags output] prepend "kind:" to k/ (or K/) field outpu - overline no ReStructuredText --b no -- whether using overline & underline for declaring section - sectionMarker no ReStructuredText s-- no -- character used for declaring section - mixin yes Ruby s-- no -- how the class or module is mixed in (mixin:HOW:MODULE) +- module yes SCSS s-- no rw the name of module behind the namespace - definer yes Scheme s-- no -- the name of the function or macro that defines the unknown/Y-kind object - parameter no SystemVerilog --b no -- parameter whose value can be overridden. - target yes Thrift s-- no -- the target language specified at "namespace" diff --git a/Tmain/optscript.d/error-undefined-if-if.expected b/Tmain/optscript.d/error-undefined-if-if.expected index bd92d0202e..ab131c6bc9 100644 --- a/Tmain/optscript.d/error-undefined-if-if.expected +++ b/Tmain/optscript.d/error-undefined-if-if.expected @@ -4,4 +4,4 @@ top| |bottom Execution stack: top| a {a} --if-- {true {a} if} --if-- |bottom Dictionary stack: -top| -dict:1- -dict:89- |bottom +top| -dict:1- -dict:91- |bottom diff --git a/Tmain/optscript.d/error-undefined-if.expected b/Tmain/optscript.d/error-undefined-if.expected index daed401f05..39d74f529e 100644 --- a/Tmain/optscript.d/error-undefined-if.expected +++ b/Tmain/optscript.d/error-undefined-if.expected @@ -4,4 +4,4 @@ top| |bottom Execution stack: top| a {a} --if-- |bottom Dictionary stack: -top| -dict:1- -dict:89- |bottom +top| -dict:1- -dict:91- |bottom diff --git a/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/input.unknownx b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/input.unknownx new file mode 100644 index 0000000000..f6510db7d6 --- /dev/null +++ b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/input.unknownx @@ -0,0 +1,10 @@ +public func foo(n, m); +protected func bar(n); +private func baz(n,...); +STR:strset@iamowner +STR:setsetempty@ +STR:notset +Y:iamowner2=tagme2 +Z:tagme-z@iamowner-z +eset:a +enoset:b diff --git a/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/knownz.ctags b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/knownz.ctags new file mode 100644 index 0000000000..92799bce41 --- /dev/null +++ b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/knownz.ctags @@ -0,0 +1,12 @@ +--langdef=knownz +--kinddef-knownz=m,mark,makers + +--_fielddef-knownz=owner,the owner of the markers{datatype=str} + +--_fielddef-knownz=len,the length of owner string{datatype=int} +--fields-knownz=+{len} +--_fielddef-knownz=lenplus,the length of owner string + 1{datatype=int} +--fields-knownz=+{lenplus} + +--_fielddef-knownz=exported,whether the marker is exported or not{datatype=bool} +--fields-knownz=+{exported} diff --git a/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/run.sh b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/run.sh new file mode 100644 index 0000000000..72756253fb --- /dev/null +++ b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/run.sh @@ -0,0 +1,19 @@ +# Copyright: 2025 Masatake YAMATO +# License: GPL-2 + +. ../utils.sh + +CTAGS=$1 + +V= +# V=valgrind + +is_feature_available "${CTAGS}" json + +echo "# output: json" +${V} ${CTAGS} --options=NONE --options=./knownz.ctags --sort=no --options=./unknownx.ctags \ + --fields=+l \ + --fields-unknownx=+'{protection}{signature}' \ + --fields-knownz=+'{owner}' \ + --output-format=json \ + -o - input.unknownx diff --git a/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/stderr-expected.txt b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/stderr-expected.txt new file mode 100644 index 0000000000..fd87c9c6da --- /dev/null +++ b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/stderr-expected.txt @@ -0,0 +1 @@ +ctags: Notice: No options will be read from files or environment diff --git a/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/stdout-expected.txt b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/stdout-expected.txt new file mode 100644 index 0000000000..5046b29aaa --- /dev/null +++ b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/stdout-expected.txt @@ -0,0 +1,12 @@ +# output: json +{"_type": "tag", "name": "foo", "path": "input.unknownx", "pattern": "/^public func foo(n, m);$/", "language": "unknownx", "kind": "func", "protection": "public ", "signature": "(n, m)"} +{"_type": "tag", "name": "bar", "path": "input.unknownx", "pattern": "/^protected func bar(n);$/", "language": "unknownx", "kind": "func", "protection": "protected ", "signature": "(n)"} +{"_type": "tag", "name": "baz", "path": "input.unknownx", "pattern": "/^private func baz(n,...);$/", "language": "unknownx", "kind": "func", "protection": "private ", "signature": "(n,...)"} +{"_type": "tag", "name": "strset", "path": "input.unknownx", "pattern": "/^STR:strset@iamowner$/", "language": "knownz", "kind": "mark", "owner": "iamowner"} +{"_type": "tag", "name": "setsetempty", "path": "input.unknownx", "pattern": "/^STR:setsetempty@$/", "language": "knownz", "kind": "mark", "owner": ""} +{"_type": "tag", "name": "notset", "path": "input.unknownx", "pattern": "/^STR:notset$/", "language": "knownz", "kind": "mark"} +{"_type": "tag", "name": "tagme2", "path": "input.unknownx", "pattern": "/^Y:iamowner2=tagme2$/", "language": "knownz", "kind": "mark", "owner": "iamowner2"} +{"_type": "tag", "name": "tagme-z", "path": "input.unknownx", "pattern": "/^Z:tagme-z@iamowner-z$/", "language": "knownz", "kind": "mark", "owner": "iamowner-z", "len": 10, "lenplus": 11} +{"_type": "tag", "name": "a", "path": "input.unknownx", "pattern": "/^eset:a$/", "language": "knownz", "kind": "mark", "exported": true} +{"_type": "tag", "name": "b", "path": "input.unknownx", "pattern": "/^enoset:b$/", "language": "knownz", "kind": "mark"} +{"_type": "tag", "name": "9_exported", "path": "input.unknownx", "pattern": "/^enoset:b$/", "language": "knownz", "kind": "mark"} diff --git a/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/unknownx.ctags b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/unknownx.ctags new file mode 100644 index 0000000000..dcc647137a --- /dev/null +++ b/Tmain/parser-specific-fields-for-foreign-lang-in-json.d/unknownx.ctags @@ -0,0 +1,41 @@ +--langdef=unknownx{_foreignLanguage=knownz} +--kinddef-unknownx=f,func,functions +--map-unknownx=+.unknownx + +--_fielddef-unknownx=protection,protections +--_fielddef-unknownx=signature,signatures + +--_prelude-unknownx={{ + /exported false def +}} + +--regex-unknownx=/^((public|protected|private) +)?func ([^\(]+)\((.*)\)/\3/f/{_field=protection:\1}{_field=signature:(\4)} +--regex-unknownx=/^STR:([a-z]+)@([a-z]+)/\1/m/{_language=knownz}{_field=owner:\2} +--regex-unknownx=/^STR:([a-z]+)@$/\1/m/{_language=knownz}{_field=owner:} +--regex-unknownx=/^STR:([a-z]+)$/\1/m/{_language=knownz} +--regex-unknownx=/^Y:([a-z0-9]+)=([a-z0-9]+)/\2/m/{_field=owner:\1}{_language=knownz} +--regex-unknownx=/^Z:([-a-z]+)@([-a-z]+)/\1/m/{_language=knownz}{{ + . \2 knownz.owner: + . :knownz.owner { + . exch length knownz.len: + } if + . :knownz.len { + 1 add + . exch knownz.lenplus: + } if +}} + +--regex-unknownx=/^eset:([-a-z]+)/\1/m/{_language=knownz}{{ + /exported . def + . true knownz.exported: +}} + +--regex-unknownx=/^enoset:([-a-z]+)/\1/m/{_language=knownz}{{ + . false knownz.exported: + exported :knownz.exported and { + mark exported 0 string cvs (_exported) _buildstring + /knownz + /mark + 1@ _foreigntag _commit pop + } if +}} diff --git a/Tmain/parser-specific-fields-for-foreign-lang.d/input.unknownx b/Tmain/parser-specific-fields-for-foreign-lang.d/input.unknownx index 8ce5984bbc..a84cd763af 100644 --- a/Tmain/parser-specific-fields-for-foreign-lang.d/input.unknownx +++ b/Tmain/parser-specific-fields-for-foreign-lang.d/input.unknownx @@ -1,5 +1,16 @@ public func foo(n, m); protected func bar(n); private func baz(n,...); -X:tagme@iamowner +STR:strset@iamowner +STR:setsetempty@ +STR:notset Y:iamowner2=tagme2 +Z:tagme-z@iamowner-z +eset:a +enoset:b + +stb:o +stb:p- +stb:q.something string +x0:O +x1:P diff --git a/Tmain/parser-specific-fields-for-foreign-lang.d/knownz.ctags b/Tmain/parser-specific-fields-for-foreign-lang.d/knownz.ctags index bf91c87100..cc23b60b87 100644 --- a/Tmain/parser-specific-fields-for-foreign-lang.d/knownz.ctags +++ b/Tmain/parser-specific-fields-for-foreign-lang.d/knownz.ctags @@ -1,4 +1,15 @@ --langdef=knownz --kinddef-knownz=m,mark,makers ---_fielddef-knownz=owner,the owner of the markers +--_fielddef-knownz=owner,the owner of the markers{datatype=str} + +--_fielddef-knownz=len,the length of owner string{datatype=int} +--fields-knownz=+{len} +--_fielddef-knownz=lenplus,the length of owner string + 1{datatype=int} +--fields-knownz=+{lenplus} + +--_fielddef-knownz=exported,whether the marker is exported or not{datatype=bool} +--fields-knownz=+{exported} + +--_fielddef-knownz=stb,string or false{datatype=str+bool} +--fields-knownz=+{stb} diff --git a/Tmain/parser-specific-fields-for-foreign-lang.d/run.sh b/Tmain/parser-specific-fields-for-foreign-lang.d/run.sh index 9abae09deb..765464235d 100644 --- a/Tmain/parser-specific-fields-for-foreign-lang.d/run.sh +++ b/Tmain/parser-specific-fields-for-foreign-lang.d/run.sh @@ -8,8 +8,17 @@ CTAGS=$1 V= # V=valgrind -${V} ${CTAGS} --options=NONE --options=./knownz.ctags --options=./unknownx.ctags \ +echo "# output: tags" +${V} ${CTAGS} --options=NONE --options=./knownz.ctags --sort=no --options=./unknownx.ctags \ --fields=+l \ --fields-unknownx=+'{protection}{signature}' \ --fields-knownz=+'{owner}' \ -o - input.unknownx + +echo "# output: xref" +${V} ${CTAGS} --options=NONE --options=./knownz.ctags --sort=no --options=./unknownx.ctags \ + --fields=+l \ + --fields-unknownx=+'{protection}{signature}' \ + --fields-knownz=+'{owner}' \ + -x --_xformat="%N %l / owner:%{knownz.owner},len:%{knownz.len},lenplus:%{knownz.lenplus},exported:%{knownz.exported},stb:%{knownz.stb} / %{unknownx.protection}%{unknownx.signature}" \ + -o - input.unknownx diff --git a/Tmain/parser-specific-fields-for-foreign-lang.d/stderr-expected.txt b/Tmain/parser-specific-fields-for-foreign-lang.d/stderr-expected.txt index fd87c9c6da..2bcdb8cd4d 100644 --- a/Tmain/parser-specific-fields-for-foreign-lang.d/stderr-expected.txt +++ b/Tmain/parser-specific-fields-for-foreign-lang.d/stderr-expected.txt @@ -1 +1,20 @@ ctags: Notice: No options will be read from files or environment +/not-set +false +(something string) +# Setting a string to a bool field +true +true +# Setting a string to a int field +true +1 +ctags: Notice: No options will be read from files or environment +/not-set +false +(something string) +# Setting a string to a bool field +true +true +# Setting a string to a int field +true +1 diff --git a/Tmain/parser-specific-fields-for-foreign-lang.d/stdout-expected.txt b/Tmain/parser-specific-fields-for-foreign-lang.d/stdout-expected.txt index 690da2ad7a..98999bcb6e 100644 --- a/Tmain/parser-specific-fields-for-foreign-lang.d/stdout-expected.txt +++ b/Tmain/parser-specific-fields-for-foreign-lang.d/stdout-expected.txt @@ -1,5 +1,34 @@ +# output: tags +foo input.unknownx /^public func foo(n, m);$/;" f language:unknownx protection:public signature:(n, m) bar input.unknownx /^protected func bar(n);$/;" f language:unknownx protection:protected signature:(n) baz input.unknownx /^private func baz(n,...);$/;" f language:unknownx protection:private signature:(n,...) -foo input.unknownx /^public func foo(n, m);$/;" f language:unknownx protection:public signature:(n, m) -tagme input.unknownx /^X:tagme@iamowner$/;" m language:knownz owner:iamowner +strset input.unknownx /^STR:strset@iamowner$/;" m language:knownz owner:iamowner +setsetempty input.unknownx /^STR:setsetempty@$/;" m language:knownz owner: +notset input.unknownx /^STR:notset$/;" m language:knownz tagme2 input.unknownx /^Y:iamowner2=tagme2$/;" m language:knownz owner:iamowner2 +tagme-z input.unknownx /^Z:tagme-z@iamowner-z$/;" m language:knownz owner:iamowner-z len:10 lenplus:11 +a input.unknownx /^eset:a$/;" m language:knownz exported: +b input.unknownx /^enoset:b$/;" m language:knownz +9_exported input.unknownx /^enoset:b$/;" m language:knownz +o input.unknownx /^stb:o$/;" m language:knownz +p input.unknownx /^stb:p-$/;" m language:knownz stb: +q input.unknownx /^stb:q.something string$/;" m language:knownz stb:something string +O input.unknownx /^x0:O$/;" m language:knownz exported: +P input.unknownx /^x1:P$/;" m language:knownz len:1 +# output: xref +foo unknownx / owner:,len:,lenplus:,exported:-,stb: / public (n, m) +bar unknownx / owner:,len:,lenplus:,exported:-,stb: / protected (n) +baz unknownx / owner:,len:,lenplus:,exported:-,stb: / private (n,...) +strset knownz / owner:iamowner,len:,lenplus:,exported:-,stb: / +setsetempty knownz / owner:,len:,lenplus:,exported:-,stb: / +notset knownz / owner:,len:,lenplus:,exported:-,stb: / +tagme2 knownz / owner:iamowner2,len:,lenplus:,exported:-,stb: / +tagme-z knownz / owner:iamowner-z,len:10,lenplus:11,exported:-,stb: / +a knownz / owner:,len:,lenplus:,exported:exported,stb: / +b knownz / owner:,len:,lenplus:,exported:-,stb: / +9_exported knownz / owner:,len:,lenplus:,exported:-,stb: / +o knownz / owner:,len:,lenplus:,exported:-,stb: / +p knownz / owner:,len:,lenplus:,exported:-,stb:- / +q knownz / owner:,len:,lenplus:,exported:-,stb:something string / +O knownz / owner:,len:,lenplus:,exported:exported,stb: / +P knownz / owner:,len:1,lenplus:,exported:-,stb: / diff --git a/Tmain/parser-specific-fields-for-foreign-lang.d/unknownx.ctags b/Tmain/parser-specific-fields-for-foreign-lang.d/unknownx.ctags index 523a670b34..367fae6fc3 100644 --- a/Tmain/parser-specific-fields-for-foreign-lang.d/unknownx.ctags +++ b/Tmain/parser-specific-fields-for-foreign-lang.d/unknownx.ctags @@ -5,6 +5,70 @@ --_fielddef-unknownx=protection,protections --_fielddef-unknownx=signature,signatures +--_prelude-unknownx={{ + /exported false def +}} + --regex-unknownx=/^((public|protected|private) +)?func ([^\(]+)\((.*)\)/\3/f/{_field=protection:\1}{_field=signature:(\4)} ---regex-unknownx=/^X:([a-z]+)@([a-z]+)/\1/m/{_language=knownz}{_field=owner:\2} +--regex-unknownx=/^STR:([a-z]+)@([a-z]+)/\1/m/{_language=knownz}{_field=owner:\2} +--regex-unknownx=/^STR:([a-z]+)@$/\1/m/{_language=knownz}{_field=owner:} +--regex-unknownx=/^STR:([a-z]+)$/\1/m/{_language=knownz} --regex-unknownx=/^Y:([a-z0-9]+)=([a-z0-9]+)/\2/m/{_field=owner:\1}{_language=knownz} +--regex-unknownx=/^Z:([-a-z]+)@([-a-z]+)/\1/m/{_language=knownz}{{ + . \2 knownz.owner: + . :knownz.owner { + . exch length knownz.len: + } if + . :knownz.len { + 1 add + . exch knownz.lenplus: + } if +}} + +--regex-unknownx=/^eset:([-a-z]+)/\1/m/{_language=knownz}{{ + /exported . def + . true knownz.exported: +}} + +--regex-unknownx=/^enoset:([-a-z]+)/\1/m/{_language=knownz}{{ + . false knownz.exported: + exported :knownz.exported and { + mark exported 0 string cvs (_exported) _buildstring + /knownz + /mark + 1@ _foreigntag _commit pop + } if +}} + +--regex-unknownx=/^stb:([a-z])$/\1/m/{_language=knownz}{{ + . :knownz.stb { + == + } { + /not-set == + } ifelse +}} +--regex-unknownx=/^stb:([a-z])-$/\1/m/{_language=knownz}{_field=stb:}{{ + . :knownz.stb { + == + } { + /not-set == + } ifelse +}} +--regex-unknownx=/^stb:([a-z])\.(.*)$/\1/m/{_language=knownz}{_field=stb:\2}{{ + . :knownz.stb { + == + } { + /not-set == + } ifelse +}} + +--regex-unknownx=/^x0:([a-zA-Z])$/\1/m/{_language=knownz}{_field=exported:alpha}{{ + (# Setting a string to a bool field) = + . :knownz.exported pstack clear +}} + +# Setting string to a int field +--regex-unknownx=/^x1:([a-zA-Z])$/\1/m/{_language=knownz}{_field=len:alpha}{{ + (# Setting a string to a int field) = + . :knownz.len pstack clear +}} diff --git a/Tmain/parser-specific-fields-with-scripts.d/exit-expected.txt b/Tmain/parser-specific-fields-with-scripts.d/exit-expected.txt new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/Tmain/parser-specific-fields-with-scripts.d/exit-expected.txt @@ -0,0 +1 @@ +0 diff --git a/Tmain/parser-specific-fields-with-scripts.d/input.testlang b/Tmain/parser-specific-fields-with-scripts.d/input.testlang new file mode 100644 index 0000000000..8e40f000e3 --- /dev/null +++ b/Tmain/parser-specific-fields-with-scripts.d/input.testlang @@ -0,0 +1,31 @@ +string => string +string => integer +string => bool +string => strbool +int => string +int => integer +int => bool +int => strbool +true => string +true => integer +true => bool +true => strbool +false => string +false => integer +false => bool +false => strbool +array => string +array => integer +array => bool +array => strbool + +empty string => string +empty string => integer +empty string => bool +empty string => strbool + +int => +bool => +strbool => + +true => bool, false => true diff --git a/Tmain/parser-specific-fields-with-scripts.d/run.sh b/Tmain/parser-specific-fields-with-scripts.d/run.sh new file mode 100755 index 0000000000..d7e1df369c --- /dev/null +++ b/Tmain/parser-specific-fields-with-scripts.d/run.sh @@ -0,0 +1,28 @@ +# Copyright: 2025 Masatake YAMATO +# License: GPL-2 + +CTAGS="$1" +CMDLINE="$CTAGS --quiet --options=NONE --options=./testlang.ctags" + +. ../utils.sh + +is_feature_available "$1" json + +echo '### ctags' && +${CMDLINE} \ + --param-Testlang.dp=true \ + -o - input.testlang && + +echo '### xref' && +${CMDLINE} \ + --_xformat="%-16N :: str:%{Testlang.str} int:%{Testlang.int} bool:%{Testlang.bool} strbool:%{Testlang.strbool}" \ + -x input.testlang && + +echo '### json' && +${CMDLINE} \ + --output-format=json -o - input.testlang && + +echo '### etags' && +${CMDLINE} \ + --output-format=etags -o - input.testlang && +: diff --git a/Tmain/parser-specific-fields-with-scripts.d/stderr-expected.txt b/Tmain/parser-specific-fields-with-scripts.d/stderr-expected.txt new file mode 100644 index 0000000000..a11b07828a --- /dev/null +++ b/Tmain/parser-specific-fields-with-scripts.d/stderr-expected.txt @@ -0,0 +1,28 @@ +string => string...OK stringtype/(str) +string => integer...XERR typecheck +string => bool...XERR typecheck +string => strbool...OK stringtype/(str) +int => string...XERR typecheck +int => integer...OK integertype/17 +int => bool...XERR typecheck +int => strbool...XERR typecheck +true => string...XERR typecheck +true => integer...XERR typecheck +true => bool...OK booleantype/true +true => strbool...XERR typecheck +false => string...XERR typecheck +false => integer...XERR typecheck +false => bool...OK /noval +false => strbool...OK booleantype/false +array => string...XERR typecheck +array => integer...XERR typecheck +array => bool...XERR typecheck +array => strbool...XERR typecheck +empty string => string...OK stringtype/() +empty string => integer...XERR typecheck +empty string => bool...XERR typecheck +empty string => strbool...OK booleantype/false +int =>...OK /noval +bool =>...OK /noval +strbool =>...OK /noval +true => bool, false => true...XERR fieldreset diff --git a/Tmain/parser-specific-fields-with-scripts.d/stdout-expected.txt b/Tmain/parser-specific-fields-with-scripts.d/stdout-expected.txt new file mode 100644 index 0000000000..be86b3f1d5 --- /dev/null +++ b/Tmain/parser-specific-fields-with-scripts.d/stdout-expected.txt @@ -0,0 +1,118 @@ +### ctags +string => string input.testlang /^string => string$/;" o str:str +string => integer input.testlang /^string => integer$/;" o +string => bool input.testlang /^string => bool$/;" o +string => strbool input.testlang /^string => strbool$/;" o strbool:str +int => string input.testlang /^int => string$/;" o +int => integer input.testlang /^int => integer$/;" o int:17 +int => bool input.testlang /^int => bool$/;" o +int => strbool input.testlang /^int => strbool$/;" o +true => string input.testlang /^true => string$/;" o +true => integer input.testlang /^true => integer$/;" o +true => bool input.testlang /^true => bool$/;" o bool: +true => strbool input.testlang /^true => strbool$/;" o +false => string input.testlang /^false => string$/;" o +false => integer input.testlang /^false => integer$/;" o +false => bool input.testlang /^false => bool$/;" o +false => strbool input.testlang /^false => strbool$/;" o strbool: +array => string input.testlang /^array => string$/;" o +array => integer input.testlang /^array => integer$/;" o +array => bool input.testlang /^array => bool$/;" o +array => strbool input.testlang /^array => strbool$/;" o +empty string => string input.testlang /^empty string => string$/;" o str: +empty string => integer input.testlang /^empty string => integer$/;" o +empty string => bool input.testlang /^empty string => bool$/;" o +empty string => strbool input.testlang /^empty string => strbool$/;" o strbool: +int => input.testlang /^int =>$/;" o +bool => input.testlang /^bool =>$/;" o +strbool => input.testlang /^strbool =>$/;" o +true => bool, false => true input.testlang /^true => bool, false => true$/;" o bool: +### xref +string => string :: str:str int: bool:- strbool: +string => integer :: str: int: bool:- strbool: +string => bool :: str: int: bool:- strbool: +string => strbool :: str: int: bool:- strbool:str +int => string :: str: int: bool:- strbool: +int => integer :: str: int:17 bool:- strbool: +int => bool :: str: int: bool:- strbool: +int => strbool :: str: int: bool:- strbool: +true => string :: str: int: bool:- strbool: +true => integer :: str: int: bool:- strbool: +true => bool :: str: int: bool:bool strbool: +true => strbool :: str: int: bool:- strbool: +false => string :: str: int: bool:- strbool: +false => integer :: str: int: bool:- strbool: +false => bool :: str: int: bool:- strbool: +false => strbool :: str: int: bool:- strbool:- +array => string :: str: int: bool:- strbool: +array => integer :: str: int: bool:- strbool: +array => bool :: str: int: bool:- strbool: +array => strbool :: str: int: bool:- strbool: +empty string => string :: str: int: bool:- strbool: +empty string => integer :: str: int: bool:- strbool: +empty string => bool :: str: int: bool:- strbool: +empty string => strbool :: str: int: bool:- strbool:- +int => :: str: int: bool:- strbool: +bool => :: str: int: bool:- strbool: +strbool => :: str: int: bool:- strbool: +true => bool, false => true :: str: int: bool:bool strbool: +### json +{"_type": "tag", "name": "string => string", "path": "input.testlang", "pattern": "/^string => string$/", "kind": "object", "str": "str"} +{"_type": "tag", "name": "string => integer", "path": "input.testlang", "pattern": "/^string => integer$/", "kind": "object"} +{"_type": "tag", "name": "string => bool", "path": "input.testlang", "pattern": "/^string => bool$/", "kind": "object"} +{"_type": "tag", "name": "string => strbool", "path": "input.testlang", "pattern": "/^string => strbool$/", "kind": "object", "strbool": "str"} +{"_type": "tag", "name": "int => string", "path": "input.testlang", "pattern": "/^int => string$/", "kind": "object"} +{"_type": "tag", "name": "int => integer", "path": "input.testlang", "pattern": "/^int => integer$/", "kind": "object", "int": 17} +{"_type": "tag", "name": "int => bool", "path": "input.testlang", "pattern": "/^int => bool$/", "kind": "object"} +{"_type": "tag", "name": "int => strbool", "path": "input.testlang", "pattern": "/^int => strbool$/", "kind": "object"} +{"_type": "tag", "name": "true => string", "path": "input.testlang", "pattern": "/^true => string$/", "kind": "object"} +{"_type": "tag", "name": "true => integer", "path": "input.testlang", "pattern": "/^true => integer$/", "kind": "object"} +{"_type": "tag", "name": "true => bool", "path": "input.testlang", "pattern": "/^true => bool$/", "kind": "object", "bool": true} +{"_type": "tag", "name": "true => strbool", "path": "input.testlang", "pattern": "/^true => strbool$/", "kind": "object"} +{"_type": "tag", "name": "false => string", "path": "input.testlang", "pattern": "/^false => string$/", "kind": "object"} +{"_type": "tag", "name": "false => integer", "path": "input.testlang", "pattern": "/^false => integer$/", "kind": "object"} +{"_type": "tag", "name": "false => bool", "path": "input.testlang", "pattern": "/^false => bool$/", "kind": "object"} +{"_type": "tag", "name": "false => strbool", "path": "input.testlang", "pattern": "/^false => strbool$/", "kind": "object", "strbool": false} +{"_type": "tag", "name": "array => string", "path": "input.testlang", "pattern": "/^array => string$/", "kind": "object"} +{"_type": "tag", "name": "array => integer", "path": "input.testlang", "pattern": "/^array => integer$/", "kind": "object"} +{"_type": "tag", "name": "array => bool", "path": "input.testlang", "pattern": "/^array => bool$/", "kind": "object"} +{"_type": "tag", "name": "array => strbool", "path": "input.testlang", "pattern": "/^array => strbool$/", "kind": "object"} +{"_type": "tag", "name": "empty string => string", "path": "input.testlang", "pattern": "/^empty string => string$/", "kind": "object", "str": ""} +{"_type": "tag", "name": "empty string => integer", "path": "input.testlang", "pattern": "/^empty string => integer$/", "kind": "object"} +{"_type": "tag", "name": "empty string => bool", "path": "input.testlang", "pattern": "/^empty string => bool$/", "kind": "object"} +{"_type": "tag", "name": "empty string => strbool", "path": "input.testlang", "pattern": "/^empty string => strbool$/", "kind": "object", "strbool": false} +{"_type": "tag", "name": "int =>", "path": "input.testlang", "pattern": "/^int =>$/", "kind": "object"} +{"_type": "tag", "name": "bool =>", "path": "input.testlang", "pattern": "/^bool =>$/", "kind": "object"} +{"_type": "tag", "name": "strbool =>", "path": "input.testlang", "pattern": "/^strbool =>$/", "kind": "object"} +{"_type": "tag", "name": "true => bool, false => true", "path": "input.testlang", "pattern": "/^true => bool, false => true$/", "kind": "object", "bool": true} +### etags + +input.testlang,1095 +string => stringstring => string1,0 +string => integerstring => integer2,17 +string => boolstring => bool3,35 +string => strboolstring => strbool4,50 +int => stringint => string5,68 +int => integerint => integer6,82 +int => boolint => bool7,97 +int => strboolint => strbool8,109 +true => stringtrue => string9,124 +true => integertrue => integer10,139 +true => booltrue => bool11,155 +true => strbooltrue => strbool12,168 +false => stringfalse => string13,184 +false => integerfalse => integer14,200 +false => boolfalse => bool15,217 +false => strboolfalse => strbool16,231 +array => stringarray => string17,248 +array => integerarray => integer18,264 +array => boolarray => bool19,281 +array => strboolarray => strbool20,295 +empty string => stringempty string => string22,313 +empty string => integerempty string => integer23,336 +empty string => boolempty string => bool24,360 +empty string => strboolempty string => strbool25,381 +int =>int =>27,406 +bool =>bool =>28,413 +strbool =>strbool =>29,421 +true => bool, false => truetrue => bool, false => true31,433 diff --git a/Tmain/parser-specific-fields-with-scripts.d/testlang.ctags b/Tmain/parser-specific-fields-with-scripts.d/testlang.ctags new file mode 100644 index 0000000000..3a11c7e641 --- /dev/null +++ b/Tmain/parser-specific-fields-with-scripts.d/testlang.ctags @@ -0,0 +1,175 @@ +--sort=no + +--langdef=Testlang +--map-Testlang=+.testlang + +--_paramdef-Testlang=dp,enable debug printing + +--kinddef-Testlang=o,object,objects + +--_fielddef-Testlang=str,String{datatype=str} +--fields-Testlang=+{str} +--_fielddef-Testlang=int,Integer{datatype=int} +--fields-Testlang=+{int} +--_fielddef-Testlang=bool,Boolean{datatype=bool} +--fields-Testlang=+{bool} +--_fielddef-Testlang=strbool,String or Boolean{datatype=str+bool} +--fields-Testlang=+{strbool} + +--_prelude-Testlang={{ + /dp _param { + pop + /q {=} def + /qq {==} def + /qa {=+} def + /qqa {==+} def + } { + /q {pop} def + /qq {pop} def + /qa {pop} def + /qqa {pop} def + } ifelse + + /T { mark \1 (...) _buildstring qa } def + /pval { + dup type qqa (/) qa qq + } def + /Tstr { T . exch { Testlang.str: } + stopped { clear (XERR ) qa _errorname q } + { (OK ) qa . :Testlang.str {pval} {/noval qq} ifelse} ifelse + } def + /Tint { T . exch { Testlang.int: } + stopped { clear (XERR ) qa _errorname q } + { (OK ) qa . :Testlang.int {pval} {/noval qq} ifelse } ifelse + } def + /Tbool { T . exch { Testlang.bool: } + stopped { clear (XERR ) qa _errorname q } + { (OK ) qa . :Testlang.bool {pval} {/noval qq} ifelse } ifelse + } def + /Tstrbool { T . exch { Testlang.strbool: } + stopped { clear (XERR ) qa _errorname q } + { (OK ) qa . :Testlang.strbool {pval} {/noval qq} ifelse } ifelse + } def + /verifygetter { + stopped { clear (XERR ) qa _errorname q } + { (OK ) qa {pval} {/noval qq} ifelse } ifelse + } def +}} + +--regex-Testlang=/^(string => string)$/\1/o/{{ + (str) Tstr +}} + +--regex-Testlang=/^(string => integer)$/\1/o/{{ + (str) Tint +}} + +--regex-Testlang=/^(string => bool)$/\1/o/{{ + (str) Tbool +}} + +--regex-Testlang=/^(string => strbool)$/\1/o/{{ + (str) Tstrbool +}} + +--regex-Testlang=/^(int => string)$/\1/o/{{ + 17 Tstr +}} + +--regex-Testlang=/^(int => integer)$/\1/o/{{ + 17 Tint +}} + +--regex-Testlang=/^(int => bool)$/\1/o/{{ + 17 Tbool +}} + +--regex-Testlang=/^(int => strbool)$/\1/o/{{ + 17 Tstrbool +}} + +--regex-Testlang=/^(true => string)$/\1/o/{{ + true Tstr +}} + +--regex-Testlang=/^(true => integer)$/\1/o/{{ + true Tint +}} + +--regex-Testlang=/^(true => bool)$/\1/o/{{ + true Tbool +}} + +--regex-Testlang=/^(true => strbool)$/\1/o/{{ + true Tstrbool +}} + +--regex-Testlang=/^(false => string)$/\1/o/{{ + false Tstr +}} + +--regex-Testlang=/^(false => integer)$/\1/o/{{ + false Tint +}} + +--regex-Testlang=/^(false => bool)$/\1/o/{{ + false Tbool +}} + +--regex-Testlang=/^(false => strbool)$/\1/o/{{ + false Tstrbool +}} + +--regex-Testlang=/^(array => string)$/\1/o/{{ + [ 1 2 3 ] Tstr +}} + +--regex-Testlang=/^(array => integer)$/\1/o/{{ + [ 1 2 3 ] Tint +}} + +--regex-Testlang=/^(array => bool)$/\1/o/{{ + [ 1 2 3 ] Tbool +}} + +--regex-Testlang=/^(array => strbool)$/\1/o/{{ + [ 1 2 3 ] Tstrbool +}} + +--regex-Testlang=/^(empty string => string)$/\1/o/{{ + () Tstr +}} + +--regex-Testlang=/^(empty string => integer)$/\1/o/{{ + () Tint +}} + +--regex-Testlang=/^(empty string => bool)$/\1/o/{{ + () Tbool +}} + +--regex-Testlang=/^(empty string => strbool)$/\1/o/{{ + () Tstrbool +}} + +--regex-Testlang=/^(string =>)$/\1/o/{{ + { T . :Testlang.str } verifygetter +}} + +--regex-Testlang=/^(int =>)$/\1/o/{{ + { T . :Testlang.int } verifygetter +}} + +--regex-Testlang=/^(bool =>)$/\1/o/{{ + { T . :Testlang.bool } verifygetter +}} + +--regex-Testlang=/^(strbool =>)$/\1/o/{{ + { T . :Testlang.strbool } verifygetter +}} + +--regex-Testlang=/^(true => bool, false => true)$/\1/o/{{ + T + . true Testlang.bool: + { . false Testlang.bool: } stopped { clear (XERR ) qa _errorname q } if +}} \ No newline at end of file diff --git a/Units/parser-scss.r/use.d/expected.tags b/Units/parser-scss.r/use.d/expected.tags index 4c1167c5ea..67d61746c6 100644 --- a/Units/parser-scss.r/use.d/expected.tags +++ b/Units/parser-scss.r/use.d/expected.tags @@ -1,10 +1,10 @@ X/y input.scss /^@use "X\/y";$/;" M roles:used -y input.scss /^@use "X\/y";$/;" n roles:def +y input.scss /^@use "X\/y";$/;" n roles:def module:X/y Z input.scss /^@use "Z";$/;" M roles:used -Z input.scss /^@use "Z";$/;" n roles:def +Z input.scss /^@use "Z";$/;" n roles:def module:Z A input.scss /^@use "A" as NS;$/;" M roles:used -NS input.scss /^@use "A" as NS;$/;" n roles:def +NS input.scss /^@use "A" as NS;$/;" n roles:def module:A B/ input.scss /^@use "B\/";$/;" M roles:used C/d/e input.scss /^@use 'C\/d\/e';$/;" M roles:used -e input.scss /^@use 'C\/d\/e';$/;" n roles:def +e input.scss /^@use 'C\/d\/e';$/;" n roles:def module:C/d/e P/q input.scss /^@use 'P\/q' as *;$/;" M roles:used diff --git a/docs/man/ctags-lang-scss.7.rst b/docs/man/ctags-lang-scss.7.rst index a415409764..9eb0940823 100644 --- a/docs/man/ctags-lang-scss.7.rst +++ b/docs/man/ctags-lang-scss.7.rst @@ -28,6 +28,7 @@ Change since "0.0" * New kind ``module`` and new role ``used`` of the ``module`` kind * New kind ``namespace`` +* New field ``module`` SEE ALSO -------- diff --git a/dsl/optscript.c b/dsl/optscript.c index 2a8833d171..09113cd2e9 100644 --- a/dsl/optscript.c +++ b/dsl/optscript.c @@ -271,6 +271,8 @@ static EsObject* op__print_objdict_rec (OptVM *vm, EsObject *name); static EsObject* op__print_objdict (OptVM *vm, EsObject *name); static EsObject* op__print_object (OptVM *vm, EsObject *name); static EsObject* op__print (OptVM *vm, EsObject *name); +static EsObject* op__print_object_nonl (OptVM *vm, EsObject *name); +static EsObject* op__print_nonl (OptVM *vm, EsObject *name); static EsObject* op__make_array (OptVM *vm, EsObject *name); static EsObject* op__make_dict (OptVM *vm, EsObject *name); @@ -472,7 +474,10 @@ opt_init (void) defOP (opt_system_dict, op__print_objdict_rec,"====", 1, "any === -"); defOP (opt_system_dict, op__print_objdict, "===", 1, "any === -"); defOP (opt_system_dict, op__print_object, "==", 1, "any == -"); - defOP (opt_system_dict, op__print, "=", 1, "any == -"); + defOP (opt_system_dict, op__print, "=", 1, "any = -"); + + defOP (opt_system_dict, op__print_object_nonl,"==+", 1, "any ==+ -"); + defOP (opt_system_dict, op__print_nonl, "=+", 1, "any =+ -"); defOP (opt_system_dict, op_mark, "<<", 0, "- << mark"); defOP (opt_system_dict, op_mark, "[", 0, "- [ mark"); @@ -2419,21 +2424,23 @@ mark_es_equal (const void *a, const void *b) /* * Operator implementations */ -#define GEN_PRINTER(NAME, BODY) \ +#define GEN_PRINTER(NAME, BODY,NL) \ static EsObject* \ NAME(OptVM *vm, EsObject *name) \ { \ EsObject * elt = ptrArrayRemoveLast (vm->ostack); \ BODY; \ - mio_putc (vm->out, '\n'); \ + if (NL) mio_putc (vm->out, '\n'); \ es_object_unref (elt); \ return es_false; \ } -GEN_PRINTER(op__print_objdict_rec, vm_print_full (vm, elt, false, 10)) -GEN_PRINTER(op__print_objdict, vm_print_full (vm, elt, false, 1)) -GEN_PRINTER(op__print_object, vm_print_full (vm, elt, false, 0)) -GEN_PRINTER(op__print, vm_print_full (vm, elt, true, 0)) +GEN_PRINTER(op__print_objdict_rec, vm_print_full (vm, elt, false, 10), true) +GEN_PRINTER(op__print_objdict, vm_print_full (vm, elt, false, 1), true) +GEN_PRINTER(op__print_object, vm_print_full (vm, elt, false, 0), true) +GEN_PRINTER(op__print, vm_print_full (vm, elt, true, 0), true) +GEN_PRINTER(op__print_object_nonl, vm_print_full (vm, elt, false, 0), false) +GEN_PRINTER(op__print_nonl, vm_print_full (vm, elt, true, 0), false) static EsObject* op__make_array (OptVM *vm, EsObject *name) diff --git a/main/field.c b/main/field.c index 3d84ff5e1f..10dac4802e 100644 --- a/main/field.c +++ b/main/field.c @@ -1316,7 +1316,7 @@ static const char* defaultRenderer (const tagEntryInfo *const tag CTAGS_ATTR_UNU return renderEscapedString (value, tag, buffer); } -static bool isValueAvailableGeneric (const tagEntryInfo *const e, const fieldDefinition *fdef) +extern bool isValueAvailableGeneric (const tagEntryInfo *const e, const fieldDefinition *fdef) { return getParserFieldValueForType(e, fdef->ftype)? true: false; } @@ -1371,6 +1371,8 @@ extern int defineField (fieldDefinition *def, langType language) fobj->sibling = FIELD_UNKNOWN; updateSiblingField (def->ftype, def->name); + installOptscriptFieldAccessor (def->ftype); + return def->ftype; } @@ -1923,3 +1925,82 @@ static EsObject* setFieldValueForInherits (tagEntryInfo *tag, const fieldDefinit return es_false; } + +extern EsObject* getFieldValueGeneric (const tagEntryInfo *tag, const fieldDefinition *fdef) +{ + const char *value = getParserFieldValueForType(tag, fdef->ftype); + unsigned int dt = fdef->dataType; + + if (dt & FIELDTYPE_STRING) + { + if (value == NULL) + return es_nil; + return (dt & FIELDTYPE_BOOL && value[0] == '\0') + ? es_false + : opt_string_new_from_cstr (value); + } + else if (dt & FIELDTYPE_INTEGER) + { + long tmp; + + if (value == NULL) + return es_nil; + else if (value[0] == '\0') + tmp = 0; + else if (!strToLong (value, 10, &tmp)) + tmp = 1; + + return es_integer_new ((int)tmp); + } + else if (dt & FIELDTYPE_BOOL) + return value? es_true: es_nil; + else + { + AssertNotReached (); + return es_nil; + } +} + +extern EsObject* setFieldValueGeneric (tagEntryInfo *tag, const fieldDefinition *fdef, const EsObject *obj) +{ + unsigned int dt = fdef->dataType; + const char * val; + char buf[1 /* [+-] */ + 20 + 1 /* for \0 */]; + + if (dt & FIELDTYPE_STRING) + { + if (es_object_get_type (obj) == OPT_TYPE_STRING) + val = opt_string_get_cstr (obj); + else if ((dt & FIELDTYPE_BOOL) && es_object_equal (es_false, obj)) + val = ""; + else + return OPT_ERR_TYPECHECK; + } + else if (dt & FIELDTYPE_INTEGER) + { + int tmp = es_integer_get (obj); + /* 2^64 => "18446744073709551616" */ + snprintf(buf, sizeof(buf), "%d", tmp); + val = buf; + } + else if (dt & FIELDTYPE_BOOL) + { + if (es_boolean_get (obj)) + val = ""; + else + { + if (doesFieldHaveValue(fdef->ftype, tag)) + return OPTSCRIPT_ERR_FIELDRESET; + val = NULL; + } + } + else + { + val = ""; + AssertNotReached (); + } + + if (val) + attachParserField (tag, fdef->ftype, val); + return es_false; +} diff --git a/main/field.h b/main/field.h index 6dc3021df9..bcf523a4e2 100644 --- a/main/field.h +++ b/main/field.h @@ -77,6 +77,17 @@ typedef enum eFieldDataType { /* used in --list-fields */ FIELDTYPE_END_MARKER = 1 << 3, + + /* If you want to allow a field to be accessed from code + * written in optscript, append FIELDTYPE_SCRIPTABLE to + * dataType member of the field's fieldDefinition. + * + * For the field defined in optlib, set {datatype=TYPE} flag to + * --_fielddef-=... option. Just specifying a type is + * enough; FIELDTYPE_SCRIPTABLE is automatically append to the + * filed definition. If you don't set the flag explicitly, + * FIELDTYPE_SCRIPTABLE is not appended. */ + FIELDTYPE_SCRIPTABLE = FIELDTYPE_END_MARKER, } fieldDataType; /* Interpretation of VALUE of field * @@ -84,133 +95,203 @@ typedef enum eFieldDataType { * as a VALUE for the specified field. * * The VALUE is interpreted very differently depending on the output - * format: ctags, xref, and json. See field.h. + * format: ctags, xref, and json. * * For FIELDTYPE_STRING - * -------------------- + * ===================================================================== + * + * output and getter + * --------------------------------------------------------------------- + * * Consider if you set "str" to the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | foo:str * xref | str * json | "foo": "str" + * -------+------------- + * :foo | (foo) => (foo) true * * Consider if you set "" to the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | foo: * xref | (nothing) * json | "foo": "" + * -------+------------- + * :foo | () => () true * * Consider if you don't set the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | (nothing) * xref | (nothing) * json | (nothing) + * -------+------------- + * :foo | null => false + * + * setter + * --------------------------------------------------------------------- + * + * stack | C sting stored to the field + * ----------+---------------------------- + * . (str) | "str" + * . () | "" + * . object | ERROR:typecheck * * * For FIELDTYPE_STRING|FIELDTYPE_BOOL - * ----------------------------------- - * If the value points "" (empty C string), the json writer prints it as - * false, and the xref writer prints it as -. - * THEREFORE, in the xref format, there is no way to distinguish the - * output for the value "" and "-". Both are printed as "-". - * If the value points a non-empty C string, Both json writer and xref - * writers print it as-is. + * ===================================================================== + * + * output and getter + * --------------------------------------------------------------------- + * + * If a field holds "" (empty C string), the json writer prints it as + * false, and the xref writer prints it as -. In the xref format, + * there is no way to distinguish the output for the value "" and + * "-". Both are printed as "-". If the value is a non-empty C + * string, Both json writer and xref writer print it as-is. * * Consider if you set "str" to the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | foo:str * xref | str * json | "foo": "str" + * -------+------------- + * :foo | (str) => (str) true * * Consider if you set "" to the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | foo: * xref | - (as specified as FIELD_NULL_LETTER_CHAR/FIELD_NULL_LETTER_STRING) * json | "foo": false + * -------+------------- + * :foo | false => false true * * Consider if you don't set the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | (nothing) * xref | (nothing) * json | (nothing) + * -------+------------- + * :foo | null => false + * + * setter + * --------------------------------------------------------------------- + * + * stack | C sting stored to the field + * ----------+---------------------------- + * . (str) | "str" + * . () | "" + * . false | "" + * . true | ERROR:typecheck + * . object | ERROR:typecheck * * * For FIELDTYPE_BOOL - * ------------------ - * Either VALUE points "" (empty C string) or a non-epmty C string, - * the json writer always prints true. In the same condition, the xref - * writer always prints the name of field. + * ===================================================================== + * + * output and getter + * --------------------------------------------------------------------- + * + * Whether a field holds "" (an empty C string) or a non-epmty C string, + * the json writer prints it as true. In the same condition, the xref + * writer prints the name of the field. * - * If a value is not set, the field is treated as holding false. + * If a value is not set, the field is treated as if it holds false. * The json writer prints nothing for the field holding false. * The xref writer prints - for the field holding false. * Consider if you set "str" to the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | foo: * xref | foo * json | "foo": true + * -------+------------- + * :foo | true => true true * * Consider if you set "" to the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | foo: * xref | foo * json | "foo": true + * -------+------------- + * :foo | true => true true * * Consider if you don't set the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | (nothing) * xref | - (as specified as FIELD_NULL_LETTER_CHAR/FIELD_NULL_LETTER_STRING) * json | (nothing) + * -------+------------- + * :foo | null => false + * + * setter + * --------------------------------------------------------------------- + * + * stack | C sting stored to the field + * ----------+---------------------------- + * . false | do nothing if the field was not set. + * | ERROR:fieldreset if the field was already set. + * | This is a limit of current implementation. + * . true | "" + * . object | ERROR:typecheck * * * For FIELDTYPE_INTEGER - * --------------------- - * If the value points "" (empty C string), the all writers print it as - * 0. If the value points a string which cannot be converted to an integer with - * strtol(3), the all writers print it as 1. + * ===================================================================== + * + * output and getter + * --------------------------------------------------------------------- + * + * If a field holds "" (an empty C string), the all writers print it as + * 0. If a field holds a string that strtol(3) cannot convert to an integer, + * all the writers print it as 1. * * Consider if you set "99" to the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | foo:99 * xref | 99 * json | "foo": 99 - * + * -------+------------- + * :foo | 99 => 99 true + * Consider if you set "str" to the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | foo:1 * xref | 1 * json | "foo": 1 - * + * -------+------------- + * :foo | 1 => 1 true + * Consider if you set "" to the field "foo": * * WRITER | OUTPUT - * -------+----------- + * -------+------------- * ctags | foo:0 * xref | 0 * json | "foo": 0 + * -------+------------- + * :foo | 0 => 0 true * * Consider if you don't set the field "foo": * @@ -219,9 +300,20 @@ typedef enum eFieldDataType { * ctags | (nothing) * xref | (nothing) * json | (nothing) + * -------+------------- + * :foo | null => false + * + * setter + * --------------------------------------------------------------------- * + * stack | C sting stored to the field + * ----------+---------------------------- + * . int | int + * . 1 | "1" (as an example) + * . object | ERROR:typecheck * - * The other data type and the combination of types are not implemented yet. + * + * The other data types and the combinations of types are not implemented yet. * */ @@ -244,16 +336,16 @@ struct sFieldDefinition { bool (* isValueAvailable) (const tagEntryInfo *const, const fieldDefinition *); - const char * getterValueType; + const char * getterValueType; /* used in --_list-operators */ struct _EsObject * (* getValueObject) (const tagEntryInfo *, const fieldDefinition *); - const char * setterValueType; + const char * setterValueType; /* used in --_list-operators */ /* Return es_false if passed value is acceptable. Return an error object is unacceptable. */ struct _EsObject * (* checkValueForSetter) (const fieldDefinition *, const struct _EsObject *); struct _EsObject * (* setValueObject) (tagEntryInfo *, const fieldDefinition *, const struct _EsObject *); - fieldDataType dataType; /* used in json output */ + fieldDataType dataType; /* used in json output. See OP column in --list-fields. */ unsigned int ftype; /* Given from the main part */ }; @@ -265,4 +357,8 @@ struct sFieldDefinition { extern bool isFieldEnabled (fieldType type); +extern bool isValueAvailableGeneric (const tagEntryInfo *const tag, const fieldDefinition *fdef); +extern struct _EsObject* getFieldValueGeneric (const tagEntryInfo *tag, const fieldDefinition *fdef); +extern struct _EsObject* setFieldValueGeneric (tagEntryInfo *tag, const fieldDefinition *fdef, const struct _EsObject *obj); + #endif /* CTAGS_MAIN_FIELD_H */ diff --git a/main/lregex.c b/main/lregex.c index 2f5fd1b348..46109163cf 100644 --- a/main/lregex.c +++ b/main/lregex.c @@ -4582,6 +4582,19 @@ static struct optscriptOperatorRegistration lropOperators [] = { }, }; +extern bool installOptscriptFieldAccessor (fieldType ftype) +{ + /* This function is called via defineField(). + defineField() is called via --fielddef option of built-in parsers + defining their specific fields. + built-in parsers are initialized lazily in the current implementation. + optvm is initialized earlier than parsers. */ + Assert (!es_null(lregex_dict)); + + optscriptInstallFieldAccessor (lregex_dict, ftype); + return true; +} + extern void initRegexOptscript (void) { if (!regexAvailable) @@ -4601,6 +4614,7 @@ extern void initRegexOptscript (void) OPTSCRIPT_ERR_UNKNOWNLANGUAGE = es_error_intern ("unknownlanguage"); OPTSCRIPT_ERR_UNKNOWNKIND = es_error_intern ("unknownkind"); OPTSCRIPT_ERR_UNKNOWNROLE = es_error_intern ("unknownrole"); + OPTSCRIPT_ERR_FIELDRESET = es_error_intern ("fieldreset"); optscriptInstallProcs (lregex_dict, lrop_get_match_string_named_group); diff --git a/main/lregex_p.h b/main/lregex_p.h index 15bd869c04..bfeb192a23 100644 --- a/main/lregex_p.h +++ b/main/lregex_p.h @@ -19,6 +19,7 @@ * INCLUDE FILES */ #include "general.h" +#include "field.h" #include "flags_p.h" #include "kind_p.h" #include "lregex.h" @@ -115,6 +116,8 @@ extern void extendRegexTable (struct lregexControlBlock *lcb, const char *src, c extern void initRegexOptscript (void); extern void listRegexOpscriptOperators (FILE *fp); +extern bool installOptscriptFieldAccessor (fieldType ftype); + extern void addOptscriptToHook (struct lregexControlBlock *lcb, enum scriptHook hook, const char *code); extern void propagateParamToOptscript (struct lregexControlBlock *lcb, const char *param, const char *value); diff --git a/main/options.c b/main/options.c index 1bfee7e287..5ba78faa9d 100644 --- a/main/options.c +++ b/main/options.c @@ -2322,6 +2322,7 @@ static void processListSubparsersOptions (const char *const option CTAGS_ATTR_UN static void processListOperators (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter) { + initializeParser (LANG_AUTO); listRegexOpscriptOperators (stdout); exit (0); } diff --git a/main/parse.c b/main/parse.c index 014850efed..2df2a84c39 100644 --- a/main/parse.c +++ b/main/parse.c @@ -4032,7 +4032,7 @@ static void field_def_flag_datatype_long (const char *const optflag CTAGS_ATTR_U if (*param == '\0') error (FATAL, "no datatype given for field: \"%s\"", fdef->name); - fdef->dataType = 0; + fdef->dataType = FIELDTYPE_SCRIPTABLE; if (strcmp (param, "int") == 0) fdef->dataType |= FIELDTYPE_INTEGER; else if (strcmp (param, "str") == 0) @@ -4047,7 +4047,7 @@ static void field_def_flag_datatype_long (const char *const optflag CTAGS_ATTR_U static flagDefinition FieldDefFlagDef [] = { { '\0', "datatype", NULL, field_def_flag_datatype_long, - "TYPE", "acceaptable datatype of the field ([str]|bool|int|str+bool)" }, + "TYPE", "acceaptable datatype of the field (str|bool|int|str+bool)" }, }; static bool processLangDefineField (const langType language, @@ -4090,17 +4090,26 @@ static bool processLangDefineField (const langType language, fdef->letter = NUL_FIELD_LETTER; fdef->name = eStrndup(parameter, name_end - parameter); fdef->description = desc; - fdef->isValueAvailable = NULL; - fdef->getValueObject = NULL; - fdef->getterValueType = NULL; - fdef->setValueObject = NULL; - fdef->setterValueType = NULL; - fdef->checkValueForSetter = NULL; + fdef->dataType = 0; if (flags) flagsEval (flags, FieldDefFlagDef, ARRAY_SIZE (FieldDefFlagDef), fdef); if (!fdef->dataType) fdef->dataType = FIELDTYPE_STRING; + + fdef->isValueAvailable = (fdef->dataType & FIELDTYPE_SCRIPTABLE) + ? isValueAvailableGeneric + : NULL; + fdef->getValueObject = (fdef->dataType & FIELDTYPE_SCRIPTABLE) + ? getFieldValueGeneric + : NULL; + fdef->getterValueType = NULL; + fdef->setValueObject = (fdef->dataType & FIELDTYPE_SCRIPTABLE) + ? setFieldValueGeneric + : NULL; + fdef->setterValueType = NULL; + + fdef->checkValueForSetter = NULL; fdef->ftype = FIELD_UNKNOWN; DEFAULT_TRASH_BOX(fdef, fieldDefinitionDestroy); diff --git a/main/script.c b/main/script.c index 5c81da8560..c0f5afe130 100644 --- a/main/script.c +++ b/main/script.c @@ -25,6 +25,7 @@ EsObject *OPTSCRIPT_ERR_NOTAGENTRY; EsObject *OPTSCRIPT_ERR_UNKNOWNLANGUAGE; +EsObject *OPTSCRIPT_ERR_FIELDRESET; int OPT_TYPE_MATCHLOC; static int locEqual (const void *a, const void *b); @@ -134,13 +135,13 @@ static EsObject* lrop_set_field_value (OptVM *vm, EsObject *name) if (!es_object_equal (e, es_false)) return e; } - else - { - if (! (((fdata_type & FIELDTYPE_STRING) && (valtype == OPT_TYPE_STRING)) - || ((fdata_type & FIELDTYPE_BOOL) && (valtype == ES_TYPE_BOOLEAN)) - || ((fdata_type & FIELDTYPE_INTEGER) && (valtype == ES_TYPE_INTEGER)))) - return OPT_ERR_TYPECHECK; - } + else if ((fdata_type & FIELDTYPE_STRING) && (fdata_type & FIELDTYPE_BOOL) + && ((valtype == OPT_TYPE_STRING) || (valtype == ES_TYPE_BOOLEAN))) + ; /* Accept; do nothing */ + else if (! (((fdata_type & FIELDTYPE_STRING) && (valtype == OPT_TYPE_STRING)) + || ((fdata_type & FIELDTYPE_BOOL) && (valtype == ES_TYPE_BOOLEAN)) + || ((fdata_type & FIELDTYPE_INTEGER) && (valtype == ES_TYPE_INTEGER)))) + return OPT_ERR_TYPECHECK; EsObject *r = setFieldValue (ftype, e, valobj); if (es_error_p (r)) @@ -152,12 +153,22 @@ static EsObject* lrop_set_field_value (OptVM *vm, EsObject *name) return es_false; } -static void optscriptInstallFieldGetter (EsObject *dict, fieldType ftype, - vString *op_name, vString *op_desc) +static void optscriptInstallFieldGetterFast (EsObject *dict, fieldType ftype, + vString *op_name, vString *op_desc) { - const char *fname = getFieldName (ftype); vStringPut (op_name, ':'); + + langType lang = getFieldLanguage (ftype); + if (lang != LANG_IGNORE) + { + const char *lname = getLanguageName (lang); + vStringCatS (op_name, lname); + vStringPut (op_name, '.'); + } + + const char *fname = getFieldName (ftype); vStringCatS (op_name, fname); + EsObject *op_sym = es_symbol_intern (vStringValue (op_name)); es_symbol_set_data (op_sym, HT_INT_TO_PTR (ftype)); @@ -198,9 +209,17 @@ static void optscriptInstallFieldGetter (EsObject *dict, fieldType ftype, es_object_unref (op); } -static void optscriptInstallFieldSetter (EsObject *dict, fieldType ftype, - vString *op_name, vString *op_desc) +static void optscriptInstallFieldSetterFast (EsObject *dict, fieldType ftype, + vString *op_name, vString *op_desc) { + langType lang = getFieldLanguage (ftype); + if (lang != LANG_IGNORE) + { + const char *lname = getLanguageName (lang); + vStringCatS (op_name, lname); + vStringPut (op_name, '.'); + } + const char *fname = getFieldName (ftype); vStringCatS (op_name, fname); vStringPut (op_name, ':'); @@ -237,22 +256,46 @@ static void optscriptInstallFieldSetter (EsObject *dict, fieldType ftype, es_object_unref (op); } +extern void optscriptInstallFieldAccessor (EsObject *dict, fieldType ftype) +{ + vString *op_name = vStringNew (); + vString *op_desc = vStringNew (); + + if (hasFieldGetter (ftype)) + { + optscriptInstallFieldGetterFast (dict, ftype, op_name, op_desc); + vStringClear (op_name); + vStringClear (op_desc); + } + + if (hasFieldSetter (ftype)) + optscriptInstallFieldSetterFast (dict, ftype, op_name, op_desc); + + vStringDelete (op_name); + vStringDelete (op_desc); +} + static void optscriptInstallFieldAccessors (EsObject *dict) { vString *op_name = vStringNew (); vString *op_desc = vStringNew (); + /* In the current implementation, the upper boundary of the loop + can be "<= FIELD_BUILTIN_LAST" because the parser specific fields + are registered via defineField. However, when we change the order + and the way to initialize parsers, we may have to use "< countFields()" + instead. */ for (fieldType ftype = 0; ftype <= FIELD_BUILTIN_LAST; ftype++) { if (hasFieldGetter (ftype)) { - optscriptInstallFieldGetter (dict, ftype, op_name, op_desc); + optscriptInstallFieldGetterFast (dict, ftype, op_name, op_desc); vStringClear (op_name); vStringClear (op_desc); } if (hasFieldSetter (ftype)) { - optscriptInstallFieldSetter (dict, ftype, op_name, op_desc); + optscriptInstallFieldSetterFast (dict, ftype, op_name, op_desc); vStringClear (op_name); vStringClear (op_desc); } diff --git a/main/script_p.h b/main/script_p.h index 7e8852da53..08899af49f 100644 --- a/main/script_p.h +++ b/main/script_p.h @@ -13,6 +13,7 @@ #include "general.h" /* must always come first */ +#include "field.h" #include "optscript.h" #include "mio.h" @@ -28,10 +29,12 @@ extern void optscriptRegisterOperators(EsObject * dict, extern EsObject *OPTSCRIPT_ERR_NOTAGENTRY; extern EsObject *OPTSCRIPT_ERR_UNKNOWNLANGUAGE; extern EsObject *OPTSCRIPT_ERR_UNKNOWNEXTRA; +extern EsObject *OPTSCRIPT_ERR_FIELDRESET; extern OptVM *optscriptInit (void); extern void optscriptInstallProcs (EsObject *dict, OptOperatorFn matchResultAccessor); +extern void optscriptInstallFieldAccessor (EsObject *dict, fieldType ftype); extern void optscriptSetup (OptVM *vm, EsObject *dict, int corkIndex); extern void optscriptTeardown (OptVM *vm, EsObject *dict); diff --git a/man/ctags-lang-scss.7.rst.in b/man/ctags-lang-scss.7.rst.in index 7c446ef9b9..62232bd12f 100644 --- a/man/ctags-lang-scss.7.rst.in +++ b/man/ctags-lang-scss.7.rst.in @@ -28,6 +28,7 @@ Change since "0.0" * New kind ``module`` and new role ``used`` of the ``module`` kind * New kind ``namespace`` +* New field ``module`` SEE ALSO -------- diff --git a/misc/optlib2c b/misc/optlib2c index 42179edf3d..0e19fd244c 100755 --- a/misc/optlib2c +++ b/misc/optlib2c @@ -61,7 +61,7 @@ my $langdef_flags = my $fielddef_flags = [ [ qr/\{datatype=([^\}]+)\}/, sub { - my $datatype = ""; + my $datatype = "FIELDTYPE_SCRIPTABLE|"; if ($1 eq 'str') { @@ -1104,6 +1104,9 @@ EOF if (defined $_->{'datatype'}) { print <{'datatype'}, + .isValueAvailable = isValueAvailableGeneric, + .getValueObject = getFieldValueGeneric, + .setValueObject = setFieldValueGeneric, EOF } print <useCork = CORK_QUEUE; def->kindTable = SCSSKindTable; def->kindCount = ARRAY_SIZE(SCSSKindTable); + def->fieldTable = SCSSFieldTable; + def->fieldCount = ARRAY_SIZE(SCSSFieldTable); def->initialize = initializeSCSSParser; return def; diff --git a/optlib/scss.ctags b/optlib/scss.ctags index 80bbd32314..c615ff8f3c 100644 --- a/optlib/scss.ctags +++ b/optlib/scss.ctags @@ -40,6 +40,9 @@ --kinddef-SCSS=M,module,modules --_roledef-SCSS.{module}=used,used +--_fielddef-SCSS=module,the name of module behind the namespace{datatype=str} +--fields-SCSS=+{module} + --_tabledef-SCSS=toplevel --_tabledef-SCSS=comment --_tabledef-SCSS=interp @@ -69,18 +72,18 @@ % module-name offset' count namespace-string _copyinterval dup length 0 gt { - /namespace @1 _tag _commit pop + /namespace @1 _tag _commit \1 SCSS.module: } { clear } ifelse } { % Extract the module name as a namespace. - \1 /namespace @1 _tag _commit pop + \1 /namespace @1 _tag _commit \1 SCSS.module: } ifelse } { % "as *" doesn't make a namespace. \3 (*) ne { - \3 /namespace @3 _tag _commit pop + \3 /namespace @3 _tag _commit \1 SCSS.module: } if } ifelse }}