Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Python list initializers & equal initializer syntax #544

Closed
wants to merge 81 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
5d1be6e
Support list literals in LF syntax
oowekyala Sep 26, 2021
328285a
turn [] token into two separate tokens
oowekyala Sep 26, 2021
419cc0d
Update python generator
oowekyala Sep 26, 2021
32e3385
Fix python test
oowekyala Sep 26, 2021
c92da0f
Merge branch 'master' into list-init-syntax
oowekyala Oct 4, 2021
0fa8c20
Update some test code
oowekyala Oct 4, 2021
7784b3a
Rename TuplePrint -> ArrayAsType
oowekyala Oct 4, 2021
f08bd72
Forbid writing [,] as list literal
oowekyala Oct 4, 2021
abfb605
Merge branch 'master' into list-init-syntax
oowekyala Oct 14, 2021
adc20a5
Refactor to use a new Initializer production
oowekyala Oct 14, 2021
599627c
Add tests
oowekyala Oct 14, 2021
03fcc57
Fix grammar
oowekyala Oct 14, 2021
ec1d7d8
Add tests
oowekyala Oct 14, 2021
9a8b6e7
Remove peelParens
oowekyala Oct 14, 2021
a8cf467
Fix checking of types
oowekyala Oct 14, 2021
48040d3
Fix C++ param initializer
oowekyala Oct 14, 2021
451e928
Fix C tests
oowekyala Oct 14, 2021
43fe613
Fix TS tests
oowekyala Oct 14, 2021
4bb0a66
Enrich c++ initializer test
oowekyala Oct 14, 2021
f500a34
Fix python empty lists
oowekyala Oct 14, 2021
57585df
Fix typo
oowekyala Oct 15, 2021
9c0697a
Merge branch 'master' into list-init-syntax
oowekyala Oct 15, 2021
30bd55a
1 class support for arithmetic exprs in more contexts
oowekyala Oct 15, 2021
1f78382
Test type inference with eq assignment
oowekyala Oct 15, 2021
6cb60fe
Remove offset checks
oowekyala Oct 15, 2021
0434933
Add common test for expressions
oowekyala Oct 15, 2021
6c0f65f
Move test
oowekyala Oct 15, 2021
8a3e49a
Apply suggestions from code review
lhstrh Oct 19, 2021
f4f3d4d
Merge branch 'master' into list-init-syntax
oowekyala Oct 20, 2021
f1924ad
Fix TargetTypes
oowekyala Oct 20, 2021
1605bec
Fix TargetTypes w/ arithmetic
oowekyala Oct 20, 2021
84d92a9
Simplify C++ generator
oowekyala Oct 20, 2021
0b5f021
Fix cpp state initializers
oowekyala Oct 21, 2021
c2f7490
Really fix C++
oowekyala Oct 22, 2021
b5bcd3d
Cleanups
oowekyala Oct 22, 2021
2cff9ea
Merge branch 'master' into list-init-syntax
oowekyala Oct 25, 2021
b257d1c
Merge branch 'master' into list-init-syntax
oowekyala Oct 27, 2021
b701c42
Fix merge
oowekyala Oct 27, 2021
b2dde5c
Merge branch 'master' into list-init-syntax
oowekyala Nov 7, 2021
6252667
Add support for array initializer literals in C
oowekyala Nov 7, 2021
640a048
Refactor GeneratorBase
oowekyala Nov 7, 2021
08ed643
More cleanups for TS
oowekyala Nov 7, 2021
8ebc6e6
Cleanup TS further
oowekyala Nov 7, 2021
ff2e42e
Make all generators use TargetTypes default methods
oowekyala Nov 7, 2021
bc08e83
Fix C++ parameters
oowekyala Nov 7, 2021
b19695d
Fix validation of state vars
oowekyala Nov 7, 2021
e2b009a
Fix validation tests
oowekyala Nov 7, 2021
30a0028
Fix TS state initializers
oowekyala Nov 7, 2021
39cbe2d
Fix TS ctor generation
oowekyala Nov 7, 2021
8348a1b
Remove dead code
oowekyala Nov 7, 2021
27b94cb
Fix C++ initializers
oowekyala Nov 7, 2021
2924bf8
Fix Python tuples
oowekyala Nov 7, 2021
f2207db
Support missing initializer in C
oowekyala Nov 7, 2021
ed3fde9
Convert some stuff from AstUtils to java
oowekyala Nov 8, 2021
006ec71
Fix C++ tests
oowekyala Nov 8, 2021
7093364
Missing ctor in typescript test
oowekyala Nov 8, 2021
4213044
Fix TS tests
oowekyala Nov 8, 2021
b1cd66a
Guard against null time values
oowekyala Nov 8, 2021
e268913
Hopefully fix C++ for good
oowekyala Nov 8, 2021
0ad105b
Merge branch 'allow-after-0' into list-init-syntax
oowekyala Dec 7, 2021
ce4a7ad
Merge branch 'master' into list-init-syntax
oowekyala Jan 23, 2022
35729b8
Fix some conflicts
oowekyala Jan 23, 2022
5d32616
Merge branch 'master' into list-init-syntax
oowekyala Jan 29, 2022
f02fa39
Fix conflicts...
oowekyala Jan 29, 2022
f073ec6
Fix conflicts...
oowekyala Jan 29, 2022
bfb524b
Fix conflicts...
oowekyala Jan 29, 2022
8043724
Fix conflicts...
oowekyala Jan 29, 2022
c618d57
Fix conflicts...
oowekyala Jan 29, 2022
01d3453
Resolved conflicts with master. Special attention paid to conflict wi…
lhstrh Feb 4, 2022
5dd07de
Update kotlin version
oowekyala Feb 5, 2022
554ec24
Merge branch 'update-kotlin-version' into list-init-syntax
oowekyala Feb 5, 2022
a675fbd
Fix validator tests
oowekyala Feb 5, 2022
30b7157
Fix some other tests
oowekyala Feb 5, 2022
5b93e15
Try to fix bugs in CGenerator
oowekyala Feb 5, 2022
3c195d5
Update reactor-c to master version
oowekyala Feb 6, 2022
14fe4c0
Fix TS delay tests
oowekyala Feb 6, 2022
8aa7ab9
Fix python errors
oowekyala Feb 6, 2022
7e7f215
Revert "Update kotlin version"
oowekyala Feb 8, 2022
fc26561
Merge remote-tracking branch 'origin/master' into list-init-syntax
oowekyala Feb 21, 2022
a4b7fe8
fix merge...
oowekyala Feb 22, 2022
19de993
Fix more conflicts - compiles
oowekyala Feb 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,29 @@ class LinguaFrancaValidationTest {
}
}
}


