Last active
August 29, 2015 14:07
-
-
Save EbiseLutica/e1e4563843aa23e99859 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace Calc | |
{ | |
class PolaScript | |
{ | |
static string[] token; | |
static int ix; | |
static Dictionary<string, Constant> varlist = new Dictionary<string, Constant>(); | |
//3 + 2 | |
static void Main(string[] args) | |
{ | |
while (true) | |
{ | |
string dat = Console.ReadLine(); | |
//Console.WriteLine(ArrayToString(GetTokenByExpression(dat))); | |
try | |
{ | |
string[] hoge = GetTokenByExpression(dat); | |
Console.WriteLine("Tokens: {0}", string.Join(", ", hoge)); | |
Expression(hoge); | |
ConstExpression(hoge); | |
} | |
catch (Exception ex) | |
{ | |
System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(ex, true); | |
Console.Error.WriteLine("[ERROR!!!: {0}]{1}\r\nStackTrace: \r\n", ex.GetType().Name, ex.Message); | |
foreach (var frame in trace.GetFrames()) | |
{ | |
Console.Error.WriteLine("{0}, {1} : {2}", frame.GetFileLineNumber(), frame.GetFileColumnNumber(), frame.GetMethod()); | |
} | |
} | |
} | |
} | |
static string ArrayToString(object[] array) | |
{ | |
string lastdata = ""; | |
foreach (object val in array) | |
{ | |
lastdata += val.ToString() + " "; | |
} | |
return lastdata; | |
} | |
static string[] GetTokenByExpression(string source) | |
{ | |
TokenMode tm = TokenMode.Null; | |
string tmp = ""; | |
List<string> lastdata = new List<string>(); | |
bool isString = false; | |
int c = 0; | |
foreach (char chr in source) | |
{ | |
if (!isString) | |
{ | |
if (char.IsSeparator(chr)) | |
{ | |
if (tmp != "") | |
{ | |
lastdata.Add(tmp); | |
tmp = ""; | |
} | |
continue; | |
} | |
TokenMode nowtm = TokenMode.Null; | |
if (char.IsNumber(chr) || chr == '.' || chr == '\\') | |
nowtm = TokenMode.Value; | |
else if ( | |
chr == '+' || chr == '-' | |
|| chr == '*' || chr == '/' || chr == '%' | |
|| chr == '(' || chr == ')' | |
|| chr == '<' || chr == '>' || chr == '=' | |
|| chr == '&' || chr == '|' || chr == '^' | |
|| chr == '!' || chr == '~' | |
) | |
{ | |
nowtm = TokenMode.Operation; | |
} | |
if ((nowtm != tm || (nowtm == TokenMode.Operation && tm == TokenMode.Operation))) | |
{ | |
tm = nowtm; | |
if (tmp != "" && tmp + chr != "<=" && tmp + chr != ">=" && tmp + chr != "==" && tmp + chr != "!=" && tmp + chr != "<<" && tmp + chr != ">>" | |
&& tmp + chr != "+=" && tmp + chr != "-=" && tmp + chr != "*=" && tmp + chr != "/=" && tmp + chr != "&=" && tmp + chr != "|=" && tmp + chr != "^=") | |
{ | |
lastdata.Add(tmp); | |
tmp = ""; | |
} | |
} | |
} | |
if (c > 0) | |
{ | |
if (chr == '"' && (tmp + chr).Substring(tmp.Length - 2) != "\\\"") | |
isString = !isString; | |
} | |
else | |
if (chr == '"') | |
isString = !isString; | |
tmp += chr; | |
} | |
if (tmp != "") | |
{ | |
lastdata.Add(tmp); | |
} | |
return lastdata.ToArray(); | |
} | |
public enum TokenMode | |
{ | |
Null, Value, Operation | |
} | |
static Node CreateNode(string name) | |
{ | |
Node pnode = new Node(); | |
pnode.childs = new List<Node>(); | |
pnode.name = name; | |
return pnode; | |
} | |
static void AddChild(ref Node pnode, Node pchild) | |
{ | |
//if (pnode.name == "=" || pnode.name == "+=" || pnode.name == "-=" || pnode.name == "*=" || pnode.name == "/=" || pnode.name == "&=" || pnode.name == "|=" || pnode.name == "^=") | |
// pnode.childs.Insert(0, pchild); | |
//else | |
pnode.childs.Add(pchild); | |
} | |
static void AddTwoChildren(ref Node pnode, Node pchild1, Node pchild2) | |
{ | |
AddChild(ref pnode, pchild1); | |
AddChild(ref pnode, pchild2); | |
} | |
static Node SetAssignment() | |
{ | |
Node pleft = SetVariableDeclaration(); | |
while (ix < token.Length && (token[ix] == "=" || token[ix] == "+=" || token[ix] == "-=" || token[ix] == "*=" || token[ix] == "/=" || token[ix] == "&=" || token[ix] == "|=" || token[ix] == "^=")) | |
{ | |
Node pnode = CreateNode(token[ix++]); | |
Node pright = SetAssignment(); | |
AddTwoChildren(ref pnode, pleft, pright); | |
pleft = pnode; | |
} | |
return pleft; | |
} | |
static Node SetVariableDeclaration() | |
{ | |
Node pright = SetLogic(); | |
while (ix < token.Length && (token[ix] == "num" || token[ix] == "string" || token[ix] == "bool" || token[ix] == "Number" || token[ix] == "String" || token[ix] == "Boolean")) | |
{ | |
Node pleft = CreateNode(token[ix++]); | |
Node pnode = CreateNode("let"); | |
AddTwoChildren(ref pnode, pleft, pright); | |
pright = pnode; | |
} | |
return pright; | |
} | |
//3 + 2 - 5 | |
//( - ( + 3 2) 5 ) | |
//a = b = c | |
//( = a ( = b c) ) | |
static Node SetLogic() | |
{ | |
Node pleft = SetBit(); | |
while (ix < token.Length && (token[ix] == "<" || token[ix] == ">" || token[ix] == "==" || token[ix] == "!=" || token[ix] == "<=" || token[ix] == ">=")) | |
{ | |
Node pnode = CreateNode(token[ix++]); | |
Node pright = SetShift(); | |
AddTwoChildren(ref pnode, pleft, pright); | |
pleft = pnode; | |
} | |
return pleft; | |
} | |
static Node SetBit() | |
{ | |
Node pleft = SetShift(); | |
while (ix < token.Length && (token[ix] == "&" || token[ix] == "|" || token[ix] == "^")) | |
{ | |
Node pnode = CreateNode(token[ix++]); | |
Node pright = SetLogic(); | |
AddTwoChildren(ref pnode, pleft, pright); | |
pleft = pnode; | |
} | |
return pleft; | |
} | |
static Node SetShift() | |
{ | |
Node pleft = SetAddSub(); | |
while (ix < token.Length && (token[ix] == "<<" || token[ix] == ">>")) | |
{ | |
Node pnode = CreateNode(token[ix++]); | |
Node pright = SetAddSub(); | |
AddTwoChildren(ref pnode, pleft, pright); | |
pleft = pnode; | |
} | |
return pleft; | |
} | |
static Node SetAddSub() | |
{ | |
Node pleft = SetMulDiv(); | |
while(ix < token.Length && (token[ix] == "+" || token[ix] == "-")) | |
{ | |
Node pnode = CreateNode(token[ix++]); | |
Node pright = SetMulDiv(); | |
AddTwoChildren(ref pnode, pleft, pright); | |
pleft = pnode; | |
} | |
return pleft; | |
} | |
static Node SetMulDiv() | |
{ | |
Node pleft = SetFactor(); | |
while (ix < token.Length && (token[ix] == "*" || token[ix] == "/" || token[ix] == "%")) | |
{ | |
Node pnode = CreateNode(token[ix++]); | |
Node pright = SetFactor(); | |
AddTwoChildren(ref pnode, pleft, pright); | |
pleft = pnode; | |
} | |
return pleft; | |
} | |
static Node SetFactor() | |
{ | |
Node pnode; | |
if (token[ix] == "(") | |
{ | |
ix++; | |
pnode = SetBit(); | |
if (token[ix++] != ")") | |
throw new Exception("\")\"がありません。"); | |
} | |
else if (token[ix] == "-" || token[ix] == "~" || token[ix] == "!") | |
{ | |
pnode = CreateNode(token[ix++]); | |
Node pright = SetAssignment(); | |
AddChild(ref pnode, pright); | |
} | |
else | |
{ | |
string tmp = token[ix++]; | |
if (isNumber(tmp)) //数値 | |
{ | |
pnode = CreateNode("num"); | |
pnode.value = tmp; | |
} | |
else if (isVarName(tmp)) | |
{ | |
pnode = CreateNode("var"); | |
pnode.value = tmp; | |
} | |
else if (tmp[0] == '"' && tmp[tmp.Length - 1] == '"' && tmp.Length > 1) | |
{ | |
pnode = CreateNode("str"); | |
pnode.value = tmp.Substring(1, tmp.Length - 2); | |
} | |
else if (tmp == "true" || tmp == "false") | |
{ | |
pnode = CreateNode("bool"); | |
pnode.value = tmp; | |
} | |
else | |
{ | |
throw new Exception(tmp + " を理解できません。"); | |
} | |
} | |
return pnode; | |
} | |
static bool isNumber(string val) | |
{ | |
double d; | |
return double.TryParse(val, out d); | |
} | |
static bool isVarName(string val) | |
{ | |
bool lastdata = true; | |
int cnt = 0; | |
foreach (char c in val) | |
{ | |
if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (cnt > 0 && c >= '0' && c <= '9') || (c == '_'))) | |
{ | |
lastdata = false; | |
} | |
cnt++; | |
} | |
return lastdata; | |
} | |
static string GetAST(Node node) | |
{ | |
StringBuilder lastdata = new StringBuilder(); | |
lastdata.AppendFormat(" ({0}", node.name); | |
for (int n = 0; n < node.childs.Count; n++) | |
{ | |
Node pchild = node.childs[n]; | |
if (pchild.name == "num" || pchild.name == "bool" || pchild.name == "var") | |
lastdata.AppendFormat(" {0} {1}", pchild.name, pchild.value); | |
else if (pchild.name == "str") | |
lastdata.AppendFormat(" {0} \"{1}\"", pchild.name, pchild.value); | |
else | |
lastdata.Append(GetAST(pchild)); | |
} | |
lastdata.Append(")"); | |
return lastdata.ToString(); | |
} | |
static void Expression(string[] intoken) | |
{ | |
foreach (string data in intoken) | |
Console.Write(data + " "); | |
Console.Write(" => "); | |
token = intoken; | |
ix = 0; | |
Node expr = SetAssignment(); | |
Console.Write(GetAST(expr)); | |
Console.WriteLine(); | |
} | |
static Constant Calc(Node pnode) | |
{ | |
if (pnode.name == "num") | |
return new Constant(Types.Number, double.Parse(pnode.value)); | |
else if (pnode.name == "str") | |
return new Constant(Types.String, pnode.value); | |
else if (pnode.name == "bool") | |
return new Constant(Types.Boolean, (pnode.value == "true" ? true : false)); | |
else if (pnode.name == "var") | |
return new Constant(Types.Variable, pnode.value); | |
Constant p1, p2; | |
Constant _p1, _p2; | |
switch (pnode.childs.Count) | |
{ | |
case 2: | |
p1 = Calc(pnode.childs[0]); | |
p2 = Calc(pnode.childs[1]); | |
if (p1.type == Types.Variable) | |
_p1 = varlist[(string)p1.value]; | |
else | |
_p1 = p1; | |
if (p2.type == Types.Variable) | |
_p2 = varlist[(string)p2.value]; | |
else | |
_p2 = p2; | |
if (pnode.name == "+") | |
{ | |
if (_p1.type == Types.String && _p2.type == Types.Number) | |
return new Constant(Types.String, (string)_p1.value + ((double)_p2.value).ToString()); | |
else if (_p1.type == Types.String && _p2.type == Types.String) | |
return new Constant(Types.String, (string)_p1.value + (string)_p2.value); | |
else if (_p1.type == Types.Number && _p2.type == Types.Number) | |
return new Constant(Types.Number, (double)_p1.value + (double)_p2.value); | |
else | |
throw new Exception(string.Format("{0} 演算子を、 {1} 型と {2} 型の演算に使用できません。", pnode.name, _p1.type, _p2.type)); | |
} | |
else if (pnode.name == "-") | |
return new Constant(Types.Number, (double)_p1.value - (double)_p2.value); | |
else if (pnode.name == "*") | |
{ | |
if (_p1.type == Types.String && _p2.type == Types.Number) | |
return new Constant(Types.String, (RepeatString((string)_p1.value, (int)(double)_p2.value))); | |
return new Constant(Types.Number, (double)_p1.value * (double)_p2.value); | |
} | |
else if (pnode.name == "/") | |
return new Constant(Types.Number, (double)_p1.value / (double)_p2.value); | |
else if (pnode.name == "%") | |
return new Constant(Types.Number, (double)_p1.value % (double)_p2.value); | |
else if (pnode.name == "<") | |
return new Constant(Types.Boolean, ((double)_p1.value < (double)_p2.value) ? true : false); | |
else if (pnode.name == ">") | |
return new Constant(Types.Boolean, ((double)_p1.value > (double)_p2.value) ? true : false); | |
else if (pnode.name == "<=") | |
return new Constant(Types.Boolean, ((double)_p1.value <= (double)_p2.value) ? true : false); | |
else if (pnode.name == ">=") | |
return new Constant(Types.Boolean, ((double)_p1.value >= (double)_p2.value) ? true : false); | |
else if (pnode.name == "==") | |
return new Constant(Types.Boolean, ((double)_p1.value == (double)_p2.value) ? true : false); | |
else if (pnode.name == "!=") | |
return new Constant(Types.Boolean, ((double)_p1.value != (double)_p2.value) ? true : false); | |
else if (pnode.name == "<<") | |
try | |
{ | |
if ((double)_p1.value - (int)_p1.value != 0 || (double)_p2.value - (int)_p2.value != 0) | |
throw new Exception(" << 演算は、整数のみ行うことができます。"); | |
else | |
return new Constant(Types.Number, (int)_p1.value << (int)_p2.value); | |
} | |
catch(InvalidCastException) | |
{ | |
throw new Exception(string.Format("{0} 演算子を、 {1} 型と {2} 型の演算に使用できません。", pnode.name, _p1.type, _p2.type)); | |
} | |
else if (pnode.name == ">>") | |
if ((double)_p1.value - (int)_p1.value != 0 || (double)_p2.value - (int)_p2.value != 0) | |
throw new Exception(" >> 演算は、整数のみ行うことができます。"); | |
else | |
return new Constant(Types.Number, (int)_p1.value >> (int)_p2.value); | |
else if (pnode.name == "&") | |
if ((double)_p1.value - (int)_p1.value != 0 || (double)_p2.value - (int)_p2.value != 0) | |
throw new Exception(" & 演算は、整数のみ行うことができます。"); | |
else | |
return new Constant(Types.Number, (int)_p1.value & (int)_p2.value); | |
else if (pnode.name == "|") | |
if ((double)_p1.value - (int)_p1.value != 0 || (double)_p2.value - (int)_p2.value != 0) | |
throw new Exception(" | 演算は、整数のみ行うことができます。"); | |
else | |
return new Constant(Types.Number, (int)_p1.value | (int)_p2.value); | |
else if (pnode.name == "^") | |
if ((double)_p1.value - (int)_p1.value != 0 || (double)_p2.value - (int)_p2.value != 0) | |
throw new Exception(" ^ 演算は、整数のみ行うことができます。"); | |
else | |
return new Constant(Types.Number, (int)_p1.value ^ (int)_p2.value); | |
else if (pnode.name == "=") | |
{ | |
if (p1.type == Types.Variable) | |
{ | |
CheckVarExists(p1); | |
varlist[(string)p1.value] = new Constant(_p2.type, _p2.value); | |
} | |
} | |
else if (pnode.name == "+=") | |
{ | |
//複合代入演算子に、変数が参照できないときの処理を追加する(キーが存在しないとき) | |
CheckVarExists(p1); | |
if (varlist[(string)p1.value].type == Types.String && _p2.type == Types.String) | |
varlist[(string)p1.value] = new Constant(_p2.type, (string)(varlist[(string)p1.value].value) + (string)_p2.value); | |
else if (varlist[(string)p1.value].type == Types.String && _p2.type == Types.Number) | |
varlist[(string)p1.value] = new Constant(varlist[(string)p1.value].type, (string)(varlist[(string)p1.value].value) + (double)_p2.value); | |
else if (varlist[(string)p1.value].type == Types.Number && _p2.type == Types.Number) | |
varlist[(string)p1.value] = new Constant(_p2.type, (double)(varlist[(string)p1.value].value) + (double)_p2.value); | |
else | |
throw new Exception(string.Format("{0} 演算子を、 {1} 型と {2} 型の演算に使用できません。", pnode.name, _p1.type, _p2.type)); | |
} | |
else if (pnode.name == "-=") | |
{ | |
CheckVarExists(p1); | |
if (varlist[(string)p1.value].type == Types.Number && p2.type == Types.Number) | |
varlist[(string)p1.value] = new Constant(p2.type, (double)(varlist[(string)p1.value].value) - (double)p2.value); | |
else | |
throw new Exception(string.Format("{0} 演算子を、 {1} 型と {2} 型の演算に使用できません。", pnode.name, p1.type, p2.type)); | |
} | |
else if (pnode.name == "*=") | |
{ | |
CheckVarExists(p1); | |
if (varlist[(string)p1.value].type == Types.Number && p2.type == Types.Number) | |
varlist[(string)p1.value] = new Constant(p2.type, (double)(varlist[(string)p1.value].value) + (double)p2.value); | |
else if (varlist[(string)p1.value].type == Types.String && p2.type == Types.Number) | |
varlist[(string)p1.value] = new Constant(varlist[(string)p1.value].type, RepeatString((string)(varlist[(string)p1.value].value), (int)p2.value)); | |
else | |
throw new Exception(string.Format("{0} 演算子を、 {1} 型と {2} 型の演算に使用できません。", pnode.name, p1.type, p2.type)); | |
} | |
break; | |
case 1: | |
p1 = Calc(pnode.childs[0]); | |
if (p1.type == Types.Variable) | |
_p1 = varlist[(string)p1.value]; | |
else | |
_p1 = p1; | |
if (pnode.name == "-") | |
return new Constant(Types.Number, -(double)p1.value); | |
else if (pnode.name == "~") | |
if ((double)p1.value - (int)p1.value != 0) | |
throw new Exception("~ 演算は、整数のみ行うことができます。"); | |
else | |
return new Constant(Types.Number, ~(int)p1.value); | |
else if (pnode.name == "!") | |
{ | |
if ((double)p1.value != 0) | |
return new Constant(Types.Number, 1); | |
else | |
return new Constant(Types.Number, 0); | |
} | |
break; | |
default: | |
throw new Exception(string.Format("{0}つの項をとる演算子はありません。", pnode.childs.Count)); | |
} | |
throw new Exception(string.Format("{0}つの項をとる、\"{1}\" 演算子はありません。", pnode.childs.Count, pnode.name)); | |
} | |
public static string RepeatString(string s, int count) | |
{ | |
System.Text.StringBuilder buf = | |
new System.Text.StringBuilder(s.Length * count); | |
for (int i = 0; i < count; i++) | |
{ | |
buf.Append(s); | |
} | |
return buf.ToString(); | |
} | |
static void ConstExpression(string[] intoken) | |
{ | |
token = intoken; | |
ix = 0; | |
Node expr = SetAssignment(); | |
Constant cst = Calc(expr); | |
Console.WriteLine(cst.type + " " + cst.value); | |
} | |
/// <summary> | |
/// 指定した Constant が変数を表すか、表すならその変数が存在するかどうか確認し、問題があれば例外を投げます。 | |
/// </summary> | |
/// <param name="vardata">チェックする Constant 構造体。</param> | |
static void CheckVarExists(Constant vardata) | |
{ | |
if (vardata.type != Types.Variable) | |
throw new Exception("左辺は変数を表しません。"); | |
if (!varlist.ContainsKey((string)vardata.value)) | |
throw new Exception(string.Format("変数 {0} は宣言されていません。", (string)vardata.value)); | |
} | |
} | |
struct Node | |
{ | |
public string name; | |
public List<Node> childs; | |
public string value; | |
} | |
/*struct Variable | |
{ | |
//public Types type; | |
public string name; | |
public Constant value; | |
public void SetValue(string nm, Constant con) | |
{ | |
name = nm; | |
if (this.value.type == con.type || this.value.type == Types.Null) | |
{ | |
this.value.type = con.type; | |
this.value = con; | |
} | |
else | |
{ | |
if (this.value.type == Types.Number && con.type == Types.String) | |
{ | |
con.value = double.Parse((string)con.value); | |
} | |
else if (this.value.type == Types.String && con.type == Types.Number) | |
{ | |
con.value = ((int)con.value).ToString(); | |
} | |
else | |
throw new Exception(string.Format("{0} から {1} への変換はできません。", con.type.ToString(), this.value.type.ToString())); | |
} | |
} | |
}*/ | |
struct Constant | |
{ | |
public Types type; | |
public object value; | |
public Constant(Types t, object val) | |
{ | |
type = t; | |
switch (t) | |
{ | |
case Types.Number: | |
if (!(val is double || val is int)) | |
throw new Exception("型が一致しません。"); | |
break; | |
case Types.Boolean: | |
if (!(val is bool)) | |
throw new Exception("型が一致しません。"); | |
break; | |
case Types.String: | |
if (!(val is string)) | |
throw new Exception("型が一致しません。"); | |
break; | |
} | |
value = val; | |
} | |
} | |
enum Types | |
{ | |
Null, Number, String, Boolean, Variable | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment