I'm working with the Esprima parser, it outputs an AST format which is compatible with the Mozilla Spider Monkey Parser API.
In the Mozilla Docs, it specifies the Function node as:
interface Function <: Node {
id: Identifier | null;
params: [ Pattern ];
defaults: [ Expression ];
rest: Identifier | null;
body: BlockStatement | Expression;
generator: boolean;
expression: boolean;
}
What will the defaults property contain? It always appears as just an empty array.
defaults of Mozilla JS AST contains ES6 default parameter values.
For example,
function t(i = 20) { }
defaults will be [{ type: 'Literal', value: 20 }].
Because it is based on ES6 draft, Esprima master branch doesn't recognize it.
Related
Goal of this expression is separate mathematic calculations into operators, symbols, numbers and brackets.
For example:
Input string: 1+3-6*(12-3+4/5)
Output list: 1, +, 3, -, 6, *, (12-3+4/5)
So I built this expression.
It is working on the web page, but in the Dart code this happens:
final calculationExpression = RegExp(
r"/(\(([a-zA-Z0-9-+/*]+)\))|([a-zA-Z0-9]+)|([+/*-]{1})/g",
unicode: true,
multiLine: true,
);
...
List<String> operators = calculationsString.split(calculationExpression); /// Output: ["", "+", "-", ...]
What did I do wrong?
The syntax /pattern/g is used to create regular expression literals in JavaScript (and sed and some other languages), just as quotes are used to create string literals. Dart doesn't have regular expression literals; you instead must invoke the RegExp constructor directly. Combining a regular expression literal syntax with an explicitly constructed RegExp object makes no sense. When you do RegExp(r'/pattern1|pattern2|pattern3/g'), you're actually matching against /pattern1 (pattern1 prefixed with a literal / character) or pattern2 or pattern3/g (pattern3 followed by a literal string /g).
String.split does not split the input string such that each element of the result matches the pattern. It treats all matches of the pattern as separators. Consequently, the resulting list will not have any elements that match the pattern, which is the opposite of what you want. You instead want to find all matches of the pattern in the string. You instead can use RegExp.allMatches if you additionally verify that the input string contains only matches from the regular expression.
Putting it all together:
void main() {
final calculationExpression = RegExp(
r"(\(([a-zA-Z0-9-+/*]+)\))|([a-zA-Z0-9]+)|([+/*-]{1})",
unicode: true,
multiLine: true,
);
var calculationsString = '1+3-6*(12-3+4/5)';
// Prints: [1, +, 3, -, 6, *, (12-3+4/5)]
print(calculationsString.tokenizeFrom(calculationExpression).toList());
}
extension on String {
Iterable<String> tokenizeFrom(RegExp regExp) sync* {
void failIf(bool condition) {
if (condition) {
throw FormatException(
'$this contains characters that do not match $regExp',
);
}
}
var matches = regExp.allMatches(this);
var lastEnd = 0;
for (var match in matches) {
// Verify that there aren't unmatched characters.
failIf(match.start != lastEnd);
lastEnd = match.end;
yield match.group(0)!;
}
failIf(lastEnd != length);
}
}
You put the JavaScript regexp literal slashes and flags inside the Dart string.
If you remove the leading / and trailing /g, you get the RegExp you intended to.
The multiLine and unicode flags are unnecessary (your regexp doesn't use any feature affected by those)
The Dart split function does not emit capture groups, so you probably want to look at getting the matches, not removing them, which is what split does.
All in all, try:
final calculationExpression = RegExp(
r"\([a-zA-Z\d\-+/*]+\)|[a-zA-Z\d]+|[+/*\-]");
List<String> tokes =
calculationExpression.allMatches(calculationsString).toList();
I can't wrap my head around this. What am I overlooking?
Here is a minimal sample: https://plnkr.co/edit/VjqGeG9JpHblyLBb?preview
<tnt:InfoLabel
text="{
path: 'LastName',
formatter: '.formatter.typeText'
}"
colorScheme="{
path: 'LastName',
formatter: '.formatter.typeColor'
}" />
// formatter.js
sap.ui.define([], function () {
"use strict";
return {
typeText: function(sLastName) {
// Called with 'sLastName' value
},
typeColor: function(sLastName) {
// Not called
}
};
});
I'm using UI5 1.79 with sap.ui.model.odata.v4.ODataModel.
Add targetType: 'any' to the property binding info that has the issue. E.g.:
<tnt:InfoLabel
text="{
path: 'LastName',
formatter: '.getMyText'
}"
colorScheme="{
path: 'LastName',
formatter: '.getMyColorScheme',
targetType: 'any'
}"
/>
With sap.ui.model.odata.v4.ODataModel, data types in property bindings are automatically determined according to the EDM type of the entity property. I.e. in the case above: even without having a certain type explicitly assigned to the text property, the v4.ODataPropertyBinding automatically picks the String type (because LastName has Type="Edm.String" in $metadata) and assigns it to the type:
<tnt:InfoLabel
text="{
path: 'LastName',
formatter: '.getMyText',
type: 'sap.ui.model.odata.type.String' <-- automatically added by v4.ODataPropertyBinding
}"
This was fine for the text property since it actually awaits a string value, but doing the same for other properties, such as colorScheme which awaits an int value, results in a FormatException.*
In order to prevent the automatic Type Determination, the targetType: 'any' has to be added.
* With commit:4611772, which is available as of 1.80, we can see the corresponding error message in the console:
FormatException in property 'colorScheme' of 'Element sap.tnt.InfoLabel#...': <LastName value> is not a valid int value. Hint: single properties referenced in composite bindings and within binding expressions are automatically converted into the type of the bound control property, unless a different 'targetType' is specified. targetType:'any' may avoid the conversion and lead to the expected behavior.
I am using UUID as primary key for my entity and it works just fine. But I wish to remove those dashes.
Now ids are saved as 8e5365f4-3d42-4274-bafc-93b97bd6e3f2 36 characters
And what I want is 8e5365f43d424274bafc93b97bd6e3f2 32 characters
I dont see any option of using transformer in #PrimaryGeneratedColumn('uuid') is there a simple way to archive this?
You can't remove the dashes when using #PrimaryGeneratedColumn('uuid').
The UUID is either generated by the database (for example postgres) or, if not supported by the database, it is generated by a function.
See Typeorm sources (https://github.com/typeorm/typeorm/blob/master/src/query-builder/InsertQueryBuilder.ts):
else if (column.isGenerated && column.generationStrategy === "uuid" && !this.connection.driver.isUUIDGenerationSupported() && value === undefined) {
const paramName = "uuid_" + column.databaseName + valueSetIndex;
value = RandomGenerator.uuid4();
this.expressionMap.nativeParameters[paramName] = value;
expression += this.connection.driver.createParameter(paramName, parametersCount);
parametersCount++;
// if value for this column was not provided then insert default value
}
If you really want this, you need to generate it yourself, for example:
import { BeforeInsert, Entity, PrimaryColumn } from 'typeorm';
import { v4 as uuid4 } from 'uuid';
#Entity({})
export class User {
#PrimaryColumn()
uuid: string;
#BeforeInsert()
generateUuid() {
this.uuid = uuid4().replace(/-/g, '');
}
}
Instead of using #PrimaryGeneratedColumn, you could use #PrimaryColumn with generated: "uuid" and specify a transformer:
const removeDashes: ValueTransformer = {
from: (str: string | null | undefined) => str != null ? str.replace(/-/g, "") : str,
to: (str: string | null | undefined) => str != null ? str.replace(/-/g, "") : str,
};
#Entity()
export class SomeEntity {
#PrimaryColumn({ type: "uuid", generated: "uuid", transformer: removeDashes })
id: string;
}
Theoretically that should work with databases other than postgres (perhaps with some minor adjustments).
But if you are using postgres, and your column is the built-in uuid type, the dashes that you see in the UUID are not actually stored in the database. Instead, they're stored as a 128-bit integer, and postgres converts it to a human-readable format (with dashes) for display when needed. Postgres accepts UUID input in a variety of formats, both with and without dashes. (See https://www.postgresql.org/docs/11/datatype-uuid.html)
Since postgres is so flexible with input, you don't need to strip the dashes when sending a UUID to postgres if you're using the uuid type:
const removeDashesPostgres: ValueTransformer = {
from: (str: string | null | undefined) => str != null ? str.replace(/-/g, "") : str,
to: (str: string | null | undefined) => str,
};
When typeorm transforms query results into the entity, the transformer will strip the dashes from the string representation of your UUID, so you always "see" the dash-less UUIDs in your objects. When typeorm transforms an entity into a query to save/update/etc., it passes through the string representation of the UUID to the database as-is, and postgres converts it to its own uuid thanks to its extreme input tolerance.
For completeness, the id column in the table in these postgres examples is defined as:
id uuid NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4()
Example XQuery (using Saxon-HE, version 9.8.0.6)
xquery version "3.1";
let $xml := <simple>
<hello>Hello World!</hello>
</simple>
return fn:serialize(map{
'greeting': data($xml/hello),
'number': data($xml/number) (: ? how to add this entry only if there is a number ? :)
}, map{'method':'json', 'indent':true()})
Output:
{
"number":null,
"greeting":"Hello World!"
}
Question
How to prevent entries with a null value (in this case 'number')? Or more specifically in this case: how to add the 'number' entry only if it is a number?
Note: I know about map:entry and map:merge. I am looking for a solution without these functions, so "inline" (within the map constructor).
Update
Based on the answer of #joewiz, this is not possible. This is the closest we can get:
xquery version "3.1";
declare namespace map="http://www.w3.org/2005/xpath-functions/map";
let $xml := <simple>
<hello>Hello World!</hello>
</simple>
return fn:serialize(
map:merge((
map:entry('greeting', data($xml/hello)),
let $n := number($xml/number) return if ($n) then map:entry('number', $n) else()
)),
map{'method':'json', 'indent':true()})
If you're doing this often enough to make it worthwile you could do:
declare function f:addConditionally($map, $key, $data) as map(*) {
if (exists($data)) then map:put($map, $key, $data) else $map
};
let $m :=
map{}
=> f:addConditionally('greeting', data($xml/hello))
=> f:addConditionally('number', data($xml/number))
According to the XQuery 3.1 specification on Map Constructors, map constructors consist of map constructor entries, which themselves consist of a map key expression and a map value expression. In other words, map constructor entries are not general expressions and cannot accommodate a conditional expression such as:
map { if ($condition) then "key": "value" else () }
If you need to put anything in a map besides a key-value expression pair, you'll need to abandon the map constructor and fall back on map:merge() and map:entry(). The correct syntax for the above case would be as follows:
map:merge( if ($condition) then map:entry("key", "value") else () )
I'm trying to create a grammar. Here's my code so far:
use Text::Table::Simple; # zef install Text::Table::Simple
my $desc = q:to"FIN";
record person
name string;
age int;
end-record
FIN
grammar rec {
token TOP { <ws>* 'record' \s+ <rec-name> <field-descriptors> <ws> 'end-record' <ws> }
token rec-name { \S+ }
token field-descriptors { <field-descriptor>* }
token field-descriptor { <ws>* <field-name> <ws>+ <field-type> <ws>* ';' }
token field-name { \S+ }
token field-type { <[a..z]>+ }
token ws { <[\r\n\t\ ]> }
}
class recActions {
method field-descriptors($/) { $/.make: $/; }
method field-descriptor($/) { $/.make: $/; }
method field-name($/) { $/.make: $/ }
method field-type($/) { $/.make: $/ }
}
my $r = rec.parse($desc, :actions(recActions));
#say $r;
my $inp = q:to"FIN";
adam 26
joe 23
mark 51
FIN
sub splitter($line) {
my #lst = split /\s+/, $line;
}
sub matrixify(&splitter, $data)
{
my #d = (split /\n/, (trim-trailing $data)).map( -> $x { splitter $x ; } );
##d.say;
#my #cols = <name age>;
#say lol2table(#cols, #d).join("\n");
#d;
}
#my #cols =<A B>;
#my #rows = ([1,2], [3,4]);
#say lol2table(#cols, #rows).join("\n");
my #m = matrixify &splitter, $inp;
sub tabulate($rec-desc, #matrix)
{
my $fds = $rec-desc<field-descriptors>;
#say %fds<field-name>;
say $fds;
my #cols = $rec-desc.<field-descriptors>.map( -> $fd { say $fd; $fd.<field-name> ; 1;} );
#say $rec-desc.<field-descriptors>;
#say #cols;
}
tabulate $r, #m ;
I really just want the grammar to create a tree of lists/hash tables from the input. The output from the code is:
「
name string;
age int;」
field-descriptor => 「
name string;」
ws => 「
」
ws => 「 」
field-name => 「name」
ws => 「 」
field-type => 「string」
field-descriptor => 「
age int;」
ws => 「
」
ws => 「 」
field-name => 「age」
ws => 「 」
ws => 「 」
field-type => 「int」
which looks fairly good. perl6 seems to be decoding the fact that field-descriptors is composed of multiple field-descriptor, but it doesn't actually seem to put them into a list. I can do say $fds;, but I can't do say $fds[0];. Why does the former "work", but the latter doesn't?
I must admit to having a fairly weak grasp of what's going on. Would I be better of using rules instead of tokens? Do I really need an actions class; can't I just get perl to "automagically" populate the parse tree for me without having to specify a class of actions?
Update: possible solution
Suppose we just want to parse:
my $desc = q:to"FIN";
record person
name string;
age int;
end-record
FIN
and report on the field names and types that we find. I'm going to make a slight simplification to the grammar I wrote above:
grammar rec {
token TOP { <ws>* 'record' \s+ <rec-name> <field-descriptor>+ <ws> 'end-record' <ws> }
token rec-name { \S+ }
token field-descriptor { <ws>* <field-name> <ws>+ <field-type> <ws>* ';' }
token field-name { \S+ }
token field-type { <[a..z]>+ }
token ws { <[\r\n\t\ ]> }
}
Let's eschew actions completely, and just parse it into a tree:
my $r1 = rec.parse($desc);
Let's now inspect our handiwork, and print out the name and type for each field that we have parsed:
for $r1<field-descriptor> -> $fd { say "Name: $fd<field-name>, Type: $fd<field-type>"; }
Our output is as we expect:
Name: name, Type: string
Name: age, Type: int
I know you're now all set but here's an answer to wrap things up for others reading things later.
How do I just create a parse tree with perl6 grammar?
It's as simple as it can get: just use the return value from calling one of the built in parsing routines.
(Provided parsing is successful parse and cousins return a parse tree.)
The output from the code ... looks fairly good. perl6 seems to be decoding the fact that field-descriptors is composed of multiple field-descriptor, but it doesn't actually seem to put them into a list. I can do say $fds;, but I can't do say $fds[0];. Why does the former "work", but the latter doesn't?
See my answer to the SO question "How do I access the captures within a match?".
Would I be better of using rules instead of tokens?
The only difference between a token and a rule is the default for interpreting bare whitespace that you include within the token/rule.
(Bare whitespace within a token is completely ignored. Bare whitespace within a rule denotes "there can be whitespace at this point in the input".)
Do I really need an actions class[?]
No.
Only bother with an actions class if you want to systematically post process the parse tree.
can't I just get perl to "automagically" populate the parse tree for me without having to specify a class of actions?
Yes. Any time you call parse and the parse is successful its return value is a parse tree.
Update: possible solution
Let's eschew actions completely, and just parse it into a tree:
Right. If all you want is the parse tree then you don't need an actions class and you don't need to call make or made.
Conversely, if you want another tree, such as an Abstract Syntax Tree, then you will probably find it convenient to use the built in make and made routines. And if you use make and made you may well find it appropriate to use them in conjunction with a separate actions class rather than just embedding them directly in the grammar's rules/tokens/regexes.