The following sets of rules are mutually left-recursive
[valeurBooleene, valeur]
I know that ANTLR handle well left-recursive rules but only in one rule, not between rules.
I have to rewrite my rules to solve the mutually left-recursive problem but I don't see how...
grammar minimal;
constante:
bool = BOOLEAN
| intgr = INTEGER
;
valeurBooleene:
'(' val = valeurBooleene ')'
| bool = BOOLEAN
| 'non' not = valeur
| leftValue = valeur '=' rightValue = valeur
;
valeur:
'(' val = valeur ')'
| cst = constante
| booleanValue = valeurBooleene
| '|' absolute = valeur '|'
| '-' opposite = valeur
;
condition:
'(' cond = condition ')'
| bool = valeurBooleene
;
si:
'Si' cond = condition 'alors' ( thenActions += action )+
( 'sinon' ( elseActions += action )+ )?
;
system:
( actions += action )+
EOF
;
action:
ifClause = si
;
BOOLEAN:
'false'
| 'true'
| 'faux'
| 'vrai'
;
INTEGER:
[0-9]+
;
WS:
[ \n\t\r]+ -> skip
;
Finally, I follow the advices provided by sepp2k like this:
grammar minimal;
value:
'(' val = value ')' # Parenthesis
| b = BOOLEAN # Boolean
| 'non' not = value # Boolean
| l = value '=' r = value # Boolean
| i = INTEGER # Numeric
| '|' abs = value '|' # Numeric
| '-' opposite = value # Numeric
;
condition:
'(' condition ')'
| cond = value
;
ifStmnt:
'IF' condition 'THEN' ( actionsThen += action )+
( 'ELSE' ( actionsElse += action )+ )?
;
system:
( actions += action )+
EOF
;
print:
'print' val = value
;
action:
ifs = ifStmnt
| pr = print
;
BOOLEAN:
'false'
| 'true'
;
INTEGER:
[0-9]+
;
WS:
[ \n\t\r]+ -> skip
;
The labels create classes which extends ValueContext, I use them like this:
package com.thalesgroup.dms.stimulus.factories;
import minimal.minimalParser.BooleanContext;
import minimal.minimalParser.ConditionContext;
import minimal.minimalParser.NumericContext;
import minimal.minimalParser.ParenthesisContext;
import minimal.minimalParser.ValueContext;
public class Sample {
void analyze( ValueContext ctxt ) {
if( ctxt instanceof ParenthesisContext ) {
analyze(((ParenthesisContext)ctxt).val );
}
else if( ctxt instanceof BooleanContext ) {
final BooleanContext bc = (BooleanContext)ctxt;
analyze( bc );
}
else if( ctxt instanceof NumericContext ) {
final NumericContext nc = (NumericContext)ctxt;
analyze( nc );
}
}
void analyze( ConditionContext ctxt ) {
if( ctxt.cond instanceof BooleanContext ) {
final BooleanContext boolCtxt = (BooleanContext)ctxt.cond;
// build a condition from a BooleanContext
analyze( boolCtxt );
}
else {
throw new IllegalStateException();
}
}
void analyze( BooleanContext ctxt ) {
if( ctxt.b != null ) {
final String literal = ctxt.b.getText();
if( literal.equals( "true" )) {
// deal with a boolean literal 'true'
}
else if( literal.equals( "false" )) {
// deal with a boolean literal 'false'
}
}
else if( ctxt.not != null ) {
final BooleanContext bCtxt = (BooleanContext)ctxt.not;
// deal with a 'not' operation
}
else if( ctxt.l != null ) {
final ValueContext left = ctxt.l;
final ValueContext right = ctxt.r;
// deal with 'equals'
}
else {
// ???
}
}
void analyze( NumericContext ctxt ) {
if( ctxt.abs != null ) {
// deal with abs( x )
}
else if( ctxt.opposite != null ) {
// deal with - x
}
else if( ctxt.i != null ) {
// deal with literal integer
}
}
}
I want to update 1 cell in an Excel (xlsx) file using OLEDB.
I have attached my code.
The first time you run, it fills in the values because INSERT is working.
The second time you run, it APPENDS the values because the UPDATE fails, and my Catch performs an INSERT. My goal is to have the UPDATE command work. When the UPDATE command executes, I get an error message:
No value given for one or more required parameters.
at System.Data.OleDb.OleDbCommand.ExecuteCommandTextErrorHandling(OleDbHResult hr)
at System.Data.OleDb.OleDbCommand.ExecuteCommandTextForSingleResult(tagDBPARAMS dbParams, Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteCommandText(Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteCommand(CommandBehavior behavior, Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteReaderInternal(CommandBehavior behavior, String method)
at System.Data.OleDb.OleDbCommand.ExecuteNonQuery()
at ConsoleApp3.Program.InsertCSVIntoSheet(String excelPath, String sheet, String csvPath) in C:\Users\jbendler.atlab\source\repos\ConsoleApp3\Program.cs:line 175
This code comes from a demo Console App. This is the program.cs file.
The main part of the code is located at the bottom of the InsertCSVIntoSheet method. The output xlsx file is simply a new workbook, unedited. The input file is simply a text file, named with a .csv extension, that contains the following separated by carriage-return/linefeed - so the file has 5 lines with one character per line:
ABCDE
Thanks.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
InsertCSVIntoSheet(#"c:\temp\book1.xlsx", "NewSheet", #"c:\temp\test.csv");
}
private static void InsertCSVIntoSheet(string excelPath, string sheet, string csvPath)
{
int column;
int row;
int pos;
bool done;
char readUntilChar;
string csvData;
string columnName;
string cell;
string excelSheetName;
List<Tuple<int, int, object>> values = new List<Tuple<int, int, object>>();
string connectionString = CreateOleDbConnectionStringForExcel(excelPath);
OleDbCommand oleDbCommand = new OleDbCommand();
decimal decimalTest;
DateTime dateTimeTest;
int status;
int numColumns;
// Put CSV in to row/column/value Tuples
using (StreamReader reader = new StreamReader(csvPath))
{
csvData = reader.ReadToEnd();
row = 1;
// Split the csv data by new line
foreach (string line in csvData.Split(new string[] { "\r\n" }, StringSplitOptions.None))
{
if (!string.IsNullOrEmpty(line))
{
column = 1;
pos = 0;
cell = string.Empty;
// Split each line by ',' to get each cell. A value wrapped in '"' can include a ','
while (pos < line.Length)
{
cell = string.Empty;
// Check the first character. If it is a '"' then we assume the cell is surrounded
// in '"' and do NOT include the '"' in the output to the excel cell.
if (line[pos] == '"')
{
readUntilChar = '"';
pos++;
}
else
{
readUntilChar = ',';
}
done = line[pos] == readUntilChar;
if (line[pos] == '"')
{
// Skip over second '"' for a blank ("")
pos++;
}
while (!done)
{
cell += line[pos++];
if (pos == line.Length || line[pos] == readUntilChar)
{
if (readUntilChar == '"')
{
// Skip the '"'
pos++;
}
done = true;
}
}
// Skip over the ','
pos++;
if (!string.IsNullOrEmpty(cell))
{
// Test the data to determine the type (check for decimal and DateTime).
if (decimal.TryParse(cell, out decimalTest))
{
values.Add(new Tuple<int, int, object>(row, column, decimalTest));
}
else if (DateTime.TryParse(cell, out dateTimeTest))
{
values.Add(new Tuple<int, int, object>(row, column, dateTimeTest));
}
else
{
// Write out the value as a string
values.Add(new Tuple<int, int, object>(row, column, cell));
}
}
column++;
}
}
row++;
}
}
using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Suppress))
{
excelSheetName = GetExcelSheetNames(connectionString).Where(n => n.ToUpper().StartsWith(sheet.ToUpper())).FirstOrDefault();
//Set the connection string to recognize the header and to operate in Update mode(IMEX= 0)
using (OleDbConnection oleDbConnection = new OleDbConnection(connectionString.Replace("IMEX=1", "IMEX=0")))
{
oleDbConnection.Open();
oleDbCommand = new OleDbCommand();
oleDbCommand.Connection = oleDbConnection;
oleDbCommand.CommandType = CommandType.Text;
if (excelSheetName != null)
{
// Delete Sheet
oleDbCommand.CommandText = "DROP TABLE [" + sheet + "]";
status = oleDbCommand.ExecuteNonQuery();
}
else
{
excelSheetName = sheet + "$";
}
numColumns = values.Max(v => v.Item2);
oleDbCommand.CommandText = "CREATE TABLE [" + sheet + "](";
for (int index = 0; index < numColumns; index++)
{
oleDbCommand.CommandText += "Column" + index.ToString() + " CHAR(255), ";
}
oleDbCommand.CommandText = oleDbCommand.CommandText.Substring(0, oleDbCommand.CommandText.Length - 2) + ")";
status = oleDbCommand.ExecuteNonQuery();
}
using (OleDbConnection oleDbConnection = new OleDbConnection(connectionString.Replace("IMEX=1", "IMEX=0")))
{
oleDbConnection.Open();
oleDbCommand.Connection = oleDbConnection;
// Write out new values
foreach (Tuple<int, int, object> tuple in values)
{
try
{
columnName = GetExcelColumnName(tuple.Item2) + (tuple.Item1 + 1).ToString();
oleDbCommand.CommandText = "UPDATE [" + excelSheetName + columnName + ":" + columnName + "] SET " + "F1" + " = '" + tuple.Item3.ToString() + "'";
status = oleDbCommand.ExecuteNonQuery();
}
catch (OleDbException oledbex)
{
oleDbCommand.CommandText = "INSERT INTO [" + excelSheetName + "] VALUES ('" + tuple.Item3.ToString() + "')";
status = oleDbCommand.ExecuteNonQuery();
}
}
}
}
}
private static List<string> GetExcelSheetNames(string connectionString)
{
OleDbConnection oleDbConnection = null;
DataTable dataTable = null;
List<string> excelSheetNames = null;
using (oleDbConnection = new OleDbConnection(connectionString))
{
oleDbConnection.Open();
dataTable = oleDbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
}
if (dataTable != null)
{
excelSheetNames = new List<string>(dataTable.Rows.Cast<DataRow>().Where(r => r["TABLE_NAME"].ToString().EndsWith("$") || r["TABLE_NAME"].ToString().EndsWith("$'")).Select(r => r["TABLE_NAME"].ToString().ToUpper()));
}
return excelSheetNames;
}
private static string CreateOleDbConnectionStringForExcel(string sourceFile)
{
var fileInfo = new FileInfo(sourceFile);
switch (fileInfo.Extension.ToUpper())
{
case ".XLS":
return string.Format("Provider=Microsoft.ACE.OLEDB.12.0;;Data Source='{0}';Extended Properties='Excel 8.0;HDR=No;IMEX=1'", sourceFile);
case ".XLSX":
case ".XLSM":
return string.Format("Provider=Microsoft.ACE.OLEDB.12.0;;Data Source='{0}';Extended Properties='Excel 12.0;HDR=No;IMEX=1'", sourceFile);
default:
throw new NotSupportedException("File type not supported.");
}
}
private static string GetExcelColumnName(int columnNumber)
{
string columnName = String.Empty;
int dividend = columnNumber;
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (int)((dividend - modulo) / 26);
}
return columnName;
}
}
}
I have been looking all over the web site, Internet, and of course SO and can't seem to find a description or specification for the markup language being used in umlet.
In the example sequence diagram for example:
title: sample
_alpha:A~id1_|_beta:B~id2_|_gamma:G~id3_
id1->>id2:id1,id2
id2-/>id1:async Msg.
id3->>>id1:id1,id3
id1.>id3:id1,id3:async return Msg
id1->id1:id1:self
iframe{:interaction frame
id2->id3:id1,id3:async Msg.
iframe}
The ->, -->, etc are fairly obvious, but what the colons do?
Why are underlines needed, etc. Inquiring minds would like to know as this looks like a useful tool for sketching.
Found an annotated script (that doesn't seem valid as of 14.2):
//UML2 style titles are optional.
title:sequence diagram
//the participating objects are separated using the pipe symbol "|". Their names may be underlined using underscores.
_alpha:A_|_beta:B_|_gamma:G_
//The following line describes a (synchonuous) message originating from the first object sent to the second object, while both objects are active.
1->>2:1,2
//This message is asynchronous being sent from the second object to the first object. The messages is named.
2-/>1:async Msg.
//A message from the third object to the first object; both objects are active.
3->>>1:1,3
//A named asynchronous return message in UML2 style (using a stick arrow head).
1.>3:1,3:async return Msg
//This is a self call of the first object.
1->1:1:self
//Interaction frames may be used to show optional or conditional blocks within sequence diagrams.
iframe{:interaction frame
2->3:2,3:async Msg.
iframe}
RE: BNF, there's umlet-elements/src/main/javacc/SequenceAllInOneParser.jj in the source:
options {
static = false;
IGNORE_CASE = false;
JAVA_UNICODE_ESCAPE = true;
JDK_VERSION = "1.6";
// Not yet detected as a valid option by the Eclipse plugin, but works nonetheless. This is essential for GWT compatible generated code.
JAVA_TEMPLATE_TYPE = "modern";
}
PARSER_BEGIN(SequenceAllInOneParser)
package com.baselet.element.facet.specific.sequence_aio.gen;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import com.baselet.control.enums.LineType;
import com.baselet.element.facet.specific.sequence_aio.Lifeline;
import com.baselet.element.facet.specific.sequence_aio.Message.ArrowType;
import com.baselet.element.facet.specific.sequence_aio.SequenceDiagramBuilder;
import com.baselet.element.facet.specific.sequence_aio.SequenceDiagramException;
public class SequenceAllInOneParser {
private static final Logger log = LoggerFactory.getLogger(SequenceAllInOneParser.class);
private boolean autoTick;
/**
* Replaces "\\\\" with "\\" in the output so that any further specified match replace pair can't match the replaced "\\".
* #param matchReplacePairs
* #return the string with the replacements
*/
public static String backslashReplace(String input, String... matchReplacePairs) {
if (matchReplacePairs.length % 2 == 1) {
throw new IllegalArgumentException("matchReplacePairs must have an even number of elements.");
}
String split = "\\\\";
StringBuilder strBuilder = new StringBuilder(input.length());
int firstIndex = 0;
// use the indexOf function instead of a split, because split uses regex and regex is not 100% supported by GWT
int foundIndex = input.indexOf(split, firstIndex);
while (firstIndex < input.length()) {
int lastIndex = foundIndex == -1 ? input.length() : foundIndex;
String tmp = input.substring(firstIndex, lastIndex);
for (int j = 0; j < matchReplacePairs.length - 1; j += 2) {
tmp = tmp.replace(matchReplacePairs[j], matchReplacePairs[j + 1]);
}
strBuilder.append(tmp);
if (foundIndex != -1) {
strBuilder.append('\\');
}
firstIndex = lastIndex + split.length();
foundIndex = input.indexOf(split, firstIndex);
}
return strBuilder.toString();
}
/**
* Small data container to pass all informations.
*/
private class MessageArrowInfo {
boolean fromLeftToRight;
LineType lineType;
ArrowType arrowType;
}
private class InteractionConstraint {
private String lifelineId = null;
private String text = "";
}
private class LifelineInterval {
private String startId;
private String endId;
}
}
PARSER_END(SequenceAllInOneParser)
/* defines input to be ignored */
< * > SKIP:
{
" "
| "\t"
}
< DIAGRAM_DEF_TEXT > SKIP:
{
"\n" : DEFAULT
| "\r\n" : DEFAULT
| "\r" : DEFAULT
}
< DEFAULT > TOKEN:
{
< DIAGRAM_OPTION_OVERRIDE_ID :"overrideIds=" >
| < DIAGRAM_OPTION_AUTO_TICK: "autoTick=" >
| < TRUE: "true" >
| < FALSE: "false" >
| < DIAGRAM_TITLE: "title=" > : DIAGRAM_DEF_TEXT
| < DIAGRAM_DESC: "desc=" > : DIAGRAM_DEF_TEXT
| < LIFELINE_DEFINITIONS: "obj=" > : LIFELINE_DEF
}
< DIAGRAM_DEF_TEXT > TOKEN:
{
< DIAGRAM_DEFINITION_TEXT:
(
~ ["\\","\r","\n"]
| ("\\" ["\\", "n"] )
)+> : DIAGRAM_DEF_TEXT
}
< LIFELINE_DEF > TOKEN :
{
< LL_DEF_NEW_LINE: "\n" | "\r\n" | "\r" > : DEFAULT
| < LIFELINE_DEF_DELIMITER: "|" >
/*| < LIFELINE_TITLE_DELIMITER: "~" > integrated in the title */
| < LIFELINE_ACTOR: "ACTOR" >
| < LIFELINE_ACTIVE: "ACTIVE" >
| < LIFELINE_CREATED_LATER :"CREATED_LATER" >
| < LIFELINE_EXEC_SPEC_FROM_START: "EXECUTION" >
| < LIFELINE_TITLE: /* since | and ~ are special characters which delimit the title they need to be escaped */
(
~["~","\\","\n","\r","|"]
| ("\\" ["\\","~","|","n"])
)+
"~" >
}
< DEFAULT > TOKEN :
{
< TEXT_DELIMITER: ":" > : DIAGRAM_SEQ_TEXT
| < LIST_DELIMITER: "," >
| < OPEN_CURLY_BRACKET: "{" >
| < CLOSE_CURLY_BRACKET: "}" >
| < COMMAND_DELIMITER: ";" >
| < T_DASH: "-" >
| < T_DOT: "." >
| < T_DDOT: ".." >
| < T_EQ: "=" >
| < MESSAGE_ARROW_LEFT_OPEN: "<" >
| < MESSAGE_ARROW_LEFT_FILLED: "<<<" >
| < MESSAGE_ARROW_RIGHT_OPEN: ">" >
| < MESSAGE_ARROW_RIGHT_FILLED: ">>>" >
| < MESSAGE_DURATION_INC: "+" >
| < LOST: "lost" >
| < FOUND: "found" >
| < GATE: "gate" >
| < START_COREGION: "coregionStart=" >
| < END_COREGION: "coregionEnd=" >
| < INVARIANT: "invariant=" >
| < STATE_INVARIANT: "stateInvariant=" >
| < EXEC_SPEC_START: "on=" >
| < EXEC_SPEC_END: "off=" >
| < LL_DESTROY: "destroy=" >
| < REF: "ref=" >
| < CONTINUATION: "continuation=" >
| < TEXT_ON_LIFELINE: "text=" >
| < TICK: "tick=" >
| < COMBINED_FRAGMENT: "combinedFragment=" > : CF_OPERATOR
| < INTERACTION_CONSTRAINT: "constraint=" >
| < UNSIGNED_INT_CONSTANT: ( ["0" - "9"] ) + >
| < DEFAULT_NEW_LINE: "\n" | "\r\n" | "\r" >
}
< LIFELINE_DEF, DEFAULT > TOKEN :
{
< LIFELINE_ID: ["a"-"z","A"-"Z"] (["a"-"z","A"-"Z","0"-"9"])* >
}
< DIAGRAM_SEQ_TEXT > TOKEN :
{
< TEXT_UNTIL_NEXT_COMMAND : ( /* since ; is special characters which delimit the text it must be escaped */
~["\\","\n","\r",";"]
| ("\\" ["\\",";","n"])
)+ > : DEFAULT
}
< CF_OPERATOR > TOKEN :
{
< COMBINED_FRAGMENT_OPERATOR: /* since ; and ~ are special characters which delimit the operator they need to be escaped */
(
~["~","\\","\n","\r",";"]
| ("\\" ["\\","~","|","n",";"])
)+
"~" > : DEFAULT
}
/* general Tokens */
/* skip comments */
< * > SKIP :
{
<"//"(~["\n","\r"])*>
}
< * > TOKEN :
{
<LAST_LINE_COMMENT: "//"(~["\n","\r"])*>
}
/**
* The main function which parses the whole diagram.
* Line comments are skipped (see Tokens)
*/
SequenceDiagramBuilder start() :
{
String titleText = "";
String descText = "";
SequenceDiagramBuilder diagram = new SequenceDiagramBuilder();
autoTick = true;
}
{
(
(
titleText=DiagramTitle() /* NL skip and change state to DEFAULT */
| descText = DiagramDescription() /* NL skip and change state to DEFAULT */
| Option(diagram) < DEFAULT_NEW_LINE >
| LifelineDefinitions(diagram) /* NL part of the nonterminal */
)+//diagram definition as new NT followed by NL alternating with sequence so that the sequence= can be removed
Sequence(diagram)
)
(< LAST_LINE_COMMENT >)?
<EOF>
{
diagram.setTitle(titleText);
diagram.setText(descText);
return diagram;
}
}
String DiagramTitle() :
{
String text = "";
}
{
< DIAGRAM_TITLE >
(
< DIAGRAM_DEFINITION_TEXT > { text = backslashReplace(token.image, "\\n","\n");}
)?
{return text;}
}
String DiagramDescription() :
{
String desc = "";
}
{
< DIAGRAM_DESC >
< DIAGRAM_DEFINITION_TEXT > { desc = backslashReplace(token.image, "\\n","\n");}
{return desc;}
}
/**
* Options for the whole diagram
*/
void Option(SequenceDiagramBuilder diagram) :
{
boolean overrideIds;
}
{
< DIAGRAM_OPTION_OVERRIDE_ID > overrideIds = booleanConstant()
{
diagram.setOverrideDefaultIds(overrideIds);
}
| < DIAGRAM_OPTION_AUTO_TICK > autoTick = booleanConstant()
}
/**
* Defines all Lifelines
*/
void LifelineDefinitions(SequenceDiagramBuilder diagram) :
{}
{
< LIFELINE_DEFINITIONS > LifelineDef(diagram) (< LIFELINE_DEF_DELIMITER > LifelineDef(diagram))* < LL_DEF_NEW_LINE >
}
/**
* Defines one Lifeline, the id can't be LIFELINE_ACTOR, LIFELINE_ACTIVE
* or LIFELINE_CREATED_LATER because these are keywords.
*/
void LifelineDef(SequenceDiagramBuilder diagram) :
{
String name = "";
String id = null;
boolean createdOnStart = true;
Lifeline.LifelineHeadType headType = Lifeline.LifelineHeadType.STANDARD;
boolean execSpecFromStart = false;
}
{
name = LifelineDefTitleText() (id=LifelineId())?
(
< LIFELINE_ACTOR > { headType = Lifeline.LifelineHeadType.ACTOR; }
| < LIFELINE_ACTIVE > { headType = Lifeline.LifelineHeadType.ACTIVE_CLASS; }
| < LIFELINE_CREATED_LATER > { createdOnStart = false; }
| < LIFELINE_EXEC_SPEC_FROM_START > { execSpecFromStart = true; }
) *
{
if("lost".equals(id) || "found".equals(id)) {
throw new SequenceDiagramException("'lost' and 'found' are keywords and can not be used as lifeline identifiers.");
}
diagram.addLiveline(name, id, headType, createdOnStart, execSpecFromStart);
}
}
/** can could be multiple lines */
String LifelineDefTitleText() :
{}
{
< LIFELINE_TITLE >
{
/* remove trailing ~ and handle the escaping of \, |, n and ~ */
return backslashReplace(token.image.substring(0, token.image.length() - 1), "\\n", "\n", "\\~", "~", "\\|", "|");
}
}
/**
* Can't be one of the following, because these are keywords in the lifeline definition!
* < LIFELINE_ACTOR: "ACTOR" >
* < LIFELINE_ACTIVE: "ACTIVE" >
* < LIFELINE_CREATED_LATER :"CREATED_LATER" >
*/
String LifelineId() :
{}
{
< LIFELINE_ID >
{
return token.image;
}
}
void Sequence(SequenceDiagramBuilder diagram) :
{}
{
(
SequenceTick(diagram) < DEFAULT_NEW_LINE >
| (
SequenceElement(diagram)
( LOOKAHEAD(2) < COMMAND_DELIMITER > SequenceElement(diagram))*
(< COMMAND_DELIMITER >)?
< DEFAULT_NEW_LINE >
{ if(autoTick) { diagram.tick(); } }
)
| < DEFAULT_NEW_LINE >
)*
}
void SequenceTick(SequenceDiagramBuilder diagram) :
{
int tickCount = 1;
}
{
< TICK > (tickCount = unsignedIntConstant())?
{ diagram.tick(tickCount); }
}
void SequenceElement(SequenceDiagramBuilder diagram) :
{}
{
(
MessageOrGeneralOrderingOrText(diagram) /* Message, GeneralOrdering have a common prefix for more clarity a new nonterminal was created*/
| Coregion(diagram)
| DestroyLL(diagram)
| ExecutionSpecification(diagram)
| StateInvariant(diagram)
//| (diagram) //general ordering, TimeConstraint TimeObservation and DurationConstraint Duration Observation missing
| InteractionUse(diagram)
| Continuation(diagram)
| CombinedFragment(diagram)
)
}
void MessageOrGeneralOrderingOrText(SequenceDiagramBuilder diagram) :
{}
{
/*
* (Message and GeneralOrdering) A common prefix is: <LIFELINE_ID> "." <LIFELINE_ID> "<"
* All three have <LIFELINE_ID > as common prefix
* therefore use LOOKAHEAD(2) if it is a text, if not use a LOOKAHEAD(5) to determine if it is a message or a general ordering
*/
LOOKAHEAD(2) TextOnLifeline(diagram)
| LOOKAHEAD(5) Message(diagram)
| GeneralOrdering(diagram)
}
void CombinedFragment(SequenceDiagramBuilder diagram) :
{
LifelineInterval interval = new LifelineInterval();
String operator = "";
String id = null;
}
{
(
< COMBINED_FRAGMENT >
(
< COMBINED_FRAGMENT_OPERATOR >
{ operator = backslashReplace(token.image.substring(0, token.image.length() - 1), "\\n", "\n", "\\;", ";", "\\~", "~"); }
)
[
id = LifelineId()
[interval = LifelineInterval()]
]
{ diagram.beginCombinedFragment(interval.startId, interval.endId, id, operator); }
)
| (
< T_DASH > < T_DASH > (< T_EQ > id=LifelineId())?
{ diagram.endCombinedFragment(id); }
)
| (
< T_DDOT > (< T_EQ > id=LifelineId())?
{ diagram.endAndBeginOperand(id); }
)
}
void Message(SequenceDiagramBuilder diagram) :
{
String leftLifelineId = null;
String rightLifelineId = null;
String leftLifelineLocalId = null;
String rightLifelineLocalId = null;
MessageArrowInfo messageArrowInfo;
String msgText = "";
int lostCount = 0;
int foundCount = 0;
int gateCount = 0;
int duration = 0;
}
{
(
(
leftLifelineId = LifelineId() [LOOKAHEAD(2)< T_DOT > leftLifelineLocalId = LifelineId()]
| < LOST > { leftLifelineId = "lost"; lostCount++; }
| < FOUND > { leftLifelineId = "found"; foundCount++; }
| < GATE > { leftLifelineId = "gate"; gateCount++; }
)
messageArrowInfo = MessageArrow()
(
rightLifelineId = LifelineId() [< T_DOT > rightLifelineLocalId = LifelineId()]
| < LOST > { rightLifelineId = "lost"; lostCount++; }
| < FOUND > { rightLifelineId = "found"; foundCount++; }
| < GATE > { rightLifelineId = "gate"; gateCount++; }
)
(duration = MessageDuration())?
(msgText = TextUntilNewLine())?
)
{
if(lostCount + foundCount + gateCount > 1) {
throw new SequenceDiagramException("Error: 'lost', 'found' and 'gate' can only occur once per message.");
}
String send;
String receive;
String sendLocalId;
String receiveLocalId;
if(messageArrowInfo.fromLeftToRight) {
send = leftLifelineId;
receive = rightLifelineId;
sendLocalId = leftLifelineLocalId;
receiveLocalId = rightLifelineLocalId;
}
else {
send = rightLifelineId;
receive = leftLifelineId;
sendLocalId = rightLifelineLocalId;
receiveLocalId = leftLifelineLocalId;
}
if(gateCount > 0) {
if(duration != 0) {
throw new SequenceDiagramException("Error: a messages with a gate can only have a duration of 0, but the duration was " + duration + ".");
}
if(send.equals("gate")) {
diagram.addSendGateMessage(receive, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, receiveLocalId);
}
else {
diagram.addReceiveGateMessage(send, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, sendLocalId);
}
}
else if(send.equals("lost")) {
throw new SequenceDiagramException("Error: 'lost' can only be on the receiving end of a message.");
}
else if(send.equals("found")) {
if(duration != 0) {
throw new SequenceDiagramException("Error: 'lost' and 'found' messages can only have a duration of 0, but the duration was " + duration + ".");
}
diagram.addFoundMessage(receive, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, receiveLocalId);
}
else
{
if(receive.equals("lost")) {
if(duration != 0) {
throw new SequenceDiagramException("Error: 'lost' and 'found' messages can only have a duration of 0, but the duration was " + duration + ".");
}
diagram.addLostMessage(send, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, sendLocalId);
}
else if(receive.equals("found")) {
throw new SequenceDiagramException("Error: 'found' can only be on the sending end of a message.");
}
else {
diagram.addMessage(send, receive, duration, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, sendLocalId, receiveLocalId);
}
}
}
}
MessageArrowInfo MessageArrow():
{
MessageArrowInfo messageArrowInfo = new MessageArrowInfo();
}
{
(
(
messageArrowInfo.lineType = MessageArrowLineType()
(
< MESSAGE_ARROW_RIGHT_OPEN > { messageArrowInfo.arrowType = ArrowType.OPEN; }
| < MESSAGE_ARROW_RIGHT_FILLED > { messageArrowInfo.arrowType = ArrowType.FILLED; }
)
)
{
messageArrowInfo.fromLeftToRight = true;
}
| (
(
< MESSAGE_ARROW_LEFT_OPEN > { messageArrowInfo.arrowType = ArrowType.OPEN; }
| < MESSAGE_ARROW_LEFT_FILLED > { messageArrowInfo.arrowType = ArrowType.FILLED; }
)
messageArrowInfo.lineType = MessageArrowLineType()
)
{
messageArrowInfo.fromLeftToRight = false;
}
)
{ return messageArrowInfo; }
}
LineType MessageArrowLineType() :
{
LineType lineType;
}
{
(
< T_DASH > { lineType = LineType.SOLID; }
| < T_DOT > { lineType = LineType.DASHED; }
)
{
return lineType;
}
}
int MessageDuration() :
{
int duration;
}
{
(
(
< MESSAGE_DURATION_INC > { duration = 1; }
(
duration = unsignedIntConstant()
| (< MESSAGE_DURATION_INC > { duration++; })*
)
)
| (
< T_DASH > { duration = -1; }
(
duration = unsignedIntConstant() { duration = -duration; }
| (< T_DASH > { duration--; })*
)
)
)
{
return duration;
}
}
void GeneralOrdering(SequenceDiagramBuilder diagram):
{
String leftLifelineId = null;
String rightLifelineId = null;
String leftLifelineLocalId = null;
String rightLifelineLocalId = null;
boolean leftEarlier;
}
{
leftLifelineId = LifelineId()
< T_DOT >
leftLifelineLocalId = LifelineId()
(
< MESSAGE_ARROW_RIGHT_OPEN > { leftEarlier = true; }
| < MESSAGE_ARROW_LEFT_OPEN > { leftEarlier = false; }
)
rightLifelineId = LifelineId()
< T_DOT >
rightLifelineLocalId = LifelineId()
{
if(leftEarlier) {
diagram.addGeneralOrdering(leftLifelineId, leftLifelineLocalId, rightLifelineId, rightLifelineLocalId);
}
else {
diagram.addGeneralOrdering(rightLifelineId, rightLifelineLocalId, leftLifelineId, leftLifelineLocalId);
}
}
}
void Coregion(SequenceDiagramBuilder diagram) :
{
String lifelineId;
boolean start;
}
{
(
< START_COREGION > { start = true; }
| < END_COREGION > { start = false; }
)
lifelineId = LifelineId()
{
diagram.addCoregion(lifelineId, start);
}
}
void DestroyLL(SequenceDiagramBuilder diagram) :
{
String lifelineId;
}
{
< LL_DESTROY >
lifelineId = LifelineId()
{
diagram.destroyLifeline(lifelineId);
}
}
void ExecutionSpecification(SequenceDiagramBuilder diagram) :
{
String lifelineId;
boolean on;
}
{
(
< EXEC_SPEC_START > { on = true; }
| < EXEC_SPEC_END > { on = false; }
)
lifelineId = LifelineId()
{
diagram.changeExecutionSpecification(lifelineId, on);
}
( (< LIST_DELIMITER >)? lifelineId = LifelineId()
{
diagram.changeExecutionSpecification(lifelineId, on);
}
)*
}
void StateInvariant(SequenceDiagramBuilder diagram) :
{
String lifelineId;
String text = "";
boolean stateStyle;
}
{
(
< INVARIANT > { stateStyle = false; }
| < STATE_INVARIANT > { stateStyle = true; }
)
lifelineId = LifelineId()
(text = TextUntilNewLine())?
{
diagram.addStateInvariant(lifelineId, text, stateStyle);
}
}
void TextOnLifeline(SequenceDiagramBuilder diagram) :
{
String lifelineId;
String text = "";
}
{
[< TEXT_ON_LIFELINE >]
lifelineId = LifelineId()
text = TextUntilNewLine() /* text is mandatory, if not lookahead with message and general ordering would not work*/
{
diagram.addTextOnLifeline(lifelineId, text);
}
}
String TextUntilNewLine() :
{
}
{
< TEXT_DELIMITER >
< TEXT_UNTIL_NEXT_COMMAND >
{
return backslashReplace(token.image, "\\n", "\n", "\\;", ";");
}
}
void InteractionUse(SequenceDiagramBuilder diagram):
{
LifelineInterval interval;
String text = "";
}
{
(
< REF >
interval = LifelineInterval()
(text = TextUntilNewLine())?
)
{
diagram.addInteractionUse(interval.startId, interval.endId, text);
}
}
void Continuation(SequenceDiagramBuilder diagram):
{
LifelineInterval interval;
String text = "";
}
{
(
< CONTINUATION >
interval = LifelineInterval()
(text = TextUntilNewLine())?
)
{
diagram.addContinuation(interval.startId, interval.endId, text);
}
}
/**
* #return the start and end of the interval.
*/
LifelineInterval LifelineInterval():
{
LifelineInterval interval = new LifelineInterval();
}
{
interval.startId = LifelineId()
(< LIST_DELIMITER >)?
interval.endId = LifelineId()
{
return interval;
}
}
boolean booleanConstant() :
{ boolean value;}
{
(
<FALSE> { value = false;}
| <TRUE> { value = true;}
) {return value; }
}
int unsignedIntConstant() :
{}
{
<UNSIGNED_INT_CONSTANT> {
int value;
try {
value = Integer.parseInt(token.image);
} catch(NumberFormatException e) {
// only digits are accepted by the gramer, so the only reason for a NumberFormatException should be that the number is too big for int
throw (ParseException) new ParseException("Error: The string '" + token.image + "' couldn't be parsed as integer. The most probable reason is that the number is too big.").initCause(e);
}
return value;
}
}
In a calculator I own 4 input type ="number" in particular:
<input type = "number" id ="mol" min="1" max="4">
<input type = "number" id ="div" min="1" max="4">
<input type = "number" id ="add" min="1" max="4">
<input type = "number" id ="min" min="1" max="4">
In a textbox, I insert the mathematical expression for extended.
<input type = "text" id = "exp" readonly>
The numerical values and the operators have gone through the normal buttons.
So, for example, the expression inserted in the textbox is this: 8*5-9/2+3
Now I would want that when I press the equal key, based on the priority that the customer has given to the operands, the expression result changed.
multiplication: 1
division: 4
subtraction: 2
addition: 3
-> 40 - 9 / 2 + 3
-> 31 / 2 + 3
-> 31 / 5
-> = 6.2
I think this is very difficult to achieve.
Get the value of the textbox and evaluate the result based on the priority of the operands is really extreme. What do you propose?
Try using the split method: http://msdn.microsoft.com/en-us/library/t5az126b(v=vs.94).aspx
Split on the operator with the first priority first, then split each group by the second operator, then for each subgroup split by the third operator, and for each of those sub-subgroups split by the fourth. Then apply the operator to the groups moving upward.
Conceptual example (not the full code you'll need, but should give an idea):
var expression = "8*5-9/2+3"; // Get this from the data
var operator1Groups = expression.split("*"); // Instead of hardcoding this, retrieve which operator has highest priority from the data
for (var i in operator1Groups) {
var operator2Groups = operator1Groups[i].split("+"); // Similarly, don't hardcode the operator
// And so on...
// Once you get to the lowest operator, start applying the operator to each group
}
The way to do this is with an Operator Precedence Parser. They are commonly implemented using Djikstra's Shunting-yard Algorithm. You could specify the precedence by loading the operators and precedences into a list and then running the algorithm:
function parse(inputExpression, operators) {
// Strip out anything that isn't a number or a math operator
var tokens = inputExpression
.replace(/[^0-9+\-*\/()]/g, '')
.split(/([+\-*\/()])/)
.filter(function(x) { return x != ""; });
var outputQ = []; // push = push, pop = pop
var operStk = []; // enqueue = push, dequeue = shift
function top(stack) { return stack[stack.length-1]; }
while(tokens.length !== 0) {
var token = tokens.shift();
if(!isNaN(Number(token))) {
outputQ.push(token);
} else if (operators.hasOwnProperty(token)) {
while(operators[token] >= operators[top(operStk)])
outputQ.push(operStk.pop());
operStk.push(token);
} else if (token == '(') {
operStk.push(token);
} else if (token == ')') {
while(top(operStk) != '(' && operStk.length != 0)
outputQ.push(operStk.pop());
if(operStk.length == 0)
return null;
operStk.pop(); // Get rid of the l-paren
} else {
console.log("Bad token '" + token + "'");
return null;
}
}
while(operStk.length > 0)
outputQ.push(operStk.pop());
return outputQ;
}
function evaluate(parseResult) {
if (parseResult === null) return null;
var valueStack = [];
while(parseResult.length !== 0) {
var op = parseResult.shift();
if(!isNaN(Number(op)))
valueStack.push(Number(op));
else {
var val2 = valueStack.pop();
var val1 = valueStack.pop();
if(op == '+') {
valueStack.push(val1 + val2);
} else if(op == '-') {
valueStack.push(val1 - val2);
} else if(op == '*') {
valueStack.push(val1 * val2);
} else if(op == '/') {
valueStack.push(val1 / val2);
} else {
console.log("Invalid operator '" + op + "'");
return null;
}
}
}
if(valueStack.length !== 1) {
console.log("Invalid stack. Remaining: " + valueStack);
return null;
}
return valueStack.pop();
}
Usage:
var operators = {
"+": 3,
"-": 2,
"*": 1,
"/": 4
};
evaluate(parse("40 - 9 / 2 + 3", operators)); // == 6.2
Note that this will not handle unary minus, because it's difficult to do with Shunting-yard, and because you aren't asking for a precedence for it. On the bright side, it does support parenthesis.
Hope this helps!
Alex
A call to update a record in an early-bound custom entity, the record already retrieved, returns the above message. Code fragment follows (heavily instrumented but I'll leave everything in as I'm new to CRM code and might remove someting relevant)
// retrieve and update ContactFact record -
string strcontactFactId = reader["contactFactId"] == DBNull.Value ? string.Empty : (string)reader["contactFactId"];
string strcontactId = reader["contactId"] == DBNull.Value ? string.Empty : (string)reader["contactId"];
whereAmI = "retrieved strcontactId = " + strcontactId;
string straccountId = reader["accountId"] == DBNull.Value ? string.Empty : (string)reader["accountId"];
Guid contactFactId;
Guid contactId;
Guid accountId;
Guid.TryParse(strcontactFactId, out contactFactId);
whereAmI = "try to generate contactId from " + strcontactId;
Guid.TryParse(strcontactId, out contactId);
whereAmI = "generated contactId = " + contactId.ToString();
Guid.TryParse(straccountId, out accountId);
int score = reader.GetInt32(3);
whereAmI = "try to retrieve contactFact " + contactFactId.ToString();
sbtls_contactfact contactFact = (sbtls_contactfact)service.Retrieve("sbtls_contactfact", contactFactId, columnSet); // xx prob don't need to retrieve current values
whereAmI = "try to set sbtls_ContactId to " + contactId.ToString();
contactFact.sbtls_ContactId = contactId == Guid.Empty ? null : new EntityReference("Contact", contactId);
whereAmI = "successfully set sbtls_ContactId to " + contactId.ToString();
contactFact.sbtls_AccountId = accountId == Guid.Empty ? null : new EntityReference("Account", accountId);
contactFact.sbtls_Score = score;
contactFact.sbtls_Fact = "Updated with contactId " + strcontactId + " parsed to GUID " + contactId.ToString();
whereAmI = "about to update";
service.Update(contactFact);
whereAmI = "updated";
You need to change these two line:
contactFact.sbtls_ContactId = contactId == Guid.Empty ? null : new EntityReference("Contact", contactId);
contactFact.sbtls_AccountId = accountId == Guid.Empty ? null : new EntityReference("Account", accountId);
To use lowercase for the EntityReference.LogicalName values, so it reads like:
contactFact.sbtls_ContactId = contactId == Guid.Empty ? null : new EntityReference("contact", contactId);
contactFact.sbtls_AccountId = accountId == Guid.Empty ? null : new EntityReference("account", accountId);
The logical name is case sensitive and should be lower-case.