diff --git a/README.md b/README.md index 43482f8..26b4d99 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,49 @@ $ logica primes.l run Prime +-------+ ``` +### Cities with largest beer variety + +Let's use beer variety dataset from [plotly](https://github.com/plotly/datasets/blob/master/beers.csv). + +Let us find top 5 states with largest variety of beers. In each state we will pick city with the largest +variety in the state. + +Program `beer.l`: + +``` +@Engine("duckdb"); + +@Ground(Beer); +Beer(..r) :- + `('https://github.com/plotly/datasets/blob/master/beers.csv?raw=true')`(..r); + +BeersInState(state) += 1 :- Beer(state:); +BeersInCity(state, city) += 1 :- Beer(state:, city:); + +ArgMax5(x) = ArgMaxK(x, 5); +BestCityForBeer(state:, city:, + city_beers: BeersInCity(state, city), + state_beers: BeersInState(state)) :- + state in ArgMax5{s -> BeersInState(s)}, + city = ArgMax{c -> BeersInCity(state, c)}; +``` + +Running `beer.l`: + +``` +# logica beer.l run BestCityForBeer ++-------+--------------+------------+-------------+ +| state | city | city_beers | state_beers | ++-------+--------------+------------+-------------+ +| IN | Indianapolis | 43 | 139 | +| CO | Boulder | 41 | 265 | +| CA | San Diego | 42 | 183 | +| TX | Austin | 25 | 130 | +| MI | Grand Rapids | 66 | 162 | ++-------+--------------+------------+-------------+ +``` + + ## Feedback Feel free to create [github issues](https://github.com/EvgSkv/logica/issues) diff --git a/compiler/dialect_libraries/duckdb_library.py b/compiler/dialect_libraries/duckdb_library.py index 7a5f271..2e73b35 100644 --- a/compiler/dialect_libraries/duckdb_library.py +++ b/compiler/dialect_libraries/duckdb_library.py @@ -54,6 +54,13 @@ Num(a) = a; Str(a) = a; +Epoch(a) = epoch :- + epoch = SqlExpr("epoch_ns({a})", {a:}) / 1000000000, + a ~ Time, + epoch ~ Num; +TimeDiffSeconds(a, b) = Epoch(SqlExpr("{a} - {b}", {a:, b:})); +ToTime(a) = SqlExpr("cast({a} as timestamp)", {a:}); + NaturalHash(x) = ToInt64(SqlExpr("hash(cast({x} as string)) // cast(2 as ubigint)", {x:})); # This is unsafe to use because due to the way Logica compiles this number diff --git a/compiler/dialects.py b/compiler/dialects.py index 6ee88cf..81ff306 100755 --- a/compiler/dialects.py +++ b/compiler/dialects.py @@ -416,7 +416,9 @@ def BuiltInFunctions(self): 'Greatest': 'GREATEST(%s)', 'ToString': 'CAST(%s AS TEXT)', 'DateAddDay': "DATE({0}, {1} || ' days')", - 'DateDiffDay': "CAST(JULIANDAY({0}) - JULIANDAY({1}) AS INT64)" + 'DateDiffDay': "CAST(JULIANDAY({0}) - JULIANDAY({1}) AS INT64)", + 'CurrentTimestamp': 'GET_CURRENT_TIMESTAMP()', + 'TimeAdd': '{0} + to_microseconds(cast(1000000 * {1} as int64))' } def DecorateCombineRule(self, rule, var): diff --git a/type_inference/research/infer.py b/type_inference/research/infer.py index 1ba244e..10566ef 100644 --- a/type_inference/research/infer.py +++ b/type_inference/research/infer.py @@ -416,7 +416,7 @@ def ActMindingRecordLiterals(self, node): def ActMindingTypingPredicateLiterals(self, node): if 'type' in node and 'literal' in node and 'the_predicate' in node['literal']: predicate_name = node['literal']['the_predicate']['predicate_name'] - if predicate_name in ['Str', 'Num', 'Bool']: + if predicate_name in ['Str', 'Num', 'Bool', 'Time']: reference_algebra.Unify(node['type']['the_type'], reference_algebra.TypeReference(predicate_name)) @@ -700,6 +700,8 @@ def PsqlType(self, t): return 'numeric' if t == 'Bool': return 'bool' + if t == 'Time': + return 'timestamp' if isinstance(t, dict): return RecordTypeName(reference_algebra.RenderType(t)) if isinstance(t, list): diff --git a/type_inference/research/reference_algebra.py b/type_inference/research/reference_algebra.py index 0c4253a..5c37e82 100644 --- a/type_inference/research/reference_algebra.py +++ b/type_inference/research/reference_algebra.py @@ -210,12 +210,14 @@ def Rank(x): return 4 if x == 'Bool': return 5 - if isinstance(x, list): + if x == 'Time': return 6 - if isinstance(x, OpenRecord): + if isinstance(x, list): return 7 - if isinstance(x, ClosedRecord): + if isinstance(x, OpenRecord): return 8 + if isinstance(x, ClosedRecord): + return 9 assert False, 'Bad type: %s' % x @@ -276,7 +278,7 @@ def Unify(a, b): Incompatible(b.target, a.target)) return - if concrete_a in ('Num', 'Str', 'Bool'): + if concrete_a in ('Num', 'Str', 'Bool', 'Time'): if concrete_a == concrete_b: return # It's all fine. # Type error: a is incompatible with b. diff --git a/type_inference/research/types_of_builtins.py b/type_inference/research/types_of_builtins.py index 3cf3b06..86a5f57 100644 --- a/type_inference/research/types_of_builtins.py +++ b/type_inference/research/types_of_builtins.py @@ -77,6 +77,9 @@ def TypesOfBultins(): 0: 'Str', 'logica_value': 'Str' }, + 'Time': { + 'logica_value': 'Time' + }, 'Agg+': { 0: 'Num', 'logica_value': 'Num' @@ -248,6 +251,9 @@ def TypesOfBultins(): 0: x, 1: x, 'logica_value': x + }, + 'CurrentTimestamp': { + 'logica_value': 'Time' } } types_of_predicate['<'] = types_of_predicate['<='] = types_of_predicate['>='] = types_of_predicate['>']