@Test
def void forbidListLiterals() {
parseWithoutError('''
target C;
reactor Contained {
state x: int[]([1, 2]);
}
''').assertError(LfPackage::eINSTANCE.listLiteral,
null, 'Target C does not support LF list literals')
}


@Test
def void allowListLiteralsInPython() {
parseWithoutError('''
target Python;
reactor Contained {
state x([1, 2]);
}
''').assertNoErrors()
}


/**
* Tests for state and parameter declarations, including native lists.
Expand Down
18 changes: 14 additions & 4 deletions org.lflang/src/org/lflang/LinguaFranca.xtext
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ Expr :
// If it is a constant, the validator should check that if the value
// is non-zero, it is accompanied by a unit.
Value:
(parameter=[Parameter] | time=Time | literal=Literal | code=Code);
(parameter=[Parameter] | time=Time | literal=Literal | list=ListLiteral | code=Code);


Time:
Expand All @@ -341,10 +341,10 @@ Type:
;

ArraySpec:
ofVariableLength?='[]' | '[' length=INT ']';
'[' ( ofVariableLength?=']' | length=INT ']' );

WidthSpec:
ofVariableLength?='[]' | '[' (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']';
'[' ( ofVariableLength?=']' | (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']' );

WidthTerm:
width=INT
Expand Down Expand Up @@ -385,6 +385,16 @@ SignedInt:
INT | NEGINT
;

// List literals support even empty lists, and a trailing comma.

// Note: this expansion is weirdly written, but without the
// emptyList property, Xtext fails to push a ListLiteral node
// when the list is empty... Another bug of Xtext.
ListLiteral:
'[' (items+=Value (',' items+=Value)* ','? ']'
| ','? emptyList?=']')
;

Literal:
STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean;

Expand Down Expand Up @@ -522,7 +532,7 @@ Token:
// Braces
'(' | ')' | '{' | '}' |
// Brackets
'[' | ']' | '<' | '>' | '[]' |
'[' | ']' | '<' | '>' |
// Punctuation
':' | ';' | ',' | '.' | '::' |
// Slashes
Expand Down
8 changes: 8 additions & 0 deletions org.lflang/src/org/lflang/Target.java
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,14 @@ public enum Target {
this(description, requiresTypes, "N/A", "N/A", keywords);
}

/**
* Returns true if the target supports list literals in LF syntax.
* If not, list literals produce validator errors.
*/
public boolean supportsLfListLiterals() {
return this == Python;
}


/**
* Return the target that matches the given string.
Expand Down
133 changes: 26 additions & 107 deletions org.lflang/src/org/lflang/generator/PythonGenerator.xtend
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ package org.lflang.generator
import java.io.File
import java.util.ArrayList
import java.util.LinkedHashSet
import java.util.LinkedList
import java.util.List
import java.util.regex.Pattern
import org.eclipse.emf.ecore.resource.Resource
Expand All @@ -48,12 +47,10 @@ import org.lflang.lf.Input
import org.lflang.lf.Instantiation
import org.lflang.lf.Model
import org.lflang.lf.Output
import org.lflang.lf.Parameter
import org.lflang.lf.Port
import org.lflang.lf.Reaction
import org.lflang.lf.Reactor
import org.lflang.lf.ReactorDecl
import org.lflang.lf.StateVar
import org.lflang.lf.TriggerRef
import org.lflang.lf.Value
import org.lflang.lf.VarRef
Expand Down Expand Up @@ -182,99 +179,35 @@ class PythonGenerator extends CGenerator {
* @param v A value
* @return A value string in the target language
*/
private def getPythonTargetValue(Value v) {
var String returnValue = "";
switch(v.toText) {
case "false": returnValue = "False"
case "true": returnValue = "True"
default: returnValue = super.getTargetValue(v)
}

protected def String getPythonTargetValue(Value v) {
// Parameters in Python are always prepended with a 'self.'
// predicate. Therefore, we need to append the returned value
// if it is a parameter.
if (v.parameter !== null) {
returnValue = "self." + returnValue;
}

return returnValue;
}

/**
* Create a list of state initializers in target code.
*
* @param state The state variable to create initializers for
* @return A list of initializers in target code
*/
protected def List<String> getPythonInitializerList(StateVar state) {
if (!state.isInitialized) {
return null
return "self." + super.getTargetValue(v);
} else if (v.list !== null) {
return "[" + v.list.items.join(', ', [it.pythonTargetValue]) + "]"
}

var list = new LinkedList<String>();

for (i : state?.init) {
if (i.parameter !== null) {
list.add(i.parameter.targetReference)
} else if (state.isOfTimeType) {
list.add(i.targetTime)
} else {
list.add(i.pythonTargetValue)
}
switch(v.toText) {
case "false": return "False"
case "true": return "True"
default: return super.getTargetValue(v)
}
return list
}

/**
* Create a Python tuple for parameter initialization in target code.
*
* @param p The parameter instance to create initializers for
* @return Initialization code
*/
protected def String getPythonInitializer(StateVar state) throws Exception {
if (state.init.size > 1) {
// state variables are initialized as mutable lists
return state.init.join('[', ', ', ']', [it.pythonTargetValue])
} else if (state.isInitialized) {
return state.init.get(0).getPythonTargetValue
} else {
return "None"
}

}

/**
* Create a Python list for parameter initialization in target code.
*
* @param p The parameter instance to create initializers for
* @return Initialization code
*/
protected def String getPythonInitializer(ParameterInstance p) {
if (p.init.size > 1) {
// parameters are initialized as immutable tuples
return p.init.join('(', ', ', ')', [it.pythonTargetValue])
} else {
return p.init.get(0).getPythonTargetValue
}

}

/**
* Create a Python list for parameter initialization in target code.
*
* @param p The parameter to create initializers for
* @return Initialization code
*/
protected def String getPythonInitializer(Parameter p) {
if (p.init.size > 1) {
// parameters are initialized as immutable tuples
return p.init.join('(', ', ', ')', [it.pythonTargetValue])
} else {
return p.init.get(0).pythonTargetValue
}


/** Returns the python initializer corresponding to the given LF init list. */
def String getPythonInitializer(List<Value> init) {
if (init === null) {
return "None"
} if (init.size > 1) {
// corresponds to a tuple
return init.join('(', ', ', ')', [it.pythonTargetValue])
} else {
return init.get(0).getPythonTargetValue
}
}

/**
* Generate parameters and their respective initialization code for a reaction function
* The initialization code is put at the beginning of the reaction before user code
Expand Down Expand Up @@ -389,20 +322,6 @@ class PythonGenerator extends CGenerator {

}

/**
* Handle initialization for state variable
* @param state a state variable
*/
def String getTargetInitializer(StateVar state) {
if(!state.isInitialized)
{
return '''None'''
}

'''«FOR init : state.pythonInitializerList SEPARATOR ", "»«init»«ENDFOR»'''
}


/**
* Wrapper function for the more elaborate generatePythonReactorClass that keeps track
* of visited reactors to avoid duplicate generation
Expand Down Expand Up @@ -522,11 +441,11 @@ class PythonGenerator extends CGenerator {
if (!param.inferredType.targetType.equals("PyObject*")) {
// If type is given, use it
temporary_code.
append(''' self._«param.name»:«param.inferredType.pythonType» = «param.pythonInitializer»
append(''' self._«param.name»:«param.inferredType.pythonType» = «param.init.pythonInitializer»
''')
} else {
// If type is not given, just pass along the initialization
temporary_code.append(''' self._«param.name» = «param.pythonInitializer»
temporary_code.append(''' self._«param.name» = «param.init.pythonInitializer»
''')

}
Expand All @@ -547,11 +466,11 @@ class PythonGenerator extends CGenerator {
if (!stateVar.inferredType.targetType.equals("PyObject*")) {
// If type is given, use it
temporary_code.
append(''' self.«stateVar.name»:«stateVar.inferredType.pythonType» = «stateVar.pythonInitializer»
append(''' self.«stateVar.name»:«stateVar.inferredType.pythonType» = «stateVar.init.pythonInitializer»
''')
} else if (stateVar.isInitialized) {
// If type is not given, pass along the initialization directly if it is present
temporary_code.append(''' self.«stateVar.name» = «stateVar.pythonInitializer»
temporary_code.append(''' self.«stateVar.name» = «stateVar.init.pythonInitializer»
''')
} else {
// If neither the type nor the initialization is given, use None
Expand Down Expand Up @@ -665,14 +584,14 @@ class PythonGenerator extends CGenerator {
«instance.uniqueID»_lf = \
[«FOR member : instance.bankMembers SEPARATOR ", \\\n"»\
_«className»(bank_index = «member.bankIndex/* bank_index is specially assigned by us*/»,\
«FOR param : member.parameters SEPARATOR ", "»_«param.name»=«param.pythonInitializer»«ENDFOR»)«ENDFOR»]
«FOR param : member.parameters SEPARATOR ", "»_«param.name»=«param.init.pythonInitializer»«ENDFOR»)«ENDFOR»]
''')
return
} else if (instance.bankIndex === -1 && !instance.definition.reactorClass.toDefinition.allReactions.isEmpty) {
pythonClassesInstantiation.append('''
«instance.uniqueID»_lf = \
[_«className»(bank_index = 0«/* bank_index is specially assigned by us*/», \
«FOR param : instance.parameters SEPARATOR ", \\\n"»_«param.name»=«param.pythonInitializer»«ENDFOR»)]
«FOR param : instance.parameters SEPARATOR ", \\\n"»_«param.name»=«param.init.pythonInitializer»«ENDFOR»)]
''')
}

Expand Down
8 changes: 8 additions & 0 deletions org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import org.lflang.lf.Instantiation
import org.lflang.lf.KeyValuePair
import org.lflang.lf.KeyValuePairs
import org.lflang.lf.LfPackage.Literals
import org.lflang.lf.ListLiteral
import org.lflang.lf.Model
import org.lflang.lf.NamedHost
import org.lflang.lf.Output
Expand Down Expand Up @@ -371,6 +372,13 @@ class LFValidatorImpl extends AbstractLFValidator {
}
}

@Check(FAST)
def checkListLiteral(ListLiteral list) {
if (!target.supportsLfListLiterals()) {
error("Target " + target + " does not support LF list literals", Literals.LIST_LITERAL__ITEMS)
}
}

@Check(FAST)
def checkConnection(Connection connection) {

Expand Down
6 changes: 3 additions & 3 deletions test/Python/src/ArrayAsParameter.lf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Source has an array as a parameter, the elements of which it passes
// to Print.
target Python;
reactor Source(sequence(0, 1, 2)) {
reactor Source(sequence([0, 1, 2])) {
output out;
state count(0);
logical action next;
Expand Down Expand Up @@ -31,7 +31,7 @@ reactor Print {
=}
}
main reactor ArrayAsParameter {
s = new Source(sequence = (1, 2, 3, 4));
s = new Source(sequence = [1, 2, 3, 4]);
p = new Print();
s.out -> p._in;
}
}
25 changes: 25 additions & 0 deletions test/Python/src/ListLiterals.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This example illustrates state variables and parameters on the wiki.
// For this test, success is just compiling and running.
target Python;


reactor Sub(seq([1,2])) {

}

main reactor {
state l12([1,2]);
state empty([]);
state empty2([,]);

sub = new Sub(seq = [1, 2]);
sub2 = new Sub(seq = (1, 2));
sub3 = new Sub(seq = ([1, 2]));
lhstrh marked this conversation as resolved.
Show resolved Hide resolved

reaction(startup) {=
assert self.empty == []
assert self.empty2 == []
assert self.l12 == [1, 2]
print("Success")
=}
}
4 changes: 2 additions & 2 deletions test/Python/src/MovingAverage.lf
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ reactor MASource {
=}
}
reactor MovingAverageImpl {
state delay_line(0.0, 0.0, 0.0);
state delay_line([0.0, 0.0, 0.0]);
state index(0);
input m_in;
output out;
Expand Down Expand Up @@ -46,4 +46,4 @@ main reactor MovingAverage {
p = new TestDouble(expected=(0.0, 0.25, 0.75, 1.5, 2.5, 3.5));
s.out -> m.m_in;
m.out -> p.t_in;
}
}
Loading