ifpscc: initial commit
libifpscc: initial commit readme: document ifpscc/libifpscc license: add credits for ifpscc/libifpscc (derived from code also MIT licensed) libifps: make additional fields/types public for libifpscc libifps: fix field documentation for some opcodes libifps: fix loading functions that are not exported libifps: allow saving a nonexistant primitive type if the same primitive type was added already libifps: fix parsing Extended constants libifps: fix ushort/short being mapped to the wrong types in one table csproj: set Prefer32Bit=false for release builds
@ -7,7 +7,7 @@ namespace IFPSLib.Emit
|
||||
/// <summary>
|
||||
/// Type of a PascalScript operand at the bytecode level.
|
||||
/// </summary>
|
||||
internal enum BytecodeOperandType : byte
|
||||
public enum BytecodeOperandType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Operand refers to a local or global variable, or an argument.
|
||||
|
@ -147,8 +147,8 @@ namespace IFPSLib.Emit
|
||||
"dll(\"{0}\",\"{1}\"{2}{3}) {4}",
|
||||
DllName.Replace("\"", "\\\""),
|
||||
ProcedureName.Replace("\"", "\\\""),
|
||||
DelayLoad ? "delayload, " : "",
|
||||
LoadWithAlteredSearchPath ? "alteredsearchpath" : "",
|
||||
DelayLoad ? ", delayload" : "",
|
||||
LoadWithAlteredSearchPath ? ", alteredsearchpath" : "",
|
||||
base.ToString()
|
||||
);
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ namespace IFPSLib.Emit
|
||||
/// <param name="operands">Operands</param>
|
||||
internal Instruction(OpCode opcode, List<Operand> operands) : this(opcode)
|
||||
{
|
||||
if (operands.Any((op) => op == null)) throw new ArgumentNullException("operand");
|
||||
m_Operands = operands;
|
||||
}
|
||||
|
||||
@ -180,6 +181,16 @@ namespace IFPSLib.Emit
|
||||
public static Instruction Create<TType>(OpCode opcode, IVariable op0, TType val)
|
||||
=> Create(opcode, Operand.Create(op0), Operand.Create(val));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instruction with two variable operands
|
||||
/// </summary>
|
||||
/// <param name="opcode">Opcode</param>
|
||||
/// <param name="op0">First operand</param>
|
||||
/// <param name="op1">Second operand</param>
|
||||
/// <returns>New instruction</returns>
|
||||
public static Instruction Create(OpCode opcode, IVariable op0, Operand op1)
|
||||
=> Create(opcode, Operand.Create(op0), op1);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new branch instruction with an operand
|
||||
/// </summary>
|
||||
|
@ -80,7 +80,7 @@ namespace IFPSLib.Emit
|
||||
/// </summary>
|
||||
public static readonly OpCode Mod = new OpCode("mod", Code.Mod, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro);
|
||||
/// <summary>
|
||||
/// Shifts the first value left by the second value; <c>op0 <<= op1</c>
|
||||
/// Shifts the first value left by the second value; <c>op0 <<= op1</c>
|
||||
/// </summary>
|
||||
public static readonly OpCode Shl = new OpCode("shl", Code.Shl, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro);
|
||||
/// <summary>
|
||||
@ -88,7 +88,7 @@ namespace IFPSLib.Emit
|
||||
/// </summary>
|
||||
public static readonly OpCode Shr = new OpCode("shr", Code.Shr, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro);
|
||||
/// <summary>
|
||||
/// Bitwise ANDs the first value by the second value; <c>op0 &= op1</c>
|
||||
/// Bitwise ANDs the first value by the second value; <c>op0 &= op1</c>
|
||||
/// </summary>
|
||||
public static readonly OpCode And = new OpCode("and", Code.And, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro);
|
||||
/// <summary>
|
||||
@ -194,7 +194,7 @@ namespace IFPSLib.Emit
|
||||
/// </summary>
|
||||
public static readonly OpCode CallVar = new OpCode("callvar", Code.CallVar, OperandType.InlineValue, FlowControl.Call, OpCodeType.Primitive, StackBehaviour.Push1);
|
||||
/// <summary>
|
||||
/// op0 must be a pointer.<br/>
|
||||
/// op0 must be a pointer, which may be a null pointer.<br/>
|
||||
/// If op1 is not a pointer, loads the equivalent address of op1 into op0; <c>op0 = &op1</c><br/>
|
||||
/// If op1 is a pointer, loads op1 into op0; <c>op0 = op1</c>
|
||||
/// </summary>
|
||||
@ -253,7 +253,11 @@ namespace IFPSLib.Emit
|
||||
/// </summary>
|
||||
public static readonly OpCode Not = new OpCode("not", Code.Not, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive);
|
||||
/// <summary>
|
||||
/// <c>*op0 = *op1</c>
|
||||
/// Copy constructor.<br/>
|
||||
/// Operand 0 must be a pointer, which may be a null pointer.<br/>
|
||||
/// First, <c>op0</c> is set to newly constructed pointer of <c>op1</c>'s type (if <c>op1</c> is a pointer, then <c>*op1</c>'s type).<br/>
|
||||
/// If Operand 1 is a pointer, <c>*op0 = *op1</c><br/>
|
||||
/// Otherwise, <c>*op0 = op1</c>
|
||||
/// </summary>
|
||||
public static readonly OpCode Cpval = new OpCode("cpval", Code.Cpval, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Primitive);
|
||||
/// <summary>
|
||||
|
@ -41,6 +41,8 @@ namespace IFPSLib.Emit
|
||||
internal BytecodeOperandType m_Type;
|
||||
private Value m_Value;
|
||||
|
||||
public BytecodeOperandType Type => m_Type;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private T EnsureType<T>(ref T value, BytecodeOperandType type)
|
||||
{
|
||||
@ -75,12 +77,14 @@ namespace IFPSLib.Emit
|
||||
|
||||
public Operand(IVariable var)
|
||||
{
|
||||
if (var == null) throw new ArgumentNullException(nameof(var));
|
||||
m_Type = BytecodeOperandType.Variable;
|
||||
m_Value.Variable = var;
|
||||
}
|
||||
|
||||
public Operand(IVariable arr, uint immIdx)
|
||||
{
|
||||
if (arr == null) throw new ArgumentNullException(nameof(arr));
|
||||
m_Type = BytecodeOperandType.IndexedImmediate;
|
||||
m_Value.Indexed.Variable = arr;
|
||||
m_Value.Indexed.Index.m_Immediate = new StrongBox<uint>(immIdx);
|
||||
@ -88,11 +92,19 @@ namespace IFPSLib.Emit
|
||||
|
||||
public Operand(IVariable arr, IVariable varIdx)
|
||||
{
|
||||
if (arr == null) throw new ArgumentNullException(nameof(arr));
|
||||
if (varIdx == null) throw new ArgumentNullException(nameof(varIdx));
|
||||
m_Type = BytecodeOperandType.IndexedVariable;
|
||||
m_Value.Indexed.Variable = arr;
|
||||
m_Value.Indexed.Index.Variable = varIdx;
|
||||
}
|
||||
|
||||
public Operand(Operand op)
|
||||
{
|
||||
m_Type = op.m_Type;
|
||||
m_Value = op.m_Value;
|
||||
}
|
||||
|
||||
public static Operand Create<TType>(Types.PrimitiveType type, TType value)
|
||||
{
|
||||
return new Operand(TypedData.Create(type, value));
|
||||
@ -100,20 +112,31 @@ namespace IFPSLib.Emit
|
||||
|
||||
public static Operand Create<TType>(TType value)
|
||||
{
|
||||
// this is supposed to be for only primitives, but derived interfaces can also lead here.
|
||||
// check for those derived interfaces.
|
||||
switch (value)
|
||||
{
|
||||
case Types.IType type:
|
||||
return Create(type);
|
||||
case IFunction fn:
|
||||
return Create(fn);
|
||||
case IVariable var:
|
||||
return Create(var);
|
||||
}
|
||||
return new Operand(TypedData.Create(value));
|
||||
}
|
||||
|
||||
internal static Operand Create(Types.IType value)
|
||||
public static Operand Create(Types.IType value)
|
||||
{
|
||||
return new Operand(TypedData.Create(value));
|
||||
}
|
||||
|
||||
internal static Operand Create(Instruction value)
|
||||
public static Operand Create(Instruction value)
|
||||
{
|
||||
return new Operand(TypedData.Create(value));
|
||||
}
|
||||
|
||||
internal static Operand Create(IFunction value)
|
||||
public static Operand Create(IFunction value)
|
||||
{
|
||||
return new Operand(TypedData.Create(value));
|
||||
}
|
||||
|
@ -48,7 +48,8 @@ namespace IFPSLib.Emit
|
||||
|
||||
// Next are arguments. First char is the argument type ('@' means in, otherwise '!'), followed by the type index
|
||||
ret.Arguments = new List<FunctionArgument>(decl.Length - 1);
|
||||
for (int i = 1; i < decl.Length; i++) {
|
||||
for (int i = 1; i < decl.Length; i++)
|
||||
{
|
||||
var arg = new FunctionArgument();
|
||||
arg.ArgumentType = (decl[i][0] == ARGUMENT_TYPE_IN ? FunctionArgumentType.In : FunctionArgumentType.Out);
|
||||
if (!int.TryParse(decl[i].Substring(1), out var idxType) || idxType < 0) arg.Type = null;
|
||||
@ -57,6 +58,11 @@ namespace IFPSLib.Emit
|
||||
ret.Arguments.Add(arg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.Name = string.Format("func_{0:x}", ret.CodeOffset);
|
||||
ret.Arguments = new List<FunctionArgument>();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -160,6 +160,7 @@ namespace IFPSLib
|
||||
internal class SaveContext
|
||||
{
|
||||
internal readonly Dictionary<IType, int> tblTypes;
|
||||
internal readonly Dictionary<PascalTypeCode, int> tblPrimitiveTypes;
|
||||
internal readonly Dictionary<IFunction, int> tblFunctions;
|
||||
internal readonly Dictionary<GlobalVariable, int> tblGlobals;
|
||||
internal readonly Dictionary<IFunction, long> tblFunctionOffsets;
|
||||
@ -168,13 +169,19 @@ namespace IFPSLib
|
||||
internal SaveContext(Script script)
|
||||
{
|
||||
tblTypes = new Dictionary<IType, int>();
|
||||
tblPrimitiveTypes = new Dictionary<PascalTypeCode, int>();
|
||||
tblFunctions = new Dictionary<IFunction, int>();
|
||||
tblGlobals = new Dictionary<GlobalVariable, int>();
|
||||
tblFunctionOffsets = new Dictionary<IFunction, long>();
|
||||
|
||||
FileVersion = script.FileVersion;
|
||||
|
||||
for (int i = 0; i < script.Types.Count; i++) tblTypes.Add(script.Types[i], i);
|
||||
for (int i = 0; i < script.Types.Count; i++)
|
||||
{
|
||||
var type = script.Types[i];
|
||||
tblTypes.Add(type, i);
|
||||
if (type is PrimitiveType && !tblPrimitiveTypes.ContainsKey(type.BaseType)) tblPrimitiveTypes.Add(type.BaseType, i);
|
||||
}
|
||||
for (int i = 0; i < script.Functions.Count; i++) tblFunctions.Add(script.Functions[i], i);
|
||||
for (int i = 0; i < script.GlobalVariables.Count; i++) tblGlobals.Add(script.GlobalVariables[i], i);
|
||||
}
|
||||
@ -185,11 +192,15 @@ namespace IFPSLib
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(string.Format("Used an unknown type"));
|
||||
}
|
||||
if (!tblTypes.TryGetValue(type, out var idx))
|
||||
if (tblTypes.TryGetValue(type, out var idx)) return idx;
|
||||
|
||||
// For a primitive type, check the primitives table too.
|
||||
if (type is PrimitiveType)
|
||||
{
|
||||
throw new KeyNotFoundException(string.Format("Used unreferenced type {0}, make sure it's added to the Types list.", type.Name));
|
||||
if (tblPrimitiveTypes.TryGetValue(type.BaseType, out idx)) return idx;
|
||||
}
|
||||
return idx;
|
||||
|
||||
throw new KeyNotFoundException(string.Format("Used unreferenced type {0}, make sure it's added to the Types list.", type.Name));
|
||||
}
|
||||
|
||||
internal int GetFunctionIndex(IFunction function)
|
||||
|
@ -64,6 +64,35 @@ namespace IFPSLib
|
||||
return (TType)Value;
|
||||
}
|
||||
|
||||
private static void TrimDecimalString(StringBuilder sb)
|
||||
{
|
||||
// find the "e" looking from the end
|
||||
int idx = sb.Length;
|
||||
for (int i = sb.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (sb[i] == 'e')
|
||||
{
|
||||
idx = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// remove while character is '0'
|
||||
int length = 0;
|
||||
while (idx >= 0 && sb[idx] == '0')
|
||||
{
|
||||
length++;
|
||||
idx--;
|
||||
}
|
||||
if (sb[idx] == '.') // need at least one zero
|
||||
{
|
||||
length--;
|
||||
idx++;
|
||||
}
|
||||
if (length == 0) return;
|
||||
sb.Remove(idx + 1, length);
|
||||
}
|
||||
|
||||
internal static TypedData Load(BinaryReader br, Script script)
|
||||
{
|
||||
var idxType = br.Read<uint>();
|
||||
@ -98,8 +127,13 @@ namespace IFPSLib
|
||||
case PascalTypeCode.Double:
|
||||
return new TypedData(type, br.Read<double>());
|
||||
case PascalTypeCode.Extended:
|
||||
// BUGBUG: there must be something beter than this... but for now, it'll do
|
||||
return new TypedData(type, decimal.Parse(br.Read<ExtF80>().ToString()));
|
||||
{
|
||||
// BUGBUG: there must be something beter than this... but for now, it'll do
|
||||
var sb = new StringBuilder();
|
||||
ExtF80.PrintFloat80(sb, br.Read<ExtF80>(), PrintFloatFormat.ScientificFormat, 19);
|
||||
TrimDecimalString(sb);
|
||||
return new TypedData(type, decimal.Parse(sb.ToString(), System.Globalization.NumberStyles.Float));
|
||||
}
|
||||
|
||||
case PascalTypeCode.Currency:
|
||||
return new TypedData(type, new CurrencyWrapper(decimal.FromOACurrency(br.Read<long>())));
|
||||
|
@ -60,8 +60,8 @@ namespace IFPSLib.Types
|
||||
{
|
||||
{ typeof(byte), PascalTypeCode.U8 },
|
||||
{ typeof(sbyte), PascalTypeCode.S8 },
|
||||
{ typeof(ushort), PascalTypeCode.S16 },
|
||||
{ typeof(short), PascalTypeCode.U16 },
|
||||
{ typeof(ushort), PascalTypeCode.U16 },
|
||||
{ typeof(short), PascalTypeCode.S16 },
|
||||
{ typeof(uint), PascalTypeCode.U32 },
|
||||
{ typeof(int), PascalTypeCode.S32 },
|
||||
{ typeof(long), PascalTypeCode.S64 },
|
||||
|
@ -11,12 +11,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ifpsdasm", "ifpsdasm\ifpsda
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ifpsasm", "ifpsasm\ifpsasm.csproj", "{8C1A1532-1A40-46DB-BBDF-B4489BD0F966}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFPSAsmLib", "IFPSAsmLib\IFPSAsmLib.csproj", "{63B7741E-D712-4277-B345-F5B77AC80EB1}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFPSAsmLib", "IFPSAsmLib\IFPSAsmLib.csproj", "{63B7741E-D712-4277-B345-F5B77AC80EB1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "uninno", "uninno\uninno.csproj", "{5092701D-8D7E-4AB4-9877-DADCE1266B3D}"
|
||||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "LoadSaveExtensions", "LoadSaveExtensions\LoadSaveExtensions.shproj", "{AF654578-B67D-4E5E-9CA6-E8AED9D0051A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibIFPSCC", "LibIFPSCC\LibIFPSCC.csproj", "{B56F2451-B1FF-4103-A2BC-0C61463D6971}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ifpscc", "ifpscc\ifpscc.csproj", "{8B953068-8B67-4D9A-802C-7657A7CAFC78}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -47,6 +51,14 @@ Global
|
||||
{5092701D-8D7E-4AB4-9877-DADCE1266B3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5092701D-8D7E-4AB4-9877-DADCE1266B3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5092701D-8D7E-4AB4-9877-DADCE1266B3D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B56F2451-B1FF-4103-A2BC-0C61463D6971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B56F2451-B1FF-4103-A2BC-0C61463D6971}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B56F2451-B1FF-4103-A2BC-0C61463D6971}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B56F2451-B1FF-4103-A2BC-0C61463D6971}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8B953068-8B67-4D9A-802C-7657A7CAFC78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8B953068-8B67-4D9A-802C-7657A7CAFC78}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8B953068-8B67-4D9A-802C-7657A7CAFC78}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8B953068-8B67-4D9A-802C-7657A7CAFC78}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -1,6 +1,7 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 zc
|
||||
ifpscc/libifpscc derived from code Copyright (c) 2015 Jonson Tan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
735
LibIFPSCC/ABT/BinaryOperators.cs
Normal file
@ -0,0 +1,735 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Resources;
|
||||
using AST;
|
||||
using CodeGeneration;
|
||||
using IFPSLib.Emit;
|
||||
|
||||
namespace ABT {
|
||||
|
||||
// BinaryOp [type = abstract]
|
||||
// |
|
||||
// +-- BinaryOpSupportingIntegralOperands [typeof(left) == typeof(right)]
|
||||
// | | ([abstract Operate{Long, ULong},
|
||||
// | | CGenIntegral)
|
||||
// | |
|
||||
// | +-- BinaryOpSupportingOnlyIntegralOperands [type = typeof(left)]
|
||||
// | | ([sealed] CGenValue = CGenIntegral)
|
||||
// | | (Modulo, Xor, BitwiseOr, BitwiseAnd, LShift, RShift)
|
||||
// | |
|
||||
// | +-- BinaryOpSupportingArithmeticOperands
|
||||
// | | (CGenValue { try float, then base.CGenValue })
|
||||
// | |
|
||||
// | +-- BinaryArithmeticOp [type = typeof(left)]
|
||||
// | | ([abstract] Operate{Long, ULong, Float, Double})
|
||||
// | | (Add, Sub, Mult, Div)
|
||||
// | |
|
||||
// | +-- BinaryComparisonOp [type = int]
|
||||
// | ([sealed] CGenValue,
|
||||
// | [sealed] Operate{Long, ULong, Float, Double},
|
||||
// | [abstract] Set{Long, ULong, Float, Double})
|
||||
// | (GEqual, Greater, LEqual, Less, Equal, NotEqual)
|
||||
// |
|
||||
// +-- BinaryLogicalOp [type = int]
|
||||
// ([abstract] CGenValue)
|
||||
// (LogicalAnd, LogicalOr)
|
||||
|
||||
public abstract partial class BinaryOp : Expr {
|
||||
protected BinaryOp(Expr left, Expr right) {
|
||||
this.Left = left;
|
||||
this.Right = right;
|
||||
}
|
||||
|
||||
public Expr Left { get; }
|
||||
|
||||
public Expr Right { get; }
|
||||
|
||||
public override abstract ExprType Type { get; }
|
||||
|
||||
public override sealed Env Env => this.Right.Env;
|
||||
|
||||
public override sealed Boolean IsLValue => false;
|
||||
}
|
||||
|
||||
public abstract partial class BinaryOpSupportingIntegralOperands : BinaryOp {
|
||||
protected BinaryOpSupportingIntegralOperands(Expr left, Expr right)
|
||||
: base(left, right) {
|
||||
if (!left.Type.EqualType(right.Type)) {
|
||||
throw new InvalidOperationException("Operand types mismatch.").Attach(left);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A binary integral operator only takes integral operands.
|
||||
///
|
||||
/// After semantic analysis, only two cases are possible:
|
||||
/// 1) long op long
|
||||
/// 2) ulong op ulong
|
||||
///
|
||||
/// The procedure is always:
|
||||
/// %eax = Left, %ebx = Right
|
||||
/// %eax = %eax op %ebx
|
||||
/// </summary>
|
||||
public abstract partial class BinaryOpSupportingOnlyIntegralOperands : BinaryOpSupportingIntegralOperands {
|
||||
protected BinaryOpSupportingOnlyIntegralOperands(Expr left, Expr right)
|
||||
: base(left, right) {
|
||||
if (!(left.Type is LongType || left.Type is ULongType)) {
|
||||
throw new InvalidOperationException("Only support long or ulong.").Attach(left);
|
||||
}
|
||||
this.Type = left.Type.GetQualifiedType(true, false);
|
||||
}
|
||||
|
||||
public override sealed ExprType Type { get; }
|
||||
}
|
||||
|
||||
public abstract partial class BinaryOpSupportingArithmeticOperands : BinaryOpSupportingIntegralOperands {
|
||||
protected BinaryOpSupportingArithmeticOperands(Expr left, Expr right)
|
||||
: base(left, right) {
|
||||
if (!(left.Type is LongType || left.Type is ULongType
|
||||
|| left.Type is FloatType || left.Type is DoubleType
|
||||
|| left.Type is AnsiStringType || left.Type is UnicodeStringType)) {
|
||||
throw new InvalidOperationException("Only support long, ulong, float, double, string.").Attach(left);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These operators perform usual arithmetic conversion.
|
||||
///
|
||||
/// After semantic analysis, only four cases are possible:
|
||||
/// 1) long op long
|
||||
/// 2) ulong op ulong
|
||||
/// 3) float op float
|
||||
/// 4) double op double
|
||||
///
|
||||
/// The procedure for long or ulong is the same as that of binary integral operators.
|
||||
/// The procedure for float and double is always:
|
||||
/// %st(0) = Left, %st(1) = Right
|
||||
/// %st(0) = %st(0) op %st(1), invalidate %st(1)
|
||||
/// </summary>
|
||||
public abstract partial class BinaryArithmeticOp : BinaryOpSupportingArithmeticOperands {
|
||||
protected BinaryArithmeticOp(Expr left, Expr right)
|
||||
: base(left, right) {
|
||||
this.Type = left.Type.GetQualifiedType(true, false);
|
||||
}
|
||||
|
||||
public override sealed ExprType Type { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binary arithmetic comparison operation.
|
||||
///
|
||||
/// After semantic analysis, only four cases are possible:
|
||||
/// 1) long op long
|
||||
/// 2) ulong op ulong
|
||||
/// 3) float op float
|
||||
/// 4) double op double
|
||||
///
|
||||
/// http://x86.renejeschke.de/html/file_module_x86_id_288.html
|
||||
/// </summary>
|
||||
public abstract partial class BinaryComparisonOp : BinaryOpSupportingArithmeticOperands {
|
||||
protected BinaryComparisonOp(Expr left, Expr right)
|
||||
: base(left, right)
|
||||
{
|
||||
this.Type = left.Type.GetQualifiedType(true, false);
|
||||
}
|
||||
public override sealed ExprType Type { get; }
|
||||
}
|
||||
|
||||
public abstract partial class BinaryLogicalOp : BinaryOp {
|
||||
protected BinaryLogicalOp(Expr left, Expr right)
|
||||
: base(left, right) {
|
||||
if (!(left.Type is LongType || left.Type is ULongType
|
||||
|| left.Type is FloatType || left.Type is DoubleType)) {
|
||||
throw new InvalidOperationException("Invalid operand type.").Attach(left);
|
||||
}
|
||||
if (!(right.Type is LongType || right.Type is ULongType
|
||||
|| right.Type is FloatType || right.Type is DoubleType)) {
|
||||
throw new InvalidOperationException("Invalid operand type.").Attach(right);
|
||||
}
|
||||
this.Type = left.Type.GetQualifiedType(true, false);
|
||||
}
|
||||
|
||||
public override sealed ExprType Type { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The modulo (%) operator can only take integral operands.
|
||||
///
|
||||
/// After semantic analysis, only two cases are possible:
|
||||
/// 1) long % long
|
||||
/// 2) ulong % ulong
|
||||
/// </summary>
|
||||
public sealed partial class Modulo : BinaryOpSupportingOnlyIntegralOperands {
|
||||
public Modulo(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The xor (^) operator can only take integral operands.
|
||||
///
|
||||
/// After semantic analysis, only two cases are possible:
|
||||
/// 1) long ^ long
|
||||
/// 2) ulong ^ ulong
|
||||
///
|
||||
/// https://msdn.microsoft.com/en-us/library/17zwb64t.aspx
|
||||
/// </summary>
|
||||
public sealed partial class Xor : BinaryOpSupportingOnlyIntegralOperands {
|
||||
public Xor(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bitwise or (|) operator can only take integral operands.
|
||||
///
|
||||
/// After semantic analysis, only two cases are possible:
|
||||
/// 1) long | long
|
||||
/// 2) ulong | ulong
|
||||
///
|
||||
/// https://msdn.microsoft.com/en-us/library/17zwb64t.aspx
|
||||
/// </summary>
|
||||
public sealed partial class BitwiseOr : BinaryOpSupportingOnlyIntegralOperands {
|
||||
public BitwiseOr(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bitwise and (&) operator can only take integral operands.
|
||||
///
|
||||
/// After semantic analysis, only two cases are possible:
|
||||
/// 1) long & long
|
||||
/// 2) ulong & ulong
|
||||
///
|
||||
/// https://msdn.microsoft.com/en-us/library/17zwb64t.aspx
|
||||
/// </summary>
|
||||
public sealed partial class BitwiseAnd : BinaryOpSupportingOnlyIntegralOperands {
|
||||
public BitwiseAnd(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The left shift operator can only take integral operands.
|
||||
/// Append 0's on the right.
|
||||
///
|
||||
/// After semantic analysis, only two cases are possible:
|
||||
/// 1) long %lt;%lt; long
|
||||
/// 2) ulong %lt;%lt; ulong
|
||||
/// </summary>
|
||||
public sealed partial class LShift : BinaryOpSupportingOnlyIntegralOperands {
|
||||
public LShift(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The right shift operator can only take integral operands.
|
||||
///
|
||||
/// After semantic analysis, only two cases are possible:
|
||||
/// 1) long >> long (arithmetic shift, append sign bit)
|
||||
/// 2) ulong >> ulong (logical shift, append 0)
|
||||
/// </summary>
|
||||
public sealed partial class RShift : BinaryOpSupportingOnlyIntegralOperands {
|
||||
public RShift(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The addition operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, pointer additions are converted into
|
||||
/// combinations of Type-casts and series of operations. So in AST,
|
||||
/// only four cases are possible:
|
||||
/// 1) long + long
|
||||
/// 2) ulong + ulong
|
||||
/// 3) float + float
|
||||
/// 4) double + double
|
||||
/// </summary>
|
||||
public sealed partial class Add : BinaryArithmeticOp {
|
||||
public Add(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The array index dereference operator for arrays of known or unknown length.
|
||||
/// Used at all times for unknown-length arrays, and when we know buffer overflow will not occur for arrays of known length.
|
||||
///
|
||||
/// Left operator MUST be array, right operator MUST be index (cast to u32).
|
||||
/// </summary>
|
||||
public sealed partial class ArrayIndexDeref : Expr {
|
||||
|
||||
public Expr Left { get; }
|
||||
|
||||
public Expr Right { get; }
|
||||
|
||||
public override sealed Env Env => this.Left.Env;
|
||||
|
||||
public override sealed Boolean IsLValue => Left.IsLValue;
|
||||
private static Expr CastIfNeeded(Expr expr)
|
||||
{
|
||||
if (!expr.Type.IsIntegral)
|
||||
{
|
||||
throw new InvalidOperationException("Expected array index to be integral.").Attach(expr);
|
||||
}
|
||||
return TypeCast.MakeCast(expr, new ABT.ULongType(expr.Type.IsConst, expr.Type.IsVolatile));
|
||||
}
|
||||
public ArrayIndexDeref(Expr left, Expr right)
|
||||
{
|
||||
if (!(left.Type is ArrayType || left.Type is IncompleteArrayType))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot dereference non-array type.").Attach(Left);
|
||||
}
|
||||
|
||||
Left = left;
|
||||
Right = CastIfNeeded(right);
|
||||
if (Type.Kind == ABT.ExprTypeKind.STRUCT_OR_UNION && !((ABT.StructOrUnionType)Type).IsComplete)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot dereference incomplete Type.").Attach(left);
|
||||
}
|
||||
}
|
||||
|
||||
private ExprType ElementType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Left.Type.Kind == ExprTypeKind.ARRAY) return ((ArrayType)Left.Type).ElemType;
|
||||
return ((IncompleteArrayType)Left.Type).ElemType;
|
||||
}
|
||||
}
|
||||
public override ExprType Type => ElementType;
|
||||
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
// get the last instruction first in case it emits
|
||||
var last = state.CurrInsns.LastOrDefault();
|
||||
var left = Left.CGenValue(state, null);
|
||||
|
||||
// if this is not a variable, we need to save it somewhere
|
||||
if (left.Type == BytecodeOperandType.Immediate) throw new InvalidProgramException().Attach(Left);
|
||||
if (left.Type == BytecodeOperandType.IndexedImmediate || left.Type == BytecodeOperandType.IndexedVariable)
|
||||
{
|
||||
// last instruction should be pushvar or setptr
|
||||
// if not, then pushvar it
|
||||
switch (last?.OpCode.Code)
|
||||
{
|
||||
case Code.PushVar:
|
||||
var pv = LocalVariable.Create(state.FunctionState.LocalsCount - 1);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, pv, left));
|
||||
left = Operand.Create(pv);
|
||||
break;
|
||||
case Code.SetPtr:
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, last.Operands[0], left));
|
||||
left = last.Operands[0];
|
||||
break;
|
||||
default:
|
||||
left = state.FunctionState.PushVar(left);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (left.Type != BytecodeOperandType.Variable)
|
||||
{
|
||||
// todo, not sure how to do this for now
|
||||
throw new InvalidOperationException().Attach(Left);
|
||||
}
|
||||
|
||||
Operand right = null;
|
||||
if (!Right.IsConstExpr)
|
||||
{
|
||||
// add a nop, we might need it
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Nop));
|
||||
// get the last instruction first in case it emits
|
||||
last = state.CurrInsns.LastOrDefault();
|
||||
right = Right.CGenValue(state, null);
|
||||
|
||||
// if this is not a variable, we need to save it somewhere
|
||||
if (right.Type == BytecodeOperandType.Immediate) throw new InvalidProgramException().Attach(Right);
|
||||
if (right.Type == BytecodeOperandType.IndexedImmediate || right.Type == BytecodeOperandType.IndexedVariable)
|
||||
{
|
||||
// last instruction should be pushvar or setptr
|
||||
// if not, then pushvar it
|
||||
switch (last?.OpCode.Code)
|
||||
{
|
||||
case Code.PushVar:
|
||||
var pv = LocalVariable.Create(state.FunctionState.LocalsCount - 1);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, pv, right));
|
||||
right = Operand.Create(pv);
|
||||
break;
|
||||
case Code.SetPtr:
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, last.Operands[0], right));
|
||||
right = last.Operands[0];
|
||||
break;
|
||||
default:
|
||||
right = state.FunctionState.PushVar(right);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (right.Type != BytecodeOperandType.Variable)
|
||||
{
|
||||
// todo, not sure how to do this for now
|
||||
throw new InvalidOperationException().Attach(Right);
|
||||
}
|
||||
}
|
||||
|
||||
// can't be a function designator.
|
||||
switch (this.Type.Kind)
|
||||
{
|
||||
case ExprTypeKind.ARRAY:
|
||||
case ExprTypeKind.INCOMPLETE_ARRAY:
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
case ExprTypeKind.CHAR:
|
||||
case ExprTypeKind.UCHAR:
|
||||
case ExprTypeKind.SHORT:
|
||||
case ExprTypeKind.USHORT:
|
||||
case ExprTypeKind.LONG:
|
||||
case ExprTypeKind.ULONG:
|
||||
case ExprTypeKind.S64:
|
||||
case ExprTypeKind.U64:
|
||||
case ExprTypeKind.FLOAT:
|
||||
case ExprTypeKind.DOUBLE:
|
||||
case ExprTypeKind.ANSI_STRING:
|
||||
case ExprTypeKind.UNICODE_STRING:
|
||||
case ExprTypeKind.COM_INTERFACE:
|
||||
case ExprTypeKind.COM_VARIANT:
|
||||
if (Right.IsConstExpr)
|
||||
{
|
||||
return Operand.Create(left.Variable, ((ConstULong)Right).Value);
|
||||
}
|
||||
return Operand.Create(left.Variable, right.Variable);
|
||||
|
||||
case ExprTypeKind.POINTER:
|
||||
var ptr = Type as PointerType;
|
||||
if (Right.IsConstExpr)
|
||||
{
|
||||
left = Operand.Create(left.Variable, ((ConstULong)Right).Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
left = Operand.Create(left.Variable, right.Variable);
|
||||
}
|
||||
if (!ptr.IsRef) return left;
|
||||
if (ptr.IsForOpenArray) return left;
|
||||
// array element, and the value is wanted, so we must cast to correct pointer type
|
||||
state.CGenPushStackSize();
|
||||
// we have u32 or u32*, we want (T*)ptr or *(T*)pptr
|
||||
var dummyForType = state.FunctionState.PushType(state.EmitType(ptr.RefType));
|
||||
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.PushVar(left);
|
||||
state.FunctionState.PushVar(dummyU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.CGenPopStackSize();
|
||||
state.FunctionState.PushVar(Operand.Create(state.PointerInitialiser));
|
||||
state.FunctionState.Push(dummyU32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CGenPopStackSize();
|
||||
var ptrInit = Operand.Create(state.PointerInitialiser, 0);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, left, ptrInit));
|
||||
return left;
|
||||
default:
|
||||
throw new InvalidProgramException().Attach(Left);
|
||||
}
|
||||
}
|
||||
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
// get the last instruction first in case it emits
|
||||
var last = state.CurrInsns.LastOrDefault();
|
||||
|
||||
var left = Left.CGenAddress(state, null);
|
||||
|
||||
// if this is not a variable, we need to save it somewhere
|
||||
if (left.Type == BytecodeOperandType.Immediate) throw new InvalidProgramException().Attach(Left);
|
||||
if (left.Type == BytecodeOperandType.IndexedImmediate || left.Type == BytecodeOperandType.IndexedVariable)
|
||||
{
|
||||
// last instruction should be pushvar or setptr
|
||||
// if not, then pushvar it
|
||||
switch (last?.OpCode.Code)
|
||||
{
|
||||
case Code.PushVar:
|
||||
var pv = LocalVariable.Create(state.FunctionState.LocalsCount - 1);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, pv, left));
|
||||
left = Operand.Create(pv);
|
||||
break;
|
||||
case Code.SetPtr:
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, last.Operands[0], left));
|
||||
left = last.Operands[0];
|
||||
break;
|
||||
default:
|
||||
left = state.FunctionState.PushVar(left);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (left.Type != BytecodeOperandType.Variable)
|
||||
{
|
||||
// todo, not sure how to do this for now
|
||||
throw new InvalidOperationException().Attach(Left);
|
||||
}
|
||||
|
||||
if (Right.IsConstExpr) return Operand.Create(left.Variable, ((ConstULong)Right).Value);
|
||||
|
||||
// add a nop, we might need it
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Nop));
|
||||
// get the last instruction first in case it emits
|
||||
last = state.CurrInsns.LastOrDefault();
|
||||
Operand right = Right.CGenValue(state, null);
|
||||
|
||||
// if this is not a variable, we need to save it somewhere
|
||||
if (right.Type == BytecodeOperandType.Immediate) throw new InvalidProgramException().Attach(Right);
|
||||
if (right.Type == BytecodeOperandType.IndexedImmediate || right.Type == BytecodeOperandType.IndexedVariable)
|
||||
{
|
||||
// last instruction should be pushvar or setptr
|
||||
// if not, then pushvar it
|
||||
switch (last?.OpCode.Code)
|
||||
{
|
||||
case Code.PushVar:
|
||||
var pv = LocalVariable.Create(state.FunctionState.LocalsCount - 1);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, pv, right));
|
||||
right = Operand.Create(pv);
|
||||
break;
|
||||
case Code.SetPtr:
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, last.Operands[0], right));
|
||||
right = last.Operands[0];
|
||||
break;
|
||||
default:
|
||||
right = state.FunctionState.PushVar(right);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (right.Type != BytecodeOperandType.Variable)
|
||||
{
|
||||
// todo, not sure how to do this for now
|
||||
throw new InvalidOperationException().Attach(Right);
|
||||
}
|
||||
return Operand.Create(left.Variable, right.Variable);
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
return Left.CallerNeedsToCleanStack(state, retLocKnown, forAddress) || Right.CallerNeedsToCleanStack(state, retLocKnown);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The subtraction operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, pointer subtractions are converted into
|
||||
/// combinations of Type-casts and series of operations. So in AST,
|
||||
/// only four cases are possible:
|
||||
/// 1) long - long
|
||||
/// 2) ulong - ulong
|
||||
/// 3) float - float
|
||||
/// 4) double - double
|
||||
/// </summary>
|
||||
public sealed partial class Sub : BinaryArithmeticOp {
|
||||
public Sub(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The multiplication (*) operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, only four cases are possible:
|
||||
/// 1) long * long
|
||||
/// 2) ulong * ulong
|
||||
/// 3) float * float
|
||||
/// 4) double * double
|
||||
/// </summary>
|
||||
public sealed partial class Multiply : BinaryArithmeticOp {
|
||||
public Multiply(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The division (/) operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, only four cases are possible:
|
||||
/// 1) long / long
|
||||
/// 2) ulong / ulong
|
||||
/// 3) float / float
|
||||
/// 4) double / double
|
||||
/// </summary>
|
||||
public sealed partial class Divide : BinaryArithmeticOp {
|
||||
public Divide(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The "greater than or equal to" operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, pointer comparisons are converted into
|
||||
/// integer comparisons. So in AST, only four cases are possible:
|
||||
/// 1) long >= long
|
||||
/// 2) ulong >= ulong
|
||||
/// 3) float >= float
|
||||
/// 4) double >= double
|
||||
///
|
||||
/// http://x86.renejeschke.de/html/file_module_x86_id_288.html
|
||||
/// </summary>
|
||||
public sealed partial class GEqual : BinaryComparisonOp {
|
||||
public GEqual(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The "greater than" operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, pointer comparisons are converted into
|
||||
/// integer comparisons. So in AST, only four cases are possible:
|
||||
/// 1) long > long
|
||||
/// 2) ulong > ulong
|
||||
/// 3) float > float
|
||||
/// 4) double > double
|
||||
///
|
||||
/// http://x86.renejeschke.de/html/file_module_x86_id_288.html
|
||||
/// </summary>
|
||||
public sealed partial class Greater : BinaryComparisonOp {
|
||||
public Greater(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The "less than or equal to" operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, pointer comparisons are converted into
|
||||
/// integer comparisons. So in AST, only four cases are possible:
|
||||
/// 1) long %lt;= long
|
||||
/// 2) ulong %lt;= ulong
|
||||
/// 3) float %lt;= float
|
||||
/// 4) double %lt;= double
|
||||
///
|
||||
/// http://x86.renejeschke.de/html/file_module_x86_id_288.html
|
||||
/// </summary>
|
||||
public sealed partial class LEqual : BinaryComparisonOp {
|
||||
public LEqual(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The "less than" operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, pointer comparisons are converted into
|
||||
/// integer comparisons. So in AST, only four cases are possible:
|
||||
/// 1) long %lt; long
|
||||
/// 2) ulong %lt; ulong
|
||||
/// 3) float %lt; float
|
||||
/// 4) double %lt; double
|
||||
///
|
||||
/// http://x86.renejeschke.de/html/file_module_x86_id_288.html
|
||||
/// </summary>
|
||||
public sealed partial class Less : BinaryComparisonOp {
|
||||
public Less(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The "equal to" operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, pointer comparisons are converted into
|
||||
/// integer comparisons. So in AST, only four cases are possible:
|
||||
/// 1) long == long
|
||||
/// 2) ulong == ulong
|
||||
/// 3) float == float
|
||||
/// 4) double == double
|
||||
///
|
||||
/// It's surprising that the C equal operator doesn't support structs and unions.
|
||||
/// http://x86.renejeschke.de/html/file_module_x86_id_288.html
|
||||
/// </summary>
|
||||
public sealed partial class Equal : BinaryComparisonOp {
|
||||
public Equal(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The "not equal to" operator can either take
|
||||
/// 1) integral- or 2) floating-Type operands.
|
||||
///
|
||||
/// After semantic analysis, pointer comparisons are converted into
|
||||
/// integer comparisons. So in AST, only four cases are possible:
|
||||
/// 1) long != long
|
||||
/// 2) ulong != ulong
|
||||
/// 3) float != float
|
||||
/// 4) double != double
|
||||
///
|
||||
/// It's surprising that the C equal operator doesn't support structs and unions.
|
||||
/// http://x86.renejeschke.de/html/file_module_x86_id_288.html
|
||||
/// </summary>
|
||||
public sealed partial class NotEqual : BinaryComparisonOp {
|
||||
public NotEqual(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Left && Right: can only take scalars (to compare with 0).
|
||||
///
|
||||
/// After semantic analysis, each operand can only be
|
||||
/// long, ulong, float, double.
|
||||
/// Pointers are casted to ulongs.
|
||||
///
|
||||
/// if Left == 0:
|
||||
/// return 0
|
||||
/// else:
|
||||
/// return Right != 0
|
||||
///
|
||||
/// Generate the assembly in this fashion,
|
||||
/// then every route would only have one jump.
|
||||
///
|
||||
/// +---------+ 0
|
||||
/// | cmp Left |-------+
|
||||
/// +---------+ |
|
||||
/// | |
|
||||
/// | 1 |
|
||||
/// | |
|
||||
/// +----+----+ 0 |
|
||||
/// | cmp Right |-------+
|
||||
/// +---------+ |
|
||||
/// | |
|
||||
/// | 1 |
|
||||
/// | |
|
||||
/// +----+----+ |
|
||||
/// | eax = 1 | |
|
||||
/// +---------+ |
|
||||
/// | |
|
||||
/// +---------+ |
|
||||
/// | |
|
||||
/// | +------------+ label_reset
|
||||
/// | |
|
||||
/// | +---------+
|
||||
/// | | eax = 0 |
|
||||
/// | +---------+
|
||||
/// | |
|
||||
/// +---------+ label_finish
|
||||
/// |
|
||||
///
|
||||
/// </summary>
|
||||
public sealed partial class LogicalAnd : BinaryLogicalOp {
|
||||
public LogicalAnd(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Left || Right: can only take scalars (to compare with 0).
|
||||
///
|
||||
/// After semantic analysis, each operand can only be
|
||||
/// long, ulong, float, double.
|
||||
/// Pointers are casted to ulongs.
|
||||
///
|
||||
/// if Left != 0:
|
||||
/// return 1
|
||||
/// else:
|
||||
/// return Right != 0
|
||||
/// </summary>
|
||||
public sealed partial class LogicalOr : BinaryLogicalOp {
|
||||
public LogicalOr(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
}
|
||||
}
|
201
LibIFPSCC/ABT/ConstExpressions.cs
Normal file
@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using CodeGeneration;
|
||||
using IFPSLib.Emit;
|
||||
|
||||
namespace ABT {
|
||||
|
||||
/// <summary>
|
||||
/// Compile-time constant. Cannot get the address.
|
||||
/// </summary>
|
||||
public abstract partial class ConstExpr : Expr {
|
||||
protected ConstExpr(Env env) {
|
||||
this.Env = env;
|
||||
}
|
||||
|
||||
public override sealed Env Env { get; }
|
||||
|
||||
public override sealed Boolean IsConstExpr => true;
|
||||
|
||||
public override sealed Boolean IsLValue => false;
|
||||
|
||||
public override sealed bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false) => false;
|
||||
}
|
||||
|
||||
public sealed partial class ConstLong : ConstExpr {
|
||||
public ConstLong(Int32 value, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
}
|
||||
public Int32 Value { get; }
|
||||
|
||||
public override String ToString() => $"{this.Value}";
|
||||
|
||||
private static ExprType _type = new LongType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstULong : ConstExpr {
|
||||
public ConstULong(UInt32 value, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
}
|
||||
public UInt32 Value { get; }
|
||||
|
||||
public override String ToString() => $"{this.Value}u";
|
||||
|
||||
private static ExprType _type = new ULongType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstS64 : ConstExpr
|
||||
{
|
||||
public ConstS64(long value, Env env)
|
||||
: base(env)
|
||||
{
|
||||
this.Value = value;
|
||||
}
|
||||
public long Value { get; }
|
||||
|
||||
public override String ToString() => $"{this.Value}ll";
|
||||
|
||||
private static ExprType _type = new S64Type(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstU64 : ConstExpr
|
||||
{
|
||||
public ConstU64(ulong value, Env env)
|
||||
: base(env)
|
||||
{
|
||||
this.Value = value;
|
||||
}
|
||||
public ulong Value { get; }
|
||||
|
||||
public override String ToString() => $"{this.Value}ull";
|
||||
|
||||
private static ExprType _type = new U64Type(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstShort : ConstExpr {
|
||||
public ConstShort(Int16 value, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public Int16 Value { get; }
|
||||
|
||||
private static ExprType _type = new ShortType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstUShort : ConstExpr {
|
||||
public ConstUShort(UInt16 value, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public UInt16 Value { get; }
|
||||
|
||||
private static ExprType _type = new UShortType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstChar : ConstExpr {
|
||||
public ConstChar(SByte value, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public SByte Value { get; }
|
||||
|
||||
private static ExprType _type = new CharType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstUChar : ConstExpr {
|
||||
public ConstUChar(Byte value, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public Byte Value { get; }
|
||||
|
||||
private static ExprType _type = new UCharType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstPtr : ConstExpr {
|
||||
public ConstPtr(UInt32 value, ExprType type, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
this.Type = type;
|
||||
}
|
||||
|
||||
public UInt32 Value { get; }
|
||||
|
||||
public override ExprType Type { get; }
|
||||
|
||||
public override String ToString() =>
|
||||
$"({this.Type} *)0x{this.Value.ToString("X8")}";
|
||||
}
|
||||
|
||||
public sealed partial class ConstFloat : ConstExpr {
|
||||
public ConstFloat(Single value, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public Single Value { get; }
|
||||
|
||||
public override String ToString() => $"{this.Value}f";
|
||||
|
||||
private static ExprType _type = new FloatType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstDouble : ConstExpr {
|
||||
public ConstDouble(Double value, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public Double Value { get; }
|
||||
|
||||
public override String ToString() => $"{this.Value}";
|
||||
|
||||
private static ExprType _type = new DoubleType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstStringLiteral : ConstExpr, AST.IStringLiteral {
|
||||
public ConstStringLiteral(String value, Env env)
|
||||
: base(env) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public String Value { get; }
|
||||
|
||||
public override String ToString() => $"\"{this.Value}\"";
|
||||
|
||||
private static ExprType _type = new AnsiStringType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
public sealed partial class ConstUnicodeStringLiteral : ConstExpr, AST.IStringLiteral
|
||||
{
|
||||
public ConstUnicodeStringLiteral(String value, Env env)
|
||||
: base(env)
|
||||
{
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public String Value { get; }
|
||||
|
||||
public override String ToString() => $"L\"{this.Value}\"";
|
||||
|
||||
private static ExprType _type = new UnicodeStringType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
|
||||
}
|
995
LibIFPSCC/ABT/Declarations.cs
Normal file
@ -0,0 +1,995 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using CodeGeneration;
|
||||
using IFPSLib.Emit;
|
||||
using Decls = IFPSLib.Emit.FDecl;
|
||||
|
||||
namespace ABT {
|
||||
public enum StorageClass {
|
||||
AUTO,
|
||||
STATIC,
|
||||
EXTERN,
|
||||
TYPEDEF
|
||||
}
|
||||
|
||||
public sealed class Decln : IExternDecln {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
public Decln(String name, StorageClass scs, ExprType type, Option<Initr> initr) {
|
||||
this.name = name;
|
||||
this.scs = scs;
|
||||
this.type = type;
|
||||
this.initr = initr;
|
||||
}
|
||||
|
||||
public override String ToString() {
|
||||
String str = "[" + this.scs + "] ";
|
||||
str += this.name;
|
||||
str += " : " + this.type;
|
||||
return str;
|
||||
}
|
||||
|
||||
private static bool TypeNeedsWalking(ExprType type)
|
||||
{
|
||||
return type is IncompleteArrayType || type is ArrayType || type is StructOrUnionType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Walks through the type tree and emits instructions to get the operand pointing to the element at <paramref name="offset"/> in a variable of type <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="insns">Instructions list to emit the instructions to</param>
|
||||
/// <param name="operand">On entry, reference to the global variable; on exit, reference to the operand to write to.</param>
|
||||
/// <param name="stackUsage">On exit, the amount of pop instructions to emit to clean up the stack.</param>
|
||||
/// <param name="offset">Offset to the variable.</param>
|
||||
/// <param name="type">Type of the base variable.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown if operation is unsupported for now.</exception>
|
||||
private static void EmitElementWalk(CGenState.IFunctionGen ctor, ref Operand operand, out int stackUsage, int offset, ExprType type)
|
||||
{
|
||||
stackUsage = 0;
|
||||
Operand local = null;
|
||||
ExprType lastType = null;
|
||||
uint baseOffset = 0;
|
||||
while (true)
|
||||
{
|
||||
var thisOffset = (uint)offset - baseOffset;
|
||||
switch (type)
|
||||
{
|
||||
case IncompleteArrayType unbounded:
|
||||
{
|
||||
// This is an array, so get the element from the offset.
|
||||
// If this is an array of structures, and the offset points into the middle of the structure, the correct index will still be calculated.
|
||||
uint arrIdx = thisOffset / (uint)unbounded.ElemType.SizeOf;
|
||||
if (TypeNeedsWalking(lastType))
|
||||
{
|
||||
// we need to pushvar or setptr the new operand.
|
||||
if (stackUsage == 0)
|
||||
{
|
||||
// we need to pushvar the operand
|
||||
local = ctor.PushVar(operand);
|
||||
stackUsage++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctor.Function.Instructions.Add(Instruction.Create(OpCodes.SetPtr, local, operand));
|
||||
}
|
||||
operand = local;
|
||||
}
|
||||
operand = Operand.Create(operand.Variable, arrIdx);
|
||||
baseOffset += (uint)unbounded.ElemType.SizeOf * arrIdx;
|
||||
type = unbounded.ElemType;
|
||||
break;
|
||||
}
|
||||
case ArrayType arr:
|
||||
{
|
||||
// This is an array, so get the element from the offset.
|
||||
// If this is an array of structures, and the offset points into the middle of the structure, the correct index will still be calculated.
|
||||
uint arrIdx = thisOffset / (uint)arr.ElemType.SizeOf;
|
||||
if (TypeNeedsWalking(lastType))
|
||||
{
|
||||
// we need to pushvar or setptr the new operand.
|
||||
if (stackUsage == 0)
|
||||
{
|
||||
// we need to pushvar the operand
|
||||
local = ctor.PushVar(operand);
|
||||
stackUsage++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctor.Function.Instructions.Add(Instruction.Create(OpCodes.SetPtr, local, operand));
|
||||
}
|
||||
operand = local;
|
||||
}
|
||||
operand = Operand.Create(operand.Variable, arrIdx);
|
||||
baseOffset += (uint)arr.ElemType.SizeOf * arrIdx;
|
||||
type = arr.ElemType;
|
||||
break;
|
||||
}
|
||||
case StructOrUnionType record:
|
||||
// This is a struct or a union.
|
||||
// TODO: figure out how to do this for a union.
|
||||
if (!record.IsStruct) throw new InvalidOperationException();
|
||||
if (stackUsage == 0)
|
||||
{
|
||||
// we need to pushvar the operand
|
||||
local = ctor.PushVar(operand);
|
||||
operand = local;
|
||||
stackUsage++;
|
||||
} else if (TypeNeedsWalking(lastType))
|
||||
{
|
||||
ctor.Function.Instructions.Add(Instruction.Create(OpCodes.SetPtr, local, operand));
|
||||
operand = local;
|
||||
}
|
||||
// Is the offset pointing to an element in this substruct?
|
||||
var element = record.Attribs.Select((a, i) => (a, i)).FirstOrDefault((v) => v.a.offset == thisOffset);
|
||||
// if this is pointing to the offset of a substruct then we need to recurse further
|
||||
if (element.a != null && element.a.type.Kind != ExprTypeKind.ARRAY && element.a.type.Kind != ExprTypeKind.STRUCT_OR_UNION)
|
||||
{
|
||||
// found the actual operand!
|
||||
operand = Operand.Create(operand.Variable, (uint)element.i);
|
||||
type = element.a.type;
|
||||
break;
|
||||
}
|
||||
// Find the structure element that contains the offset.
|
||||
element = record.Attribs.Select((a, i) => (a, i)).FirstOrDefault(
|
||||
(v) => v.a.offset > thisOffset && (v.a.offset + v.a.type.SizeOf) <= (thisOffset + type.SizeOf)
|
||||
);
|
||||
if (element.a == null) throw new InvalidOperationException();
|
||||
baseOffset += (uint)element.a.offset;
|
||||
operand = Operand.Create(operand.Variable, (uint)element.i);
|
||||
type = element.a.type;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
lastType = type;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitInitialiser(Initr value, ExprType type, Operand op, CGenState state)
|
||||
{
|
||||
var prim = type.Kind != ExprTypeKind.STRUCT_OR_UNION && type.Kind != ExprTypeKind.INCOMPLETE_ARRAY && type.Kind != ExprTypeKind.ARRAY;
|
||||
var ptr = type as PointerType;
|
||||
var isArrOfConst = false;
|
||||
{
|
||||
var arr = type as IncompleteArrayType;
|
||||
PointerType basePtr = null;
|
||||
if (arr != null)
|
||||
{
|
||||
basePtr = arr.ElemType as PointerType;
|
||||
isArrOfConst = basePtr != null && basePtr.IsForOpenArray;
|
||||
}
|
||||
}
|
||||
var insns = state.CurrInsns;
|
||||
value.Iterate(type, (Int32 offset, Expr expr) =>
|
||||
{
|
||||
var stackUsage = 0;
|
||||
var operand = new Operand(op);
|
||||
state.CGenPushStackSize();
|
||||
if (!prim && !type.EqualType(expr.Type))
|
||||
{
|
||||
// We have an offset, we need to emit the instructions required to be able to touch the element which may be in sub-structure.
|
||||
EmitElementWalk(state.FunctionState, ref operand, out stackUsage, offset, type);
|
||||
}
|
||||
|
||||
Operand ret = expr.CGenValue(state, operand);
|
||||
if (ret == operand)
|
||||
{
|
||||
state.CGenPopStackSize();
|
||||
return;
|
||||
}
|
||||
switch (expr.Type.Kind)
|
||||
{
|
||||
|
||||
case ExprTypeKind.POINTER:
|
||||
if (ptr != null && offset == 0)
|
||||
{
|
||||
Operand ptrPush = null;
|
||||
if (ret.Type == BytecodeOperandType.Variable)
|
||||
{
|
||||
if (ret.Variable.VarType == VariableType.Argument)
|
||||
{
|
||||
ptrPush = ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we don't know where on the stack this is. do a pointer-to-pointer cast with the pointer initialiser.
|
||||
IFPSLib.Types.IType refType = null;
|
||||
if (!ptr.IsRef)
|
||||
{
|
||||
refType = state.TypeUByte;
|
||||
}
|
||||
else
|
||||
{
|
||||
refType = state.EmitType(ptr.RefType);
|
||||
}
|
||||
var dummyForType = state.FunctionState.PushType(refType);
|
||||
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
|
||||
state.FunctionState.PushVar(ret);
|
||||
state.FunctionState.PushVar(dummyU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.FunctionState.Pop();
|
||||
state.FunctionState.Pop();
|
||||
state.FunctionState.PushVar(Operand.Create(state.PointerInitialiser));
|
||||
state.FunctionState.Push(dummyU32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
ptrPush = Operand.Create(state.PointerInitialiser, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, state.PointerInitialiser, Operand.Create(ret.IndexedVariable)));
|
||||
ptrPush = Operand.Create(state.PointerInitialiser, 0);
|
||||
}
|
||||
state.CGenPopStackSize();
|
||||
if (!ptr.IsRef)
|
||||
{
|
||||
var voidPtr = state.FunctionState.PushType(state.TypeU32);
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.PushVar(ptrPush);
|
||||
state.FunctionState.PushVar(voidPtr);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.CGenPopStackSize();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, operand, ptrPush));
|
||||
}
|
||||
state.CGenPushStackSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
var currPtr = expr.Type as PointerType;
|
||||
if (!currPtr.IsRef)
|
||||
{
|
||||
var voidPtr = state.FunctionState.PushType(state.TypeU32);
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.PushVar(ret);
|
||||
state.FunctionState.PushVar(voidPtr);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.CGenPopStackSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
insns.Add(Instruction.Create(OpCodes.SetPtr, operand, ret));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExprTypeKind.CHAR:
|
||||
case ExprTypeKind.UCHAR:
|
||||
case ExprTypeKind.SHORT:
|
||||
case ExprTypeKind.USHORT:
|
||||
case ExprTypeKind.DOUBLE:
|
||||
case ExprTypeKind.FLOAT:
|
||||
case ExprTypeKind.LONG:
|
||||
case ExprTypeKind.ULONG:
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
case ExprTypeKind.ANSI_STRING:
|
||||
case ExprTypeKind.UNICODE_STRING:
|
||||
case ExprTypeKind.S64:
|
||||
case ExprTypeKind.U64:
|
||||
case ExprTypeKind.COM_INTERFACE:
|
||||
case ExprTypeKind.COM_VARIANT:
|
||||
insns.Add(Instruction.Create(isArrOfConst ? OpCodes.Cpval : OpCodes.Assign, operand, ret));
|
||||
break;
|
||||
|
||||
case ExprTypeKind.ARRAY:
|
||||
case ExprTypeKind.FUNCTION:
|
||||
throw new InvalidProgramException($"How could a {expr.Type.Kind} be in a init list?").Attach(expr);
|
||||
|
||||
default:
|
||||
throw new InvalidProgramException();
|
||||
}
|
||||
|
||||
state.CGenPopStackSize();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void EnsureAttributeCountEquals(AST.TypeAttrib attrib, int count)
|
||||
{
|
||||
if (attrib.Args.Count != count)
|
||||
throw new InvalidProgramException(String.Format("{0} attribute on extern function {1} requires {2} {3}",
|
||||
attrib.Name, this.name,
|
||||
count, count == 1 ? "argument" : "arguments")).Attach(attrib);
|
||||
}
|
||||
|
||||
private void EnsureAttributeCountAtLeast(AST.TypeAttrib attrib, int count)
|
||||
{
|
||||
if (attrib.Args.Count < count)
|
||||
throw new InvalidProgramException(String.Format("{0} attribute on extern function {1} requires at least {2} {3}",
|
||||
attrib.Name, this.name,
|
||||
count, count == 1 ? "argument" : "arguments")).Attach(attrib);
|
||||
}
|
||||
|
||||
private void EnsureAttributeCountAtMost(AST.TypeAttrib attrib, int count)
|
||||
{
|
||||
if (attrib.Args.Count > count)
|
||||
throw new InvalidProgramException(String.Format("{0} attribute on extern function {1} requires at most {2} {3}",
|
||||
attrib.Name, this.name,
|
||||
count, count == 1 ? "argument" : "arguments")).Attach(attrib);
|
||||
}
|
||||
|
||||
private T EnsureAttributeIs<T>(AST.TypeAttrib attrib, int index)
|
||||
where T : AST.Expr
|
||||
{
|
||||
var arg = attrib.Args[index] as T;
|
||||
if (arg != null) return arg;
|
||||
throw new InvalidProgramException(string.Format("{0} attribute on extern function {1} requires argument {2} to be of type {3}",
|
||||
attrib.Name, this.name, index, typeof(T).Name)).Attach(attrib);
|
||||
}
|
||||
|
||||
private AST.IStringLiteral EnsureAttributeIsString(AST.TypeAttrib attrib, int index)
|
||||
{
|
||||
var arg = attrib.Args[index] as AST.IStringLiteral;
|
||||
if (arg != null) return arg;
|
||||
throw new InvalidProgramException(string.Format("{0} attribute on extern function {1} requires argument {2} to be string literal",
|
||||
attrib.Name, this.name, index)).Attach(attrib);
|
||||
}
|
||||
|
||||
private static readonly ImmutableDictionary<string, NativeCallingConvention> s_CCTable = new Dictionary<string, NativeCallingConvention>()
|
||||
{
|
||||
{ "__fastcall", NativeCallingConvention.Register },
|
||||
{ "__pascal", NativeCallingConvention.Pascal },
|
||||
{ "__cdecl", NativeCallingConvention.CDecl },
|
||||
{ "__stdcall", NativeCallingConvention.Stdcall }
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
|
||||
// * function;
|
||||
// * extern function;
|
||||
// * static function;
|
||||
// * obj;
|
||||
// * obj = Init;
|
||||
// * static obj;
|
||||
// * static obj = Init;
|
||||
// * extern obj;
|
||||
// * extern obj = Init;
|
||||
public void CGenDecln(Env env, CGenState state)
|
||||
{
|
||||
if (scs == StorageClass.TYPEDEF)
|
||||
{
|
||||
// For a typedef that can be named, change the name.
|
||||
var namableType = type as IExprTypeWithName;
|
||||
if (namableType != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(namableType.TypeName) || namableType.TypeName.Contains(' ')) state.ChangeTypeName(namableType, name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.Kind == ExprTypeKind.FUNCTION && env.IsGlobal())
|
||||
{
|
||||
// This is a global function.
|
||||
// We need to add this as the relevant extern...
|
||||
|
||||
var attribs = type.TypeAttribs;
|
||||
|
||||
var externTypes = attribs.Where((a) =>
|
||||
{
|
||||
var n = a.Name;
|
||||
return n == "__internal" || n == "__dll" || n == "__class" || n == "__com";
|
||||
});
|
||||
|
||||
var ccTypes = attribs.Where((a) => s_CCTable.ContainsKey(a.Name));
|
||||
|
||||
var externType = externTypes.FirstOrDefault();
|
||||
if (externType == null)
|
||||
{
|
||||
// no extern attribute found
|
||||
// if this is AUTO storage class, assume it's just a prototype for later.
|
||||
if (scs == StorageClass.AUTO)
|
||||
{
|
||||
state.DeclareFunction(name, (FunctionType)type, scs);
|
||||
return;
|
||||
}
|
||||
throw new InvalidProgramException(String.Format("Extern function {0} needs attributes to determine what is being imported", this.name)).Attach(this);
|
||||
}
|
||||
if (externTypes.Skip(1).Any()) throw new InvalidProgramException(String.Format("More than one extern attribute is present on function {0}", this.name)).Attach(this);
|
||||
|
||||
|
||||
var ccType = ccTypes.FirstOrDefault();
|
||||
if (externType.Name != "__internal")
|
||||
{
|
||||
// default = cdecl
|
||||
if (ccType == null) ccType = AST.TypeAttrib.Create((AST.Expr)AST.Variable.Create("__cdecl"));
|
||||
else if (ccTypes.Skip(1).Any()) throw new InvalidProgramException(String.Format("More than one calling convention attribute is present on function {0}", this.name)).Attach(this);
|
||||
}
|
||||
|
||||
var ext = new ExternalFunction();
|
||||
ext.Exported = true;
|
||||
|
||||
switch (externType.Name)
|
||||
{
|
||||
case "__internal":
|
||||
EnsureAttributeCountEquals(externType, 0);
|
||||
ext.Declaration = new Decls.Internal();
|
||||
break;
|
||||
case "__dll":
|
||||
EnsureAttributeCountAtLeast(externType, 2);
|
||||
EnsureAttributeCountAtMost(externType, 4);
|
||||
|
||||
var dll = new Decls.DLL();
|
||||
dll.DllName = EnsureAttributeIsString(externType, 0).Value;
|
||||
dll.ProcedureName = EnsureAttributeIsString(externType, 1).Value;
|
||||
|
||||
if (externType.Args.Count > 2)
|
||||
{
|
||||
var nextVal = EnsureAttributeIs<AST.Variable>(externType, 2).Name;
|
||||
switch (nextVal)
|
||||
{
|
||||
case "delayload":
|
||||
dll.DelayLoad = true;
|
||||
break;
|
||||
case "alteredsearchpath":
|
||||
dll.LoadWithAlteredSearchPath = true;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidProgramException(String.Format("Unknown DLL attribute value {0} in extern function {1}", nextVal, name)).Attach(externType);
|
||||
}
|
||||
if (externType.Args.Count > 3)
|
||||
{
|
||||
nextVal = EnsureAttributeIs<AST.Variable>(externType, 3).Name;
|
||||
switch (nextVal)
|
||||
{
|
||||
case "delayload":
|
||||
dll.DelayLoad = true;
|
||||
break;
|
||||
case "alteredsearchpath":
|
||||
dll.LoadWithAlteredSearchPath = true;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidProgramException(String.Format("Unknown DLL attribute value {0} in extern function {1}", nextVal, name)).Attach(externType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dll.CallingConvention = s_CCTable[ccType.Name];
|
||||
ext.Declaration = dll;
|
||||
break;
|
||||
case "__class":
|
||||
EnsureAttributeCountAtLeast(externType, 2);
|
||||
EnsureAttributeCountAtMost(externType, 3);
|
||||
|
||||
var cls = new Decls.Class();
|
||||
cls.ClassName = EnsureAttributeIsString(externType, 0).Value;
|
||||
cls.FunctionName = EnsureAttributeIsString(externType, 1).Value;
|
||||
if (externType.Args.Count == 3)
|
||||
{
|
||||
var nextVal = EnsureAttributeIs<AST.Variable>(externType, 2).Name;
|
||||
if (nextVal != "property")
|
||||
throw new InvalidProgramException(string.Format("Unknown class attribute value {0} in extern function {1}", nextVal, name)).Attach(externType);
|
||||
cls.IsProperty = true;
|
||||
}
|
||||
|
||||
cls.CallingConvention = s_CCTable[ccType.Name];
|
||||
ext.Declaration = cls;
|
||||
break;
|
||||
case "__com":
|
||||
EnsureAttributeCountEquals(externType, 1);
|
||||
|
||||
var functype = (FunctionType)type;
|
||||
if (functype.Args.Count == 0) throw new InvalidProgramException(string.Format("COM function must have at least one argument in extern function {0}", name)).Attach(externType);
|
||||
if (functype.Args[0].type.Kind != ExprTypeKind.COM_INTERFACE) throw new InvalidProgramException(string.Format("COM function's first argument must be of COM interface type in extern function {0}", name)).Attach(externType);
|
||||
|
||||
var com = new Decls.COM();
|
||||
var idx = EnsureAttributeIs<AST.IntLiteral>(externType, 0).Value;
|
||||
if (idx < 0) throw new InvalidProgramException(string.Format("COM vtable index must be positive in extern function {0}", name)).Attach(externType);
|
||||
com.VTableIndex = (uint)idx;
|
||||
|
||||
com.CallingConvention = s_CCTable[ccType.Name];
|
||||
ext.Declaration = com;
|
||||
break;
|
||||
}
|
||||
|
||||
if (externType.Name == "__internal" && name.ToLower() == "vartype")
|
||||
{
|
||||
// make sure there is exactly ONE definition of VarType :)
|
||||
state.VarType.Name.ToLower();
|
||||
return;
|
||||
}
|
||||
if (externType.Name == "__internal" && name.ToLower() == "setarraylength")
|
||||
{
|
||||
// make sure there is exactly ONE definition of SetArrayLength :)
|
||||
state.SetArrayLength.Name.ToLower();
|
||||
return;
|
||||
}
|
||||
|
||||
state.DeclareExternalFunction(name, ext, (FunctionType)type, this);
|
||||
if (externType.Name == "__com")
|
||||
{
|
||||
// remove the first argument
|
||||
ext.Arguments.RemoveAt(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (env.IsGlobal())
|
||||
{
|
||||
|
||||
// This is a global, which may be initialised to a value.
|
||||
// PascalScript can't do initialised globals, so we need a constructor function to set all of them.
|
||||
|
||||
// First, create the global.
|
||||
GlobalVariable global = null;
|
||||
int? numElems = null;
|
||||
if (type.Kind == ExprTypeKind.INCOMPLETE_ARRAY)
|
||||
{
|
||||
numElems = ((IncompleteArrayType)type).DeclaratorElems;
|
||||
}
|
||||
if (global == null) global = state.CreateGlobal(type, name, this);
|
||||
|
||||
if (this.initr.IsSome) // TODO: need to deal with global pointers, global structures containing pointers
|
||||
{
|
||||
// Initialised global.
|
||||
// Emit the code for this.
|
||||
|
||||
var ctor = state.Constructor;
|
||||
var insns = ctor.Function.Instructions;
|
||||
|
||||
// If this is a primitive type then we can just emit a single instruction.
|
||||
// Otherwise, we need to emit an instruction for all elements.
|
||||
|
||||
var isPrim = type.Kind != ExprTypeKind.STRUCT_OR_UNION && type.Kind != ExprTypeKind.INCOMPLETE_ARRAY && type.Kind != ExprTypeKind.ARRAY;
|
||||
var value = initr.Value;
|
||||
|
||||
if (numElems.HasValue)
|
||||
{
|
||||
// this is an incomplete array, so set its length
|
||||
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.Push(Operand.Create(numElems.Value));
|
||||
state.FunctionState.PushVar(new Operand(global));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.SetArrayLength));
|
||||
state.CGenPopStackSize();
|
||||
}
|
||||
|
||||
value.Iterate(this.type, (Int32 offset, Expr expr) =>
|
||||
{
|
||||
if (!expr.IsConstExpr)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot initialize with non-const expression.").Attach(expr);
|
||||
}
|
||||
|
||||
var operand = new IFPSLib.Emit.Operand(global);
|
||||
var stackUsage = 0;
|
||||
if (!isPrim && !type.EqualType(expr.Type))
|
||||
{
|
||||
// We have an offset, we need to emit the instructions required to be able to touch the element which may be in sub-structure.
|
||||
EmitElementWalk(ctor, ref operand, out stackUsage, offset, type);
|
||||
}
|
||||
|
||||
switch (expr.Type.Kind)
|
||||
{
|
||||
// TODO: without const char/short, how do I initialize?
|
||||
case ExprTypeKind.CHAR:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create((sbyte)((ConstLong)expr).Value)));
|
||||
break;
|
||||
case ExprTypeKind.UCHAR:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create((byte)((ConstLong)expr).Value)));
|
||||
break;
|
||||
case ExprTypeKind.SHORT:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create((short)((ConstLong)expr).Value)));
|
||||
break;
|
||||
case ExprTypeKind.USHORT:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create((ushort)((ConstLong)expr).Value)));
|
||||
break;
|
||||
case ExprTypeKind.LONG:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create(((ConstLong)expr).Value)));
|
||||
break;
|
||||
case ExprTypeKind.ULONG:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create(((ConstULong)expr).Value)));
|
||||
break;
|
||||
|
||||
case ExprTypeKind.S64:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create(((ConstS64)expr).Value)));
|
||||
break;
|
||||
case ExprTypeKind.U64:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create(((ConstU64)expr).Value)));
|
||||
break;
|
||||
|
||||
case ExprTypeKind.POINTER:
|
||||
// A global pointer is a Pointer[1]
|
||||
var ptrType = expr.Type as PointerType;
|
||||
if (ptrType == null) throw new InvalidProgramException().Attach(expr);
|
||||
if (!ptrType.IsRef)
|
||||
{
|
||||
// void*==u32
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create(((ConstPtr)expr).Value)));
|
||||
break;
|
||||
}
|
||||
var dummyForType = ctor.PushType(state.EmitType(ptrType.RefType));
|
||||
ctor.PushVar(operand);
|
||||
ctor.Push(Operand.Create(((ConstPtr)expr).Value));
|
||||
ctor.PushVar(dummyForType);
|
||||
insns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
ctor.Pop();
|
||||
ctor.Pop();
|
||||
ctor.Pop();
|
||||
ctor.Pop();
|
||||
break;
|
||||
|
||||
case ExprTypeKind.FLOAT:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create(((ConstFloat)expr).Value)));
|
||||
break;
|
||||
|
||||
case ExprTypeKind.DOUBLE:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create(((ConstDouble)expr).Value)));
|
||||
break;
|
||||
|
||||
case ExprTypeKind.ANSI_STRING:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create(new IFPSLib.Types.PrimitiveType(IFPSLib.Types.PascalTypeCode.String), ((ConstStringLiteral)expr).Value)));
|
||||
break;
|
||||
|
||||
case ExprTypeKind.UNICODE_STRING:
|
||||
insns.Add(Instruction.Create(OpCodes.Assign, operand, Operand.Create(((ConstUnicodeStringLiteral)expr).Value)));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidProgramException().Attach(expr);
|
||||
}
|
||||
|
||||
for (int i = 0; i < stackUsage; i++) insns.Add(Instruction.Create(OpCodes.Pop));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// stack object
|
||||
|
||||
Int32 stack_size = env.StackSize;
|
||||
|
||||
// pos should be equal to stack_size, but whatever...
|
||||
var envVar = env.Find(this.name);
|
||||
Int32 pos = envVar.Value.Offset;
|
||||
var ptr = type as PointerType;
|
||||
if (ptr != null && ptr.IsRef)
|
||||
{
|
||||
// this is ref type. pushtype Pointer.
|
||||
state.FunctionState.PushType(state.TypePointer);
|
||||
}
|
||||
else if (type.Kind == ExprTypeKind.INCOMPLETE_ARRAY)
|
||||
{
|
||||
var arr = type as IncompleteArrayType;
|
||||
var arr_op = state.FunctionState.PushType(state.EmitType(arr));
|
||||
// emit SetArrayLength(&arr_op, length)
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.Push(Operand.Create(arr.DeclaratorElems.Value));
|
||||
state.FunctionState.PushVar(arr_op);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.SetArrayLength));
|
||||
state.CGenPopStackSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
state.FunctionState.PushType(state.EmitType(this.type));
|
||||
}
|
||||
|
||||
if (this.initr.IsNone)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var local = LocalVariable.Create(pos);
|
||||
Initr value = this.initr.Value;
|
||||
EmitInitialiser(value, type, new IFPSLib.Emit.Operand(local), state);
|
||||
|
||||
} // stack object
|
||||
}
|
||||
|
||||
private readonly String name;
|
||||
private readonly StorageClass scs;
|
||||
private readonly ExprType type;
|
||||
private readonly Option<Initr> initr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 1. Scalar: an expression, optionally enclosed in braces.
|
||||
/// int a = 1; // valid
|
||||
/// int a = { 1 }; // valid
|
||||
/// int a[] = { { 1 }, 2 }; // valid
|
||||
/// int a = {{ 1 }}; // warning in gcc, a == 1; error in MSVC
|
||||
/// int a = { { 1 }, 2 }; // warning in gcc, a == 1; error in MSVC
|
||||
/// int a = { 1, 2 }; // warning in gcc, a == 1; error in MSVC
|
||||
/// I'm following MSVC: you either put an expression, or add a single layer of brace.
|
||||
///
|
||||
/// 2. Union:
|
||||
/// union A { int a; int b; };
|
||||
/// union A u = { 1 }; // always initialize the first member, i.e. a, not b.
|
||||
/// union A u = {{ 1 }}; // valid
|
||||
/// union A u = another_union; // valid
|
||||
///
|
||||
/// 3. Struct:
|
||||
/// struct A { int a; int b; };
|
||||
/// struct A = another_struct; // valid
|
||||
/// struct A = { another_struct }; // error, once you put a brace, the compiler assumes you want to initialize members.
|
||||
///
|
||||
/// From 2 and 3, once seen union or struct, either read expression or brace.
|
||||
///
|
||||
/// 4. Array of characters:
|
||||
/// char a[] = { 'a', 'b' }; // valid
|
||||
/// char a[] = "abc"; // becomes char a[4]: include '\0'
|
||||
/// char a[3] = "abc"; // valid, ignore '\0'
|
||||
/// char a[2] = "abc"; // warning in gcc; error in MSVC
|
||||
/// If the aggregate contains members that are aggregates or unions, or if the first member of a union is an aggregate or union, the rules apply recursively to the subaggregates or contained unions. If the initializer of a subaggregate or contained union begins with a left brace, the initializers enclosed by that brace and its matching right brace initialize the members of the subaggregate or the first member of the contained union. Otherwise, only enough initializers from the list are taken to account for the members of the first subaggregate or the first member of the contained union; any remaining initializers are left to initialize the next member of the aggregate of which the current subaggregate or contained union is a part.
|
||||
/// </summary>
|
||||
public abstract class Initr {
|
||||
public enum Kind {
|
||||
EXPR,
|
||||
INIT_LIST
|
||||
}
|
||||
public abstract Kind kind { get; }
|
||||
|
||||
public abstract Initr ConformType(MemberIterator iter);
|
||||
|
||||
public Initr ConformType(ExprType type) => ConformType(new MemberIterator(type));
|
||||
|
||||
public abstract void Iterate(MemberIterator iter, Action<Int32, Expr> action);
|
||||
|
||||
public void Iterate(ExprType type, Action<Int32, Expr> action) => Iterate(new MemberIterator(type), action);
|
||||
}
|
||||
|
||||
public class InitExpr : Initr {
|
||||
public InitExpr(Expr expr) {
|
||||
this.expr = expr;
|
||||
}
|
||||
public readonly Expr expr;
|
||||
public override Kind kind => Kind.EXPR;
|
||||
|
||||
public override Initr ConformType(MemberIterator iter) {
|
||||
iter.Locate(this.expr.Type);
|
||||
Expr expr = TypeCast.MakeCast(this.expr, iter.CurType);
|
||||
return new InitExpr(expr);
|
||||
}
|
||||
|
||||
public override void Iterate(MemberIterator iter, Action<Int32, Expr> action) {
|
||||
iter.Locate(this.expr.Type);
|
||||
Int32 offset = iter.CurOffset;
|
||||
Expr expr = this.expr;
|
||||
action(offset, expr);
|
||||
}
|
||||
}
|
||||
|
||||
public class InitList : Initr {
|
||||
public InitList(List<Initr> initrs) {
|
||||
this.initrs = initrs;
|
||||
}
|
||||
public override Kind kind => Kind.INIT_LIST;
|
||||
public readonly List<Initr> initrs;
|
||||
|
||||
public override Initr ConformType(MemberIterator iter) {
|
||||
iter.InBrace();
|
||||
List<Initr> initrs = new List<Initr>();
|
||||
for (Int32 i = 0; i < this.initrs.Count; ++i) {
|
||||
initrs.Add(this.initrs[i].ConformType(iter));
|
||||
if (i != this.initrs.Count - 1) {
|
||||
iter.Next();
|
||||
}
|
||||
}
|
||||
iter.OutBrace();
|
||||
return new InitList(initrs);
|
||||
}
|
||||
|
||||
public override void Iterate(MemberIterator iter, Action<Int32, Expr> action) {
|
||||
iter.InBrace();
|
||||
for (Int32 i = 0; i < this.initrs.Count; ++i) {
|
||||
this.initrs[i].Iterate(iter, action);
|
||||
if (i != this.initrs.Count - 1) {
|
||||
iter.Next();
|
||||
}
|
||||
}
|
||||
iter.OutBrace();
|
||||
}
|
||||
}
|
||||
|
||||
public class MemberIterator {
|
||||
public MemberIterator(ExprType type) {
|
||||
this.trace = new List<Status> { new Status(type) };
|
||||
}
|
||||
|
||||
public class Status {
|
||||
public Status(ExprType base_type) {
|
||||
this.base_type = base_type;
|
||||
this.indices = new List<Int32>();
|
||||
}
|
||||
|
||||
public ExprType CurType => GetType(this.base_type, this.indices);
|
||||
|
||||
public Int32 CurOffset => GetOffset(this.base_type, this.indices);
|
||||
|
||||
//public List<Tuple<ExprType, Int32>> GetPath(ExprType base_type, IReadOnlyList<Int32> indices) {
|
||||
// ExprType Type = base_type;
|
||||
// List<Tuple<ExprType, Int32>> path = new List<Tuple<ExprType, int>>();
|
||||
// foreach (Int32 index in indices) {
|
||||
// switch (Type.Kind) {
|
||||
// case ExprType.Kind.ARRAY:
|
||||
// Type = ((ArrayType)Type).ElemType;
|
||||
// break;
|
||||
// case ExprType.Kind.INCOMPLETE_ARRAY:
|
||||
// case ExprType.Kind.STRUCT_OR_UNION:
|
||||
// default:
|
||||
// throw new InvalidProgramException("Not an aggregate Type.");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
public static ExprType GetType(ExprType from_type, Int32 to_index) {
|
||||
switch (from_type.Kind) {
|
||||
case ExprTypeKind.ARRAY:
|
||||
return ((ArrayType)from_type).ElemType;
|
||||
|
||||
case ExprTypeKind.INCOMPLETE_ARRAY:
|
||||
return ((IncompleteArrayType)from_type).ElemType;
|
||||
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
return ((StructOrUnionType)from_type).Attribs[to_index].type;
|
||||
|
||||
default:
|
||||
throw new InvalidProgramException("Not an aggregate Type.");
|
||||
}
|
||||
}
|
||||
|
||||
public static ExprType GetType(ExprType base_type, IReadOnlyList<Int32> indices) =>
|
||||
indices.Aggregate(base_type, GetType);
|
||||
|
||||
public static Int32 GetOffset(ExprType from_type, Int32 to_index) {
|
||||
switch (from_type.Kind) {
|
||||
case ExprTypeKind.ARRAY:
|
||||
return to_index * ((ArrayType)from_type).ElemType.SizeOf;
|
||||
|
||||
case ExprTypeKind.INCOMPLETE_ARRAY:
|
||||
return to_index * ((IncompleteArrayType)from_type).ElemType.SizeOf;
|
||||
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
return ((StructOrUnionType)from_type).Attribs[to_index].offset;
|
||||
|
||||
default:
|
||||
throw new InvalidProgramException("Not an aggregate Type.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Int32 GetOffset(ExprType base_type, IReadOnlyList<Int32> indices) {
|
||||
Int32 offset = 0;
|
||||
ExprType from_type = base_type;
|
||||
foreach (Int32 to_index in indices) {
|
||||
offset += GetOffset(from_type, to_index);
|
||||
from_type = GetType(from_type, to_index);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
public List<ExprType> GetTypes(ExprType base_type, IReadOnlyList<Int32> indices) {
|
||||
List<ExprType> types = new List<ExprType> { base_type };
|
||||
ExprType from_type = base_type;
|
||||
foreach (Int32 to_index in indices) {
|
||||
from_type = GetType(from_type, to_index);
|
||||
types.Add(from_type);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
public void Next() {
|
||||
|
||||
// From base_type to CurType.
|
||||
List<ExprType> types = GetTypes(this.base_type, this.indices);
|
||||
|
||||
// We try to jump as many levels out as we can.
|
||||
do {
|
||||
Int32 index = this.indices.Last();
|
||||
this.indices.RemoveAt(this.indices.Count - 1);
|
||||
|
||||
types.RemoveAt(types.Count - 1);
|
||||
ExprType type = types.Last();
|
||||
|
||||
switch (type.Kind) {
|
||||
case ExprTypeKind.ARRAY:
|
||||
if (index < ((ArrayType)type).NumElems - 1) {
|
||||
// There are more elements in the array.
|
||||
this.indices.Add(index + 1);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExprTypeKind.INCOMPLETE_ARRAY:
|
||||
this.indices.Add(index + 1);
|
||||
return;
|
||||
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
if (((StructOrUnionType)type).IsStruct && index < ((StructOrUnionType)type).Attribs.Count - 1) {
|
||||
// There are more members in the struct.
|
||||
// (not union, since we can only initialize the first member of a union)
|
||||
this.indices.Add(index + 1);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
} while (this.indices.Any());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an expression in the initializer list, locate the corresponding position.
|
||||
/// </summary>
|
||||
public void Locate(ExprType type) {
|
||||
switch (type.Kind) {
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
LocateStruct((StructOrUnionType)type);
|
||||
return;
|
||||
default:
|
||||
// Even if the expression is of array Type, treat it as a scalar (pointer).
|
||||
LocateScalar();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to match a scalar.
|
||||
/// This step doesn't check what scalar it is. Further steps would perform implicit conversions.
|
||||
/// </summary>
|
||||
private void LocateScalar() {
|
||||
while (!this.CurType.IsScalar) {
|
||||
this.indices.Add(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to match a given struct.
|
||||
/// Go down to find the first element of the same struct Type.
|
||||
/// </summary>
|
||||
private void LocateStruct(StructOrUnionType type) {
|
||||
while (!this.CurType.EqualType(type)) {
|
||||
if (this.CurType.IsScalar) {
|
||||
throw new InvalidOperationException("Trying to match a struct or union, but found a scalar.");
|
||||
}
|
||||
|
||||
// Go down one level.
|
||||
this.indices.Add(0);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly ExprType base_type;
|
||||
public readonly List<Int32> indices;
|
||||
}
|
||||
|
||||
public ExprType CurType => this.trace.Last().CurType;
|
||||
|
||||
public Int32 CurOffset => this.trace.Select(_ => _.CurOffset).Sum();
|
||||
|
||||
public void Next() => this.trace.Last().Next();
|
||||
|
||||
public void Locate(ExprType type) => this.trace.Last().Locate(type);
|
||||
|
||||
public void InBrace() {
|
||||
|
||||
/// Push the current position into the stack, so that we can get back by <see cref="OutBrace"/>
|
||||
this.trace.Add(new Status(this.trace.Last().CurType));
|
||||
|
||||
// For aggregate types, go inside and locate the first member.
|
||||
if (!this.CurType.IsScalar) {
|
||||
this.trace.Last().indices.Add(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void OutBrace() => this.trace.RemoveAt(this.trace.Count - 1);
|
||||
|
||||
public readonly List<Status> trace;
|
||||
}
|
||||
}
|
644
LibIFPSCC/ABT/Environment.cs
Normal file
@ -0,0 +1,644 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace ABT {
|
||||
public class Env {
|
||||
|
||||
// enum EntryLoc
|
||||
// =============
|
||||
// the location of an object
|
||||
// STACK: this is a variable stored in the stack
|
||||
// FRAME: this is a function parameter
|
||||
// GLOBAL: this is a global symbol
|
||||
//
|
||||
public enum EntryKind {
|
||||
ENUM,
|
||||
TYPEDEF,
|
||||
STACK,
|
||||
FRAME,
|
||||
GLOBAL
|
||||
}
|
||||
|
||||
|
||||
// class Entry
|
||||
// ===========
|
||||
// the return Value when searching for a symbol in the environment
|
||||
// attributes:
|
||||
// entry_loc: the location of this object
|
||||
// entry_type: the Type of the object
|
||||
// entry_offset: this is used to determine the address of the object
|
||||
// STACK: addr = %ebp - offset
|
||||
// GLOBAL: N/A
|
||||
//
|
||||
public class Entry {
|
||||
public Entry(EntryKind kind, ExprType type, Int32 offset) {
|
||||
this.Kind = kind;
|
||||
this.Type = type;
|
||||
this.Offset = offset;
|
||||
}
|
||||
public readonly EntryKind Kind;
|
||||
public readonly ExprType Type;
|
||||
public readonly Int32 Offset;
|
||||
}
|
||||
|
||||
private class Scope {
|
||||
|
||||
// private constructor
|
||||
// ===================
|
||||
//
|
||||
private Scope(List<Utils.StoreEntry> stack_entries,
|
||||
Int32 stack_offset,
|
||||
List<Utils.StoreEntry> global_entries,
|
||||
FunctionType curr_func,
|
||||
List<Utils.StoreEntry> typedef_entries,
|
||||
List<Utils.StoreEntry> enum_entries) {
|
||||
this.locals = stack_entries;
|
||||
this.count_locals = stack_offset;
|
||||
this.globals = global_entries;
|
||||
this.func = curr_func;
|
||||
this.typedefs = typedef_entries;
|
||||
this.enums = enum_entries;
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
// ================
|
||||
//
|
||||
private Scope(Scope other)
|
||||
: this(new List<Utils.StoreEntry>(other.locals),
|
||||
other.count_locals,
|
||||
new List<Utils.StoreEntry>(other.globals),
|
||||
other.func,
|
||||
new List<Utils.StoreEntry>(other.typedefs),
|
||||
new List<Utils.StoreEntry>(other.enums)) {}
|
||||
|
||||
// empty Scope
|
||||
// ===========
|
||||
//
|
||||
public Scope()
|
||||
: this(new List<Utils.StoreEntry>(),
|
||||
0,
|
||||
new List<Utils.StoreEntry>(),
|
||||
new EmptyFunctionType(),
|
||||
new List<Utils.StoreEntry>(),
|
||||
new List<Utils.StoreEntry>()) {}
|
||||
|
||||
|
||||
// InScope
|
||||
// =======
|
||||
// create a new scope with:
|
||||
// the same stack offset
|
||||
// the same current function
|
||||
// other entries are empty
|
||||
//
|
||||
public Scope InScope() {
|
||||
return new Scope(new List<Utils.StoreEntry>(), this.count_locals,
|
||||
new List<Utils.StoreEntry>(), this.func,
|
||||
new List<Utils.StoreEntry>(),
|
||||
new List<Utils.StoreEntry>());
|
||||
}
|
||||
|
||||
|
||||
// PushEntry
|
||||
// =========
|
||||
// input: loc, name, Type
|
||||
// output: Scope
|
||||
// returns a new scope with everything the same as this, excpet for a new entry
|
||||
//
|
||||
public Scope PushEntry(EntryKind loc, String name, ExprType type) {
|
||||
Scope scope = new Scope(this);
|
||||
switch (loc) {
|
||||
case EntryKind.STACK:
|
||||
scope.locals.Add(new Utils.StoreEntry(name, type, scope.count_locals));
|
||||
scope.count_locals++;
|
||||
break;
|
||||
case EntryKind.GLOBAL:
|
||||
scope.globals.Add(new Utils.StoreEntry(name, type, 0));
|
||||
break;
|
||||
case EntryKind.TYPEDEF:
|
||||
scope.typedefs.Add(new Utils.StoreEntry(name, type, 0));
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
|
||||
// PushEnum
|
||||
// ========
|
||||
// input: name, Type
|
||||
// output: Environment
|
||||
// return a new environment which adds a enum Value
|
||||
//
|
||||
public Scope PushEnum(String name, ExprType type, Int32 value) {
|
||||
Scope scope = new Scope(this);
|
||||
scope.enums.Add(new Utils.StoreEntry(name, type, value));
|
||||
return scope;
|
||||
}
|
||||
|
||||
|
||||
// SetCurrFunc
|
||||
// ===========
|
||||
// set the current function
|
||||
public Scope SetCurrentFunction(FunctionType type) {
|
||||
return new Scope(this.locals, this.count_locals, this.globals,
|
||||
type, this.typedefs, this.enums
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Find
|
||||
// ====
|
||||
// input: name
|
||||
// output: Entry
|
||||
// search for a symbol in the current scope
|
||||
//
|
||||
public Entry Find(String name) {
|
||||
Utils.StoreEntry store_entry;
|
||||
|
||||
// search the enum entries
|
||||
if ((store_entry = this.enums.FindLast(entry => entry.name == name)) != null) {
|
||||
return new Entry(EntryKind.ENUM, store_entry.type, store_entry.offset);
|
||||
}
|
||||
|
||||
// search the typedef entries
|
||||
if ((store_entry = this.typedefs.FindLast(entry => entry.name == name)) != null) {
|
||||
return new Entry(EntryKind.TYPEDEF, store_entry.type, store_entry.offset);
|
||||
}
|
||||
|
||||
// search the stack entries
|
||||
if ((store_entry = this.locals.FindLast(entry => entry.name == name)) != null) {
|
||||
return new Entry(EntryKind.STACK, store_entry.type, store_entry.offset);
|
||||
}
|
||||
|
||||
// search the function arguments
|
||||
if ((store_entry = this.func.Args.FindLast(entry => entry.name == name)) != null) {
|
||||
return new Entry(EntryKind.FRAME, store_entry.type, store_entry.offset);
|
||||
}
|
||||
|
||||
// search the global entries
|
||||
if ((store_entry = this.globals.FindLast(entry => entry.name == name)) != null) {
|
||||
return new Entry(EntryKind.GLOBAL, store_entry.type, store_entry.offset);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Dump
|
||||
// ====
|
||||
// input: depth, indent
|
||||
// output: String
|
||||
// dumps the content in this level
|
||||
//
|
||||
public String Dump(Int32 depth, String single_indent) {
|
||||
String indent = "";
|
||||
for (; depth > 0; depth--) {
|
||||
indent += single_indent;
|
||||
}
|
||||
|
||||
String str = "";
|
||||
foreach (Utils.StoreEntry entry in this.func.Args) {
|
||||
str += indent;
|
||||
str += "[%ebp + " + entry.offset + "] " + entry.name + " : " + entry.type + "\n";
|
||||
}
|
||||
foreach (Utils.StoreEntry entry in this.globals) {
|
||||
str += indent;
|
||||
str += "[extern] " + entry.name + " : " + entry.type + "\n";
|
||||
}
|
||||
foreach (Utils.StoreEntry entry in this.locals) {
|
||||
str += indent;
|
||||
str += "[%ebp - " + entry.offset + "] " + entry.name + " : " + entry.type + "\n";
|
||||
}
|
||||
foreach (Utils.StoreEntry entry in this.typedefs) {
|
||||
str += indent;
|
||||
str += "typedef: " + entry.name + " <- " + entry.type + "\n";
|
||||
}
|
||||
foreach (Utils.StoreEntry entry in this.enums) {
|
||||
str += indent;
|
||||
str += entry.name + " = " + entry.offset + "\n";
|
||||
}
|
||||
return str;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ================================================================
|
||||
// private members
|
||||
// ================================================================
|
||||
public readonly List<Utils.StoreEntry> locals;
|
||||
public readonly FunctionType func;
|
||||
public readonly List<Utils.StoreEntry> globals;
|
||||
public readonly List<Utils.StoreEntry> typedefs;
|
||||
public readonly List<Utils.StoreEntry> enums;
|
||||
public Int32 count_locals;
|
||||
|
||||
}
|
||||
|
||||
// Environment
|
||||
// ===========
|
||||
// construct an environment with a single empty scope
|
||||
public Env() {
|
||||
this._scopes = ImmutableStack.Create(new Scope());
|
||||
}
|
||||
|
||||
// Environment
|
||||
// ===========
|
||||
// construct an environment with the given scopes
|
||||
//
|
||||
private Env(ImmutableStack<Scope> scopes) {
|
||||
this._scopes = scopes;
|
||||
}
|
||||
|
||||
// InScope
|
||||
// =======
|
||||
// input: void
|
||||
// output: Environment
|
||||
// return a new environment which has a new inner scope
|
||||
//
|
||||
public Env InScope() {
|
||||
return new Env(this._scopes.Push(this._scopes.Peek().InScope()));
|
||||
}
|
||||
|
||||
// OutScope
|
||||
// ========
|
||||
// input: void
|
||||
// output: Environment
|
||||
// return a new environment which goes out of the most inner scope of the current environment
|
||||
//
|
||||
public Env OutScope() {
|
||||
return new Env(this._scopes.Pop());
|
||||
}
|
||||
|
||||
// PushEntry
|
||||
// =========
|
||||
// input: loc, name, Type
|
||||
// ouput: Environment
|
||||
// return a new environment which adds a symbol entry
|
||||
//
|
||||
public Env PushEntry(EntryKind loc, String name, ExprType type) {
|
||||
Scope top = this._scopes.Peek();
|
||||
return new Env(this._scopes.Pop().Push(top.PushEntry(loc, name, type)));
|
||||
}
|
||||
|
||||
// PushEnum
|
||||
// ========
|
||||
// input: name, Type
|
||||
// output: Environment
|
||||
// return a new environment which adds a enum Value
|
||||
//
|
||||
public Env PushEnum(String name, ExprType type, Int32 value) {
|
||||
Scope top = this._scopes.Peek();
|
||||
return new Env(this._scopes.Pop().Push(top.PushEnum(name, type, value)));
|
||||
}
|
||||
|
||||
// SetCurrentFunction
|
||||
// ==================
|
||||
// input: Type
|
||||
// ouput: Environment
|
||||
// return a new environment which sets the current function
|
||||
//
|
||||
public Env SetCurrentFunction(FunctionType type) {
|
||||
Scope top = this._scopes.Peek();
|
||||
return new Env(this._scopes.Pop().Push(top.SetCurrentFunction(type)));
|
||||
}
|
||||
|
||||
// GetCurrentFunction
|
||||
// ==================
|
||||
// input: void
|
||||
// output: FunctionType
|
||||
// return the Type of the current function
|
||||
//
|
||||
public FunctionType GetCurrentFunction() {
|
||||
return this._scopes.Peek().func;
|
||||
}
|
||||
|
||||
// GetStackOffset
|
||||
// ==============
|
||||
// input: void
|
||||
// output: Int32
|
||||
// return the current stack size
|
||||
//
|
||||
public Int32 StackSize => this._scopes.Peek().count_locals;
|
||||
|
||||
public Option<Entry> Find(String name) {
|
||||
Entry entry = null;
|
||||
foreach (Scope scope in this._scopes) {
|
||||
if ((entry = scope.Find(name)) != null) {
|
||||
return new Some<Entry>(entry);
|
||||
}
|
||||
}
|
||||
return new None<Entry>();
|
||||
}
|
||||
|
||||
public Option<Entry> FindInCurrentScope(String name) {
|
||||
var entry = this._scopes.Peek().Find(name);
|
||||
if (entry == null) {
|
||||
return Option<Entry>.None;
|
||||
}
|
||||
return Option.Some(entry);
|
||||
}
|
||||
|
||||
public Boolean IsGlobal() {
|
||||
return this._scopes.Count() == 1;
|
||||
}
|
||||
|
||||
public String Dump() {
|
||||
String str = "";
|
||||
Int32 depth = 0;
|
||||
foreach (Scope scope in this._scopes) {
|
||||
str += scope.Dump(depth, " ");
|
||||
depth++;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private readonly ImmutableStack<Scope> _scopes;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 1. A global scope.
|
||||
/// 2. A function scope, with multiple name scopes.
|
||||
/// 3. ObjectId.
|
||||
/// 4. TypeId.
|
||||
///
|
||||
/// </summary>
|
||||
public sealed class Env2 {
|
||||
|
||||
// global scope
|
||||
// global object
|
||||
// static object
|
||||
// typedef
|
||||
// enum value
|
||||
// local scope
|
||||
// static object
|
||||
// typedef
|
||||
// frame object
|
||||
// enum value
|
||||
// parameter object
|
||||
|
||||
public enum EntryKind {
|
||||
FRAME,
|
||||
// STACK,
|
||||
GLOBAL,
|
||||
TYPEDEF,
|
||||
ENUM,
|
||||
NAMED,
|
||||
PARAM
|
||||
}
|
||||
|
||||
public abstract class SymbolEntry {
|
||||
public abstract EntryKind Kind { get; }
|
||||
}
|
||||
|
||||
public abstract class ObjectEntry : SymbolEntry {
|
||||
public ExprType Type { get; }
|
||||
}
|
||||
|
||||
// public sealed class StackObjectEntry : ObjectEntry {
|
||||
// public override EntryKind Kind => EntryKind.STACK;
|
||||
// }
|
||||
|
||||
public sealed class ParameterObjectEntry : ObjectEntry {
|
||||
public override EntryKind Kind => EntryKind.PARAM;
|
||||
}
|
||||
|
||||
public sealed class FrameObjectEntry : ObjectEntry {
|
||||
public override EntryKind Kind => EntryKind.FRAME;
|
||||
}
|
||||
|
||||
public sealed class NamedObjectEntry : ObjectEntry {
|
||||
public override EntryKind Kind => EntryKind.NAMED;
|
||||
}
|
||||
|
||||
public sealed class TypeEntry : SymbolEntry {
|
||||
public override EntryKind Kind => EntryKind.TYPEDEF;
|
||||
public ExprType Type { get; }
|
||||
}
|
||||
|
||||
public sealed class EnumEntry : SymbolEntry {
|
||||
public override EntryKind Kind => EntryKind.ENUM;
|
||||
}
|
||||
|
||||
private abstract class SymbolTable {
|
||||
protected SymbolTable(ImmutableList<TypeEntry> typeDefs, ImmutableList<EnumEntry> enums) {
|
||||
this.TypeDefs = typeDefs;
|
||||
this.Enums = enums;
|
||||
}
|
||||
|
||||
protected SymbolTable()
|
||||
: this(ImmutableList<TypeEntry>.Empty, ImmutableList<EnumEntry>.Empty) { }
|
||||
|
||||
public ImmutableList<TypeEntry> TypeDefs { get; }
|
||||
|
||||
public ImmutableList<EnumEntry> Enums { get; }
|
||||
}
|
||||
|
||||
private sealed class GlobalSymbolTable : SymbolTable {
|
||||
public GlobalSymbolTable(ImmutableList<TypeEntry> typeDefs, ImmutableList<EnumEntry> enums, ImmutableList<NamedObjectEntry> globalObjects)
|
||||
: base(typeDefs, enums) {
|
||||
this.GlobalObjects = globalObjects;
|
||||
}
|
||||
|
||||
public GlobalSymbolTable()
|
||||
: this(ImmutableList<TypeEntry>.Empty, ImmutableList<EnumEntry>.Empty, ImmutableList<NamedObjectEntry>.Empty) { }
|
||||
|
||||
public GlobalSymbolTable Add(TypeEntry typeEntry) =>
|
||||
new GlobalSymbolTable(this.TypeDefs.Add(typeEntry), this.Enums, this.GlobalObjects);
|
||||
|
||||
public GlobalSymbolTable Add(EnumEntry enumEntry) =>
|
||||
new GlobalSymbolTable(this.TypeDefs, this.Enums.Add(enumEntry), this.GlobalObjects);
|
||||
|
||||
public GlobalSymbolTable Add(NamedObjectEntry globalObjectEntry) =>
|
||||
new GlobalSymbolTable(this.TypeDefs, this.Enums, this.GlobalObjects.Add(globalObjectEntry));
|
||||
|
||||
public ImmutableList<NamedObjectEntry> GlobalObjects { get; }
|
||||
}
|
||||
|
||||
private sealed class LocalSymbolTable : SymbolTable {
|
||||
public LocalSymbolTable(ImmutableList<TypeEntry> typeDefs, ImmutableList<EnumEntry> enums, ImmutableList<FrameObjectEntry> frameObjects)
|
||||
: base(typeDefs, enums) {
|
||||
this.FrameObjects = frameObjects;
|
||||
}
|
||||
|
||||
public LocalSymbolTable() {
|
||||
this.FrameObjects = ImmutableList<FrameObjectEntry>.Empty;
|
||||
}
|
||||
|
||||
public LocalSymbolTable Add(TypeEntry typeEntry) =>
|
||||
new LocalSymbolTable(this.TypeDefs.Add(typeEntry), this.Enums, this.FrameObjects);
|
||||
|
||||
public LocalSymbolTable Add(EnumEntry enumEntry) =>
|
||||
new LocalSymbolTable(this.TypeDefs, this.Enums.Add(enumEntry), this.FrameObjects);
|
||||
|
||||
public LocalSymbolTable Add(FrameObjectEntry frameObjectEntry) =>
|
||||
new LocalSymbolTable(this.TypeDefs, this.Enums, this.FrameObjects.Add(frameObjectEntry));
|
||||
|
||||
public ImmutableList<FrameObjectEntry> FrameObjects { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function info, local symbol tables.
|
||||
/// </summary>
|
||||
private sealed class FunctionScope {
|
||||
public FunctionScope(FunctionType functionType, ImmutableList<ParameterObjectEntry> functionParams, ImmutableStack<LocalSymbolTable> localScopes) {
|
||||
this.FunctionType = functionType;
|
||||
this.FunctionParams = FunctionParams;
|
||||
this.LocalScopes = localScopes;
|
||||
}
|
||||
|
||||
public FunctionScope(FunctionType functionType, ImmutableList<ParameterObjectEntry> functionParams)
|
||||
: this(functionType, functionParams, ImmutableStack<LocalSymbolTable>.Empty) { }
|
||||
|
||||
public FunctionScope Add(TypeEntry typeEntry) {
|
||||
var localSymbleTable = this.LocalScopes.Peek().Add(typeEntry);
|
||||
return new FunctionScope(
|
||||
this.FunctionType,
|
||||
this.FunctionParams,
|
||||
this.LocalScopes.Pop().Push(localSymbleTable)
|
||||
);
|
||||
}
|
||||
|
||||
public FunctionScope Add(EnumEntry enumEntry) {
|
||||
var localSymbleTable = this.LocalScopes.Peek().Add(enumEntry);
|
||||
return new FunctionScope(
|
||||
this.FunctionType,
|
||||
this.FunctionParams,
|
||||
this.LocalScopes.Pop().Push(localSymbleTable)
|
||||
);
|
||||
}
|
||||
|
||||
public FunctionScope Add(FrameObjectEntry frameObjectEntry) {
|
||||
var localSymbleTable = this.LocalScopes.Peek().Add(frameObjectEntry);
|
||||
return new FunctionScope(
|
||||
this.FunctionType,
|
||||
this.FunctionParams,
|
||||
this.LocalScopes.Pop().Push(localSymbleTable)
|
||||
);
|
||||
}
|
||||
|
||||
public FunctionType FunctionType { get; }
|
||||
|
||||
public ImmutableList<ParameterObjectEntry> FunctionParams { get; }
|
||||
|
||||
public ImmutableStack<LocalSymbolTable> LocalScopes { get; }
|
||||
}
|
||||
|
||||
private Env2(GlobalSymbolTable globalSymbolTable, Option<FunctionScope> functionScope) {
|
||||
this._globalSymbolTable = globalSymbolTable;
|
||||
this._functionScope = functionScope;
|
||||
}
|
||||
|
||||
public Env2() : this(new GlobalSymbolTable(), Option<FunctionScope>.None) { }
|
||||
|
||||
private GlobalSymbolTable _globalSymbolTable { get; }
|
||||
|
||||
private Option<FunctionScope> _functionScope { get; }
|
||||
|
||||
public Env2 InFunction(FunctionType functionType, ImmutableList<ParameterObjectEntry> functionParams) {
|
||||
if (this._functionScope.IsSome) {
|
||||
throw new InvalidProgramException("Is already in a function. Cannot go in function.");
|
||||
}
|
||||
return new Env2(
|
||||
this._globalSymbolTable,
|
||||
Option.Some(new FunctionScope(functionType, functionParams))
|
||||
);
|
||||
}
|
||||
|
||||
public Env2 OutFunction() {
|
||||
if (this._functionScope.IsNone) {
|
||||
throw new InvalidProgramException("Is already global. Cannot go out of function.");
|
||||
}
|
||||
return new Env2(
|
||||
this._globalSymbolTable,
|
||||
Option<FunctionScope>.None
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new local symbol table.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The new environment.
|
||||
/// </returns>
|
||||
public Env2 InScope() {
|
||||
if (this._functionScope.IsNone) {
|
||||
throw new InvalidProgramException("Isn't in a function. Cannot push scope.");
|
||||
}
|
||||
|
||||
return new Env2(
|
||||
this._globalSymbolTable,
|
||||
Option.Some(new FunctionScope(
|
||||
this._functionScope.Value.FunctionType,
|
||||
this._functionScope.Value.FunctionParams,
|
||||
this._functionScope.Value.LocalScopes.Push(new LocalSymbolTable())
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pop a local symbol table.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The new environment.
|
||||
/// </returns>
|
||||
public Env2 OutScope() {
|
||||
if (this._functionScope.IsNone) {
|
||||
throw new InvalidProgramException("Isn't in a function. Cannot pop scope.");
|
||||
}
|
||||
if (this._functionScope.Value.LocalScopes.IsEmpty) {
|
||||
throw new InvalidProgramException("No Local scope to pop.");
|
||||
}
|
||||
return new Env2(
|
||||
this._globalSymbolTable,
|
||||
Option.Some(new FunctionScope(
|
||||
this._functionScope.Value.FunctionType,
|
||||
this._functionScope.Value.FunctionParams,
|
||||
this._functionScope.Value.LocalScopes.Pop()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
public Env2 Add(EnumEntry entry) {
|
||||
if (this._functionScope.IsNone) {
|
||||
// global
|
||||
return new Env2(
|
||||
this._globalSymbolTable.Add(entry),
|
||||
this._functionScope
|
||||
);
|
||||
} else {
|
||||
// local
|
||||
return new Env2(
|
||||
this._globalSymbolTable,
|
||||
Option.Some(this._functionScope.Value.Add(entry))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public Env2 Add(TypeEntry entry) {
|
||||
if (this._functionScope.IsNone) {
|
||||
// global
|
||||
return new Env2(
|
||||
this._globalSymbolTable.Add(entry),
|
||||
this._functionScope
|
||||
);
|
||||
} else {
|
||||
// local
|
||||
return new Env2(
|
||||
this._globalSymbolTable,
|
||||
Option.Some(this._functionScope.Value.Add(entry))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public Env2 Add(NamedObjectEntry entry) {
|
||||
if (this._functionScope.IsSome) {
|
||||
|
||||
}
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
}
|
237
LibIFPSCC/ABT/Expressions.cs
Normal file
@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using CodeGeneration;
|
||||
|
||||
namespace ABT {
|
||||
// Expr
|
||||
// ========================================================================
|
||||
|
||||
/// <summary>
|
||||
/// The cdecl calling convention:
|
||||
/// 1. arguments are passed on the stack, right to left.
|
||||
/// 2. int values and pointer values are returned in %eax.
|
||||
/// 3. floats are returned in %st(0).
|
||||
/// 4. when calling a function, %st(0) ~ %st(7) are all free.
|
||||
/// 5. functions are free to use %eax, %ecx, %edx, because caller needs to save them.
|
||||
/// 6. stack must be aligned to 4 bytes (before gcc 4.5, for gcc 4.5+, aligned to 16 bytes).
|
||||
/// </summary>
|
||||
|
||||
public abstract partial class Expr : IStoredLineInfo {
|
||||
protected Expr() { }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the Value is known at compile time.
|
||||
/// </summary>
|
||||
public virtual Boolean IsConstExpr => false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the expression refers to an object (that can be assigned to).
|
||||
/// </summary>
|
||||
public abstract Boolean IsLValue { get; }
|
||||
|
||||
public abstract Env Env { get; }
|
||||
|
||||
public abstract ExprType Type { get; }
|
||||
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ExprInitList : Expr
|
||||
{
|
||||
public ExprInitList(InitList list, Env env) {
|
||||
this.List = list;
|
||||
this.Env = env;
|
||||
}
|
||||
|
||||
public InitList List { get; }
|
||||
|
||||
public override Env Env { get; }
|
||||
|
||||
private ExprType m_Type = null;
|
||||
|
||||
public void CoerceType(ExprType type)
|
||||
{
|
||||
if (m_Type == null) m_Type = type;
|
||||
}
|
||||
|
||||
public override ExprType Type => m_Type;
|
||||
|
||||
public override Boolean IsLValue => !(Type is FunctionType);
|
||||
}
|
||||
|
||||
public sealed partial class Variable : Expr {
|
||||
public Variable(ExprType type, String name, Env env) {
|
||||
this.Name = name;
|
||||
this.Env = env;
|
||||
this.Type = type;
|
||||
}
|
||||
|
||||
public String Name { get; }
|
||||
|
||||
public override Env Env { get; }
|
||||
|
||||
public override ExprType Type { get; }
|
||||
|
||||
public override Boolean IsLValue => !(Type is FunctionType);
|
||||
}
|
||||
|
||||
public sealed partial class AssignList : Expr {
|
||||
public AssignList(ImmutableList<Expr> exprs, ILineInfo info) {
|
||||
if (exprs.Count == 0) {
|
||||
throw new InvalidOperationException("Need at least one expression.").Attach(info);
|
||||
}
|
||||
this.Exprs = exprs;
|
||||
}
|
||||
|
||||
public ImmutableList<Expr> Exprs { get; }
|
||||
|
||||
public override Env Env => this.Exprs.Last().Env;
|
||||
|
||||
public override Boolean IsLValue => false;
|
||||
|
||||
public override ExprType Type => this.Exprs.Last().Type;
|
||||
}
|
||||
|
||||
public sealed partial class Assign : Expr {
|
||||
public Assign(Expr left, Expr right) {
|
||||
this.Left = left;
|
||||
this.Right = right;
|
||||
|
||||
if (!this.Left.IsLValue) {
|
||||
throw new InvalidOperationException("Can only assign to lvalue.").Attach(left);
|
||||
}
|
||||
}
|
||||
|
||||
public Expr Left { get; }
|
||||
|
||||
public Expr Right { get; }
|
||||
|
||||
public override Env Env => this.Right.Env;
|
||||
|
||||
public override Boolean IsLValue => false;
|
||||
|
||||
public override ExprType Type => this.Left.Type.GetQualifiedType(false, false);
|
||||
}
|
||||
|
||||
public sealed partial class ConditionalExpr : Expr {
|
||||
public ConditionalExpr(Expr cond, Expr trueExpr, Expr falseExpr, ExprType type) {
|
||||
this.Cond = cond;
|
||||
this.TrueExpr = trueExpr;
|
||||
this.FalseExpr = falseExpr;
|
||||
this.Type = type;
|
||||
}
|
||||
|
||||
public readonly Expr Cond;
|
||||
|
||||
public readonly Expr TrueExpr;
|
||||
|
||||
public readonly Expr FalseExpr;
|
||||
|
||||
public override Boolean IsLValue => false;
|
||||
|
||||
public override ExprType Type { get; }
|
||||
|
||||
public override Env Env => this.FalseExpr.Env;
|
||||
}
|
||||
|
||||
public sealed partial class FuncCall : Expr {
|
||||
public FuncCall(Expr func, FunctionType funcType, List<Expr> args) {
|
||||
this.Func = func;
|
||||
this.FuncType = funcType;
|
||||
this.Args = args;
|
||||
}
|
||||
|
||||
public Expr Func { get; }
|
||||
|
||||
public FunctionType FuncType { get; }
|
||||
|
||||
public IReadOnlyList<Expr> Args { get; }
|
||||
|
||||
public override ExprType Type => this.FuncType.ReturnType;
|
||||
|
||||
public override Env Env => this.Args.Any() ? this.Args.Last().Env : this.Func.Env;
|
||||
|
||||
public override Boolean IsLValue => false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expr.name: Expr must be a struct or union.
|
||||
/// </summary>
|
||||
public sealed partial class Attribute : Expr {
|
||||
public Attribute(Expr expr, String name, ExprType type) {
|
||||
this.Expr = expr;
|
||||
this.Name = name;
|
||||
this.Type = type;
|
||||
}
|
||||
|
||||
public Expr Expr { get; }
|
||||
|
||||
public String Name { get; }
|
||||
|
||||
public override Env Env => this.Expr.Env;
|
||||
|
||||
public override ExprType Type { get; }
|
||||
|
||||
// You might want to think of some special case like this.
|
||||
// struct EvilStruct {
|
||||
// int a[10];
|
||||
// } evil;
|
||||
// evil.a <--- is this an lvalue?
|
||||
// Yes, it is. It cannot be assigned, but that's because of the wrong Type.
|
||||
public override Boolean IsLValue => this.Expr.IsLValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// &Expr: get the address of Expr.
|
||||
/// </summary>
|
||||
public sealed partial class Reference : Expr {
|
||||
public Reference(Expr expr) {
|
||||
this.Expr = expr;
|
||||
this.Type = new PointerType(expr.Type);
|
||||
}
|
||||
|
||||
public Expr Expr { get; }
|
||||
|
||||
public override Env Env => this.Expr.Env;
|
||||
|
||||
public override ExprType Type { get; }
|
||||
|
||||
// You might want to think of some special case like this.
|
||||
// int *a;
|
||||
// &(*a) = 3; // Is this okay?
|
||||
// But this should lead to an error: lvalue required.
|
||||
// The 'reference' operator only gets the 'current address'.
|
||||
public override Boolean IsLValue => false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// *Expr: Expr must be a pointer.
|
||||
///
|
||||
/// Arrays and functions are implicitly converted to pointers.
|
||||
///
|
||||
/// This is an lvalue, so it has an address.
|
||||
/// </summary>
|
||||
public sealed partial class Dereference : Expr {
|
||||
public Dereference(Expr expr, ExprType type) {
|
||||
this.Expr = expr;
|
||||
this.Type = type;
|
||||
}
|
||||
|
||||
public Expr Expr { get; }
|
||||
|
||||
public override Env Env => this.Expr.Env;
|
||||
|
||||
public override Boolean IsLValue => true;
|
||||
|
||||
public override ExprType Type { get; }
|
||||
}
|
||||
}
|
88
LibIFPSCC/ABT/ExternalDefinitions.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CodeGeneration;
|
||||
|
||||
namespace ABT {
|
||||
public class TranslnUnit {
|
||||
public TranslnUnit(List<Tuple<Env, IExternDecln>> _declns) {
|
||||
this.declns = _declns;
|
||||
}
|
||||
public readonly List<Tuple<Env, IExternDecln>> declns;
|
||||
|
||||
public void CodeGenerate(CGenState state) {
|
||||
foreach (Tuple<Env, IExternDecln> decln in this.declns) {
|
||||
decln.Item2.CGenDecln(decln.Item1, state);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public interface IExternDecln : IStoredLineInfo {
|
||||
void CGenDecln(Env env, CGenState state);
|
||||
}
|
||||
|
||||
public class FuncDef : IExternDecln {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
public FuncDef(String name, StorageClass scs, FunctionType type, Stmt stmt) {
|
||||
this.name = name;
|
||||
this.scs = scs;
|
||||
this.type = type;
|
||||
this.stmt = stmt;
|
||||
}
|
||||
|
||||
public override String ToString() => $"fn {this.name}: {this.type}";
|
||||
|
||||
public void CGenDecln(Env env, CGenState state) {
|
||||
|
||||
Env.Entry entry = env.Find(this.name).Value;
|
||||
switch (entry.Kind) {
|
||||
case Env.EntryKind.GLOBAL:
|
||||
switch (scs) {
|
||||
case StorageClass.AUTO:
|
||||
case StorageClass.EXTERN:
|
||||
case StorageClass.STATIC:
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException().Attach(this);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException().Attach(this);
|
||||
}
|
||||
state.CGenFuncStart(this.name, type, scs, this);
|
||||
|
||||
state.InFunction(GotoLabelsGrabber.GrabLabels(this.stmt));
|
||||
|
||||
this.stmt.CGenStmt(env, state);
|
||||
|
||||
//state.CGenLabel(state.ReturnLabel);
|
||||
|
||||
if (state.CurrInsns.LastOrDefault()?.OpCode.Code != IFPSLib.Emit.Code.Ret)
|
||||
{
|
||||
// remove any pop instructions before ret to save space, ret cleans up stack itself
|
||||
while (state.CurrInsns.LastOrDefault()?.OpCode.Code == IFPSLib.Emit.Code.Pop) state.CurrInsns.RemoveAt(state.CurrInsns.Count - 1);
|
||||
// if last insn is nop, replace it with ret, otherwise, append
|
||||
var ret = IFPSLib.Emit.Instruction.Create(IFPSLib.Emit.OpCodes.Ret);
|
||||
var last = state.CurrInsns.LastOrDefault();
|
||||
if (last?.OpCode.Code == IFPSLib.Emit.Code.Nop) last.Replace(ret);
|
||||
else state.CurrInsns.Add(ret);
|
||||
}
|
||||
|
||||
state.OutFunction();
|
||||
|
||||
}
|
||||
|
||||
public readonly String name;
|
||||
public readonly StorageClass scs;
|
||||
public readonly FunctionType type;
|
||||
public readonly Stmt stmt;
|
||||
}
|
||||
}
|
371
LibIFPSCC/ABT/Statements.cs
Normal file
@ -0,0 +1,371 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ABT {
|
||||
//public enum StmtKind {
|
||||
// GOTO,
|
||||
// LABELED,
|
||||
// CONT,
|
||||
// BREAK,
|
||||
// EXPR,
|
||||
// COMPOUND,
|
||||
// RETURN,
|
||||
// WHILE,
|
||||
// DO,
|
||||
// FOR,
|
||||
// SWITCH,
|
||||
// CASE,
|
||||
// DEFAULT,
|
||||
// IF,
|
||||
// IF_ELSE
|
||||
//}
|
||||
|
||||
public abstract partial class Stmt {
|
||||
public Env Env { get; }
|
||||
|
||||
public abstract void Accept(StmtVisitor visitor);
|
||||
|
||||
public virtual bool IsJump { get => false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Goto Statement
|
||||
/// </summary>
|
||||
public sealed partial class GotoStmt : Stmt {
|
||||
public GotoStmt(String label) {
|
||||
this.Label = label;
|
||||
}
|
||||
|
||||
public String Label { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
public override bool IsJump => true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Labeled Statement
|
||||
/// </summary>
|
||||
public sealed partial class LabeledStmt : Stmt {
|
||||
public LabeledStmt(String label, Stmt stmt) {
|
||||
this.Label = label;
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public String Label { get; }
|
||||
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
public override bool IsJump => Stmt.IsJump;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continue Statement
|
||||
/// </summary>
|
||||
public sealed partial class ContStmt : Stmt {
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
public override bool IsJump => true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Break Statement
|
||||
/// </summary>
|
||||
public sealed partial class BreakStmt : Stmt {
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
public override bool IsJump => true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expression Statement
|
||||
/// </summary>
|
||||
public sealed partial class ExprStmt : Stmt {
|
||||
public ExprStmt(Option<Expr> exprOpt) {
|
||||
this.ExprOpt = exprOpt;
|
||||
}
|
||||
|
||||
public Option<Expr> ExprOpt { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
public override bool IsJump => false;
|
||||
}
|
||||
|
||||
public sealed partial class CompoundStmt : Stmt {
|
||||
public CompoundStmt(List<Tuple<Env, Decln>> declns, List<Tuple<Env, Stmt>> stmts) {
|
||||
this.Declns = declns;
|
||||
this.Stmts = stmts;
|
||||
}
|
||||
|
||||
public List<Tuple<Env, Decln>> Declns { get; }
|
||||
|
||||
public List<Tuple<Env, Stmt>> Stmts { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
public override bool IsJump => Declns.Count == 0 && Stmts.Count >= 1 && Stmts.First().Item2.IsJump;
|
||||
}
|
||||
|
||||
public sealed partial class ReturnStmt : Stmt {
|
||||
public ReturnStmt(Option<Expr> exprOpt) {
|
||||
this.ExprOpt = exprOpt;
|
||||
}
|
||||
|
||||
public Option<Expr> ExprOpt { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// While Statement
|
||||
///
|
||||
/// while (Cond) {
|
||||
/// Body
|
||||
/// }
|
||||
///
|
||||
/// Cond must be of scalar Type
|
||||
/// </summary>
|
||||
// +--> start: continue:
|
||||
// | test Cond
|
||||
// | jz finish --+
|
||||
// | Body |
|
||||
// +------- jmp start |
|
||||
// finish: <-------+
|
||||
//
|
||||
public sealed partial class WhileStmt : Stmt {
|
||||
public WhileStmt(Expr cond, Stmt body) {
|
||||
if (!cond.Type.IsScalar) {
|
||||
throw new InvalidProgramException().Attach(cond);
|
||||
}
|
||||
this.Cond = cond;
|
||||
this.Body = body;
|
||||
}
|
||||
|
||||
public Expr Cond { get; }
|
||||
|
||||
public Stmt Body { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do-while Stmt
|
||||
///
|
||||
/// do {
|
||||
/// Body
|
||||
/// } while (Cond);
|
||||
///
|
||||
/// Cond must be of scalar Type
|
||||
/// </summary>
|
||||
// +--> start:
|
||||
// | Body
|
||||
// | continue:
|
||||
// | test Cond
|
||||
// +------- jnz start
|
||||
// finish:
|
||||
public sealed partial class DoWhileStmt : Stmt {
|
||||
public DoWhileStmt(Stmt body, Expr cond) {
|
||||
this.Body = body;
|
||||
this.Cond = cond;
|
||||
}
|
||||
|
||||
public Stmt Body { get; }
|
||||
|
||||
public Expr Cond { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
public override bool IsJump => Body.IsJump;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// for (Init; Cond; Loop) {
|
||||
/// Body
|
||||
/// }
|
||||
///
|
||||
/// Cond must be scalar
|
||||
/// </summary>
|
||||
//
|
||||
// Init
|
||||
// +--> start:
|
||||
// | test Cond
|
||||
// | jz finish --+
|
||||
// | Body |
|
||||
// | continue: |
|
||||
// | Loop |
|
||||
// +------- jmp start |
|
||||
// finish: <-------+
|
||||
//
|
||||
public sealed partial class ForStmt : Stmt {
|
||||
public ForStmt(Option<Expr> init, Option<Expr> cond, Option<Expr> loop, Stmt body) {
|
||||
this.Init = init;
|
||||
this.Cond = cond;
|
||||
this.Loop = loop;
|
||||
this.Body = body;
|
||||
}
|
||||
|
||||
public Option<Expr> Init { get; }
|
||||
|
||||
public Option<Expr> Cond { get; }
|
||||
|
||||
public Option<Expr> Loop { get; }
|
||||
|
||||
public Stmt Body { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switch Statement
|
||||
/// </summary>
|
||||
//
|
||||
// cmp Cond, value1
|
||||
// je case1
|
||||
// cmp Cond, value2
|
||||
// je case2
|
||||
// ...
|
||||
// cmp Cond, value_n
|
||||
// je case_n
|
||||
// jmp default # if no default, then default = finish
|
||||
//
|
||||
// case1:
|
||||
// stmt
|
||||
// case2:
|
||||
// stmt
|
||||
// ...
|
||||
// case_n:
|
||||
// stmt
|
||||
// finish:
|
||||
public sealed partial class SwitchStmt : Stmt {
|
||||
public SwitchStmt(Expr expr, Stmt stmt) {
|
||||
this.Expr = expr;
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public Expr Expr { get; }
|
||||
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Case Statement
|
||||
/// </summary>
|
||||
public sealed partial class CaseStmt : Stmt {
|
||||
public CaseStmt(Int32 value, Stmt stmt) {
|
||||
this.Value = value;
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public Int32 Value { get; }
|
||||
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
public override bool IsJump => Stmt.IsJump;
|
||||
}
|
||||
|
||||
public sealed partial class DefaultStmt : Stmt {
|
||||
public DefaultStmt(Stmt stmt) {
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
public override bool IsJump => Stmt.IsJump;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If Statement: if (Cond) stmt;
|
||||
/// If Cond is non-zero, stmt is executed.
|
||||
///
|
||||
/// Cond must be arithmetic or pointer Type.
|
||||
/// </summary>
|
||||
// test Cond
|
||||
// +------- jz finish
|
||||
// | Body
|
||||
// +--> finish:
|
||||
public sealed partial class IfStmt : Stmt {
|
||||
public IfStmt(Expr cond, Stmt stmt) {
|
||||
this.Cond = cond;
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public Expr Cond { get; }
|
||||
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If-else Statement
|
||||
/// if (Cond) {
|
||||
/// true_stmt
|
||||
/// } else {
|
||||
/// false_stmt
|
||||
/// }
|
||||
/// </summary>
|
||||
///
|
||||
// test Cond
|
||||
// +------- jz false
|
||||
// | true_stmt
|
||||
// | jmp finish --+
|
||||
// +--> false: |
|
||||
// false_stmt |
|
||||
// finish: <--------+
|
||||
//
|
||||
public sealed partial class IfElseStmt : Stmt {
|
||||
public IfElseStmt(Expr cond, Stmt trueStmt, Stmt falseStmt) {
|
||||
this.Cond = cond;
|
||||
this.TrueStmt = trueStmt;
|
||||
this.FalseStmt = falseStmt;
|
||||
}
|
||||
|
||||
public Expr Cond { get; }
|
||||
|
||||
public Stmt TrueStmt { get; }
|
||||
|
||||
public Stmt FalseStmt { get; }
|
||||
|
||||
public override void Accept(StmtVisitor visitor) {
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
}
|
146
LibIFPSCC/ABT/StmtVisitor.cs
Normal file
@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ABT {
|
||||
public abstract class StmtVisitor {
|
||||
public virtual void Visit(Stmt stmt) {}
|
||||
public virtual void Visit(GotoStmt stmt) {}
|
||||
public virtual void Visit(LabeledStmt stmt) {}
|
||||
public virtual void Visit(ContStmt stmt) {}
|
||||
public virtual void Visit(BreakStmt stmt) {}
|
||||
public virtual void Visit(ExprStmt stmt) {}
|
||||
public virtual void Visit(CompoundStmt stmt) {}
|
||||
public virtual void Visit(ReturnStmt stmt) {}
|
||||
public virtual void Visit(WhileStmt stmt) {}
|
||||
public virtual void Visit(DoWhileStmt stmt) {}
|
||||
public virtual void Visit(ForStmt stmt) {}
|
||||
public virtual void Visit(SwitchStmt stmt) {}
|
||||
public virtual void Visit(CaseStmt stmt) {}
|
||||
public virtual void Visit(DefaultStmt stmt) {}
|
||||
public virtual void Visit(IfStmt stmt) {}
|
||||
public virtual void Visit(IfElseStmt stmt) {}
|
||||
}
|
||||
|
||||
public class CaseLabelsGrabber : StmtVisitor {
|
||||
private readonly List<Int32> _labels = new List<Int32>();
|
||||
public IReadOnlyList<Int32> Labels => this._labels;
|
||||
|
||||
public static IReadOnlyList<Int32> GrabLabels(SwitchStmt stmt) {
|
||||
CaseLabelsGrabber grabber = new CaseLabelsGrabber();
|
||||
stmt.Stmt.Accept(grabber);
|
||||
return grabber.Labels;
|
||||
}
|
||||
|
||||
public override void Visit(Stmt stmt) {
|
||||
throw new InvalidOperationException("Cannot visit abstract Stmt");
|
||||
}
|
||||
|
||||
public override void Visit(GotoStmt stmt) {}
|
||||
|
||||
public override void Visit(LabeledStmt stmt) =>
|
||||
stmt.Stmt.Accept(this);
|
||||
|
||||
public override void Visit(ContStmt stmt) {}
|
||||
|
||||
public override void Visit(BreakStmt stmt) {}
|
||||
|
||||
public override void Visit(ExprStmt stmt) {}
|
||||
|
||||
public override void Visit(CompoundStmt stmt) =>
|
||||
stmt.Stmts.ForEach(_ => _.Item2.Accept(this));
|
||||
|
||||
public override void Visit(ReturnStmt stmt) {}
|
||||
|
||||
public override void Visit(WhileStmt stmt) =>
|
||||
stmt.Body.Accept(this);
|
||||
|
||||
public override void Visit(DoWhileStmt stmt) =>
|
||||
stmt.Body.Accept(this);
|
||||
|
||||
public override void Visit(ForStmt stmt) =>
|
||||
stmt.Body.Accept(this);
|
||||
|
||||
public override void Visit(SwitchStmt stmt) {
|
||||
// Must ignore this.
|
||||
}
|
||||
|
||||
public override void Visit(CaseStmt stmt) {
|
||||
// Record the Value.
|
||||
this._labels.Add(stmt.Value);
|
||||
stmt.Stmt.Accept(this);
|
||||
}
|
||||
|
||||
public override void Visit(DefaultStmt stmt) =>
|
||||
stmt.Stmt.Accept(this);
|
||||
|
||||
public override void Visit(IfStmt stmt) =>
|
||||
stmt.Stmt.Accept(this);
|
||||
|
||||
public override void Visit(IfElseStmt stmt) {
|
||||
stmt.TrueStmt.Accept(this);
|
||||
stmt.FalseStmt.Accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class GotoLabelsGrabber : StmtVisitor {
|
||||
private readonly List<String> _labels = new List<String>();
|
||||
public IReadOnlyList<String> Labels => this._labels;
|
||||
|
||||
public static IReadOnlyList<String> GrabLabels(Stmt stmt) {
|
||||
GotoLabelsGrabber grabber = new GotoLabelsGrabber();
|
||||
stmt.Accept(grabber);
|
||||
return grabber.Labels;
|
||||
}
|
||||
|
||||
public override void Visit(Stmt stmt) {
|
||||
throw new InvalidOperationException("Cannot visit abstract Stmt");
|
||||
}
|
||||
|
||||
public override void Visit(GotoStmt stmt) { }
|
||||
|
||||
public override void Visit(LabeledStmt stmt) {
|
||||
this._labels.Add(stmt.Label);
|
||||
stmt.Stmt.Accept(this);
|
||||
}
|
||||
|
||||
public override void Visit(ContStmt stmt) { }
|
||||
|
||||
public override void Visit(BreakStmt stmt) { }
|
||||
|
||||
public override void Visit(ExprStmt stmt) { }
|
||||
|
||||
public override void Visit(CompoundStmt stmt) =>
|
||||
stmt.Stmts.ForEach(_ => _.Item2.Accept(this));
|
||||
|
||||
public override void Visit(ReturnStmt stmt) { }
|
||||
|
||||
public override void Visit(WhileStmt stmt) =>
|
||||
stmt.Body.Accept(this);
|
||||
|
||||
public override void Visit(DoWhileStmt stmt) =>
|
||||
stmt.Body.Accept(this);
|
||||
|
||||
public override void Visit(ForStmt stmt) =>
|
||||
stmt.Body.Accept(this);
|
||||
|
||||
public override void Visit(SwitchStmt stmt) {
|
||||
stmt.Stmt.Accept(this);
|
||||
}
|
||||
|
||||
public override void Visit(CaseStmt stmt) {
|
||||
stmt.Stmt.Accept(this);
|
||||
}
|
||||
|
||||
public override void Visit(DefaultStmt stmt) =>
|
||||
stmt.Stmt.Accept(this);
|
||||
|
||||
public override void Visit(IfStmt stmt) =>
|
||||
stmt.Stmt.Accept(this);
|
||||
|
||||
public override void Visit(IfElseStmt stmt) {
|
||||
stmt.TrueStmt.Accept(this);
|
||||
stmt.FalseStmt.Accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1009
LibIFPSCC/ABT/TypeCast.cs
Normal file
164
LibIFPSCC/ABT/Types/TypePrinter.cs
Normal file
@ -0,0 +1,164 @@
|
||||
using CodeGeneration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace ABT {
|
||||
public abstract partial class ExprType {
|
||||
public virtual Int32 Precedence => 0;
|
||||
|
||||
public abstract String Decl(String name, Int32 precedence);
|
||||
|
||||
public String Decl(String name) => this.Decl(name, 0);
|
||||
|
||||
public String Decl() => this.Decl("");
|
||||
|
||||
public abstract IFPSLib.Types.IType Emit(CGenState state);
|
||||
}
|
||||
|
||||
public partial class VoidType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}void {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class CharType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}char {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class UCharType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}unsigned char {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class ShortType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}short {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class UShortType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}unsigned short {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class LongType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}long {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class ULongType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{DumpQualifiers()}unsigned long {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class S64Type
|
||||
{
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}__int64 {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class U64Type
|
||||
{
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{DumpQualifiers()}unsigned __int64 {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class FloatType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}float {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class DoubleType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}double {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class PointerType {
|
||||
public override String Decl(String name, Int32 precedence) {
|
||||
if (precedence > this.Precedence) {
|
||||
name = $"({name})";
|
||||
}
|
||||
return this.RefType.Decl($"*{this.DumpQualifiers()}{name}", this.Precedence);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class IncompleteArrayType {
|
||||
public override String Decl(String name, Int32 precedence) {
|
||||
if (precedence > this.Precedence) {
|
||||
name = $"({name})";
|
||||
}
|
||||
return this.ElemType.Decl($"{name}[]", this.Precedence);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ArrayType {
|
||||
public override String Decl(String name, Int32 precedence) {
|
||||
if (precedence > this.Precedence) {
|
||||
name = $"({name})";
|
||||
}
|
||||
return this.ElemType.Decl($"{name}[{this.NumElems}]", this.Precedence);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class UnicodeStringType
|
||||
{
|
||||
public override string Decl(string name, int precedence)
|
||||
{
|
||||
return $"{this.DumpQualifiers()}unsigned __String {name}".TrimEnd(' ');
|
||||
}
|
||||
}
|
||||
|
||||
public partial class AnsiStringType
|
||||
{
|
||||
public override string Decl(string name, int precedence)
|
||||
{
|
||||
return $"{this.DumpQualifiers()}__String {name}".TrimEnd(' ');
|
||||
}
|
||||
}
|
||||
|
||||
public partial class StructOrUnionType {
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}{this._layout.TypeName} {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class FunctionType {
|
||||
public override String Decl(String name, Int32 precedence) {
|
||||
if (precedence > this.Precedence) {
|
||||
name = $"({name})";
|
||||
}
|
||||
|
||||
String str = "";
|
||||
if (this.Args.Count == 0) {
|
||||
if (this.HasVarArgs) {
|
||||
str = "(...)";
|
||||
} else {
|
||||
str = "(void)";
|
||||
}
|
||||
} else {
|
||||
str = this.Args[0].type.Decl();
|
||||
for (Int32 i = 1; i < this.Args.Count; ++i) {
|
||||
str += $", {this.Args[i].type.Decl()}";
|
||||
}
|
||||
if (this.HasVarArgs) {
|
||||
str += ", ...";
|
||||
}
|
||||
str = $"({str})";
|
||||
}
|
||||
|
||||
return this.ReturnType.Decl($"{name}{str}", this.Precedence);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ComInterfaceType
|
||||
{
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}__interface({this.InterfaceGuid}) {name}".TrimEnd(' ');
|
||||
}
|
||||
|
||||
public partial class ComVariantType
|
||||
{
|
||||
public override String Decl(String name, Int32 precedence) =>
|
||||
$"{this.DumpQualifiers()}__variant {name}".TrimEnd(' ');
|
||||
}
|
||||
}
|
1024
LibIFPSCC/ABT/Types/Types.cs
Normal file
133
LibIFPSCC/ABT/UnaryOperators.cs
Normal file
@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using CodeGeneration;
|
||||
|
||||
namespace ABT {
|
||||
|
||||
// IncDecExpr
|
||||
// |
|
||||
// +-- PostIncrement, PostDecrement, PreIncrement, PreDecrement
|
||||
|
||||
public abstract partial class IncDecExpr : Expr {
|
||||
protected IncDecExpr(Expr expr) {
|
||||
if (!(expr.Type is ScalarType)) {
|
||||
throw new InvalidOperationException("Only supports scalars.").Attach(expr);
|
||||
}
|
||||
this.Expr = expr;
|
||||
}
|
||||
|
||||
public Expr Expr { get; }
|
||||
|
||||
public override Env Env => this.Expr.Env;
|
||||
|
||||
public override Boolean IsLValue => false;
|
||||
|
||||
public override ExprType Type => this.Expr.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expr++: must be integral, float or pointer.
|
||||
///
|
||||
/// If Expr is an array, it is converted to a pointer in semantic analysis.
|
||||
/// </summary>
|
||||
public sealed partial class PostIncrement : IncDecExpr {
|
||||
public PostIncrement(Expr expr)
|
||||
: base(expr) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expr--: must be a scalar
|
||||
/// </summary>
|
||||
public sealed partial class PostDecrement : IncDecExpr {
|
||||
public PostDecrement(Expr expr)
|
||||
: base(expr) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ++Expr: must be a scalar
|
||||
/// </summary>
|
||||
public sealed partial class PreIncrement : IncDecExpr {
|
||||
public PreIncrement(Expr expr)
|
||||
: base(expr) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// --Expr: must be a scalar
|
||||
/// </summary>
|
||||
public sealed partial class PreDecrement : IncDecExpr {
|
||||
public PreDecrement(Expr expr)
|
||||
: base(expr) { }
|
||||
}
|
||||
|
||||
public abstract partial class UnaryArithOp : Expr {
|
||||
protected UnaryArithOp(Expr expr) {
|
||||
this.Expr = expr;
|
||||
}
|
||||
|
||||
public Expr Expr { get; }
|
||||
|
||||
public override Env Env => this.Expr.Env;
|
||||
|
||||
public override Boolean IsLValue => false;
|
||||
|
||||
public override abstract ExprType Type { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// -Expr: only takes arithmetic Type.
|
||||
///
|
||||
/// After semantic analysis, only the following 4 types are possible:
|
||||
/// 1) long
|
||||
/// 2) ulong
|
||||
/// 3) float
|
||||
/// 4) double
|
||||
/// </summary>
|
||||
public sealed partial class Negative : UnaryArithOp {
|
||||
public Negative(Expr expr)
|
||||
: base(expr) { }
|
||||
|
||||
public override ExprType Type => this.Expr.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ~Expr: only takes integral Type.
|
||||
///
|
||||
/// After semantic analysis, only the following 2 types are possible:
|
||||
/// 1) long
|
||||
/// 2) ulong
|
||||
/// </summary>
|
||||
public sealed partial class BitwiseNot : UnaryArithOp {
|
||||
public BitwiseNot(Expr expr)
|
||||
: base(expr) {
|
||||
if (!(expr.Type is LongType || expr.Type is ULongType)) {
|
||||
throw new InvalidOperationException("Invalid operand type.").Attach(expr);
|
||||
}
|
||||
}
|
||||
|
||||
public override ExprType Type => this.Expr.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// !Expr: only takes scalar Type.
|
||||
///
|
||||
/// After semantic analysis, only the following 4 types are possible:
|
||||
/// 1) long
|
||||
/// 2) ulong
|
||||
/// 3) float
|
||||
/// 4) double
|
||||
///
|
||||
/// Pointers are converted to ulongs.
|
||||
/// </summary>
|
||||
public sealed partial class LogicalNot : UnaryArithOp {
|
||||
public LogicalNot(Expr expr)
|
||||
: base(expr) {
|
||||
if (!(expr.Type is LongType || expr.Type is ULongType
|
||||
|| expr.Type is FloatType || expr.Type is DoubleType)) {
|
||||
throw new InvalidOperationException("Invalid operand type.").Attach(expr);
|
||||
}
|
||||
}
|
||||
|
||||
private static ExprType _type = new LongType(true);
|
||||
public override ExprType Type => _type;
|
||||
}
|
||||
}
|
39
LibIFPSCC/ABT/Utils.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ABT {
|
||||
public class Utils {
|
||||
|
||||
// class StoreEntry
|
||||
// ================
|
||||
// the inner storage of entries
|
||||
//
|
||||
public class StoreEntry {
|
||||
public StoreEntry(String name, ExprType type, Int32 offset, bool arg = false) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.offset = offset;
|
||||
isArg = arg;
|
||||
}
|
||||
public readonly String name;
|
||||
public readonly ExprType type;
|
||||
public readonly Int32 offset;
|
||||
public readonly bool isArg;
|
||||
}
|
||||
|
||||
public static Int32 RoundUp(Int32 value, Int32 alignment) {
|
||||
return (value + alignment - 1) & ~(alignment- 1);
|
||||
}
|
||||
|
||||
public static Tuple<Int32, IReadOnlyList<Int32>> PackArguments(IReadOnlyList<ExprType> types) {
|
||||
List<Int32> offsets = new List<Int32>();
|
||||
Int32 offset = 0;
|
||||
foreach (ExprType type in types) {
|
||||
offsets.Add(offset);
|
||||
offset++;
|
||||
}
|
||||
return new Tuple<Int32, IReadOnlyList<Int32>>(offset, offsets);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
150
LibIFPSCC/AST/AssignmentOperators.cs
Normal file
@ -0,0 +1,150 @@
|
||||
namespace AST {
|
||||
using static SemanticAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Assignment: Left = Right
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Left must be a lvalue, but this check is left to the cgen phase.
|
||||
/// </remarks>
|
||||
public sealed class Assignment : Expr {
|
||||
private Assignment(Expr left, Expr right) {
|
||||
this.Left = left;
|
||||
this.Right = right;
|
||||
}
|
||||
|
||||
public Expr Left { get; }
|
||||
|
||||
public Expr Right { get; }
|
||||
|
||||
public static Expr Create(Expr left, Expr right) =>
|
||||
new Assignment(left, right);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
var left = SemantExpr(this.Left, ref env);
|
||||
var right = SemantExpr(this.Right, ref env);
|
||||
right = ABT.TypeCast.MakeCast(right, left.Type);
|
||||
return new ABT.Assign(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
public abstract class AssignOp : Expr {
|
||||
protected AssignOp(Expr left, Expr right) {
|
||||
this.Left = left;
|
||||
this.Right = right;
|
||||
}
|
||||
|
||||
public Expr Left { get; }
|
||||
|
||||
public Expr Right { get; }
|
||||
|
||||
public abstract Expr ConstructBinaryOp();
|
||||
|
||||
protected override sealed ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info) =>
|
||||
Assignment.Create(this.Left, ConstructBinaryOp()).GetExpr(env, info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MultAssign: a *= b
|
||||
/// </summary>
|
||||
public sealed class MultAssign : AssignOp {
|
||||
private MultAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new MultAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => Multiply.Create(this.Left, this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DivAssign: a /= b
|
||||
/// </summary>
|
||||
public sealed class DivAssign : AssignOp {
|
||||
private DivAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new DivAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => Divide.Create(this.Left, this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ModAssign: a %= b
|
||||
/// </summary>
|
||||
public sealed class ModAssign : AssignOp {
|
||||
private ModAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new ModAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => Modulo.Create(this.Left, this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AddAssign: a += b
|
||||
/// </summary>
|
||||
public sealed class AddAssign : AssignOp {
|
||||
private AddAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new AddAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => Add.Create(this.Left, this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SubAssign: a -= b
|
||||
/// </summary>
|
||||
public sealed class SubAssign : AssignOp {
|
||||
private SubAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new SubAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => Sub.Create(this.Left, this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LShiftAssign: a <<= b
|
||||
/// </summary>
|
||||
public sealed class LShiftAssign : AssignOp {
|
||||
private LShiftAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new LShiftAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => LShift.Create(this.Left, this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RShiftAssign: a >>= b
|
||||
/// </summary>
|
||||
public sealed class RShiftAssign : AssignOp {
|
||||
private RShiftAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new RShiftAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => RShift.Create(this.Left, this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BitwiseAndAssign: a &= b
|
||||
/// </summary>
|
||||
public sealed class BitwiseAndAssign : AssignOp {
|
||||
private BitwiseAndAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new BitwiseAndAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => BitwiseAnd.Create(this.Left, this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// XorAssign: a ^= b
|
||||
/// </summary>
|
||||
public sealed class XorAssign : AssignOp {
|
||||
private XorAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new XorAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => Xor.Create(this.Left, this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BitwiseOrAssign: a |= b
|
||||
/// </summary>
|
||||
public sealed class BitwiseOrAssign : AssignOp {
|
||||
private BitwiseOrAssign(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new BitwiseOrAssign(left, right);
|
||||
public override Expr ConstructBinaryOp() => BitwiseOr.Create(this.Left, this.Right);
|
||||
}
|
||||
}
|
791
LibIFPSCC/AST/BinaryOperators.cs
Normal file
@ -0,0 +1,791 @@
|
||||
using System;
|
||||
|
||||
namespace AST {
|
||||
using static SemanticAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Binary operator: Left op Right
|
||||
/// </summary>
|
||||
public abstract class BinaryOp : Expr {
|
||||
protected BinaryOp(Expr left, Expr right) {
|
||||
this.Left = left;
|
||||
this.Right = right;
|
||||
}
|
||||
public Expr Left { get; }
|
||||
public Expr Right { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binary integral operator: takes in two integrals, returns an integer.
|
||||
/// </summary>
|
||||
public abstract class BinaryIntegralOp : BinaryOp {
|
||||
protected BinaryIntegralOp(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
|
||||
public abstract Int32 OperateLong(Int32 left, Int32 right);
|
||||
public abstract UInt32 OperateULong(UInt32 left, UInt32 right);
|
||||
public abstract long OperateS64(long left, long right);
|
||||
public abstract ulong OperateU64(ulong left, ulong right);
|
||||
|
||||
public virtual string OperateString(string left, string right)
|
||||
{
|
||||
throw new InvalidOperationException("Does not support string").Attach(Left);
|
||||
}
|
||||
public abstract ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
|
||||
// 1. semant operands
|
||||
var left = SemantExpr(this.Left, ref env);
|
||||
var right = SemantExpr(this.Right, ref env);
|
||||
|
||||
// 2. perform usual arithmetic conversion
|
||||
Tuple<ABT.Expr, ABT.Expr, ABT.ExprTypeKind> castReturn = ABT.TypeCast.UsualArithmeticConversion(left, right);
|
||||
left = castReturn.Item1;
|
||||
right = castReturn.Item2;
|
||||
var typeKind = castReturn.Item3;
|
||||
|
||||
var isConst = left.Type.IsConst || right.Type.IsConst;
|
||||
var isVolatile = left.Type.IsVolatile || right.Type.IsVolatile;
|
||||
|
||||
// 3. if both operands are constants
|
||||
if (left.IsConstExpr && right.IsConstExpr) {
|
||||
switch (typeKind) {
|
||||
case ABT.ExprTypeKind.ULONG:
|
||||
return new ABT.ConstULong(OperateULong(((ABT.ConstULong)left).Value, ((ABT.ConstULong)right).Value), env);
|
||||
case ABT.ExprTypeKind.LONG:
|
||||
return new ABT.ConstLong(OperateLong(((ABT.ConstLong)left).Value, ((ABT.ConstLong)right).Value), env);
|
||||
case ABT.ExprTypeKind.U64:
|
||||
return new ABT.ConstU64(OperateU64(((ABT.ConstU64)left).Value, ((ABT.ConstU64)right).Value), env);
|
||||
case ABT.ExprTypeKind.S64:
|
||||
return new ABT.ConstS64(OperateS64(((ABT.ConstS64)left).Value, ((ABT.ConstS64)right).Value), env);
|
||||
case ABT.ExprTypeKind.ANSI_STRING:
|
||||
return new ABT.ConstStringLiteral(OperateString(((ABT.ConstStringLiteral)left).Value, ((ABT.ConstStringLiteral)right).Value), env);
|
||||
case ABT.ExprTypeKind.UNICODE_STRING:
|
||||
return new ABT.ConstUnicodeStringLiteral(OperateString(((ABT.ConstUnicodeStringLiteral)left).Value, ((ABT.ConstUnicodeStringLiteral)right).Value), env);
|
||||
default:
|
||||
throw new InvalidOperationException("Expected long or unsigned long.").Attach(Left);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. if not both operands are constants
|
||||
switch (typeKind) {
|
||||
case ABT.ExprTypeKind.ULONG:
|
||||
return ConstructExpr(left, right, new ABT.ULongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.LONG:
|
||||
return ConstructExpr(left, right, new ABT.LongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.U64:
|
||||
return ConstructExpr(left, right, new ABT.U64Type(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.S64:
|
||||
return ConstructExpr(left, right, new ABT.S64Type(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.ANSI_STRING:
|
||||
return ConstructExpr(left, right, new ABT.AnsiStringType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.UNICODE_STRING:
|
||||
return ConstructExpr(left, right, new ABT.UnicodeStringType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.COM_VARIANT:
|
||||
return ConstructExpr(left, right, new ABT.ComVariantType(isConst, isVolatile));
|
||||
default:
|
||||
throw new InvalidOperationException("Expected long or unsigned long.").Attach(Left);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binary integral operator: takes in two int/uint/float/double, returns an int/uint/float/double.
|
||||
/// </summary>
|
||||
public abstract class BinaryArithmeticOp : BinaryIntegralOp {
|
||||
protected BinaryArithmeticOp(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
|
||||
public abstract Single OperateFloat(Single left, Single right);
|
||||
public abstract Double OperateDouble(Double left, Double right);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
|
||||
// 1. semant operands
|
||||
var left = SemantExpr(this.Left, ref env);
|
||||
var right = SemantExpr(this.Right, ref env);
|
||||
|
||||
// 2. perform usual arithmetic conversion
|
||||
Tuple<ABT.Expr, ABT.Expr, ABT.ExprTypeKind> castReturn = ABT.TypeCast.UsualArithmeticConversion(left, right);
|
||||
left = castReturn.Item1;
|
||||
right = castReturn.Item2;
|
||||
var typeKind = castReturn.Item3;
|
||||
|
||||
var isConst = left.Type.IsConst || right.Type.IsConst;
|
||||
var isVolatile = left.Type.IsVolatile || right.Type.IsVolatile;
|
||||
|
||||
// 3. if both operands are constants
|
||||
if (left.IsConstExpr && right.IsConstExpr) {
|
||||
switch (typeKind) {
|
||||
case ABT.ExprTypeKind.DOUBLE:
|
||||
return new ABT.ConstDouble(OperateDouble(((ABT.ConstDouble)left).Value, ((ABT.ConstDouble)right).Value), env);
|
||||
case ABT.ExprTypeKind.FLOAT:
|
||||
return new ABT.ConstFloat(OperateFloat(((ABT.ConstFloat)left).Value, ((ABT.ConstFloat)right).Value), env);
|
||||
case ABT.ExprTypeKind.ULONG:
|
||||
return new ABT.ConstULong(OperateULong(((ABT.ConstULong)left).Value, ((ABT.ConstULong)right).Value), env);
|
||||
case ABT.ExprTypeKind.LONG:
|
||||
return new ABT.ConstLong(OperateLong(((ABT.ConstLong)left).Value, ((ABT.ConstLong)right).Value), env);
|
||||
case ABT.ExprTypeKind.U64:
|
||||
return new ABT.ConstU64(OperateU64(((ABT.ConstU64)left).Value, ((ABT.ConstU64)right).Value), env);
|
||||
case ABT.ExprTypeKind.S64:
|
||||
return new ABT.ConstS64(OperateS64(((ABT.ConstS64)left).Value, ((ABT.ConstS64)right).Value), env);
|
||||
case ABT.ExprTypeKind.ANSI_STRING:
|
||||
return new ABT.ConstStringLiteral(OperateString(((ABT.ConstStringLiteral)left).Value, ((ABT.ConstStringLiteral)right).Value), env);
|
||||
case ABT.ExprTypeKind.UNICODE_STRING:
|
||||
return new ABT.ConstUnicodeStringLiteral(OperateString(((ABT.ConstUnicodeStringLiteral)left).Value, ((ABT.ConstUnicodeStringLiteral)right).Value), env);
|
||||
default:
|
||||
throw new InvalidOperationException("Expected arithmetic Type.").Attach(Left);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. if not both operands are constants
|
||||
switch (typeKind) {
|
||||
case ABT.ExprTypeKind.DOUBLE:
|
||||
return ConstructExpr(left, right, new ABT.DoubleType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.FLOAT:
|
||||
return ConstructExpr(left, right, new ABT.FloatType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.ULONG:
|
||||
return ConstructExpr(left, right, new ABT.ULongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.LONG:
|
||||
return ConstructExpr(left, right, new ABT.LongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.U64:
|
||||
return ConstructExpr(left, right, new ABT.U64Type(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.S64:
|
||||
return ConstructExpr(left, right, new ABT.S64Type(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.ANSI_STRING:
|
||||
return ConstructExpr(left, right, new ABT.AnsiStringType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.UNICODE_STRING:
|
||||
return ConstructExpr(left, right, new ABT.UnicodeStringType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.COM_VARIANT:
|
||||
return ConstructExpr(left, right, new ABT.ComVariantType(isConst, isVolatile));
|
||||
default:
|
||||
throw new InvalidOperationException("Expected arithmetic Type.").Attach(Left);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binary logical operator: first turn pointers to ulongs, then always returns long.
|
||||
/// </summary>
|
||||
public abstract class BinaryLogicalOp : BinaryOp {
|
||||
protected BinaryLogicalOp(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
|
||||
public abstract Int32 OperateLong(Int32 left, Int32 right);
|
||||
public abstract Int32 OperateULong(UInt32 left, UInt32 right);
|
||||
public abstract int OperateS64(long left, long right);
|
||||
public abstract int OperateU64(ulong left, ulong right);
|
||||
public abstract Int32 OperateFloat(Single left, Single right);
|
||||
public abstract Int32 OperateDouble(Double left, Double right);
|
||||
|
||||
public abstract ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
|
||||
// 1. semant operands
|
||||
var left = SemantExpr(this.Left, ref env);
|
||||
var right = SemantExpr(this.Right, ref env);
|
||||
|
||||
// 2. perform usual scalar conversion
|
||||
Tuple<ABT.Expr, ABT.Expr, ABT.ExprTypeKind> castReturn = ABT.TypeCast.UsualScalarConversion(left, right);
|
||||
left = castReturn.Item1;
|
||||
right = castReturn.Item2;
|
||||
var typeKind = castReturn.Item3;
|
||||
|
||||
var isConst = left.Type.IsConst || right.Type.IsConst;
|
||||
var isVolatile = left.Type.IsVolatile || right.Type.IsVolatile;
|
||||
|
||||
// 3. if both operands are constants
|
||||
if (left.IsConstExpr && right.IsConstExpr) {
|
||||
switch (typeKind) {
|
||||
case ABT.ExprTypeKind.DOUBLE:
|
||||
return new ABT.ConstLong(OperateDouble(((ABT.ConstDouble)left).Value, ((ABT.ConstDouble)right).Value), env);
|
||||
case ABT.ExprTypeKind.FLOAT:
|
||||
return new ABT.ConstLong(OperateFloat(((ABT.ConstFloat)left).Value, ((ABT.ConstFloat)right).Value), env);
|
||||
case ABT.ExprTypeKind.ULONG:
|
||||
return new ABT.ConstLong(OperateULong(((ABT.ConstULong)left).Value, ((ABT.ConstULong)right).Value), env);
|
||||
case ABT.ExprTypeKind.LONG:
|
||||
return new ABT.ConstLong(OperateLong(((ABT.ConstLong)left).Value, ((ABT.ConstLong)right).Value), env);
|
||||
case ABT.ExprTypeKind.U64:
|
||||
return new ABT.ConstLong(OperateU64(((ABT.ConstU64)left).Value, ((ABT.ConstU64)right).Value), env);
|
||||
case ABT.ExprTypeKind.S64:
|
||||
return new ABT.ConstLong(OperateS64(((ABT.ConstS64)left).Value, ((ABT.ConstS64)right).Value), env);
|
||||
default:
|
||||
throw new InvalidOperationException("Expected arithmetic Type.").Attach(Left);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. if not both operands are constants
|
||||
switch (typeKind) {
|
||||
case ABT.ExprTypeKind.DOUBLE:
|
||||
return ConstructExpr(left, right, new ABT.LongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.FLOAT:
|
||||
return ConstructExpr(left, right, new ABT.LongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.ULONG:
|
||||
return ConstructExpr(left, right, new ABT.LongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.LONG:
|
||||
return ConstructExpr(left, right, new ABT.LongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.U64:
|
||||
return ConstructExpr(left, right, new ABT.LongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.S64:
|
||||
return ConstructExpr(left, right, new ABT.LongType(isConst, isVolatile));
|
||||
case ABT.ExprTypeKind.COM_VARIANT:
|
||||
return ConstructExpr(left, right, new ABT.ComVariantType(isConst, isVolatile));
|
||||
default:
|
||||
throw new InvalidOperationException("Expected arithmetic Type.").Attach(Left);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplication: perform usual arithmetic conversion.
|
||||
/// </summary>
|
||||
public sealed class Multiply : BinaryArithmeticOp {
|
||||
private Multiply(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new Multiply(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left * right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => left * right;
|
||||
public override long OperateS64(long left, long right) => left * right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => left * right;
|
||||
public override Single OperateFloat(Single left, Single right) => left * right;
|
||||
public override Double OperateDouble(Double left, Double right) => left * right;
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.Multiply(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Division: perform usual arithmetic conversion.
|
||||
/// </summary>
|
||||
public sealed class Divide : BinaryArithmeticOp {
|
||||
private Divide(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new Divide(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left / right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => left / right;
|
||||
public override long OperateS64(long left, long right) => left / right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => left / right;
|
||||
public override Single OperateFloat(Single left, Single right) => left / right;
|
||||
public override Double OperateDouble(Double left, Double right) => left / right;
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.Divide(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modulo: only accepts integrals.
|
||||
/// </summary>
|
||||
public sealed class Modulo : BinaryIntegralOp {
|
||||
private Modulo(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new Modulo(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left % right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => left % right;
|
||||
public override long OperateS64(long left, long right) => left % right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => left % right;
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.Modulo(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Addition
|
||||
///
|
||||
/// There are two kinds of addition:
|
||||
/// 1. both operands are of arithmetic Type
|
||||
/// 2. one operand is a pointer, and the other is an integral
|
||||
/// 3. both operands are strings
|
||||
///
|
||||
/// </summary>
|
||||
public sealed class Add : BinaryArithmeticOp {
|
||||
private Add(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new Add(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left + right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => left + right;
|
||||
public override long OperateS64(long left, long right) => left + right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => left + right;
|
||||
public override Single OperateFloat(Single left, Single right) => left + right;
|
||||
public override Double OperateDouble(Double left, Double right) => left + right;
|
||||
public override string OperateString(string left, string right) => left + right;
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.Add(left, right);
|
||||
|
||||
public ABT.Expr GetPointerAddition(ABT.Expr ptr, ABT.Expr offset, Boolean order = true) {
|
||||
if (ptr.Type.Kind != ABT.ExprTypeKind.POINTER) {
|
||||
throw new InvalidOperationException().Attach(ptr);
|
||||
}
|
||||
if (offset.Type.Kind != ABT.ExprTypeKind.LONG) {
|
||||
throw new InvalidOperationException().Attach(offset);
|
||||
}
|
||||
|
||||
var env = order ? ptr.Env : offset.Env;
|
||||
|
||||
if (ptr.IsConstExpr && offset.IsConstExpr) {
|
||||
var baseValue = (Int32)((ABT.ConstPtr)ptr).Value;
|
||||
Int32 scaleValue = ((ABT.PointerType)(ptr.Type)).RefType.SizeOf;
|
||||
Int32 offsetValue = ((ABT.ConstLong)offset).Value;
|
||||
return new ABT.ConstPtr((UInt32)(baseValue + scaleValue * offsetValue), ptr.Type, env);
|
||||
}
|
||||
|
||||
var baseAddress = ABT.TypeCast.FromPointer(ptr, new ABT.LongType(ptr.Type.IsConst, ptr.Type.IsVolatile), ptr.Env);
|
||||
var scaleFactor = new ABT.Multiply(
|
||||
offset,
|
||||
new ABT.ConstLong(((ABT.PointerType)(ptr.Type)).RefType.SizeOf, env)
|
||||
);
|
||||
var type = new ABT.LongType(offset.Type.IsConst, offset.Type.IsVolatile);
|
||||
var add =
|
||||
order
|
||||
? new ABT.Add(baseAddress, scaleFactor)
|
||||
: new ABT.Add(scaleFactor, baseAddress);
|
||||
|
||||
return ABT.TypeCast.ToPointer(add, ptr.Type, env);
|
||||
}
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
|
||||
// 1. semant the operands
|
||||
var left = SemantExpr(this.Left, ref env);
|
||||
var right = SemantExpr(this.Right, ref env);
|
||||
|
||||
var leftKind = left.Type.Kind;
|
||||
var rightKind = right.Type.Kind;
|
||||
if (leftKind == ABT.ExprTypeKind.ARRAY || leftKind == ABT.ExprTypeKind.INCOMPLETE_ARRAY || rightKind == ABT.ExprTypeKind.ARRAY || rightKind == ABT.ExprTypeKind.INCOMPLETE_ARRAY)
|
||||
{
|
||||
if (leftKind == ABT.ExprTypeKind.ARRAY || rightKind == ABT.ExprTypeKind.ARRAY)
|
||||
{
|
||||
var arrLeft = left.Type as ABT.ArrayType;
|
||||
var arrRight = right.Type as ABT.ArrayType;
|
||||
|
||||
if (arrLeft != null && right.IsConstExpr)
|
||||
{
|
||||
var offset = ABT.TypeCast.MakeCast(right, new ABT.LongType(right.Type.IsConst, right.Type.IsVolatile));
|
||||
int offsetValue = ((ABT.ConstLong)offset).Value;
|
||||
if (offsetValue >= arrLeft.NumElems)
|
||||
{
|
||||
Console.WriteLine("[Line {0}, column {1}] Potential buffer overflow, decaying to pointer.", Left.Line, Left.Column);
|
||||
left = ABT.TypeCast.MakeCast(left, new ABT.PointerType(((ABT.ArrayType)left.Type).ElemType, left.Type.IsConst, left.Type.IsVolatile));
|
||||
arrLeft = null;
|
||||
leftKind = ABT.ExprTypeKind.POINTER;
|
||||
}
|
||||
}
|
||||
|
||||
if (arrRight != null && left.IsConstExpr)
|
||||
{
|
||||
var offset = ABT.TypeCast.MakeCast(left, new ABT.LongType(left.Type.IsConst, left.Type.IsVolatile));
|
||||
int offsetValue = ((ABT.ConstLong)offset).Value;
|
||||
if (offsetValue >= arrRight.NumElems)
|
||||
{
|
||||
Console.WriteLine("[Line {0}, column {1}] Potential buffer overflow, decaying to pointer.", Left.Line, Left.Column);
|
||||
right = ABT.TypeCast.MakeCast(right, new ABT.PointerType(((ABT.ArrayType)right.Type).ElemType, right.Type.IsConst, right.Type.IsVolatile));
|
||||
arrRight = null;
|
||||
rightKind = ABT.ExprTypeKind.POINTER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if leftKind and rightKind are not pointer, then we can go ahead and set up the array-to-index addition :)
|
||||
if (leftKind != ABT.ExprTypeKind.POINTER && rightKind != ABT.ExprTypeKind.POINTER)
|
||||
{
|
||||
if (leftKind == ABT.ExprTypeKind.ARRAY || leftKind == ABT.ExprTypeKind.INCOMPLETE_ARRAY)
|
||||
{
|
||||
if (!right.Type.IsIntegral)
|
||||
{
|
||||
throw new InvalidOperationException("Expected integral to be array index.").Attach(right);
|
||||
}
|
||||
right = ABT.TypeCast.MakeCast(right, new ABT.ULongType(right.Type.IsConst, right.Type.IsVolatile));
|
||||
return new ABT.ArrayIndexDeref(left, right);
|
||||
}
|
||||
if (!left.Type.IsIntegral)
|
||||
{
|
||||
throw new InvalidOperationException("Expected integral to be array index.").Attach(left);
|
||||
}
|
||||
left = ABT.TypeCast.MakeCast(left, new ABT.ULongType(left.Type.IsConst, left.Type.IsVolatile));
|
||||
return new ABT.ArrayIndexDeref(right, left);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 2. ptr + int
|
||||
if (left.Type.Kind == ABT.ExprTypeKind.POINTER) {
|
||||
if (!right.Type.IsIntegral) {
|
||||
throw new InvalidOperationException("Expected integral to be added to a pointer.").Attach(right);
|
||||
}
|
||||
right = ABT.TypeCast.MakeCast(right, new ABT.LongType(right.Type.IsConst, right.Type.IsVolatile));
|
||||
return GetPointerAddition(left, right);
|
||||
}
|
||||
|
||||
// 3. int + ptr
|
||||
if (right.Type.Kind == ABT.ExprTypeKind.POINTER) {
|
||||
if (!left.Type.IsIntegral) {
|
||||
throw new InvalidOperationException("Expected integral to be added to a pointer.").Attach(left);
|
||||
}
|
||||
left = ABT.TypeCast.MakeCast(left, new ABT.LongType(left.Type.IsConst, left.Type.IsVolatile));
|
||||
return GetPointerAddition(right, left, false);
|
||||
}
|
||||
|
||||
// 4. usual arithmetic conversion
|
||||
return base.GetExprImpl(env, info);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtraction
|
||||
///
|
||||
/// There are three kinds of subtractions:
|
||||
/// 1. arithmetic - arithmetic
|
||||
/// 2. pointer - integral
|
||||
/// 3. pointer - pointer
|
||||
/// </summary>
|
||||
public sealed class Sub : BinaryArithmeticOp {
|
||||
private Sub(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new Sub(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left - right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => left - right;
|
||||
public override long OperateS64(long left, long right) => left - right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => left - right;
|
||||
public override Single OperateFloat(Single left, Single right) => left - right;
|
||||
public override Double OperateDouble(Double left, Double right) => left - right;
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.Sub(left, right);
|
||||
|
||||
public static ABT.Expr GetPointerSubtraction(ABT.Expr ptr, ABT.Expr offset) {
|
||||
if (ptr.Type.Kind != ABT.ExprTypeKind.POINTER) {
|
||||
throw new InvalidOperationException("Error: expect a pointer").Attach(ptr);
|
||||
}
|
||||
if (offset.Type.Kind != ABT.ExprTypeKind.LONG) {
|
||||
throw new InvalidOperationException("Error: expect an integer").Attach(offset);
|
||||
}
|
||||
|
||||
if (ptr.IsConstExpr && offset.IsConstExpr) {
|
||||
Int32 baseAddressValue = (Int32)((ABT.ConstPtr)ptr).Value;
|
||||
Int32 scaleFactorValue = ((ABT.PointerType)(ptr.Type)).RefType.SizeOf;
|
||||
Int32 offsetValue = ((ABT.ConstLong)offset).Value;
|
||||
return new ABT.ConstPtr((UInt32)(baseAddressValue - scaleFactorValue * offsetValue), ptr.Type, offset.Env);
|
||||
}
|
||||
|
||||
return ABT.TypeCast.ToPointer(new ABT.Sub(
|
||||
ABT.TypeCast.FromPointer(ptr, new ABT.LongType(ptr.Type.IsConst, ptr.Type.IsVolatile), ptr.Env),
|
||||
new ABT.Multiply(
|
||||
offset,
|
||||
new ABT.ConstLong(((ABT.PointerType)(ptr.Type)).RefType.SizeOf, offset.Env)
|
||||
)
|
||||
), ptr.Type, offset.Env
|
||||
);
|
||||
}
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
|
||||
var left = SemantExpr(this.Left, ref env);
|
||||
var right = SemantExpr(this.Right, ref env);
|
||||
|
||||
if (left.Type is ABT.ArrayType) {
|
||||
left = ABT.TypeCast.MakeCast(left, new ABT.PointerType((left.Type as ABT.ArrayType).ElemType, left.Type.IsConst, left.Type.IsVolatile));
|
||||
}
|
||||
|
||||
if (right.Type is ABT.ArrayType) {
|
||||
right = ABT.TypeCast.MakeCast(right, new ABT.PointerType((right.Type as ABT.ArrayType).ElemType, right.Type.IsConst, right.Type.IsVolatile));
|
||||
}
|
||||
|
||||
var isConst = left.Type.IsConst || right.Type.IsConst;
|
||||
var isVolatile = left.Type.IsVolatile || right.Type.IsVolatile;
|
||||
|
||||
if (left.Type.Kind == ABT.ExprTypeKind.POINTER) {
|
||||
|
||||
// 1. ptr - ptr
|
||||
if (right.Type.Kind == ABT.ExprTypeKind.POINTER) {
|
||||
ABT.PointerType leftType = (ABT.PointerType)(left.Type);
|
||||
ABT.PointerType rightType = (ABT.PointerType)(right.Type);
|
||||
if (!leftType.RefType.EqualType(rightType.RefType)) {
|
||||
throw new InvalidOperationException("The 2 pointers don't match.").Attach(left);
|
||||
}
|
||||
|
||||
Int32 scale = leftType.RefType.SizeOf;
|
||||
|
||||
if (left.IsConstExpr && right.IsConstExpr) {
|
||||
return new ABT.ConstLong((Int32)(((ABT.ConstPtr)left).Value - ((ABT.ConstPtr)right).Value) / scale, env);
|
||||
}
|
||||
|
||||
return new ABT.Divide(
|
||||
new ABT.Sub(
|
||||
ABT.TypeCast.MakeCast(left, new ABT.LongType(isConst, isVolatile)),
|
||||
ABT.TypeCast.MakeCast(right, new ABT.LongType(isConst, isVolatile))
|
||||
),
|
||||
new ABT.ConstLong(scale, env)
|
||||
);
|
||||
}
|
||||
|
||||
// 2. ptr - integral
|
||||
if (!right.Type.IsIntegral) {
|
||||
throw new InvalidOperationException("Expected an integral.").Attach(right);
|
||||
}
|
||||
right = ABT.TypeCast.MakeCast(right, new ABT.LongType(right.Type.IsConst, right.Type.IsVolatile));
|
||||
return GetPointerSubtraction(left, right);
|
||||
|
||||
}
|
||||
|
||||
// 3. arith - arith
|
||||
return base.GetExprImpl(env, info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Left Shift: takes in two integrals, returns an integer.
|
||||
/// </summary>
|
||||
public sealed class LShift : BinaryIntegralOp {
|
||||
private LShift(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new LShift(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left << right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => (UInt32)((Int32)left << (Int32)right);
|
||||
public override long OperateS64(long left, long right) => left << (int)right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => (ulong)((long)left << (int)right);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.LShift(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Right Shift: takes in two integrals, returns an integer;
|
||||
/// </summary>
|
||||
public sealed class RShift : BinaryIntegralOp {
|
||||
private RShift(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new RShift(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left >> right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => (UInt32)((Int32)left >> (Int32)right);
|
||||
public override long OperateS64(long left, long right) => left >> (int)right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => (ulong)((long)left >> (int)right);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.RShift(left, right);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Less than
|
||||
/// </summary>
|
||||
public sealed class Less : BinaryLogicalOp {
|
||||
private Less(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new Less(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => Convert.ToInt32(left < right);
|
||||
public override Int32 OperateULong(UInt32 left, UInt32 right) => Convert.ToInt32(left < right);
|
||||
public override int OperateS64(long left, long right) => Convert.ToInt32(left < right);
|
||||
public override int OperateU64(ulong left, ulong right) => Convert.ToInt32(left < right);
|
||||
public override Int32 OperateFloat(Single left, Single right) => Convert.ToInt32(left < right);
|
||||
public override Int32 OperateDouble(Double left, Double right) => Convert.ToInt32(left < right);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.Less(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Less or Equal than
|
||||
/// </summary>
|
||||
public sealed class LEqual : BinaryLogicalOp {
|
||||
private LEqual(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new LEqual(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => Convert.ToInt32(left <= right);
|
||||
public override Int32 OperateULong(UInt32 left, UInt32 right) => Convert.ToInt32(left <= right);
|
||||
public override int OperateS64(long left, long right) => Convert.ToInt32(left <= right);
|
||||
public override int OperateU64(ulong left, ulong right) => Convert.ToInt32(left <= right);
|
||||
public override Int32 OperateFloat(Single left, Single right) => Convert.ToInt32(left <= right);
|
||||
public override Int32 OperateDouble(Double left, Double right) => Convert.ToInt32(left <= right);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.LEqual(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Greater than
|
||||
/// </summary>
|
||||
public sealed class Greater : BinaryLogicalOp {
|
||||
private Greater(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new Greater(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => Convert.ToInt32(left > right);
|
||||
public override Int32 OperateULong(UInt32 left, UInt32 right) => Convert.ToInt32(left > right);
|
||||
public override int OperateS64(long left, long right) => Convert.ToInt32(left > right);
|
||||
public override int OperateU64(ulong left, ulong right) => Convert.ToInt32(left > right);
|
||||
public override Int32 OperateFloat(Single left, Single right) => Convert.ToInt32(left > right);
|
||||
public override Int32 OperateDouble(Double left, Double right) => Convert.ToInt32(left > right);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.Greater(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Greater or Equal than
|
||||
/// </summary>
|
||||
public sealed class GEqual : BinaryLogicalOp {
|
||||
private GEqual(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new GEqual(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => Convert.ToInt32(left >= right);
|
||||
public override Int32 OperateULong(UInt32 left, UInt32 right) => Convert.ToInt32(left >= right);
|
||||
public override int OperateS64(long left, long right) => Convert.ToInt32(left >= right);
|
||||
public override int OperateU64(ulong left, ulong right) => Convert.ToInt32(left >= right);
|
||||
public override Int32 OperateFloat(Single left, Single right) => Convert.ToInt32(left >= right);
|
||||
public override Int32 OperateDouble(Double left, Double right) => Convert.ToInt32(left >= right);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.GEqual(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equal
|
||||
/// </summary>
|
||||
public sealed class Equal : BinaryLogicalOp {
|
||||
private Equal(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new Equal(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => Convert.ToInt32(left == right);
|
||||
public override Int32 OperateULong(UInt32 left, UInt32 right) => Convert.ToInt32(left == right);
|
||||
public override int OperateS64(long left, long right) => Convert.ToInt32(left == right);
|
||||
public override int OperateU64(ulong left, ulong right) => Convert.ToInt32(left == right);
|
||||
public override Int32 OperateFloat(Single left, Single right) => Convert.ToInt32(left == right);
|
||||
public override Int32 OperateDouble(Double left, Double right) => Convert.ToInt32(left == right);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.Equal(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not equal
|
||||
/// </summary>
|
||||
public sealed class NotEqual : BinaryLogicalOp {
|
||||
private NotEqual(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new NotEqual(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => Convert.ToInt32(left != right);
|
||||
public override Int32 OperateULong(UInt32 left, UInt32 right) => Convert.ToInt32(left != right);
|
||||
public override int OperateS64(long left, long right) => Convert.ToInt32(left != right);
|
||||
public override int OperateU64(ulong left, ulong right) => Convert.ToInt32(left != right);
|
||||
public override Int32 OperateFloat(Single left, Single right) => Convert.ToInt32(left != right);
|
||||
public override Int32 OperateDouble(Double left, Double right) => Convert.ToInt32(left != right);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.NotEqual(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise And: returns an integer.
|
||||
/// </summary>
|
||||
public sealed class BitwiseAnd : BinaryIntegralOp {
|
||||
private BitwiseAnd(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new BitwiseAnd(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left & right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => left & right;
|
||||
public override long OperateS64(long left, long right) => left & right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => left & right;
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.BitwiseAnd(left, right);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Xor: returns an integer.
|
||||
/// </summary>
|
||||
public sealed class Xor : BinaryIntegralOp {
|
||||
private Xor(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new Xor(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left ^ right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => left ^ right;
|
||||
public override long OperateS64(long left, long right) => left ^ right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => left ^ right;
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.Xor(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise Or: accepts two integrals, returns an integer.
|
||||
/// </summary>
|
||||
public sealed class BitwiseOr : BinaryIntegralOp {
|
||||
private BitwiseOr(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new BitwiseOr(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => left | right;
|
||||
public override UInt32 OperateULong(UInt32 left, UInt32 right) => left | right;
|
||||
public override long OperateS64(long left, long right) => left | right;
|
||||
public override ulong OperateU64(ulong left, ulong right) => left | right;
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.BitwiseOr(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logical and: both operands need to be non-zero.
|
||||
/// </summary>
|
||||
public sealed class LogicalAnd : BinaryLogicalOp {
|
||||
private LogicalAnd(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) => new LogicalAnd(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => Convert.ToInt32(left != 0 && right != 0);
|
||||
public override Int32 OperateULong(UInt32 left, UInt32 right) => Convert.ToInt32(left != 0 && right != 0);
|
||||
public override int OperateS64(long left, long right) => Convert.ToInt32(left != 0 && right != 0);
|
||||
public override int OperateU64(ulong left, ulong right) => Convert.ToInt32(left != 0 && right != 0);
|
||||
public override Int32 OperateFloat(Single left, Single right) => Convert.ToInt32(left != 0 && right != 0);
|
||||
public override Int32 OperateDouble(Double left, Double right) => Convert.ToInt32(left != 0 && right != 0);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.LogicalAnd(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logical or: at least one of operands needs to be non-zero.
|
||||
/// </summary>
|
||||
public sealed class LogicalOr : BinaryLogicalOp {
|
||||
private LogicalOr(Expr left, Expr right)
|
||||
: base(left, right) { }
|
||||
public static Expr Create(Expr left, Expr right) =>
|
||||
new LogicalOr(left, right);
|
||||
|
||||
public override Int32 OperateLong(Int32 left, Int32 right) => Convert.ToInt32(left != 0 || right != 0);
|
||||
public override Int32 OperateULong(UInt32 left, UInt32 right) => Convert.ToInt32(left != 0 || right != 0);
|
||||
public override int OperateS64(long left, long right) => Convert.ToInt32(left != 0 || right != 0);
|
||||
public override int OperateU64(ulong left, ulong right) => Convert.ToInt32(left != 0 || right != 0);
|
||||
public override Int32 OperateFloat(Single left, Single right) => Convert.ToInt32(left != 0 || right != 0);
|
||||
public override Int32 OperateDouble(Double left, Double right) => Convert.ToInt32(left != 0 || right != 0);
|
||||
|
||||
public override ABT.Expr ConstructExpr(ABT.Expr left, ABT.Expr right, ABT.ExprType type) =>
|
||||
new ABT.LogicalOr(left, right);
|
||||
}
|
||||
}
|
108
LibIFPSCC/AST/ConstExpressions.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using LexicalAnalysis;
|
||||
|
||||
namespace AST {
|
||||
|
||||
public abstract class Literal : Expr { }
|
||||
|
||||
/// <summary>
|
||||
/// May be a float or double
|
||||
/// </summary>
|
||||
public class FloatLiteral : Literal {
|
||||
public FloatLiteral(Double value, TokenFloat.FloatSuffix floatSuffix) {
|
||||
this.Value = value;
|
||||
this.FloatSuffix = floatSuffix;
|
||||
}
|
||||
|
||||
public TokenFloat.FloatSuffix FloatSuffix { get; }
|
||||
|
||||
public Double Value { get; }
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
switch (this.FloatSuffix) {
|
||||
case TokenFloat.FloatSuffix.F:
|
||||
return new ABT.ConstFloat((Single)this.Value, env);
|
||||
|
||||
case TokenFloat.FloatSuffix.NONE:
|
||||
case TokenFloat.FloatSuffix.L:
|
||||
return new ABT.ConstDouble(this.Value, env);
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// May be signed or unsigned
|
||||
/// C doesn't have char constant, only int constant
|
||||
/// </summary>
|
||||
public class IntLiteral : Literal {
|
||||
public IntLiteral(Int64 value, TokenInt.IntSuffix suffix) {
|
||||
this.Value = value;
|
||||
this.Suffix = suffix;
|
||||
}
|
||||
|
||||
public TokenInt.IntSuffix Suffix { get; }
|
||||
public Int64 Value { get; }
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
switch (this.Suffix) {
|
||||
case TokenInt.IntSuffix.ULL:
|
||||
return new ABT.ConstU64((ulong)this.Value, env);
|
||||
case TokenInt.IntSuffix.LL:
|
||||
return new ABT.ConstS64(this.Value, env);
|
||||
|
||||
case TokenInt.IntSuffix.U:
|
||||
case TokenInt.IntSuffix.UL:
|
||||
return new ABT.ConstULong((UInt32)this.Value, env);
|
||||
|
||||
case TokenInt.IntSuffix.NONE:
|
||||
case TokenInt.IntSuffix.L:
|
||||
return new ABT.ConstLong((Int32)this.Value, env);
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IStringLiteral
|
||||
{
|
||||
string Value { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String Literal
|
||||
/// </summary>
|
||||
public class StringLiteral : Expr, IStringLiteral {
|
||||
public StringLiteral(String value) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public String Value { get; }
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
return new ABT.ConstStringLiteral(this.Value, env);
|
||||
}
|
||||
}
|
||||
|
||||
public class UnicodeStringLiteral : Expr, IStringLiteral
|
||||
{
|
||||
public UnicodeStringLiteral(String value)
|
||||
{
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public String Value { get; }
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
return new ABT.ConstUnicodeStringLiteral(this.Value, env);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
649
LibIFPSCC/AST/DeclarationSpecifiers.cs
Normal file
@ -0,0 +1,649 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using ABT;
|
||||
|
||||
namespace AST {
|
||||
using static SemanticAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// storage-class-specifier
|
||||
/// : auto | register | static | extern | typedef
|
||||
/// </summary>
|
||||
public enum StorageClsSpec {
|
||||
NULL,
|
||||
AUTO,
|
||||
REGISTER,
|
||||
STATIC,
|
||||
EXTERN,
|
||||
TYPEDEF,
|
||||
|
||||
ATTRIBUTE
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type-specifier
|
||||
/// : void --+
|
||||
/// | char |
|
||||
/// | short |
|
||||
/// | int |
|
||||
/// | long +--> Basic Type specifier
|
||||
/// | float |
|
||||
/// | double |
|
||||
/// | signed |
|
||||
/// | unsigned --+
|
||||
/// | struct-or-union-specifier
|
||||
/// | enum-specifier
|
||||
/// | typedef-name
|
||||
/// </summary>
|
||||
public abstract class TypeSpec : ISyntaxTreeNode {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
|
||||
[SemantMethod]
|
||||
public abstract ISemantReturn<ExprType> GetExprType(Env env);
|
||||
|
||||
public abstract TypeSpecKind Kind { get; }
|
||||
}
|
||||
|
||||
public enum TypeSpecKind {
|
||||
NON_BASIC,
|
||||
VOID,
|
||||
CHAR,
|
||||
SHORT,
|
||||
INT,
|
||||
LONG,
|
||||
FLOAT,
|
||||
DOUBLE,
|
||||
SIGNED,
|
||||
UNSIGNED,
|
||||
STRING,
|
||||
INT64,
|
||||
COM_VARIANT
|
||||
}
|
||||
|
||||
public sealed class BasicTypeSpec : TypeSpec {
|
||||
public BasicTypeSpec(TypeSpecKind kind) {
|
||||
this.Kind = kind;
|
||||
}
|
||||
|
||||
public override TypeSpecKind Kind { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ExprType> GetExprType(Env env) {
|
||||
throw new InvalidProgramException().Attach(this);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class NonBasicTypeSpec : TypeSpec {
|
||||
public override TypeSpecKind Kind => TypeSpecKind.NON_BASIC;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// typedef-name
|
||||
/// : identifier
|
||||
/// </summary>
|
||||
public sealed class TypedefName : NonBasicTypeSpec {
|
||||
private TypedefName(String name) {
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
public static TypedefName Create(String name) =>
|
||||
new TypedefName(name);
|
||||
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ExprType> GetExprType(Env env) {
|
||||
var entryOpt = env.Find(this.Name);
|
||||
if (entryOpt.IsNone) {
|
||||
throw new InvalidProgramException("This should not pass the parser.").Attach(this);
|
||||
}
|
||||
var entry = entryOpt.Value;
|
||||
if (entry.Kind != Env.EntryKind.TYPEDEF) {
|
||||
throw new InvalidProgramException("This should not pass the parser.").Attach(this);
|
||||
}
|
||||
return SemantReturn.Create(env, entry.Type);
|
||||
}
|
||||
|
||||
public String Name { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type-qualifier
|
||||
/// : const | volatile
|
||||
/// </summary>
|
||||
public enum TypeQual {
|
||||
NULL,
|
||||
CONST,
|
||||
VOLATILE
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TypeAttrib: __attribute(name(args))
|
||||
/// </summary>
|
||||
public class TypeAttrib : ISyntaxTreeNode
|
||||
{
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
protected TypeAttrib(Variable func, ImmutableList<Expr> args)
|
||||
{
|
||||
this.Name = func.Name;
|
||||
this.Args = args;
|
||||
}
|
||||
|
||||
public static TypeAttrib Create(Expr func) => Create(func, ImmutableList<Expr>.Empty);
|
||||
|
||||
public static TypeAttrib Create(Expr func, ImmutableList<Expr> args) =>
|
||||
new TypeAttrib((Variable)func, args);
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public ImmutableList<Expr> Args { get; }
|
||||
}
|
||||
|
||||
public class InterfaceTypeSpec : NonBasicTypeSpec
|
||||
{
|
||||
protected InterfaceTypeSpec(Expr itfGuid)
|
||||
{
|
||||
Arg = itfGuid;
|
||||
}
|
||||
|
||||
public static InterfaceTypeSpec Create(Expr itfGuid) => new InterfaceTypeSpec(itfGuid);
|
||||
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ExprType> GetExprType(Env env)
|
||||
{
|
||||
var expr = Arg.GetExpr(env, this);
|
||||
if (!expr.IsConstExpr)
|
||||
{
|
||||
throw new InvalidProgramException("Interface argument must be const").Attach(this);
|
||||
}
|
||||
var kind = expr.Type.Kind;
|
||||
if (kind != ExprTypeKind.ANSI_STRING && kind != ExprTypeKind.UNICODE_STRING)
|
||||
{
|
||||
throw new InvalidProgramException("Interface argument must be string").Attach(this);
|
||||
}
|
||||
var lit = expr as IStringLiteral;
|
||||
if (!Guid.TryParse(lit.Value, out var guid))
|
||||
{
|
||||
throw new InvalidProgramException("Interface argument must be a COM interface GUID").Attach(this);
|
||||
}
|
||||
|
||||
return SemantReturn.Create(env, new ComInterfaceType(guid));
|
||||
}
|
||||
|
||||
public Expr Arg { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// specifier-qualifier-list
|
||||
/// : [ Type-specifier | Type-qualifier ]+
|
||||
/// </summary>
|
||||
public class SpecQualList : ISyntaxTreeNode {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
protected SpecQualList(ImmutableList<TypeSpec> typeSpecs, ImmutableList<TypeQual> typeQuals, ImmutableList<TypeAttrib> typeAttribs) {
|
||||
this.TypeSpecs = typeSpecs;
|
||||
this.TypeQuals = typeQuals;
|
||||
this.TypeAttribs = typeAttribs;
|
||||
}
|
||||
|
||||
public static SpecQualList Create(ImmutableList<TypeSpec> typeSpecs, ImmutableList<TypeQual> typeQuals, ImmutableList<TypeAttrib> typeAttribs) =>
|
||||
new SpecQualList(typeSpecs, typeQuals, typeAttribs);
|
||||
|
||||
public static SpecQualList Empty { get; } =
|
||||
Create(ImmutableList<TypeSpec>.Empty, ImmutableList<TypeQual>.Empty, ImmutableList<TypeAttrib>.Empty);
|
||||
|
||||
public static SpecQualList Add(SpecQualList list, TypeSpec typeSpec) =>
|
||||
Create(list.TypeSpecs.Add(typeSpec), list.TypeQuals, list.TypeAttribs);
|
||||
|
||||
public static SpecQualList Add(SpecQualList list, TypeQual typeQual) =>
|
||||
Create(list.TypeSpecs, list.TypeQuals.Add(typeQual), list.TypeAttribs);
|
||||
|
||||
public static SpecQualList Add(SpecQualList list, TypeAttrib typeAttrib) =>
|
||||
Create(list.TypeSpecs, list.TypeQuals, list.TypeAttribs.Add(typeAttrib));
|
||||
|
||||
public ImmutableList<TypeSpec> TypeSpecs { get; }
|
||||
public ImmutableList<TypeQual> TypeQuals { get; }
|
||||
|
||||
public ImmutableList<TypeAttrib> TypeAttribs { get; }
|
||||
|
||||
private static ImmutableDictionary<ImmutableSortedSet<TypeSpecKind>, ExprType> BasicTypeSpecLookupTable { get; }
|
||||
|
||||
static SpecQualList() {
|
||||
|
||||
BasicTypeSpecLookupTable = ImmutableDictionary<ImmutableSortedSet<TypeSpecKind>, ExprType>.Empty
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.VOID), new VoidType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.CHAR), new CharType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.CHAR, TypeSpecKind.SIGNED), new CharType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.CHAR, TypeSpecKind.UNSIGNED), new UCharType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SHORT), new ShortType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SHORT, TypeSpecKind.SIGNED), new ShortType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SHORT, TypeSpecKind.INT), new ShortType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SHORT, TypeSpecKind.INT, TypeSpecKind.SIGNED), new ShortType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SHORT, TypeSpecKind.UNSIGNED), new UShortType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SHORT, TypeSpecKind.INT, TypeSpecKind.UNSIGNED), new UShortType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.INT), new LongType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.INT, TypeSpecKind.SIGNED), new LongType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.INT, TypeSpecKind.LONG), new LongType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.INT, TypeSpecKind.SIGNED, TypeSpecKind.LONG), new LongType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SIGNED), new LongType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SIGNED, TypeSpecKind.LONG), new LongType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.LONG), new LongType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.UNSIGNED), new ULongType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.UNSIGNED, TypeSpecKind.INT), new ULongType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.UNSIGNED, TypeSpecKind.LONG), new ULongType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.UNSIGNED, TypeSpecKind.INT, TypeSpecKind.LONG), new ULongType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.FLOAT), new FloatType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.DOUBLE), new DoubleType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.DOUBLE, TypeSpecKind.LONG), new DoubleType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.STRING), new AnsiStringType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SIGNED, TypeSpecKind.STRING), new AnsiStringType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.UNSIGNED, TypeSpecKind.STRING), new UnicodeStringType())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.STRING, TypeSpecKind.UNSIGNED), new UnicodeStringType())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.INT64), new S64Type())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.SIGNED, TypeSpecKind.INT64), new S64Type())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.INT64, TypeSpecKind.SIGNED), new S64Type())
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.INT64, TypeSpecKind.UNSIGNED), new U64Type())
|
||||
|
||||
.Add(ImmutableSortedSet.Create(TypeSpecKind.COM_VARIANT), new ComVariantType())
|
||||
;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get qualified Type, based on Type specifiers & Type qualifiers.
|
||||
/// </summary>
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ExprType> GetExprType(Env env) {
|
||||
Boolean isConst = this.TypeQuals.Contains(TypeQual.CONST);
|
||||
Boolean isVolatile = this.TypeQuals.Contains(TypeQual.VOLATILE);
|
||||
|
||||
// If no Type specifier is given, assume long Type.
|
||||
if (this.TypeSpecs.IsEmpty) {
|
||||
return SemantReturn.Create(env, new LongType(isConst, isVolatile) { TypeAttribsSet = TypeAttribs });
|
||||
}
|
||||
|
||||
// If every Type specifier is basic, go to the lookup table.
|
||||
if (this.TypeSpecs.All(typeSpec => typeSpec.Kind != TypeSpecKind.NON_BASIC)) {
|
||||
var basicTypeSpecKinds =
|
||||
this.TypeSpecs
|
||||
.ConvertAll(typeSpec => typeSpec.Kind)
|
||||
.Distinct()
|
||||
.ToImmutableSortedSet();
|
||||
|
||||
foreach (var pair in BasicTypeSpecLookupTable) {
|
||||
if (pair.Key.SetEquals(basicTypeSpecKinds)) {
|
||||
var value = pair.Value;
|
||||
if (!TypeAttribs.IsEmpty)
|
||||
{
|
||||
value = value.GetQualifiedType(false, false);
|
||||
value.TypeAttribsSet = TypeAttribs;
|
||||
}
|
||||
return SemantReturn.Create(env, value);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Invalid Type specifier set.").Attach(this);
|
||||
}
|
||||
|
||||
// If there is a non-basic Type specifier, semant it.
|
||||
if (this.TypeSpecs.Count != 1) {
|
||||
throw new InvalidOperationException("Invalid Type specifier set.").Attach(this);
|
||||
}
|
||||
|
||||
var type = Semant(this.TypeSpecs[0].GetExprType, ref env);
|
||||
if (type.Kind != ExprTypeKind.INCOMPLETE_ARRAY && type.Kind != ExprTypeKind.ARRAY)
|
||||
type = type.GetQualifiedType(isConst, isVolatile);
|
||||
if (TypeAttribs.Count != 0)
|
||||
type.TypeAttribsSet = TypeAttribs;
|
||||
return SemantReturn.Create(env, type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// declaration-specifiers
|
||||
/// : [ storage-class-specifier | Type-specifier | Type-qualifier ]+
|
||||
/// </summary>
|
||||
public sealed class DeclnSpecs : SpecQualList {
|
||||
private DeclnSpecs(ImmutableList<StorageClsSpec> storageClsSpecs, ImmutableList<TypeSpec> typeSpecs, ImmutableList<TypeQual> typeQuals, ImmutableList<TypeAttrib> typeAttribs)
|
||||
: base(typeSpecs, typeQuals, typeAttribs) {
|
||||
this.StorageClsSpecs = storageClsSpecs;
|
||||
}
|
||||
|
||||
public static DeclnSpecs Create(ImmutableList<StorageClsSpec> storageClsSpecs, ImmutableList<TypeSpec> typeSpecs, ImmutableList<TypeQual> typeQuals, ImmutableList<TypeAttrib> typeAttribs) =>
|
||||
new DeclnSpecs(storageClsSpecs, typeSpecs, typeQuals, typeAttribs);
|
||||
|
||||
public new static DeclnSpecs Empty { get; } = Create(ImmutableList<StorageClsSpec>.Empty, ImmutableList<TypeSpec>.Empty, ImmutableList<TypeQual>.Empty, ImmutableList<TypeAttrib>.Empty);
|
||||
|
||||
public static DeclnSpecs Add(DeclnSpecs declnSpecs, StorageClsSpec storageClsSpec) =>
|
||||
Create(declnSpecs.StorageClsSpecs.Add(storageClsSpec), declnSpecs.TypeSpecs, declnSpecs.TypeQuals, declnSpecs.TypeAttribs);
|
||||
|
||||
public static DeclnSpecs Add(DeclnSpecs declnSpecs, TypeSpec typeSpec) =>
|
||||
Create(declnSpecs.StorageClsSpecs, declnSpecs.TypeSpecs.Add(typeSpec), declnSpecs.TypeQuals, declnSpecs.TypeAttribs);
|
||||
|
||||
public static DeclnSpecs Add(DeclnSpecs declnSpecs, TypeQual typeQual) =>
|
||||
Create(declnSpecs.StorageClsSpecs, declnSpecs.TypeSpecs, declnSpecs.TypeQuals.Add(typeQual), declnSpecs.TypeAttribs);
|
||||
|
||||
public static DeclnSpecs Add(DeclnSpecs declnSpecs, TypeAttrib typeAttrib) =>
|
||||
Create(declnSpecs.StorageClsSpecs, declnSpecs.TypeSpecs, declnSpecs.TypeQuals, declnSpecs.TypeAttribs.Add(typeAttrib));
|
||||
|
||||
[SemantMethod]
|
||||
public StorageClass GetStorageClass() {
|
||||
if (this.StorageClsSpecs.Count == 0) {
|
||||
return StorageClass.AUTO;
|
||||
}
|
||||
|
||||
if (this.StorageClsSpecs.Count == 1) {
|
||||
switch (this.StorageClsSpecs[0]) {
|
||||
case StorageClsSpec.AUTO:
|
||||
case StorageClsSpec.NULL:
|
||||
case StorageClsSpec.REGISTER:
|
||||
return StorageClass.AUTO;
|
||||
|
||||
case StorageClsSpec.EXTERN:
|
||||
return StorageClass.EXTERN;
|
||||
|
||||
case StorageClsSpec.STATIC:
|
||||
return StorageClass.STATIC;
|
||||
|
||||
case StorageClsSpec.TYPEDEF:
|
||||
return StorageClass.TYPEDEF;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException().Attach(this);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Multiple storage class specifiers.").Attach(this);
|
||||
}
|
||||
|
||||
public ImmutableList<StorageClsSpec> StorageClsSpecs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Only used by the parser.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public bool IsTypedef() => this.StorageClsSpecs.Contains(StorageClsSpec.TYPEDEF);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// struct-or-union
|
||||
/// : struct | union
|
||||
/// </summary>
|
||||
public enum StructOrUnion {
|
||||
STRUCT,
|
||||
UNION
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// struct-or-union-specifier
|
||||
/// </summary>
|
||||
public sealed class StructOrUnionSpec : NonBasicTypeSpec {
|
||||
private static uint s_UnnamedCount = 0;
|
||||
private StructOrUnionSpec(StructOrUnion structOrUnion, Option<String> name, Option<ImmutableList<StructDecln>> memberDeclns) {
|
||||
this.StructOrUnion = structOrUnion;
|
||||
this.Name = name;
|
||||
this.MemberDeclns = memberDeclns;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static StructOrUnionSpec Create(StructOrUnion structOrUnion, Option<String> name, Option<ImmutableList<StructDecln>> memberDeclns) =>
|
||||
new StructOrUnionSpec(structOrUnion, name, memberDeclns);
|
||||
|
||||
public static StructOrUnionSpec Create(StructOrUnion structOrUnion, Option<String> name, ImmutableList<StructDecln> memberDeclns) =>
|
||||
new StructOrUnionSpec(structOrUnion, name, Option.Some(memberDeclns));
|
||||
|
||||
public static StructOrUnionSpec Create(StructOrUnion structOrUnion, String name) =>
|
||||
new StructOrUnionSpec(structOrUnion, Option.Some(name), Option<ImmutableList<StructDecln>>.None);
|
||||
|
||||
public StructOrUnion StructOrUnion { get; }
|
||||
public Option<String> Name { get; }
|
||||
public Option<ImmutableList<StructDecln>> MemberDeclns { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ImmutableList<Tuple<Option<String>, ExprType>>> GetMembers(Env env, ImmutableList<StructDecln> memberDeclns) {
|
||||
var result = memberDeclns.Aggregate(ImmutableList<Tuple<Option<String>, ExprType>>.Empty, (acc, decln) => acc.AddRange(Semant(decln.GetMemberDeclns, ref env))
|
||||
);
|
||||
|
||||
return SemantReturn.Create(env, result);
|
||||
}
|
||||
|
||||
// +----------------------------------------------+-------------------------------------------+
|
||||
// | members | members X |
|
||||
// +----------+----------------------------------------------+-------------------------------------------+
|
||||
// | | May have incomplete Type in current scope. | |
|
||||
// | name | 1. Get/New incomplete Type in current scope; | Name must appear in previous environment. |
|
||||
// | | 2. Fill up with members. | |
|
||||
// +----------+----------------------------------------------+-------------------------------------------+
|
||||
// | name X | Fill up with members. | X |
|
||||
// +----------+----------------------------------------------+-------------------------------------------+
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ExprType> GetExprType(Env env) {
|
||||
|
||||
StructOrUnionType type;
|
||||
|
||||
// If no members provided, then we need to find the Type in the current environment.
|
||||
if (this.MemberDeclns.IsNone) {
|
||||
|
||||
if (this.Name.IsNone) {
|
||||
throw new InvalidProgramException("This should not pass the parser").Attach(this);
|
||||
}
|
||||
|
||||
var name = this.Name.Value;
|
||||
var typeName = (this.StructOrUnion == StructOrUnion.STRUCT ? "struct" : "union") + $" {name}";
|
||||
|
||||
// Try to find Type name in the current environment.
|
||||
var entryOpt = env.Find(typeName);
|
||||
|
||||
// If name not found: create an incomplete Type and add it into the environment.
|
||||
if (entryOpt.IsNone) {
|
||||
type = StructOrUnionType.CreateIncompleteType(this.StructOrUnion, name);
|
||||
env = env.PushEntry(Env.EntryKind.TYPEDEF, typeName, type);
|
||||
return SemantReturn.Create(env, type);
|
||||
}
|
||||
|
||||
// If name found: fetch it.
|
||||
if (entryOpt.Value.Kind != Env.EntryKind.TYPEDEF) {
|
||||
throw new InvalidProgramException("A struct or union in env that is not typedef? This should not appear.").Attach(this);
|
||||
}
|
||||
|
||||
return SemantReturn.Create(env, entryOpt.Value.Type);
|
||||
|
||||
}
|
||||
|
||||
// If members are provided, the user is trying to define a new struct/union.
|
||||
|
||||
if (this.Name.IsSome) {
|
||||
|
||||
var name = this.Name.Value;
|
||||
var typeName = (this.StructOrUnion == StructOrUnion.STRUCT ? "struct" : "union") + $" {name}";
|
||||
|
||||
// Try to find Type name in the current environment.
|
||||
// Notice we need to search the current **scope** only.
|
||||
var entryOpt = env.FindInCurrentScope(typeName);
|
||||
|
||||
// If name not found: create an incomplete Type and add it into the environment.
|
||||
if (entryOpt.IsNone) {
|
||||
type = StructOrUnionType.CreateIncompleteType(this.StructOrUnion, name);
|
||||
env = env.PushEntry(Env.EntryKind.TYPEDEF, typeName, type);
|
||||
} else {
|
||||
if (entryOpt.Value.Kind != Env.EntryKind.TYPEDEF) {
|
||||
throw new InvalidProgramException("A struct or union in env that is not typedef? This should not appear.").Attach(this);
|
||||
}
|
||||
|
||||
type = entryOpt.Value.Type as StructOrUnionType;
|
||||
if (type == null) {
|
||||
throw new InvalidProgramException($"{typeName} is not a struct or union? This should not appear.").Attach(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Current Type mustn't be already complete.
|
||||
if (type.IsComplete) {
|
||||
throw new InvalidOperationException($"Redefinition of {typeName}").Attach(this);
|
||||
}
|
||||
|
||||
} else {
|
||||
s_UnnamedCount++;
|
||||
var typeName = (this.StructOrUnion == StructOrUnion.STRUCT ? "struct" : "union") + string.Format(" <unnamed {0}>", s_UnnamedCount);
|
||||
type = StructOrUnionType.CreateIncompleteType(this.StructOrUnion, typeName);
|
||||
}
|
||||
|
||||
var members = Semant(GetMembers, this.MemberDeclns.Value, ref env);
|
||||
type.Define(this.StructOrUnion, members);
|
||||
|
||||
return SemantReturn.Create(env, type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// enum-specifier
|
||||
/// : enum [identifier]? '{' enumerator-list '}'
|
||||
/// | enum identifier
|
||||
///
|
||||
/// enumerator-list
|
||||
/// : enumerator [ ',' enumerator ]*
|
||||
/// </summary>
|
||||
public sealed class EnumSpec : NonBasicTypeSpec {
|
||||
private EnumSpec(Option<String> name, Option<ImmutableList<Enumr>> enumrs) {
|
||||
this.Name = name;
|
||||
this.Enumrs = enumrs;
|
||||
}
|
||||
|
||||
private static EnumSpec Create(Option<String> name, Option<ImmutableList<Enumr>> enumrs) =>
|
||||
new EnumSpec(name, enumrs);
|
||||
|
||||
public static EnumSpec Create(Option<String> name, ImmutableList<Enumr> enumrs) =>
|
||||
Create(name, Option.Some(enumrs));
|
||||
|
||||
public static EnumSpec Create(String name) =>
|
||||
Create(Option.Some(name), Option<ImmutableList<Enumr>>.None);
|
||||
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ExprType> GetExprType(Env env) {
|
||||
if (this.Enumrs.IsNone) {
|
||||
// If no enumerators provided: must find enum Type in the current environment.
|
||||
|
||||
if (this.Name.IsNone) {
|
||||
throw new InvalidProgramException("This should not pass the parser.").Attach(this);
|
||||
}
|
||||
|
||||
var name = this.Name.Value;
|
||||
var entryOpt = env.Find($"enum {name}");
|
||||
|
||||
if (entryOpt.IsNone || entryOpt.Value.Kind != Env.EntryKind.TYPEDEF) {
|
||||
throw new InvalidOperationException($"enum {name} has not been defined.").Attach(this);
|
||||
}
|
||||
|
||||
return SemantReturn.Create(env, new LongType());
|
||||
}
|
||||
|
||||
// If enumerators are provided: add names to environment
|
||||
Int32 offset = 0;
|
||||
foreach (var enumr in this.Enumrs.Value) {
|
||||
|
||||
if (enumr.Init.IsSome) {
|
||||
// If the user provides an initialization Value, use it.
|
||||
var init = SemantExpr(enumr.Init.Value, ref env);
|
||||
init = ABT.TypeCast.MakeCast(init, new LongType());
|
||||
if (!init.IsConstExpr) {
|
||||
throw new InvalidOperationException("Enumerator initialization must have a constant Value.").Attach(this);
|
||||
}
|
||||
offset = ((ConstLong)init).Value;
|
||||
}
|
||||
|
||||
env = env.PushEnum(enumr.Name, new LongType(), offset);
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
// If the user provides a name to the enum, add it to the environment.
|
||||
if (this.Name.IsSome) {
|
||||
var typeName = $"enum {this.Name.Value}";
|
||||
|
||||
if (env.FindInCurrentScope(typeName).IsSome) {
|
||||
throw new InvalidOperationException($"{typeName} is already defined.").Attach(this);
|
||||
}
|
||||
env = env.PushEntry(Env.EntryKind.TYPEDEF, typeName, new LongType());
|
||||
}
|
||||
|
||||
return SemantReturn.Create(env, new LongType());
|
||||
}
|
||||
|
||||
public Option<String> Name { get; }
|
||||
public Option<ImmutableList<Enumr>> Enumrs { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// enumerator
|
||||
/// : enumeration-constant [ '=' constant-expression ]?
|
||||
///
|
||||
/// enumeration-constant
|
||||
/// : identifier
|
||||
/// </summary>
|
||||
public sealed class Enumr : ISyntaxTreeNode {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
private Enumr(String name, Option<Expr> init) {
|
||||
this.Name = name;
|
||||
this.Init = init;
|
||||
}
|
||||
|
||||
public String Name { get; }
|
||||
public Option<Expr> Init { get; }
|
||||
|
||||
public static Enumr Create(String name, Option<Expr> init) =>
|
||||
new Enumr(name, init);
|
||||
|
||||
[Obsolete]
|
||||
public Tuple<Env, String, Int32> GetEnumerator(Env env, Int32 idx) {
|
||||
if (this.Init.IsNone) {
|
||||
return new Tuple<Env, String, Int32>(env, this.Name, idx);
|
||||
}
|
||||
|
||||
ABT.Expr init = this.Init.Value.GetExpr(env, this);
|
||||
|
||||
init = ABT.TypeCast.MakeCast(init, new LongType());
|
||||
if (!init.IsConstExpr) {
|
||||
throw new InvalidOperationException("Error: expected constant integer").Attach(this);
|
||||
}
|
||||
Int32 initIdx = ((ConstLong)init).Value;
|
||||
|
||||
return new Tuple<Env, String, int>(env, this.Name, initIdx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
218
LibIFPSCC/AST/Declarations.cs
Normal file
@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using ABT;
|
||||
using static AST.SemanticAnalysis;
|
||||
|
||||
namespace AST {
|
||||
|
||||
/// <summary>
|
||||
/// declaration
|
||||
/// : declaration-specifiers [Init-declarator-list]? ';'
|
||||
/// </summary>
|
||||
public sealed class Decln : IExternDecln {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
private Decln(DeclnSpecs declnSpecs, ImmutableList<InitDeclr> initDeclrs) {
|
||||
this.DeclnSpecs = declnSpecs;
|
||||
this.InitDeclrs = initDeclrs;
|
||||
}
|
||||
|
||||
public static Decln Create(DeclnSpecs declnSpecs, ImmutableList<InitDeclr> initDeclrs) =>
|
||||
new Decln(declnSpecs, initDeclrs);
|
||||
|
||||
public DeclnSpecs DeclnSpecs { get; }
|
||||
public ImmutableList<InitDeclr> InitDeclrs { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ImmutableList<Tuple<Env, ABT.Decln>>> GetDeclns(Env env) {
|
||||
var storageClass = this.DeclnSpecs.GetStorageClass();
|
||||
var baseType = Semant(this.DeclnSpecs.GetExprType, ref env);
|
||||
|
||||
var declns = this.InitDeclrs.ConvertAll(
|
||||
initDeclr => {
|
||||
var typeAndInitr = Semant(initDeclr.GetDecoratedTypeAndInitr, baseType, ref env);
|
||||
var type = typeAndInitr.Item1;
|
||||
var initr = typeAndInitr.Item2;
|
||||
var name = initDeclr.GetName();
|
||||
|
||||
// Add the new symbol into the environment.
|
||||
Env.EntryKind kind;
|
||||
switch (storageClass) {
|
||||
case StorageClass.AUTO:
|
||||
if (env.IsGlobal()) {
|
||||
kind = Env.EntryKind.GLOBAL;
|
||||
} else {
|
||||
kind = Env.EntryKind.STACK;
|
||||
}
|
||||
break;
|
||||
case StorageClass.EXTERN:
|
||||
kind = Env.EntryKind.GLOBAL;
|
||||
break;
|
||||
case StorageClass.STATIC:
|
||||
kind = Env.EntryKind.GLOBAL;
|
||||
break;
|
||||
case StorageClass.TYPEDEF:
|
||||
kind = Env.EntryKind.TYPEDEF;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException().Attach(initDeclr);
|
||||
}
|
||||
env = env.PushEntry(kind, name, type);
|
||||
|
||||
var decln = new ABT.Decln(name, storageClass, type, initr);
|
||||
decln.Copy(this);
|
||||
|
||||
return Tuple.Create(env, decln);
|
||||
}
|
||||
);
|
||||
|
||||
return SemantReturn.Create(env, declns);
|
||||
}
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ImmutableList<Tuple<Env, ABT.IExternDecln>>> GetExternDecln(Env env) {
|
||||
var declns = Semant(GetDeclns, ref env);
|
||||
var externDeclns = declns.ConvertAll(_ => Tuple.Create(_.Item1, _.Item2 as ABT.IExternDecln));
|
||||
return SemantReturn.Create(env, externDeclns);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// struct-declaration
|
||||
/// : specifier-qualifier-list struct-declarator-list ';'
|
||||
///
|
||||
/// struct-declarator-list
|
||||
/// : struct-declarator [ ',' struct-declarator ]*
|
||||
///
|
||||
/// struct-declarator
|
||||
/// : declarator
|
||||
/// | [declarator]? ':' constant-expression
|
||||
/// </summary>
|
||||
public class StructDecln : ISyntaxTreeNode {
|
||||
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
|
||||
protected StructDecln(SpecQualList specQualList, ImmutableList<StructDeclr> structDeclrs) {
|
||||
this.SpecQualList = specQualList;
|
||||
this.StructDeclrs = structDeclrs;
|
||||
}
|
||||
|
||||
public static StructDecln Create(SpecQualList specQualList, ImmutableList<StructDeclr> structDeclrs) =>
|
||||
new StructDecln(specQualList, structDeclrs);
|
||||
|
||||
public SpecQualList SpecQualList { get; }
|
||||
public ImmutableList<StructDeclr> StructDeclrs { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ImmutableList<Tuple<Option<String>, ExprType>>> GetMemberDeclns(Env env) {
|
||||
// Semant specifier-qualifier-list.
|
||||
var baseType = Semant(this.SpecQualList.GetExprType, ref env);
|
||||
|
||||
// Decorate types, based on struct declarators.
|
||||
var memberTypes =
|
||||
this.StructDeclrs
|
||||
.ConvertAll(
|
||||
structDeclr =>
|
||||
Semant(structDeclr.DecorateType, baseType, ref env)
|
||||
);
|
||||
|
||||
// Get (optional) member names.
|
||||
var memberNames =
|
||||
this.StructDeclrs
|
||||
.ConvertAll(
|
||||
structDeclr => structDeclr.Name
|
||||
);
|
||||
|
||||
return SemantReturn.Create(env, memberNames.Zip(memberTypes, Tuple.Create).ToImmutableList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameter Declaration.
|
||||
///
|
||||
/// int foo(int arg0, int arg1);
|
||||
/// ~~~~~~~~
|
||||
///
|
||||
/// int foo(int, int);
|
||||
/// ~~~
|
||||
///
|
||||
/// The declarator can be completely omitted.
|
||||
/// </summary>
|
||||
public class ParamDecln : ISyntaxTreeNode {
|
||||
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
protected ParamDecln(DeclnSpecs declnSpecs, ParamDeclr paramDeclr) {
|
||||
this.DeclnSpecs = declnSpecs;
|
||||
this.ParamDeclr = paramDeclr;
|
||||
}
|
||||
|
||||
public static ParamDecln Create(DeclnSpecs declnSpecs, Option<ParamDeclr> paramDeclr) =>
|
||||
new ParamDecln(declnSpecs, paramDeclr.IsSome ? paramDeclr.Value : ParamDeclr.Empty);
|
||||
|
||||
public DeclnSpecs DeclnSpecs { get; }
|
||||
public ParamDeclr ParamDeclr { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ExprType> GetParamType(Env env) {
|
||||
var baseType = Semant(this.DeclnSpecs.GetExprType, ref env);
|
||||
var type = Semant(this.ParamDeclr.DecorateType, baseType, ref env);
|
||||
return SemantReturn.Create(env, type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type-name
|
||||
/// : specifier-qualifier-list [abstract-declarator]?
|
||||
/// </summary>
|
||||
public class TypeName : ISyntaxTreeNode {
|
||||
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
protected TypeName(SpecQualList specQualList, AbstractDeclr abstractDeclr) {
|
||||
this.SpecQualList = specQualList;
|
||||
this.AbstractDeclr = abstractDeclr;
|
||||
}
|
||||
|
||||
public static TypeName Create(SpecQualList specQualList, Option<AbstractDeclr> abstractDeclr) =>
|
||||
new TypeName(specQualList, abstractDeclr.IsSome ? abstractDeclr.Value : AbstractDeclr.Empty);
|
||||
|
||||
public SpecQualList SpecQualList { get; }
|
||||
public AbstractDeclr AbstractDeclr { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ExprType> GetExprType(Env env) {
|
||||
var baseType = Semant(this.SpecQualList.GetExprType, ref env);
|
||||
var type = Semant(this.AbstractDeclr.DecorateType, baseType, ref env);
|
||||
return SemantReturn.Create(env, type);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
416
LibIFPSCC/AST/Declarators.cs
Normal file
@ -0,0 +1,416 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace AST {
|
||||
using static SemanticAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Modify a Type into a function, array, or pointer
|
||||
/// </summary>
|
||||
public abstract class TypeModifier : ISyntaxTreeNode {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
[SemantMethod]
|
||||
public abstract ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType baseType);
|
||||
}
|
||||
|
||||
public sealed class FunctionModifier : TypeModifier {
|
||||
private FunctionModifier(Option<ParamTypeList> paramTypeList) {
|
||||
this.ParamTypeList = paramTypeList;
|
||||
}
|
||||
|
||||
public static FunctionModifier Create(Option<ParamTypeList> paramTypeList) =>
|
||||
new FunctionModifier(paramTypeList);
|
||||
|
||||
public Option<ParamTypeList> ParamTypeList { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType returnType) {
|
||||
if (this.ParamTypeList.IsNone) {
|
||||
return SemantReturn.Create(env, ABT.FunctionType.Create(returnType));
|
||||
}
|
||||
|
||||
var paramTypeList = this.ParamTypeList.Value;
|
||||
|
||||
var namesAndTypes = Semant(paramTypeList.GetNamesAndTypes, ref env);
|
||||
var hasVarArgs = paramTypeList.HasVarArgs;
|
||||
|
||||
return SemantReturn.Create(env, ABT.FunctionType.Create(returnType, namesAndTypes, hasVarArgs));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// parameter-Type-list
|
||||
/// : parameter-list [ ',' '...' ]?
|
||||
///
|
||||
/// parameter-list
|
||||
/// : parameter-declaration [ ',' parameter-declaration ]*
|
||||
/// </summary>
|
||||
public sealed class ParamTypeList : ISyntaxTreeNode {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
private ParamTypeList(ImmutableList<ParamDecln> paramDeclns, Boolean hasVarArgs) {
|
||||
this.ParamDeclns = paramDeclns;
|
||||
this.HasVarArgs = hasVarArgs;
|
||||
}
|
||||
|
||||
public static ParamTypeList Create(ImmutableList<ParamDecln> paramDeclns, Boolean hasVarArgs) =>
|
||||
new ParamTypeList(paramDeclns, hasVarArgs);
|
||||
|
||||
public static ParamTypeList Create() =>
|
||||
Create(ImmutableList<ParamDecln>.Empty, true);
|
||||
|
||||
public ImmutableList<ParamDecln> ParamDeclns { get; }
|
||||
public Boolean HasVarArgs { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ImmutableList<Tuple<Option<String>, ABT.ExprType>>> GetNamesAndTypes(ABT.Env env) {
|
||||
var namesAndTypes = this.ParamDeclns.ConvertAll(
|
||||
paramDecln =>
|
||||
Tuple.Create(
|
||||
paramDecln.ParamDeclr.Name,
|
||||
Semant(paramDecln.GetParamType, ref env)
|
||||
)
|
||||
);
|
||||
return SemantReturn.Create(env, namesAndTypes);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ArrayModifier : TypeModifier {
|
||||
private ArrayModifier(Option<Expr> numElements) {
|
||||
this.NumElems = numElements;
|
||||
}
|
||||
|
||||
public static ArrayModifier Create(Option<Expr> numElements) =>
|
||||
new ArrayModifier(numElements);
|
||||
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType elemType) {
|
||||
if (this.NumElems.IsNone) {
|
||||
return SemantReturn.Create(env, new ABT.IncompleteArrayType(elemType));
|
||||
}
|
||||
|
||||
// Get number of elements.
|
||||
// Be careful: the environment might change.
|
||||
var numElems = SemantExpr(this.NumElems.Value, ref env);
|
||||
|
||||
// Try to cast number of elements to a integer.
|
||||
// TODO: allow float???
|
||||
numElems = ABT.TypeCast.MakeCast(numElems, new ABT.LongType(true, false));
|
||||
|
||||
if (!numElems.IsConstExpr) {
|
||||
throw new InvalidOperationException("Number of elements of an array must be constant.").Attach(this);
|
||||
}
|
||||
|
||||
return SemantReturn.Create(env, new ABT.ArrayType(elemType, ((ABT.ConstLong) numElems).Value));
|
||||
}
|
||||
|
||||
public Option<Expr> NumElems { get; }
|
||||
}
|
||||
|
||||
public sealed class PointerModifier : TypeModifier {
|
||||
private PointerModifier(ImmutableList<TypeQual> typeQuals) {
|
||||
this.TypeQuals = typeQuals;
|
||||
}
|
||||
|
||||
public static PointerModifier Create(ImmutableList<TypeQual> typeQuals) =>
|
||||
new PointerModifier(typeQuals);
|
||||
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType targetType) {
|
||||
var isConst = this.TypeQuals.Contains(TypeQual.CONST);
|
||||
var isVolatile = this.TypeQuals.Contains(TypeQual.VOLATILE);
|
||||
return SemantReturn.Create(env, new ABT.PointerType(targetType, isConst, isVolatile));
|
||||
}
|
||||
|
||||
public ImmutableList<TypeQual> TypeQuals { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Has a list of modifiers. Has an optional name.
|
||||
/// </summary>
|
||||
public class ParamDeclr : AbstractDeclr {
|
||||
protected ParamDeclr(Option<String> name, ImmutableList<TypeModifier> typeModifiers)
|
||||
: base(typeModifiers) {
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
public static ParamDeclr Create(Declr declr) =>
|
||||
new ParamDeclr(Option.Some(declr.Name), declr.TypeModifiers);
|
||||
|
||||
public static ParamDeclr Create(AbstractDeclr abstractDeclr) =>
|
||||
new ParamDeclr(Option<String>.None, abstractDeclr.TypeModifiers);
|
||||
|
||||
public new static ParamDeclr Empty { get; }
|
||||
= new ParamDeclr(Option<String>.None, ImmutableList<TypeModifier>.Empty);
|
||||
|
||||
public Option<String> Name { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// struct-declarator
|
||||
/// : [declarator]? ':' constant-expression
|
||||
/// | declarator
|
||||
/// </summary>
|
||||
public sealed class StructDeclr : ParamDeclr {
|
||||
private StructDeclr(Option<String> name, ImmutableList<TypeModifier> typeModifiers, Option<Expr> numBits)
|
||||
: base(name, typeModifiers) {
|
||||
this.NumBits = numBits;
|
||||
}
|
||||
|
||||
public static StructDeclr Create(Option<Declr> declr, Expr numBits) {
|
||||
return declr.IsNone
|
||||
? new StructDeclr(Option<String>.None, ImmutableList<TypeModifier>.Empty, Option.Some(numBits))
|
||||
: new StructDeclr(Option.Some(declr.Value.Name), declr.Value.TypeModifiers, Option<Expr>.None);
|
||||
}
|
||||
|
||||
public new static StructDeclr Create(Declr declr) =>
|
||||
new StructDeclr(Option.Some(declr.Name), declr.TypeModifiers, Option<Expr>.None);
|
||||
|
||||
public Option<Expr> NumBits { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// abstract-declarator
|
||||
/// : [pointer]? direct-abstract-declarator
|
||||
/// | pointer
|
||||
///
|
||||
/// direct-abstract-declarator
|
||||
/// : [
|
||||
/// '(' abstract-declarator ')'
|
||||
/// | '[' [constant-expression]? ']' // array modifier
|
||||
/// | '(' [parameter-type_list]? ')' // function modifier
|
||||
/// ] [
|
||||
/// '[' [constant-expression]? ']' // array modifier
|
||||
/// | '(' [parameter-Type-list]? ')' // function modifier
|
||||
/// ]*
|
||||
///
|
||||
/// An abstract declarator is a list of (pointer, function, or array) Type modifiers
|
||||
/// </summary>
|
||||
public class AbstractDeclr {
|
||||
protected AbstractDeclr(ImmutableList<TypeModifier> typeModifiers) {
|
||||
this.TypeModifiers = typeModifiers;
|
||||
}
|
||||
|
||||
public static AbstractDeclr Create<Modifier>(ImmutableList<Modifier> typeModifiers) where Modifier : TypeModifier =>
|
||||
new AbstractDeclr(typeModifiers.ToImmutableList<TypeModifier>());
|
||||
|
||||
public static AbstractDeclr Empty { get; } =
|
||||
Create(ImmutableList<TypeModifier>.Empty);
|
||||
|
||||
public static AbstractDeclr Add(AbstractDeclr abstractDeclr, TypeModifier typeModifier) =>
|
||||
Create(abstractDeclr.TypeModifiers.Add(typeModifier));
|
||||
|
||||
public static AbstractDeclr Add(ImmutableList<PointerModifier> pointerModifiers, AbstractDeclr abstractDeclr) =>
|
||||
Create(abstractDeclr.TypeModifiers.AddRange(pointerModifiers));
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType baseType) {
|
||||
var type = this.TypeModifiers
|
||||
.Reverse() // The first Type modifier is nearest to the symbol name, which indicates the outmost Type.
|
||||
.Aggregate( // Wrap up the Type based on the Type modifiers.
|
||||
baseType, (currentType, typeModifier) => Semant(typeModifier.DecorateType, currentType, ref env)
|
||||
);
|
||||
|
||||
return SemantReturn.Create(env, type);
|
||||
}
|
||||
|
||||
public ImmutableList<TypeModifier> TypeModifiers { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Has a name and a list of modifiers.
|
||||
/// </summary>
|
||||
public sealed class Declr : AbstractDeclr {
|
||||
private Declr(String name, ImmutableList<TypeModifier> typeModifiers)
|
||||
: base(typeModifiers) {
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
public static Declr Create(String name, ImmutableList<TypeModifier> typeModifiers) =>
|
||||
new Declr(name, typeModifiers);
|
||||
|
||||
public static Declr Create(String name) =>
|
||||
new Declr(name, ImmutableList<TypeModifier>.Empty);
|
||||
|
||||
public static Declr Create(Option<ImmutableList<PointerModifier>> pointerModifiers, Declr declr) =>
|
||||
Add(pointerModifiers.IsSome ? pointerModifiers.Value : ImmutableList<PointerModifier>.Empty, declr);
|
||||
|
||||
public static Declr Add(Declr declr, TypeModifier typeModifier) =>
|
||||
Create(declr.Name, declr.TypeModifiers.Add(typeModifier));
|
||||
|
||||
public static Declr Add(ImmutableList<PointerModifier> pointerModifiers, Declr declr) =>
|
||||
Create(declr.Name, declr.TypeModifiers.AddRange(pointerModifiers));
|
||||
|
||||
public String Name { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Init-declarator
|
||||
/// : declarator [ '=' initializer ]?
|
||||
/// </summary>
|
||||
public sealed class InitDeclr : ISyntaxTreeNode {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
private InitDeclr(Declr declr, Option<Initr> initr) {
|
||||
this.Declr = declr;
|
||||
this.Initr = initr;
|
||||
}
|
||||
|
||||
public Declr Declr { get; }
|
||||
public Option<Initr> Initr { get; }
|
||||
|
||||
public static InitDeclr Create(Declr declr, Option<Initr> initr)
|
||||
=> new InitDeclr(declr, initr);
|
||||
|
||||
[SemantMethod]
|
||||
public String GetName() => this.Declr.Name;
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<Tuple<ABT.ExprType, Option<ABT.Initr>>> GetDecoratedTypeAndInitr(ABT.Env env, ABT.ExprType baseType) {
|
||||
|
||||
// Get the Type based on the declarator. Note that this might be an incomplete array.
|
||||
var type = Semant(this.Declr.DecorateType, baseType, ref env);
|
||||
|
||||
Option<ABT.Initr> initrOption;
|
||||
|
||||
if (this.Initr.IsNone) {
|
||||
initrOption = Option<ABT.Initr>.None;
|
||||
|
||||
} else {
|
||||
// If an initializer is present:
|
||||
|
||||
var initr = Semant(this.Initr.Value.GetInitr, ref env);
|
||||
|
||||
// Check that the initializer conforms to the Type.
|
||||
initr = initr.ConformType(type);
|
||||
|
||||
// If the object is an incomplete array, we must determine the length based on the initializer.
|
||||
if (type.Kind == ABT.ExprTypeKind.INCOMPLETE_ARRAY) {
|
||||
var incomplete = (ABT.IncompleteArrayType)type;
|
||||
// Now we need to determine the length.
|
||||
// Find the last element in the Init list.
|
||||
var lastOffset = -1;
|
||||
initr.Iterate(type, (offset, _) => { lastOffset = offset; });
|
||||
|
||||
/*
|
||||
if (lastOffset == -1) {
|
||||
throw new InvalidOperationException("Cannot determine the length of the array based on an empty initializer list.").Attach(this);
|
||||
}
|
||||
*/
|
||||
|
||||
var elemType = incomplete.ElemType;
|
||||
|
||||
int numElems = 0;
|
||||
if (lastOffset != -1)
|
||||
{
|
||||
var initList = initr as ABT.InitList;
|
||||
if (initList != null)
|
||||
{
|
||||
numElems = initList.initrs.Count;
|
||||
} else
|
||||
{
|
||||
numElems = 1 + lastOffset / incomplete.ElemType.SizeOf;
|
||||
}
|
||||
}
|
||||
|
||||
type = incomplete.SetDeclaratorElems(numElems);
|
||||
}
|
||||
|
||||
initrOption = Option.Some(initr);
|
||||
}
|
||||
|
||||
// Now everything is created. Check that the Type is complete.
|
||||
if (!type.IsComplete) {
|
||||
throw new InvalidOperationException("Cannot create an object with an incomplete Type.").Attach(this);
|
||||
}
|
||||
|
||||
return SemantReturn.Create(env, Tuple.Create(type, initrOption));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// initializer
|
||||
/// : assignment-expression
|
||||
/// | '{' initializer-list '}'
|
||||
/// | '{' initializer-list ',' '}'
|
||||
///
|
||||
/// initializer-list
|
||||
/// : initializer [ ',' initializer ]*
|
||||
/// </summary>
|
||||
public abstract class Initr : ISyntaxTreeNode {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
[SemantMethod]
|
||||
public abstract ISemantReturn<ABT.Initr> GetInitr(ABT.Env env);
|
||||
}
|
||||
|
||||
public sealed class InitExpr : Initr {
|
||||
private InitExpr(Expr expr) {
|
||||
this.Expr = expr;
|
||||
}
|
||||
|
||||
public static InitExpr Create(Expr expr) =>
|
||||
new InitExpr(expr);
|
||||
|
||||
public Expr Expr { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ABT.Initr> GetInitr(ABT.Env env) {
|
||||
var expr = SemantExpr(this.Expr, ref env);
|
||||
return SemantReturn.Create(env, new ABT.InitExpr(expr));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class InitList : Initr {
|
||||
private InitList(ImmutableList<Initr> initrs) {
|
||||
this.Initrs = initrs;
|
||||
}
|
||||
|
||||
public static InitList Create(ImmutableList<Initr> initrs) =>
|
||||
new InitList(initrs);
|
||||
|
||||
public static InitList Create(Option<ImmutableList<Initr>> initrs)
|
||||
{
|
||||
if (initrs.IsNone) return Create(ImmutableList<Initr>.Empty);
|
||||
return Create(initrs.Value);
|
||||
}
|
||||
|
||||
public ImmutableList<Initr> Initrs { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public override ISemantReturn<ABT.Initr> GetInitr(ABT.Env env) {
|
||||
var initrs = this.Initrs.ConvertAll(
|
||||
initr => Semant(initr.GetInitr, ref env)
|
||||
);
|
||||
return SemantReturn.Create(env, new ABT.InitList(initrs.ToList()));
|
||||
}
|
||||
}
|
||||
}
|
317
LibIFPSCC/AST/Expressions.cs
Normal file
@ -0,0 +1,317 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace AST {
|
||||
|
||||
// 3.2.1.5
|
||||
/* First, if either operand has Type long double, the other operand is converted to long double.
|
||||
* Otherwise, if either operand has Type double, the other operand is converted to double.
|
||||
* Otherwise, if either operand has Type float, the other operand is converted to float.
|
||||
* Otherwise, the integral promotions are performed on both operands.
|
||||
* Then the following rules are applied:
|
||||
* If either operand has Type unsigned long Int32, the other operand is converted to unsigned long Int32.
|
||||
* Otherwise, if one operand has Type long Int32 and the other has Type unsigned Int32, if a long Int32 can represent all values of an unsigned Int32, the operand of Type unsigned Int32 is converted to long Int32;
|
||||
* if a long Int32 cannot represent all the values of an unsigned Int32, both operands are converted to unsigned long Int32. Otherwise, if either operand has Type long Int32, the other operand is converted to long Int32.
|
||||
* Otherwise, if either operand has Type unsigned Int32, the other operand is converted to unsigned Int32.
|
||||
* Otherwise, both operands have Type Int32.*/
|
||||
|
||||
// My simplification:
|
||||
// I let long = int, long double = double
|
||||
|
||||
public abstract class Expr : ISyntaxTreeNode {
|
||||
protected abstract ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info);
|
||||
|
||||
public ABT.Expr GetExpr(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
var ret = GetExprImpl(env, info);
|
||||
ret.Copy(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExprInitList : Expr
|
||||
{
|
||||
protected ExprInitList(InitList list)
|
||||
{
|
||||
this.List = list;
|
||||
}
|
||||
|
||||
public InitList List { get; }
|
||||
|
||||
public static Expr Create(InitList list) =>
|
||||
new ExprInitList(list);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
var list = this.List.GetInitr(env);
|
||||
return new ABT.ExprInitList(list.Value as ABT.InitList, env);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Only a name
|
||||
/// </summary>
|
||||
public class Variable : Expr {
|
||||
public Variable(String name) {
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
public static Expr Create(String name) =>
|
||||
new Variable(name);
|
||||
|
||||
public String Name { get; }
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
Option<ABT.Env.Entry> entry_opt = env.Find(this.Name);
|
||||
|
||||
if (entry_opt.IsNone) {
|
||||
throw new InvalidOperationException($"Cannot find variable '{this.Name}'").Attach(info);
|
||||
}
|
||||
|
||||
ABT.Env.Entry entry = entry_opt.Value;
|
||||
|
||||
switch (entry.Kind) {
|
||||
case ABT.Env.EntryKind.TYPEDEF:
|
||||
throw new InvalidOperationException($"Expected a variable '{this.Name}', not a typedef.").Attach(info);
|
||||
case ABT.Env.EntryKind.ENUM:
|
||||
return new ABT.ConstLong(entry.Offset, env);
|
||||
case ABT.Env.EntryKind.FRAME:
|
||||
case ABT.Env.EntryKind.GLOBAL:
|
||||
case ABT.Env.EntryKind.STACK:
|
||||
return new ABT.Variable(entry.Type, this.Name, env);
|
||||
default:
|
||||
throw new InvalidOperationException($"Cannot find variable '{this.Name}'").Attach(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of assignment expressions.
|
||||
/// e.g.
|
||||
/// a = 3, b = 4;
|
||||
/// </summary>
|
||||
public class AssignmentList : Expr {
|
||||
protected AssignmentList(ImmutableList<Expr> exprs) {
|
||||
this.Exprs = exprs;
|
||||
}
|
||||
|
||||
public ImmutableList<Expr> Exprs { get; }
|
||||
|
||||
public static Expr Create(ImmutableList<Expr> exprs) =>
|
||||
new AssignmentList(exprs);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ImmutableList<ABT.Expr> exprs = this.Exprs.ConvertAll(expr => expr.GetExpr(env, info));
|
||||
return new ABT.AssignList(exprs, info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Conditional Expression
|
||||
///
|
||||
/// Cond ? true_expr : false_expr
|
||||
///
|
||||
/// Cond must be of scalar Type
|
||||
///
|
||||
/// 1. if both true_expr and false_expr have arithmetic types
|
||||
/// perform usual arithmetic conversion
|
||||
/// 2.
|
||||
/// </summary>
|
||||
// TODO : What if const???
|
||||
public class ConditionalExpression : Expr {
|
||||
public ConditionalExpression(Expr cond, Expr trueExpr, Expr falseExpr) {
|
||||
this.Cond = cond;
|
||||
this.TrueExpr = trueExpr;
|
||||
this.FalseExpr = falseExpr;
|
||||
}
|
||||
|
||||
public Expr Cond { get; }
|
||||
public Expr TrueExpr { get; }
|
||||
public Expr FalseExpr { get; }
|
||||
|
||||
public static Expr Create(Expr cond, Expr trueExpr, Expr falseExpr) =>
|
||||
new ConditionalExpression(cond, trueExpr, falseExpr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr cond = this.Cond.GetExpr(env, info);
|
||||
|
||||
if (!cond.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Expected a scalar condition in conditional expression.").Attach(cond);
|
||||
}
|
||||
|
||||
if (cond.Type.IsIntegral) {
|
||||
cond = ABT.TypeCast.IntegralPromotion(cond).Item1;
|
||||
}
|
||||
|
||||
ABT.Expr true_expr = this.TrueExpr.GetExpr(env, info);
|
||||
ABT.Expr false_expr = this.FalseExpr.GetExpr(env, info);
|
||||
|
||||
// 1. if both true_expr and false_Expr have arithmetic types:
|
||||
// perform usual arithmetic conversion
|
||||
if (true_expr.Type.IsArith && false_expr.Type.IsArith) {
|
||||
var r_cast = ABT.TypeCast.UsualArithmeticConversion(true_expr, false_expr);
|
||||
true_expr = r_cast.Item1;
|
||||
false_expr = r_cast.Item2;
|
||||
return new ABT.ConditionalExpr(cond, true_expr, false_expr, true_expr.Type);
|
||||
}
|
||||
|
||||
if (true_expr.Type.Kind != false_expr.Type.Kind) {
|
||||
throw new InvalidOperationException("Operand types not match in conditional expression.").Attach(info);
|
||||
}
|
||||
|
||||
switch (true_expr.Type.Kind) {
|
||||
// 2. if both true_expr and false_expr have struct or union Type
|
||||
// make sure they are compatible
|
||||
case ABT.ExprTypeKind.STRUCT_OR_UNION:
|
||||
if (!true_expr.Type.EqualType(false_expr.Type)) {
|
||||
throw new InvalidOperationException("Expected compatible types in conditional expression.").Attach(info);
|
||||
}
|
||||
return new ABT.ConditionalExpr(cond, true_expr, false_expr, true_expr.Type);
|
||||
|
||||
// 3. if both true_expr and false_expr have void Type
|
||||
// return void
|
||||
case ABT.ExprTypeKind.VOID:
|
||||
return new ABT.ConditionalExpr(cond, true_expr, false_expr, true_expr.Type);
|
||||
|
||||
// 4. if both true_expr and false_expr have pointer Type
|
||||
case ABT.ExprTypeKind.POINTER:
|
||||
|
||||
// if either points to void, convert to void *
|
||||
if (((ABT.PointerType)true_expr.Type).RefType.Kind == ABT.ExprTypeKind.VOID
|
||||
|| ((ABT.PointerType)false_expr.Type).RefType.Kind == ABT.ExprTypeKind.VOID) {
|
||||
return new ABT.ConditionalExpr(cond, true_expr, false_expr, new ABT.PointerType(new ABT.VoidType()));
|
||||
}
|
||||
|
||||
throw new NotImplementedException("More comparisons here.").Attach(info);
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Expected compatible types in conditional expression.").Attach(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function call: func(args)
|
||||
/// </summary>
|
||||
public class FuncCall : Expr {
|
||||
protected FuncCall(Expr func, ImmutableList<Expr> args) {
|
||||
this.Func = func;
|
||||
this.Args = args;
|
||||
}
|
||||
|
||||
public static Expr Create(Expr func, ImmutableList<Expr> args) =>
|
||||
new FuncCall(func, args);
|
||||
|
||||
public Expr Func { get; }
|
||||
|
||||
public ImmutableList<Expr> Args { get; }
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
|
||||
// Step 1: get arguments passed into the function.
|
||||
// Note that currently the arguments are not casted based on the prototype.
|
||||
var args = this.Args.Select(_ => _.GetExpr(env, info)).ToList();
|
||||
|
||||
// A special case:
|
||||
// If we cannot find the function prototype in the environment, make one up.
|
||||
// This function returns int.
|
||||
// Update the environment to add this function Type.
|
||||
if ((this.Func is Variable) && env.Find((this.Func as Variable).Name).IsNone) {
|
||||
// TODO: get this env used.
|
||||
env = env.PushEntry(ABT.Env.EntryKind.TYPEDEF, (this.Func as Variable).Name, ABT.FunctionType.Create(new ABT.LongType(true), args.ConvertAll(_ => Tuple.Create("", _.Type)), false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: get function expression.
|
||||
ABT.Expr func = this.Func.GetExpr(env, info);
|
||||
|
||||
// Step 3: get the function Type.
|
||||
ABT.FunctionType func_type;
|
||||
switch (func.Type.Kind) {
|
||||
case ABT.ExprTypeKind.FUNCTION:
|
||||
func_type = func.Type as ABT.FunctionType;
|
||||
break;
|
||||
|
||||
case ABT.ExprTypeKind.POINTER:
|
||||
var ref_t = (func.Type as ABT.PointerType).RefType;
|
||||
if (!(ref_t is ABT.FunctionType)) {
|
||||
throw new InvalidOperationException("Expected a function pointer.").Attach(info);
|
||||
}
|
||||
func_type = ref_t as ABT.FunctionType;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Expected a function in function call.").Attach(info);
|
||||
}
|
||||
|
||||
|
||||
Int32 num_args_prototype = func_type.Args.Count;
|
||||
Int32 num_args_actual = args.Count;
|
||||
|
||||
// If this function doesn't take varargs, make sure the number of arguments match that in the prototype.
|
||||
if (!func_type.HasVarArgs && num_args_actual != num_args_prototype) {
|
||||
throw new InvalidOperationException("Number of arguments mismatch.").Attach(info);
|
||||
}
|
||||
|
||||
// Anyway, you can't call a function with fewer arguments than the prototype.
|
||||
if (num_args_actual < num_args_prototype) {
|
||||
throw new InvalidOperationException("Too few arguments.").Attach(info);
|
||||
}
|
||||
|
||||
// Make implicit cast.
|
||||
args = args.GetRange(0, num_args_prototype).Zip(func_type.Args,
|
||||
(arg, entry) => ABT.TypeCast.MakeCast(arg, entry.type)
|
||||
).Concat(args.GetRange(num_args_prototype, num_args_actual - num_args_prototype)).ToList();
|
||||
|
||||
return new ABT.FuncCall(func, func_type, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expr.attrib: get an attribute from a struct or union
|
||||
/// </summary>
|
||||
public class Attribute : Expr {
|
||||
protected Attribute(Expr expr, String member) {
|
||||
this.Expr = expr;
|
||||
this.Member = member;
|
||||
}
|
||||
|
||||
public Expr Expr { get; }
|
||||
public String Member { get; }
|
||||
|
||||
public static Expr Create(Expr expr, String member) =>
|
||||
new Attribute(expr, member);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
String name = this.Member;
|
||||
|
||||
if (expr.Type.Kind != ABT.ExprTypeKind.STRUCT_OR_UNION) {
|
||||
throw new InvalidOperationException("Must get the attribute from a struct or union.").Attach(info);
|
||||
}
|
||||
|
||||
ABT.Utils.StoreEntry entry = (expr.Type as ABT.StructOrUnionType).Attribs.First(_ => _.name == name);
|
||||
ABT.ExprType type = entry.type;
|
||||
|
||||
return new ABT.Attribute(expr, name, type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
106
LibIFPSCC/AST/ExternalDeclarations.cs
Normal file
@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using ABT;
|
||||
using static AST.SemanticAnalysis;
|
||||
|
||||
namespace AST {
|
||||
public interface ISyntaxTreeNode : IStoredLineInfo { }
|
||||
|
||||
/// <summary>
|
||||
/// A translation unit consists of a list of external declarations - functions and objects.
|
||||
/// </summary>
|
||||
public sealed class TranslnUnit : ISyntaxTreeNode {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
private TranslnUnit(ImmutableList<IExternDecln> declns) {
|
||||
this.Declns = declns;
|
||||
}
|
||||
|
||||
public static TranslnUnit Create(ImmutableList<IExternDecln> externDeclns) =>
|
||||
new TranslnUnit(externDeclns);
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ABT.TranslnUnit> GetTranslnUnit() {
|
||||
var env = new Env();
|
||||
var externDeclns = this.Declns.Aggregate(ImmutableList<Tuple<Env, ABT.IExternDecln>>.Empty, (acc, externDecln) => acc.AddRange(Semant(externDecln.GetExternDecln, ref env))
|
||||
);
|
||||
return SemantReturn.Create(env, new ABT.TranslnUnit(externDeclns.ToList()));
|
||||
}
|
||||
|
||||
public ImmutableList<IExternDecln> Declns { get; }
|
||||
}
|
||||
|
||||
|
||||
public interface IExternDecln : ISyntaxTreeNode {
|
||||
[SemantMethod]
|
||||
ISemantReturn<ImmutableList<Tuple<Env, ABT.IExternDecln>>> GetExternDecln(Env env);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A function definition gives the implementation.
|
||||
/// </summary>
|
||||
public sealed class FuncDef : IExternDecln {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
public FuncDef(DeclnSpecs specs, Declr declr, CompoundStmt stmt) {
|
||||
this.Specs = specs;
|
||||
this.Declr = declr;
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public static FuncDef Create(Option<DeclnSpecs> declnSpecs, Declr declr, Stmt body) =>
|
||||
new FuncDef(declnSpecs.IsSome ? declnSpecs.Value : DeclnSpecs.Empty, declr, body as CompoundStmt);
|
||||
|
||||
public DeclnSpecs Specs { get; }
|
||||
public Declr Declr { get; }
|
||||
public CompoundStmt Stmt { get; }
|
||||
|
||||
[SemantMethod]
|
||||
public ISemantReturn<ImmutableList<Tuple<Env, ABT.IExternDecln>>> GetExternDecln(Env env) {
|
||||
var storageClass = this.Specs.GetStorageClass();
|
||||
var baseType = Semant(this.Specs.GetExprType, ref env);
|
||||
var name = this.Declr.Name;
|
||||
var type = Semant(this.Declr.DecorateType, baseType, ref env);
|
||||
|
||||
var funcType = type as FunctionType;
|
||||
if (funcType == null) {
|
||||
throw new InvalidOperationException("Expected a function Type.").Attach(this);
|
||||
}
|
||||
|
||||
switch (storageClass) {
|
||||
case StorageClass.AUTO:
|
||||
case StorageClass.EXTERN:
|
||||
case StorageClass.STATIC:
|
||||
env = env.PushEntry(Env.EntryKind.GLOBAL, name, type);
|
||||
break;
|
||||
case StorageClass.TYPEDEF:
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid storage class specifier for function definition.").Attach(this);
|
||||
}
|
||||
|
||||
env = env.InScope();
|
||||
env = env.SetCurrentFunction(funcType);
|
||||
var stmt = SemantStmt(this.Stmt.GetStmt, ref env);
|
||||
env = env.OutScope();
|
||||
|
||||
ABT.IExternDecln funcDef = new ABT.FuncDef(name, storageClass, funcType, stmt);
|
||||
funcDef.Copy(this);
|
||||
|
||||
return SemantReturn.Create(env, ImmutableList.Create(Tuple.Create(env, funcDef)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
58
LibIFPSCC/AST/SemantUtils.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
|
||||
namespace AST {
|
||||
public interface ISemantReturn<out T> {
|
||||
T Value { get; }
|
||||
ABT.Env Env { get; }
|
||||
}
|
||||
|
||||
public class SemantReturn<T> : ISemantReturn<T> {
|
||||
protected SemantReturn(ABT.Env env, T value) {
|
||||
this.Value = value;
|
||||
this.Env = env;
|
||||
}
|
||||
|
||||
public static ISemantReturn<T> Create(ABT.Env env, T value) =>
|
||||
new SemantReturn<T>(env, value);
|
||||
|
||||
public T Value { get; }
|
||||
public ABT.Env Env { get; }
|
||||
}
|
||||
|
||||
public class SemantReturn {
|
||||
public static ISemantReturn<T> Create<T>(ABT.Env env, T value) =>
|
||||
SemantReturn<T>.Create(env, value);
|
||||
}
|
||||
|
||||
public static class SemanticAnalysis {
|
||||
public class SemantMethod : System.Attribute { }
|
||||
|
||||
public static R Semant<R>(Func<ABT.Env, ISemantReturn<R>> semantFunc, ref ABT.Env env) {
|
||||
var semantReturn = semantFunc(env);
|
||||
env = semantReturn.Env;
|
||||
return semantReturn.Value;
|
||||
}
|
||||
|
||||
public static R Semant<I, R>(Func<ABT.Env, I, ISemantReturn<R>> semantFunc, I arg, ref ABT.Env env) {
|
||||
var semantReturn = semantFunc(env, arg);
|
||||
env = semantReturn.Env;
|
||||
return semantReturn.Value;
|
||||
}
|
||||
|
||||
public static ABT.Expr SemantExpr(Func<ABT.Env, ILineInfo, ABT.Expr> semantFunc, ILineInfo info, ref ABT.Env env) {
|
||||
var semantReturn = semantFunc(env, info);
|
||||
env = semantReturn.Env;
|
||||
return semantReturn;
|
||||
}
|
||||
|
||||
public static ABT.Expr SemantExpr(Expr expr, ref ABT.Env env) =>
|
||||
SemantExpr(expr.GetExpr, expr, ref env);
|
||||
|
||||
public static ABT.Stmt SemantStmt(Func<ABT.Env, Tuple<ABT.Env, ABT.Stmt>> semantFunc, ref ABT.Env env) {
|
||||
var semantReturn = semantFunc(env);
|
||||
env = semantReturn.Item1;
|
||||
return semantReturn.Item2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
435
LibIFPSCC/AST/Statements.cs
Normal file
@ -0,0 +1,435 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using static AST.SemanticAnalysis;
|
||||
|
||||
namespace AST {
|
||||
|
||||
public abstract class Stmt : ISyntaxTreeNode {
|
||||
public int Line { get; private set; }
|
||||
public int Column { get; private set; }
|
||||
|
||||
public void Copy(ILineInfo info)
|
||||
{
|
||||
Line = info.Line;
|
||||
Column = info.Column;
|
||||
}
|
||||
public abstract Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// goto label;
|
||||
/// </summary>
|
||||
public sealed class GotoStmt : Stmt {
|
||||
public GotoStmt(String label) {
|
||||
this.Label = label;
|
||||
}
|
||||
public String Label { get; }
|
||||
|
||||
public static Stmt Create(String label) =>
|
||||
new GotoStmt(label);
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.GotoStmt(this.Label));
|
||||
}
|
||||
|
||||
//public override ISemantReturn<AST.Stmt> SemantStmt(AST.Env env) {
|
||||
// return SemantReturn.Create(env, new AST.GotoStmt(this.Label));
|
||||
//}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// continue;
|
||||
/// </summary>
|
||||
public sealed class ContStmt : Stmt {
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.ContStmt());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// break;
|
||||
/// </summary>
|
||||
public sealed class BreakStmt : Stmt {
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.BreakStmt());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// return [expr];
|
||||
/// </summary>
|
||||
public sealed class ReturnStmt : Stmt {
|
||||
public ReturnStmt(Option<Expr> expr) {
|
||||
this.Expr = expr;
|
||||
}
|
||||
|
||||
public static Stmt Create(Option<Expr> expr) =>
|
||||
new ReturnStmt(expr);
|
||||
|
||||
public readonly Option<Expr> Expr;
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
var expr = this.Expr.Map(_ => _.GetExpr(env, this));
|
||||
expr = expr.Map(_ => ABT.TypeCast.MakeCast(_, env.GetCurrentFunction().ReturnType));
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.ReturnStmt(expr));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// {
|
||||
/// declaration*
|
||||
/// statement*
|
||||
/// }
|
||||
/// </summary>
|
||||
public sealed class CompoundStmt : Stmt {
|
||||
public CompoundStmt(List<Decln> declns, List<Stmt> stmts) {
|
||||
this.Declns = declns;
|
||||
this.Stmts = stmts;
|
||||
}
|
||||
public List<Decln> Declns { get; }
|
||||
public List<Stmt> Stmts { get; }
|
||||
|
||||
public static Stmt Create(ImmutableList<Decln> declns, ImmutableList<Stmt> stmts) =>
|
||||
new CompoundStmt(declns.ToList(), stmts.ToList());
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
env = env.InScope();
|
||||
List<Tuple<ABT.Env, ABT.Decln>> declns = new List<Tuple<ABT.Env, ABT.Decln>>();
|
||||
List<Tuple<ABT.Env, ABT.Stmt>> stmts = new List<Tuple<ABT.Env, ABT.Stmt>>();
|
||||
|
||||
foreach (Decln decln in this.Declns) {
|
||||
//Tuple<AST.Env, List<Tuple<AST.Env, AST.Decln>>> r_decln = decln.GetDeclns_(env);
|
||||
//env = r_decln.Item1;
|
||||
//declns.AddRange(r_decln.Item2);
|
||||
|
||||
var declns_ = Semant(decln.GetDeclns, ref env);
|
||||
declns.AddRange(declns_);
|
||||
}
|
||||
|
||||
foreach (Stmt stmt in this.Stmts) {
|
||||
Tuple<ABT.Env, ABT.Stmt> r_stmt = stmt.GetStmt(env);
|
||||
env = r_stmt.Item1;
|
||||
stmts.Add(r_stmt);
|
||||
}
|
||||
|
||||
env = env.OutScope();
|
||||
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.CompoundStmt(declns, stmts));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// expr;
|
||||
/// </summary>
|
||||
public sealed class ExprStmt : Stmt {
|
||||
public ExprStmt(Option<Expr> expr) {
|
||||
this.Expr = expr;
|
||||
}
|
||||
public Option<Expr> Expr { get; }
|
||||
public static Stmt Create(Option<Expr> expr) =>
|
||||
new ExprStmt(expr);
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
var expr = this.Expr.Map(_ => _.GetExpr(env, this));
|
||||
env = expr.IsSome ? expr.Value.Env : env;
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.ExprStmt(expr));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// while (Cond) {
|
||||
/// Body
|
||||
/// }
|
||||
///
|
||||
/// Cond must be of scalar Type
|
||||
/// </summary>
|
||||
public sealed class WhileStmt : Stmt {
|
||||
public WhileStmt(Expr cond, Stmt body) {
|
||||
this.Cond = cond;
|
||||
this.Body = body;
|
||||
}
|
||||
|
||||
public static Stmt Create(Expr cond, Stmt body) =>
|
||||
new WhileStmt(cond, body);
|
||||
|
||||
public Expr Cond { get; }
|
||||
public Stmt Body { get; }
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
ABT.Expr cond = this.Cond.GetExpr(env, this);
|
||||
env = cond.Env;
|
||||
|
||||
if (!cond.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Error: conditional expression in while Loop must be scalar.").Attach(cond);
|
||||
}
|
||||
|
||||
Tuple<ABT.Env, ABT.Stmt> r_body = this.Body.GetStmt(env);
|
||||
env = r_body.Item1;
|
||||
ABT.Stmt body = r_body.Item2;
|
||||
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.WhileStmt(cond, body));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// do {
|
||||
/// Body
|
||||
/// } while (Cond);
|
||||
///
|
||||
/// Cond must be of scalar Type
|
||||
/// </summary>
|
||||
public sealed class DoWhileStmt : Stmt {
|
||||
public DoWhileStmt(Stmt body, Expr cond) {
|
||||
this.Body = body;
|
||||
this.Cond = cond;
|
||||
}
|
||||
|
||||
public Stmt Body { get; }
|
||||
public Expr Cond { get; }
|
||||
|
||||
public static Stmt Create(Stmt body, Expr cond) =>
|
||||
new DoWhileStmt(body, cond);
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
Tuple<ABT.Env, ABT.Stmt> r_body = this.Body.GetStmt(env);
|
||||
env = r_body.Item1;
|
||||
ABT.Stmt body = r_body.Item2;
|
||||
|
||||
ABT.Expr cond = this.Cond.GetExpr(env, this);
|
||||
env = cond.Env;
|
||||
|
||||
if (!cond.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Error: conditional expression in while Loop must be scalar.").Attach(Cond);
|
||||
}
|
||||
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.DoWhileStmt(body, cond));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// for (Init; Cond; Loop) {
|
||||
/// Body
|
||||
/// }
|
||||
///
|
||||
/// Cond must be of scalar Type
|
||||
/// </summary>
|
||||
public sealed class ForStmt : Stmt {
|
||||
public ForStmt(Option<Expr> init, Option<Expr> cond, Option<Expr> loop, Stmt body) {
|
||||
this.Init = init;
|
||||
this.Cond = cond;
|
||||
this.Loop = loop;
|
||||
this.Body = body;
|
||||
}
|
||||
|
||||
public Option<Expr> Init { get; }
|
||||
public Option<Expr> Cond { get; }
|
||||
public Option<Expr> Loop { get; }
|
||||
public Stmt Body { get; }
|
||||
|
||||
public static Stmt Create(Option<Expr> init, Option<Expr> cond, Option<Expr> loop, Stmt body) =>
|
||||
new ForStmt(init, cond, loop, body);
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
Option<ABT.Expr> init = this.Init.Map(_ => _.GetExpr(env, this));
|
||||
if (init.IsSome) {
|
||||
env = init.Value.Env;
|
||||
}
|
||||
|
||||
Option<ABT.Expr> cond = this.Cond.Map(_ => _.GetExpr(env, this));
|
||||
if (cond.IsSome) {
|
||||
env = cond.Value.Env;
|
||||
}
|
||||
|
||||
if (cond.IsSome && !cond.Value.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Error: conditional expression in while Loop must be scalar.").Attach(this);
|
||||
}
|
||||
|
||||
Option<ABT.Expr> loop = this.Loop.Map(_ => _.GetExpr(env, this));
|
||||
if (loop.IsSome) {
|
||||
env = loop.Value.Env;
|
||||
}
|
||||
|
||||
Tuple<ABT.Env, ABT.Stmt> r_body = this.Body.GetStmt(env);
|
||||
env = r_body.Item1;
|
||||
ABT.Stmt body = r_body.Item2;
|
||||
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.ForStmt(init, cond, loop, body));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// switch (expr)
|
||||
/// stmt
|
||||
/// </summary>
|
||||
public sealed class SwitchStmt : Stmt {
|
||||
public SwitchStmt(Expr expr, Stmt stmt) {
|
||||
this.Expr = expr;
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
public Expr Expr { get; }
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public static Stmt Create(Expr expr, Stmt stmt) =>
|
||||
new SwitchStmt(expr, stmt);
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, this);
|
||||
|
||||
Tuple<ABT.Env, ABT.Stmt> r_stmt = this.Stmt.GetStmt(env);
|
||||
env = r_stmt.Item1;
|
||||
ABT.Stmt stmt = r_stmt.Item2;
|
||||
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.SwitchStmt(expr, stmt));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// if (Cond)
|
||||
/// stmt
|
||||
/// </summary>
|
||||
public sealed class IfStmt : Stmt {
|
||||
public IfStmt(Expr cond, Stmt stmt) {
|
||||
this.Cond = cond;
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public Expr Cond { get; }
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public static Stmt Create(Expr cond, Stmt stmt) =>
|
||||
new IfStmt(cond, stmt);
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
ABT.Expr cond = this.Cond.GetExpr(env, this);
|
||||
|
||||
if (!cond.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Error: expected scalar Type").Attach(this);
|
||||
}
|
||||
|
||||
Tuple<ABT.Env, ABT.Stmt> r_stmt = this.Stmt.GetStmt(env);
|
||||
env = r_stmt.Item1;
|
||||
ABT.Stmt stmt = r_stmt.Item2;
|
||||
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.IfStmt(cond, stmt));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// if (Cond)
|
||||
/// true-stmt
|
||||
/// else
|
||||
/// false-stmt
|
||||
/// </summary>
|
||||
public sealed class IfElseStmt : Stmt {
|
||||
public IfElseStmt(Expr cond, Stmt trueStmt, Stmt falseStmt) {
|
||||
this.Cond = cond;
|
||||
this.TrueStmt = trueStmt;
|
||||
this.FalseStmt = falseStmt;
|
||||
}
|
||||
|
||||
public Expr Cond { get; }
|
||||
public Stmt TrueStmt { get; }
|
||||
public Stmt FalseStmt { get; }
|
||||
|
||||
public static Stmt Create(Expr cond, Stmt trueStmt, Stmt falseStmt) =>
|
||||
new IfElseStmt(cond, trueStmt, falseStmt);
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
ABT.Expr cond = this.Cond.GetExpr(env, this);
|
||||
|
||||
if (!cond.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Error: expected scalar Type").Attach(this);
|
||||
}
|
||||
|
||||
Tuple<ABT.Env, ABT.Stmt> r_true_stmt = this.TrueStmt.GetStmt(env);
|
||||
env = r_true_stmt.Item1;
|
||||
ABT.Stmt true_stmt = r_true_stmt.Item2;
|
||||
|
||||
Tuple<ABT.Env, ABT.Stmt> r_false_stmt = this.FalseStmt.GetStmt(env);
|
||||
env = r_false_stmt.Item1;
|
||||
ABT.Stmt false_stmt = r_false_stmt.Item2;
|
||||
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.IfElseStmt(cond, true_stmt, false_stmt));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// label:
|
||||
/// stmt
|
||||
/// </summary>
|
||||
public sealed class LabeledStmt : Stmt {
|
||||
private LabeledStmt(String label, Stmt stmt) {
|
||||
this.Label = label;
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public String Label { get; }
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public static Stmt Create(String label, Stmt stmt) =>
|
||||
new LabeledStmt(label, stmt);
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
Tuple<ABT.Env, ABT.Stmt> r_stmt = this.Stmt.GetStmt(env);
|
||||
env = r_stmt.Item1;
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.LabeledStmt(this.Label, r_stmt.Item2));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// case expr:
|
||||
/// stmt
|
||||
/// </summary>
|
||||
public sealed class CaseStmt : Stmt {
|
||||
private CaseStmt(Expr expr, Stmt stmt) {
|
||||
this.Expr = expr;
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public Expr Expr { get; }
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public static Stmt Create(Expr expr, Stmt stmt) =>
|
||||
new CaseStmt(expr, stmt);
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, this);
|
||||
env = expr.Env;
|
||||
|
||||
expr = ABT.TypeCast.MakeCast(expr, new ABT.LongType());
|
||||
if (!expr.IsConstExpr) {
|
||||
throw new InvalidOperationException("case Expr not const").Attach(this);
|
||||
}
|
||||
Int32 value = ((ABT.ConstLong)expr).Value;
|
||||
|
||||
Tuple<ABT.Env, ABT.Stmt> r_stmt = this.Stmt.GetStmt(env);
|
||||
env = r_stmt.Item1;
|
||||
|
||||
return new Tuple<ABT.Env, ABT.Stmt>(env, new ABT.CaseStmt(value, r_stmt.Item2));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// default:
|
||||
/// stmt
|
||||
/// </summary>
|
||||
public sealed class DefaultStmt : Stmt {
|
||||
private DefaultStmt(Stmt stmt) {
|
||||
this.Stmt = stmt;
|
||||
}
|
||||
|
||||
public static DefaultStmt Create(Stmt stmt) =>
|
||||
new DefaultStmt(stmt);
|
||||
|
||||
public Stmt Stmt { get; }
|
||||
|
||||
public override Tuple<ABT.Env, ABT.Stmt> GetStmt(ABT.Env env) {
|
||||
var stmt = SemantStmt(this.Stmt.GetStmt, ref env);
|
||||
return Tuple.Create(env, new ABT.DefaultStmt(stmt) as ABT.Stmt);
|
||||
}
|
||||
}
|
||||
}
|
374
LibIFPSCC/AST/UnaryOperators.cs
Normal file
@ -0,0 +1,374 @@
|
||||
using System;
|
||||
using static AST.SemanticAnalysis;
|
||||
|
||||
namespace AST {
|
||||
|
||||
public abstract class UnaryExprOperator : Expr {
|
||||
protected UnaryExprOperator(Expr expr) {
|
||||
this.Expr = expr;
|
||||
}
|
||||
public Expr Expr { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Postfix increment: x++
|
||||
/// </summary>
|
||||
// TODO: Check lvalue
|
||||
public sealed class PostIncrement : UnaryExprOperator {
|
||||
public PostIncrement(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) => new PostIncrement(expr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
|
||||
if (!expr.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Expected a scalar.").Attach(expr);
|
||||
}
|
||||
|
||||
return new ABT.PostIncrement(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Postfix decrement: x--
|
||||
/// </summary>
|
||||
// TODO: Check lvalue
|
||||
public sealed class PostDecrement : UnaryExprOperator {
|
||||
public PostDecrement(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Func<Expr, Expr> Create { get; } = expr => new PostDecrement(expr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
|
||||
if (!expr.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Expected a scalar.").Attach(expr);
|
||||
}
|
||||
|
||||
return new ABT.PostDecrement(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sizeof(Type)
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class SizeofType : Expr {
|
||||
public SizeofType(TypeName typeName) {
|
||||
this.TypeName = typeName;
|
||||
}
|
||||
public TypeName TypeName { get; }
|
||||
public static Expr Create(TypeName typeName) =>
|
||||
new SizeofType(typeName);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
//Tuple<AST.Env, AST.ExprType> type_env = this.TypeName.GetTypeEnv(env);
|
||||
//env = type_env.Item1;
|
||||
//AST.ExprType Type = type_env.Item2;
|
||||
|
||||
var type = Semant(this.TypeName.GetExprType, ref env);
|
||||
|
||||
return new ABT.ConstULong((UInt32)type.SizeOf, env);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sizeof(Expr)
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class SizeofExpr : UnaryExprOperator {
|
||||
public SizeofExpr(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) =>
|
||||
new SizeofExpr(expr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
return new ABT.ConstULong((UInt32)expr.Type.SizeOf, env);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix increment: ++x
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class PreIncrement : UnaryExprOperator {
|
||||
public PreIncrement(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) =>
|
||||
new PreIncrement(expr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
|
||||
if (!expr.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Expected a scalar.").Attach(expr);
|
||||
}
|
||||
|
||||
return new ABT.PreIncrement(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix decrement: --x
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class PreDecrement : UnaryExprOperator {
|
||||
public PreDecrement(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) =>
|
||||
new PreDecrement(expr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
|
||||
if (!expr.Type.IsScalar) {
|
||||
throw new InvalidOperationException("Expected a scalar.").Attach(expr);
|
||||
}
|
||||
|
||||
return new ABT.PreDecrement(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference: &expr
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class Reference : UnaryExprOperator {
|
||||
public Reference(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) =>
|
||||
new Reference(expr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
return new ABT.Reference(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dereference: *Expr
|
||||
///
|
||||
/// Note that Expr might have an **incomplete** Type.
|
||||
/// We need to search the environment
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class Dereference : UnaryExprOperator {
|
||||
public Dereference(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) => new Dereference(expr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
|
||||
if (expr is ABT.ArrayIndexDeref) return new ABT.Dereference(expr, expr.Type);
|
||||
|
||||
if (expr.Type.Kind != ABT.ExprTypeKind.POINTER) {
|
||||
throw new InvalidOperationException("Expected a pointer.").Attach(expr);
|
||||
}
|
||||
|
||||
ABT.ExprType type = ((ABT.PointerType)expr.Type).RefType;
|
||||
if (type.Kind == ABT.ExprTypeKind.STRUCT_OR_UNION && !((ABT.StructOrUnionType)type).IsComplete) {
|
||||
throw new InvalidOperationException("Cannot dereference incomplete Type.").Attach(expr);
|
||||
}
|
||||
|
||||
return new ABT.Dereference(expr, type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merely a check on arithmetic Type.
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class Positive : UnaryExprOperator {
|
||||
public Positive(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) =>
|
||||
new Positive(expr);
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
|
||||
if (!expr.Type.IsArith) {
|
||||
throw new InvalidOperationException("Expected arithmetic Type.").Attach(expr);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Negative: requires arithmetic Type.
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class Negative : UnaryExprOperator {
|
||||
public Negative(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) =>
|
||||
new Negative(expr);
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
|
||||
if (!expr.Type.IsArith) {
|
||||
throw new InvalidOperationException("Expected arithmetic Type.").Attach(expr);
|
||||
}
|
||||
|
||||
if (expr.Type.IsIntegral) {
|
||||
expr = ABT.TypeCast.IntegralPromotion(expr).Item1;
|
||||
}
|
||||
|
||||
if (expr.IsConstExpr) {
|
||||
switch (expr.Type.Kind) {
|
||||
case ABT.ExprTypeKind.LONG:
|
||||
return new ABT.ConstLong(-((ABT.ConstLong)expr).Value, env);
|
||||
|
||||
case ABT.ExprTypeKind.ULONG:
|
||||
return new ABT.ConstLong(-(Int32)((ABT.ConstULong)expr).Value, env);
|
||||
|
||||
case ABT.ExprTypeKind.S64:
|
||||
return new ABT.ConstS64(-((ABT.ConstS64)expr).Value, env);
|
||||
|
||||
case ABT.ExprTypeKind.U64:
|
||||
return new ABT.ConstS64(-(long)((ABT.ConstU64)expr).Value, env);
|
||||
|
||||
case ABT.ExprTypeKind.FLOAT:
|
||||
return new ABT.ConstFloat(-((ABT.ConstFloat)expr).Value, env);
|
||||
|
||||
case ABT.ExprTypeKind.DOUBLE:
|
||||
return new ABT.ConstDouble(-((ABT.ConstDouble)expr).Value, env);
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
return new ABT.Negative(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise not: requires integral.
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class BitwiseNot : UnaryExprOperator {
|
||||
public BitwiseNot(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) =>
|
||||
new BitwiseNot(expr);
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
|
||||
if (!expr.Type.IsIntegral) {
|
||||
throw new InvalidOperationException("Expected integral Type.").Attach(expr);
|
||||
}
|
||||
|
||||
expr = ABT.TypeCast.IntegralPromotion(expr).Item1;
|
||||
|
||||
if (expr.IsConstExpr) {
|
||||
switch (expr.Type.Kind) {
|
||||
case ABT.ExprTypeKind.LONG:
|
||||
return new ABT.ConstLong(~((ABT.ConstLong)expr).Value, env);
|
||||
case ABT.ExprTypeKind.ULONG:
|
||||
return new ABT.ConstULong(~((ABT.ConstULong)expr).Value, env);
|
||||
case ABT.ExprTypeKind.S64:
|
||||
return new ABT.ConstS64(~((ABT.ConstS64)expr).Value, env);
|
||||
case ABT.ExprTypeKind.U64:
|
||||
return new ABT.ConstU64(~((ABT.ConstU64)expr).Value, env);
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
return new ABT.BitwiseNot(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logical not
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class LogicalNot : UnaryExprOperator {
|
||||
public LogicalNot(Expr expr)
|
||||
: base(expr) { }
|
||||
public static Expr Create(Expr expr) =>
|
||||
new LogicalNot(expr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
var expr = this.Expr.GetExpr(env, info);
|
||||
|
||||
if (!expr.Type.IsArith) {
|
||||
throw new InvalidOperationException("Expected arithmetic Type.").Attach(expr);
|
||||
}
|
||||
|
||||
if (expr.Type.IsIntegral) {
|
||||
expr = ABT.TypeCast.IntegralPromotion(expr).Item1;
|
||||
}
|
||||
|
||||
if (expr.IsConstExpr) {
|
||||
Boolean isZero;
|
||||
switch (expr.Type.Kind) {
|
||||
case ABT.ExprTypeKind.LONG:
|
||||
isZero = ((ABT.ConstLong)expr).Value == 0;
|
||||
break;
|
||||
case ABT.ExprTypeKind.ULONG:
|
||||
isZero = ((ABT.ConstULong)expr).Value == 0;
|
||||
break;
|
||||
case ABT.ExprTypeKind.S64:
|
||||
isZero = ((ABT.ConstS64)expr).Value == 0;
|
||||
break;
|
||||
case ABT.ExprTypeKind.U64:
|
||||
isZero = ((ABT.ConstU64)expr).Value == 0;
|
||||
break;
|
||||
case ABT.ExprTypeKind.FLOAT:
|
||||
isZero = ((ABT.ConstFloat)expr).Value == 0;
|
||||
break;
|
||||
case ABT.ExprTypeKind.DOUBLE:
|
||||
isZero = ((ABT.ConstDouble)expr).Value == 0;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return new ABT.ConstLong(Convert.ToInt32(isZero), env);
|
||||
}
|
||||
|
||||
return new ABT.LogicalNot(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User-specified explicit Type cast
|
||||
/// </summary>
|
||||
[Checked]
|
||||
public sealed class TypeCast : Expr {
|
||||
public TypeCast(TypeName typeName, Expr expr) {
|
||||
this.TypeName = typeName;
|
||||
this.Expr = expr;
|
||||
}
|
||||
|
||||
public TypeName TypeName { get; }
|
||||
public Expr Expr { get; }
|
||||
|
||||
public static Expr Create(TypeName typeName, Expr expr) =>
|
||||
new TypeCast(typeName, expr);
|
||||
|
||||
protected override ABT.Expr GetExprImpl(ABT.Env env, ILineInfo info)
|
||||
{
|
||||
ABT.ExprType type = Semant(this.TypeName.GetExprType, ref env);
|
||||
ABT.Expr expr = this.Expr.GetExpr(env, info);
|
||||
return ABT.TypeCast.MakeCast(expr, type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
600
LibIFPSCC/CGen/BinaryOperators.cs
Normal file
@ -0,0 +1,600 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Resources;
|
||||
using AST;
|
||||
using CodeGeneration;
|
||||
using IFPSLib.Emit;
|
||||
|
||||
namespace ABT {
|
||||
public abstract partial class BinaryOp {
|
||||
public override sealed Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of a binary operator.").Attach(this);
|
||||
}
|
||||
|
||||
protected Operand CGenPrepare(CGenState state, Operand retLoc)
|
||||
{
|
||||
var ptr = Type as PointerType;
|
||||
Operand op = null;
|
||||
if (ptr != null && !ptr.IsRef)
|
||||
{
|
||||
// This is really an array of pointer. assign VarArrOfPtr, Arr[0] is incorrect.
|
||||
// Instead do the following:
|
||||
op = Left.CGenValue(state, retLoc);
|
||||
return state.FunctionState.PushVar(op);
|
||||
}
|
||||
op = retLoc != null ? retLoc : state.FunctionState.PushType(state.EmitType(Type));
|
||||
var leftOp = Left.CGenValue(state, retLoc);
|
||||
if (OperandEquator.Equals(leftOp, retLoc)) return op;
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, op, leftOp));
|
||||
return op;
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
if (Type.Kind == ExprTypeKind.POINTER && !(Type as PointerType).IsRef) return false;
|
||||
return !retLocKnown;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class BinaryOpSupportingIntegralOperands {
|
||||
/// <summary>
|
||||
/// Before calling this method, %eax = Left, %ebx = Right
|
||||
/// This method should let %eax = %eax op %ebx
|
||||
/// </summary>
|
||||
public abstract void OperateLong(CGenState state, Operand dest);
|
||||
|
||||
/// <summary>
|
||||
/// Before calling this method, %eax = Left, %ebx = Right
|
||||
/// This method should let %eax = %eax op %ebx
|
||||
/// </summary>
|
||||
public abstract void OperateULong(CGenState state, Operand dest);
|
||||
|
||||
public virtual void OperateString(CGenState state, Operand dest)
|
||||
{
|
||||
throw new InvalidOperationException("Does not support string").Attach(this);
|
||||
}
|
||||
|
||||
private Operand CGenLong(CGenState state, Operand retLoc)
|
||||
{
|
||||
var dest = CGenPrepare(state, retLoc);
|
||||
OperateLong(state, dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
private Operand CGenULong(CGenState state, Operand retLoc)
|
||||
{
|
||||
var dest = CGenPrepare(state, retLoc);
|
||||
OperateULong(state, dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 1. %eax = left, %ebx = right, stack unchanged
|
||||
/// 2. Operate{Long, ULong}
|
||||
/// </summary>
|
||||
protected Operand CGenIntegral(CGenState state, Operand retLoc)
|
||||
{
|
||||
// %eax = left, %ebx = right, stack unchanged
|
||||
var dest = CGenPrepare(state, retLoc);
|
||||
|
||||
if (this.Type is LongType || this.Type is ComVariantType) {
|
||||
// %eax = left op right, stack unchanged
|
||||
OperateLong(state, dest);
|
||||
} else if (this.Type is ULongType) {
|
||||
// %eax = left op right, stack unchanged
|
||||
OperateULong(state, dest);
|
||||
} else if (this.Type is UnicodeStringType || this.Type is AnsiStringType)
|
||||
{
|
||||
OperateString(state, dest);
|
||||
} else {
|
||||
throw new InvalidOperationException().Attach(this);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class BinaryOpSupportingOnlyIntegralOperands {
|
||||
public override sealed Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return CGenIntegral(state, retLoc);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class BinaryOpSupportingArithmeticOperands {
|
||||
/// <summary>
|
||||
/// Before: %st(0) = left, %st(1) = right, stack unchanged.
|
||||
/// After: 'left op right' stored in the correct register.
|
||||
/// </summary>
|
||||
public abstract void OperateFloat(CGenState state, Operand dest);
|
||||
|
||||
/// <summary>
|
||||
/// Before: %st(0) = left, %st(1) = right, stack unchanged.
|
||||
/// After: 'left op right' stored in the correct register.
|
||||
/// </summary>
|
||||
public abstract void OperateDouble(CGenState state, Operand dest);
|
||||
|
||||
/// <summary>
|
||||
/// 1. %st(0) = left, %st(1) = right, stack unchanged
|
||||
/// 2. OperateDouble
|
||||
/// </summary>
|
||||
public Operand CGenFloat(CGenState state, Operand retLoc)
|
||||
{
|
||||
var dest = CGenPrepare(state, retLoc);
|
||||
OperateFloat(state, dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 1. %st(0) = left, %st(1) = right, stack unchanged
|
||||
/// 2. OperateDouble
|
||||
/// </summary>
|
||||
public Operand CGenDouble(CGenState state, Operand retLoc)
|
||||
{
|
||||
var dest = CGenPrepare(state, retLoc);
|
||||
OperateDouble(state, dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 1. %st(0) = left, %st(1) = right, stack unchanged
|
||||
/// 2. Operate{Float, Double}
|
||||
/// </summary>
|
||||
public Operand CGenArithmetic(CGenState state, Operand retLoc)
|
||||
{
|
||||
if (this.Type is FloatType) {
|
||||
return CGenFloat(state, retLoc);
|
||||
} else if (this.Type is DoubleType) {
|
||||
return CGenDouble(state, retLoc);
|
||||
} else {
|
||||
return CGenIntegral(state, retLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class BinaryArithmeticOp {
|
||||
public override sealed Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return CGenArithmetic(state, retLoc);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class BinaryComparisonOp {
|
||||
public abstract void SetLong(CGenState state, Operand dest);
|
||||
|
||||
public abstract void SetULong(CGenState state, Operand dest);
|
||||
|
||||
public abstract void SetFloat(CGenState state, Operand dest);
|
||||
|
||||
public abstract void SetDouble(CGenState state, Operand dest);
|
||||
|
||||
public override sealed void OperateLong(CGenState state, Operand dest) {
|
||||
SetLong(state, dest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Before: %eax = left, %ebx = right, stack unchanged.</para>
|
||||
/// <para>After: with SetULong, %eax = left op right, stack unchanged.</para>
|
||||
/// </summary>
|
||||
public override sealed void OperateULong(CGenState state, Operand dest) {
|
||||
SetULong(state, dest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Before: %st(0) = left, %st(1) = right, stack unchanged.</para>
|
||||
/// <para>After: with SetFloat, %eax = left op right, stack unchanged.</para>
|
||||
/// </summary>
|
||||
public override sealed void OperateFloat(CGenState state, Operand dest) {
|
||||
SetFloat(state, dest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Before: %st(0) = left, %st(1) = right, stack unchanged.
|
||||
/// After: with SetDouble, %eax = left op right, stack unchanged.
|
||||
/// </summary>
|
||||
public override sealed void OperateDouble(CGenState state, Operand dest) {
|
||||
SetDouble(state, dest);
|
||||
}
|
||||
|
||||
public override sealed Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return CGenArithmetic(state, retLoc);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Multiply {
|
||||
public override void OperateLong(CGenState state, Operand dest) {
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Mul, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Mul, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Mul, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Mul, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Divide {
|
||||
public override void OperateLong(CGenState state, Operand dest) {
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Div, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Div, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Div, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Div, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Modulo {
|
||||
public override void OperateLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Mod, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Mod, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Xor {
|
||||
public override void OperateLong(CGenState state, Operand dest) {
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Xor, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Xor, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class BitwiseOr {
|
||||
public override void OperateLong(CGenState state, Operand dest) {
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Or, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Or, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class BitwiseAnd {
|
||||
public override void OperateLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.And, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.And, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class LShift {
|
||||
public override void OperateLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Shl, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Shl, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class RShift
|
||||
{
|
||||
public override void OperateLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Shr, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Shr, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Add {
|
||||
public override void OperateLong(CGenState state, Operand dest) {
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Add, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Add, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Add, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Add, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateString(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Add, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Sub {
|
||||
public override void OperateLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Sub, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Sub, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Sub, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void OperateDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Sub, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public sealed partial class GEqual {
|
||||
public override void SetLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ge, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ge, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ge, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ge, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Greater {
|
||||
public override void SetLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Gt, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Gt, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Gt, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Gt, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class LEqual {
|
||||
public override void SetLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Le, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Le, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Le, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Le, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Less {
|
||||
public override void SetLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Lt, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Lt, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Lt, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Lt, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Equal {
|
||||
public override void SetLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Eq, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Eq, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Eq, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Eq, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class NotEqual {
|
||||
public override void SetLong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ne, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetULong(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ne, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetFloat(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ne, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
|
||||
public override void SetDouble(CGenState state, Operand dest)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ne, dest, dest, Right.CGenValue(state, null)));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class LogicalAnd {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
Int32 label_finish = state.RequestLabel();
|
||||
|
||||
var dest = retLoc != null ? retLoc : state.FunctionState.PushType(state.TypeU32); // initialises to default(u32) == 0
|
||||
var temp = state.FunctionState.PushType(state.TypeU32);
|
||||
|
||||
var ret = this.Left.CGenValue(state, null);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Eq, temp, ret, dest));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpNZ, state.FunctionState.Labels[label_finish], temp));
|
||||
|
||||
ret = this.Right.CGenValue(state, null);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Eq, temp, ret, dest));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpNZ, state.FunctionState.Labels[label_finish], temp));
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Inc, dest));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[label_finish]));
|
||||
|
||||
state.CGenLabel(label_finish);
|
||||
state.FunctionState.Pop();
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Left || Right: can only take scalars (to compare with 0).
|
||||
///
|
||||
/// After semantic analysis, each operand can only be
|
||||
/// long, ulong, float, double.
|
||||
/// Pointers are casted to ulongs.
|
||||
///
|
||||
/// if Left != 0:
|
||||
/// return 1
|
||||
/// else:
|
||||
/// return Right != 0
|
||||
///
|
||||
/// Generate the assembly in this fashion,
|
||||
/// then every route would only have one jump.
|
||||
///
|
||||
/// +---------+ 1
|
||||
/// | cmp lhs |-------+
|
||||
/// +---------+ |
|
||||
/// | |
|
||||
/// | 0 |
|
||||
/// | |
|
||||
/// +----+----+ 1 |
|
||||
/// | cmp rhs |-------+
|
||||
/// +---------+ |
|
||||
/// | |
|
||||
/// | 0 |
|
||||
/// | |
|
||||
/// +----+----+ |
|
||||
/// | eax = 0 | |
|
||||
/// +---------+ |
|
||||
/// | |
|
||||
/// +---------+ |
|
||||
/// | |
|
||||
/// | +------------+ label_set
|
||||
/// | |
|
||||
/// | +---------+
|
||||
/// | | eax = 1 |
|
||||
/// | +---------+
|
||||
/// | |
|
||||
/// +---------+ label_finish
|
||||
/// |
|
||||
///
|
||||
/// </summary>
|
||||
public sealed partial class LogicalOr {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
Int32 label_set = state.RequestLabel();
|
||||
Int32 label_finish = state.RequestLabel();
|
||||
|
||||
var dest = retLoc != null ? retLoc : state.FunctionState.PushType(state.TypeU32); // initialises to default(u32) == 0
|
||||
var temp = state.FunctionState.PushType(state.TypeU32);
|
||||
// can't use "just" jnz, it doesn't support all possible types that could come through here
|
||||
|
||||
var ret = this.Left.CGenValue(state, null);
|
||||
// temp = ret != dest; // dest == 0
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ne, temp, ret, dest));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpNZ, state.FunctionState.Labels[label_set], temp));
|
||||
|
||||
ret = this.Right.CGenValue(state, null);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ne, temp, ret, dest));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpZ, state.FunctionState.Labels[label_finish], temp));
|
||||
|
||||
state.CGenLabel(label_set);
|
||||
|
||||
state.FunctionState.Labels[label_set].Replace(Instruction.Create(OpCodes.Inc, dest));
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[label_finish]));
|
||||
|
||||
state.CGenLabel(label_finish);
|
||||
state.FunctionState.Pop();
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
}
|
924
LibIFPSCC/CGen/CGen.cs
Normal file
@ -0,0 +1,924 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml.Linq;
|
||||
using IFPSLib;
|
||||
using Emit = IFPSLib.Emit;
|
||||
using Types = IFPSLib.Types;
|
||||
|
||||
namespace CodeGeneration {
|
||||
|
||||
internal static class OperandEquator
|
||||
{
|
||||
internal static bool Equals(Emit.IVariable lhs, Emit.IVariable rhs)
|
||||
{
|
||||
if (lhs == rhs) return true;
|
||||
if (lhs == null || rhs == null) return false;
|
||||
if (lhs.VarType != rhs.VarType) return false;
|
||||
return lhs.Index == rhs.Index;
|
||||
}
|
||||
internal static bool Equals(Emit.Operand lhs, Emit.Operand rhs)
|
||||
{
|
||||
if (lhs == rhs) return true;
|
||||
if (lhs == null || rhs == null) return false;
|
||||
if (lhs.Type != rhs.Type) return false;
|
||||
switch (lhs.Type)
|
||||
{
|
||||
case Emit.BytecodeOperandType.Immediate:
|
||||
return lhs.Immediate == rhs.Immediate;
|
||||
case Emit.BytecodeOperandType.Variable:
|
||||
return Equals(lhs.Variable, rhs.Variable);
|
||||
case Emit.BytecodeOperandType.IndexedImmediate:
|
||||
return Equals(lhs.IndexedVariable, rhs.IndexedVariable) && lhs.IndexImmediate == rhs.IndexImmediate;
|
||||
case Emit.BytecodeOperandType.IndexedVariable:
|
||||
return Equals(lhs.IndexedVariable, rhs.IndexedVariable) && Equals(lhs.IndexVariable, rhs.IndexVariable);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CGenState {
|
||||
public interface IFunctionGen
|
||||
{
|
||||
Emit.ScriptFunction Function { get; }
|
||||
List<Emit.Instruction> Labels { get; }
|
||||
|
||||
Dictionary<int, int> LabelToIndex { get; }
|
||||
Dictionary<int, int> IndexToLabel { get; }
|
||||
|
||||
int LocalsCount { get; }
|
||||
|
||||
Emit.Operand PushType(Types.IType type);
|
||||
Emit.Operand PushVar(Emit.Operand op);
|
||||
Emit.Operand Push(Emit.Operand op);
|
||||
|
||||
Emit.Operand AddLocal();
|
||||
void Pop();
|
||||
void PopForRevert();
|
||||
}
|
||||
private sealed class FunctionGen : IFunctionGen
|
||||
{
|
||||
public Emit.ScriptFunction Function { get; }
|
||||
public List<Emit.Instruction> Labels { get; } = new List<Emit.Instruction>();
|
||||
|
||||
public Dictionary<int, int> LabelToIndex { get; } = new Dictionary<int, int>();
|
||||
public Dictionary<int, int> IndexToLabel { get; } = new Dictionary<int, int>();
|
||||
|
||||
public int LocalsCount { get; private set; }
|
||||
|
||||
private Emit.Operand PushCore(Emit.Instruction insn)
|
||||
{
|
||||
Function.Instructions.Add(insn);
|
||||
return AddLocal();
|
||||
}
|
||||
|
||||
public Emit.Operand PushType(Types.IType type) => PushCore(Emit.Instruction.Create(Emit.OpCodes.PushType, type));
|
||||
public Emit.Operand PushVar(Emit.Operand op) => PushCore(Emit.Instruction.Create(Emit.OpCodes.PushVar, op));
|
||||
public Emit.Operand Push(Emit.Operand op) => PushCore(Emit.Instruction.Create(Emit.OpCodes.Push, op));
|
||||
|
||||
public Emit.Operand AddLocal()
|
||||
{
|
||||
var ret = Emit.Operand.Create(Emit.LocalVariable.Create(LocalsCount));
|
||||
LocalsCount++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Pop()
|
||||
{
|
||||
Function.Instructions.Add(Emit.Instruction.Create(Emit.OpCodes.Pop));
|
||||
LocalsCount--;
|
||||
}
|
||||
|
||||
public void PopForRevert()
|
||||
{
|
||||
LocalsCount--;
|
||||
}
|
||||
|
||||
public FunctionGen(string name, ABT.StorageClass scs)
|
||||
{
|
||||
Function = new Emit.ScriptFunction();
|
||||
Function.Name = name;
|
||||
Function.Exported = scs != ABT.StorageClass.STATIC;
|
||||
// caller must deal with arguments.
|
||||
}
|
||||
}
|
||||
public Script Script { get; } = new Script();
|
||||
private IFunctionGen m_Constructor = null;
|
||||
public IFunctionGen FunctionState { get; private set; } = null;
|
||||
public IList<Emit.Instruction> CurrInsns => FunctionState?.Function.Instructions;
|
||||
private Dictionary<string, IFunctionGen> m_Functions = new Dictionary<string, IFunctionGen>();
|
||||
private Dictionary<string, Emit.ExternalFunction> m_FunctionsExternal = new Dictionary<string, Emit.ExternalFunction>();
|
||||
private Types.IType m_TypePointer = null;
|
||||
private Types.IType m_TypeUByte = null;
|
||||
private Types.IType m_TypeU16 = null;
|
||||
private Types.IType m_TypeU32 = null;
|
||||
private Types.IType m_TypeIUnknown = null;
|
||||
private Types.IType m_TypeIDispatch = null;
|
||||
|
||||
// Additional runtime functions/types required.
|
||||
/// <summary>
|
||||
/// Imports a stubbed function in ntdll.dll (just returns);
|
||||
/// This can be used with fastcall calling convention to get the underlying pointer of a value passed by reference
|
||||
/// </summary>
|
||||
private Emit.ExternalFunction m_CastPointerRef = null;
|
||||
/// <summary>
|
||||
/// Imports ntdll!RtlMoveMemory, copying between two pointers passed as by-reference variables
|
||||
/// </summary>
|
||||
private Emit.ExternalFunction m_RtlMoveMemoryRef = null;
|
||||
/// <summary>
|
||||
/// Imports ntdll!RtlMoveMemory, copying between two pointers passed as values.
|
||||
/// </summary>
|
||||
private Emit.ExternalFunction m_RtlMoveMemoryVal = null;
|
||||
|
||||
/// <summary>
|
||||
/// 1-length array of pointer, used to work around the inability to create a raw Pointer in the runtime (causes null deref if you try)
|
||||
/// </summary>
|
||||
private Types.IType m_TypeArrayOfPointer = null;
|
||||
/// <summary>
|
||||
/// <code>void CreateValidPointer(pointer_as_size_t pPtrValue, pointer_as_size_t pPtrType, ref ArrayOfPointer outPtr);</code>
|
||||
/// Creates a valid pointer in *outPtr, setting the pointer value to ptrValue and the pointer type to pPtrType.pType
|
||||
/// </summary>
|
||||
private Emit.ScriptFunction m_CreateValidPointer = null;
|
||||
/// <summary>
|
||||
/// <code>void CastRefPointer(ref anytype varForType, size_t ptrValue, ref ArrayOfPointer outPtr)</code>
|
||||
/// Creates a valid pointer in *outPtr, setting the pointer value to ptrValue and the pointer type to ((pointer)varForType)->pType
|
||||
/// Basically a wrapper for CreateValidPointer(ptrValue, CastPointerRef(ref varForType), ref outPtr)
|
||||
/// </summary>
|
||||
private Emit.ScriptFunction m_CastRefPointer = null;
|
||||
/// <summary>
|
||||
/// <code>__interface Cast(__interface self, u32 typeIdx)</code>
|
||||
/// Casts a COM interface to another, typeIdx is used for internal class to internal class cast and not for COM interface casting.
|
||||
/// </summary>
|
||||
private Emit.ExternalFunction m_ComInterfaceCast = null;
|
||||
/// <summary>
|
||||
/// <code>u16 VarType(__variant self)</code>
|
||||
/// Gets the type of a Variant. Used for casting interfaces, we need to know if the type is IUnknown or IDispatch because of lack of support in runtime.
|
||||
/// </summary>
|
||||
private Emit.ExternalFunction m_VarType = null;
|
||||
/// <summary>
|
||||
/// <code>void SetArrayLength(TArray* array, s32 length)</code>
|
||||
/// Sets the length of an unbounded array at runtime.
|
||||
/// </summary>
|
||||
private Emit.ExternalFunction m_SetArrayLength = null;
|
||||
|
||||
/// <summary>
|
||||
/// When initialising pointers, a global ArrayOfPointer is needed.
|
||||
/// The pointer needs to be stored in the expected stack local.
|
||||
/// </summary>
|
||||
private Emit.GlobalVariable m_PointerInitialiser = null;
|
||||
|
||||
public Emit.GlobalVariable PointerInitialiser
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_PointerInitialiser == null)
|
||||
{
|
||||
m_PointerInitialiser = Emit.GlobalVariable.Create(Script.GlobalVariables.Count, TypeArrayOfPointer, "__PointerInitialiser");
|
||||
Script.GlobalVariables.Add(m_PointerInitialiser);
|
||||
}
|
||||
return m_PointerInitialiser;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IFunctionGen Constructor {
|
||||
get
|
||||
{
|
||||
if (m_Constructor == null)
|
||||
{
|
||||
m_Constructor = new FunctionGen("__ctor", ABT.StorageClass.AUTO);
|
||||
m_Constructor.Function.Arguments = new List<FunctionArgument>();
|
||||
Script.Functions.Add(m_Constructor.Function);
|
||||
Script.EntryPoint = m_Constructor.Function;
|
||||
var runonce = CreateGlobal(TypeUByte, "__ctor_runonce");
|
||||
var insns = m_Constructor.Function.Instructions;
|
||||
var jumploc = Emit.Instruction.Create<byte>(Emit.OpCodes.Assign, runonce, 1);
|
||||
insns.Add(Emit.Instruction.Create(Emit.OpCodes.JumpZ, runonce, jumploc));
|
||||
insns.Add(Emit.Instruction.Create(Emit.OpCodes.Ret));
|
||||
insns.Add(jumploc);
|
||||
}
|
||||
return m_Constructor;
|
||||
}
|
||||
}
|
||||
|
||||
private Types.IType EnsureTypeCreated(ref Types.IType type, Types.PascalTypeCode code)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
type = new Types.PrimitiveType(code);
|
||||
Script.Types.Add(type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private Emit.ExternalFunction EnsureDllImportedFunctionCreated(ref Emit.ExternalFunction func, string dllName, string export, Emit.NativeCallingConvention cc, bool hasReturnArgument, string name, IList<FunctionArgument> args)
|
||||
{
|
||||
if (func == null)
|
||||
{
|
||||
func = new Emit.ExternalFunction();
|
||||
var dll = new Emit.FDecl.DLL();
|
||||
dll.DllName = dllName;
|
||||
dll.ProcedureName = export;
|
||||
dll.CallingConvention = cc;
|
||||
func.Declaration = dll;
|
||||
func.Name = name;
|
||||
func.ReturnArgument = hasReturnArgument ? Types.UnknownType.Instance : null;
|
||||
func.Arguments = args;
|
||||
func.Exported = true;
|
||||
Script.Functions.Add(func);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
private Types.IType EnsureTypeCreated<T>(ref Types.IType type)
|
||||
{
|
||||
return EnsureTypeCreated(ref type, Types.EnumHelpers.ToIFPSTypeCode(typeof(T)));
|
||||
}
|
||||
|
||||
private Types.IType EnsureArrayTypeCreated(ref Types.IType type, Types.IType elem, int count, string name)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
type = new Types.StaticArrayType(elem, count);
|
||||
type.Name = name;
|
||||
Script.Types.Add(type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public Types.IType TypePointer => EnsureTypeCreated(ref m_TypePointer, Types.PascalTypeCode.Pointer);
|
||||
public Types.IType TypeUByte => EnsureTypeCreated<byte>(ref m_TypeUByte);
|
||||
public Types.IType TypeU16 => EnsureTypeCreated<ushort>(ref m_TypeU16);
|
||||
public Types.IType TypeU32 => EnsureTypeCreated<uint>(ref m_TypeU32);
|
||||
public Types.IType TypeArrayOfPointer => EnsureArrayTypeCreated(ref m_TypeArrayOfPointer, TypePointer, 1, "__ArrayOfPointer");
|
||||
|
||||
public Types.IType TypeIUnknown
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_TypeIUnknown == null)
|
||||
{
|
||||
m_TypeIUnknown = new Types.ComInterfaceType(GUID_IUNKNOWN) { Name = "IUnknown" };
|
||||
Script.Types.Add(m_TypeIUnknown);
|
||||
}
|
||||
return m_TypeIUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
public Types.IType TypeIDispatch
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_TypeIDispatch == null)
|
||||
{
|
||||
m_TypeIDispatch = new Types.ComInterfaceType(GUID_IDISPATCH)
|
||||
{
|
||||
Name = "IDISPATCH",
|
||||
Exported = true
|
||||
};
|
||||
Script.Types.Add(m_TypeIDispatch);
|
||||
}
|
||||
return m_TypeIDispatch;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static FunctionArgument CreateImportedFunctionArgument(FunctionArgumentType type) => new FunctionArgument()
|
||||
{
|
||||
ArgumentType = type,
|
||||
Type = Types.UnknownType.Instance
|
||||
};
|
||||
|
||||
private static readonly IList<FunctionArgument> ARGS_CASTPOINTERREF = new FunctionArgument[] { CreateImportedFunctionArgument(FunctionArgumentType.Out) };
|
||||
private static readonly IList<FunctionArgument> ARGS_RTLMOVEMEMORYREF = new FunctionArgument[]
|
||||
{
|
||||
CreateImportedFunctionArgument(FunctionArgumentType.Out),
|
||||
CreateImportedFunctionArgument(FunctionArgumentType.Out),
|
||||
CreateImportedFunctionArgument(FunctionArgumentType.In)
|
||||
};
|
||||
private static readonly IList<FunctionArgument> ARGS_RTLMOVEMEMORYVAL = new FunctionArgument[]
|
||||
{
|
||||
CreateImportedFunctionArgument(FunctionArgumentType.In),
|
||||
CreateImportedFunctionArgument(FunctionArgumentType.In),
|
||||
CreateImportedFunctionArgument(FunctionArgumentType.In)
|
||||
};
|
||||
private static readonly IList<FunctionArgument> ARGS_COMINTERFACECAST = new FunctionArgument[]
|
||||
{
|
||||
CreateImportedFunctionArgument(FunctionArgumentType.In),
|
||||
CreateImportedFunctionArgument(FunctionArgumentType.In)
|
||||
};
|
||||
private static readonly IList<FunctionArgument> ARGS_VARTYPE = new FunctionArgument[]
|
||||
{
|
||||
CreateImportedFunctionArgument(FunctionArgumentType.In),
|
||||
};
|
||||
private static readonly IList<FunctionArgument> ARGS_EMPTY = new FunctionArgument[0];
|
||||
|
||||
public IFunction CastPointerRef => EnsureDllImportedFunctionCreated(ref m_CastPointerRef, "ntdll.dll", "RtlDebugPrintTimes", Emit.NativeCallingConvention.Register, true, "__CastPointerRef", ARGS_CASTPOINTERREF);
|
||||
public IFunction RtlMoveMemoryRef => EnsureDllImportedFunctionCreated(ref m_RtlMoveMemoryRef, "ntdll.dll", "RtlMoveMemory", Emit.NativeCallingConvention.Stdcall, false, "__RtlMoveMemoryRef", ARGS_RTLMOVEMEMORYREF);
|
||||
public IFunction RtlMoveMemoryVal => EnsureDllImportedFunctionCreated(ref m_RtlMoveMemoryVal, "ntdll.dll", "RtlMoveMemory", Emit.NativeCallingConvention.Stdcall, false, "__RtlMoveMemoryVal", ARGS_RTLMOVEMEMORYVAL);
|
||||
|
||||
public IFunction ComInterfaceCast
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ComInterfaceCast == null)
|
||||
{
|
||||
m_ComInterfaceCast = new Emit.ExternalFunction();
|
||||
var cls = new Emit.FDecl.Class();
|
||||
cls.ClassName = "Class";
|
||||
cls.FunctionName = "CastToType";
|
||||
cls.CallingConvention = Emit.NativeCallingConvention.Pascal;
|
||||
m_ComInterfaceCast.Declaration = cls;
|
||||
m_ComInterfaceCast.Name = "ComInterfaceCast";
|
||||
m_ComInterfaceCast.ReturnArgument = Types.UnknownType.Instance;
|
||||
m_ComInterfaceCast.Arguments = ARGS_COMINTERFACECAST;
|
||||
m_ComInterfaceCast.Exported = true;
|
||||
Script.Functions.Add(m_ComInterfaceCast);
|
||||
}
|
||||
return m_ComInterfaceCast;
|
||||
}
|
||||
}
|
||||
|
||||
public IFunction VarType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_FunctionsExternal.TryGetValue("VarType", out var func)) return func;
|
||||
if (m_VarType == null)
|
||||
{
|
||||
m_VarType = new Emit.ExternalFunction();
|
||||
var decl = new Emit.FDecl.Internal();
|
||||
m_VarType.Declaration = decl;
|
||||
m_VarType.Name = "VarType";
|
||||
m_VarType.ReturnArgument = Types.UnknownType.Instance;
|
||||
m_VarType.Arguments = ARGS_VARTYPE;
|
||||
m_VarType.Exported = true;
|
||||
Script.Functions.Add(m_VarType);
|
||||
m_FunctionsExternal.Add("VarType", m_VarType);
|
||||
}
|
||||
return m_VarType;
|
||||
}
|
||||
}
|
||||
|
||||
public IFunction SetArrayLength
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_FunctionsExternal.TryGetValue("SetArrayLength", out var func)) return func;
|
||||
if (m_SetArrayLength == null)
|
||||
{
|
||||
m_SetArrayLength = new Emit.ExternalFunction();
|
||||
var decl = new Emit.FDecl.Internal();
|
||||
m_SetArrayLength.Declaration = decl;
|
||||
m_SetArrayLength.Name = "SetArrayLength";
|
||||
m_SetArrayLength.ReturnArgument = null;
|
||||
m_SetArrayLength.Arguments = ARGS_EMPTY;
|
||||
m_SetArrayLength.Exported = true;
|
||||
Script.Functions.Add(m_SetArrayLength);
|
||||
m_FunctionsExternal.Add("SetArrayLength", m_SetArrayLength);
|
||||
}
|
||||
return m_SetArrayLength;
|
||||
}
|
||||
}
|
||||
|
||||
public IFunction CreateValidPointer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_CreateValidPointer == null)
|
||||
{
|
||||
var gen = new FunctionGen("__CreateValidPointer", ABT.StorageClass.STATIC);
|
||||
m_CreateValidPointer = gen.Function;
|
||||
m_CreateValidPointer.Arguments = new List<FunctionArgument>() {
|
||||
new FunctionArgument()
|
||||
{
|
||||
ArgumentType = FunctionArgumentType.In,
|
||||
Name = "pPtr",
|
||||
Type = TypeU32
|
||||
},
|
||||
new FunctionArgument()
|
||||
{
|
||||
ArgumentType = FunctionArgumentType.In,
|
||||
Name = "pObjTypeOf",
|
||||
Type = TypeU32
|
||||
},
|
||||
new FunctionArgument()
|
||||
{
|
||||
ArgumentType = FunctionArgumentType.Out,
|
||||
Name = "outPtr",
|
||||
Type = TypeArrayOfPointer
|
||||
}
|
||||
};
|
||||
m_CreateValidPointer.ReturnArgument = null;
|
||||
var insns = m_CreateValidPointer.Instructions;
|
||||
var pPtr = Emit.Operand.Create(m_CreateValidPointer.CreateArgumentVariable(0));
|
||||
var pObjTypeOf = Emit.Operand.Create(m_CreateValidPointer.CreateArgumentVariable(1));
|
||||
var outPtr = Emit.Operand.Create(m_CreateValidPointer.CreateArgumentVariable(2));
|
||||
// outPtr[0].pPtr = pPtr
|
||||
gen.Push(Emit.Operand.Create(ABT.ExprType.SIZEOF_POINTER)); // push SIZEOF_POINTER // 1
|
||||
gen.PushVar(pPtr); // pushvar pPtr // 2
|
||||
gen.PushVar(outPtr); // pushvar outPtr // 3
|
||||
insns.Add(Emit.Instruction.Create(Emit.OpCodes.Call, RtlMoveMemoryRef)); // call RtlMoveMemoryRef
|
||||
gen.Pop(); // 2
|
||||
gen.Pop(); // 1
|
||||
// outPtr[0].pType = pObjTypeOf.pType
|
||||
var Var2 = gen.Push(pObjTypeOf).Variable; // push pObjTypeOf // 2
|
||||
insns.Add(Emit.Instruction.Create<uint>(Emit.OpCodes.Sub, Var2, ABT.ExprType.SIZEOF_POINTER)); // sub Var2, SIZEOF_POINTER
|
||||
var Var3 = gen.PushType(TypeU32); // pushtype U32 // 3
|
||||
gen.PushVar(outPtr); // pushvar outPtr // 4
|
||||
gen.PushVar(Var3); // pushvar Var3 // 5
|
||||
insns.Add(Emit.Instruction.Create(Emit.OpCodes.Call, CastPointerRef)); // call CastPointerRef
|
||||
gen.Pop(); // 4
|
||||
gen.Pop(); // 3
|
||||
insns.Add(Emit.Instruction.Create<uint>(Emit.OpCodes.Add, Var3.Variable, ABT.ExprType.SIZEOF_POINTER)); // add Var3, SIZEOF_POINTER
|
||||
insns.Add(Emit.Instruction.Create(Emit.OpCodes.Call, RtlMoveMemoryVal));
|
||||
// return;
|
||||
insns.Add(Emit.Instruction.Create(Emit.OpCodes.Ret));
|
||||
|
||||
Script.Functions.Add(m_CreateValidPointer);
|
||||
}
|
||||
return m_CreateValidPointer;
|
||||
}
|
||||
}
|
||||
|
||||
public IFunction CastRefPointer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_CastRefPointer == null)
|
||||
{
|
||||
var gen = new FunctionGen("__CastRefPointer", ABT.StorageClass.STATIC);
|
||||
m_CastRefPointer = gen.Function;
|
||||
m_CastRefPointer.Arguments = new List<FunctionArgument>() {
|
||||
new FunctionArgument()
|
||||
{
|
||||
ArgumentType = FunctionArgumentType.Out,
|
||||
Name = "pType",
|
||||
Type = TypeU32 // actually anytype, runtime doesn't care about the type :)
|
||||
},
|
||||
new FunctionArgument()
|
||||
{
|
||||
ArgumentType = FunctionArgumentType.In,
|
||||
Name = "pVal",
|
||||
Type = TypeU32
|
||||
},
|
||||
new FunctionArgument()
|
||||
{
|
||||
ArgumentType = FunctionArgumentType.Out,
|
||||
Name = "outPtr",
|
||||
Type = TypeArrayOfPointer
|
||||
}
|
||||
};
|
||||
m_CastRefPointer.ReturnArgument = null;
|
||||
var insns = m_CastRefPointer.Instructions;
|
||||
var pType = Emit.Operand.Create(m_CastRefPointer.CreateArgumentVariable(0));
|
||||
var pVal = Emit.Operand.Create(m_CastRefPointer.CreateArgumentVariable(1));
|
||||
var outPtr = Emit.Operand.Create(m_CastRefPointer.CreateArgumentVariable(2));
|
||||
|
||||
// CreateValidPointer(pVal, (u32)pType, outPtr);
|
||||
gen.PushVar(outPtr);
|
||||
var Var2 = gen.PushType(TypeU32);
|
||||
gen.PushVar(pType);
|
||||
gen.PushVar(Var2);
|
||||
insns.Add(Emit.Instruction.Create(Emit.OpCodes.Call, CastPointerRef)); // call CastPointerRef
|
||||
gen.Pop();
|
||||
gen.Pop();
|
||||
gen.Push(pVal);
|
||||
insns.Add(Emit.Instruction.Create(Emit.OpCodes.Call, CreateValidPointer)); // call CreateValidPointer
|
||||
// return;
|
||||
insns.Add(Emit.Instruction.Create(Emit.OpCodes.Ret));
|
||||
|
||||
Script.Functions.Add(m_CastRefPointer);
|
||||
}
|
||||
return m_CastRefPointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private Dictionary<string, Types.IType> m_TypesCache = new Dictionary<string, Types.IType>();
|
||||
private Dictionary<string, ABT.ExprType> m_TypesCacheNamed = new Dictionary<string, ABT.ExprType>();
|
||||
|
||||
private static Guid GUID_IDISPATCH = new Guid(0x00020400, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
|
||||
private static Guid GUID_IUNKNOWN = new Guid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
|
||||
public static bool TypeIsIDispatch(ABT.ExprType type)
|
||||
{
|
||||
if (type.Kind != ABT.ExprTypeKind.COM_INTERFACE) return false;
|
||||
return ((ABT.ComInterfaceType)type).InterfaceGuid == GUID_IDISPATCH;
|
||||
}
|
||||
|
||||
public static bool TypeImplementsIDispatch(ABT.ExprType type)
|
||||
{
|
||||
if (type.Kind != ABT.ExprTypeKind.COM_INTERFACE) return false;
|
||||
return type.TypeAttribs.Any((attr) => attr.Name == "__dispatch");
|
||||
}
|
||||
|
||||
public static readonly ABT.ComInterfaceType ExprTypeIDispatch = new ABT.ComInterfaceType(GUID_IDISPATCH);
|
||||
|
||||
// Fixes up any types that the runtime expects to be exported with a specific name
|
||||
private static void FixUpTypeForRuntime(ABT.ExprType type, Types.IType emit)
|
||||
{
|
||||
// openarray is done by name prefix
|
||||
if (type.Kind == ABT.ExprTypeKind.INCOMPLETE_ARRAY)
|
||||
{
|
||||
if (((ABT.IncompleteArrayType)type).ElemType.TypeAttribs.Any((attr) => attr.Name == "__open"))
|
||||
{
|
||||
emit.Name = "!OPENARRAY" + emit.Name;
|
||||
emit.Exported = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Types.IType EmitType(ABT.ExprType type, bool forGlobal = false)
|
||||
{
|
||||
// Check for specific hardcoded types.
|
||||
switch (type)
|
||||
{
|
||||
case ABT.PointerType ptr:
|
||||
// We can't use type pointer, it's impossible to initialise.
|
||||
// Use type array of pointer instead.
|
||||
// for void*, use u32 (void* is impossible to specify, we can cast it later)
|
||||
if (!ptr.IsRef) return TypeU32;
|
||||
if (!(ptr.RefType is ABT.FunctionType))
|
||||
return TypeArrayOfPointer;
|
||||
break;
|
||||
case ABT.UCharType u8:
|
||||
return TypeUByte;
|
||||
case ABT.UShortType u16:
|
||||
return TypeU16;
|
||||
case ABT.ULongType u32:
|
||||
return TypeU32;
|
||||
case ABT.ComInterfaceType com:
|
||||
if (com.InterfaceGuid == GUID_IDISPATCH) return TypeIDispatch;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the named cache.
|
||||
if (m_TypesCacheNamed.TryGetValue(type.ToString(), out var named)) type = named;
|
||||
|
||||
// Check the cache.
|
||||
if (m_TypesCache.TryGetValue(type.ToString(), out var ret)) return ret;
|
||||
|
||||
ret = type.Emit(this);
|
||||
var namable = type as ABT.IExprTypeWithName;
|
||||
if (namable != null) ret.Name = namable.TypeName.Replace(' ', '_');
|
||||
FixUpTypeForRuntime(type, ret);
|
||||
if (ret == null) throw new InvalidOperationException("trying to emit null type");
|
||||
Script.Types.Add(ret);
|
||||
m_TypesCache.Add(type.ToString(), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void ChangeTypeName(ABT.IExprTypeWithName type, string name)
|
||||
{
|
||||
Types.IType emit = null;
|
||||
var realType = (ABT.ExprType)type;
|
||||
var old = realType.ToString();
|
||||
if (m_TypesCacheNamed.TryGetValue(old, out var named))
|
||||
{
|
||||
realType = named;
|
||||
type = (ABT.IExprTypeWithName)named;
|
||||
}
|
||||
else m_TypesCacheNamed.Add(old, realType);
|
||||
if (m_TypesCache.TryGetValue(old, out emit)) m_TypesCache.Remove(old);
|
||||
type.TypeName = name;
|
||||
var realName = realType.ToString();
|
||||
if (emit != null)
|
||||
{
|
||||
emit.Name = name.Replace(' ', '_');
|
||||
FixUpTypeForRuntime(realType, emit);
|
||||
m_TypesCache.Add(realName, emit);
|
||||
}
|
||||
if (!m_TypesCacheNamed.ContainsKey(realName)) m_TypesCacheNamed.Add(realName, realType);
|
||||
}
|
||||
|
||||
private Dictionary<string, Emit.GlobalVariable> m_Globals = new Dictionary<string, Emit.GlobalVariable>();
|
||||
public IReadOnlyDictionary<string, Emit.GlobalVariable> Globals => m_Globals;
|
||||
|
||||
private Emit.GlobalVariable CreateGlobal(Types.IType type, string name)
|
||||
{
|
||||
var ret = Emit.GlobalVariable.Create(Script.GlobalVariables.Count, type, name);
|
||||
Script.GlobalVariables.Add(ret);
|
||||
m_Globals.Add(name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Emit.GlobalVariable CreateGlobal(ABT.ExprType type, string name, IStoredLineInfo info)
|
||||
{
|
||||
if (m_Globals.ContainsKey(name)) throw new InvalidOperationException(string.Format("Global with name {0} already exists", name)).Attach(info);
|
||||
return CreateGlobal(EmitType(type, true), name);
|
||||
}
|
||||
|
||||
public CGenState() {
|
||||
this.label_idx = 2;
|
||||
this.label_packs = new Stack<LabelPack>();
|
||||
}
|
||||
|
||||
public IFunction GetFunction(string name)
|
||||
{
|
||||
if (m_Functions.TryGetValue(name, out var impl)) return impl.Function;
|
||||
if (!m_FunctionsExternal.TryGetValue(name, out var ext)) throw new KeyNotFoundException();
|
||||
return ext;
|
||||
}
|
||||
|
||||
private void DeclareFunctionArguments(FunctionBase func, ABT.FunctionType type)
|
||||
{
|
||||
// ensure that script has pointer type
|
||||
TypePointer.ToString();
|
||||
|
||||
if (type.ReturnType is ABT.VoidType)
|
||||
func.ReturnArgument = null;
|
||||
else
|
||||
func.ReturnArgument = EmitType(type.ReturnType);
|
||||
|
||||
func.Arguments = new List<FunctionArgument>();
|
||||
foreach (var arg in type.Args)
|
||||
{
|
||||
var farg = new FunctionArgument();
|
||||
farg.Name = arg.name;
|
||||
switch (arg.type)
|
||||
{
|
||||
case ABT.PointerType ptr:
|
||||
// void* == u32
|
||||
if (!ptr.IsRef)
|
||||
{
|
||||
break;
|
||||
}
|
||||
farg.ArgumentType = FunctionArgumentType.Out; //ref
|
||||
farg.Type = EmitType(ptr.RefType);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (farg.Type == null)
|
||||
{
|
||||
farg.ArgumentType = FunctionArgumentType.In;
|
||||
farg.Type = EmitType(arg.type);
|
||||
}
|
||||
func.Arguments.Add(farg);
|
||||
}
|
||||
}
|
||||
|
||||
private FunctionGen DeclareFunctionCore(string name, ABT.FunctionType type, ABT.StorageClass scs)
|
||||
{
|
||||
if (m_Functions.ContainsKey(name)) return null;
|
||||
if (m_FunctionsExternal.ContainsKey(name)) return null;
|
||||
|
||||
var state = new FunctionGen(name, scs);
|
||||
|
||||
|
||||
DeclareFunctionArguments(state.Function, type);
|
||||
|
||||
m_Functions[name] = state;
|
||||
Script.Functions.Add(state.Function);
|
||||
return state;
|
||||
}
|
||||
|
||||
public void DeclareFunction(string name, ABT.FunctionType type, ABT.StorageClass scs)
|
||||
{
|
||||
DeclareFunctionCore(name, type, scs);
|
||||
}
|
||||
|
||||
public void DeclareExternalFunction(string name, Emit.ExternalFunction func, ABT.FunctionType type, IStoredLineInfo info)
|
||||
{
|
||||
if (m_Functions.ContainsKey(name) || m_FunctionsExternal.ContainsKey(name)) throw new InvalidOperationException(string.Format("Already declared a function with name {0}", name)).Attach(info);
|
||||
|
||||
func.Name = name;
|
||||
DeclareFunctionArguments(func, type);
|
||||
|
||||
m_FunctionsExternal[name] = func;
|
||||
Script.Functions.Add(func);
|
||||
}
|
||||
|
||||
public void CGenFuncStart(string name, ABT.FunctionType type, ABT.StorageClass scs, IStoredLineInfo info) {
|
||||
if (m_Functions.TryGetValue(name, out var state))
|
||||
{
|
||||
if (state.Function.Instructions.Count != 0) throw new InvalidOperationException(string.Format("Already emitted instructions for function {0}", name)).Attach(info);
|
||||
FunctionState = state;
|
||||
return;
|
||||
}
|
||||
if (m_FunctionsExternal.ContainsKey(name)) throw new InvalidOperationException(string.Format("Already declared an external function with name {0}", name)).Attach(info);
|
||||
|
||||
FunctionState = DeclareFunctionCore(name, type, scs);
|
||||
}
|
||||
|
||||
// CGenExpandStack
|
||||
// ===============
|
||||
//
|
||||
|
||||
public void CGenForceStackSizeTo(Int32 nbytes) {
|
||||
if (!FunctionState.IndexToLabel.ContainsKey(CurrInsns.Count - 1))
|
||||
{
|
||||
var lastop = CurrInsns.LastOrDefault()?.OpCode.Code;
|
||||
if (lastop == null || lastop == Emit.Code.Jump || lastop == Emit.Code.Ret) return;
|
||||
}
|
||||
while (StackSize > nbytes) FunctionState.Pop();
|
||||
}
|
||||
|
||||
private Stack<int> stackSizes = new Stack<int>();
|
||||
|
||||
public void CGenPushStackSize()
|
||||
{
|
||||
stackSizes.Push(StackSize);
|
||||
}
|
||||
|
||||
public void CGenPeekStackSize()
|
||||
{
|
||||
if (stackSizes.Count == 0) return;
|
||||
CGenForceStackSizeTo(stackSizes.Peek());
|
||||
}
|
||||
|
||||
public void CGenPopStackSize()
|
||||
{
|
||||
if (stackSizes.Count == 0) return;
|
||||
CGenForceStackSizeTo(stackSizes.Pop());
|
||||
}
|
||||
|
||||
public void CGenPopStackSizeForRevert()
|
||||
{
|
||||
if (stackSizes.Count == 0) return;
|
||||
var expected = stackSizes.Pop();
|
||||
while (StackSize > expected) FunctionState.PopForRevert();
|
||||
}
|
||||
|
||||
public void CGenLabel(Int32 label)
|
||||
{
|
||||
if (FunctionState.IndexToLabel.ContainsKey(CurrInsns.Count - 1))
|
||||
{
|
||||
var lbl = FunctionState.Labels[label];
|
||||
// For each instruction referencing this label: replace the operand with CurrInsns.Last()
|
||||
var jump = CurrInsns.Last();
|
||||
foreach (var insn in CurrInsns)
|
||||
{
|
||||
switch (insn.OpCode.OperandType)
|
||||
{
|
||||
case Emit.OperandType.InlineBrTarget:
|
||||
case Emit.OperandType.InlineBrTargetValue:
|
||||
if (insn[0].ImmediateAs<Emit.Instruction>() == lbl) insn[0] = Emit.Operand.Create(jump);
|
||||
break;
|
||||
case Emit.OperandType.InlineEH:
|
||||
if (insn[0].ImmediateAs<Emit.Instruction>() == lbl) insn[0] = Emit.Operand.Create(jump);
|
||||
if (insn[1].ImmediateAs<Emit.Instruction>() == lbl) insn[1] = Emit.Operand.Create(jump);
|
||||
if (insn[2].ImmediateAs<Emit.Instruction>() == lbl) insn[2] = Emit.Operand.Create(jump);
|
||||
if (insn[3].ImmediateAs<Emit.Instruction>() == lbl) insn[3] = Emit.Operand.Create(jump);
|
||||
break;
|
||||
}
|
||||
}
|
||||
FunctionState.Labels[label] = jump;
|
||||
return;
|
||||
}
|
||||
|
||||
var idx = CurrInsns.Count;
|
||||
CurrInsns.Add(FunctionState.Labels[label]);
|
||||
FunctionState.LabelToIndex.Add(label, idx);
|
||||
FunctionState.IndexToLabel.Add(idx, label);
|
||||
}
|
||||
|
||||
public Int32 label_idx;
|
||||
|
||||
|
||||
public Int32 StackSize => FunctionState.LocalsCount;
|
||||
|
||||
public Int32 RequestLabel() {
|
||||
this.label_idx = FunctionState.Labels.Count;
|
||||
var ret = label_idx;
|
||||
FunctionState.Labels.Add(Emit.Instruction.Create(Emit.OpCodes.Nop));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
//private Stack<Int32> _continue_labels;
|
||||
//private Stack<Int32> _break_labels;
|
||||
|
||||
private struct LabelPack {
|
||||
public LabelPack(Int32 continue_label, Int32 break_label, Int32 default_label, Dictionary<Int32, Int32> value_to_label) {
|
||||
this.continue_label = continue_label;
|
||||
this.break_label = break_label;
|
||||
this.default_label = default_label;
|
||||
this.value_to_label = value_to_label;
|
||||
}
|
||||
public readonly Int32 continue_label;
|
||||
public readonly Int32 break_label;
|
||||
public readonly Int32 default_label;
|
||||
public readonly Dictionary<Int32, Int32> value_to_label;
|
||||
}
|
||||
|
||||
private readonly Stack<LabelPack> label_packs;
|
||||
|
||||
public Int32 ContinueLabel => this.label_packs.First(_ => _.continue_label != -1).continue_label;
|
||||
|
||||
public Int32 BreakLabel => this.label_packs.First(_ => _.break_label != -1).break_label;
|
||||
|
||||
public Int32 DefaultLabel {
|
||||
get {
|
||||
Int32 ret = this.label_packs.First().default_label;
|
||||
if (ret == -1) {
|
||||
throw new InvalidOperationException("Not in a switch statement.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInSwitch => label_packs.Count > 0 ? label_packs.Peek().value_to_label != null : false;
|
||||
|
||||
public Int32 CaseLabel(Int32 value) => this.label_packs.First(_ => _.value_to_label != null).value_to_label[value];
|
||||
// label_packs.First().value_to_label[Value];
|
||||
|
||||
public void InLoop(Int32 continue_label, Int32 break_label) {
|
||||
this.label_packs.Push(new LabelPack(continue_label, break_label, -1, null));
|
||||
//_continue_labels.Push(continue_label);
|
||||
//_break_labels.Push(break_label);
|
||||
}
|
||||
|
||||
public void InSwitch(Int32 break_label, Int32 default_label, Dictionary<Int32, Int32> value_to_label) {
|
||||
this.label_packs.Push(new LabelPack(-1, break_label, default_label, value_to_label));
|
||||
}
|
||||
|
||||
public void OutLabels() {
|
||||
this.label_packs.Pop();
|
||||
//_continue_labels.Pop();
|
||||
//_break_labels.Pop();
|
||||
}
|
||||
|
||||
private readonly Dictionary<String, Int32> _goto_labels = new Dictionary<String, Int32>();
|
||||
|
||||
public Int32 GotoLabel(String label) {
|
||||
return this._goto_labels[label];
|
||||
}
|
||||
|
||||
public void JumpTo(int label, bool ignoreStack)
|
||||
{
|
||||
if (FunctionState.IndexToLabel.TryGetValue(CurrInsns.Count - 1, out var lastLabel))
|
||||
{
|
||||
var lbl = CurrInsns.Last();
|
||||
// For each instruction referencing this label: replace the operand with CurrInsns.Last()
|
||||
var jump = FunctionState.Labels[label];
|
||||
foreach (var insn in CurrInsns)
|
||||
{
|
||||
switch (insn.OpCode.OperandType)
|
||||
{
|
||||
case Emit.OperandType.InlineBrTarget:
|
||||
case Emit.OperandType.InlineBrTargetValue:
|
||||
if (insn[0].ImmediateAs<Emit.Instruction>() == lbl) insn[0] = Emit.Operand.Create(jump);
|
||||
break;
|
||||
case Emit.OperandType.InlineEH:
|
||||
if (insn[0].ImmediateAs<Emit.Instruction>() == lbl) insn[0] = Emit.Operand.Create(jump);
|
||||
if (insn[1].ImmediateAs<Emit.Instruction>() == lbl) insn[1] = Emit.Operand.Create(jump);
|
||||
if (insn[2].ImmediateAs<Emit.Instruction>() == lbl) insn[2] = Emit.Operand.Create(jump);
|
||||
if (insn[3].ImmediateAs<Emit.Instruction>() == lbl) insn[3] = Emit.Operand.Create(jump);
|
||||
break;
|
||||
}
|
||||
}
|
||||
FunctionState.Labels[lastLabel] = jump;
|
||||
return;
|
||||
}
|
||||
if (!ignoreStack) CGenPeekStackSize();
|
||||
CurrInsns.Add(Emit.Instruction.Create(Emit.OpCodes.Jump, FunctionState.Labels[label]));
|
||||
}
|
||||
|
||||
|
||||
public void InFunction(IReadOnlyList<String> goto_labels) {
|
||||
this._goto_labels.Clear();
|
||||
foreach (String goto_label in goto_labels) {
|
||||
this._goto_labels.Add(goto_label, RequestLabel());
|
||||
}
|
||||
}
|
||||
|
||||
public void OutFunction() {
|
||||
this._goto_labels.Clear();
|
||||
|
||||
// Optimisation: replace any nop instruction at label with instruction after.
|
||||
foreach (var label in FunctionState.LabelToIndex.OrderByDescending((l) => l.Value))
|
||||
{
|
||||
if (CurrInsns.Count > label.Value && CurrInsns[label.Value].OpCode.Code == Emit.Code.Nop)
|
||||
{
|
||||
CurrInsns[label.Value].Replace(CurrInsns[label.Value + 1]);
|
||||
CurrInsns.RemoveAt(label.Value + 1);
|
||||
}
|
||||
}
|
||||
|
||||
FunctionState.Function.UpdateInstructionOffsets();
|
||||
FunctionState.Function.UpdateInstructionCrossReferences();
|
||||
}
|
||||
|
||||
public void EmitCallsToCtor()
|
||||
{
|
||||
// if the constructor function wasn't created (ie, no initialised global variables), nothing needs to be done
|
||||
if (m_Constructor == null) return;
|
||||
// Emit the return instruction for the constructor.
|
||||
m_Constructor.Function.Instructions.Add(Emit.Instruction.Create(Emit.OpCodes.Ret));
|
||||
// Enumerate through all public functions (non-static).
|
||||
var publicFunctions = m_Functions.Values.Select(f => f.Function).Where(f => f.Exported);
|
||||
|
||||
foreach (var f in publicFunctions)
|
||||
{
|
||||
// Add an instruction to the start to call the constructor.
|
||||
f.Instructions.Insert(0, Emit.Instruction.Create(Emit.OpCodes.Call, m_Constructor.Function as IFunction));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
LibIFPSCC/CGen/ConstExpressions.cs
Normal file
@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using CodeGeneration;
|
||||
using IFPSLib.Emit;
|
||||
|
||||
namespace ABT {
|
||||
public abstract partial class ConstExpr {
|
||||
public override sealed Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of a constant").Attach(this);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstLong {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstULong {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstS64
|
||||
{
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstU64
|
||||
{
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstShort {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstUShort {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstChar {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstUChar {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstPtr {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
var ptrType = Type as PointerType;
|
||||
if (ptrType == null) throw new InvalidProgramException().Attach(this);
|
||||
if (!ptrType.IsRef) return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
var operand = state.FunctionState.PushType(state.TypeArrayOfPointer);
|
||||
state.CGenPushStackSize();
|
||||
var dummyForType = state.FunctionState.PushType(state.EmitType(ptrType.RefType));
|
||||
state.FunctionState.PushVar(operand);
|
||||
state.FunctionState.Push(Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value));
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CGenPopStackSize();
|
||||
return Operand.Create(operand.Variable, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstFloat {
|
||||
/// <summary>
|
||||
/// flds addr
|
||||
/// </summary>
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstDouble {
|
||||
/// <summary>
|
||||
/// fldl addr
|
||||
/// </summary>
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstStringLiteral {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConstUnicodeStringLiteral
|
||||
{
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Operand.Create((IFPSLib.Types.PrimitiveType)state.EmitType(Type), Value);
|
||||
}
|
||||
}
|
||||
}
|
917
LibIFPSCC/CGen/Expressions.cs
Normal file
@ -0,0 +1,917 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using AST;
|
||||
using CodeGeneration;
|
||||
using IFPSLib.Emit;
|
||||
|
||||
namespace ABT {
|
||||
public abstract partial class Expr {
|
||||
public abstract Operand CGenValue(CGenState state, Operand retLoc);
|
||||
|
||||
public abstract Operand CGenAddress(CGenState state, Operand retLoc);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this expression modifies the stack in such a way that the callee needs to clean up (ie, expression allocates then returns a new Operand)
|
||||
/// </summary>
|
||||
/// <param name="state">Code generation state</param>
|
||||
/// <param name="retLocKnown"></param>
|
||||
/// <returns></returns>
|
||||
/// <param name="forAddress">True if being called for CGenAddress, false if being called for CGenValue</param>
|
||||
public abstract bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false);
|
||||
}
|
||||
|
||||
public sealed partial class ExprInitList
|
||||
{
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
Operand op = retLoc != null ? retLoc : state.FunctionState.PushType(state.EmitType(Type));
|
||||
if (Type.Kind == ExprTypeKind.INCOMPLETE_ARRAY)
|
||||
{
|
||||
var arr = Type as IncompleteArrayType;
|
||||
var arr_op = op;
|
||||
// emit SetArrayLength(&arr_op, length)
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.Push(Operand.Create(List.initrs.Count));
|
||||
state.FunctionState.PushVar(arr_op);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.SetArrayLength));
|
||||
state.CGenPopStackSize();
|
||||
}
|
||||
Decln.EmitInitialiser(List, Type, op, state);
|
||||
return op;
|
||||
}
|
||||
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of an initialiser list.").Attach(this);
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false) => !retLocKnown;
|
||||
}
|
||||
|
||||
public sealed partial class Variable {
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
Env.Entry entry = this.Env.Find(this.Name).Value;
|
||||
Int32 offset = entry.Offset;
|
||||
|
||||
switch (entry.Kind) {
|
||||
case Env.EntryKind.FRAME:
|
||||
return Operand.Create(state.FunctionState.Function.CreateArgumentVariable(offset));
|
||||
case Env.EntryKind.STACK:
|
||||
return Operand.Create(LocalVariable.Create(offset));
|
||||
|
||||
case Env.EntryKind.GLOBAL:
|
||||
return Operand.Create(state.Globals[Name]);
|
||||
|
||||
case Env.EntryKind.ENUM:
|
||||
case Env.EntryKind.TYPEDEF:
|
||||
default:
|
||||
throw new InvalidProgramException("cannot get the address of " + entry.Kind).Attach(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
Env.Entry entry = this.Env.Find(this.Name).Value;
|
||||
|
||||
Int32 offset = entry.Offset;
|
||||
//if (entry.Kind == Env.EntryKind.STACK) {
|
||||
// offset = -offset;
|
||||
//}
|
||||
|
||||
IVariable var = null;
|
||||
|
||||
switch (entry.Kind) {
|
||||
case Env.EntryKind.ENUM:
|
||||
// 1. If the variable is an enum constant,
|
||||
// return the Value in %eax.
|
||||
return Operand.Create(offset);
|
||||
|
||||
case Env.EntryKind.FRAME:
|
||||
var = state.FunctionState.Function.CreateArgumentVariable(offset);
|
||||
break;
|
||||
case Env.EntryKind.STACK:
|
||||
// 2. If the variable is a function argument or a local variable,
|
||||
// the address would be offset(%ebp).
|
||||
var = LocalVariable.Create(offset);
|
||||
break;
|
||||
|
||||
case Env.EntryKind.GLOBAL:
|
||||
switch (this.Type.Kind) {
|
||||
case ExprTypeKind.CHAR:
|
||||
case ExprTypeKind.UCHAR:
|
||||
case ExprTypeKind.SHORT:
|
||||
case ExprTypeKind.USHORT:
|
||||
case ExprTypeKind.LONG:
|
||||
case ExprTypeKind.ULONG:
|
||||
case ExprTypeKind.S64:
|
||||
case ExprTypeKind.U64:
|
||||
case ExprTypeKind.POINTER:
|
||||
case ExprTypeKind.FLOAT:
|
||||
case ExprTypeKind.DOUBLE:
|
||||
case ExprTypeKind.ANSI_STRING:
|
||||
case ExprTypeKind.UNICODE_STRING:
|
||||
case ExprTypeKind.COM_INTERFACE:
|
||||
case ExprTypeKind.COM_VARIANT:
|
||||
return Operand.Create(state.Globals[Name]);
|
||||
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
case ExprTypeKind.ARRAY:
|
||||
case ExprTypeKind.INCOMPLETE_ARRAY:
|
||||
return Operand.Create(state.Globals[Name]);
|
||||
|
||||
//state.LEA(name, Reg.ESI); // source address
|
||||
//state.CGenExpandStackBy(Utils.RoundUp(Type.SizeOf, 4));
|
||||
//state.LEA(0, Reg.ESP, Reg.EDI); // destination address
|
||||
//state.MOVL(Type.SizeOf, Reg.ECX); // nbytes
|
||||
//state.CGenMemCpy();
|
||||
//return Reg.STACK;
|
||||
|
||||
case ExprTypeKind.FUNCTION:
|
||||
var fptrt = (Type as FunctionType).EmitPointer(state) as IFPSLib.Types.FunctionPointerType;
|
||||
return new Operand(IFPSLib.TypedData.Create(fptrt, state.GetFunction(Name)));
|
||||
|
||||
case ExprTypeKind.VOID:
|
||||
throw new InvalidProgramException("How could a variable be void?").Attach(this);
|
||||
//state.MOVL(0, Reg.EAX);
|
||||
//return Reg.EAX;
|
||||
|
||||
|
||||
default:
|
||||
throw new InvalidProgramException("cannot get the Value of a " + this.Type.Kind).Attach(this);
|
||||
}
|
||||
|
||||
case Env.EntryKind.TYPEDEF:
|
||||
default:
|
||||
throw new InvalidProgramException("cannot get the Value of a " + entry.Kind).Attach(this);
|
||||
}
|
||||
|
||||
switch (this.Type.Kind)
|
||||
{
|
||||
|
||||
case ExprTypeKind.POINTER:
|
||||
if ((this.Type as PointerType).IsRef) return Operand.Create(var);
|
||||
else return Operand.Create(var, 0);
|
||||
case ExprTypeKind.CHAR:
|
||||
case ExprTypeKind.UCHAR:
|
||||
case ExprTypeKind.SHORT:
|
||||
case ExprTypeKind.USHORT:
|
||||
case ExprTypeKind.LONG:
|
||||
case ExprTypeKind.ULONG:
|
||||
case ExprTypeKind.S64:
|
||||
case ExprTypeKind.U64:
|
||||
case ExprTypeKind.FLOAT:
|
||||
case ExprTypeKind.DOUBLE:
|
||||
case ExprTypeKind.ANSI_STRING:
|
||||
case ExprTypeKind.UNICODE_STRING:
|
||||
case ExprTypeKind.COM_INTERFACE:
|
||||
case ExprTypeKind.COM_VARIANT:
|
||||
return Operand.Create(var);
|
||||
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
case ExprTypeKind.ARRAY:
|
||||
case ExprTypeKind.INCOMPLETE_ARRAY:
|
||||
return Operand.Create(var);
|
||||
|
||||
//state.LEA(offset, Reg.EBP, Reg.ESI); // source address
|
||||
//state.CGenExpandStackBy(Utils.RoundUp(Type.SizeOf, 4));
|
||||
//state.LEA(0, Reg.ESP, Reg.EDI); // destination address
|
||||
//state.MOVL(Type.SizeOf, Reg.ECX); // nbytes
|
||||
//state.CGenMemCpy();
|
||||
//return Reg.STACK;
|
||||
|
||||
case ExprTypeKind.VOID:
|
||||
throw new InvalidProgramException("How could a variable be void?").Attach(this);
|
||||
// %eax = $0
|
||||
// state.MOVL(0, Reg.EAX);
|
||||
// return Reg.EAX;
|
||||
|
||||
case ExprTypeKind.FUNCTION:
|
||||
throw new InvalidProgramException("How could a variable be a function designator?").Attach(this);
|
||||
// %eax = function_name
|
||||
// state.MOVL(name, Reg.EAX);
|
||||
// return Reg.EAX;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"Cannot get value of {this.Type.Kind}").Attach(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false) => false;
|
||||
}
|
||||
|
||||
public sealed partial class AssignList {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
Operand op = null;
|
||||
foreach (Expr expr in this.Exprs) {
|
||||
op = expr.CGenValue(state, null);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of an assignment list.").Attach(this);
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false) => Exprs.Any((expr) => expr.CallerNeedsToCleanStack(state, false));
|
||||
}
|
||||
|
||||
public sealed partial class Assign {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
var operand = Left.CGenAddress(state, retLoc);
|
||||
var ret = Right.CGenValue(state, operand);
|
||||
if (OperandEquator.Equals(ret, retLoc)) return ret;
|
||||
|
||||
switch (this.Left.Type.Kind) {
|
||||
case ExprTypeKind.CHAR:
|
||||
case ExprTypeKind.UCHAR:
|
||||
|
||||
case ExprTypeKind.SHORT:
|
||||
case ExprTypeKind.USHORT:
|
||||
|
||||
case ExprTypeKind.LONG:
|
||||
case ExprTypeKind.ULONG:
|
||||
case ExprTypeKind.S64:
|
||||
case ExprTypeKind.U64:
|
||||
|
||||
case ExprTypeKind.FLOAT:
|
||||
|
||||
case ExprTypeKind.DOUBLE:
|
||||
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
|
||||
|
||||
case ExprTypeKind.ANSI_STRING:
|
||||
case ExprTypeKind.UNICODE_STRING:
|
||||
|
||||
case ExprTypeKind.COM_INTERFACE:
|
||||
case ExprTypeKind.COM_VARIANT:
|
||||
if (OperandEquator.Equals(operand, ret)) return operand;
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, operand, ret));
|
||||
break;
|
||||
|
||||
|
||||
case ExprTypeKind.POINTER:
|
||||
var lAttr = Left as ABT.Attribute;
|
||||
var rAttr = Right as ABT.Attribute;
|
||||
var lArr = Left as ABT.ArrayIndexDeref;
|
||||
var rArr = Right as ABT.ArrayIndexDeref;
|
||||
|
||||
var ptr = Left.Type as PointerType;
|
||||
var ptrR = Right.Type as PointerType;
|
||||
|
||||
var needCast = ptr.IsRef != ptrR.IsRef;
|
||||
bool lIsVoidPointer = lAttr != null || lArr != null; // both Attribute and ArrayIndexDeref CGenAddress gives a u32*
|
||||
var isVoid = !ptr.IsRef;
|
||||
if (needCast) {
|
||||
// assigning to or from a structure or array element.
|
||||
// CGenAddress for both of these returns u32*.
|
||||
// Ptr-to-ptr cast into operand.
|
||||
var rIsUnion = ((StructOrUnionType)(rAttr?.Expr.Type))?.IsStruct == false;
|
||||
bool isVoidR = !ptrR.IsRef;
|
||||
if (lIsVoidPointer)
|
||||
{
|
||||
// *lhs = (u32)rhs;
|
||||
state.CGenPushStackSize();
|
||||
var cast = state.FunctionState.PushType(state.TypeU32);
|
||||
state.CGenPushStackSize();
|
||||
if (rIsUnion) // for a union, we have u32* already
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, cast, ret));
|
||||
}
|
||||
else
|
||||
{
|
||||
state.FunctionState.PushVar(ret);
|
||||
state.FunctionState.PushVar(cast);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
}
|
||||
state.CGenPopStackSize();
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, operand, cast));
|
||||
state.CGenPopStackSize();
|
||||
break;
|
||||
}
|
||||
if ((!isVoid && !isVoidR) || rIsUnion)
|
||||
{
|
||||
|
||||
state.CGenPushStackSize();
|
||||
|
||||
var dummyForType = state.FunctionState.PushType(state.EmitType(ptr.RefType));
|
||||
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
|
||||
if (ptr.IsRef)
|
||||
{
|
||||
// lhs is byref, deal
|
||||
// do a pointer-to-pointer cast
|
||||
var ptrArr = state.FunctionState.PushType(state.TypeArrayOfPointer);
|
||||
state.CGenPushStackSize();
|
||||
if (rIsUnion) // for a union, we have u32* already
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, dummyU32, ret));
|
||||
}
|
||||
else
|
||||
{
|
||||
state.FunctionState.PushVar(ret);
|
||||
state.FunctionState.PushVar(dummyU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
}
|
||||
state.CGenPopStackSize();
|
||||
state.FunctionState.PushVar(ptrArr);
|
||||
state.FunctionState.Push(dummyU32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, operand, Operand.Create(ptrArr.Variable, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
state.CGenPushStackSize();
|
||||
if (rIsUnion) // for a union, we have u32* already
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, dummyU32, ret));
|
||||
}
|
||||
else
|
||||
{
|
||||
state.FunctionState.PushVar(ret);
|
||||
state.FunctionState.PushVar(dummyU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
}
|
||||
state.CGenPopStackSize();
|
||||
state.FunctionState.PushVar(operand);
|
||||
state.FunctionState.Push(dummyU32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
}
|
||||
state.CGenPopStackSize();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(isVoid ? OpCodes.Assign : OpCodes.SetPtr, operand, ret));
|
||||
break;
|
||||
|
||||
case ExprTypeKind.FUNCTION:
|
||||
case ExprTypeKind.VOID:
|
||||
case ExprTypeKind.ARRAY:
|
||||
case ExprTypeKind.INCOMPLETE_ARRAY:
|
||||
default:
|
||||
throw new InvalidProgramException("cannot assign to a " + this.Type.Kind);
|
||||
}
|
||||
|
||||
return operand;
|
||||
}
|
||||
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of an assignment expression.").Attach(this);
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
return Left.CallerNeedsToCleanStack(state, retLocKnown) || Right.CallerNeedsToCleanStack(state, retLocKnown, true);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ConditionalExpr {
|
||||
//
|
||||
// jz false, Cond ---+
|
||||
// true_expr |
|
||||
// +------- jmp finish |
|
||||
// | false: <--------+
|
||||
// | false_expr
|
||||
// +--> finish:
|
||||
//
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
//Int32 stack_size = state.StackSize;
|
||||
var ret = this.Cond.CGenValue(state, null);
|
||||
|
||||
Int32 false_label = state.RequestLabel();
|
||||
Int32 finish_label = state.RequestLabel();
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpZ, state.FunctionState.Labels[false_label], ret));
|
||||
|
||||
this.TrueExpr.CGenValue(state, null);
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[finish_label], ret));
|
||||
|
||||
state.CGenLabel(false_label);
|
||||
|
||||
var count = state.CurrInsns.Count;
|
||||
|
||||
ret = this.FalseExpr.CGenValue(state, null);
|
||||
|
||||
if (state.CurrInsns.Count > count)
|
||||
{
|
||||
state.FunctionState.Labels[false_label].Replace(state.CurrInsns[count]);
|
||||
state.CurrInsns.RemoveAt(count);
|
||||
}
|
||||
|
||||
state.CGenLabel(finish_label);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of a conditional expression.").Attach(this);
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
return Cond.CallerNeedsToCleanStack(state, retLocKnown) || TrueExpr.CallerNeedsToCleanStack(state, retLocKnown) || FalseExpr.CallerNeedsToCleanStack(state, retLocKnown);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class FuncCall {
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Error: cannot get the address of a function call.").Attach(this);
|
||||
}
|
||||
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
|
||||
// PascalScript bytecode calling convention:
|
||||
// Arguments pushed last-to-first
|
||||
// Then, if function returns a value, pointer to return value is pushed
|
||||
// Then call.
|
||||
// Callee cleans up stack.
|
||||
|
||||
FunctionType ft = null;
|
||||
switch (Func.Type)
|
||||
{
|
||||
case FunctionType _ft:
|
||||
ft = _ft;
|
||||
break;
|
||||
case PointerType _pt:
|
||||
ft = (FunctionType)_pt.RefType;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException().Attach(Func);
|
||||
}
|
||||
|
||||
var rt = ft.ReturnType;
|
||||
Operand retval = null;
|
||||
if (!(rt is VoidType))
|
||||
{
|
||||
// returns a value
|
||||
// prep the stack for retval
|
||||
retval = retLoc != null ? retLoc : state.FunctionState.PushType(state.EmitType(rt));
|
||||
}
|
||||
|
||||
state.CGenPushStackSize();
|
||||
|
||||
// Push the arguments onto the stack in reverse order
|
||||
foreach (var arg in Args.Reverse()) {
|
||||
var ptr = arg.Type as PointerType;
|
||||
// Get the arg in a way that changes can be reverted, so to know if stack gets touched or not.
|
||||
var insnCount = state.CurrInsns.Count;
|
||||
var oldStack = state.StackSize;
|
||||
var doesPush = arg.CallerNeedsToCleanStack(state, false);
|
||||
Operand argop = null;
|
||||
/*
|
||||
state.CGenPushStackSize();
|
||||
var argop = arg.CGenValue(state);
|
||||
var doesPush = state.StackSize != oldStack;
|
||||
state.CGenPopStackSizeForRevert();
|
||||
*/
|
||||
if (doesPush)
|
||||
{
|
||||
// Stack was touched. Revert all changes.
|
||||
//while (state.CurrInsns.Count > insnCount) state.CurrInsns.RemoveAt(state.CurrInsns.Count - 1);
|
||||
Operand _argop = null;
|
||||
if (arg.Type.Kind == ExprTypeKind.POINTER && ptr.IsRef)
|
||||
{
|
||||
// this is a reftype, so, pushtype pointer ; ... ; setptr argop, value ; fix stack
|
||||
argop = state.FunctionState.PushType(state.TypePointer);
|
||||
state.CGenPushStackSize();
|
||||
_argop = arg.CGenValue(state, argop);
|
||||
if (_argop != argop) state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, argop, _argop));
|
||||
state.CGenPopStackSize();
|
||||
continue;
|
||||
}
|
||||
// this is a valtype, so pushtype type ; ... ; assign argop, value ; fix stack
|
||||
argop = state.FunctionState.PushType(state.EmitType(arg.Type));
|
||||
state.CGenPushStackSize();
|
||||
_argop = arg.CGenValue(state, argop);
|
||||
if (_argop != argop) state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, argop, _argop));
|
||||
state.CGenPopStackSize();
|
||||
continue;
|
||||
}
|
||||
argop = arg.CGenValue(state, null);
|
||||
switch (arg.Type.Kind) {
|
||||
case ExprTypeKind.ARRAY:
|
||||
case ExprTypeKind.INCOMPLETE_ARRAY:
|
||||
case ExprTypeKind.CHAR:
|
||||
case ExprTypeKind.UCHAR:
|
||||
case ExprTypeKind.SHORT:
|
||||
case ExprTypeKind.USHORT:
|
||||
case ExprTypeKind.LONG:
|
||||
case ExprTypeKind.ULONG:
|
||||
case ExprTypeKind.S64:
|
||||
case ExprTypeKind.U64:
|
||||
case ExprTypeKind.DOUBLE:
|
||||
case ExprTypeKind.FLOAT:
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
case ExprTypeKind.ANSI_STRING:
|
||||
case ExprTypeKind.UNICODE_STRING:
|
||||
case ExprTypeKind.COM_INTERFACE:
|
||||
case ExprTypeKind.COM_VARIANT:
|
||||
state.FunctionState.Push(argop);
|
||||
break;
|
||||
|
||||
case ExprTypeKind.POINTER:
|
||||
if (!ptr.IsRef) state.FunctionState.Push(argop);
|
||||
else state.FunctionState.PushVar(argop);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidProgramException().Attach(arg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (retval != null) state.FunctionState.PushVar(retval);
|
||||
|
||||
// Get function address
|
||||
if (this.Func.Type is FunctionType) {
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, Func.CGenValue(state, null).ImmediateTyped.ValueAs<IFPSLib.IFunction>()));
|
||||
} else if (this.Func.Type is PointerType) {
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.CallVar, Func.CGenValue(state, null)));
|
||||
} else {
|
||||
throw new InvalidProgramException().Attach(this.Func);
|
||||
}
|
||||
|
||||
// Fix up the stack.
|
||||
state.CGenPopStackSize();
|
||||
|
||||
// For a pointer, we've returned arrayofpointer so fix it up.
|
||||
var retPtr = rt as PointerType;
|
||||
if (retPtr != null)
|
||||
{
|
||||
if (!retPtr.IsRef) return retval;
|
||||
return Operand.Create(retval.Variable, 0);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
FunctionType ft = null;
|
||||
switch (Func.Type)
|
||||
{
|
||||
case FunctionType _ft:
|
||||
ft = _ft;
|
||||
break;
|
||||
case PointerType _pt:
|
||||
ft = (FunctionType)_pt.RefType;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException().Attach(Func);
|
||||
}
|
||||
|
||||
var rt = ft.ReturnType;
|
||||
return rt.Kind != ExprTypeKind.VOID && !retLocKnown;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Attribute {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
|
||||
if (this.Expr.Type.Kind != ExprTypeKind.STRUCT_OR_UNION) {
|
||||
throw new InvalidProgramException().Attach(this.Expr);
|
||||
}
|
||||
|
||||
// get the last instruction first in case it emits
|
||||
var last = state.CurrInsns.LastOrDefault();
|
||||
var op = Expr.CGenValue(state, retLoc);
|
||||
// if this is not a variable, we need to save it somewhere
|
||||
if (op.Type == BytecodeOperandType.Immediate) throw new InvalidProgramException().Attach(Expr);
|
||||
if (op.Type == BytecodeOperandType.IndexedImmediate || op.Type == BytecodeOperandType.IndexedVariable)
|
||||
{
|
||||
// last instruction should be pushvar or setptr
|
||||
// if not, then pushvar it
|
||||
switch (last?.OpCode.Code)
|
||||
{
|
||||
case Code.PushVar:
|
||||
var pv = LocalVariable.Create(state.FunctionState.LocalsCount - 1);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, pv, op));
|
||||
op = Operand.Create(pv);
|
||||
break;
|
||||
case Code.SetPtr:
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, last.Operands[0], op));
|
||||
op = last.Operands[0];
|
||||
break;
|
||||
default:
|
||||
if (!CallerNeedsToCleanStack(state, retLoc != null, false)) throw new InvalidOperationException().Attach(Expr);
|
||||
op = state.FunctionState.PushVar(op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (op.Type != BytecodeOperandType.Variable)
|
||||
{
|
||||
// todo, not sure how to do this for now
|
||||
throw new InvalidOperationException().Attach(Expr);
|
||||
}
|
||||
|
||||
|
||||
// size of the struct or union
|
||||
Int32 struct_size = this.Expr.Type.SizeOf;
|
||||
|
||||
var type = (StructOrUnionType)this.Expr.Type;
|
||||
|
||||
// offset inside the pack
|
||||
int attrib_offset = type
|
||||
.Attribs
|
||||
.Select((a, i) => (a, i))
|
||||
.First(_ => _.a.name == this.Name)
|
||||
.i;
|
||||
|
||||
|
||||
if (!type.IsStruct)
|
||||
{
|
||||
// this is a union, so:
|
||||
// op is a byte array
|
||||
// get the pointer to that then do ptr-to-ptr cast
|
||||
op = Operand.Create(op.Variable, 0);
|
||||
last = state.CurrInsns.LastOrDefault();
|
||||
switch (last?.OpCode.Code)
|
||||
{
|
||||
case Code.PushVar:
|
||||
var pv = LocalVariable.Create(state.FunctionState.LocalsCount - 1);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, pv, op));
|
||||
op = Operand.Create(pv);
|
||||
break;
|
||||
case Code.SetPtr:
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, last.Operands[0], op));
|
||||
op = last.Operands[0];
|
||||
break;
|
||||
default:
|
||||
if (!CallerNeedsToCleanStack(state, retLoc != null, false)) throw new InvalidOperationException().Attach(Expr);
|
||||
op = state.FunctionState.PushVar(op);
|
||||
break;
|
||||
}
|
||||
// use pointerinitialiser as we need to have setptr as last insn
|
||||
state.CGenPushStackSize();
|
||||
// for a pointer, always cast to u32*
|
||||
var ptr = Type as PointerType;
|
||||
var dummyForType = state.FunctionState.PushType(ptr != null ? state.TypeU32 : state.EmitType(Type));
|
||||
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.PushVar(op);
|
||||
state.FunctionState.PushVar(dummyU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.CGenPopStackSize();
|
||||
state.FunctionState.PushVar(Operand.Create(state.PointerInitialiser));
|
||||
state.FunctionState.Push(dummyU32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CGenPopStackSize();
|
||||
var ptrInit = Operand.Create(state.PointerInitialiser, 0);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, op, ptrInit));
|
||||
return op;
|
||||
}
|
||||
|
||||
// can't be a function designator.
|
||||
switch (this.Type.Kind) {
|
||||
case ExprTypeKind.ARRAY:
|
||||
case ExprTypeKind.INCOMPLETE_ARRAY:
|
||||
case ExprTypeKind.STRUCT_OR_UNION:
|
||||
case ExprTypeKind.CHAR:
|
||||
case ExprTypeKind.UCHAR:
|
||||
case ExprTypeKind.SHORT:
|
||||
case ExprTypeKind.USHORT:
|
||||
case ExprTypeKind.LONG:
|
||||
case ExprTypeKind.ULONG:
|
||||
case ExprTypeKind.S64:
|
||||
case ExprTypeKind.U64:
|
||||
case ExprTypeKind.FLOAT:
|
||||
case ExprTypeKind.DOUBLE:
|
||||
case ExprTypeKind.ANSI_STRING:
|
||||
case ExprTypeKind.UNICODE_STRING:
|
||||
case ExprTypeKind.COM_INTERFACE:
|
||||
case ExprTypeKind.COM_VARIANT:
|
||||
return Operand.Create(op.Variable, (uint)attrib_offset);
|
||||
|
||||
case ExprTypeKind.POINTER:
|
||||
var ptr = Type as PointerType;
|
||||
op = Operand.Create(op.Variable, (uint)attrib_offset);
|
||||
if (!ptr.IsRef) return op;
|
||||
// structure element, so we must cast to correct pointer type
|
||||
state.CGenPushStackSize();
|
||||
// we have u32 or u32*, we want (T*)ptr or *(T*)pptr
|
||||
var dummyForType = state.FunctionState.PushType(state.EmitType(ptr.RefType));
|
||||
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.PushVar(op);
|
||||
state.FunctionState.PushVar(dummyU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.CGenPopStackSize();
|
||||
state.FunctionState.PushVar(Operand.Create(state.PointerInitialiser));
|
||||
state.FunctionState.Push(dummyU32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CGenPopStackSize();
|
||||
var ptrInit = Operand.Create(state.PointerInitialiser, 0);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, op, ptrInit));
|
||||
return op;
|
||||
default:
|
||||
throw new InvalidProgramException().Attach(Expr);
|
||||
}
|
||||
}
|
||||
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
if (this.Expr.Type.Kind != ExprTypeKind.STRUCT_OR_UNION) {
|
||||
throw new InvalidProgramException().Attach(Expr);
|
||||
}
|
||||
|
||||
// get the operand of the struct or union
|
||||
// get the last instruction first in case it emits
|
||||
var last = state.CurrInsns.LastOrDefault();
|
||||
|
||||
var type = ((StructOrUnionType)this.Expr.Type);
|
||||
var op = Expr.CGenAddress(state, retLoc);
|
||||
var isDeref = Expr is Dereference;
|
||||
if (type.IsStruct || !isDeref)
|
||||
{
|
||||
// if this is not a variable, we need to save it somewhere
|
||||
if (op.Type == BytecodeOperandType.Immediate) throw new InvalidProgramException().Attach(Expr);
|
||||
if (op.Type == BytecodeOperandType.IndexedImmediate || op.Type == BytecodeOperandType.IndexedVariable)
|
||||
{
|
||||
// last instruction should be pushvar or setptr
|
||||
// if not, then pushvar it
|
||||
switch (last?.OpCode.Code)
|
||||
{
|
||||
case Code.PushVar:
|
||||
var pv = LocalVariable.Create(state.FunctionState.LocalsCount - 1);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, pv, op));
|
||||
op = Operand.Create(pv);
|
||||
break;
|
||||
case Code.SetPtr:
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, last.Operands[0], op));
|
||||
op = last.Operands[0];
|
||||
break;
|
||||
default:
|
||||
if (!CallerNeedsToCleanStack(state, retLoc != null, true)) throw new InvalidOperationException().Attach(Expr);
|
||||
op = state.FunctionState.PushVar(op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (op.Type != BytecodeOperandType.Variable)
|
||||
{
|
||||
// todo, not sure how to do this for now
|
||||
throw new InvalidOperationException().Attach(Expr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// offset inside the pack
|
||||
Int32 offset = type
|
||||
.Attribs
|
||||
.Select((a, i) => (a, i))
|
||||
.First(_ => _.a.name == this.Name)
|
||||
.i;
|
||||
|
||||
if (!type.IsStruct)
|
||||
{
|
||||
// this is a union, so:
|
||||
// op is a byte array
|
||||
// get the pointer to that then do ptr-to-ptr cast
|
||||
if (!isDeref)
|
||||
{
|
||||
op = Operand.Create(op.Variable, 0);
|
||||
last = state.CurrInsns.LastOrDefault();
|
||||
switch (last?.OpCode.Code)
|
||||
{
|
||||
case Code.PushVar:
|
||||
var pv = LocalVariable.Create(state.FunctionState.LocalsCount - 1);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, pv, op));
|
||||
op = Operand.Create(pv);
|
||||
break;
|
||||
case Code.SetPtr:
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, last.Operands[0], op));
|
||||
op = last.Operands[0];
|
||||
break;
|
||||
default:
|
||||
if (!CallerNeedsToCleanStack(state, retLoc != null, true)) throw new InvalidOperationException().Attach(Expr);
|
||||
op = state.FunctionState.PushVar(op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// use pointerinitialiser as we need to have setptr as last insn
|
||||
state.CGenPushStackSize();
|
||||
// for a pointer, always cast to u32*
|
||||
var ptr = Type as PointerType;
|
||||
var dummyForType = state.FunctionState.PushType(ptr != null ? state.TypeU32 : state.EmitType(Type));
|
||||
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
|
||||
if (!isDeref)
|
||||
{
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.PushVar(op);
|
||||
state.FunctionState.PushVar(dummyU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.CGenPopStackSize();
|
||||
} else
|
||||
{
|
||||
// deref union, already byref u32
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, dummyU32, op));
|
||||
}
|
||||
state.FunctionState.PushVar(Operand.Create(state.PointerInitialiser));
|
||||
state.FunctionState.Push(dummyU32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CGenPopStackSize();
|
||||
var ptrInit = Operand.Create(state.PointerInitialiser, 0);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, op, ptrInit));
|
||||
return op;
|
||||
}
|
||||
|
||||
// not a union. any element that happens to be a pointer is already of type u32 anyway
|
||||
return Operand.Create(op.Variable, (uint)offset);
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
if (Expr.CallerNeedsToCleanStack(state, retLocKnown, forAddress)) return true;
|
||||
|
||||
// If Expr is also an Attribute then caller will need to clean stack
|
||||
if (Expr is Attribute) return true;
|
||||
// Probably same for ArrayIndexDeref
|
||||
if (Expr is ArrayIndexDeref) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Reference {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
// todo: should we pushvar?
|
||||
return Expr.CGenAddress(state, retLoc);
|
||||
}
|
||||
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of a pointer value.").Attach(Expr);
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
return Expr.CallerNeedsToCleanStack(state, retLocKnown, true);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Dereference {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
// generally we do not need to do anything special to deref
|
||||
// however, if our expr is getting a pointer element of a union, we have ptr to ptr and need to deref that
|
||||
var op = Expr.CGenValue(state, retLoc);
|
||||
var attr = Expr as Attribute;
|
||||
if (attr == null) return op;
|
||||
bool isUnion = ((StructOrUnionType)Expr.Type)?.IsStruct == false;
|
||||
if (!isUnion) return op;
|
||||
var ptr = (PointerType)Expr.Type;
|
||||
// we have ref u32
|
||||
if (!ptr.IsRef) return op;
|
||||
// do u32-to-pointer cast
|
||||
state.CGenPushStackSize();
|
||||
var dummyForType = state.FunctionState.PushType(state.EmitType(ptr.RefType));
|
||||
state.FunctionState.PushVar(Operand.Create(state.PointerInitialiser));
|
||||
var u32 = state.FunctionState.PushType(state.TypeU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, u32, op));
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CGenPopStackSize();
|
||||
// op is on top of the stack. pop it and push PointerInitialiser
|
||||
state.FunctionState.Pop();
|
||||
op = state.FunctionState.Push(Operand.Create(state.PointerInitialiser));
|
||||
return Operand.Create(op.Variable, 0);
|
||||
}
|
||||
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
return Expr.CGenValue(state, retLoc);
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
if (Expr.CallerNeedsToCleanStack(state, retLocKnown)) return true;
|
||||
|
||||
var attr = Expr as Attribute;
|
||||
if (attr == null) return false;
|
||||
bool isUnion = ((StructOrUnionType)Expr.Type)?.IsStruct == false;
|
||||
if (!isUnion) return false;
|
||||
var ptr = (PointerType)Expr.Type;
|
||||
// we have ref u32
|
||||
if (!ptr.IsRef) return false;
|
||||
|
||||
return true; // expects op to be at top of stack in this case
|
||||
}
|
||||
}
|
||||
}
|
332
LibIFPSCC/CGen/Statements.cs
Normal file
@ -0,0 +1,332 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CodeGeneration;
|
||||
using IFPSLib.Emit;
|
||||
|
||||
namespace ABT {
|
||||
public abstract partial class Stmt {
|
||||
public abstract void CGenStmt(Env env, CGenState state);
|
||||
|
||||
public void Pop(CGenState state)
|
||||
{
|
||||
state.CGenPopStackSize();
|
||||
}
|
||||
|
||||
public Operand CGenExprStmt(Env env, Expr expr, CGenState state, bool cleanUp = false) {
|
||||
state.CGenPushStackSize();
|
||||
var ret = expr.CGenValue(state, null);
|
||||
if (cleanUp) Pop(state);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class GotoStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
state.JumpTo(state.GotoLabel(this.Label), false);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class LabeledStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
var label = state.GotoLabel(this.Label);
|
||||
state.CGenLabel(label);
|
||||
this.Stmt.CGenStmt(env, state);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ContStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
state.JumpTo(state.ContinueLabel, false);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class BreakStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
state.JumpTo(state.BreakLabel, state.IsInSwitch);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ExprStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
if (this.ExprOpt.IsSome) {
|
||||
Int32 stack_size = state.StackSize;
|
||||
this.ExprOpt.Value.CGenValue(state, null);
|
||||
state.CGenForceStackSizeTo(stack_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class CompoundStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
state.CGenPushStackSize();
|
||||
foreach (Tuple<Env, Decln> decln in this.Declns) {
|
||||
decln.Item2.CGenDecln(decln.Item1, state);
|
||||
}
|
||||
foreach (Tuple<Env, Stmt> stmt in this.Stmts) {
|
||||
stmt.Item2.CGenStmt(stmt.Item1, state);
|
||||
}
|
||||
state.CGenPopStackSize();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ReturnStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
// remove all pop instructions leading up to here.
|
||||
// about to return, runtime cleans up the stack itself on ret
|
||||
while (state.CurrInsns.Count > 0 && state.CurrInsns.Last().OpCode.Code == Code.Pop) state.CurrInsns.RemoveAt(state.CurrInsns.Count - 1);
|
||||
|
||||
if (this.ExprOpt.IsSome) {
|
||||
var retval = state.FunctionState.Function.CreateReturnVariable();
|
||||
// Is this function supposed to return a pointer?
|
||||
var ptr = ExprOpt.Value.Type as PointerType;
|
||||
var needsPtrCast = ptr != null && ptr.IsRef;
|
||||
// Evaluate the Value.
|
||||
var retOp = Operand.Create(retval);
|
||||
var val = ExprOpt.Value.CGenValue(state, needsPtrCast ? null : retOp);
|
||||
// Remove all ending pops to save space. ret instruction cleans up stack itself
|
||||
while (state.CurrInsns.LastOrDefault()?.OpCode.Code == Code.Pop) state.CurrInsns.RemoveAt(state.CurrInsns.Count - 1);
|
||||
// Is this function supposed to return a pointer?
|
||||
if (needsPtrCast)
|
||||
{
|
||||
// Ptr-to-ptr cast into retval.
|
||||
// Don't bother cleaning up the stack at all, we are about to return.
|
||||
var dummyForType = state.FunctionState.PushType(state.EmitType(ptr.RefType));
|
||||
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
|
||||
state.FunctionState.PushVar(val);
|
||||
state.FunctionState.PushVar(dummyU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.FunctionState.PushVar(Operand.Create(retval));
|
||||
state.FunctionState.Push(dummyU32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ret));
|
||||
return;
|
||||
}
|
||||
if (val != retOp) state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, retOp, val));
|
||||
}
|
||||
// Return, runtime will fix up stack itself.
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Ret));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class WhileStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
Int32 start_label = state.RequestLabel();
|
||||
Int32 finish_label = state.RequestLabel();
|
||||
|
||||
// start:
|
||||
state.CGenLabel(start_label);
|
||||
|
||||
// jz finish, Cond
|
||||
|
||||
var ret = CGenExprStmt(env, this.Cond, state);
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagZ, ret));
|
||||
Pop(state);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[finish_label]));
|
||||
|
||||
// Body
|
||||
state.InLoop(start_label, finish_label);
|
||||
this.Body.CGenStmt(env, state);
|
||||
state.OutLabels();
|
||||
|
||||
// jmp start
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[start_label]));
|
||||
|
||||
// finish:
|
||||
state.CGenLabel(finish_label);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class DoWhileStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
Int32 start_label = state.RequestLabel();
|
||||
Int32 finish_label = state.RequestLabel();
|
||||
Int32 continue_label = state.RequestLabel();
|
||||
|
||||
// start:
|
||||
state.CGenLabel(start_label);
|
||||
|
||||
// Body
|
||||
state.InLoop(continue_label, finish_label);
|
||||
this.Body.CGenStmt(env, state);
|
||||
state.OutLabels();
|
||||
|
||||
state.CGenLabel(continue_label);
|
||||
|
||||
// test Cond
|
||||
var ret = CGenExprStmt(env, this.Cond, state);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagNZ, ret));
|
||||
Pop(state);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[start_label]));
|
||||
|
||||
state.CGenLabel(finish_label);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class ForStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
// Init
|
||||
this.Init.Map(_ => CGenExprStmt(env, _, state, true));
|
||||
|
||||
Int32 start_label = state.RequestLabel();
|
||||
Int32 finish_label = state.RequestLabel();
|
||||
Int32 continue_label = state.RequestLabel();
|
||||
|
||||
// start:
|
||||
state.CGenLabel(start_label);
|
||||
|
||||
// test cond
|
||||
this.Cond.Map(_ => {
|
||||
var ret = CGenExprStmt(env, _, state);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagZ, ret));
|
||||
Pop(state);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[finish_label]));
|
||||
return ret;
|
||||
});
|
||||
|
||||
// Body
|
||||
state.InLoop(continue_label, finish_label);
|
||||
this.Body.CGenStmt(env, state);
|
||||
state.OutLabels();
|
||||
|
||||
// continue:
|
||||
state.CGenLabel(continue_label);
|
||||
|
||||
// Loop
|
||||
this.Loop.Map(_ => CGenExprStmt(env, _, state, true));
|
||||
|
||||
// jmp start
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[start_label]));
|
||||
|
||||
// finish:
|
||||
state.CGenLabel(finish_label);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class SwitchStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
|
||||
// Inside a switch statement, the initializations are ignored,
|
||||
// but the stack size should be changed.
|
||||
List<Tuple<Env, Decln>> declns;
|
||||
List<Tuple<Env, Stmt>> stmts;
|
||||
|
||||
var compoundStmt = this.Stmt as CompoundStmt;
|
||||
if (compoundStmt == null) {
|
||||
throw new NotImplementedException().Attach(Expr);
|
||||
}
|
||||
|
||||
declns = compoundStmt.Declns;
|
||||
stmts = compoundStmt.Stmts;
|
||||
|
||||
// Track all case values.
|
||||
IReadOnlyList<Int32> values = CaseLabelsGrabber.GrabLabels(this);
|
||||
|
||||
// Make sure there are no duplicates.
|
||||
if (values.Distinct().Count() != values.Count) {
|
||||
throw new InvalidOperationException("case labels not unique.").Attach(Expr);
|
||||
}
|
||||
// Request labels for these values.
|
||||
Dictionary<Int32, Int32> value_to_label = values.ToDictionary(value => value, value => state.RequestLabel());
|
||||
|
||||
Int32 label_finish = state.RequestLabel();
|
||||
|
||||
Int32 num_default_stmts = stmts.Count(_ => _.Item2 is DefaultStmt);
|
||||
if (num_default_stmts > 1) {
|
||||
throw new InvalidOperationException("duplicate defaults.").Attach(Expr);
|
||||
}
|
||||
Int32 label_default =
|
||||
num_default_stmts == 1 ?
|
||||
state.RequestLabel() :
|
||||
label_finish;
|
||||
|
||||
//Int32 saved_stack_size = state.StackSize;
|
||||
|
||||
// 1. Evaluate Expr.
|
||||
var op = CGenExprStmt(env, this.Expr, state);
|
||||
|
||||
// 2. Expand stack.
|
||||
//state.CGenForceStackSizeTo(stack_size);
|
||||
|
||||
// 3. Make the Jump list.
|
||||
var cmp = state.FunctionState.PushType(state.TypeU32);
|
||||
foreach (KeyValuePair<Int32, Int32> value_label_pair in value_to_label)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Eq, cmp, op, Operand.Create(value_label_pair.Key)));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpNZ, state.FunctionState.Labels[value_label_pair.Value], cmp));
|
||||
}
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[label_default]));
|
||||
|
||||
// 4. List all the statements.
|
||||
state.InSwitch(label_finish, label_default, value_to_label);
|
||||
foreach (Tuple<Env, Stmt> env_stmt_pair in stmts) {
|
||||
env_stmt_pair.Item2.CGenStmt(env_stmt_pair.Item1, state);
|
||||
}
|
||||
state.OutLabels();
|
||||
|
||||
// 5. finish:
|
||||
state.CGenLabel(label_finish);
|
||||
|
||||
// 6. Restore stack size.
|
||||
Pop(state);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class CaseStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
Int32 label = state.CaseLabel(this.Value);
|
||||
state.CGenLabel(label);
|
||||
this.Stmt.CGenStmt(env, state);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class DefaultStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
Int32 label = state.DefaultLabel;
|
||||
state.CGenLabel(label);
|
||||
this.Stmt.CGenStmt(env, state);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class IfStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
var ret = CGenExprStmt(env, this.Cond, state);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagZ, ret));
|
||||
Pop(state);
|
||||
|
||||
Int32 finish_label = state.RequestLabel();
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[finish_label]));
|
||||
|
||||
this.Stmt.CGenStmt(env, state);
|
||||
|
||||
state.CGenLabel(finish_label);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class IfElseStmt {
|
||||
public override void CGenStmt(Env env, CGenState state) {
|
||||
var ret = CGenExprStmt(env, this.Cond, state);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagZ, ret));
|
||||
Pop(state);
|
||||
|
||||
Int32 false_label = state.RequestLabel();
|
||||
Int32 finish_label = state.RequestLabel();
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[false_label]));
|
||||
|
||||
this.TrueStmt.CGenStmt(env, state);
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[finish_label]));
|
||||
|
||||
state.CGenLabel(false_label);
|
||||
|
||||
this.FalseStmt.CGenStmt(env, state);
|
||||
|
||||
state.CGenLabel(finish_label);
|
||||
}
|
||||
}
|
||||
}
|
155
LibIFPSCC/CGen/TypeCast.cs
Normal file
@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using CodeGeneration;
|
||||
using IFPSLib.Emit;
|
||||
|
||||
namespace ABT {
|
||||
public sealed partial class TypeCast {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
if (this.Kind == TypeCastType.FUNC_TO_PTR)
|
||||
{
|
||||
switch (this.Expr)
|
||||
{
|
||||
case Variable func:
|
||||
return new Operand(IFPSLib.TypedData.Create(state.EmitType(this.Type) as IFPSLib.Types.FunctionPointerType, state.GetFunction(func.Name)));
|
||||
default:
|
||||
throw new InvalidProgramException().Attach(Expr);
|
||||
}
|
||||
}
|
||||
if (this.Kind == TypeCastType.NOP) return Expr.CGenValue(state, retLoc);
|
||||
|
||||
var ret = this.Expr.CGenValue(state, null);
|
||||
Operand op = null;
|
||||
PointerType ptrType = null;
|
||||
Operand dummyForType = null;
|
||||
switch (this.Kind) {
|
||||
case TypeCastType.ARRAY_TO_PTR:
|
||||
// easy, pushvar
|
||||
return state.FunctionState.PushVar(ret);
|
||||
|
||||
case TypeCastType.PTR_TO_INT32:
|
||||
op = state.FunctionState.PushType(state.EmitType(Type));
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.PushVar(ret);
|
||||
state.FunctionState.PushVar(op);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.CGenPopStackSize();
|
||||
return op;
|
||||
|
||||
case TypeCastType.INT32_TO_PTR:
|
||||
op = state.FunctionState.PushType(state.TypeArrayOfPointer);
|
||||
ptrType = Type as PointerType;
|
||||
if (ptrType == null) throw new InvalidProgramException().Attach(Expr);
|
||||
state.CGenPushStackSize();
|
||||
dummyForType = state.FunctionState.PushType(state.EmitType(ptrType.RefType));
|
||||
state.FunctionState.PushVar(op);
|
||||
state.FunctionState.Push(ret);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CGenPopStackSize();
|
||||
return Operand.Create(op.Variable, 0);
|
||||
|
||||
case TypeCastType.PTR_TO_PTR:
|
||||
// same as PTR_TO_INT32 then INT32_TO_PTR
|
||||
ptrType = Type as PointerType;
|
||||
if (ptrType == null) throw new InvalidProgramException().Attach(Expr);
|
||||
op = state.FunctionState.PushType(state.TypeArrayOfPointer);
|
||||
state.CGenPushStackSize();
|
||||
dummyForType = state.FunctionState.PushType(state.EmitType(ptrType.RefType));
|
||||
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.PushVar(ret);
|
||||
state.FunctionState.PushVar(dummyU32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.CGenPopStackSize();
|
||||
state.FunctionState.PushVar(op);
|
||||
state.FunctionState.Push(dummyU32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CGenPopStackSize();
|
||||
return Operand.Create(op.Variable, 0);
|
||||
|
||||
case TypeCastType.VARIANT_TO_COM_INTERFACE:
|
||||
// this might be interface and might be dispatch.
|
||||
op = retLoc != null ? retLoc : state.FunctionState.PushType(state.EmitType(Type));
|
||||
state.CGenPushStackSize();
|
||||
// u32 type = VarType(variant)
|
||||
var varType = state.FunctionState.PushType(state.TypeU16);
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.Push(ret);
|
||||
state.FunctionState.PushVar(varType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.VarType));
|
||||
state.CGenPopStackSize();
|
||||
// if (varType != vtDispatch) { regular com interface cast }
|
||||
// else { cast to dispatch first }
|
||||
// cast is same for both, just need to provide the "correct" type
|
||||
const ushort VT_DISPATCH = 9;
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Sub, varType, Operand.Create(VT_DISPATCH)));
|
||||
// regular com interface cast
|
||||
state.CGenPushStackSize();
|
||||
// typeNo
|
||||
state.FunctionState.PushType(state.TypeU32);
|
||||
// self
|
||||
|
||||
var comInterface = state.FunctionState.AddLocal();
|
||||
|
||||
var insnDispatch = Instruction.Create(OpCodes.PushType, state.TypeIDispatch);
|
||||
var insnEnd = Instruction.Create(OpCodes.Assign, comInterface, ret);
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpZ, insnDispatch, varType));
|
||||
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.PushType, state.TypeIUnknown));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, insnEnd));
|
||||
|
||||
state.CurrInsns.Add(insnDispatch);
|
||||
|
||||
state.CurrInsns.Add(insnEnd);
|
||||
|
||||
// retval
|
||||
state.FunctionState.PushVar(op);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.ComInterfaceCast));
|
||||
state.CGenPopStackSize();
|
||||
return op;
|
||||
|
||||
case TypeCastType.COM_INTERFACE:
|
||||
op = retLoc != null ? retLoc : state.FunctionState.PushType(state.EmitType(Type));
|
||||
state.CGenPushStackSize();
|
||||
// typeNo
|
||||
state.FunctionState.PushType(state.TypeU32);
|
||||
// self
|
||||
state.FunctionState.Push(ret);
|
||||
// retval
|
||||
state.FunctionState.PushVar(op);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.ComInterfaceCast));
|
||||
state.CGenPopStackSize();
|
||||
return op;
|
||||
|
||||
|
||||
case TypeCastType.NOP:
|
||||
return ret;
|
||||
|
||||
case TypeCastType.COPY_CONSTRUCTOR:
|
||||
if (retLoc == null) throw new InvalidOperationException("Cannot copy construct to an unknown variable").Attach(Expr);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Cpval, retLoc, ret));
|
||||
return retLoc;
|
||||
|
||||
default:
|
||||
op = retLoc != null ? retLoc : state.FunctionState.PushType(state.EmitType(Type));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, op, ret));
|
||||
return op;
|
||||
}
|
||||
}
|
||||
|
||||
public override Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of a cast expression.").Attach(Expr);
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
if (Kind == TypeCastType.NOP) return Expr.CallerNeedsToCleanStack(state, retLocKnown);
|
||||
if (Expr.CallerNeedsToCleanStack(state, false)) return true;
|
||||
return !retLocKnown;
|
||||
}
|
||||
}
|
||||
}
|
222
LibIFPSCC/CGen/UnaryOperators.cs
Normal file
@ -0,0 +1,222 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using CodeGeneration;
|
||||
using IFPSLib.Emit;
|
||||
|
||||
namespace ABT {
|
||||
public abstract partial class IncDecExpr {
|
||||
|
||||
protected abstract bool IsPost { get; }
|
||||
|
||||
public abstract void EmitPtr(CGenState state, uint sizeOf, Operand op);
|
||||
|
||||
public abstract void Emit(CGenState state, Operand op);
|
||||
|
||||
public override sealed Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
|
||||
// 1. Get an operand that can be used for pushvar.
|
||||
var op = Expr.CGenAddress(state, retLoc);
|
||||
|
||||
var op2 = op;
|
||||
|
||||
if (Expr.Type.Kind == ExprTypeKind.POINTER)
|
||||
{
|
||||
if (IsPost)
|
||||
{
|
||||
op2 = state.FunctionState.Push(op);
|
||||
}
|
||||
var ptrType = Type as PointerType;
|
||||
if (ptrType == null) throw new InvalidProgramException().Attach(Expr);
|
||||
if (ptrType.IsRef) {
|
||||
// This is a pointer.
|
||||
// Convert to u32.
|
||||
state.CGenPushStackSize();
|
||||
var u32 = state.FunctionState.PushType(state.TypeU32);
|
||||
state.CGenPushStackSize();
|
||||
state.FunctionState.PushVar(op);
|
||||
state.FunctionState.PushVar(u32);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
|
||||
state.CGenPopStackSize();
|
||||
// Emit instructions for the pointer.
|
||||
EmitPtr(state, (uint)ptrType.RefType.SizeOf, u32);
|
||||
// Convert back to pointer.
|
||||
var arr = state.FunctionState.PushType(state.TypeArrayOfPointer);
|
||||
state.CGenPushStackSize();
|
||||
var dummyForType = state.FunctionState.PushType(state.EmitType(ptrType.RefType));
|
||||
state.FunctionState.PushVar(op);
|
||||
state.FunctionState.Push(u32);
|
||||
state.FunctionState.PushVar(dummyForType);
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
|
||||
state.CGenPopStackSize();
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetPtr, op, Operand.Create(arr.Variable, 0)));
|
||||
state.CGenPopStackSize();
|
||||
return op2;
|
||||
}
|
||||
EmitPtr(state, ExprType.SIZEOF_CHAR, op);
|
||||
return op2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsPost)
|
||||
{
|
||||
var last = state.CurrInsns.LastOrDefault();
|
||||
var attr = Expr as Attribute;
|
||||
if (Expr is Dereference || (attr != null && !((StructOrUnionType)attr.Expr.Type).IsStruct) )
|
||||
{
|
||||
// it's a deref, this is one of the edge cases where we have to do the deref ourselves
|
||||
op2 = state.FunctionState.PushType(state.EmitType(Expr.Type));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, op2, op));
|
||||
}
|
||||
else
|
||||
{
|
||||
op2 = state.FunctionState.Push(op);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Emit the instruction(s).
|
||||
Emit(state, op);
|
||||
|
||||
// Done!
|
||||
return op2;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override sealed Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of an increment/decrement expression.").Attach(Expr);
|
||||
}
|
||||
|
||||
public override sealed bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
|
||||
{
|
||||
return Expr.CallerNeedsToCleanStack(state, retLocKnown, true) || IsPost;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class PostIncrement {
|
||||
protected override bool IsPost => true;
|
||||
public override void EmitPtr(CGenState state, uint sizeOf, Operand op)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Add, op, Operand.Create(sizeOf)));
|
||||
}
|
||||
public override void Emit(CGenState state, Operand op)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Inc, op));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class PostDecrement
|
||||
{
|
||||
protected override bool IsPost => true;
|
||||
public override void EmitPtr(CGenState state, uint sizeOf, Operand op)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Sub, op, Operand.Create(sizeOf)));
|
||||
}
|
||||
public override void Emit(CGenState state, Operand op)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Dec, op));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class PreIncrement
|
||||
{
|
||||
protected override bool IsPost => false;
|
||||
public override void EmitPtr(CGenState state, uint sizeOf, Operand op)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Add, op, Operand.Create(sizeOf)));
|
||||
}
|
||||
|
||||
public override void Emit(CGenState state, Operand op)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Inc, op));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class PreDecrement
|
||||
{
|
||||
protected override bool IsPost => false;
|
||||
public override void EmitPtr(CGenState state, uint sizeOf, Operand op)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Sub, op, Operand.Create(sizeOf)));
|
||||
}
|
||||
|
||||
public override void Emit(CGenState state, Operand op)
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Dec, op));
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class UnaryArithOp {
|
||||
public override sealed Operand CGenAddress(CGenState state, Operand retLoc)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get the address of an unary arithmetic operator.").Attach(Expr);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Negative {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
var ret = Expr.CGenValue(state, retLoc);
|
||||
if (retLoc == null)
|
||||
{
|
||||
ret = state.FunctionState.Push(ret);
|
||||
}
|
||||
else if (!OperandEquator.Equals(retLoc, ret))
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, retLoc, ret));
|
||||
ret = retLoc;
|
||||
}
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Neg, ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false) => !retLocKnown;
|
||||
}
|
||||
|
||||
public sealed partial class BitwiseNot {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
var ret = Expr.CGenValue(state, retLoc);
|
||||
if (retLoc == null)
|
||||
{
|
||||
ret = state.FunctionState.Push(ret);
|
||||
}
|
||||
else if (!OperandEquator.Equals(retLoc, ret))
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, retLoc, ret));
|
||||
ret = retLoc;
|
||||
}
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Not, ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false) => !retLocKnown;
|
||||
}
|
||||
|
||||
public sealed partial class LogicalNot {
|
||||
public override Operand CGenValue(CGenState state, Operand retLoc)
|
||||
{
|
||||
|
||||
var ret = Expr.CGenValue(state, retLoc);
|
||||
if (retLoc == null)
|
||||
{
|
||||
ret = state.FunctionState.Push(ret);
|
||||
}
|
||||
else if (!OperandEquator.Equals(retLoc, ret))
|
||||
{
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, retLoc, ret));
|
||||
ret = retLoc;
|
||||
}
|
||||
// ret = (ret == 0) ; sets to 1 if false
|
||||
// ret = (ret == 0) ; inverts
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetZ, ret));
|
||||
state.CurrInsns.Add(Instruction.Create(OpCodes.SetZ, ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false) => !retLocKnown;
|
||||
}
|
||||
}
|
5
LibIFPSCC/Driver/Checked.cs
Normal file
@ -0,0 +1,5 @@
|
||||
using System;
|
||||
|
||||
public class Checked : Attribute {
|
||||
}
|
||||
|
55
LibIFPSCC/Driver/Compiler.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CodeGeneration;
|
||||
using LexicalAnalysis;
|
||||
using Parsing;
|
||||
|
||||
namespace Driver {
|
||||
public class Compiler {
|
||||
private Compiler(String source) {
|
||||
this.Source = source;
|
||||
|
||||
// Lexical analysis
|
||||
Scanner scanner = new Scanner(source);
|
||||
this.Tokens = scanner.Tokens.ToImmutableList();
|
||||
|
||||
// Parse
|
||||
var parserResult = CParsers.Parse(this.Tokens);
|
||||
if (!parserResult.IsSuccessful || parserResult.Source.Count() != 1) {
|
||||
throw new InvalidOperationException($"Parsing error:\n{parserResult}");
|
||||
}
|
||||
this.SyntaxTree = parserResult.Result;
|
||||
|
||||
// Semantic analysis
|
||||
var semantReturn = this.SyntaxTree.GetTranslnUnit();
|
||||
this.AbstractSyntaxTree = semantReturn.Value;
|
||||
this.Environment = semantReturn.Env;
|
||||
|
||||
// Code generation
|
||||
var state = new CGenState();
|
||||
this.AbstractSyntaxTree.CodeGenerate(state);
|
||||
state.EmitCallsToCtor();
|
||||
this.Script = state.Script;
|
||||
}
|
||||
|
||||
public static Compiler FromSource(String src) {
|
||||
return new Compiler(src);
|
||||
}
|
||||
|
||||
public static Compiler FromFile(String fileName) {
|
||||
if (File.Exists(fileName)) {
|
||||
return new Compiler(File.ReadAllText(fileName));
|
||||
}
|
||||
throw new FileNotFoundException($"{fileName} does not exist!");
|
||||
}
|
||||
|
||||
public readonly String Source;
|
||||
public readonly ImmutableList<Token> Tokens;
|
||||
public readonly AST.TranslnUnit SyntaxTree;
|
||||
public readonly ABT.TranslnUnit AbstractSyntaxTree;
|
||||
public readonly ABT.Env Environment;
|
||||
public readonly IFPSLib.Script Script;
|
||||
}
|
||||
}
|
17
LibIFPSCC/Driver/CovariantTuple.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
public interface ICovariantTuple<out T1, out T2> {
|
||||
T1 Head { get; }
|
||||
T2 Tail { get; }
|
||||
}
|
||||
|
||||
public class CovariantTuple<T1, T2> : Tuple<T1, T2>, ICovariantTuple<T1, T2> {
|
||||
public CovariantTuple(T1 head, T2 tail) : base(head, tail) { }
|
||||
public T1 Head => this.Item1;
|
||||
public T2 Tail => this.Item2;
|
||||
}
|
||||
|
||||
public class CovariantTuple {
|
||||
public static ICovariantTuple<T1, T2> Create<T1, T2>(T1 head, T2 tail) =>
|
||||
new CovariantTuple<T1, T2>(head, tail);
|
||||
}
|
18
LibIFPSCC/Driver/HList.cs
Normal file
@ -0,0 +1,18 @@
|
||||
public static class HList {
|
||||
public static HNil HNil() => hnil;
|
||||
public static HCons<H, T> HCons<H, T>(H head, T tail) where T : HList<T> => new HCons<H, T>(head, tail);
|
||||
private static readonly HNil hnil = new HNil();
|
||||
}
|
||||
|
||||
public class HList<T> { }
|
||||
|
||||
public class HNil : HList<HNil> { }
|
||||
|
||||
public class HCons<H, T> : HList<HCons<H, T>> where T : HList<T> {
|
||||
public HCons(H head, T tail) {
|
||||
this.head = head;
|
||||
this.tail = tail;
|
||||
}
|
||||
public readonly H head;
|
||||
public readonly T tail;
|
||||
}
|
42
LibIFPSCC/Driver/Option.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
|
||||
public abstract class Option<T> {
|
||||
public Option<O> Map<O>(Converter<T, O> converter) {
|
||||
if (this.IsSome) {
|
||||
return new Some<O>(converter(this.Value));
|
||||
}
|
||||
return new None<O>();
|
||||
}
|
||||
|
||||
public abstract T Value { get; }
|
||||
public abstract Boolean IsSome { get; }
|
||||
public abstract Boolean IsNone { get; }
|
||||
|
||||
public static Option<T> None { get; } = new None<T>();
|
||||
}
|
||||
|
||||
public static class Option {
|
||||
public static Option<T> Some<T>(T value) => new Some<T>(value);
|
||||
}
|
||||
|
||||
public sealed class None<T> : Option<T> {
|
||||
public override T Value {
|
||||
get {
|
||||
throw new NotSupportedException("No Value in None.");
|
||||
}
|
||||
}
|
||||
public override Boolean IsSome => false;
|
||||
public override Boolean IsNone => true;
|
||||
}
|
||||
|
||||
public sealed class Some<T> : Option<T> {
|
||||
public Some(T value) {
|
||||
if (value == null) {
|
||||
throw new ArgumentNullException(nameof(value), "The Value in Some cannot be null.");
|
||||
}
|
||||
this.Value = value;
|
||||
}
|
||||
public override T Value { get; }
|
||||
public override Boolean IsSome => true;
|
||||
public override Boolean IsNone => false;
|
||||
}
|
29
LibIFPSCC/Driver/SetOnce.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
public class SetOnce<T> {
|
||||
public SetOnce() {
|
||||
this.IsSet = false;
|
||||
this._value = default(T);
|
||||
}
|
||||
|
||||
public Boolean IsSet { get; private set; }
|
||||
private T _value;
|
||||
|
||||
public T Value {
|
||||
get {
|
||||
if (!this.IsSet) {
|
||||
throw new InvalidOperationException("Value hasn't been set.");
|
||||
}
|
||||
return this._value;
|
||||
}
|
||||
set {
|
||||
if (this.IsSet) {
|
||||
throw new InvalidOperationException("Value cannot be set twice.");
|
||||
}
|
||||
this._value = value;
|
||||
this.IsSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
57
LibIFPSCC/ILineInfo.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Dynamic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
public interface ILineInfo
|
||||
{
|
||||
int Line { get; }
|
||||
int Column { get; }
|
||||
|
||||
}
|
||||
|
||||
public interface IStoredLineInfo : ILineInfo
|
||||
{
|
||||
void Copy(ILineInfo lineInfo);
|
||||
}
|
||||
|
||||
public class ExceptionWithLineInfo : Exception
|
||||
{
|
||||
private readonly Exception exception;
|
||||
private readonly ILineInfo lineInfo;
|
||||
|
||||
public override string Message => string.Format("{0} [line {1}, column {2}]", exception.Message, lineInfo.Line, lineInfo.Column);
|
||||
|
||||
public override IDictionary Data => exception.Data;
|
||||
public override Exception GetBaseException()
|
||||
{
|
||||
return exception;
|
||||
}
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
exception.GetObjectData(info, context);
|
||||
}
|
||||
|
||||
public override string HelpLink { get => exception.HelpLink; set => exception.HelpLink = value; }
|
||||
|
||||
public override string Source { get => exception.Source; set => exception.Source = value; }
|
||||
|
||||
public override string StackTrace => exception.StackTrace;
|
||||
|
||||
public Exception BaseException => exception;
|
||||
|
||||
public ExceptionWithLineInfo(Exception exception, ILineInfo lineInfo)
|
||||
{
|
||||
this.exception = exception;
|
||||
this.lineInfo = lineInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExceptionWithLineInfoExtensions
|
||||
{
|
||||
public static ExceptionWithLineInfo Attach(this Exception exception, ILineInfo lineInfo)
|
||||
{
|
||||
return new ExceptionWithLineInfo(exception, lineInfo);
|
||||
}
|
||||
}
|
15
LibIFPSCC/LibIFPSCC.csproj
Normal file
@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Collections.Immutable" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IFPSLib\IFPSLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
117
LibIFPSCC/Parser/CParsers.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LexicalAnalysis;
|
||||
using AST;
|
||||
using static Parsing.ParserCombinator;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Parsing {
|
||||
public partial class CParsers {
|
||||
static CParsers() {
|
||||
SetExpressionRules();
|
||||
SetDeclarationRules();
|
||||
SetExternalDefinitionRules();
|
||||
SetStatementRules();
|
||||
}
|
||||
|
||||
public static IParserResult<TranslnUnit> Parse(IEnumerable<Token> tokens) =>
|
||||
TranslationUnit.Parse(new ParserInput(new ParserEnvironment(), tokens));
|
||||
|
||||
public class ConstCharParser : IParser<Expr> {
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParserResult<Expr> Parse(ParserInput input) {
|
||||
var token = input.Source.First() as TokenCharConst;
|
||||
if (token == null) {
|
||||
return new ParserFailed<Expr>(input);
|
||||
}
|
||||
return ParserSucceeded.Create(new IntLiteral(token.Value, TokenInt.IntSuffix.NONE), input.Environment, input.Source.Skip(1));
|
||||
}
|
||||
}
|
||||
|
||||
public class ConstIntParser : IParser<Expr> {
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParserResult<Expr> Parse(ParserInput input) {
|
||||
var token = input.Source.First() as TokenInt;
|
||||
if (token == null) {
|
||||
return new ParserFailed<Expr>(input);
|
||||
}
|
||||
return ParserSucceeded.Create(new IntLiteral(token.Val, token.Suffix), input.Environment, input.Source.Skip(1));
|
||||
}
|
||||
}
|
||||
|
||||
public class ConstFloatParser : IParser<Expr> {
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParserResult<Expr> Parse(ParserInput input) {
|
||||
var token = input.Source.First() as TokenFloat;
|
||||
if (token == null) {
|
||||
return new ParserFailed<Expr>(input);
|
||||
}
|
||||
return ParserSucceeded.Create(new FloatLiteral(token.Value, token.Suffix), input.Environment, input.Source.Skip(1));
|
||||
}
|
||||
}
|
||||
|
||||
public class StringLiteralParser : IParser<Expr> {
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParserResult<Expr> Parse(ParserInput input) {
|
||||
var token = input.Source.First() as TokenString;
|
||||
if (token == null) {
|
||||
return new ParserFailed<Expr>(input);
|
||||
}
|
||||
return ParserSucceeded.Create(new StringLiteral(token.Val), input.Environment, input.Source.Skip(1));
|
||||
}
|
||||
}
|
||||
|
||||
public class UnicodeStringLiteralParser : IParser<Expr>
|
||||
{
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParserResult<Expr> Parse(ParserInput input)
|
||||
{
|
||||
var token = input.Source.First() as TokenUnicodeString;
|
||||
if (token == null)
|
||||
{
|
||||
return new ParserFailed<Expr>(input);
|
||||
}
|
||||
return ParserSucceeded.Create(new UnicodeStringLiteral(token.Val), input.Environment, input.Source.Skip(1));
|
||||
}
|
||||
}
|
||||
|
||||
public class BinaryOperatorBuilder {
|
||||
public BinaryOperatorBuilder(IConsumer operatorConsumer, Func<Expr, Expr, Expr> nodeCreator) {
|
||||
this.OperatorConsumer = operatorConsumer;
|
||||
this.NodeCreator = nodeCreator;
|
||||
}
|
||||
|
||||
public static BinaryOperatorBuilder Create(IConsumer operatorConsumer, Func<Expr, Expr, Expr> nodeCreator) =>
|
||||
new BinaryOperatorBuilder(operatorConsumer, nodeCreator);
|
||||
|
||||
public IConsumer OperatorConsumer { get; }
|
||||
public Func<Expr, Expr, Expr> NodeCreator { get; }
|
||||
}
|
||||
|
||||
// TODO: create a dedicated class for this.
|
||||
public static IParser<Expr> BinaryOperator(IParser<Expr> operandParser, params BinaryOperatorBuilder[] builders) {
|
||||
ImmutableList<ITransformer<Expr, Expr>> transformers = builders.Select(builder =>
|
||||
Given<Expr>()
|
||||
.Then(builder.OperatorConsumer)
|
||||
.Then(operandParser)
|
||||
.Then(builder.NodeCreator)
|
||||
).ToImmutableList();
|
||||
return operandParser.Then((new OrTransformer<Expr, Expr>(transformers)).ZeroOrMore());
|
||||
}
|
||||
|
||||
public static IParser<Expr> AssignmentOperator(
|
||||
IParser<Expr> lhsParser,
|
||||
IParser<Expr> rhsParser,
|
||||
params BinaryOperatorBuilder[] builders
|
||||
) {
|
||||
var transformers = builders.Select(builder =>
|
||||
Given<Expr>()
|
||||
.Then(builder.OperatorConsumer)
|
||||
.Then(rhsParser)
|
||||
.Then(builder.NodeCreator)
|
||||
).ToImmutableList();
|
||||
return lhsParser.Then((new OrTransformer<Expr, Expr>(transformers)).OneOrMore());
|
||||
}
|
||||
}
|
||||
}
|
838
LibIFPSCC/Parser/Declaration.cs
Normal file
@ -0,0 +1,838 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using AST;
|
||||
using static Parsing.ParserCombinator;
|
||||
|
||||
namespace Parsing {
|
||||
public partial class CParsers {
|
||||
|
||||
/// <summary>
|
||||
/// declaration
|
||||
/// : declaration-specifiers [Init-declarator-list]? ';'
|
||||
/// </summary>
|
||||
public static NamedParser<Decln>
|
||||
Declaration { get; } = new NamedParser<Decln>("declaration");
|
||||
|
||||
/// <summary>
|
||||
/// declaration-specifiers
|
||||
/// : [ storage-class-specifier | Type-specifier | Type-qualifier ]+
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 1. You can only have **one** storage class specifier.
|
||||
/// 2. You can have duplicate Type qualifiers, since it doesn't cause ambiguity.
|
||||
/// </remarks>
|
||||
public static NamedParser<DeclnSpecs>
|
||||
DeclarationSpecifiers { get; } = new NamedParser<DeclnSpecs>("declaration-specifiers");
|
||||
|
||||
/// <summary>
|
||||
/// Init-declarator-list
|
||||
/// : Init-declarator [ ',' Init-declarator ]*
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// a non-empty list of init_declarators separated by ','
|
||||
/// </remarks>
|
||||
public static NamedParser<ImmutableList<InitDeclr>>
|
||||
InitDeclaratorList { get; } = new NamedParser<ImmutableList<InitDeclr>>("Init-declarator-list");
|
||||
|
||||
/// <summary>
|
||||
/// Init-declarator
|
||||
/// : declarator [ '=' initializer ]?
|
||||
/// </summary>
|
||||
public static NamedParser<InitDeclr>
|
||||
InitDeclarator { get; } = new NamedParser<InitDeclr>("Init-declarator");
|
||||
|
||||
/// <summary>
|
||||
/// storage-class-specifier
|
||||
/// : auto | register | static | extern | typedef
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There can only be *one* storage class specifier in one declaration.
|
||||
/// </remarks>
|
||||
public static NamedParser<StorageClsSpec>
|
||||
StorageClassSpecifier { get; } = new NamedParser<StorageClsSpec>("storage-class-specifier");
|
||||
|
||||
/// <summary>
|
||||
/// Type-specifier
|
||||
/// : void
|
||||
/// | char
|
||||
/// | short
|
||||
/// | int
|
||||
/// | long
|
||||
/// | float
|
||||
/// | double
|
||||
/// | signed
|
||||
/// | unsigned
|
||||
/// | struct-or-union-specifier
|
||||
/// | enum-specifier
|
||||
/// | typedef-name
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 1. void, char, short, int, long, float, double, signed, unsigned are called "basic Type specifiers".
|
||||
/// 2. struct-or-union_specifier and enum-specifier need more complicated parsing.
|
||||
/// 3. Parsing typedef-name actually requires the environment to participate. For example, consider this statement:
|
||||
/// T *v;
|
||||
/// Is T a Type or an object? If T is a Type, then this statement is a declaration: v is a pointer; if T is a object, then this statement is an expression.
|
||||
/// So, we need to keep track of the typedefs in the environment even in the parsing stage!
|
||||
/// </remarks>
|
||||
public static NamedParser<TypeSpec>
|
||||
TypeSpecifier { get; } = new NamedParser<TypeSpec>("Type-specifier");
|
||||
|
||||
/// <summary>
|
||||
/// Type-qualifier
|
||||
/// : const
|
||||
/// | volatile
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that there can be multiple Type qualifiers in one declarations.
|
||||
/// </remarks>
|
||||
public static NamedParser<TypeQual>
|
||||
TypeQualifier { get; } = new NamedParser<TypeQual>("Type-qualifier");
|
||||
|
||||
/// <summary>
|
||||
/// declarator
|
||||
/// : [pointer]? direct-declarator
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A declarator gives a name to the object and also modifies the Type.
|
||||
/// </remarks>
|
||||
public static NamedParser<Declr>
|
||||
Declarator { get; } = new NamedParser<Declr>("declarator");
|
||||
|
||||
/// <summary>
|
||||
/// pointer
|
||||
/// : [ '*' [Type-qualifier-list]? ]+
|
||||
/// </summary>
|
||||
public static NamedParser<ImmutableList<PointerModifier>>
|
||||
Pointer { get; } = new NamedParser<ImmutableList<PointerModifier>>("pointer");
|
||||
|
||||
/// <summary>
|
||||
/// parameter-Type-list
|
||||
/// : parameter-list [ ',' '...' ]?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A parameter list and an optional vararg signature.
|
||||
/// Used in function declarations.
|
||||
/// </remarks>
|
||||
public static NamedParser<ParamTypeList>
|
||||
ParameterTypeList { get; } = new NamedParser<ParamTypeList>("parameter-Type-list");
|
||||
|
||||
/// <summary>
|
||||
/// parameter-list
|
||||
/// : parameter-declaration [ ',' parameter-declaration ]*
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A non-empty list of parameters separated by ','.
|
||||
/// Used in a function signature.
|
||||
/// </remarks>
|
||||
public static NamedParser<ImmutableList<ParamDecln>>
|
||||
ParameterList { get; } = new NamedParser<ImmutableList<ParamDecln>>("parameter-list");
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Type-qualifier-list
|
||||
/// : [Type-qualifier]+
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A non-empty list of Type qualifiers.
|
||||
/// </remarks>
|
||||
public static NamedParser<ImmutableList<TypeQual>>
|
||||
TypeQualifierList { get; } = new NamedParser<ImmutableList<TypeQual>>("Type-qualifier-list");
|
||||
|
||||
/// <summary>
|
||||
/// direct-declarator
|
||||
/// : [
|
||||
/// identifier | '(' declarator ')'
|
||||
/// ] [
|
||||
/// '[' [constant-expression]? ']'
|
||||
/// | '(' [parameter-Type-list]? ')'
|
||||
/// ]*
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There is an old style of function definition:
|
||||
/// +-------------------------------+
|
||||
/// | int foo(param1, param2) |
|
||||
/// | int param1; |
|
||||
/// | char param2; |
|
||||
/// | { |
|
||||
/// | .... |
|
||||
/// | } |
|
||||
/// +-------------------------------+
|
||||
///
|
||||
/// I'm not gonna support this style, and function definitions should always be like this:
|
||||
/// +------------------------------------------+
|
||||
/// | int foo(int param1, char param2) { |
|
||||
/// | .... |
|
||||
/// | } |
|
||||
/// +------------------------------------------+
|
||||
/// </remarks>
|
||||
public static NamedParser<Declr>
|
||||
DirectDeclarator { get; } = new NamedParser<Declr>("direct-declarator");
|
||||
|
||||
/// <summary>
|
||||
/// enum-specifier
|
||||
/// : enum [identifier]? '{' enumerator-list '}'
|
||||
/// | enum identifier
|
||||
/// </summary>
|
||||
public static NamedParser<EnumSpec>
|
||||
EnumSpecifier { get; } = new NamedParser<EnumSpec>("enum-specifier");
|
||||
|
||||
/// <summary>
|
||||
/// enumerator-list
|
||||
/// : enumerator [ ',' enumerator ]*
|
||||
/// </summary>
|
||||
public static NamedParser<ImmutableList<Enumr>>
|
||||
EnumeratorList { get; } = new NamedParser<ImmutableList<Enumr>>("enumerator-list");
|
||||
|
||||
/// <summary>
|
||||
/// enumerator
|
||||
/// : enumeration-constant [ '=' constant-expression ]?
|
||||
/// </summary>
|
||||
public static NamedParser<Enumr>
|
||||
Enumerator { get; } = new NamedParser<Enumr>("enumerator");
|
||||
|
||||
/// <summary>
|
||||
/// enumeration-constant
|
||||
/// : identifier
|
||||
/// </summary>
|
||||
public static NamedParser<String>
|
||||
EnumerationConstant { get; } = new NamedParser<string>("enumeration-constant");
|
||||
|
||||
/// <summary>
|
||||
/// struct-or-union-specifier
|
||||
/// : struct-or-union [identifier]? { struct-declaration-list }
|
||||
/// | struct-or-union identifier
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note: if no struct-declaration-list given, the Type is considered incomplete.
|
||||
/// </remarks>
|
||||
public static NamedParser<StructOrUnionSpec>
|
||||
StructOrUnionSpecifier { get; } = new NamedParser<StructOrUnionSpec>("struct-or-union-specifier");
|
||||
|
||||
/// <summary>
|
||||
/// struct-or-union
|
||||
/// : struct | union
|
||||
/// </summary>
|
||||
public static NamedParser<StructOrUnion>
|
||||
StructOrUnion { get; } = new NamedParser<StructOrUnion>("struct-or-union");
|
||||
|
||||
/// <summary>
|
||||
/// struct-declaration-list
|
||||
/// : [struct-declaration]+
|
||||
/// </summary>
|
||||
public static NamedParser<ImmutableList<StructDecln>>
|
||||
StructDeclarationList { get; } = new NamedParser<ImmutableList<StructDecln>>("struct-declaration-list");
|
||||
|
||||
/// <summary>
|
||||
/// struct-declaration
|
||||
/// : specifier-qualifier-list struct-declarator-list ';'
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that a struct declaration does not need a storage class specifier.
|
||||
/// </remarks>
|
||||
public static NamedParser<StructDecln>
|
||||
StructDeclaration { get; } = new NamedParser<StructDecln>("struct-declaration");
|
||||
|
||||
/// <summary>
|
||||
/// specifier-qualifier-list
|
||||
/// : [ Type-specifier | Type-qualifier ]+
|
||||
/// </summary>
|
||||
public static NamedParser<SpecQualList>
|
||||
SpecifierQualifierList { get; } = new NamedParser<SpecQualList>("specifier-qualifier-list");
|
||||
|
||||
/// <summary>
|
||||
/// struct-declarator-list
|
||||
/// : struct-declarator [ ',' struct-declarator ]*
|
||||
/// </summary>
|
||||
public static NamedParser<ImmutableList<StructDeclr>>
|
||||
StructDeclaratorList { get; } = new NamedParser<ImmutableList<StructDeclr>>("struct-declarator-list");
|
||||
|
||||
/// <summary>
|
||||
/// struct-declarator
|
||||
/// : [declarator]? ':' constant-expression
|
||||
/// | declarator
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that the second one represents a 'bit-field', which I'm not going to support.
|
||||
/// </remarks>
|
||||
public static NamedParser<StructDeclr>
|
||||
StructDeclarator { get; } = new NamedParser<StructDeclr>("struct-declarator");
|
||||
|
||||
/// <summary>
|
||||
/// parameter-declaration
|
||||
/// : declaration-specifiers [ declarator | abstract-declarator ]?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// int foo(int arg1, int arg2);
|
||||
/// ~~~~~~~~
|
||||
///
|
||||
/// int foo(int, int);
|
||||
/// ~~~
|
||||
///
|
||||
/// The declarator can be completely omitted.
|
||||
/// </remarks>
|
||||
public static NamedParser<ParamDecln>
|
||||
ParameterDeclaration { get; } = new NamedParser<ParamDecln>("parameter-declaration");
|
||||
|
||||
// identifier_list
|
||||
// : /* old style, i'm deleting this */
|
||||
|
||||
/// <summary>
|
||||
/// abstract-declarator
|
||||
/// : [pointer]? direct-abstract-declarator
|
||||
/// | pointer
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An abstract declarator is a non-empty list of (pointer, function, or array) Type modifiers
|
||||
/// </remarks>
|
||||
public static NamedParser<AbstractDeclr>
|
||||
AbstractDeclarator { get; } = new NamedParser<AbstractDeclr>("abstract-declarator");
|
||||
|
||||
/// <summary>
|
||||
/// direct-abstract-declarator
|
||||
/// : [
|
||||
/// '(' abstract-declarator ')'
|
||||
/// | '[' [constant-expression]? ']' // array modifier
|
||||
/// | '(' [parameter-type_list]? ')' // function modifier
|
||||
/// ] [
|
||||
/// '[' [constant-expression]? ']' // array modifier
|
||||
/// | '(' [parameter-Type-list]? ')' // function modifier
|
||||
/// ]*
|
||||
/// </summary>
|
||||
public static NamedParser<AbstractDeclr>
|
||||
DirectAbstractDeclarator { get; } = new NamedParser<AbstractDeclr>("direct-abstract-declarator");
|
||||
|
||||
/// <summary>
|
||||
/// initializer
|
||||
/// : assignment-expression
|
||||
/// | '{' initializer-list '}'
|
||||
/// | '{' initializer-list ',' '}'
|
||||
/// </summary>
|
||||
public static NamedParser<Initr>
|
||||
Initializer { get; } = new NamedParser<Initr>("initializer");
|
||||
|
||||
/// <summary>
|
||||
/// initializer-list
|
||||
/// : initializer [ ',' initializer ]*
|
||||
///
|
||||
/// A non-empty list of initializers.
|
||||
/// </summary>
|
||||
public static NamedParser<Initr>
|
||||
InitializerList { get; } = new NamedParser<Initr>("initializer-list");
|
||||
|
||||
/// <summary>
|
||||
/// Type-name
|
||||
/// : specifier-qualifier-list [abstract-declarator]?
|
||||
/// </summary>
|
||||
public static NamedParser<TypeName>
|
||||
TypeName { get; } = new NamedParser<TypeName>("Type-name");
|
||||
|
||||
/// <summary>
|
||||
/// typedef-name
|
||||
/// : identifier
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It must be something already defined.
|
||||
/// We need to look it up in the parser environment.
|
||||
/// </remarks>
|
||||
public static NamedParser<String>
|
||||
TypeDefName { get; } = new NamedParser<String>("typedef-name");
|
||||
|
||||
/// <summary>
|
||||
/// attribute-list
|
||||
/// : parameter-declaration [ ',' parameter-declaration ]*
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A non-empty list of parameters separated by ','.
|
||||
/// Used in a function signature.
|
||||
/// </remarks>
|
||||
public static NamedParser<TypeAttrib>
|
||||
AttributeDecl
|
||||
{ get; } = new NamedParser<TypeAttrib>("attribute-decl");
|
||||
|
||||
/// <summary>
|
||||
/// interface-type-specifier
|
||||
/// : __interface("guid")
|
||||
/// </summary>
|
||||
public static NamedParser<InterfaceTypeSpec>
|
||||
InterfaceTypeSpecifier
|
||||
{ get; } = new NamedParser<InterfaceTypeSpec>("interface-type-specifier");
|
||||
|
||||
public static void SetDeclarationRules() {
|
||||
|
||||
// declaration
|
||||
// : declaration-specifiers [Init-declarator-list]? ';'
|
||||
Declaration.Is(
|
||||
(DeclarationSpecifiers)
|
||||
.Then(InitDeclaratorList.Optional(ImmutableList<InitDeclr>.Empty))
|
||||
.Then(Semicolon)
|
||||
.Then(Decln.Create)
|
||||
.TransformResult(
|
||||
_ => {
|
||||
var result = _.Result;
|
||||
var env = _.Environment;
|
||||
env = result.InitDeclrs.Aggregate(env, (currentEnv, initDeclr) =>
|
||||
currentEnv.AddSymbol(
|
||||
initDeclr.Declr.Name,
|
||||
result.DeclnSpecs.StorageClsSpecs.DefaultIfEmpty(StorageClsSpec.AUTO).First()
|
||||
)
|
||||
);
|
||||
return ParserSucceeded.Create(result, env, _.Source);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// declaration-specifiers
|
||||
// : [ storage-class-specifier | Type-specifier | Type-qualifier ]+
|
||||
DeclarationSpecifiers.Is(
|
||||
Parser.Seed(DeclnSpecs.Empty)
|
||||
.Then(
|
||||
Either(
|
||||
Given<DeclnSpecs>()
|
||||
.Then(StorageClassSpecifier)
|
||||
.Then(DeclnSpecs.Add)
|
||||
).Or(
|
||||
Given<DeclnSpecs>()
|
||||
.Then(TypeSpecifier)
|
||||
.Then(DeclnSpecs.Add)
|
||||
).Or(
|
||||
Given<DeclnSpecs>()
|
||||
.Then(TypeQualifier)
|
||||
.Then(DeclnSpecs.Add)
|
||||
).Or(
|
||||
Given<DeclnSpecs>()
|
||||
.Then(AttributeDecl)
|
||||
.Then(DeclnSpecs.Add)
|
||||
).OneOrMore()
|
||||
)
|
||||
);
|
||||
|
||||
// Init-declarator-list
|
||||
// : Init-declarator [ ',' Init-declarator ]*
|
||||
InitDeclaratorList.Is(
|
||||
InitDeclarator.OneOrMore(Comma)
|
||||
);
|
||||
|
||||
// Init-declarator
|
||||
// : declarator [ '=' initializer ]?
|
||||
InitDeclarator.Is(
|
||||
(Declarator)
|
||||
.Then(
|
||||
(Assign).Then(Initializer).Optional()
|
||||
).Then(InitDeclr.Create)
|
||||
);
|
||||
|
||||
// storage-class-specifier
|
||||
// : auto | register | static | extern | typedef
|
||||
StorageClassSpecifier.Is(
|
||||
Either(Auto)
|
||||
.Or(Register)
|
||||
.Or(Static)
|
||||
.Or(Extern)
|
||||
.Or(Typedef)
|
||||
);
|
||||
|
||||
// Type-specifier
|
||||
// : void
|
||||
// | char
|
||||
// | short
|
||||
// | int
|
||||
// | long
|
||||
// | float
|
||||
// | double
|
||||
// | signed
|
||||
// | unsigned
|
||||
// | struct-or-union-specifier
|
||||
// | enum-specifier
|
||||
// | typedef-name
|
||||
TypeSpecifier.Is(
|
||||
Either(
|
||||
Either(Void)
|
||||
.Or(Char)
|
||||
.Or(Short)
|
||||
.Or(Int)
|
||||
.Or(Long)
|
||||
.Or(Int64)
|
||||
.Or(Float)
|
||||
.Or(Double)
|
||||
.Or(Signed)
|
||||
.Or(Unsigned)
|
||||
.Or(String)
|
||||
.Or(Variant)
|
||||
.Then(kind => new BasicTypeSpec(kind) as TypeSpec)
|
||||
)
|
||||
.Or(InterfaceTypeSpecifier)
|
||||
.Or(StructOrUnionSpecifier)
|
||||
.Or(EnumSpecifier)
|
||||
.Or(TypeDefName.Then(TypedefName.Create))
|
||||
);
|
||||
|
||||
// type_qualifier
|
||||
// : const
|
||||
// | volatile
|
||||
TypeQualifier.Is(
|
||||
Either(Const).Or(Volatile)
|
||||
);
|
||||
|
||||
// declarator
|
||||
// : [pointer]? direct-declarator
|
||||
Declarator.Is(
|
||||
(Pointer.Optional())
|
||||
.Then(DirectDeclarator)
|
||||
.Then(Declr.Create)
|
||||
);
|
||||
|
||||
// pointer
|
||||
// : [ '*' [Type-qualifier-list]? ]+
|
||||
Pointer.Is(
|
||||
(
|
||||
Mult.
|
||||
Then(TypeQualifierList.Optional(ImmutableList<TypeQual>.Empty))
|
||||
.Then(PointerModifier.Create)
|
||||
).OneOrMore()
|
||||
.Then(pointerModifiers => pointerModifiers.Reverse())
|
||||
);
|
||||
|
||||
// parameter-Type-list
|
||||
// : parameter-list [ ',' '...' ]?
|
||||
ParameterTypeList.Is(
|
||||
ParameterList
|
||||
.Then(
|
||||
(Comma)
|
||||
.Then(Period).Then(Period).Then(Period)
|
||||
.Optional()
|
||||
).Then(ParamTypeList.Create)
|
||||
);
|
||||
|
||||
// parameter-list
|
||||
// : parameter-declaration [ ',' parameter-declaration ]*
|
||||
ParameterList.Is(
|
||||
ParameterDeclaration.OneOrMore(Comma)
|
||||
);
|
||||
|
||||
// Type-qualifier-list
|
||||
// : [Type-qualifier]+
|
||||
TypeQualifierList.Is(
|
||||
TypeQualifier.OneOrMore()
|
||||
);
|
||||
|
||||
// direct-declarator
|
||||
// : [
|
||||
// identifier | '(' declarator ')'
|
||||
// ] [
|
||||
// '[' [constant-expression]? ']'
|
||||
// | '(' [parameter-Type-list]? ')'
|
||||
// ]*
|
||||
DirectDeclarator.Is(
|
||||
(
|
||||
Either(
|
||||
(Identifier).Then(Declr.Create)
|
||||
).Or(
|
||||
(LeftParen).Then(Declarator).Then(RightParen)
|
||||
)
|
||||
).Then(
|
||||
Either(
|
||||
Given<Declr>()
|
||||
.Then(LeftBracket)
|
||||
.Then(
|
||||
ConstantExpression.Optional().Then(ArrayModifier.Create)
|
||||
).Then(RightBracket)
|
||||
.Then(Declr.Add)
|
||||
).Or(
|
||||
Given<Declr>()
|
||||
.Then(LeftParen)
|
||||
.Then(
|
||||
ParameterTypeList
|
||||
.Optional()
|
||||
.Then(FunctionModifier.Create)
|
||||
).Then(RightParen)
|
||||
.Then(Declr.Add)
|
||||
)
|
||||
.ZeroOrMore()
|
||||
)
|
||||
);
|
||||
|
||||
// enum-specifier
|
||||
// : enum [identifier]? '{' enumerator-list '}'
|
||||
// | enum identifier
|
||||
EnumSpecifier.Is(
|
||||
(Enum)
|
||||
.Then(
|
||||
Either(
|
||||
Identifier.Optional()
|
||||
.Then(LeftCurlyBrace)
|
||||
.Then(EnumeratorList)
|
||||
.Then(RightCurlyBrace)
|
||||
.Then(EnumSpec.Create)
|
||||
).Or(
|
||||
(Identifier)
|
||||
.Then(EnumSpec.Create)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// enumerator-list
|
||||
// : enumerator [ ',' enumerator ]*
|
||||
EnumeratorList.Is(
|
||||
Enumerator.OneOrMore(Comma)
|
||||
);
|
||||
|
||||
// enumerator
|
||||
// : enumeration-constant [ '=' constant-expression ]?
|
||||
Enumerator.Is(
|
||||
EnumerationConstant
|
||||
.Then(
|
||||
(Assign)
|
||||
.Then(ConstantExpression)
|
||||
.Optional()
|
||||
).Then(Enumr.Create)
|
||||
);
|
||||
|
||||
// enumeration-constant
|
||||
// : identifier
|
||||
EnumerationConstant.Is(
|
||||
Identifier
|
||||
);
|
||||
|
||||
// struct-or-union-specifier
|
||||
// : struct-or-union [identifier]? { struct-declaration-list }
|
||||
// | struct-or-union identifier
|
||||
StructOrUnionSpecifier.Is(
|
||||
(StructOrUnion)
|
||||
.Then(
|
||||
Either(
|
||||
Given<StructOrUnion>()
|
||||
.Then(Identifier.Optional())
|
||||
.Then(LeftCurlyBrace)
|
||||
.Then(StructDeclarationList)
|
||||
.Then(RightCurlyBrace)
|
||||
.Then(StructOrUnionSpec.Create)
|
||||
).Or(
|
||||
Given<StructOrUnion>()
|
||||
.Then(Identifier)
|
||||
.Then(StructOrUnionSpec.Create)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// struct-or-union
|
||||
// : struct | union
|
||||
StructOrUnion.Is(
|
||||
Either(Struct).Or(Union)
|
||||
);
|
||||
|
||||
// struct-declaration-list
|
||||
// : [struct-declaration]+
|
||||
StructDeclarationList.Is(
|
||||
StructDeclaration.OneOrMore()
|
||||
);
|
||||
|
||||
// struct-declaration
|
||||
// : specifier-qualifier-list struct-declarator-list ';'
|
||||
StructDeclaration.Is(
|
||||
(SpecifierQualifierList)
|
||||
.Then(StructDeclaratorList)
|
||||
.Then(Semicolon)
|
||||
.Then(StructDecln.Create)
|
||||
);
|
||||
|
||||
// specifier-qualifier-list
|
||||
// : [ Type-specifier | Type-qualifier ]+
|
||||
SpecifierQualifierList.Is(
|
||||
Parser.Seed(SpecQualList.Empty)
|
||||
.Then(
|
||||
Either(
|
||||
Given<SpecQualList>()
|
||||
.Then(TypeSpecifier)
|
||||
.Then(SpecQualList.Add)
|
||||
).Or(
|
||||
Given<SpecQualList>()
|
||||
.Then(TypeQualifier)
|
||||
.Then(SpecQualList.Add)
|
||||
)
|
||||
.OneOrMore()
|
||||
)
|
||||
);
|
||||
|
||||
// struct-declarator-list
|
||||
// : struct-declarator [ ',' struct-declarator ]*
|
||||
StructDeclaratorList.Is(
|
||||
StructDeclarator.OneOrMore(Comma)
|
||||
);
|
||||
|
||||
// struct-declarator
|
||||
// : [declarator]? ':' constant-expression
|
||||
// | declarator
|
||||
StructDeclarator.Is(
|
||||
Either(
|
||||
(Declarator.Optional())
|
||||
.Then(Colon)
|
||||
.Then(ConstantExpression)
|
||||
.Then(StructDeclr.Create)
|
||||
).Or(
|
||||
(Declarator)
|
||||
.Then(StructDeclr.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// parameter-declaration
|
||||
// : declaration-specifiers [ declarator | abstract-declarator ]?
|
||||
ParameterDeclaration.Is(
|
||||
(DeclarationSpecifiers)
|
||||
.Then(
|
||||
Either(
|
||||
(Declarator).Then(ParamDeclr.Create)
|
||||
).Or(
|
||||
(AbstractDeclarator).Then(ParamDeclr.Create)
|
||||
).Optional()
|
||||
).Then(ParamDecln.Create)
|
||||
);
|
||||
|
||||
// abstract-declarator
|
||||
// : [pointer]? direct-abstract-declarator
|
||||
// | pointer
|
||||
AbstractDeclarator.Is(
|
||||
Either(
|
||||
(Pointer.Optional(ImmutableList<PointerModifier>.Empty))
|
||||
.Then(DirectAbstractDeclarator)
|
||||
.Then(AbstractDeclr.Add)
|
||||
).Or(
|
||||
(Pointer)
|
||||
.Then(AbstractDeclr.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// direct-abstract-declarator
|
||||
// : [
|
||||
// '(' abstract-declarator ')'
|
||||
// | '[' [constant-expression]? ']' // array modifier
|
||||
// | '(' [parameter-type_list]? ')' // function modifier
|
||||
// ] [
|
||||
// '[' [constant-expression]? ']' // array modifier
|
||||
// | '(' [parameter-Type-list]? ')' // function modifier
|
||||
// ]*
|
||||
DirectAbstractDeclarator.Is(
|
||||
(
|
||||
Either(
|
||||
(LeftParen)
|
||||
.Then(AbstractDeclarator)
|
||||
.Then(RightParen)
|
||||
).Or(
|
||||
(LeftBracket)
|
||||
.Then(ConstantExpression.Optional())
|
||||
.Then(RightBracket)
|
||||
.Then(ArrayModifier.Create)
|
||||
.Then<ArrayModifier, ImmutableList<ArrayModifier>>(ImmutableList.Create)
|
||||
.Then(AbstractDeclr.Create)
|
||||
).Or(
|
||||
(LeftParen)
|
||||
.Then(ParameterTypeList.Optional())
|
||||
.Then(RightParen)
|
||||
.Then(FunctionModifier.Create)
|
||||
.Then<FunctionModifier, ImmutableList<FunctionModifier>>(ImmutableList.Create)
|
||||
.Then(AbstractDeclr.Create)
|
||||
)
|
||||
).Then(
|
||||
Either(
|
||||
Given<AbstractDeclr>()
|
||||
.Then(
|
||||
LeftBracket
|
||||
.Then(ConstantExpression.Optional())
|
||||
.Then(RightBracket)
|
||||
.Then(ArrayModifier.Create)
|
||||
).Then(
|
||||
AbstractDeclr.Add
|
||||
)
|
||||
).Or(
|
||||
Given<AbstractDeclr>()
|
||||
.Then(
|
||||
(LeftParen)
|
||||
.Then(ParameterTypeList.Optional())
|
||||
.Then(RightParen)
|
||||
.Then(FunctionModifier.Create)
|
||||
).Then(
|
||||
AbstractDeclr.Add
|
||||
)
|
||||
)
|
||||
.ZeroOrMore()
|
||||
)
|
||||
);
|
||||
|
||||
// initializer
|
||||
// : assignment-expression
|
||||
// | '{' initializer-list '}'
|
||||
// | '{' initializer-list ',' '}'
|
||||
Initializer.Is(
|
||||
Either<Initr>(
|
||||
(AssignmentExpression)
|
||||
.Then(InitExpr.Create)
|
||||
).Or(
|
||||
(LeftCurlyBrace)
|
||||
.Then(InitializerList)
|
||||
.Then(RightCurlyBrace)
|
||||
).Or(
|
||||
(LeftCurlyBrace)
|
||||
.Then(InitializerList)
|
||||
.Then(Comma)
|
||||
.Then(RightCurlyBrace)
|
||||
)
|
||||
);
|
||||
|
||||
// initializer-list
|
||||
// : initializer [ ',' initializer ]*
|
||||
InitializerList.Is(
|
||||
Initializer.OneOrMore(Comma).Optional()
|
||||
.Then(InitList.Create)
|
||||
);
|
||||
|
||||
// Type-name
|
||||
// : specifier-qualifier-list [abstract-declarator]?
|
||||
TypeName.Is(
|
||||
(SpecifierQualifierList)
|
||||
.Then(AbstractDeclarator.Optional())
|
||||
.Then(AST.TypeName.Create)
|
||||
);
|
||||
|
||||
// typedef-name
|
||||
// : identifier
|
||||
TypeDefName.Is(
|
||||
(Identifier)
|
||||
.Check(
|
||||
result => result.Environment.IsTypedefName(result.Result)
|
||||
)
|
||||
);
|
||||
|
||||
AttributeDecl.Is(
|
||||
(AttributeKey)
|
||||
.Then(LeftParen)
|
||||
.Then(Variable)
|
||||
.Then(
|
||||
Either(
|
||||
Given<Expr>()
|
||||
.Then(RightParen)
|
||||
.Then(TypeAttrib.Create)
|
||||
).Or(
|
||||
Given<Expr>()
|
||||
.Then(LeftParen)
|
||||
.Then(ArgumentExpressionList.Optional(ImmutableList<Expr>.Empty))
|
||||
.Then(RightParen)
|
||||
.Then(RightParen)
|
||||
.Then(TypeAttrib.Create)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
InterfaceTypeSpecifier.Is(
|
||||
(InterfaceTypeKey)
|
||||
.Then(LeftParen)
|
||||
.Then(PrimaryExpression)
|
||||
.Then(
|
||||
Given<Expr>()
|
||||
.Then(RightParen)
|
||||
.Then(InterfaceTypeSpec.Create)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
397
LibIFPSCC/Parser/Expressions.cs
Normal file
@ -0,0 +1,397 @@
|
||||
using System.Collections.Immutable;
|
||||
using AST;
|
||||
using static Parsing.ParserCombinator;
|
||||
|
||||
namespace Parsing {
|
||||
public partial class CParsers {
|
||||
public static NamedParser<Expr>
|
||||
Expression { get; } = Parser.Create<Expr>("expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
PrimaryExpression { get; } = Parser.Create<Expr>("primary-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
Variable { get; } = Parser.Create<Expr>("variable");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
Constant { get; } = Parser.Create<Expr>("constant");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
ConstantExpression { get; } = Parser.Create<Expr>("constant-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
ConditionalExpression { get; } = Parser.Create<Expr>("conditional-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
AssignmentExpression { get; } = Parser.Create<Expr>("assignment-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
ArgumentExpression { get; } = Parser.Create<Expr>("argument-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
PostfixExpression { get; } = Parser.Create<Expr>("postfix-expression");
|
||||
|
||||
public static NamedParser<ImmutableList<Expr>>
|
||||
ArgumentExpressionList { get; } = Parser.Create<ImmutableList<Expr>>("argument-expression-list");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
UnaryExpression { get; } = Parser.Create<Expr>("unary-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
CastExpression { get; } = Parser.Create<Expr>("cast-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
MultiplicativeExpression { get; } = Parser.Create<Expr>("multiplicative-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
AdditiveExpression { get; } = Parser.Create<Expr>("additive-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
ShiftExpression { get; } = Parser.Create<Expr>("shift-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
RelationalExpression { get; } = Parser.Create<Expr>("relational-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
EqualityExpression { get; } = Parser.Create<Expr>("equality-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
AndExpression { get; } = Parser.Create<Expr>("and-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
ExclusiveOrExpression { get; } = Parser.Create<Expr>("exclusive-or-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
InclusiveOrExpression { get; } = Parser.Create<Expr>("inclusive-or-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
LogicalAndExpression { get; } = Parser.Create<Expr>("logical-and-expression");
|
||||
|
||||
public static NamedParser<Expr>
|
||||
LogicalOrExpression { get; } = Parser.Create<Expr>("logical-or-expression");
|
||||
|
||||
/// <summary>
|
||||
/// The following are rules for C expressions.
|
||||
/// </summary>
|
||||
public static void SetExpressionRules() {
|
||||
|
||||
// expression
|
||||
// : assignment-expression [ ',' assignment-expression ]*
|
||||
Expression.Is(
|
||||
AssignmentExpression
|
||||
.OneOrMore(Comma)
|
||||
.Then(exprs => {
|
||||
if (exprs.Count == 1) {
|
||||
return exprs[0];
|
||||
}
|
||||
return AssignmentList.Create(exprs);
|
||||
})
|
||||
);
|
||||
|
||||
// primary-expression
|
||||
// : identifier # Cannot be a typedef name.
|
||||
// | constant
|
||||
// | string-literal
|
||||
// | '(' expression ')'
|
||||
PrimaryExpression.Is(
|
||||
Either(Variable)
|
||||
.Or(Constant)
|
||||
.Or(StringLiteral)
|
||||
.Or(UnicodeStringLiteral)
|
||||
.Or(
|
||||
(LeftParen).Then(Expression).Then(RightParen)
|
||||
)
|
||||
);
|
||||
|
||||
// An identifier for a variable must not be defined as a typedef name.
|
||||
Variable.Is(
|
||||
Identifier.Check(result => !result.Environment.IsTypedefName(result.Result)).Then(AST.Variable.Create)
|
||||
);
|
||||
|
||||
// constant
|
||||
// : const-char
|
||||
// : const-int
|
||||
// : const-float
|
||||
Constant.Is(
|
||||
Either(ConstChar)
|
||||
.Or(ConstInt)
|
||||
.Or(ConstFloat)
|
||||
);
|
||||
|
||||
|
||||
// constant-expression
|
||||
// : conditional-expression
|
||||
//
|
||||
// Note:
|
||||
// The size of an array should be a constant.
|
||||
// Note that the check is actually performed in semantic analysis.
|
||||
ConstantExpression.Is(
|
||||
ConditionalExpression
|
||||
);
|
||||
|
||||
// conditional-expression
|
||||
// : logical-or-expression [ '?' expression ':' conditional-expression ]?
|
||||
ConditionalExpression.Is(
|
||||
(LogicalOrExpression)
|
||||
.Then(
|
||||
Given<Expr>()
|
||||
.Then(Question)
|
||||
.Then(Expression)
|
||||
.Then(Colon)
|
||||
.Then(ConditionalExpression)
|
||||
.Then(AST.ConditionalExpression.Create)
|
||||
.Optional()
|
||||
)
|
||||
);
|
||||
|
||||
// assignment-expression
|
||||
// : unary-expression assignment-operator assignment-expression # first-set = first-set(unary-expression)
|
||||
// | conditional-expression # first-set = first-set(cast-expression) = first-set(unary-expression) ++ { '(' }
|
||||
//
|
||||
// Note:
|
||||
// Assignment operators are:
|
||||
// '=', '*=', '/=', '%=', '+=', '-=', '<<=', '>>=', '&=', '^=', '|='
|
||||
AssignmentExpression.Is(
|
||||
Either(
|
||||
AssignmentOperator(
|
||||
UnaryExpression,
|
||||
AssignmentExpression,
|
||||
BinaryOperatorBuilder.Create(Assign, Assignment.Create),
|
||||
BinaryOperatorBuilder.Create(MultAssign, AST.MultAssign.Create),
|
||||
BinaryOperatorBuilder.Create(DivAssign, AST.DivAssign.Create),
|
||||
BinaryOperatorBuilder.Create(ModAssign, AST.ModAssign.Create),
|
||||
BinaryOperatorBuilder.Create(AddAssign, AST.AddAssign.Create),
|
||||
BinaryOperatorBuilder.Create(SubAssign, AST.SubAssign.Create),
|
||||
BinaryOperatorBuilder.Create(LeftShiftAssign, LShiftAssign.Create),
|
||||
BinaryOperatorBuilder.Create(RightShiftAssign, RShiftAssign.Create),
|
||||
BinaryOperatorBuilder.Create(BitwiseAndAssign, AST.BitwiseAndAssign.Create),
|
||||
BinaryOperatorBuilder.Create(XorAssign, AST.XorAssign.Create),
|
||||
BinaryOperatorBuilder.Create(BitwiseOrAssign, AST.BitwiseOrAssign.Create)
|
||||
)
|
||||
).Or(
|
||||
ConditionalExpression
|
||||
)
|
||||
);
|
||||
|
||||
ArgumentExpression.Is(
|
||||
Either(
|
||||
AssignmentExpression
|
||||
).Or(
|
||||
(LeftCurlyBrace)
|
||||
.Then(InitializerList)
|
||||
.Then(RightCurlyBrace)
|
||||
.Then((x) => ExprInitList.Create(x as InitList))
|
||||
).Or(
|
||||
(LeftCurlyBrace)
|
||||
.Then(InitializerList)
|
||||
.Then(Comma)
|
||||
.Then(RightCurlyBrace)
|
||||
.Then((x) => ExprInitList.Create(x as InitList))
|
||||
)
|
||||
);
|
||||
|
||||
// postfix-expression
|
||||
// : primary-expression [
|
||||
// '[' expression ']' # Get element from array
|
||||
// | '(' [argument-expression-list]? ')' # Function call
|
||||
// | '.' identifier # Get member from struct/union
|
||||
// | '->' identifier # Get member from struct/union
|
||||
// | '++' # Increment
|
||||
// | '--' # Decrement
|
||||
// ]*
|
||||
PostfixExpression.Is(
|
||||
PrimaryExpression
|
||||
.Then(
|
||||
Either(
|
||||
Given<Expr>()
|
||||
.Then(LeftBracket)
|
||||
.Then(Expression)
|
||||
.Then(RightBracket)
|
||||
.Then((array, index) => Dereference.Create(AST.Add.Create(array, index)))
|
||||
).Or(
|
||||
Given<Expr>()
|
||||
.Then(LeftParen)
|
||||
.Then(ArgumentExpressionList.Optional(ImmutableList<Expr>.Empty))
|
||||
.Then(RightParen)
|
||||
.Then(FuncCall.Create)
|
||||
).Or(
|
||||
Given<Expr>()
|
||||
.Then(Period)
|
||||
.Then(Identifier)
|
||||
.Then(Attribute.Create)
|
||||
).Or(
|
||||
Given<Expr>()
|
||||
.Then(RightArrow)
|
||||
.Then(Identifier)
|
||||
.Then((expr, member) => Attribute.Create(Dereference.Create(expr), member))
|
||||
).Or(
|
||||
Given<Expr>()
|
||||
.Then(Increment)
|
||||
.Then(PostIncrement.Create)
|
||||
).Or(
|
||||
Given<Expr>()
|
||||
.Then(Decrement)
|
||||
.Then(PostDecrement.Create)
|
||||
).ZeroOrMore()
|
||||
)
|
||||
);
|
||||
|
||||
// argument-expression-list
|
||||
// : assignment-expression [ ',' assignment-expression ]*
|
||||
ArgumentExpressionList.Is(
|
||||
ArgumentExpression.OneOrMore(Comma)
|
||||
);
|
||||
|
||||
// unary-expression
|
||||
// : postfix-expression # first-set = { id, const, string }
|
||||
// | '++' unary-expression # first-set = { '++' }
|
||||
// | '--' unary-expression # first-set = { '--' }
|
||||
// | unary-operator cast-expression # first-set = { '&', '*', '+', '-', '~', '!' }
|
||||
// | 'sizeof' unary-expression # first-set = { 'sizeof' }
|
||||
// | 'sizeof' '(' Type-name ')' # first-set = { 'sizeof' }
|
||||
//
|
||||
// Notes:
|
||||
// 1. unary-operator can be '&', '*', '+', '-', '~', '!'.
|
||||
// 2. The last two rules are ambiguous: you can't figure out whether the x in sizeof(x) is a typedef of a variable.
|
||||
// I have a parser hack for this: add a parser environment to track all the typedefs.
|
||||
// 3. first_set = first_set(postfix-expression) + { '++', '--', '&', '*', '+', '-', '~', '!', 'sizeof' }
|
||||
// = first_set(primary-expression) + { '++', '--', '&', '*', '+', '-', '~', '!', 'sizeof' }
|
||||
// = { id, const, string, '++', '--', '&', '*', '+', '-', '~', '!', 'sizeof' }
|
||||
UnaryExpression.Is(
|
||||
Either(
|
||||
PostfixExpression
|
||||
).Or(
|
||||
(Increment).Then(UnaryExpression).Then(PreIncrement.Create)
|
||||
).Or(
|
||||
(Decrement).Then(UnaryExpression).Then(PreDecrement.Create)
|
||||
).Or(
|
||||
(BitwiseAnd).Then(CastExpression).Then(Reference.Create)
|
||||
).Or(
|
||||
(Mult).Then(CastExpression).Then(Dereference.Create)
|
||||
).Or(
|
||||
(Add).Then(CastExpression).Then(Positive.Create)
|
||||
).Or(
|
||||
(Sub).Then(CastExpression).Then(Negative.Create)
|
||||
).Or(
|
||||
(BitwiseNot).Then(CastExpression).Then(AST.BitwiseNot.Create)
|
||||
).Or(
|
||||
(LogicalNot).Then(CastExpression).Then(AST.LogicalNot.Create)
|
||||
).Or(
|
||||
(SizeOf).Then(UnaryExpression).Then(SizeofExpr.Create)
|
||||
).Or(
|
||||
(SizeOf).Then(LeftParen).Then(TypeName).Then(RightParen).Then(SizeofType.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// cast-expression
|
||||
// : unary-expression # first-set = { id, const, string, '++', '--', '&', '*', '+', '-', '~', '!', 'sizeof' }
|
||||
// | '(' type_name ')' cast-expression # first-set = '('
|
||||
CastExpression.Is(
|
||||
Either(
|
||||
UnaryExpression
|
||||
).Or(
|
||||
(LeftParen).Then(TypeName).Then(RightParen).Then(CastExpression)
|
||||
.Then(TypeCast.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// multiplicative-expression
|
||||
// : cast-expression [ [ '*' | '/' | '%' ] cast-expression ]*
|
||||
MultiplicativeExpression.Is(
|
||||
BinaryOperator(
|
||||
CastExpression,
|
||||
BinaryOperatorBuilder.Create(Mult, Multiply.Create),
|
||||
BinaryOperatorBuilder.Create(Div, Divide.Create),
|
||||
BinaryOperatorBuilder.Create(Mod, Modulo.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// additive-expression
|
||||
// : multiplicative-expression [ [ '+' | '-' ] multiplicative-expression ]*
|
||||
AdditiveExpression.Is(
|
||||
BinaryOperator(
|
||||
MultiplicativeExpression,
|
||||
BinaryOperatorBuilder.Create(Add, AST.Add.Create),
|
||||
BinaryOperatorBuilder.Create(Sub, AST.Sub.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// shift-expression
|
||||
// : additive-expression [ [ '<<' | '>>' ] additive-expression ]*
|
||||
ShiftExpression.Is(
|
||||
BinaryOperator(
|
||||
AdditiveExpression,
|
||||
BinaryOperatorBuilder.Create(LeftShift, LShift.Create),
|
||||
BinaryOperatorBuilder.Create(RightShift, RShift.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// relational-expression
|
||||
// : shift-expression [ [ '<' | '>' | '<=' | '>=' ] shift-expression ]*
|
||||
RelationalExpression.Is(
|
||||
BinaryOperator(
|
||||
ShiftExpression,
|
||||
BinaryOperatorBuilder.Create(Less, AST.Less.Create),
|
||||
BinaryOperatorBuilder.Create(Greater, AST.Greater.Create),
|
||||
BinaryOperatorBuilder.Create(LessEqual, LEqual.Create),
|
||||
BinaryOperatorBuilder.Create(GreaterEqual, GEqual.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// equality-expression
|
||||
// : relational-expression [ [ '==' | '!=' ] relational-expression ]*
|
||||
EqualityExpression.Is(
|
||||
BinaryOperator(
|
||||
RelationalExpression,
|
||||
BinaryOperatorBuilder.Create(Equal, AST.Equal.Create),
|
||||
BinaryOperatorBuilder.Create(NotEqual, AST.NotEqual.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// and-expression
|
||||
// : equality-expression [ '&' equality-expression ]*
|
||||
AndExpression.Is(
|
||||
BinaryOperator(
|
||||
EqualityExpression,
|
||||
BinaryOperatorBuilder.Create(BitwiseAnd, AST.BitwiseAnd.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// exclusive-or-expression
|
||||
// : and-expression [ '^' and-expression ]*
|
||||
ExclusiveOrExpression.Is(
|
||||
BinaryOperator(
|
||||
AndExpression,
|
||||
BinaryOperatorBuilder.Create(Xor, AST.Xor.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// inclusive-or-expression
|
||||
// : exclusive-or-expression [ '|' exclusive-or-expression ]*
|
||||
InclusiveOrExpression.Is(
|
||||
BinaryOperator(
|
||||
ExclusiveOrExpression,
|
||||
BinaryOperatorBuilder.Create(BitwiseOr, AST.BitwiseOr.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// logical-and-expression
|
||||
// : inclusive-or-expression [ '&&' inclusive-or-expression ]*
|
||||
LogicalAndExpression.Is(
|
||||
BinaryOperator(
|
||||
InclusiveOrExpression,
|
||||
BinaryOperatorBuilder.Create(LogicalAnd, AST.LogicalAnd.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// logical-or-expression
|
||||
// :logical-and-expression [ '||' logical-and-expression ]*
|
||||
LogicalOrExpression.Is(
|
||||
BinaryOperator(
|
||||
LogicalAndExpression,
|
||||
BinaryOperatorBuilder.Create(LogicalOr, AST.LogicalOr.Create)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
80
LibIFPSCC/Parser/ExternalDefinitions.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using AST;
|
||||
using static Parsing.ParserCombinator;
|
||||
|
||||
namespace Parsing {
|
||||
public partial class CParsers {
|
||||
|
||||
/// <summary>
|
||||
/// translation-unit
|
||||
/// : [external-declaration]+
|
||||
/// </summary>
|
||||
public static NamedParser<TranslnUnit>
|
||||
TranslationUnit { get; } = new NamedParser<TranslnUnit>("translation-unit");
|
||||
|
||||
/// <summary>
|
||||
/// external-declaration
|
||||
/// : function-definition | declaration
|
||||
/// </summary>
|
||||
public static NamedParser<IExternDecln>
|
||||
ExternalDeclaration { get; } = new NamedParser<IExternDecln>("external-declaration");
|
||||
|
||||
/// <summary>
|
||||
/// function-definition
|
||||
/// : [declaration-specifiers]? declarator [declaration-list]? compound-statement
|
||||
///
|
||||
/// NOTE: the optional declaration_list is for the **old-style** function prototype like this:
|
||||
/// +-------------------------------+
|
||||
/// | int foo(param1, param2) |
|
||||
/// | int param1; |
|
||||
/// | char param2; |
|
||||
/// | { |
|
||||
/// | .... |
|
||||
/// | } |
|
||||
/// +-------------------------------+
|
||||
///
|
||||
/// i'm **not** going to support this style. function prototypes should always be like this:
|
||||
/// +------------------------------------------+
|
||||
/// | int foo(int param1, char param2) { |
|
||||
/// | .... |
|
||||
/// | } |
|
||||
/// +------------------------------------------+
|
||||
///
|
||||
/// so the grammar becomes:
|
||||
/// function-definition
|
||||
/// : [declaration-specifiers]? declarator compound-statement
|
||||
/// </summary>
|
||||
public static NamedParser<FuncDef>
|
||||
FunctionDefinition { get; } = new NamedParser<FuncDef>("function-definition");
|
||||
|
||||
public static void SetExternalDefinitionRules() {
|
||||
|
||||
// translation-unit
|
||||
// : [external-declaration]+
|
||||
TranslationUnit.Is(
|
||||
(ExternalDeclaration)
|
||||
.OneOrMoreForEntireUnit()
|
||||
.Then(TranslnUnit.Create)
|
||||
);
|
||||
|
||||
// external-declaration
|
||||
// : function-definition | declaration
|
||||
ExternalDeclaration.Is(
|
||||
Either<IExternDecln>(
|
||||
FunctionDefinition
|
||||
).Or(
|
||||
Declaration
|
||||
)
|
||||
);
|
||||
|
||||
// function-definition
|
||||
// : [declaration-specifiers]? declarator compound-statement
|
||||
FunctionDefinition.Is(
|
||||
DeclarationSpecifiers.Optional()
|
||||
.Then(Declarator)
|
||||
.Then(CompoundStatement)
|
||||
.Then(FuncDef.Create)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
766
LibIFPSCC/Parser/ParserClasses.cs
Normal file
@ -0,0 +1,766 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using LexicalAnalysis;
|
||||
|
||||
namespace Parsing {
|
||||
public class ParserThenParser<R1, R2> : IParser<Tuple<R2, R1>> {
|
||||
public ParserThenParser(IParser<R1> firstParser, IParser<R2> secondParser) {
|
||||
this.FirstParser = firstParser;
|
||||
this.SecondParser = secondParser;
|
||||
}
|
||||
|
||||
public IParser<R1> FirstParser { get; }
|
||||
public IParser<R2> SecondParser { get; }
|
||||
public RuleCombining Combining => RuleCombining.THEN;
|
||||
|
||||
public IParserResult<Tuple<R2, R1>> Parse(ParserInput input) {
|
||||
var firstResult = this.FirstParser.Parse(input);
|
||||
if (!firstResult.IsSuccessful) {
|
||||
return new ParserFailed<Tuple<R2, R1>>(firstResult);
|
||||
}
|
||||
var secondResult = this.SecondParser.Parse(firstResult.ToInput());
|
||||
if (!secondResult.IsSuccessful) {
|
||||
return new ParserFailed<Tuple<R2, R1>>(secondResult);
|
||||
}
|
||||
return ParserSucceeded.Create(Tuple.Create(secondResult.Result, firstResult.Result), secondResult.Environment, secondResult.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class ParserThenConsumer<R> : IParser<R> {
|
||||
public ParserThenConsumer(IParser<R> parser, IConsumer consumer) {
|
||||
this.Parser = parser;
|
||||
this.Consumer = consumer;
|
||||
}
|
||||
|
||||
public IParser<R> Parser { get; }
|
||||
public IConsumer Consumer { get; }
|
||||
public RuleCombining Combining => RuleCombining.THEN;
|
||||
|
||||
public IParserResult<R> Parse(ParserInput input) {
|
||||
var firstResult = this.Parser.Parse(input);
|
||||
if (!firstResult.IsSuccessful) {
|
||||
return new ParserFailed<R>(firstResult);
|
||||
}
|
||||
var secondResult = this.Consumer.Consume(firstResult.ToInput());
|
||||
if (!secondResult.IsSuccessful) {
|
||||
return new ParserFailed<R>(secondResult);
|
||||
}
|
||||
return ParserSucceeded.Create(firstResult.Result, secondResult.Environment, secondResult.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class ParserThenTransformer<R1, R2> : IParser<R2> {
|
||||
public ParserThenTransformer(IParser<R1> parser, ITransformer<R1, R2> transformer) {
|
||||
this.Parser = parser;
|
||||
this.Transformer = transformer;
|
||||
}
|
||||
|
||||
public IParser<R1> Parser { get; }
|
||||
public ITransformer<R1, R2> Transformer { get; }
|
||||
public RuleCombining Combining => RuleCombining.THEN;
|
||||
|
||||
public IParserResult<R2> Parse(ParserInput input) {
|
||||
var firstResult = this.Parser.Parse(input);
|
||||
if (!firstResult.IsSuccessful) {
|
||||
return new ParserFailed<R2>(firstResult);
|
||||
}
|
||||
return this.Transformer.Transform(firstResult.Result, firstResult.ToInput());
|
||||
}
|
||||
}
|
||||
|
||||
public class ConsumerThenParser<R> : IParser<R> {
|
||||
public ConsumerThenParser(IConsumer consumer, IParser<R> parser) {
|
||||
this.Consumer = consumer;
|
||||
this.Parser = parser;
|
||||
}
|
||||
|
||||
public IConsumer Consumer { get; }
|
||||
public IParser<R> Parser { get; }
|
||||
public RuleCombining Combining => RuleCombining.THEN;
|
||||
|
||||
public IParserResult<R> Parse(ParserInput input) {
|
||||
var firstResult = this.Consumer.Consume(input);
|
||||
if (!firstResult.IsSuccessful) {
|
||||
return new ParserFailed<R>(firstResult);
|
||||
}
|
||||
return this.Parser.Parse(firstResult.ToInput());
|
||||
}
|
||||
}
|
||||
|
||||
public class ConsumerThenConsumer : IConsumer {
|
||||
public ConsumerThenConsumer(IConsumer firstConsumer, IConsumer secondConsumer) {
|
||||
this.FirstConsumer = firstConsumer;
|
||||
this.SecondConsumer = secondConsumer;
|
||||
}
|
||||
|
||||
public IConsumer FirstConsumer { get; }
|
||||
public IConsumer SecondConsumer { get; }
|
||||
|
||||
public IParserResult Consume(ParserInput input) {
|
||||
var result1 = this.FirstConsumer.Consume(input);
|
||||
if (!result1.IsSuccessful) {
|
||||
return result1;
|
||||
}
|
||||
return this.SecondConsumer.Consume(result1.ToInput());
|
||||
}
|
||||
}
|
||||
|
||||
public class TransformerThenParser<S, R1, R2> : ITransformer<S, Tuple<R2, R1>> {
|
||||
public TransformerThenParser(ITransformer<S, R1> transformer, IParser<R2> parser) {
|
||||
this.Transformer = transformer;
|
||||
this.Parser = parser;
|
||||
}
|
||||
|
||||
public ITransformer<S, R1> Transformer { get; }
|
||||
public IParser<R2> Parser { get; }
|
||||
|
||||
public IParserResult<Tuple<R2, R1>> Transform(S seed, ParserInput input) {
|
||||
var result1 = this.Transformer.Transform(seed, input);
|
||||
if (!result1.IsSuccessful) {
|
||||
return new ParserFailed<Tuple<R2, R1>>(result1);
|
||||
}
|
||||
var result2 = this.Parser.Parse(result1.ToInput());
|
||||
if (!result2.IsSuccessful) {
|
||||
return new ParserFailed<Tuple<R2, R1>>(result2);
|
||||
}
|
||||
return ParserSucceeded.Create(Tuple.Create(result2.Result, result1.Result), result2.Environment, result2.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class TransformerThenConsumer<S, R> : ITransformer<S, R> {
|
||||
public TransformerThenConsumer(ITransformer<S, R> transformer, IConsumer consumer) {
|
||||
this.Transformer = transformer;
|
||||
this.Consumer = consumer;
|
||||
}
|
||||
|
||||
public ITransformer<S, R> Transformer { get; }
|
||||
public IConsumer Consumer { get; }
|
||||
|
||||
public IParserResult<R> Transform(S seed, ParserInput input) {
|
||||
var result1 = this.Transformer.Transform(seed, input);
|
||||
if (!result1.IsSuccessful) {
|
||||
return result1;
|
||||
}
|
||||
var result2 = this.Consumer.Consume(result1.ToInput());
|
||||
if (!result2.IsSuccessful) {
|
||||
return new ParserFailed<R>(result2);
|
||||
}
|
||||
return ParserSucceeded.Create(result1.Result, result2.Environment, result2.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class TransformerThenTransformer<S, I, R> : ITransformer<S, R> {
|
||||
public TransformerThenTransformer(ITransformer<S, I> firstTransformer, ITransformer<I, R> secondTransformer) {
|
||||
this.FirstTransformer = firstTransformer;
|
||||
this.SecondTransformer = secondTransformer;
|
||||
}
|
||||
|
||||
public ITransformer<S, I> FirstTransformer { get; }
|
||||
public ITransformer<I, R> SecondTransformer { get; }
|
||||
|
||||
public IParserResult<R> Transform(S seed, ParserInput input) {
|
||||
var result1 = this.FirstTransformer.Transform(seed, input);
|
||||
if (!result1.IsSuccessful) {
|
||||
return new ParserFailed<R>(result1);
|
||||
}
|
||||
return this.SecondTransformer.Transform(result1.Result, result1.ToInput());
|
||||
}
|
||||
}
|
||||
|
||||
public enum RuleCombining {
|
||||
NONE,
|
||||
THEN,
|
||||
OR
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A parser consumes one or several tokens, and produces a result.
|
||||
/// </summary>
|
||||
public interface IParser<out R> {
|
||||
IParserResult<R> Parse(ParserInput input);
|
||||
RuleCombining Combining { get; }
|
||||
}
|
||||
|
||||
public static class Parser {
|
||||
public static NamedParser<R> Create<R>(String name) =>
|
||||
new NamedParser<R>(name);
|
||||
public static IParser<R> Seed<R>(R seed) =>
|
||||
new AlwaysSucceedingParser<R>(seed);
|
||||
}
|
||||
|
||||
public class NamedParser<R> : IParser<R> {
|
||||
public NamedParser(String name) {
|
||||
this.Name = name;
|
||||
this.Parser = new SetOnce<IParser<R>>();
|
||||
}
|
||||
|
||||
public SetOnce<IParser<R>> Parser { get; }
|
||||
|
||||
public void Is(IParser<R> parser) {
|
||||
this.Parser.Value = parser;
|
||||
}
|
||||
|
||||
public IParserResult<R> Parse(ParserInput input)
|
||||
{
|
||||
var result = this.Parser.Value.Parse(input);
|
||||
result.Name = Name;
|
||||
return result;
|
||||
}
|
||||
|
||||
public String Name { get; }
|
||||
public String Rule => this.Parser.Value.ToString();
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public override String ToString() => this.Name;
|
||||
}
|
||||
|
||||
public class OptionalParser<R> : IParser<Option<R>> {
|
||||
public OptionalParser(IParser<R> parser) {
|
||||
this.Parser = parser;
|
||||
}
|
||||
public IParser<R> Parser { get; }
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParserResult<Option<R>> Parse(ParserInput input) {
|
||||
var result = this.Parser.Parse(input);
|
||||
if (result.IsSuccessful) {
|
||||
return ParserSucceeded.Create(new Some<R>(result.Result), result.Environment, result.Source);
|
||||
}
|
||||
return ParserSucceeded.Create(new None<R>(), input.Environment, input.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class OptionalParserWithDefault<R> : IParser<R> {
|
||||
public OptionalParserWithDefault(IParser<R> parser, R defaultValue) {
|
||||
this.Parser = parser;
|
||||
this.DefaultValue = defaultValue;
|
||||
}
|
||||
public IParser<R> Parser { get; }
|
||||
public R DefaultValue { get; }
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParserResult<R> Parse(ParserInput input) {
|
||||
var result = this.Parser.Parse(input);
|
||||
if (result.IsSuccessful) {
|
||||
return result;
|
||||
}
|
||||
return ParserSucceeded.Create(this.DefaultValue, input.Environment, input.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class AlwaysSucceedingParser<R> : IParser<R> {
|
||||
public AlwaysSucceedingParser(R result) {
|
||||
this.Result = result;
|
||||
}
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public R Result { get; }
|
||||
public IParserResult<R> Parse(ParserInput input) =>
|
||||
ParserSucceeded.Create(this.Result, input.Environment, input.Source);
|
||||
}
|
||||
|
||||
public class ParserThenCheck<R> : IParser<R> {
|
||||
public ParserThenCheck(IParser<R> parser, Predicate<IParserResult<R>> predicate) {
|
||||
this.Parser = parser;
|
||||
this.Predicate = predicate;
|
||||
}
|
||||
public IParser<R> Parser { get; }
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public Predicate<IParserResult<R>> Predicate { get; }
|
||||
public IParserResult<R> Parse(ParserInput input) {
|
||||
var result1 = this.Parser.Parse(input);
|
||||
if (result1.IsSuccessful && !this.Predicate(result1)) {
|
||||
return new ParserFailed<R>(result1);
|
||||
}
|
||||
return result1;
|
||||
}
|
||||
}
|
||||
|
||||
public class OrParser<R> : IParser<R> {
|
||||
public OrParser(ImmutableList<IParser<R>> parsers) {
|
||||
this.Parsers = parsers;
|
||||
}
|
||||
public ImmutableList<IParser<R>> Parsers { get; }
|
||||
public RuleCombining Combining => RuleCombining.OR;
|
||||
public IParserResult<R> Parse(ParserInput input) {
|
||||
IParserResult<R> result = null;
|
||||
List<IParserFailed> failed = new List<IParserFailed>();
|
||||
foreach (var parser in this.Parsers) {
|
||||
result = parser.Parse(input);
|
||||
if (result.IsSuccessful) {
|
||||
return result;
|
||||
} else
|
||||
{
|
||||
failed.Add((IParserFailed)result);
|
||||
}
|
||||
}
|
||||
return new ParserFailed<R>(input, failed);
|
||||
}
|
||||
}
|
||||
|
||||
//public class ParserOrParser<R> : IParser<R> {
|
||||
// public ParserOrParser(IParser<R> firstParser, IParser<R> secondParser) {
|
||||
// this.FirstParser = firstParser;
|
||||
// this.SecondParser = secondParser;
|
||||
// }
|
||||
// public IParser<R> FirstParser { get; }
|
||||
// public IParser<R> SecondParser { get; }
|
||||
// public RuleCombining Combining => RuleCombining.OR;
|
||||
// public IParserResult<R> Parse(ParserInput input) {
|
||||
// var result1 = this.FirstParser.Parse(input);
|
||||
// if (result1.IsSuccessful) {
|
||||
// return result1;
|
||||
// }
|
||||
// return this.SecondParser.Parse(input);
|
||||
// }
|
||||
//}
|
||||
|
||||
public class ZeroOrMoreParser<R> : IParser<ImmutableList<R>> {
|
||||
public ZeroOrMoreParser(IParser<R> parser) {
|
||||
this.Parser = parser;
|
||||
}
|
||||
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
|
||||
public IParser<R> Parser { get; }
|
||||
|
||||
public IParserResult<ImmutableList<R>> Parse(ParserInput input) {
|
||||
var list = ImmutableList<R>.Empty;
|
||||
IParserResult<R> curResult;
|
||||
while ((curResult = this.Parser.Parse(input)).IsSuccessful) {
|
||||
list = list.Add(curResult.Result);
|
||||
input = curResult.ToInput();
|
||||
}
|
||||
return ParserSucceeded.Create(list, input.Environment, input.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class OneOrMoreParser<R> : IParser<ImmutableList<R>> {
|
||||
public OneOrMoreParser(IParser<R> parser) {
|
||||
this.Parser = parser;
|
||||
}
|
||||
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParser<R> Parser { get; }
|
||||
|
||||
public IParserResult<ImmutableList<R>> Parse(ParserInput input) {
|
||||
var list = ImmutableList<R>.Empty;
|
||||
var curResult = this.Parser.Parse(input);
|
||||
if (!curResult.IsSuccessful) {
|
||||
return new ParserFailed<ImmutableList<R>>(curResult);
|
||||
}
|
||||
|
||||
IParserResult<R> lastSuccessfulResult;
|
||||
do {
|
||||
list = list.Add(curResult.Result);
|
||||
lastSuccessfulResult = curResult;
|
||||
curResult = this.Parser.Parse(lastSuccessfulResult.ToInput());
|
||||
} while (curResult.IsSuccessful);
|
||||
|
||||
return ParserSucceeded.Create(list, lastSuccessfulResult.Environment, lastSuccessfulResult.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class OneOrMoreParserForEntireUnit<R> : IParser<ImmutableList<R>>
|
||||
{
|
||||
public OneOrMoreParserForEntireUnit(IParser<R> parser)
|
||||
{
|
||||
this.Parser = parser;
|
||||
}
|
||||
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParser<R> Parser { get; }
|
||||
|
||||
public IParserResult<ImmutableList<R>> Parse(ParserInput input)
|
||||
{
|
||||
var list = ImmutableList<R>.Empty;
|
||||
var curResult = this.Parser.Parse(input);
|
||||
if (!curResult.IsSuccessful)
|
||||
{
|
||||
return new ParserFailed<ImmutableList<R>>(curResult);
|
||||
}
|
||||
|
||||
IParserResult<R> lastSuccessfulResult;
|
||||
do
|
||||
{
|
||||
list = list.Add(curResult.Result);
|
||||
lastSuccessfulResult = curResult;
|
||||
curResult = this.Parser.Parse(lastSuccessfulResult.ToInput());
|
||||
} while (curResult.IsSuccessful);
|
||||
|
||||
if (!lastSuccessfulResult.Source.Skip(1).Any())
|
||||
return ParserSucceeded.Create(list, lastSuccessfulResult.Environment, lastSuccessfulResult.Source);
|
||||
|
||||
return new ParserFailed<ImmutableList<R>>(curResult);
|
||||
}
|
||||
}
|
||||
|
||||
public class OneOrMoreParserWithSeparator<R> : IParser<ImmutableList<R>> {
|
||||
public OneOrMoreParserWithSeparator(IConsumer separatorConsumer, IParser<R> elementParser) {
|
||||
this.SeparatorConsumer = separatorConsumer;
|
||||
this.ElementParser = elementParser;
|
||||
}
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IConsumer SeparatorConsumer { get; }
|
||||
public IParser<R> ElementParser { get; }
|
||||
|
||||
public IParserResult<ImmutableList<R>> Parse(ParserInput input) {
|
||||
var list = ImmutableList<R>.Empty;
|
||||
var curResult = this.ElementParser.Parse(input);
|
||||
if (!curResult.IsSuccessful) {
|
||||
return new ParserFailed<ImmutableList<R>>(curResult);
|
||||
}
|
||||
IParserResult<R> lastElementResult;
|
||||
|
||||
do {
|
||||
list = list.Add(curResult.Result);
|
||||
lastElementResult = curResult;
|
||||
|
||||
var separatorResult = this.SeparatorConsumer.Consume(curResult.ToInput());
|
||||
if (!separatorResult.IsSuccessful) {
|
||||
break;
|
||||
}
|
||||
curResult = this.ElementParser.Parse(separatorResult.ToInput());
|
||||
} while (curResult.IsSuccessful);
|
||||
|
||||
return ParserSucceeded.Create(list, lastElementResult.Environment, lastElementResult.Source);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A consumer consumes one or several tokens, and doesn't produce any result.
|
||||
/// </summary>
|
||||
public interface IConsumer {
|
||||
IParserResult Consume(ParserInput input);
|
||||
}
|
||||
|
||||
public class NamedConsumer : IConsumer {
|
||||
public NamedConsumer() {
|
||||
this.Consumer = new SetOnce<IConsumer>();
|
||||
}
|
||||
|
||||
public SetOnce<IConsumer> Consumer { get; }
|
||||
|
||||
public void Is(IConsumer consumer) {
|
||||
this.Consumer.Value = consumer;
|
||||
}
|
||||
|
||||
public IParserResult Consume(ParserInput input)
|
||||
{
|
||||
var result = this.Consumer.Value.Consume(input);
|
||||
result.Name = ToString();
|
||||
return result;
|
||||
}
|
||||
|
||||
public override String ToString() {
|
||||
if (this.Consumer.IsSet) {
|
||||
return this.Consumer.Value.ToString();
|
||||
}
|
||||
return "<Unset Consumer>";
|
||||
}
|
||||
}
|
||||
|
||||
public class OptionalConsumer : IParser<Boolean> {
|
||||
public OptionalConsumer(IConsumer consumer) {
|
||||
this.Consumer = consumer;
|
||||
}
|
||||
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
|
||||
public IConsumer Consumer { get; }
|
||||
|
||||
public IParserResult<Boolean> Parse(ParserInput input) {
|
||||
var result = this.Consumer.Consume(input);
|
||||
if (result.IsSuccessful) {
|
||||
return ParserSucceeded.Create(true, result.Environment, result.Source);
|
||||
}
|
||||
return ParserSucceeded.Create(false, input.Environment, input.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class OrConsumer : IConsumer {
|
||||
public OrConsumer(ImmutableList<IConsumer> consumers) {
|
||||
this.Consumers = consumers;
|
||||
}
|
||||
|
||||
public ImmutableList<IConsumer> Consumers { get; }
|
||||
|
||||
public IParserResult Consume(ParserInput input) {
|
||||
IParserResult result = null;
|
||||
List<IParserFailed> failed = new List<IParserFailed>();
|
||||
foreach (var consumer in this.Consumers) {
|
||||
result = consumer.Consume(input);
|
||||
if (result.IsSuccessful) {
|
||||
return result;
|
||||
} else
|
||||
{
|
||||
failed.Add((IParserFailed)result);
|
||||
}
|
||||
}
|
||||
return new ParserFailed(input, failed);
|
||||
}
|
||||
}
|
||||
|
||||
//public class ConsumerOrConsumer : IConsumer {
|
||||
// public ConsumerOrConsumer(IConsumer firstConsumer, IConsumer secondConsumer) {
|
||||
// this.FirstConsumer = firstConsumer;
|
||||
// this.SecondConsumer = secondConsumer;
|
||||
// }
|
||||
|
||||
// public IConsumer FirstConsumer { get; }
|
||||
// public IConsumer SecondConsumer { get; }
|
||||
|
||||
// public IParserResult Consume(ParserInput input) {
|
||||
// var result1 = this.FirstConsumer.Consume(input);
|
||||
// if (!result1.IsSuccessful) {
|
||||
// return new ParserFailed();
|
||||
// }
|
||||
// return this.SecondConsumer.Consume(result1.ToInput());
|
||||
// }
|
||||
//}
|
||||
|
||||
public class EnvironmentTransformer : IConsumer {
|
||||
public EnvironmentTransformer(Func<ParserEnvironment, ParserEnvironment> transformer) {
|
||||
this.Transformer = transformer;
|
||||
}
|
||||
|
||||
public Func<ParserEnvironment, ParserEnvironment> Transformer { get; }
|
||||
|
||||
public IParserResult Consume(ParserInput input) {
|
||||
return ParserSucceeded.Create(this.Transformer(input.Environment), input.Source);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A transformer consumes zero or more tokens, and takes a previous result to produce a new result.
|
||||
/// </summary>
|
||||
public interface ITransformer<in S, out R> {
|
||||
IParserResult<R> Transform(S seed, ParserInput input);
|
||||
}
|
||||
|
||||
public class IdentityTransformer<R> : ITransformer<R, R> {
|
||||
public IParserResult<R> Transform(R seed, ParserInput input) =>
|
||||
ParserSucceeded.Create(seed, input.Environment, input.Source);
|
||||
}
|
||||
|
||||
public class SimpleTransformer<S, R> : ITransformer<S, R> {
|
||||
public SimpleTransformer(Func<S, R> transformFunc) {
|
||||
this.TransformFunc = transformFunc;
|
||||
}
|
||||
public Func<S, R> TransformFunc { get; }
|
||||
public IParserResult<R> Transform(S seed, ParserInput input)
|
||||
{
|
||||
var transform = this.TransformFunc(seed);
|
||||
var ret = ParserSucceeded.Create(transform, input.Environment, input.Source);
|
||||
var expr = transform as IStoredLineInfo;
|
||||
if (expr != null)
|
||||
{
|
||||
expr.Copy(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public class NamedTransformer<S, R> : ITransformer<S, R> {
|
||||
public NamedTransformer() {
|
||||
this.Transformer = new SetOnce<ITransformer<S, R>>();
|
||||
}
|
||||
|
||||
public SetOnce<ITransformer<S, R>> Transformer { get; }
|
||||
|
||||
public void Is(ITransformer<S, R> transformer) {
|
||||
this.Transformer.Value = transformer;
|
||||
}
|
||||
|
||||
public IParserResult<R> Transform(S seed, ParserInput input)
|
||||
{
|
||||
var result = this.Transformer.Value.Transform(seed, input);
|
||||
result.Name = ToString();
|
||||
return result;
|
||||
}
|
||||
|
||||
public override String ToString() {
|
||||
if (this.Transformer.IsSet) {
|
||||
return this.Transformer.Value.ToString();
|
||||
}
|
||||
return "<Unset transformer>";
|
||||
}
|
||||
}
|
||||
|
||||
public class OptionalTransformer<R> : ITransformer<R, R> {
|
||||
public OptionalTransformer(ITransformer<R, R> transformer) {
|
||||
this.Transformer = transformer;
|
||||
}
|
||||
public ITransformer<R, R> Transformer { get; }
|
||||
public IParserResult<R> Transform(R seed, ParserInput input) {
|
||||
var result = this.Transformer.Transform(seed, input);
|
||||
if (result.IsSuccessful) {
|
||||
return result;
|
||||
}
|
||||
return ParserSucceeded.Create(seed, input.Environment, input.Source);
|
||||
}
|
||||
}
|
||||
|
||||
public class OrTransformer<S, R> : ITransformer<S, R> {
|
||||
public OrTransformer(ImmutableList<ITransformer<S, R>> transformers) {
|
||||
this.Transformers = transformers;
|
||||
}
|
||||
|
||||
public ImmutableList<ITransformer<S, R>> Transformers { get; }
|
||||
|
||||
public IParserResult<R> Transform(S seed, ParserInput input) {
|
||||
List<IParserFailed> failed = new List<IParserFailed>();
|
||||
IParserResult<R> result = null;
|
||||
foreach (var transformer in this.Transformers) {
|
||||
result = transformer.Transform(seed, input);
|
||||
if (result.IsSuccessful) {
|
||||
return result;
|
||||
} else
|
||||
{
|
||||
failed.Add((IParserFailed)result);
|
||||
}
|
||||
}
|
||||
return new ParserFailed<R>(input, failed);
|
||||
}
|
||||
}
|
||||
|
||||
//public class TransformerOrTransformer<S, R> : ITransformer<S, R> {
|
||||
// public TransformerOrTransformer(ITransformer<S, R> firstTransformer, ITransformer<S, R> secondTransformer) {
|
||||
// this.FirstTransformer = firstTransformer;
|
||||
// this.SecondTransformer = secondTransformer;
|
||||
// }
|
||||
|
||||
// public ITransformer<S, R> FirstTransformer { get; }
|
||||
// public ITransformer<S, R> SecondTransformer { get; }
|
||||
|
||||
// public IParserResult<R> Transform(S seed, ParserInput input) {
|
||||
// var result1 = this.FirstTransformer.Transform(seed, input);
|
||||
// if (result1.IsSuccessful) {
|
||||
// return result1;
|
||||
// }
|
||||
// return this.SecondTransformer.Transform(seed, input);
|
||||
|
||||
// }
|
||||
|
||||
// public override String ToString() {
|
||||
// return this.FirstTransformer + " | " + this.SecondTransformer;
|
||||
// }
|
||||
//}
|
||||
|
||||
public class ResultTransformer<R> : ITransformer<R, R> {
|
||||
public ResultTransformer(Func<IParserResult<R>, IParserResult<R>> transformFunc) {
|
||||
this.TransformFunc = transformFunc;
|
||||
}
|
||||
|
||||
public Func<IParserResult<R>, IParserResult<R>> TransformFunc { get; }
|
||||
|
||||
public IParserResult<R> Transform(R seed, ParserInput input) =>
|
||||
this.TransformFunc(ParserSucceeded.Create(seed, input.Environment, input.Source));
|
||||
}
|
||||
|
||||
public class ZeroOrMoreTransformer<R> : ITransformer<R, R> {
|
||||
public ZeroOrMoreTransformer(ITransformer<R, R> transformer) {
|
||||
this.Transformer = transformer;
|
||||
}
|
||||
public ITransformer<R, R> Transformer { get; }
|
||||
public IParserResult<R> Transform(R seed, ParserInput input) {
|
||||
IParserResult<R> curResult = ParserSucceeded.Create(seed, input.Environment, input.Source);
|
||||
|
||||
IParserResult<R> lastSuccessfulResult;
|
||||
do {
|
||||
lastSuccessfulResult = curResult;
|
||||
curResult = this.Transformer.Transform(lastSuccessfulResult.Result, lastSuccessfulResult.ToInput());
|
||||
} while (curResult.IsSuccessful);
|
||||
|
||||
return lastSuccessfulResult;
|
||||
}
|
||||
}
|
||||
|
||||
public class OneOrMoreTransformer<R> : ITransformer<R, R> {
|
||||
public OneOrMoreTransformer(ITransformer<R, R> transformer) {
|
||||
this.Transformer = transformer;
|
||||
}
|
||||
public ITransformer<R, R> Transformer { get; }
|
||||
public IParserResult<R> Transform(R seed, ParserInput input) {
|
||||
var curResult = this.Transformer.Transform(seed, input);
|
||||
if (!curResult.IsSuccessful) {
|
||||
return new ParserFailed<R>(curResult);
|
||||
}
|
||||
|
||||
IParserResult<R> lastSuccessfulResult;
|
||||
do {
|
||||
lastSuccessfulResult = curResult;
|
||||
curResult = this.Transformer.Transform(lastSuccessfulResult.Result, lastSuccessfulResult.ToInput());
|
||||
} while (curResult.IsSuccessful);
|
||||
|
||||
return lastSuccessfulResult;
|
||||
}
|
||||
}
|
||||
|
||||
public class OperatorConsumer : IConsumer {
|
||||
public OperatorConsumer(OperatorVal operatorVal) {
|
||||
this.OperatorVal = operatorVal;
|
||||
}
|
||||
|
||||
public static IConsumer Create(OperatorVal operatorVal) =>
|
||||
new OperatorConsumer(operatorVal);
|
||||
|
||||
public OperatorVal OperatorVal { get; }
|
||||
|
||||
public IParserResult Consume(ParserInput input) {
|
||||
if ((input.Source.First() as TokenOperator)?.Val == this.OperatorVal) {
|
||||
return ParserSucceeded.Create(input.Environment, input.Source.Skip(1));
|
||||
}
|
||||
return new ParserFailed(input);
|
||||
}
|
||||
}
|
||||
|
||||
public class IdentifierParser : IParser<String> {
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
public IParserResult<String> Parse(ParserInput input) {
|
||||
var token = input.Source.First() as TokenIdentifier;
|
||||
if (token == null) {
|
||||
return new ParserFailed<String>(input);
|
||||
}
|
||||
return ParserSucceeded.Create(token.Val, input.Environment, input.Source.Skip(1));
|
||||
}
|
||||
}
|
||||
|
||||
public class KeywordConsumer : IConsumer {
|
||||
public KeywordConsumer(KeywordVal keywordVal) {
|
||||
this.KeywordVal = keywordVal;
|
||||
}
|
||||
public KeywordVal KeywordVal { get; }
|
||||
public static KeywordConsumer Create(KeywordVal keywordVal) =>
|
||||
new KeywordConsumer(keywordVal);
|
||||
public IParserResult Consume(ParserInput input) {
|
||||
if ((input.Source.First() as TokenKeyword)?.Val == this.KeywordVal) {
|
||||
return ParserSucceeded.Create(input.Environment, input.Source.Skip(1));
|
||||
}
|
||||
return new ParserFailed(input);
|
||||
}
|
||||
}
|
||||
|
||||
public class KeywordParser<R> : IParser<R> {
|
||||
public KeywordParser(KeywordVal keywordVal, R result) {
|
||||
this.KeywordVal = keywordVal;
|
||||
this.Result = result;
|
||||
}
|
||||
|
||||
public RuleCombining Combining => RuleCombining.NONE;
|
||||
|
||||
public KeywordVal KeywordVal { get; }
|
||||
public R Result { get; }
|
||||
|
||||
public IParserResult<R> Parse(ParserInput input) {
|
||||
if ((input.Source.First() as TokenKeyword)?.Val == this.KeywordVal) {
|
||||
return ParserSucceeded.Create(this.Result, input.Environment, input.Source.Skip(1));
|
||||
}
|
||||
return new ParserFailed<R>(input);
|
||||
}
|
||||
}
|
||||
|
||||
public class KeywordParser {
|
||||
public static KeywordParser<R> Create<R>(KeywordVal keywordVal, R result) =>
|
||||
new KeywordParser<R>(keywordVal, result);
|
||||
}
|
||||
|
||||
}
|
170
LibIFPSCC/Parser/ParserCombinator.cs
Normal file
@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Parsing {
|
||||
public static class ParserCombinator {
|
||||
|
||||
/// <summary>
|
||||
/// Parser then Parser = Parser
|
||||
/// ( => R1 ) then ( => R2 ) is ( => Tuple[R2, R1] )
|
||||
/// </summary>
|
||||
public static IParser<Tuple<R2, R1>> Then<R1, R2>(this IParser<R1> firstParser, IParser<R2> secondParser) =>
|
||||
new ParserThenParser<R1, R2>(firstParser, secondParser);
|
||||
|
||||
/// <summary>
|
||||
/// Parser then Consumer = Parser
|
||||
/// ( => R ) then ( => ) is ( => R )
|
||||
/// </summary>
|
||||
public static IParser<R> Then<R>(this IParser<R> parser, IConsumer consumer) =>
|
||||
new ParserThenConsumer<R>(parser, consumer);
|
||||
|
||||
/// <summary>
|
||||
/// Parser then Transformer = Parser
|
||||
/// ( => R1 ) then ( R1 => R2 ) is ( => R2 )
|
||||
/// </summary>
|
||||
public static IParser<R2> Then<R1, R2>(this IParser<R1> parser, ITransformer<R1, R2> transformer) =>
|
||||
new ParserThenTransformer<R1, R2>(parser, transformer);
|
||||
|
||||
public static IParser<R> Then<S, R>(this IParser<S> parser, Func<S, R> transformer) =>
|
||||
parser.Then(new SimpleTransformer<S, R>(transformer));
|
||||
|
||||
public static IParser<R> Then<I1, I2, R>(this IParser<Tuple<I2, I1>> parser, Func<I1, I2, R> transformer) =>
|
||||
parser.Then(_ => transformer(_.Item2, _.Item1));
|
||||
|
||||
public static IParser<R> Then<I1, I2, I3, R>(this IParser<Tuple<I3, Tuple<I2, I1>>> parser, Func<I1, I2, I3, R> transformer) =>
|
||||
parser.Then(_ => transformer(_.Item2.Item2, _.Item2.Item1, _.Item1));
|
||||
|
||||
public static IParser<R> Then<I1, I2, I3, I4, R>(this IParser<Tuple<I4, Tuple<I3, Tuple<I2, I1>>>> parser, Func<I1, I2, I3, I4, R> transformer) =>
|
||||
parser.Then(_ => transformer(_.Item2.Item2.Item2, _.Item2.Item2.Item1, _.Item2.Item1, _.Item1));
|
||||
|
||||
/// <summary>
|
||||
/// Consumer then Parser = Parser
|
||||
/// ( => ) then ( => R ) is ( => R )
|
||||
/// </summary>
|
||||
public static IParser<R> Then<R>(this IConsumer consumer, IParser<R> parser) =>
|
||||
new ConsumerThenParser<R>(consumer, parser);
|
||||
|
||||
/// <summary>
|
||||
/// Consumer then Consumer = Consumer
|
||||
/// ( => ) then ( => ) is ( => )
|
||||
/// </summary>
|
||||
public static IConsumer Then(this IConsumer firstConsumer, IConsumer secondConsumer) =>
|
||||
new ConsumerThenConsumer(firstConsumer, secondConsumer);
|
||||
|
||||
/// <summary>
|
||||
/// Transformer then Parser = Transformer
|
||||
/// ( S => R1 ) then ( => R2 ) is ( S => Tuple[R2, R1] )
|
||||
/// </summary>
|
||||
public static ITransformer<S, Tuple<R2, R1>> Then<S, R1, R2>(this ITransformer<S, R1> transformer, IParser<R2> parser) =>
|
||||
new TransformerThenParser<S, R1, R2>(transformer, parser);
|
||||
|
||||
/// <summary>
|
||||
/// Transformer then Consumer = Transformer
|
||||
/// ( S => R ) then ( => ) is ( S => R )
|
||||
/// </summary>
|
||||
public static ITransformer<S, R> Then<S, R>(this ITransformer<S, R> transformer, IConsumer consumer) =>
|
||||
new TransformerThenConsumer<S, R>(transformer, consumer);
|
||||
|
||||
/// <summary>
|
||||
/// Transformer then Transformer = Transformer
|
||||
/// ( S => I ) then ( I => R ) is ( S => R )
|
||||
/// </summary>
|
||||
public static ITransformer<S, R> Then<S, I, R>(this ITransformer<S, I> firstTransformer, ITransformer<I, R> secondTransformer) =>
|
||||
new TransformerThenTransformer<S, I, R>(firstTransformer, secondTransformer);
|
||||
|
||||
public static ITransformer<S, R> Then<S, I, R>(this ITransformer<S, I> firstTransformer, Func<I, R> secondTransformer) =>
|
||||
firstTransformer.Then(new SimpleTransformer<I, R>(secondTransformer));
|
||||
|
||||
public static ITransformer<S, R> Then<S, I1, I2, R>(this ITransformer<S, Tuple<I2, I1>> firstTransformer, Func<I1, I2, R> secondTransformer) =>
|
||||
firstTransformer.Then(_ => secondTransformer(_.Item2, _.Item1));
|
||||
|
||||
public static ITransformer<S, R> Then<S, I1, I2, I3, R>(this ITransformer<S, Tuple<I3, Tuple<I2, I1>>> firstTransformer, Func<I1, I2, I3, R> secondTransformer) =>
|
||||
firstTransformer.Then(_ => secondTransformer(_.Item2.Item2, _.Item2.Item1, _.Item1));
|
||||
|
||||
/// <summary>
|
||||
/// Create an optional parser.
|
||||
/// </summary>
|
||||
public static IParser<Option<R>> Optional<R>(this IParser<R> parser) =>
|
||||
new OptionalParser<R>(parser);
|
||||
|
||||
public static IParser<R> Optional<R>(this IParser<R> parser, R defaultValue) =>
|
||||
new OptionalParserWithDefault<R>(parser, defaultValue);
|
||||
|
||||
public static OrConsumer Either(IConsumer consumer) =>
|
||||
new OrConsumer(ImmutableList.Create(consumer));
|
||||
|
||||
public static OrConsumer Or(ImmutableList<IConsumer> firstConsumer, IConsumer secondConsumer) =>
|
||||
new OrConsumer(firstConsumer.Add(secondConsumer));
|
||||
|
||||
/// <summary>
|
||||
/// Consumer or Consumer
|
||||
/// </summary>
|
||||
//public static IConsumer Or(this IConsumer firstConsumer, IConsumer secondConsumer) =>
|
||||
// new ConsumerOrConsumer(firstConsumer, secondConsumer);
|
||||
|
||||
/// <summary>
|
||||
/// ( => R ) check Predicate[IParserResult[R]] is ( => R)
|
||||
/// </summary>
|
||||
public static IParser<R> Check<R>(this IParser<R> parser, Predicate<IParserResult<R>> predicate) =>
|
||||
new ParserThenCheck<R>(parser, predicate);
|
||||
|
||||
public static IParser<R> TransformResult<R>(this IParser<R> parser, Func<IParserResult<R>, IParserResult<R>> transformFunc) =>
|
||||
Then(parser, new ResultTransformer<R>(transformFunc));
|
||||
|
||||
public static IParser<R> TransformEnvironment<R>(this IParser<R> parser,
|
||||
Func<ParserEnvironment, ParserEnvironment> transformFunc) =>
|
||||
parser.Then(new EnvironmentTransformer(transformFunc));
|
||||
|
||||
public static IConsumer TransformEnvironment(this IConsumer consumer,
|
||||
Func<ParserEnvironment, ParserEnvironment> transformFunc) =>
|
||||
consumer.Then(new EnvironmentTransformer(transformFunc));
|
||||
|
||||
public static ITransformer<R, R> Optional<R>(this ITransformer<R, R> transformer) =>
|
||||
new OptionalTransformer<R>(transformer);
|
||||
|
||||
public static OrTransformer<S, R> Either<S, R>(ITransformer<S, R> transformer) =>
|
||||
new OrTransformer<S, R>(ImmutableList.Create(transformer));
|
||||
|
||||
public static OrTransformer<S, R> Or<S, R>(this OrTransformer<S, R> firstTransformer, ITransformer<S, R> secondTransformer) =>
|
||||
new OrTransformer<S, R>(firstTransformer.Transformers.Add(secondTransformer));
|
||||
|
||||
//public static ITransformer<S, R> Or<S, R>(this ITransformer<S, R> firstTransformer, ITransformer<S, R> secondTransformer) =>
|
||||
// new TransformerOrTransformer<S, R>(firstTransformer, secondTransformer);
|
||||
|
||||
//public static IParser<R> Or<R>(this IParser<R> firstParser, IParser<R> secondParser) =>
|
||||
// new ParserOrParser<R>(firstParser, secondParser);
|
||||
|
||||
public static OrParser<R> Either<R>(IParser<R> parser) =>
|
||||
new OrParser<R>(ImmutableList.Create(parser));
|
||||
|
||||
public static OrParser<R> Or<R>(this OrParser<R> firstParser, IParser<R> secondParser) =>
|
||||
new OrParser<R>(firstParser.Parsers.Add(secondParser));
|
||||
|
||||
public static IParser<Boolean> Optional(this IConsumer consumer) =>
|
||||
new OptionalConsumer(consumer);
|
||||
|
||||
public static ITransformer<R, R> ZeroOrMore<R>(this ITransformer<R, R> transformer) =>
|
||||
new ZeroOrMoreTransformer<R>(transformer);
|
||||
|
||||
public static ITransformer<R, R> OneOrMore<R>(this ITransformer<R, R> transformer) =>
|
||||
new OneOrMoreTransformer<R>(transformer);
|
||||
|
||||
public static IParser<ImmutableList<R>> ZeroOrMore<R>(this IParser<R> parser) =>
|
||||
new ZeroOrMoreParser<R>(parser);
|
||||
|
||||
public static IParser<ImmutableList<R>> OneOrMore<R>(this IParser<R> parser) =>
|
||||
new OneOrMoreParser<R>(parser);
|
||||
|
||||
public static IParser<ImmutableList<R>> OneOrMoreForEntireUnit<R>(this IParser<R> parser) =>
|
||||
new OneOrMoreParserForEntireUnit<R>(parser);
|
||||
|
||||
public static IParser<ImmutableList<R>> OneOrMore<R>(this IParser<R> elementParser, IConsumer separatorConsumer) =>
|
||||
new OneOrMoreParserWithSeparator<R>(separatorConsumer, elementParser);
|
||||
|
||||
public static ITransformer<R, R> Given<R>() =>
|
||||
new IdentityTransformer<R>();
|
||||
|
||||
public static ITransformer<Tuple<R2, R1>, Tuple<R2, R1>> Given<R1, R2>() =>
|
||||
Given<Tuple<R2, R1>>();
|
||||
}
|
||||
}
|
463
LibIFPSCC/Parser/ParserUtils.cs
Normal file
@ -0,0 +1,463 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using LexicalAnalysis;
|
||||
using AST;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Parsing {
|
||||
|
||||
/// <summary>
|
||||
/// A minimal environment solely for parsing, intended to resolve ambiguity.
|
||||
/// </summary>
|
||||
public sealed class ParserEnvironment {
|
||||
private ParserEnvironment(ImmutableStack<Scope> scopes) {
|
||||
this.Scopes = scopes;
|
||||
}
|
||||
|
||||
public ParserEnvironment()
|
||||
: this(ImmutableStack.Create(new Scope())) { }
|
||||
|
||||
public ParserEnvironment InScope() =>
|
||||
new ParserEnvironment(this.Scopes.Push(new Scope()));
|
||||
|
||||
public ParserEnvironment OutScope() =>
|
||||
new ParserEnvironment(this.Scopes.Pop());
|
||||
|
||||
public ParserEnvironment AddSymbol(String name, StorageClsSpec storageClsSpec) =>
|
||||
new ParserEnvironment(
|
||||
this.Scopes.Pop().Push(
|
||||
this.Scopes.Peek().AddSymbol(name, storageClsSpec)
|
||||
)
|
||||
);
|
||||
|
||||
public Boolean IsTypedefName(String name) {
|
||||
foreach (var scope in this.Scopes) {
|
||||
if (scope.Symbols.ContainsKey(name)) {
|
||||
return scope.Symbols[name] == StorageClsSpec.TYPEDEF;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class Scope {
|
||||
public Scope()
|
||||
: this(ImmutableDictionary<String, StorageClsSpec>.Empty) { }
|
||||
|
||||
private Scope(ImmutableDictionary<String, StorageClsSpec> symbols) {
|
||||
this.Symbols = symbols;
|
||||
}
|
||||
|
||||
public Scope AddSymbol(String name, StorageClsSpec storageClsSpec) =>
|
||||
new Scope(this.Symbols.Add(name, storageClsSpec));
|
||||
|
||||
public ImmutableDictionary<String, StorageClsSpec> Symbols { get; }
|
||||
}
|
||||
|
||||
private ImmutableStack<Scope> Scopes { get; }
|
||||
}
|
||||
|
||||
/// <summary>Bypasses a number of elements in a sequence and then returns the remaining elements.</summary>
|
||||
/// <typeparam name="TSource">The type of the elements of the sequence.</typeparam>
|
||||
/// <notes>Allows for stacking many Skips at once without overflowing the stack, unlike the version in LINQ</notes>
|
||||
public sealed class SkipEnumerable<T> : IEnumerable<T>
|
||||
{
|
||||
private readonly IEnumerable<T> source;
|
||||
private uint count = 0;
|
||||
|
||||
internal SkipEnumerable(IEnumerable<T> source)
|
||||
{
|
||||
this.source = source;
|
||||
// Merge any downlevel SkipEnumerables into this one.
|
||||
var skip = source as SkipEnumerable<T>;
|
||||
while (skip != null)
|
||||
{
|
||||
this.source = skip.source;
|
||||
count += skip.count;
|
||||
skip = this.source as SkipEnumerable<T>;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return new Enumerator(source.GetEnumerator(), count);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public SkipEnumerable<T> Skip(uint count)
|
||||
{
|
||||
if (count == 0) return this;
|
||||
var ret = new SkipEnumerable<T>(source);
|
||||
ret.count = this.count + count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public SkipEnumerable<T> Skip(int count)
|
||||
{
|
||||
if (count <= 0) return this;
|
||||
return Skip((uint)count);
|
||||
}
|
||||
|
||||
private sealed class Enumerator : IEnumerator<T>
|
||||
{
|
||||
private readonly IEnumerator<T> source;
|
||||
private readonly uint count;
|
||||
|
||||
internal Enumerator(IEnumerator<T> source, uint count)
|
||||
{
|
||||
this.source = source;
|
||||
this.count = count;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public T Current => source.Current;
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose() => source.Dispose();
|
||||
|
||||
public bool MoveNext() => source.MoveNext();
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
source.Reset();
|
||||
for (int i = 0; i < count; i++) source.MoveNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The input Type for every parsing function.
|
||||
/// </summary>
|
||||
public sealed class ParserInput {
|
||||
public ParserInput(ParserEnvironment environment, SkipEnumerable<Token> source) {
|
||||
this.Environment = environment;
|
||||
this.Source = source;
|
||||
}
|
||||
|
||||
public ParserInput(ParserEnvironment environment, IEnumerable<Token> source)
|
||||
: this(environment, new SkipEnumerable<Token>(source))
|
||||
{
|
||||
}
|
||||
public ParserEnvironment Environment { get; }
|
||||
public SkipEnumerable<Token> Source { get; }
|
||||
public IParserResult<R> Parse<R>(IParser<R> parser) =>
|
||||
parser.Parse(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A parser result with/without content.
|
||||
/// </summary>
|
||||
public interface IParserResult : ILineInfo {
|
||||
ParserInput ToInput();
|
||||
|
||||
Boolean IsSuccessful { get; }
|
||||
|
||||
ParserEnvironment Environment { get; }
|
||||
|
||||
IEnumerable<Token> Source { get; }
|
||||
|
||||
string Name { get; set; }
|
||||
}
|
||||
|
||||
public interface IParserFailed : IParserResult
|
||||
{
|
||||
IReadOnlyList<IParserFailed> InnerFailures { get; }
|
||||
|
||||
IEnumerable<IParserFailed> WalkInnerFailures(bool named = false);
|
||||
|
||||
IEnumerable<StringBuilder> ToStrings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A failed result.
|
||||
/// </summary>
|
||||
public sealed class ParserFailed : IParserFailed {
|
||||
public ParserInput ToInput() {
|
||||
throw new InvalidOperationException("Parser failed, can't construct input.");
|
||||
}
|
||||
|
||||
public Boolean IsSuccessful => false;
|
||||
|
||||
public ParserEnvironment Environment {
|
||||
get {
|
||||
throw new NotSupportedException("Parser failed, can't get environment.");
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Token> Source {
|
||||
get {
|
||||
throw new NotSupportedException("Parser failed, can't get source.");
|
||||
}
|
||||
}
|
||||
|
||||
public int Line { get; }
|
||||
public int Column { get; }
|
||||
|
||||
private string m_Name = string.Empty;
|
||||
public string Name
|
||||
{
|
||||
get => m_Name;
|
||||
set
|
||||
{
|
||||
if (m_Name == string.Empty) m_Name = value;
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IParserFailed> InnerFailures { get; } = null;
|
||||
|
||||
public IEnumerable<IParserFailed> WalkInnerFailures(bool named = false)
|
||||
{
|
||||
var inner = InnerFailures;
|
||||
if (inner == null) yield break;
|
||||
foreach (var item in inner)
|
||||
{
|
||||
if (named && !string.IsNullOrEmpty(item.Name)) yield return item;
|
||||
foreach (var innerItem in item.WalkInnerFailures(named)) yield return innerItem;
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
public IEnumerable<StringBuilder> ToStrings()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(Name)) sb.AppendFormat("{0}: ", Name);
|
||||
sb.AppendFormat("line {0}, column {1}", Line, Column);
|
||||
yield return sb;
|
||||
|
||||
if (InnerFailures == null) yield break;
|
||||
|
||||
foreach (var inner in InnerFailures)
|
||||
{
|
||||
foreach (var innerSb in inner.ToStrings())
|
||||
{
|
||||
innerSb.Insert(0, " ");
|
||||
yield return innerSb;
|
||||
}
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var innerSb in ToStrings())
|
||||
{
|
||||
sb.Append(innerSb);
|
||||
sb.AppendLine();
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ParserFailed(int line, int column)
|
||||
{
|
||||
Line = line;
|
||||
Column = column;
|
||||
}
|
||||
public ParserFailed(IParserResult result) : this(result.Line, result.Column) {
|
||||
var failure = result as IParserFailed;
|
||||
if (failure != null)
|
||||
{
|
||||
InnerFailures = new List<IParserFailed>() { failure }.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public ParserFailed(ParserInput input, List<IParserFailed> inner) : this(input)
|
||||
{
|
||||
InnerFailures = inner.AsReadOnly();
|
||||
}
|
||||
public ParserFailed(ParserInput input) : this(input.Source) { }
|
||||
|
||||
public ParserFailed(IEnumerable<Token> source) : this(source.First()) { }
|
||||
|
||||
public ParserFailed(Token token) : this(token.Line, token.Column) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A succeeded result.
|
||||
/// </summary>
|
||||
public sealed class ParserSucceeded : IParserResult {
|
||||
public ParserSucceeded(ParserEnvironment environment, IEnumerable<Token> source) {
|
||||
this.Environment = environment;
|
||||
this.Source = source;
|
||||
}
|
||||
|
||||
public Boolean IsSuccessful => true;
|
||||
|
||||
public ParserInput ToInput() => new ParserInput(this.Environment, this.Source);
|
||||
|
||||
public ParserEnvironment Environment { get; }
|
||||
|
||||
public IEnumerable<Token> Source { get; }
|
||||
|
||||
public int Line => Source.First().Line;
|
||||
public int Column => Source.First().Column;
|
||||
|
||||
private string m_Name = string.Empty;
|
||||
public string Name
|
||||
{
|
||||
get => m_Name;
|
||||
set
|
||||
{
|
||||
if (m_Name == string.Empty) m_Name = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static ParserSucceeded Create(ParserEnvironment environment, IEnumerable<Token> source) =>
|
||||
new ParserSucceeded(environment, source);
|
||||
|
||||
public static ParserSucceeded<R> Create<R>(R result, ParserEnvironment environment, IEnumerable<Token> source) =>
|
||||
new ParserSucceeded<R>(result, environment, source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A parser result with content.
|
||||
/// </summary>
|
||||
public interface IParserResult<out R> : IParserResult {
|
||||
R Result { get; }
|
||||
}
|
||||
|
||||
public sealed class ParserFailed<R> : IParserResult<R>, IParserFailed {
|
||||
public ParserInput ToInput() {
|
||||
throw new InvalidOperationException("Parser failed, can't construct input.");
|
||||
}
|
||||
|
||||
public Boolean IsSuccessful => false;
|
||||
|
||||
public R Result {
|
||||
get {
|
||||
throw new NotSupportedException("Parser failed, can't get result.");
|
||||
}
|
||||
}
|
||||
|
||||
public ParserEnvironment Environment {
|
||||
get {
|
||||
throw new NotSupportedException("Parser failed, can't get environment.");
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Token> Source {
|
||||
get {
|
||||
throw new NotSupportedException("Parser failed, can't get source.");
|
||||
}
|
||||
}
|
||||
|
||||
public int Line { get; }
|
||||
public int Column { get; }
|
||||
|
||||
private string m_Name = string.Empty;
|
||||
public string Name
|
||||
{
|
||||
get => m_Name;
|
||||
set
|
||||
{
|
||||
if (m_Name == string.Empty) m_Name = value;
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IParserFailed> InnerFailures { get; } = null;
|
||||
|
||||
public IEnumerable<IParserFailed> WalkInnerFailures(bool named = false)
|
||||
{
|
||||
var inner = InnerFailures;
|
||||
if (inner == null) yield break;
|
||||
foreach (var item in inner)
|
||||
{
|
||||
if (!named || !string.IsNullOrEmpty(item.Name)) yield return item;
|
||||
foreach (var innerItem in item.WalkInnerFailures(named)) yield return innerItem;
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
public IEnumerable<StringBuilder> ToStrings()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(Name)) sb.AppendFormat("{0}: ", Name);
|
||||
sb.AppendFormat("line {0}, column {1}", Line, Column);
|
||||
yield return sb;
|
||||
|
||||
if (InnerFailures == null) yield break;
|
||||
|
||||
foreach (var inner in InnerFailures)
|
||||
{
|
||||
foreach (var innerSb in inner.ToStrings())
|
||||
{
|
||||
innerSb.Insert(0, " ");
|
||||
yield return innerSb;
|
||||
}
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var innerSb in ToStrings())
|
||||
{
|
||||
sb.Append(innerSb);
|
||||
sb.AppendLine();
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ParserFailed(int line, int column)
|
||||
{
|
||||
Line = line;
|
||||
Column = column;
|
||||
}
|
||||
|
||||
public ParserFailed(IParserResult result) : this(result.Line, result.Column)
|
||||
{
|
||||
var failure = result as IParserFailed;
|
||||
if (failure != null)
|
||||
{
|
||||
InnerFailures = new List<IParserFailed>() { failure }.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public ParserFailed(ParserInput input, List<IParserFailed> inner) : this(input)
|
||||
{
|
||||
InnerFailures = inner.AsReadOnly();
|
||||
}
|
||||
|
||||
public ParserFailed(ParserInput input) : this(input.Source) { }
|
||||
|
||||
public ParserFailed(IEnumerable<Token> source) : this(source.First()) { }
|
||||
|
||||
public ParserFailed(Token token) : this(token.Line, token.Column) { }
|
||||
}
|
||||
|
||||
public sealed class ParserSucceeded<R> : IParserResult<R> {
|
||||
public ParserSucceeded(R result, ParserEnvironment environment, IEnumerable<Token> source) {
|
||||
this.Result = result;
|
||||
this.Environment = environment;
|
||||
this.Source = source;
|
||||
}
|
||||
|
||||
public ParserInput ToInput() => new ParserInput(this.Environment, this.Source);
|
||||
|
||||
public Boolean IsSuccessful => true;
|
||||
|
||||
public R Result { get; }
|
||||
|
||||
public ParserEnvironment Environment { get; }
|
||||
|
||||
public IEnumerable<Token> Source { get; }
|
||||
|
||||
private string m_Name = string.Empty;
|
||||
public string Name
|
||||
{
|
||||
get => m_Name;
|
||||
set
|
||||
{
|
||||
if (m_Name == string.Empty) m_Name = value;
|
||||
}
|
||||
}
|
||||
public int Line => Source.First().Line;
|
||||
public int Column => Source.First().Column;
|
||||
}
|
||||
|
||||
}
|
244
LibIFPSCC/Parser/Statements.cs
Normal file
@ -0,0 +1,244 @@
|
||||
using System.Collections.Immutable;
|
||||
using AST;
|
||||
using static Parsing.ParserCombinator;
|
||||
|
||||
namespace Parsing {
|
||||
public partial class CParsers {
|
||||
|
||||
/// <summary>
|
||||
/// statement
|
||||
/// : labeled-statement
|
||||
/// | compound-statement
|
||||
/// | expression-statement
|
||||
/// | selection-statement
|
||||
/// | iteration-statement
|
||||
/// | jump-statement
|
||||
/// </summary>
|
||||
public static NamedParser<Stmt>
|
||||
Statement = new NamedParser<Stmt>("statement");
|
||||
|
||||
/// <summary>
|
||||
/// jump-statement
|
||||
/// : 'goto' identifier ';'
|
||||
/// | 'continue' ';'
|
||||
/// | 'break' ';'
|
||||
/// | 'return' [expression]? ';'
|
||||
/// </summary>
|
||||
public static NamedParser<Stmt>
|
||||
JumpStatement = new NamedParser<Stmt>("jump-statement");
|
||||
|
||||
/// <summary>
|
||||
/// compound-statement
|
||||
/// : '{' [declaration-list]? [statement-list]? '}'
|
||||
/// </summary>
|
||||
public static NamedParser<Stmt>
|
||||
CompoundStatement = new NamedParser<Stmt>("compound-statement");
|
||||
|
||||
/// <summary>
|
||||
/// declaration-list
|
||||
/// : [declaration]+
|
||||
/// </summary>
|
||||
public static NamedParser<ImmutableList<Decln>>
|
||||
DeclarationList = new NamedParser<ImmutableList<Decln>>("declaration-list");
|
||||
|
||||
/// <summary>
|
||||
/// statement-list
|
||||
/// : [statement]+
|
||||
/// </summary>
|
||||
public static NamedParser<ImmutableList<Stmt>>
|
||||
StatementList = new NamedParser<ImmutableList<Stmt>>("statement-list");
|
||||
|
||||
/// <summary>
|
||||
/// expression-statement
|
||||
/// : [expression]? ';'
|
||||
/// </summary>
|
||||
public static NamedParser<Stmt>
|
||||
ExpressionStatement = new NamedParser<Stmt>("expression-statement");
|
||||
|
||||
/// <summary>
|
||||
/// iteration-statement
|
||||
/// : 'while' '(' expression ')' statement
|
||||
/// | 'do' statement 'while' '(' expression ')' ';'
|
||||
/// | 'for' '(' [expression]? ';' [expression]? ';' [expression]? ')' statement
|
||||
/// </summary>
|
||||
public static NamedParser<Stmt>
|
||||
IterationStatement = new NamedParser<Stmt>("iteration-statement");
|
||||
|
||||
/// <summary>
|
||||
/// selection-statement
|
||||
/// : 'if' '(' expression ')' statement 'else' statement
|
||||
/// | 'if' '(' expression ')' statement
|
||||
/// | 'switch' '(' expression ')' statement
|
||||
/// </summary>
|
||||
public static NamedParser<Stmt>
|
||||
SelectionStatement = new NamedParser<Stmt>("selection-statement");
|
||||
|
||||
/// <summary>
|
||||
/// labeled-statement
|
||||
/// : identifier ':' statement
|
||||
/// | 'case' constant-expression ':' statement
|
||||
/// | 'default' ':' statement
|
||||
/// </summary>
|
||||
public static NamedParser<Stmt>
|
||||
LabeledStatement = new NamedParser<Stmt>("labeled-statement");
|
||||
|
||||
public static void SetStatementRules() {
|
||||
|
||||
// statement
|
||||
// : labeled-statement
|
||||
// | compound-statement
|
||||
// | expression-statement
|
||||
// | selection-statement
|
||||
// | iteration-statement
|
||||
// | jump-statement
|
||||
Statement.Is(
|
||||
Either(LabeledStatement)
|
||||
.Or(CompoundStatement)
|
||||
.Or(ExpressionStatement)
|
||||
.Or(SelectionStatement)
|
||||
.Or(IterationStatement)
|
||||
.Or(JumpStatement)
|
||||
);
|
||||
|
||||
// jump-statement
|
||||
// : 'goto' identifier ';'
|
||||
// | 'continue' ';'
|
||||
// | 'break' ';'
|
||||
// | 'return' [expression]? ';'
|
||||
JumpStatement.Is(
|
||||
(
|
||||
Either(
|
||||
(Goto).Then(Identifier).Then(GotoStmt.Create)
|
||||
).Or(Continue)
|
||||
.Or(Break)
|
||||
.Or((Return).Then(Expression.Optional()).Then(ReturnStmt.Create))
|
||||
)
|
||||
.Then(Semicolon)
|
||||
);
|
||||
|
||||
// compound-statement
|
||||
// : '{' [declaration-list]? [statement-list]? '}'
|
||||
CompoundStatement.Is(
|
||||
(LeftCurlyBrace)
|
||||
.TransformEnvironment(env => env.InScope())
|
||||
.Then(DeclarationList.Optional(ImmutableList<Decln>.Empty))
|
||||
.Then(StatementList.Optional(ImmutableList<Stmt>.Empty))
|
||||
.Then(RightCurlyBrace)
|
||||
.TransformEnvironment(env => env.OutScope())
|
||||
.Then(CompoundStmt.Create)
|
||||
);
|
||||
|
||||
// declaration-list
|
||||
// : [declaration]+
|
||||
DeclarationList.Is(
|
||||
Declaration.OneOrMore()
|
||||
);
|
||||
|
||||
// statement-list
|
||||
// : [statement]+
|
||||
StatementList.Is(
|
||||
Statement.OneOrMore()
|
||||
);
|
||||
|
||||
// expression-statement
|
||||
// : [expression]? ';'
|
||||
ExpressionStatement.Is(
|
||||
Expression.Optional()
|
||||
.Then(Semicolon)
|
||||
.Then(ExprStmt.Create)
|
||||
);
|
||||
|
||||
// iteration-statement
|
||||
// : 'while' '(' expression ')' statement
|
||||
// | 'do' statement 'while' '(' expression ')' ';'
|
||||
// | 'for' '(' [expression]? ';' [expression]? ';' [expression]? ')' statement
|
||||
IterationStatement.Is(
|
||||
Either(
|
||||
(While)
|
||||
.Then(LeftParen)
|
||||
.Then(Expression)
|
||||
.Then(RightParen)
|
||||
.Then(Statement)
|
||||
.Then(WhileStmt.Create)
|
||||
).Or(
|
||||
(Do)
|
||||
.Then(Statement)
|
||||
.Then(While)
|
||||
.Then(LeftParen)
|
||||
.Then(Expression)
|
||||
.Then(RightParen)
|
||||
.Then(Semicolon)
|
||||
.Then(DoWhileStmt.Create)
|
||||
).Or(
|
||||
(For)
|
||||
.Then(LeftParen)
|
||||
.Then(Expression.Optional())
|
||||
.Then(Semicolon)
|
||||
.Then(Expression.Optional())
|
||||
.Then(Semicolon)
|
||||
.Then(Expression.Optional())
|
||||
.Then(RightParen)
|
||||
.Then(Statement)
|
||||
.Then(ForStmt.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// selection-statement
|
||||
// : 'if' '(' expression ')' statement 'else' statement
|
||||
// | 'if' '(' expression ')' statement
|
||||
// | 'switch' '(' expression ')' statement
|
||||
SelectionStatement.Is(
|
||||
Either(
|
||||
(If)
|
||||
.Then(LeftParen)
|
||||
.Then(Expression)
|
||||
.Then(RightParen)
|
||||
.Then(Statement)
|
||||
.Then(
|
||||
Either(
|
||||
Given<Expr, Stmt>()
|
||||
.Then(Else)
|
||||
.Then(Statement)
|
||||
.Then(IfElseStmt.Create)
|
||||
).Or(
|
||||
Given<Expr, Stmt>()
|
||||
.Then(IfStmt.Create)
|
||||
)
|
||||
)
|
||||
).Or(
|
||||
(Switch)
|
||||
.Then(LeftParen)
|
||||
.Then(Expression)
|
||||
.Then(RightParen)
|
||||
.Then(Statement)
|
||||
.Then(SwitchStmt.Create)
|
||||
)
|
||||
);
|
||||
|
||||
// labeled-statement
|
||||
// : identifier ':' statement
|
||||
// | 'case' constant-expression ':' statement
|
||||
// | 'default' ':' statement
|
||||
LabeledStatement.Is(
|
||||
Either(
|
||||
(Identifier)
|
||||
.Then(Colon)
|
||||
.Then(Statement)
|
||||
.Then(LabeledStmt.Create)
|
||||
)
|
||||
.Or(
|
||||
(Case)
|
||||
.Then(ConstantExpression)
|
||||
.Then(Colon)
|
||||
.Then(Statement)
|
||||
.Then(CaseStmt.Create)
|
||||
).Or(
|
||||
(Default)
|
||||
.Then(Colon)
|
||||
.Then(Statement)
|
||||
.Then(DefaultStmt.Create)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
110
LibIFPSCC/Parser/Tokens.cs
Normal file
@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using LexicalAnalysis;
|
||||
using AST;
|
||||
|
||||
namespace Parsing {
|
||||
|
||||
public partial class CParsers {
|
||||
|
||||
public static IParser<String> Identifier { get; } = new IdentifierParser();
|
||||
public static IParser<Expr> ConstChar { get; } = new ConstCharParser();
|
||||
public static IParser<Expr> ConstInt { get; } = new ConstIntParser();
|
||||
public static IParser<Expr> ConstFloat { get; } = new ConstFloatParser();
|
||||
public static IParser<Expr> StringLiteral { get; } = new StringLiteralParser();
|
||||
public static IParser<Expr> UnicodeStringLiteral { get; } = new UnicodeStringLiteralParser();
|
||||
|
||||
public static IConsumer LeftParen { get; } = OperatorConsumer.Create(OperatorVal.LPAREN);
|
||||
public static IConsumer RightParen { get; } = OperatorConsumer.Create(OperatorVal.RPAREN);
|
||||
public static IConsumer Question { get; } = OperatorConsumer.Create(OperatorVal.QUESTION);
|
||||
public static IConsumer Comma { get; } = OperatorConsumer.Create(OperatorVal.COMMA);
|
||||
public static IConsumer Colon { get; } = OperatorConsumer.Create(OperatorVal.COLON);
|
||||
|
||||
public static IConsumer Assign { get; } = OperatorConsumer.Create(OperatorVal.ASSIGN);
|
||||
public static IConsumer MultAssign { get; } = OperatorConsumer.Create(OperatorVal.MULTASSIGN);
|
||||
public static IConsumer DivAssign { get; } = OperatorConsumer.Create(OperatorVal.DIVASSIGN);
|
||||
public static IConsumer ModAssign { get; } = OperatorConsumer.Create(OperatorVal.MODASSIGN);
|
||||
public static IConsumer AddAssign { get; } = OperatorConsumer.Create(OperatorVal.ADDASSIGN);
|
||||
public static IConsumer SubAssign { get; } = OperatorConsumer.Create(OperatorVal.SUBASSIGN);
|
||||
public static IConsumer LeftShiftAssign { get; } = OperatorConsumer.Create(OperatorVal.LSHIFTASSIGN);
|
||||
public static IConsumer RightShiftAssign { get; } = OperatorConsumer.Create(OperatorVal.RSHIFTASSIGN);
|
||||
public static IConsumer BitwiseAndAssign { get; } = OperatorConsumer.Create(OperatorVal.ANDASSIGN);
|
||||
public static IConsumer XorAssign { get; } = OperatorConsumer.Create(OperatorVal.XORASSIGN);
|
||||
public static IConsumer BitwiseOrAssign { get; } = OperatorConsumer.Create(OperatorVal.ORASSIGN);
|
||||
public static IConsumer LeftBracket { get; } = OperatorConsumer.Create(OperatorVal.LBRACKET);
|
||||
public static IConsumer RightBracket { get; } = OperatorConsumer.Create(OperatorVal.RBRACKET);
|
||||
public static IConsumer LeftCurlyBrace { get; } = OperatorConsumer.Create(OperatorVal.LCURL);
|
||||
public static IConsumer RightCurlyBrace { get; } = OperatorConsumer.Create(OperatorVal.RCURL);
|
||||
public static IConsumer Period { get; } = OperatorConsumer.Create(OperatorVal.PERIOD);
|
||||
public static IConsumer RightArrow { get; } = OperatorConsumer.Create(OperatorVal.RARROW);
|
||||
public static IConsumer Increment { get; } = OperatorConsumer.Create(OperatorVal.INC);
|
||||
public static IConsumer Decrement { get; } = OperatorConsumer.Create(OperatorVal.DEC);
|
||||
public static IConsumer Mult { get; } = OperatorConsumer.Create(OperatorVal.MULT);
|
||||
public static IConsumer Add { get; } = OperatorConsumer.Create(OperatorVal.ADD);
|
||||
public static IConsumer Sub { get; } = OperatorConsumer.Create(OperatorVal.SUB);
|
||||
public static IConsumer BitwiseNot { get; } = OperatorConsumer.Create(OperatorVal.TILDE);
|
||||
public static IConsumer LogicalNot { get; } = OperatorConsumer.Create(OperatorVal.NOT);
|
||||
public static IConsumer Div { get; } = OperatorConsumer.Create(OperatorVal.DIV);
|
||||
public static IConsumer Mod { get; } = OperatorConsumer.Create(OperatorVal.MOD);
|
||||
public static IConsumer LeftShift { get; } = OperatorConsumer.Create(OperatorVal.LSHIFT);
|
||||
public static IConsumer RightShift { get; } = OperatorConsumer.Create(OperatorVal.RSHIFT);
|
||||
public static IConsumer Less { get; } = OperatorConsumer.Create(OperatorVal.LT);
|
||||
public static IConsumer Greater { get; } = OperatorConsumer.Create(OperatorVal.GT);
|
||||
public static IConsumer LessEqual { get; } = OperatorConsumer.Create(OperatorVal.LEQ);
|
||||
public static IConsumer GreaterEqual { get; } = OperatorConsumer.Create(OperatorVal.GEQ);
|
||||
public static IConsumer Equal { get; } = OperatorConsumer.Create(OperatorVal.EQ);
|
||||
public static IConsumer NotEqual { get; } = OperatorConsumer.Create(OperatorVal.NEQ);
|
||||
public static IConsumer Xor { get; } = OperatorConsumer.Create(OperatorVal.XOR);
|
||||
public static IConsumer BitwiseAnd { get; } = OperatorConsumer.Create(OperatorVal.BITAND);
|
||||
public static IConsumer BitwiseOr { get; } = OperatorConsumer.Create(OperatorVal.BITOR);
|
||||
public static IConsumer LogicalAnd { get; } = OperatorConsumer.Create(OperatorVal.AND);
|
||||
public static IConsumer LogicalOr { get; } = OperatorConsumer.Create(OperatorVal.OR);
|
||||
public static IConsumer Semicolon { get; } = OperatorConsumer.Create(OperatorVal.SEMICOLON);
|
||||
|
||||
public static IConsumer SizeOf { get; } = KeywordConsumer.Create(KeywordVal.SIZEOF);
|
||||
public static IParser<StorageClsSpec> Auto { get; } = KeywordParser.Create(KeywordVal.AUTO, StorageClsSpec.AUTO);
|
||||
public static IParser<StorageClsSpec> Register { get; } = KeywordParser.Create(KeywordVal.REGISTER, StorageClsSpec.REGISTER);
|
||||
public static IParser<StorageClsSpec> Static { get; } = KeywordParser.Create(KeywordVal.STATIC, StorageClsSpec.STATIC);
|
||||
public static IParser<StorageClsSpec> Extern { get; } = KeywordParser.Create(KeywordVal.EXTERN, StorageClsSpec.EXTERN);
|
||||
public static IParser<StorageClsSpec> Typedef { get; } = KeywordParser.Create(KeywordVal.TYPEDEF, StorageClsSpec.TYPEDEF);
|
||||
|
||||
public static IParser<TypeSpecKind> Void { get; } = KeywordParser.Create(KeywordVal.VOID, TypeSpecKind.VOID);
|
||||
public static IParser<TypeSpecKind> Char { get; } = KeywordParser.Create(KeywordVal.CHAR, TypeSpecKind.CHAR);
|
||||
public static IParser<TypeSpecKind> Short { get; } = KeywordParser.Create(KeywordVal.SHORT, TypeSpecKind.SHORT);
|
||||
public static IParser<TypeSpecKind> Int { get; } = KeywordParser.Create(KeywordVal.INT, TypeSpecKind.INT);
|
||||
public static IParser<TypeSpecKind> Long { get; } = KeywordParser.Create(KeywordVal.LONG, TypeSpecKind.LONG);
|
||||
public static IParser<TypeSpecKind> Float { get; } = KeywordParser.Create(KeywordVal.FLOAT, TypeSpecKind.FLOAT);
|
||||
public static IParser<TypeSpecKind> Double { get; } = KeywordParser.Create(KeywordVal.DOUBLE, TypeSpecKind.DOUBLE);
|
||||
public static IParser<TypeSpecKind> Signed { get; } = KeywordParser.Create(KeywordVal.SIGNED, TypeSpecKind.SIGNED);
|
||||
public static IParser<TypeSpecKind> Unsigned { get; } = KeywordParser.Create(KeywordVal.UNSIGNED, TypeSpecKind.UNSIGNED);
|
||||
public static IParser<TypeSpecKind> String { get; } = KeywordParser.Create(KeywordVal.STRING, TypeSpecKind.STRING);
|
||||
public static IParser<TypeSpecKind> Int64 { get; } = KeywordParser.Create(KeywordVal.INT64, TypeSpecKind.INT64);
|
||||
public static IParser<TypeSpecKind> Variant { get; } = KeywordParser.Create(KeywordVal.VARIANT, TypeSpecKind.COM_VARIANT);
|
||||
|
||||
public static IParser<TypeQual> Const { get; } = KeywordParser.Create(KeywordVal.CONST, TypeQual.CONST);
|
||||
public static IParser<TypeQual> Volatile { get; } = KeywordParser.Create(KeywordVal.VOLATILE, TypeQual.VOLATILE);
|
||||
|
||||
public static IConsumer Enum { get; } = KeywordConsumer.Create(KeywordVal.ENUM);
|
||||
public static IParser<StructOrUnion> Struct { get; } = KeywordParser.Create(KeywordVal.STRUCT, AST.StructOrUnion.STRUCT);
|
||||
public static IParser<StructOrUnion> Union { get; } = KeywordParser.Create(KeywordVal.UNION, AST.StructOrUnion.UNION);
|
||||
|
||||
public static IConsumer Goto { get; } = KeywordConsumer.Create(KeywordVal.GOTO);
|
||||
public static IParser<Stmt> Continue { get; } = KeywordParser.Create(KeywordVal.CONTINUE, new ContStmt());
|
||||
public static IParser<Stmt> Break { get; } = KeywordParser.Create(KeywordVal.BREAK, new BreakStmt());
|
||||
public static IConsumer Return { get; } = KeywordConsumer.Create(KeywordVal.RETURN);
|
||||
|
||||
public static IConsumer While { get; } = KeywordConsumer.Create(KeywordVal.WHILE);
|
||||
public static IConsumer Do { get; } = KeywordConsumer.Create(KeywordVal.DO);
|
||||
public static IConsumer For { get; } = KeywordConsumer.Create(KeywordVal.FOR);
|
||||
|
||||
public static IConsumer If { get; } = KeywordConsumer.Create(KeywordVal.IF);
|
||||
public static IConsumer Else { get; } = KeywordConsumer.Create(KeywordVal.ELSE);
|
||||
public static IConsumer Switch { get; } = KeywordConsumer.Create(KeywordVal.SWITCH);
|
||||
|
||||
public static IConsumer Case { get; } = KeywordConsumer.Create(KeywordVal.CASE);
|
||||
public static IConsumer Default { get; } = KeywordConsumer.Create(KeywordVal.DEFAULT);
|
||||
|
||||
public static IConsumer AttributeKey { get; } = KeywordConsumer.Create(KeywordVal.ATTRIBUTE);
|
||||
public static IConsumer InterfaceTypeKey { get; } = KeywordConsumer.Create(KeywordVal.INTERFACE);
|
||||
|
||||
}
|
||||
}
|
378
LibIFPSCC/Scanner/Char.cs
Normal file
@ -0,0 +1,378 @@
|
||||
using System;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
/// <summary>
|
||||
/// A character constant
|
||||
/// </summary>
|
||||
public sealed class TokenCharConst : Token {
|
||||
public TokenCharConst(String raw, Char value) {
|
||||
this.Raw = raw;
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public override TokenKind Kind { get; } = TokenKind.CHAR;
|
||||
public String Raw { get; }
|
||||
public Char Value { get; }
|
||||
public override String ToString() => $"{this.Kind} [{Line}:{Column}]: '{this.Raw}'";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The FSA for scanning a C character.
|
||||
/// Note that this FSA doesn't scan the surrounding quotes.
|
||||
/// It is used in both FSACharConst and FSAString.
|
||||
///
|
||||
/// There are multiple ways to represent a character:
|
||||
/// * A normal character : any character other than \\ \n or [quote]
|
||||
/// Note that [quote] might be \' or \" depending on the context.
|
||||
/// For example, inside a String, single quote are allowed, which means that the following code is legal:
|
||||
/// Char *str = "single quote here: ' see that?";
|
||||
///
|
||||
/// However, if we need a double quote inside a String, we have to use an escape character, like this:
|
||||
/// Char *str = "double quote needs to be escaped: \" ";
|
||||
///
|
||||
/// Inside a Char, double quotes are allowed while single quotes need to be escaped.
|
||||
/// Char double_quote = '"'; // allowed
|
||||
/// Char single_quote = '\''; // needs to be escaped
|
||||
///
|
||||
/// * An escape character : \a \b \f \n \r \t \v \' \" \\ \?
|
||||
/// Note that even though \' and \" might not needs to be escaped, you can always use them as escaped.
|
||||
/// If you escape a character not listed above, the behavior is undefined in the standard.
|
||||
/// I'll just assume you need the unescaped character.
|
||||
/// For example, if you typed '\c', then I'll just treat it as 'c'.
|
||||
///
|
||||
/// * An octal number after a backslash. For example : \123.
|
||||
///
|
||||
/// * A hexadecimal number after a backslash and an 'x' or 'X'. FOr example : \xFF.
|
||||
///
|
||||
/// </summary>
|
||||
public sealed class FSAChar : FSA {
|
||||
private enum State {
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
S,
|
||||
C,
|
||||
SO,
|
||||
SOO,
|
||||
SOOO,
|
||||
SX,
|
||||
SXH,
|
||||
SXHH
|
||||
}
|
||||
|
||||
private State _state;
|
||||
private String _scanned;
|
||||
|
||||
// quote : Char
|
||||
// ============
|
||||
// \' in a Char, and \" in a String.
|
||||
private readonly Char _quote;
|
||||
|
||||
public FSAChar(Char quote) {
|
||||
this._state = State.START;
|
||||
this._quote = quote;
|
||||
this._scanned = "";
|
||||
}
|
||||
|
||||
public override void Reset() {
|
||||
this._scanned = "";
|
||||
this._state = State.START;
|
||||
}
|
||||
|
||||
public override FSAStatus GetStatus() {
|
||||
if (this._state == State.START) {
|
||||
return FSAStatus.NONE;
|
||||
}
|
||||
if (this._state == State.END) {
|
||||
return FSAStatus.END;
|
||||
}
|
||||
if (this._state == State.ERROR) {
|
||||
return FSAStatus.ERROR;
|
||||
}
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
|
||||
// IsChar : Char -> Boolean
|
||||
// ========================
|
||||
// the character is a 'normal' Char, other than <quote> \\ or \n
|
||||
//
|
||||
private Boolean IsChar(Char ch) {
|
||||
return ch != this._quote && ch != '\\' && ch != '\n';
|
||||
}
|
||||
|
||||
|
||||
|
||||
// RetrieveRaw : () -> String
|
||||
// ==========================
|
||||
//
|
||||
public String RetrieveRaw() {
|
||||
return this._scanned.Substring(0, this._scanned.Length - 1);
|
||||
}
|
||||
|
||||
// RetrieveChar : () -> Char
|
||||
// =========================
|
||||
//
|
||||
public Char RetrieveChar() {
|
||||
if (this._scanned.Length == 3) {
|
||||
switch (this._scanned[1]) {
|
||||
case 'a':
|
||||
return '\a';
|
||||
case 'b':
|
||||
return '\b';
|
||||
case 'f':
|
||||
return '\f';
|
||||
case 'n':
|
||||
return '\n';
|
||||
case 'r':
|
||||
return '\r';
|
||||
case 't':
|
||||
return '\t';
|
||||
case 'v':
|
||||
return '\v';
|
||||
case '\'':
|
||||
return '\'';
|
||||
case '\"':
|
||||
return '\"';
|
||||
case '\\':
|
||||
return '\\';
|
||||
case '?':
|
||||
return '?';
|
||||
default:
|
||||
return this._scanned[1];
|
||||
}
|
||||
}
|
||||
return this._scanned[0];
|
||||
}
|
||||
|
||||
// RetrieveToken : () -> Token
|
||||
// ===========================
|
||||
// Note that this function never gets used, because FSAChar is just an inner FSA for other FSAs.
|
||||
//
|
||||
public override Token RetrieveToken() {
|
||||
return new EmptyToken();
|
||||
}
|
||||
|
||||
// ReadChar : Char -> ()
|
||||
// =====================
|
||||
// Implementation of the FSA
|
||||
//
|
||||
public override void ReadChar(Char ch) {
|
||||
this._scanned = this._scanned + ch;
|
||||
switch (this._state) {
|
||||
case State.END:
|
||||
case State.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
case State.START:
|
||||
if (IsChar(ch)) {
|
||||
this._state = State.C;
|
||||
} else if (ch == '\\') {
|
||||
this._state = State.S;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.C:
|
||||
this._state = State.END;
|
||||
break;
|
||||
case State.S:
|
||||
if (Utils.IsEscapeChar(ch)) {
|
||||
this._state = State.C;
|
||||
} else if (Utils.IsOctDigit(ch)) {
|
||||
this._state = State.SO;
|
||||
} else if (ch == 'x' || ch == 'X') {
|
||||
this._state = State.SX;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.SX:
|
||||
if (Utils.IsHexDigit(ch)) {
|
||||
this._state = State.SXH;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.SXH:
|
||||
if (Utils.IsHexDigit(ch)) {
|
||||
this._state = State.SXHH;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.SXHH:
|
||||
this._state = State.END;
|
||||
break;
|
||||
case State.SO:
|
||||
if (Utils.IsOctDigit(ch)) {
|
||||
this._state = State.SOO;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.SOO:
|
||||
if (Utils.IsOctDigit(ch)) {
|
||||
this._state = State.SOOO;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.SOOO:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ReadEOF : () -> ()
|
||||
// ==================
|
||||
//
|
||||
public override void ReadEOF() {
|
||||
this._scanned = this._scanned + '0';
|
||||
switch (this._state) {
|
||||
case State.C:
|
||||
case State.SO:
|
||||
case State.SOO:
|
||||
case State.SOOO:
|
||||
case State.SXH:
|
||||
case State.SXHH:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FSACharConst
|
||||
/// ============
|
||||
/// The FSA for scanning a C character constant.
|
||||
/// Upon finish, we can retrive a token of character.
|
||||
///
|
||||
/// A character constant can either be represented by
|
||||
/// * '[char]'
|
||||
/// or
|
||||
/// * L'[char]'
|
||||
///
|
||||
/// The character inside the quotes is read by FSAChar.
|
||||
/// Note that if the inner character is a single quote, it needs to be escaped.
|
||||
/// </summary>
|
||||
public sealed class FSACharConst : FSA {
|
||||
private enum State {
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
L,
|
||||
Q,
|
||||
QC,
|
||||
QCQ
|
||||
};
|
||||
|
||||
private State _state;
|
||||
private Char _val;
|
||||
private String _raw;
|
||||
private readonly FSAChar _fsachar;
|
||||
|
||||
public FSACharConst() {
|
||||
this._state = State.START;
|
||||
this._fsachar = new FSAChar('\'');
|
||||
}
|
||||
|
||||
public override void Reset() {
|
||||
this._state = State.START;
|
||||
this._fsachar.Reset();
|
||||
}
|
||||
|
||||
public override FSAStatus GetStatus() {
|
||||
if (this._state == State.START) {
|
||||
return FSAStatus.NONE;
|
||||
}
|
||||
if (this._state == State.END) {
|
||||
return FSAStatus.END;
|
||||
}
|
||||
if (this._state == State.ERROR) {
|
||||
return FSAStatus.ERROR;
|
||||
}
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
|
||||
public override Token RetrieveToken() {
|
||||
return new TokenCharConst(this._raw, this._val);
|
||||
}
|
||||
|
||||
public override void ReadChar(Char ch) {
|
||||
switch (this._state) {
|
||||
case State.END:
|
||||
case State.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
case State.START:
|
||||
switch (ch) {
|
||||
case 'L':
|
||||
this._state = State.L;
|
||||
break;
|
||||
case '\'':
|
||||
this._state = State.Q;
|
||||
this._fsachar.Reset();
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.L:
|
||||
if (ch == '\'') {
|
||||
this._state = State.Q;
|
||||
this._fsachar.Reset();
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.Q:
|
||||
this._fsachar.ReadChar(ch);
|
||||
switch (this._fsachar.GetStatus()) {
|
||||
case FSAStatus.END:
|
||||
this._state = State.QC;
|
||||
this._raw = this._fsachar.RetrieveRaw();
|
||||
this._val = this._fsachar.RetrieveChar();
|
||||
this._fsachar.Reset();
|
||||
ReadChar(ch);
|
||||
break;
|
||||
case FSAStatus.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.QC:
|
||||
if (ch == '\'') {
|
||||
this._state = State.QCQ;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.QCQ:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReadEOF() {
|
||||
if (this._state == State.QCQ) {
|
||||
this._state = State.END;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
18
LibIFPSCC/Scanner/FSA.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
public enum FSAStatus {
|
||||
NONE,
|
||||
END,
|
||||
RUNNING,
|
||||
ERROR
|
||||
}
|
||||
|
||||
public abstract class FSA {
|
||||
public abstract FSAStatus GetStatus();
|
||||
public abstract void ReadChar(Char ch);
|
||||
public abstract void Reset();
|
||||
public abstract void ReadEOF();
|
||||
public abstract Token RetrieveToken();
|
||||
}
|
||||
}
|
5
LibIFPSCC/Scanner/FSAGraphs/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
## Graphs of the scanner FSAs
|
||||
|
||||
`*.svg`: the rendered graphs
|
||||
|
||||
Use `draw_fsas.py` to render the graphs with the `.dot` files.
|
20
LibIFPSCC/Scanner/FSAGraphs/char.dot
Normal file
@ -0,0 +1,20 @@
|
||||
digraph {
|
||||
node [shape = circle, height = 0.6, fontname = "monospace", fixedsize = true, fontsize = 12];
|
||||
edge [fontname = "monospace", fontsize = 12];
|
||||
|
||||
node [shape = circle];
|
||||
S; SX; START;
|
||||
|
||||
node [style = filled];
|
||||
C; SO; SOO; SOOO; SXH; SXHH;
|
||||
|
||||
START -> S [label = "\\"];
|
||||
START -> C [label = "[^\\\'\\\\\\n]"];
|
||||
S -> C [label = "[abfnrtv\\\'\\\"\\\\\\\?]"];
|
||||
S -> SX [label = "[Xx]"];
|
||||
S -> SO [label = "[0-7]"];
|
||||
SO -> SOO [label = "[0-7]"];
|
||||
SOO -> SOOO [label = "[0-7]"];
|
||||
SX -> SXH [label = "[0-9a-fA-F]"];
|
||||
SXH -> SXHH [label = "[0-9a-fA-F]"];
|
||||
}
|
112
LibIFPSCC/Scanner/FSAGraphs/char.svg
Normal file
@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: %3 Pages: 1 -->
|
||||
<svg width="342pt" height="419pt"
|
||||
viewBox="0.00 0.00 341.50 419.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 415)">
|
||||
<title>%3</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-415 337.5,-415 337.5,4 -4,4"/>
|
||||
<!-- S -->
|
||||
<g id="node1" class="node"><title>S</title>
|
||||
<ellipse fill="none" stroke="black" cx="151.5" cy="-297.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="151.5" y="-294.4" font-family="monospace" font-size="12.00">S</text>
|
||||
</g>
|
||||
<!-- SX -->
|
||||
<g id="node2" class="node"><title>SX</title>
|
||||
<ellipse fill="none" stroke="black" cx="250.5" cy="-205.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="250.5" y="-202.4" font-family="monospace" font-size="12.00">SX</text>
|
||||
</g>
|
||||
<!-- S->SX -->
|
||||
<g id="edge4" class="edge"><title>S->SX</title>
|
||||
<path fill="none" stroke="black" d="M170.891,-287.634C184.409,-280.821 202.313,-270.476 215.5,-258 222.96,-250.942 229.703,-242.03 235.234,-233.612"/>
|
||||
<polygon fill="black" stroke="black" points="238.211,-235.454 240.539,-225.118 232.273,-231.746 238.211,-235.454"/>
|
||||
<text text-anchor="middle" x="243" y="-248.4" font-family="monospace" font-size="12.00">[Xx]</text>
|
||||
</g>
|
||||
<!-- C -->
|
||||
<g id="node4" class="node"><title>C</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="21.5" cy="-205.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="21.5" y="-202.4" font-family="monospace" font-size="12.00">C</text>
|
||||
</g>
|
||||
<!-- S->C -->
|
||||
<g id="edge3" class="edge"><title>S->C</title>
|
||||
<path fill="none" stroke="black" d="M130.919,-290.734C99.6843,-281.817 43.2113,-264.983 36.5,-258 30.981,-252.258 27.4755,-244.605 25.254,-236.958"/>
|
||||
<polygon fill="black" stroke="black" points="28.6369,-236.049 23.012,-227.071 21.8102,-237.597 28.6369,-236.049"/>
|
||||
<text text-anchor="middle" x="100.5" y="-248.4" font-family="monospace" font-size="12.00">[abfnrtv\'\"\\\?]</text>
|
||||
</g>
|
||||
<!-- SO -->
|
||||
<g id="node5" class="node"><title>SO</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="189.5" cy="-205.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="189.5" y="-202.4" font-family="monospace" font-size="12.00">SO</text>
|
||||
</g>
|
||||
<!-- S->SO -->
|
||||
<g id="edge5" class="edge"><title>S->SO</title>
|
||||
<path fill="none" stroke="black" d="M159.559,-277.413C164.748,-265.123 171.589,-248.921 177.422,-235.106"/>
|
||||
<polygon fill="black" stroke="black" points="180.787,-236.133 181.453,-225.559 174.339,-233.41 180.787,-236.133"/>
|
||||
<text text-anchor="middle" x="192.5" y="-248.4" font-family="monospace" font-size="12.00">[0-7]</text>
|
||||
</g>
|
||||
<!-- SXH -->
|
||||
<g id="node8" class="node"><title>SXH</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="250.5" cy="-113.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="250.5" y="-110.4" font-family="monospace" font-size="12.00">SXH</text>
|
||||
</g>
|
||||
<!-- SX->SXH -->
|
||||
<g id="edge8" class="edge"><title>SX->SXH</title>
|
||||
<path fill="none" stroke="black" d="M250.5,-183.626C250.5,-172.267 250.5,-157.996 250.5,-145.375"/>
|
||||
<polygon fill="black" stroke="black" points="254,-145.261 250.5,-135.261 247,-145.261 254,-145.261"/>
|
||||
<text text-anchor="middle" x="292" y="-156.4" font-family="monospace" font-size="12.00">[0-9a-fA-F]</text>
|
||||
</g>
|
||||
<!-- START -->
|
||||
<g id="node3" class="node"><title>START</title>
|
||||
<ellipse fill="none" stroke="black" cx="49.5" cy="-389.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="49.5" y="-386.4" font-family="monospace" font-size="12.00">START</text>
|
||||
</g>
|
||||
<!-- START->S -->
|
||||
<g id="edge1" class="edge"><title>START->S</title>
|
||||
<path fill="none" stroke="black" d="M65.4375,-374.438C82.2046,-359.643 108.772,-336.202 127.955,-319.275"/>
|
||||
<polygon fill="black" stroke="black" points="130.492,-321.704 135.674,-312.464 125.86,-316.456 130.492,-321.704"/>
|
||||
<text text-anchor="middle" x="111.5" y="-340.4" font-family="monospace" font-size="12.00">\</text>
|
||||
</g>
|
||||
<!-- START->C -->
|
||||
<g id="edge2" class="edge"><title>START->C</title>
|
||||
<path fill="none" stroke="black" d="M45.099,-368.26C39.6969,-342.694 30.6244,-297.29 25.5,-258 24.6323,-251.347 23.9301,-244.185 23.3709,-237.378"/>
|
||||
<polygon fill="black" stroke="black" points="26.8386,-236.813 22.6064,-227.1 19.8579,-237.333 26.8386,-236.813"/>
|
||||
<text text-anchor="middle" x="69.5" y="-294.4" font-family="monospace" font-size="12.00">[^\'\\\n]</text>
|
||||
</g>
|
||||
<!-- SOO -->
|
||||
<g id="node6" class="node"><title>SOO</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="189.5" cy="-113.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="189.5" y="-110.4" font-family="monospace" font-size="12.00">SOO</text>
|
||||
</g>
|
||||
<!-- SO->SOO -->
|
||||
<g id="edge6" class="edge"><title>SO->SOO</title>
|
||||
<path fill="none" stroke="black" d="M189.5,-183.626C189.5,-172.267 189.5,-157.996 189.5,-145.375"/>
|
||||
<polygon fill="black" stroke="black" points="193,-145.261 189.5,-135.261 186,-145.261 193,-145.261"/>
|
||||
<text text-anchor="middle" x="208.5" y="-156.4" font-family="monospace" font-size="12.00">[0-7]</text>
|
||||
</g>
|
||||
<!-- SOOO -->
|
||||
<g id="node7" class="node"><title>SOOO</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="189.5" cy="-21.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="189.5" y="-18.4" font-family="monospace" font-size="12.00">SOOO</text>
|
||||
</g>
|
||||
<!-- SOO->SOOO -->
|
||||
<g id="edge7" class="edge"><title>SOO->SOOO</title>
|
||||
<path fill="none" stroke="black" d="M189.5,-91.626C189.5,-80.2668 189.5,-65.9958 189.5,-53.3745"/>
|
||||
<polygon fill="black" stroke="black" points="193,-53.2608 189.5,-43.2608 186,-53.2609 193,-53.2608"/>
|
||||
<text text-anchor="middle" x="208.5" y="-64.4" font-family="monospace" font-size="12.00">[0-7]</text>
|
||||
</g>
|
||||
<!-- SXHH -->
|
||||
<g id="node9" class="node"><title>SXHH</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="250.5" cy="-21.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="250.5" y="-18.4" font-family="monospace" font-size="12.00">SXHH</text>
|
||||
</g>
|
||||
<!-- SXH->SXHH -->
|
||||
<g id="edge9" class="edge"><title>SXH->SXHH</title>
|
||||
<path fill="none" stroke="black" d="M250.5,-91.626C250.5,-80.2668 250.5,-65.9958 250.5,-53.3745"/>
|
||||
<polygon fill="black" stroke="black" points="254,-53.2608 250.5,-43.2608 247,-53.2609 254,-53.2608"/>
|
||||
<text text-anchor="middle" x="292" y="-64.4" font-family="monospace" font-size="12.00">[0-9a-fA-F]</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.5 KiB |
17
LibIFPSCC/Scanner/FSAGraphs/constchar.dot
Normal file
@ -0,0 +1,17 @@
|
||||
digraph {
|
||||
node [shape = circle, height = 0.6, fontname = "monospace", fixedsize = true, fontsize = 12];
|
||||
edge [fontname = "monospace", fontsize = 12];
|
||||
|
||||
node [shape = circle];
|
||||
S; L; Q; QC;
|
||||
|
||||
node [style = filled];
|
||||
QCQ;
|
||||
|
||||
S -> L [label = "L"];
|
||||
S -> Q [label = "\\'"];
|
||||
L -> Q [label = "\\'"];
|
||||
Q -> QC [label = "<FSAChar succeed>"];
|
||||
QC -> QCQ [label = "\\'"];
|
||||
|
||||
}
|
68
LibIFPSCC/Scanner/FSAGraphs/constchar.svg
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: %3 Pages: 1 -->
|
||||
<svg width="194pt" height="419pt"
|
||||
viewBox="0.00 0.00 193.50 419.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 415)">
|
||||
<title>%3</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-415 189.5,-415 189.5,4 -4,4"/>
|
||||
<!-- S -->
|
||||
<g id="node1" class="node"><title>S</title>
|
||||
<ellipse fill="none" stroke="black" cx="64.5" cy="-389.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="64.5" y="-386.4" font-family="monospace" font-size="12.00">S</text>
|
||||
</g>
|
||||
<!-- L -->
|
||||
<g id="node2" class="node"><title>L</title>
|
||||
<ellipse fill="none" stroke="black" cx="21.5" cy="-297.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="21.5" y="-294.4" font-family="monospace" font-size="12.00">L</text>
|
||||
</g>
|
||||
<!-- S->L -->
|
||||
<g id="edge1" class="edge"><title>S->L</title>
|
||||
<path fill="none" stroke="black" d="M55.5904,-369.852C49.5942,-357.302 41.5808,-340.53 34.8299,-326.4"/>
|
||||
<polygon fill="black" stroke="black" points="37.8673,-324.638 30.3982,-317.124 31.5512,-327.656 37.8673,-324.638"/>
|
||||
<text text-anchor="middle" x="50.5" y="-340.4" font-family="monospace" font-size="12.00">L</text>
|
||||
</g>
|
||||
<!-- Q -->
|
||||
<g id="node3" class="node"><title>Q</title>
|
||||
<ellipse fill="none" stroke="black" cx="57.5" cy="-205.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="57.5" y="-202.4" font-family="monospace" font-size="12.00">Q</text>
|
||||
</g>
|
||||
<!-- S->Q -->
|
||||
<g id="edge2" class="edge"><title>S->Q</title>
|
||||
<path fill="none" stroke="black" d="M64.9621,-367.554C65.4069,-339.579 65.6167,-288.491 62.5,-245 62.3145,-242.412 62.0717,-239.731 61.7931,-237.045"/>
|
||||
<polygon fill="black" stroke="black" points="65.2361,-236.356 60.586,-226.836 58.2845,-237.178 65.2361,-236.356"/>
|
||||
<text text-anchor="middle" x="73.5" y="-294.4" font-family="monospace" font-size="12.00">\'</text>
|
||||
</g>
|
||||
<!-- L->Q -->
|
||||
<g id="edge3" class="edge"><title>L->Q</title>
|
||||
<path fill="none" stroke="black" d="M29.3115,-276.971C34.2038,-264.741 40.5933,-248.767 46.052,-235.12"/>
|
||||
<polygon fill="black" stroke="black" points="49.3632,-236.266 49.8275,-225.681 42.8638,-233.666 49.3632,-236.266"/>
|
||||
<text text-anchor="middle" x="50.5" y="-248.4" font-family="monospace" font-size="12.00">\'</text>
|
||||
</g>
|
||||
<!-- QC -->
|
||||
<g id="node4" class="node"><title>QC</title>
|
||||
<ellipse fill="none" stroke="black" cx="57.5" cy="-113.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="57.5" y="-110.4" font-family="monospace" font-size="12.00">QC</text>
|
||||
</g>
|
||||
<!-- Q->QC -->
|
||||
<g id="edge4" class="edge"><title>Q->QC</title>
|
||||
<path fill="none" stroke="black" d="M57.5,-183.626C57.5,-172.267 57.5,-157.996 57.5,-145.375"/>
|
||||
<polygon fill="black" stroke="black" points="61.0001,-145.261 57.5,-135.261 54.0001,-145.261 61.0001,-145.261"/>
|
||||
<text text-anchor="middle" x="121.5" y="-156.4" font-family="monospace" font-size="12.00"><FSAChar succeed></text>
|
||||
</g>
|
||||
<!-- QCQ -->
|
||||
<g id="node5" class="node"><title>QCQ</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="57.5" cy="-21.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="57.5" y="-18.4" font-family="monospace" font-size="12.00">QCQ</text>
|
||||
</g>
|
||||
<!-- QC->QCQ -->
|
||||
<g id="edge5" class="edge"><title>QC->QCQ</title>
|
||||
<path fill="none" stroke="black" d="M57.5,-91.626C57.5,-80.2668 57.5,-65.9958 57.5,-53.3745"/>
|
||||
<polygon fill="black" stroke="black" points="61.0001,-53.2608 57.5,-43.2608 54.0001,-53.2609 61.0001,-53.2608"/>
|
||||
<text text-anchor="middle" x="65.5" y="-64.4" font-family="monospace" font-size="12.00">\'</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
8
LibIFPSCC/Scanner/FSAGraphs/draw_fsas.py
Normal file
@ -0,0 +1,8 @@
|
||||
import os
|
||||
|
||||
file_names = [file_name for file_name in os.listdir(".") if file_name.endswith('.dot')]
|
||||
commands = ["dot -Tsvg " + file_name + " -o " + file_name[:-3] + "svg" for file_name in file_names]
|
||||
|
||||
for command in commands:
|
||||
print(command)
|
||||
os.system(command)
|
40
LibIFPSCC/Scanner/FSAGraphs/float.dot
Normal file
@ -0,0 +1,40 @@
|
||||
digraph float {
|
||||
node [shape = circle, height = 0.6, fontname = "monospace", fixedsize = true, fontsize = 12];
|
||||
edge [fontname = "monospace", fontsize = 12];
|
||||
|
||||
node [shape = circle];
|
||||
START; D; P; DE; DES;
|
||||
|
||||
node [style = filled];
|
||||
DP PD DED PDF DPL;
|
||||
|
||||
// node [shape = circle, fixedsize = true];
|
||||
START -> D [ label = "[0-9]" ];
|
||||
START -> P [ label = ".(dot)" ];
|
||||
|
||||
D -> DE [ label = "[Ee]" ];
|
||||
D -> D [ label = "[0-9]" ];
|
||||
D -> DP [ label = ".(dot)" ];
|
||||
|
||||
P -> PD [ label = "[0-9]" ];
|
||||
|
||||
DP -> DE [ label = "[Ee]" ];
|
||||
DP -> PDF [ label = "[Ff]" ];
|
||||
DP -> PD [ label = "[0-9]" ];
|
||||
DP -> DPL [ label = "[Ll]" ];
|
||||
|
||||
PD -> PD [ label = "[0-9]" ];
|
||||
PD -> PDF [ label = "[Ff]" ];
|
||||
PD -> DE [ label = "[Ee]" ];
|
||||
PD -> DPL [ label = "[Ll]" ];
|
||||
|
||||
DE -> DES [ label = "[+-]" ];
|
||||
DE -> DED [ label = "[0-9]" ];
|
||||
|
||||
DES -> DED [ label = "[0-9]" ];
|
||||
|
||||
DED -> DED [ label = "[0-9]" ];
|
||||
DED -> PDF [ label = "[Ff]" ];
|
||||
DED -> DPL [ label = "[Ll]" ];
|
||||
|
||||
}
|
183
LibIFPSCC/Scanner/FSAGraphs/float.svg
Normal file
@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: float Pages: 1 -->
|
||||
<svg width="397pt" height="725pt"
|
||||
viewBox="0.00 0.00 396.96 725.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 721)">
|
||||
<title>float</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-721 392.96,-721 392.96,4 -4,4"/>
|
||||
<!-- START -->
|
||||
<g id="node1" class="node"><title>START</title>
|
||||
<ellipse fill="none" stroke="black" cx="262.96" cy="-695.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="262.96" y="-692.4" font-family="monospace" font-size="12.00">START</text>
|
||||
</g>
|
||||
<!-- D -->
|
||||
<g id="node2" class="node"><title>D</title>
|
||||
<ellipse fill="none" stroke="black" cx="180.96" cy="-603.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="180.96" y="-600.4" font-family="monospace" font-size="12.00">D</text>
|
||||
</g>
|
||||
<!-- START->D -->
|
||||
<g id="edge1" class="edge"><title>START->D</title>
|
||||
<path fill="none" stroke="black" d="M248.676,-678.823C235.742,-664.627 216.525,-643.535 201.863,-627.443"/>
|
||||
<polygon fill="black" stroke="black" points="204.401,-625.032 195.079,-619.997 199.227,-629.746 204.401,-625.032"/>
|
||||
<text text-anchor="middle" x="245.96" y="-646.4" font-family="monospace" font-size="12.00">[0-9]</text>
|
||||
</g>
|
||||
<!-- P -->
|
||||
<g id="node3" class="node"><title>P</title>
|
||||
<ellipse fill="none" stroke="black" cx="358.96" cy="-542.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="358.96" y="-539.4" font-family="monospace" font-size="12.00">P</text>
|
||||
</g>
|
||||
<!-- START->P -->
|
||||
<g id="edge2" class="edge"><title>START->P</title>
|
||||
<path fill="none" stroke="black" d="M274.16,-676.883C290.996,-650.401 322.928,-600.174 342.441,-569.482"/>
|
||||
<polygon fill="black" stroke="black" points="345.419,-571.322 347.831,-561.005 339.512,-567.566 345.419,-571.322"/>
|
||||
<text text-anchor="middle" x="316.96" y="-646.4" font-family="monospace" font-size="12.00">.(dot)</text>
|
||||
</g>
|
||||
<!-- D->D -->
|
||||
<g id="edge4" class="edge"><title>D->D</title>
|
||||
<path fill="none" stroke="black" d="M200.877,-611.822C211.176,-613.102 220.46,-610.328 220.46,-603.5 220.46,-599.126 216.65,-596.415 211.203,-595.369"/>
|
||||
<polygon fill="black" stroke="black" points="210.94,-591.863 200.877,-595.178 210.811,-598.862 210.94,-591.863"/>
|
||||
<text text-anchor="middle" x="239.46" y="-600.4" font-family="monospace" font-size="12.00">[0-9]</text>
|
||||
</g>
|
||||
<!-- DE -->
|
||||
<g id="node4" class="node"><title>DE</title>
|
||||
<ellipse fill="none" stroke="black" cx="152.96" cy="-297.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="152.96" y="-294.4" font-family="monospace" font-size="12.00">DE</text>
|
||||
</g>
|
||||
<!-- D->DE -->
|
||||
<g id="edge3" class="edge"><title>D->DE</title>
|
||||
<path fill="none" stroke="black" d="M162.175,-592.567C123.909,-571.118 37.7754,-516.338 4.95956,-442 -22.5468,-379.69 72.6368,-330.717 123.294,-309.688"/>
|
||||
<polygon fill="black" stroke="black" points="124.819,-312.847 132.783,-305.859 122.2,-306.356 124.819,-312.847"/>
|
||||
<text text-anchor="middle" x="20.4596" y="-432.4" font-family="monospace" font-size="12.00">[Ee]</text>
|
||||
</g>
|
||||
<!-- DP -->
|
||||
<g id="node6" class="node"><title>DP</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="180.96" cy="-481.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="180.96" y="-478.4" font-family="monospace" font-size="12.00">DP</text>
|
||||
</g>
|
||||
<!-- D->DP -->
|
||||
<g id="edge5" class="edge"><title>D->DP</title>
|
||||
<path fill="none" stroke="black" d="M180.96,-581.871C180.96,-563.068 180.96,-535.029 180.96,-513.525"/>
|
||||
<polygon fill="black" stroke="black" points="184.46,-513.261 180.96,-503.261 177.46,-513.261 184.46,-513.261"/>
|
||||
<text text-anchor="middle" x="203.96" y="-539.4" font-family="monospace" font-size="12.00">.(dot)</text>
|
||||
</g>
|
||||
<!-- PD -->
|
||||
<g id="node7" class="node"><title>PD</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="231.96" cy="-389.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="231.96" y="-386.4" font-family="monospace" font-size="12.00">PD</text>
|
||||
</g>
|
||||
<!-- P->PD -->
|
||||
<g id="edge6" class="edge"><title>P->PD</title>
|
||||
<path fill="none" stroke="black" d="M345.608,-525.626C323.276,-499.073 278.224,-445.508 252.049,-414.386"/>
|
||||
<polygon fill="black" stroke="black" points="254.629,-412.016 245.514,-406.616 249.272,-416.522 254.629,-412.016"/>
|
||||
<text text-anchor="middle" x="342.96" y="-478.4" font-family="monospace" font-size="12.00">[0-9]</text>
|
||||
</g>
|
||||
<!-- DES -->
|
||||
<g id="node5" class="node"><title>DES</title>
|
||||
<ellipse fill="none" stroke="black" cx="99.9596" cy="-205.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="99.9596" y="-202.4" font-family="monospace" font-size="12.00">DES</text>
|
||||
</g>
|
||||
<!-- DE->DES -->
|
||||
<g id="edge15" class="edge"><title>DE->DES</title>
|
||||
<path fill="none" stroke="black" d="M136.003,-283.917C127.917,-277.088 118.766,-268.003 112.96,-258 109.29,-251.677 106.646,-244.331 104.745,-237.202"/>
|
||||
<polygon fill="black" stroke="black" points="108.072,-236.043 102.458,-227.057 101.243,-237.582 108.072,-236.043"/>
|
||||
<text text-anchor="middle" x="128.46" y="-248.4" font-family="monospace" font-size="12.00">[+-]</text>
|
||||
</g>
|
||||
<!-- DED -->
|
||||
<g id="node8" class="node"><title>DED</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="125.96" cy="-113.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="125.96" y="-110.4" font-family="monospace" font-size="12.00">DED</text>
|
||||
</g>
|
||||
<!-- DE->DED -->
|
||||
<g id="edge16" class="edge"><title>DE->DED</title>
|
||||
<path fill="none" stroke="black" d="M154.056,-275.689C155.006,-247.431 154.828,-195.568 143.96,-153 143.029,-149.354 141.754,-145.633 140.31,-142.011"/>
|
||||
<polygon fill="black" stroke="black" points="143.467,-140.496 136.211,-132.776 137.069,-143.336 143.467,-140.496"/>
|
||||
<text text-anchor="middle" x="172.96" y="-202.4" font-family="monospace" font-size="12.00">[0-9]</text>
|
||||
</g>
|
||||
<!-- DES->DED -->
|
||||
<g id="edge17" class="edge"><title>DES->DED</title>
|
||||
<path fill="none" stroke="black" d="M100.673,-183.598C101.432,-174.096 102.951,-162.811 105.96,-153 107.143,-149.141 108.708,-145.21 110.442,-141.407"/>
|
||||
<polygon fill="black" stroke="black" points="113.639,-142.839 114.981,-132.329 107.378,-139.708 113.639,-142.839"/>
|
||||
<text text-anchor="middle" x="124.96" y="-156.4" font-family="monospace" font-size="12.00">[0-9]</text>
|
||||
</g>
|
||||
<!-- DP->DE -->
|
||||
<g id="edge7" class="edge"><title>DP->DE</title>
|
||||
<path fill="none" stroke="black" d="M177.697,-460.131C175.488,-446.281 172.518,-427.539 169.96,-411 165.658,-383.194 160.88,-351.502 157.488,-328.857"/>
|
||||
<polygon fill="black" stroke="black" points="160.929,-328.202 155.988,-318.83 154.006,-329.238 160.929,-328.202"/>
|
||||
<text text-anchor="middle" x="185.46" y="-386.4" font-family="monospace" font-size="12.00">[Ee]</text>
|
||||
</g>
|
||||
<!-- DP->PD -->
|
||||
<g id="edge9" class="edge"><title>DP->PD</title>
|
||||
<path fill="none" stroke="black" d="M191.28,-462.287C198.539,-449.478 208.377,-432.117 216.56,-417.676"/>
|
||||
<polygon fill="black" stroke="black" points="219.769,-419.112 221.654,-408.686 213.679,-415.66 219.769,-419.112"/>
|
||||
<text text-anchor="middle" x="228.96" y="-432.4" font-family="monospace" font-size="12.00">[0-9]</text>
|
||||
</g>
|
||||
<!-- PDF -->
|
||||
<g id="node9" class="node"><title>PDF</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="125.96" cy="-21.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="125.96" y="-18.4" font-family="monospace" font-size="12.00">PDF</text>
|
||||
</g>
|
||||
<!-- DP->PDF -->
|
||||
<g id="edge8" class="edge"><title>DP->PDF</title>
|
||||
<path fill="none" stroke="black" d="M159.912,-476.233C122.881,-467.362 49.9596,-443.203 49.9596,-390.5 49.9596,-390.5 49.9596,-390.5 49.9596,-112.5 49.9596,-81.0857 77.3229,-54.9669 99.2116,-39.0992"/>
|
||||
<polygon fill="black" stroke="black" points="101.322,-41.8956 107.562,-33.3336 97.3443,-36.1352 101.322,-41.8956"/>
|
||||
<text text-anchor="middle" x="65.4596" y="-248.4" font-family="monospace" font-size="12.00">[Ff]</text>
|
||||
</g>
|
||||
<!-- DPL -->
|
||||
<g id="node10" class="node"><title>DPL</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="298.96" cy="-21.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="298.96" y="-18.4" font-family="monospace" font-size="12.00">DPL</text>
|
||||
</g>
|
||||
<!-- DP->DPL -->
|
||||
<g id="edge10" class="edge"><title>DP->DPL</title>
|
||||
<path fill="none" stroke="black" d="M201.61,-474.722C248.301,-461.047 357.96,-425.232 357.96,-390.5 357.96,-390.5 357.96,-390.5 357.96,-112.5 357.96,-85.281 338.851,-59.8943 322.384,-43.0697"/>
|
||||
<polygon fill="black" stroke="black" points="324.69,-40.4303 315.082,-35.9662 319.809,-45.4479 324.69,-40.4303"/>
|
||||
<text text-anchor="middle" x="373.46" y="-248.4" font-family="monospace" font-size="12.00">[Ll]</text>
|
||||
</g>
|
||||
<!-- PD->DE -->
|
||||
<g id="edge13" class="edge"><title>PD->DE</title>
|
||||
<path fill="none" stroke="black" d="M215.136,-375.971C206.219,-368.889 195.398,-359.557 186.96,-350 180.294,-342.451 173.939,-333.502 168.566,-325.199"/>
|
||||
<polygon fill="black" stroke="black" points="171.421,-323.164 163.152,-316.54 165.486,-326.875 171.421,-323.164"/>
|
||||
<text text-anchor="middle" x="202.46" y="-340.4" font-family="monospace" font-size="12.00">[Ee]</text>
|
||||
</g>
|
||||
<!-- PD->PD -->
|
||||
<g id="edge11" class="edge"><title>PD->PD</title>
|
||||
<path fill="none" stroke="black" d="M251.877,-397.822C262.176,-399.102 271.46,-396.328 271.46,-389.5 271.46,-385.126 267.65,-382.415 262.203,-381.369"/>
|
||||
<polygon fill="black" stroke="black" points="261.94,-377.863 251.877,-381.178 261.811,-384.862 261.94,-377.863"/>
|
||||
<text text-anchor="middle" x="290.46" y="-386.4" font-family="monospace" font-size="12.00">[0-9]</text>
|
||||
</g>
|
||||
<!-- PD->PDF -->
|
||||
<g id="edge12" class="edge"><title>PD->PDF</title>
|
||||
<path fill="none" stroke="black" d="M231.96,-367.767C231.96,-349.525 231.96,-322.25 231.96,-298.5 231.96,-298.5 231.96,-298.5 231.96,-112.5 231.96,-88.7804 233.599,-78.8333 217.96,-61 202.405,-43.2634 177.347,-33.5009 157.308,-28.2486"/>
|
||||
<polygon fill="black" stroke="black" points="157.9,-24.7922 147.363,-25.8947 156.288,-31.6039 157.9,-24.7922"/>
|
||||
<text text-anchor="middle" x="247.46" y="-202.4" font-family="monospace" font-size="12.00">[Ff]</text>
|
||||
</g>
|
||||
<!-- PD->DPL -->
|
||||
<g id="edge14" class="edge"><title>PD->DPL</title>
|
||||
<path fill="none" stroke="black" d="M249.214,-376.339C269.065,-360.776 298.96,-331.81 298.96,-298.5 298.96,-298.5 298.96,-298.5 298.96,-112.5 298.96,-92.832 298.96,-70.7464 298.96,-53.3757"/>
|
||||
<polygon fill="black" stroke="black" points="302.46,-53.233 298.96,-43.233 295.46,-53.2331 302.46,-53.233"/>
|
||||
<text text-anchor="middle" x="314.46" y="-202.4" font-family="monospace" font-size="12.00">[Ll]</text>
|
||||
</g>
|
||||
<!-- DED->DED -->
|
||||
<g id="edge18" class="edge"><title>DED->DED</title>
|
||||
<path fill="none" stroke="black" d="M145.877,-121.822C156.176,-123.102 165.46,-120.328 165.46,-113.5 165.46,-109.126 161.65,-106.415 156.203,-105.369"/>
|
||||
<polygon fill="black" stroke="black" points="155.94,-101.863 145.877,-105.178 155.811,-108.862 155.94,-101.863"/>
|
||||
<text text-anchor="middle" x="184.46" y="-110.4" font-family="monospace" font-size="12.00">[0-9]</text>
|
||||
</g>
|
||||
<!-- DED->PDF -->
|
||||
<g id="edge19" class="edge"><title>DED->PDF</title>
|
||||
<path fill="none" stroke="black" d="M125.96,-91.626C125.96,-80.2668 125.96,-65.9958 125.96,-53.3745"/>
|
||||
<polygon fill="black" stroke="black" points="129.46,-53.2608 125.96,-43.2608 122.46,-53.2609 129.46,-53.2608"/>
|
||||
<text text-anchor="middle" x="141.46" y="-64.4" font-family="monospace" font-size="12.00">[Ff]</text>
|
||||
</g>
|
||||
<!-- DED->DPL -->
|
||||
<g id="edge20" class="edge"><title>DED->DPL</title>
|
||||
<path fill="none" stroke="black" d="M144.803,-102.697C175.221,-86.8726 235.316,-55.6095 270.822,-37.138"/>
|
||||
<polygon fill="black" stroke="black" points="272.883,-40.011 280.139,-32.2908 269.653,-33.801 272.883,-40.011"/>
|
||||
<text text-anchor="middle" x="238.46" y="-64.4" font-family="monospace" font-size="12.00">[Ll]</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 12 KiB |
14
LibIFPSCC/Scanner/FSAGraphs/identifier.dot
Normal file
@ -0,0 +1,14 @@
|
||||
digraph identifier {
|
||||
node [shape = circle, height = 0.6, fontname = "monospace", fixedsize = true, fontsize = 12];
|
||||
edge [fontname = "monospace", fontsize = 12];
|
||||
|
||||
node [shape = circle];
|
||||
START;
|
||||
|
||||
node [style = filled];
|
||||
ID;
|
||||
|
||||
START -> ID [label = "[_a-zA-Z]"];
|
||||
ID -> ID [label = "[_0-9a-zA-Z]"];
|
||||
|
||||
}
|
35
LibIFPSCC/Scanner/FSAGraphs/identifier.svg
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: identifier Pages: 1 -->
|
||||
<svg width="160pt" height="143pt"
|
||||
viewBox="0.00 0.00 160.00 143.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 139)">
|
||||
<title>identifier</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-139 156,-139 156,4 -4,4"/>
|
||||
<!-- START -->
|
||||
<g id="node1" class="node"><title>START</title>
|
||||
<ellipse fill="none" stroke="black" cx="21.5" cy="-113.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="21.5" y="-110.4" font-family="monospace" font-size="12.00">START</text>
|
||||
</g>
|
||||
<!-- ID -->
|
||||
<g id="node2" class="node"><title>ID</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="21.5" cy="-21.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="21.5" y="-18.4" font-family="monospace" font-size="12.00">ID</text>
|
||||
</g>
|
||||
<!-- START->ID -->
|
||||
<g id="edge1" class="edge"><title>START->ID</title>
|
||||
<path fill="none" stroke="black" d="M21.5,-91.626C21.5,-80.2668 21.5,-65.9958 21.5,-53.3745"/>
|
||||
<polygon fill="black" stroke="black" points="25.0001,-53.2608 21.5,-43.2608 18.0001,-53.2609 25.0001,-53.2608"/>
|
||||
<text text-anchor="middle" x="55.5" y="-64.4" font-family="monospace" font-size="12.00">[_a-zA-Z]</text>
|
||||
</g>
|
||||
<!-- ID->ID -->
|
||||
<g id="edge2" class="edge"><title>ID->ID</title>
|
||||
<path fill="none" stroke="black" d="M41.4176,-29.8218C51.7165,-31.1021 61,-28.3281 61,-21.5 61,-17.1257 57.19,-14.4153 51.7439,-13.3687"/>
|
||||
<polygon fill="black" stroke="black" points="51.4804,-9.86337 41.4176,-13.1782 51.3513,-16.8622 51.4804,-9.86337"/>
|
||||
<text text-anchor="middle" x="106.5" y="-18.4" font-family="monospace" font-size="12.00">[_0-9a-zA-Z]</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
38
LibIFPSCC/Scanner/FSAGraphs/int.dot
Normal file
@ -0,0 +1,38 @@
|
||||
digraph int {
|
||||
node [shape = circle, width = 0.6, height = 0.6, fontname = "monospace", fixedsize = true, fontsize = 12];
|
||||
edge [fontname = "monospace", fontsize = 12];
|
||||
|
||||
node [shape = circle];
|
||||
START; ZX;
|
||||
|
||||
node [style = filled];
|
||||
Z D O H L U UL;
|
||||
|
||||
node [shape = circle, fixedsize = true];
|
||||
START -> Z [ label = "0" ];
|
||||
START -> D [ label = "[1-9]" ];
|
||||
|
||||
Z -> ZX [ label = "[Xx]" ];
|
||||
Z -> O [ label = "[0-7]" ];
|
||||
Z -> U [ label = "[Uu]" ];
|
||||
Z -> L [ label = "[Ll]" ];
|
||||
|
||||
D -> D [ label = "[0-9]" ];
|
||||
D -> U [ label = "[Uu]" ];
|
||||
D -> L [ label = "[Ll]" ];
|
||||
|
||||
ZX -> H [ label = "<hex>" ];
|
||||
|
||||
O -> O [ label = "[0-7]" ];
|
||||
O -> L [ label = "[Ll]" ];
|
||||
O -> U [ label = "[Uu]" ];
|
||||
|
||||
H -> H [ label = "<hex>" ];
|
||||
H -> L [ label = "[Ll]" ];
|
||||
H -> U [ label = "[Uu]" ];
|
||||
|
||||
L -> UL [ label = "[Uu]" ];
|
||||
|
||||
U -> UL [ label = "[Ll]" ];
|
||||
|
||||
}
|
166
LibIFPSCC/Scanner/FSAGraphs/int.svg
Normal file
@ -0,0 +1,166 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: int Pages: 1 -->
|
||||
<svg width="425pt" height="511pt"
|
||||
viewBox="0.00 0.00 424.50 511.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 507)">
|
||||
<title>int</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-507 420.5,-507 420.5,4 -4,4"/>
|
||||
<!-- START -->
|
||||
<g id="node1" class="node"><title>START</title>
|
||||
<ellipse fill="none" stroke="black" cx="91.5" cy="-481.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="91.5" y="-478.4" font-family="monospace" font-size="12.00">START</text>
|
||||
</g>
|
||||
<!-- Z -->
|
||||
<g id="node3" class="node"><title>Z</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="190.5" cy="-389.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="190.5" y="-386.4" font-family="monospace" font-size="12.00">Z</text>
|
||||
</g>
|
||||
<!-- START->Z -->
|
||||
<g id="edge1" class="edge"><title>START->Z</title>
|
||||
<path fill="none" stroke="black" d="M106.969,-466.438C123.147,-451.73 148.727,-428.475 167.32,-411.573"/>
|
||||
<polygon fill="black" stroke="black" points="169.768,-414.077 174.813,-404.761 165.059,-408.898 169.768,-414.077"/>
|
||||
<text text-anchor="middle" x="151.5" y="-432.4" font-family="monospace" font-size="12.00">0</text>
|
||||
</g>
|
||||
<!-- D -->
|
||||
<g id="node4" class="node"><title>D</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="21.5" cy="-205.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="21.5" y="-202.4" font-family="monospace" font-size="12.00">D</text>
|
||||
</g>
|
||||
<!-- START->D -->
|
||||
<g id="edge2" class="edge"><title>START->D</title>
|
||||
<path fill="none" stroke="black" d="M86.3527,-460.352C74.1661,-412.65 43.4973,-292.604 29.1381,-236.398"/>
|
||||
<polygon fill="black" stroke="black" points="32.5212,-235.5 26.6548,-226.677 25.739,-237.233 32.5212,-235.5"/>
|
||||
<text text-anchor="middle" x="77.5" y="-340.4" font-family="monospace" font-size="12.00">[1-9]</text>
|
||||
</g>
|
||||
<!-- ZX -->
|
||||
<g id="node2" class="node"><title>ZX</title>
|
||||
<ellipse fill="none" stroke="black" cx="190.5" cy="-297.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="190.5" y="-294.4" font-family="monospace" font-size="12.00">ZX</text>
|
||||
</g>
|
||||
<!-- H -->
|
||||
<g id="node6" class="node"><title>H</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="190.5" cy="-205.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="190.5" y="-202.4" font-family="monospace" font-size="12.00">H</text>
|
||||
</g>
|
||||
<!-- ZX->H -->
|
||||
<g id="edge10" class="edge"><title>ZX->H</title>
|
||||
<path fill="none" stroke="black" d="M190.5,-275.626C190.5,-264.267 190.5,-249.996 190.5,-237.375"/>
|
||||
<polygon fill="black" stroke="black" points="194,-237.261 190.5,-227.261 187,-237.261 194,-237.261"/>
|
||||
<text text-anchor="middle" x="209.5" y="-248.4" font-family="monospace" font-size="12.00"><hex></text>
|
||||
</g>
|
||||
<!-- Z->ZX -->
|
||||
<g id="edge3" class="edge"><title>Z->ZX</title>
|
||||
<path fill="none" stroke="black" d="M190.5,-367.626C190.5,-356.267 190.5,-341.996 190.5,-329.375"/>
|
||||
<polygon fill="black" stroke="black" points="194,-329.261 190.5,-319.261 187,-329.261 194,-329.261"/>
|
||||
<text text-anchor="middle" x="206" y="-340.4" font-family="monospace" font-size="12.00">[Xx]</text>
|
||||
</g>
|
||||
<!-- O -->
|
||||
<g id="node5" class="node"><title>O</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="307.5" cy="-205.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="307.5" y="-202.4" font-family="monospace" font-size="12.00">O</text>
|
||||
</g>
|
||||
<!-- Z->O -->
|
||||
<g id="edge4" class="edge"><title>Z->O</title>
|
||||
<path fill="none" stroke="black" d="M205.271,-373.793C211.843,-366.876 219.446,-358.33 225.5,-350 253.052,-312.091 278.825,-264.144 293.874,-234.398"/>
|
||||
<polygon fill="black" stroke="black" points="297.024,-235.924 298.372,-225.416 290.765,-232.79 297.024,-235.924"/>
|
||||
<text text-anchor="middle" x="290.5" y="-294.4" font-family="monospace" font-size="12.00">[0-7]</text>
|
||||
</g>
|
||||
<!-- L -->
|
||||
<g id="node7" class="node"><title>L</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="205.5" cy="-113.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="205.5" y="-110.4" font-family="monospace" font-size="12.00">L</text>
|
||||
</g>
|
||||
<!-- Z->L -->
|
||||
<g id="edge6" class="edge"><title>Z->L</title>
|
||||
<path fill="none" stroke="black" d="M178.189,-371.509C150.384,-330.871 88.4613,-226.014 132.5,-153 141.727,-137.702 159.284,-128.173 174.867,-122.422"/>
|
||||
<polygon fill="black" stroke="black" points="176.319,-125.628 184.715,-119.166 174.121,-118.982 176.319,-125.628"/>
|
||||
<text text-anchor="middle" x="139" y="-248.4" font-family="monospace" font-size="12.00">[Ll]</text>
|
||||
</g>
|
||||
<!-- U -->
|
||||
<g id="node8" class="node"><title>U</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="313.5" cy="-113.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="313.5" y="-110.4" font-family="monospace" font-size="12.00">U</text>
|
||||
</g>
|
||||
<!-- Z->U -->
|
||||
<g id="edge5" class="edge"><title>Z->U</title>
|
||||
<path fill="none" stroke="black" d="M210.527,-380.945C255.264,-362.737 361.871,-311.576 393.5,-227 405.147,-195.856 403.956,-178.401 382.5,-153 372.1,-140.688 356.714,-131.568 343.108,-125.304"/>
|
||||
<polygon fill="black" stroke="black" points="344.183,-121.957 333.612,-121.242 341.43,-128.393 344.183,-121.957"/>
|
||||
<text text-anchor="middle" x="401" y="-248.4" font-family="monospace" font-size="12.00">[Uu]</text>
|
||||
</g>
|
||||
<!-- D->D -->
|
||||
<g id="edge7" class="edge"><title>D->D</title>
|
||||
<path fill="none" stroke="black" d="M41.4176,-213.822C51.7165,-215.102 61,-212.328 61,-205.5 61,-201.126 57.19,-198.415 51.7439,-197.369"/>
|
||||
<polygon fill="black" stroke="black" points="51.4804,-193.863 41.4176,-197.178 51.3513,-200.862 51.4804,-193.863"/>
|
||||
<text text-anchor="middle" x="80" y="-202.4" font-family="monospace" font-size="12.00">[0-9]</text>
|
||||
</g>
|
||||
<!-- D->L -->
|
||||
<g id="edge9" class="edge"><title>D->L</title>
|
||||
<path fill="none" stroke="black" d="M34.4128,-187.92C44.057,-176.582 58.1717,-162.026 73.5,-153 105.055,-134.419 146.191,-124.282 174.094,-119.141"/>
|
||||
<polygon fill="black" stroke="black" points="174.838,-122.565 184.093,-117.409 173.642,-115.668 174.838,-122.565"/>
|
||||
<text text-anchor="middle" x="89" y="-156.4" font-family="monospace" font-size="12.00">[Ll]</text>
|
||||
</g>
|
||||
<!-- D->U -->
|
||||
<g id="edge8" class="edge"><title>D->U</title>
|
||||
<path fill="none" stroke="black" d="M41.3493,-196.475C68.435,-185.703 118.627,-166.378 162.5,-153 194.463,-143.253 203.118,-143.247 235.5,-135 251.222,-130.996 268.692,-126.416 283.107,-122.603"/>
|
||||
<polygon fill="black" stroke="black" points="284.225,-125.927 292.994,-119.981 282.431,-119.161 284.225,-125.927"/>
|
||||
<text text-anchor="middle" x="178" y="-156.4" font-family="monospace" font-size="12.00">[Uu]</text>
|
||||
</g>
|
||||
<!-- O->O -->
|
||||
<g id="edge11" class="edge"><title>O->O</title>
|
||||
<path fill="none" stroke="black" d="M327.418,-213.822C337.716,-215.102 347,-212.328 347,-205.5 347,-201.126 343.19,-198.415 337.744,-197.369"/>
|
||||
<polygon fill="black" stroke="black" points="337.48,-193.863 327.418,-197.178 337.351,-200.862 337.48,-193.863"/>
|
||||
<text text-anchor="middle" x="366" y="-202.4" font-family="monospace" font-size="12.00">[0-7]</text>
|
||||
</g>
|
||||
<!-- O->L -->
|
||||
<g id="edge12" class="edge"><title>O->L</title>
|
||||
<path fill="none" stroke="black" d="M290.746,-191.222C281.673,-183.921 270.338,-174.619 260.5,-166 249.63,-156.478 237.9,-145.584 228.076,-136.275"/>
|
||||
<polygon fill="black" stroke="black" points="230.313,-133.572 220.659,-129.206 225.483,-138.639 230.313,-133.572"/>
|
||||
<text text-anchor="middle" x="276" y="-156.4" font-family="monospace" font-size="12.00">[Ll]</text>
|
||||
</g>
|
||||
<!-- O->U -->
|
||||
<g id="edge13" class="edge"><title>O->U</title>
|
||||
<path fill="none" stroke="black" d="M324.656,-192.275C332.604,-185.537 341.221,-176.426 345.5,-166 349.889,-155.305 344.623,-144.307 337.177,-135.236"/>
|
||||
<polygon fill="black" stroke="black" points="339.64,-132.745 330.262,-127.816 334.519,-137.517 339.64,-132.745"/>
|
||||
<text text-anchor="middle" x="363" y="-156.4" font-family="monospace" font-size="12.00">[Uu]</text>
|
||||
</g>
|
||||
<!-- H->H -->
|
||||
<g id="edge14" class="edge"><title>H->H</title>
|
||||
<path fill="none" stroke="black" d="M210.418,-213.822C220.716,-215.102 230,-212.328 230,-205.5 230,-201.126 226.19,-198.415 220.744,-197.369"/>
|
||||
<polygon fill="black" stroke="black" points="220.48,-193.863 210.418,-197.178 220.351,-200.862 220.48,-193.863"/>
|
||||
<text text-anchor="middle" x="249" y="-202.4" font-family="monospace" font-size="12.00"><hex></text>
|
||||
</g>
|
||||
<!-- H->L -->
|
||||
<g id="edge15" class="edge"><title>H->L</title>
|
||||
<path fill="none" stroke="black" d="M193.904,-184.078C195.841,-172.455 198.304,-157.678 200.461,-144.734"/>
|
||||
<polygon fill="black" stroke="black" points="203.918,-145.281 202.11,-134.842 197.013,-144.13 203.918,-145.281"/>
|
||||
<text text-anchor="middle" x="215" y="-156.4" font-family="monospace" font-size="12.00">[Ll]</text>
|
||||
</g>
|
||||
<!-- H->U -->
|
||||
<g id="edge16" class="edge"><title>H->U</title>
|
||||
<path fill="none" stroke="black" d="M211.661,-201.42C234.102,-197.129 269.313,-187.327 291.5,-166 297.729,-160.012 302.285,-151.976 305.578,-144.034"/>
|
||||
<polygon fill="black" stroke="black" points="308.902,-145.133 308.996,-134.539 302.316,-142.762 308.902,-145.133"/>
|
||||
<text text-anchor="middle" x="317" y="-156.4" font-family="monospace" font-size="12.00">[Uu]</text>
|
||||
</g>
|
||||
<!-- UL -->
|
||||
<g id="node9" class="node"><title>UL</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="265.5" cy="-21.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="265.5" y="-18.4" font-family="monospace" font-size="12.00">UL</text>
|
||||
</g>
|
||||
<!-- L->UL -->
|
||||
<g id="edge17" class="edge"><title>L->UL</title>
|
||||
<path fill="none" stroke="black" d="M217.069,-95.1471C225.95,-81.8248 238.351,-63.2242 248.38,-48.1798"/>
|
||||
<polygon fill="black" stroke="black" points="251.332,-50.0616 253.967,-39.7996 245.508,-46.1787 251.332,-50.0616"/>
|
||||
<text text-anchor="middle" x="255" y="-64.4" font-family="monospace" font-size="12.00">[Uu]</text>
|
||||
</g>
|
||||
<!-- U->UL -->
|
||||
<g id="edge18" class="edge"><title>U->UL</title>
|
||||
<path fill="none" stroke="black" d="M303.786,-94.2872C297,-81.5626 287.817,-64.3452 280.146,-49.9617"/>
|
||||
<polygon fill="black" stroke="black" points="283.158,-48.1711 275.364,-40.9947 276.982,-51.4653 283.158,-48.1711"/>
|
||||
<text text-anchor="middle" x="308" y="-64.4" font-family="monospace" font-size="12.00">[Ll]</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 10 KiB |
13
LibIFPSCC/Scanner/FSAGraphs/newline.dot
Normal file
@ -0,0 +1,13 @@
|
||||
digraph newline {
|
||||
node [shape = circle, height = 0.6, fontname = "monospace", fixedsize = true, fontsize = 12];
|
||||
edge [fontname = "monospace", fontsize = 12];
|
||||
|
||||
node [shape = circle];
|
||||
START;
|
||||
|
||||
node [style = filled];
|
||||
NEWLINE;
|
||||
|
||||
START -> NEWLINE [label = "\\n"];
|
||||
|
||||
}
|
57
LibIFPSCC/Scanner/FSAGraphs/operator.dot
Normal file
@ -0,0 +1,57 @@
|
||||
digraph operator {
|
||||
rankdir = LR;
|
||||
|
||||
node [shape = circle, width = 0.7, height = 0.7, fontname = "monospace", fixedsize = true, fontsize = 12];
|
||||
edge [fontname = "monospace", fontsize = 12];
|
||||
|
||||
node [shape = circle];
|
||||
START;
|
||||
|
||||
node [style = filled];
|
||||
FINISH; SUB; ADD; AMP; MULT; LT; GT; EQ; OR; NOT; DIV; MOD; XOR; LTLT; GTGT;
|
||||
|
||||
|
||||
START -> SUB [label = "-"];
|
||||
START -> ADD [label = "+"];
|
||||
START -> AMP [label = "&"];
|
||||
START -> MULT [label = "*"];
|
||||
START -> LT [label = "<"];
|
||||
START -> GT [label = ">"];
|
||||
START -> EQ [label = "="];
|
||||
START -> OR [label = "|"];
|
||||
START -> NOT [label = "!"];
|
||||
START -> DIV [label = "/"];
|
||||
START -> MOD [label = "%"];
|
||||
START -> XOR [label = "^"];
|
||||
START -> FINISH [label = "[\\[\\]().,?:~;{}]"];
|
||||
|
||||
SUB -> FINISH [label = "[>-=]"];
|
||||
|
||||
ADD -> FINISH [label = "[+=]"];
|
||||
|
||||
AMP -> FINISH [label = "[&=]"];
|
||||
|
||||
MULT -> FINISH [label = "="];
|
||||
|
||||
LT -> FINISH [label = "="];
|
||||
LT -> LTLT [label = "<"];
|
||||
|
||||
GT -> FINISH [label = "="];
|
||||
GT -> GTGT [label = ">"];
|
||||
|
||||
EQ -> FINISH [label = "="];
|
||||
|
||||
OR -> FINISH [label = "[|=]"];
|
||||
|
||||
NOT -> FINISH [label = "="];
|
||||
|
||||
DIV -> FINISH [label = "="];
|
||||
|
||||
MOD -> FINISH [label = "="];
|
||||
|
||||
XOR -> FINISH [label = "="];
|
||||
|
||||
LTLT -> FINISH [label = "="];
|
||||
|
||||
GTGT -> FINISH [label = "="];
|
||||
}
|
267
LibIFPSCC/Scanner/FSAGraphs/operator.svg
Normal file
@ -0,0 +1,267 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: operator Pages: 1 -->
|
||||
<svg width="476pt" height="814pt"
|
||||
viewBox="0.00 0.00 476.00 814.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 810)">
|
||||
<title>operator</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-810 472,-810 472,4 -4,4"/>
|
||||
<!-- START -->
|
||||
<g id="node1" class="node"><title>START</title>
|
||||
<ellipse fill="none" stroke="black" cx="25" cy="-382" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="25" y="-378.9" font-family="monospace" font-size="12.00">START</text>
|
||||
</g>
|
||||
<!-- FINISH -->
|
||||
<g id="node2" class="node"><title>FINISH</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="443" cy="-424" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="443" y="-420.9" font-family="monospace" font-size="12.00">FINISH</text>
|
||||
</g>
|
||||
<!-- START->FINISH -->
|
||||
<g id="edge13" class="edge"><title>START->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M25.756,-407.139C26.0751,-458.834 31.0822,-580.014 68,-673 90.7485,-730.297 104.782,-752.052 162,-775 267.256,-817.214 352.609,-786.029 400,-683 434.211,-608.625 441.087,-511.032 442.148,-459.475"/>
|
||||
<polygon fill="black" stroke="black" points="445.651,-459.245 442.293,-449.196 438.652,-459.146 445.651,-459.245"/>
|
||||
<text text-anchor="middle" x="222.5" y="-796.4" font-family="monospace" font-size="12.00">[\[\]().,?:~;{}]</text>
|
||||
</g>
|
||||
<!-- SUB -->
|
||||
<g id="node3" class="node"><title>SUB</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="222.5" cy="-745" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="222.5" y="-741.9" font-family="monospace" font-size="12.00">SUB</text>
|
||||
</g>
|
||||
<!-- START->SUB -->
|
||||
<g id="edge1" class="edge"><title>START->SUB</title>
|
||||
<path fill="none" stroke="black" d="M28.0299,-406.947C32.9912,-464.081 46.8905,-602.666 68,-644 92.507,-691.986 151.497,-720.619 188.799,-734.546"/>
|
||||
<polygon fill="black" stroke="black" points="187.644,-737.851 198.239,-737.931 190.007,-731.261 187.644,-737.851"/>
|
||||
<text text-anchor="middle" x="72" y="-659.4" font-family="monospace" font-size="12.00">-</text>
|
||||
</g>
|
||||
<!-- ADD -->
|
||||
<g id="node4" class="node"><title>ADD</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="222.5" cy="-682" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="222.5" y="-678.9" font-family="monospace" font-size="12.00">ADD</text>
|
||||
</g>
|
||||
<!-- START->ADD -->
|
||||
<g id="edge2" class="edge"><title>START->ADD</title>
|
||||
<path fill="none" stroke="black" d="M27.8873,-407.202C32.308,-460.771 44.7589,-583.855 68,-618 94.8379,-657.429 150.956,-672.621 187.391,-678.438"/>
|
||||
<polygon fill="black" stroke="black" points="187.162,-681.941 197.558,-679.901 188.159,-675.012 187.162,-681.941"/>
|
||||
<text text-anchor="middle" x="72" y="-630.4" font-family="monospace" font-size="12.00">+</text>
|
||||
</g>
|
||||
<!-- AMP -->
|
||||
<g id="node5" class="node"><title>AMP</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="222.5" cy="-625" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="222.5" y="-621.9" font-family="monospace" font-size="12.00">AMP</text>
|
||||
</g>
|
||||
<!-- START->AMP -->
|
||||
<g id="edge3" class="edge"><title>START->AMP</title>
|
||||
<path fill="none" stroke="black" d="M27.474,-407.059C30.9843,-457.733 41.6035,-569.79 68,-596 99.2189,-626.999 152.509,-630.356 187.362,-628.656"/>
|
||||
<polygon fill="black" stroke="black" points="187.792,-632.136 197.542,-627.991 187.335,-625.151 187.792,-632.136"/>
|
||||
<text text-anchor="middle" x="72" y="-604.4" font-family="monospace" font-size="12.00">&</text>
|
||||
</g>
|
||||
<!-- MULT -->
|
||||
<g id="node6" class="node"><title>MULT</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="326" cy="-597" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="326" y="-593.9" font-family="monospace" font-size="12.00">MULT</text>
|
||||
</g>
|
||||
<!-- START->MULT -->
|
||||
<g id="edge4" class="edge"><title>START->MULT</title>
|
||||
<path fill="none" stroke="black" d="M32.4715,-406.122C41.5402,-435.514 60.7629,-484.847 94,-514 151.605,-564.526 242.248,-585.022 291.101,-592.747"/>
|
||||
<polygon fill="black" stroke="black" points="290.743,-596.232 301.15,-594.247 291.777,-589.309 290.743,-596.232"/>
|
||||
<text text-anchor="middle" x="119" y="-549.4" font-family="monospace" font-size="12.00">*</text>
|
||||
</g>
|
||||
<!-- LT -->
|
||||
<g id="node7" class="node"><title>LT</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="119" cy="-480" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="119" y="-476.9" font-family="monospace" font-size="12.00">LT</text>
|
||||
</g>
|
||||
<!-- START->LT -->
|
||||
<g id="edge5" class="edge"><title>START->LT</title>
|
||||
<path fill="none" stroke="black" d="M42.8742,-399.975C57.2693,-415.309 78.129,-437.529 94.2171,-454.666"/>
|
||||
<polygon fill="black" stroke="black" points="91.8366,-457.244 101.233,-462.139 96.9401,-452.453 91.8366,-457.244"/>
|
||||
<text text-anchor="middle" x="72" y="-436.4" font-family="monospace" font-size="12.00"><</text>
|
||||
</g>
|
||||
<!-- GT -->
|
||||
<g id="node8" class="node"><title>GT</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="119" cy="-382" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="119" y="-378.9" font-family="monospace" font-size="12.00">GT</text>
|
||||
</g>
|
||||
<!-- START->GT -->
|
||||
<g id="edge6" class="edge"><title>START->GT</title>
|
||||
<path fill="none" stroke="black" d="M50.1719,-382C60.4195,-382 72.5485,-382 83.6959,-382"/>
|
||||
<polygon fill="black" stroke="black" points="83.9896,-385.5 93.9895,-382 83.9895,-378.5 83.9896,-385.5"/>
|
||||
<text text-anchor="middle" x="72" y="-385.4" font-family="monospace" font-size="12.00">></text>
|
||||
</g>
|
||||
<!-- EQ -->
|
||||
<g id="node9" class="node"><title>EQ</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="222.5" cy="-318" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="222.5" y="-314.9" font-family="monospace" font-size="12.00">EQ</text>
|
||||
</g>
|
||||
<!-- START->EQ -->
|
||||
<g id="edge7" class="edge"><title>START->EQ</title>
|
||||
<path fill="none" stroke="black" d="M47.1763,-369.808C60.3169,-362.651 77.742,-353.886 94,-348 124.82,-336.842 161.354,-328.738 187.397,-323.787"/>
|
||||
<polygon fill="black" stroke="black" points="188.322,-327.175 197.522,-321.92 187.053,-320.291 188.322,-327.175"/>
|
||||
<text text-anchor="middle" x="72" y="-361.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- OR -->
|
||||
<g id="node10" class="node"><title>OR</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="326" cy="-246" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="326" y="-242.9" font-family="monospace" font-size="12.00">OR</text>
|
||||
</g>
|
||||
<!-- START->OR -->
|
||||
<g id="edge8" class="edge"><title>START->OR</title>
|
||||
<path fill="none" stroke="black" d="M41.6589,-363.07C54.48,-348.568 73.7498,-328.918 94,-316 121.278,-298.599 131.23,-300.007 162,-290 206.268,-275.603 258.342,-262.136 291.5,-253.985"/>
|
||||
<polygon fill="black" stroke="black" points="292.681,-257.3 301.568,-251.531 291.023,-250.499 292.681,-257.3"/>
|
||||
<text text-anchor="middle" x="119" y="-319.4" font-family="monospace" font-size="12.00">|</text>
|
||||
</g>
|
||||
<!-- NOT -->
|
||||
<g id="node11" class="node"><title>NOT</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="222.5" cy="-222" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="222.5" y="-218.9" font-family="monospace" font-size="12.00">NOT</text>
|
||||
</g>
|
||||
<!-- START->NOT -->
|
||||
<g id="edge9" class="edge"><title>START->NOT</title>
|
||||
<path fill="none" stroke="black" d="M26.4981,-357.04C28.5314,-326.757 36.7662,-276.367 68,-249 100.829,-220.235 153.234,-217 187.509,-218.543"/>
|
||||
<polygon fill="black" stroke="black" points="187.328,-222.038 197.522,-219.152 187.753,-215.051 187.328,-222.038"/>
|
||||
<text text-anchor="middle" x="72" y="-252.4" font-family="monospace" font-size="12.00">!</text>
|
||||
</g>
|
||||
<!-- DIV -->
|
||||
<g id="node12" class="node"><title>DIV</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="326" cy="-129" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="326" y="-125.9" font-family="monospace" font-size="12.00">DIV</text>
|
||||
</g>
|
||||
<!-- START->DIV -->
|
||||
<g id="edge10" class="edge"><title>START->DIV</title>
|
||||
<path fill="none" stroke="black" d="M27.2748,-356.99C30.805,-316.931 44.0873,-239.214 94,-203 154.684,-158.971 242.686,-140.392 290.659,-133.159"/>
|
||||
<polygon fill="black" stroke="black" points="291.424,-136.585 300.828,-131.706 290.434,-129.656 291.424,-136.585"/>
|
||||
<text text-anchor="middle" x="119" y="-206.4" font-family="monospace" font-size="12.00">/</text>
|
||||
</g>
|
||||
<!-- MOD -->
|
||||
<g id="node13" class="node"><title>MOD</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="119" cy="-103" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="119" y="-99.9" font-family="monospace" font-size="12.00">MOD</text>
|
||||
</g>
|
||||
<!-- START->MOD -->
|
||||
<g id="edge11" class="edge"><title>START->MOD</title>
|
||||
<path fill="none" stroke="black" d="M25.6348,-356.761C26.0288,-312.435 31.5511,-218.872 68,-151 73.5357,-140.692 82.0794,-131.29 90.5476,-123.592"/>
|
||||
<polygon fill="black" stroke="black" points="92.8966,-126.189 98.2148,-117.025 88.343,-120.872 92.8966,-126.189"/>
|
||||
<text text-anchor="middle" x="72" y="-154.4" font-family="monospace" font-size="12.00">%</text>
|
||||
</g>
|
||||
<!-- XOR -->
|
||||
<g id="node14" class="node"><title>XOR</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="119" cy="-25" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="119" y="-21.9" font-family="monospace" font-size="12.00">XOR</text>
|
||||
</g>
|
||||
<!-- START->XOR -->
|
||||
<g id="edge12" class="edge"><title>START->XOR</title>
|
||||
<path fill="none" stroke="black" d="M27.6704,-356.808C31.3085,-309.862 41.8258,-206.214 68,-123 75.5847,-98.8866 88.6901,-73.5138 99.5811,-54.6282"/>
|
||||
<polygon fill="black" stroke="black" points="102.656,-56.3048 104.716,-45.9122 96.6243,-52.7517 102.656,-56.3048"/>
|
||||
<text text-anchor="middle" x="72" y="-126.4" font-family="monospace" font-size="12.00">^</text>
|
||||
</g>
|
||||
<!-- SUB->FINISH -->
|
||||
<g id="edge14" class="edge"><title>SUB->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M247.299,-740.706C287.381,-732.035 367.188,-708.244 400,-652 435.637,-590.913 442.14,-506.271 442.714,-459.187"/>
|
||||
<polygon fill="black" stroke="black" points="446.214,-459.188 442.752,-449.175 439.214,-459.161 446.214,-459.188"/>
|
||||
<text text-anchor="middle" x="326" y="-727.4" font-family="monospace" font-size="12.00">[>-=]</text>
|
||||
</g>
|
||||
<!-- ADD->FINISH -->
|
||||
<g id="edge15" class="edge"><title>ADD->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M247.797,-682.366C287.502,-681.475 365.332,-673.16 400,-624 434.872,-574.55 441.921,-502.223 442.742,-459.447"/>
|
||||
<polygon fill="black" stroke="black" points="446.243,-459.27 442.829,-449.24 439.243,-459.21 446.243,-459.27"/>
|
||||
<text text-anchor="middle" x="326" y="-679.4" font-family="monospace" font-size="12.00">[+=]</text>
|
||||
</g>
|
||||
<!-- AMP->FINISH -->
|
||||
<g id="edge16" class="edge"><title>AMP->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M247.179,-630.965C273.279,-636.44 316.18,-642.158 351,-631 376.486,-622.833 384.999,-618.163 400,-596 428.07,-554.528 437.423,-496.181 440.513,-459.234"/>
|
||||
<polygon fill="black" stroke="black" points="444.026,-459.181 441.257,-448.955 437.044,-458.676 444.026,-459.181"/>
|
||||
<text text-anchor="middle" x="326" y="-640.4" font-family="monospace" font-size="12.00">[&=]</text>
|
||||
</g>
|
||||
<!-- MULT->FINISH -->
|
||||
<g id="edge17" class="edge"><title>MULT->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M349.774,-589.04C365.92,-582.307 386.953,-571.151 400,-555 422.389,-527.284 432.947,-487.385 437.859,-458.914"/>
|
||||
<polygon fill="black" stroke="black" points="441.324,-459.409 439.425,-448.986 434.41,-458.318 441.324,-459.409"/>
|
||||
<text text-anchor="middle" x="384.5" y="-582.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- LT->FINISH -->
|
||||
<g id="edge18" class="edge"><title>LT->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M141.99,-489.989C183.84,-507.543 277.334,-539.737 351,-514 381.741,-503.26 407.107,-475.323 423.178,-453.398"/>
|
||||
<polygon fill="black" stroke="black" points="426.171,-455.227 429.078,-445.039 420.452,-451.191 426.171,-455.227"/>
|
||||
<text text-anchor="middle" x="326" y="-526.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- LTLT -->
|
||||
<g id="node15" class="node"><title>LTLT</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="326" cy="-480" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="326" y="-476.9" font-family="monospace" font-size="12.00">LTLT</text>
|
||||
</g>
|
||||
<!-- LT->LTLT -->
|
||||
<g id="edge19" class="edge"><title>LT->LTLT</title>
|
||||
<path fill="none" stroke="black" d="M144.321,-480C180.616,-480 249.108,-480 290.544,-480"/>
|
||||
<polygon fill="black" stroke="black" points="290.71,-483.5 300.71,-480 290.71,-476.5 290.71,-483.5"/>
|
||||
<text text-anchor="middle" x="222.5" y="-483.4" font-family="monospace" font-size="12.00"><</text>
|
||||
</g>
|
||||
<!-- GT->FINISH -->
|
||||
<g id="edge20" class="edge"><title>GT->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M142.934,-389.321C149.076,-391.07 155.748,-392.781 162,-394 249.542,-411.065 354.508,-419.043 407.608,-422.219"/>
|
||||
<polygon fill="black" stroke="black" points="407.636,-425.727 417.821,-422.809 408.04,-418.738 407.636,-425.727"/>
|
||||
<text text-anchor="middle" x="326" y="-420.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- GTGT -->
|
||||
<g id="node16" class="node"><title>GTGT</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="326" cy="-363" rx="25" ry="25"/>
|
||||
<text text-anchor="middle" x="326" y="-359.9" font-family="monospace" font-size="12.00">GTGT</text>
|
||||
</g>
|
||||
<!-- GT->GTGT -->
|
||||
<g id="edge21" class="edge"><title>GT->GTGT</title>
|
||||
<path fill="none" stroke="black" d="M143.917,-379.783C180.158,-376.424 249.155,-370.03 290.725,-366.177"/>
|
||||
<polygon fill="black" stroke="black" points="291.284,-369.64 300.918,-365.232 290.638,-362.67 291.284,-369.64"/>
|
||||
<text text-anchor="middle" x="222.5" y="-380.4" font-family="monospace" font-size="12.00">></text>
|
||||
</g>
|
||||
<!-- EQ->FINISH -->
|
||||
<g id="edge22" class="edge"><title>EQ->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M246.861,-311.79C273.433,-306.011 317.477,-300.41 351,-316 384.808,-331.723 410.561,-366.876 425.899,-392.827"/>
|
||||
<polygon fill="black" stroke="black" points="422.893,-394.621 430.887,-401.575 428.975,-391.154 422.893,-394.621"/>
|
||||
<text text-anchor="middle" x="326" y="-319.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- OR->FINISH -->
|
||||
<g id="edge23" class="edge"><title>OR->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M350.529,-251.468C366.614,-256.372 387.251,-265.148 400,-280 426.288,-310.624 436.14,-356.945 439.822,-388.733"/>
|
||||
<polygon fill="black" stroke="black" points="436.363,-389.315 440.849,-398.913 443.327,-388.612 436.363,-389.315"/>
|
||||
<text text-anchor="middle" x="384.5" y="-283.4" font-family="monospace" font-size="12.00">[|=]</text>
|
||||
</g>
|
||||
<!-- NOT->FINISH -->
|
||||
<g id="edge24" class="edge"><title>NOT->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M245.186,-210.66C271.333,-198.631 316.109,-183.355 351,-199 421.976,-230.825 437.909,-333.75 441.276,-388.685"/>
|
||||
<polygon fill="black" stroke="black" points="437.78,-388.866 441.781,-398.676 444.771,-388.512 437.78,-388.866"/>
|
||||
<text text-anchor="middle" x="326" y="-202.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- DIV->FINISH -->
|
||||
<g id="edge25" class="edge"><title>DIV->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M350.763,-133.899C367.175,-138.557 388.113,-147.274 400,-163 426.422,-197.957 436.795,-325.98 440.361,-388.782"/>
|
||||
<polygon fill="black" stroke="black" points="436.878,-389.2 440.913,-398.996 443.868,-388.822 436.878,-389.2"/>
|
||||
<text text-anchor="middle" x="384.5" y="-166.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- MOD->FINISH -->
|
||||
<g id="edge26" class="edge"><title>MOD->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M142.531,-93.9105C185.294,-78.1399 280.266,-50.2838 351,-82 380.873,-95.3946 386.158,-107.332 400,-137 420.614,-181.184 434.073,-322.734 439.407,-389.073"/>
|
||||
<polygon fill="black" stroke="black" points="435.918,-389.354 440.193,-399.048 442.897,-388.803 435.918,-389.354"/>
|
||||
<text text-anchor="middle" x="326" y="-85.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- XOR->FINISH -->
|
||||
<g id="edge27" class="edge"><title>XOR->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M143.668,-20.8498C201.529,-12.2454 349.124,-0.0025173 400,-88 429.167,-138.448 438.347,-312.266 441.005,-388.12"/>
|
||||
<polygon fill="black" stroke="black" points="437.522,-388.696 441.351,-398.575 444.518,-388.464 437.522,-388.696"/>
|
||||
<text text-anchor="middle" x="326" y="-43.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- LTLT->FINISH -->
|
||||
<g id="edge28" class="edge"><title>LTLT->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M348.901,-469.335C366.487,-460.772 391.396,-448.642 411.024,-439.084"/>
|
||||
<polygon fill="black" stroke="black" points="412.722,-442.15 420.18,-434.625 409.657,-435.857 412.722,-442.15"/>
|
||||
<text text-anchor="middle" x="384.5" y="-461.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
<!-- GTGT->FINISH -->
|
||||
<g id="edge29" class="edge"><title>GTGT->FINISH</title>
|
||||
<path fill="none" stroke="black" d="M349.43,-372.502C364.073,-378.935 383.469,-387.892 400,-397 404.328,-399.384 408.81,-402.046 413.164,-404.742"/>
|
||||
<polygon fill="black" stroke="black" points="411.513,-407.84 421.83,-410.249 415.267,-401.932 411.513,-407.84"/>
|
||||
<text text-anchor="middle" x="384.5" y="-400.4" font-family="monospace" font-size="12.00">=</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 17 KiB |
14
LibIFPSCC/Scanner/FSAGraphs/space.dot
Normal file
@ -0,0 +1,14 @@
|
||||
digraph space {
|
||||
node [shape = circle, height = 0.6, fontname = "monospace", fixedsize = true, fontsize = 12];
|
||||
edge [fontname = "monospace", fontsize = 12];
|
||||
|
||||
node [shape = circle];
|
||||
START;
|
||||
|
||||
node [style = filled];
|
||||
SPACE;
|
||||
|
||||
START -> SPACE [label = "[<space>\\t\\r\\f\\v]"];
|
||||
SPACE -> SPACE [label = "[<space>\\t\\r\\f\\v]"];
|
||||
|
||||
}
|
35
LibIFPSCC/Scanner/FSAGraphs/space.svg
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: space Pages: 1 -->
|
||||
<svg width="197pt" height="143pt"
|
||||
viewBox="0.00 0.00 197.00 143.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 139)">
|
||||
<title>space</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-139 193,-139 193,4 -4,4"/>
|
||||
<!-- START -->
|
||||
<g id="node1" class="node"><title>START</title>
|
||||
<ellipse fill="none" stroke="black" cx="21.5" cy="-113.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="21.5" y="-110.4" font-family="monospace" font-size="12.00">START</text>
|
||||
</g>
|
||||
<!-- SPACE -->
|
||||
<g id="node2" class="node"><title>SPACE</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="21.5" cy="-21.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="21.5" y="-18.4" font-family="monospace" font-size="12.00">SPACE</text>
|
||||
</g>
|
||||
<!-- START->SPACE -->
|
||||
<g id="edge1" class="edge"><title>START->SPACE</title>
|
||||
<path fill="none" stroke="black" d="M21.5,-91.626C21.5,-80.2668 21.5,-65.9958 21.5,-53.3745"/>
|
||||
<polygon fill="black" stroke="black" points="25.0001,-53.2608 21.5,-43.2608 18.0001,-53.2609 25.0001,-53.2608"/>
|
||||
<text text-anchor="middle" x="85.5" y="-64.4" font-family="monospace" font-size="12.00">[<space>\t\r\f\v]</text>
|
||||
</g>
|
||||
<!-- SPACE->SPACE -->
|
||||
<g id="edge2" class="edge"><title>SPACE->SPACE</title>
|
||||
<path fill="none" stroke="black" d="M41.4176,-29.8218C51.7165,-31.1021 61,-28.3281 61,-21.5 61,-17.1257 57.19,-14.4153 51.7439,-13.3687"/>
|
||||
<polygon fill="black" stroke="black" points="51.4804,-9.86337 41.4176,-13.1782 51.3513,-16.8622 51.4804,-9.86337"/>
|
||||
<text text-anchor="middle" x="125" y="-18.4" font-family="monospace" font-size="12.00">[<space>\t\r\f\v]</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
20
LibIFPSCC/Scanner/FSAGraphs/string.dot
Normal file
@ -0,0 +1,20 @@
|
||||
digraph String {
|
||||
node [shape = circle, height = 0.6, fontname = "monospace", fixedsize = true, fontsize = 12];
|
||||
edge [fontname = "monospace", fontsize = 12];
|
||||
|
||||
node [shape = circle];
|
||||
START; L; Q;
|
||||
|
||||
node [style = filled];
|
||||
QQ;
|
||||
|
||||
START -> L [label = "L"];
|
||||
START -> Q [label = "\\\""];
|
||||
|
||||
L -> Q [label = "\\\""];
|
||||
|
||||
Q -> Q [label = "<FSAChar succeed>"];
|
||||
|
||||
Q -> QQ [label = "\\\""];
|
||||
|
||||
}
|
63
LibIFPSCC/Scanner/FSAGraphs/string.svg
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: String Pages: 1 -->
|
||||
<svg width="233pt" height="327pt"
|
||||
viewBox="0.00 0.00 233.00 327.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 323)">
|
||||
<title>String</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-323 229,-323 229,4 -4,4"/>
|
||||
<!-- START -->
|
||||
<g id="node1" class="node"><title>START</title>
|
||||
<ellipse fill="none" stroke="black" cx="64.5" cy="-297.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="64.5" y="-294.4" font-family="monospace" font-size="12.00">START</text>
|
||||
</g>
|
||||
<!-- L -->
|
||||
<g id="node2" class="node"><title>L</title>
|
||||
<ellipse fill="none" stroke="black" cx="21.5" cy="-205.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="21.5" y="-202.4" font-family="monospace" font-size="12.00">L</text>
|
||||
</g>
|
||||
<!-- START->L -->
|
||||
<g id="edge1" class="edge"><title>START->L</title>
|
||||
<path fill="none" stroke="black" d="M55.5904,-277.852C49.5942,-265.302 41.5808,-248.53 34.8299,-234.4"/>
|
||||
<polygon fill="black" stroke="black" points="37.8673,-232.638 30.3982,-225.124 31.5512,-235.656 37.8673,-232.638"/>
|
||||
<text text-anchor="middle" x="50.5" y="-248.4" font-family="monospace" font-size="12.00">L</text>
|
||||
</g>
|
||||
<!-- Q -->
|
||||
<g id="node3" class="node"><title>Q</title>
|
||||
<ellipse fill="none" stroke="black" cx="57.5" cy="-113.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="57.5" y="-110.4" font-family="monospace" font-size="12.00">Q</text>
|
||||
</g>
|
||||
<!-- START->Q -->
|
||||
<g id="edge2" class="edge"><title>START->Q</title>
|
||||
<path fill="none" stroke="black" d="M64.9621,-275.554C65.4069,-247.579 65.6167,-196.491 62.5,-153 62.3145,-150.412 62.0717,-147.731 61.7931,-145.045"/>
|
||||
<polygon fill="black" stroke="black" points="65.2361,-144.356 60.586,-134.836 58.2845,-145.178 65.2361,-144.356"/>
|
||||
<text text-anchor="middle" x="73.5" y="-202.4" font-family="monospace" font-size="12.00">\"</text>
|
||||
</g>
|
||||
<!-- L->Q -->
|
||||
<g id="edge3" class="edge"><title>L->Q</title>
|
||||
<path fill="none" stroke="black" d="M29.3115,-184.971C34.2038,-172.741 40.5933,-156.767 46.052,-143.12"/>
|
||||
<polygon fill="black" stroke="black" points="49.3632,-144.266 49.8275,-133.681 42.8638,-141.666 49.3632,-144.266"/>
|
||||
<text text-anchor="middle" x="50.5" y="-156.4" font-family="monospace" font-size="12.00">\"</text>
|
||||
</g>
|
||||
<!-- Q->Q -->
|
||||
<g id="edge4" class="edge"><title>Q->Q</title>
|
||||
<path fill="none" stroke="black" d="M77.4176,-121.822C87.7165,-123.102 97,-120.328 97,-113.5 97,-109.126 93.19,-106.415 87.7439,-105.369"/>
|
||||
<polygon fill="black" stroke="black" points="87.4804,-101.863 77.4176,-105.178 87.3513,-108.862 87.4804,-101.863"/>
|
||||
<text text-anchor="middle" x="161" y="-110.4" font-family="monospace" font-size="12.00"><FSAChar succeed></text>
|
||||
</g>
|
||||
<!-- QQ -->
|
||||
<g id="node4" class="node"><title>QQ</title>
|
||||
<ellipse fill="lightgrey" stroke="black" cx="57.5" cy="-21.5" rx="21.5" ry="21.5"/>
|
||||
<text text-anchor="middle" x="57.5" y="-18.4" font-family="monospace" font-size="12.00">QQ</text>
|
||||
</g>
|
||||
<!-- Q->QQ -->
|
||||
<g id="edge5" class="edge"><title>Q->QQ</title>
|
||||
<path fill="none" stroke="black" d="M57.5,-91.626C57.5,-80.2668 57.5,-65.9958 57.5,-53.3745"/>
|
||||
<polygon fill="black" stroke="black" points="61.0001,-53.2608 57.5,-43.2608 54.0001,-53.2609 61.0001,-53.2608"/>
|
||||
<text text-anchor="middle" x="65.5" y="-64.4" font-family="monospace" font-size="12.00">\"</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.6 KiB |
268
LibIFPSCC/Scanner/Float.cs
Normal file
@ -0,0 +1,268 @@
|
||||
using System;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
/// <summary>
|
||||
/// The token representing a floating number.
|
||||
/// It can either be a float or double.
|
||||
/// </summary>
|
||||
public sealed class TokenFloat : Token {
|
||||
|
||||
public enum FloatSuffix {
|
||||
NONE,
|
||||
F,
|
||||
L
|
||||
}
|
||||
|
||||
public TokenFloat(Double value, FloatSuffix suffix, String source) {
|
||||
this.Value = value;
|
||||
this.Suffix = suffix;
|
||||
this.Source = source;
|
||||
}
|
||||
|
||||
public override TokenKind Kind { get; } = TokenKind.FLOAT;
|
||||
public Double Value { get; }
|
||||
public String Source { get; }
|
||||
public FloatSuffix Suffix { get; }
|
||||
|
||||
public override String ToString() {
|
||||
String str = this.Kind.ToString();
|
||||
switch (this.Suffix) {
|
||||
case FloatSuffix.F:
|
||||
str += "(float)";
|
||||
break;
|
||||
case FloatSuffix.L:
|
||||
str += "(long double)";
|
||||
break;
|
||||
default:
|
||||
str += "(double)";
|
||||
break;
|
||||
}
|
||||
return str + $" [{Line}:{Column}]: " + this.Value + " \"" + this.Source + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The FSA for scanning a float.
|
||||
/// </summary>
|
||||
public sealed class FSAFloat : FSA {
|
||||
private enum State {
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
D,
|
||||
P,
|
||||
DP,
|
||||
PD,
|
||||
DE,
|
||||
DES,
|
||||
DED,
|
||||
PDF,
|
||||
DPL
|
||||
};
|
||||
|
||||
private String _raw;
|
||||
private Int64 _intPart;
|
||||
private Int64 _fracPart;
|
||||
private Int64 _fracCount;
|
||||
private Int64 _expPart;
|
||||
private Boolean _expPos;
|
||||
private TokenFloat.FloatSuffix _suffix;
|
||||
private State _state;
|
||||
|
||||
public FSAFloat() {
|
||||
this._state = State.START;
|
||||
this._intPart = 0;
|
||||
this._fracPart = 0;
|
||||
this._fracCount = 0;
|
||||
this._expPart = 0;
|
||||
this._suffix = TokenFloat.FloatSuffix.NONE;
|
||||
this._expPos = true;
|
||||
this._raw = "";
|
||||
}
|
||||
|
||||
public override void Reset() {
|
||||
this._state = State.START;
|
||||
this._intPart = 0;
|
||||
this._fracPart = 0;
|
||||
this._fracCount = 0;
|
||||
this._expPart = 0;
|
||||
this._suffix = TokenFloat.FloatSuffix.NONE;
|
||||
this._expPos = true;
|
||||
this._raw = "";
|
||||
}
|
||||
|
||||
public override FSAStatus GetStatus() {
|
||||
switch (this._state) {
|
||||
case State.START:
|
||||
return FSAStatus.NONE;
|
||||
case State.END:
|
||||
return FSAStatus.END;
|
||||
case State.ERROR:
|
||||
return FSAStatus.ERROR;
|
||||
default:
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
public override Token RetrieveToken() {
|
||||
Double val;
|
||||
if (this._expPos) {
|
||||
val = (this._intPart + this._fracPart * Math.Pow(0.1, this._fracCount)) * Math.Pow(10, this._expPart);
|
||||
} else {
|
||||
val = (this._intPart + this._fracPart * Math.Pow(0.1, this._fracCount)) * Math.Pow(10, -this._expPart);
|
||||
}
|
||||
return new TokenFloat(val, this._suffix, this._raw.Substring(0, this._raw.Length - 1));
|
||||
}
|
||||
|
||||
public override void ReadChar(Char ch) {
|
||||
this._raw += ch;
|
||||
switch (this._state) {
|
||||
case State.ERROR:
|
||||
case State.END:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
|
||||
case State.START:
|
||||
if (Char.IsDigit(ch)) {
|
||||
this._intPart = ch - '0';
|
||||
this._state = State.D;
|
||||
} else if (ch == '.') {
|
||||
this._state = State.P;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.D:
|
||||
if (Char.IsDigit(ch)) {
|
||||
this._intPart *= 10;
|
||||
this._intPart += ch - '0';
|
||||
this._state = State.D;
|
||||
} else if (ch == 'e' || ch == 'E') {
|
||||
this._state = State.DE;
|
||||
} else if (ch == '.') {
|
||||
this._state = State.DP;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.P:
|
||||
if (Char.IsDigit(ch)) {
|
||||
this._fracPart = ch - '0';
|
||||
this._fracCount = 1;
|
||||
this._state = State.PD;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.DP:
|
||||
if (Char.IsDigit(ch)) {
|
||||
this._fracPart = ch - '0';
|
||||
this._fracCount = 1;
|
||||
this._state = State.PD;
|
||||
} else if (ch == 'e' || ch == 'E') {
|
||||
this._state = State.DE;
|
||||
} else if (ch == 'f' || ch == 'F') {
|
||||
this._suffix = TokenFloat.FloatSuffix.F;
|
||||
this._state = State.PDF;
|
||||
} else if (ch == 'l' || ch == 'L') {
|
||||
this._suffix = TokenFloat.FloatSuffix.L;
|
||||
this._state = State.DPL;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.PD:
|
||||
if (Char.IsDigit(ch)) {
|
||||
this._fracPart *= 10;
|
||||
this._fracPart += ch - '0';
|
||||
this._fracCount++;
|
||||
this._state = State.PD;
|
||||
} else if (ch == 'e' || ch == 'E') {
|
||||
this._state = State.DE;
|
||||
} else if (ch == 'f' || ch == 'F') {
|
||||
this._suffix = TokenFloat.FloatSuffix.F;
|
||||
this._state = State.PDF;
|
||||
} else if (ch == 'l' || ch == 'L') {
|
||||
this._suffix = TokenFloat.FloatSuffix.L;
|
||||
this._state = State.DPL;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.DE:
|
||||
if (Char.IsDigit(ch)) {
|
||||
this._expPart = ch - '0';
|
||||
this._state = State.DED;
|
||||
} else if (ch == '+' || ch == '-') {
|
||||
if (ch == '-') {
|
||||
this._expPos = false;
|
||||
}
|
||||
this._state = State.DES;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.DES:
|
||||
if (Char.IsDigit(ch)) {
|
||||
this._expPart = ch - '0';
|
||||
this._state = State.DED;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.DPL:
|
||||
this._suffix = TokenFloat.FloatSuffix.L;
|
||||
this._state = State.END;
|
||||
break;
|
||||
|
||||
case State.DED:
|
||||
if (Char.IsDigit(ch)) {
|
||||
this._expPart *= 10;
|
||||
this._expPart += ch - '0';
|
||||
this._state = State.DED;
|
||||
} else if (ch == 'f' || ch == 'F') {
|
||||
this._suffix = TokenFloat.FloatSuffix.F;
|
||||
this._state = State.PDF;
|
||||
} else if (ch == 'l' || ch == 'L') {
|
||||
this._suffix = TokenFloat.FloatSuffix.L;
|
||||
this._state = State.DPL;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.PDF:
|
||||
this._state = State.END;
|
||||
break;
|
||||
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void ReadEOF() {
|
||||
switch (this._state) {
|
||||
case State.DP:
|
||||
case State.PD:
|
||||
case State.DED:
|
||||
case State.PDF:
|
||||
case State.DPL:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
96
LibIFPSCC/Scanner/Identifier.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using System;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
/// <summary>
|
||||
/// If the identifier is found to be a keyword, then it will be a keyword
|
||||
/// </summary>
|
||||
public sealed class TokenIdentifier : Token {
|
||||
public TokenIdentifier(String val) {
|
||||
this.Val = val;
|
||||
}
|
||||
|
||||
public override TokenKind Kind { get; } = TokenKind.IDENTIFIER;
|
||||
public String Val { get; }
|
||||
public override String ToString() {
|
||||
return this.Kind + $" [{Line}:{Column}]: " + this.Val;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FSAIdentifier : FSA {
|
||||
private enum State {
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
ID
|
||||
};
|
||||
private State _state;
|
||||
private String _scanned;
|
||||
|
||||
public FSAIdentifier() {
|
||||
this._state = State.START;
|
||||
this._scanned = "";
|
||||
}
|
||||
|
||||
public override void Reset() {
|
||||
this._state = State.START;
|
||||
this._scanned = "";
|
||||
}
|
||||
|
||||
public override FSAStatus GetStatus() {
|
||||
if (this._state == State.START) {
|
||||
return FSAStatus.NONE;
|
||||
}
|
||||
if (this._state == State.END) {
|
||||
return FSAStatus.END;
|
||||
}
|
||||
if (this._state == State.ERROR) {
|
||||
return FSAStatus.ERROR;
|
||||
}
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
|
||||
public override Token RetrieveToken() {
|
||||
String name = this._scanned.Substring(0, this._scanned.Length - 1);
|
||||
if (TokenKeyword.Keywords.ContainsKey(name)) {
|
||||
return new TokenKeyword(TokenKeyword.Keywords[name]);
|
||||
}
|
||||
return new TokenIdentifier(name);
|
||||
}
|
||||
|
||||
public override void ReadChar(Char ch) {
|
||||
this._scanned = this._scanned + ch;
|
||||
switch (this._state) {
|
||||
case State.END:
|
||||
case State.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
case State.START:
|
||||
if (ch == '_' || Char.IsLetter(ch)) {
|
||||
this._state = State.ID;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.ID:
|
||||
if (Char.IsLetterOrDigit(ch) || ch == '_') {
|
||||
this._state = State.ID;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReadEOF() {
|
||||
this._scanned = this._scanned + '0';
|
||||
switch (this._state) {
|
||||
case State.ID:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
265
LibIFPSCC/Scanner/Int.cs
Normal file
@ -0,0 +1,265 @@
|
||||
using System;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
/// <summary>
|
||||
/// There are four types of integers: signed, unsigned, signed long, unsigned long
|
||||
/// </summary>
|
||||
public sealed class TokenInt : Token {
|
||||
public enum IntSuffix {
|
||||
NONE,
|
||||
U,
|
||||
L,
|
||||
LL,
|
||||
UL,
|
||||
ULL
|
||||
};
|
||||
|
||||
public TokenInt(Int64 val, IntSuffix suffix, String raw) {
|
||||
this.Val = val;
|
||||
this.Suffix = suffix;
|
||||
this.Raw = raw;
|
||||
}
|
||||
|
||||
public override TokenKind Kind { get; } = TokenKind.INT;
|
||||
|
||||
public override String ToString() {
|
||||
String str = this.Kind.ToString();
|
||||
switch (this.Suffix) {
|
||||
case IntSuffix.L:
|
||||
str += "(long)";
|
||||
break;
|
||||
case IntSuffix.LL:
|
||||
str += "(__int64)";
|
||||
break;
|
||||
case IntSuffix.U:
|
||||
str += "(unsigned)";
|
||||
break;
|
||||
case IntSuffix.UL:
|
||||
str += "(unsigned long)";
|
||||
break;
|
||||
case IntSuffix.ULL:
|
||||
str += "(unsigned __int64)";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return str + $" [{Line}:{Column}]: " + this.Val + " \"" + this.Raw + "\"";
|
||||
}
|
||||
|
||||
public readonly Int64 Val;
|
||||
public readonly String Raw;
|
||||
public readonly IntSuffix Suffix;
|
||||
}
|
||||
|
||||
public sealed class FSAInt : FSA {
|
||||
private enum State {
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
Z,
|
||||
O,
|
||||
D,
|
||||
ZX,
|
||||
H,
|
||||
L,
|
||||
LL,
|
||||
U,
|
||||
UL,
|
||||
ULL
|
||||
};
|
||||
|
||||
private Int64 _val;
|
||||
private String _raw;
|
||||
private TokenInt.IntSuffix _suffix;
|
||||
private State _state;
|
||||
|
||||
public FSAInt() {
|
||||
this._state = State.START;
|
||||
this._val = 0;
|
||||
this._raw = "";
|
||||
this._suffix = TokenInt.IntSuffix.NONE;
|
||||
}
|
||||
|
||||
public override void Reset() {
|
||||
this._state = State.START;
|
||||
this._val = 0;
|
||||
this._raw = "";
|
||||
this._suffix = TokenInt.IntSuffix.NONE;
|
||||
}
|
||||
|
||||
public override FSAStatus GetStatus() {
|
||||
switch (this._state) {
|
||||
case State.START:
|
||||
return FSAStatus.NONE;
|
||||
case State.END:
|
||||
return FSAStatus.END;
|
||||
case State.ERROR:
|
||||
return FSAStatus.ERROR;
|
||||
default:
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
public override Token RetrieveToken() {
|
||||
return new TokenInt(this._val, this._suffix, this._raw.Substring(0, this._raw.Length - 1));
|
||||
}
|
||||
|
||||
public override void ReadChar(Char ch) {
|
||||
this._raw += ch;
|
||||
switch (this._state) {
|
||||
case State.ERROR:
|
||||
case State.END:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
case State.START:
|
||||
if (ch == '0') {
|
||||
this._state = State.Z;
|
||||
} else if (Char.IsDigit(ch)) {
|
||||
this._state = State.D;
|
||||
this._val += ch - '0';
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.Z:
|
||||
if (ch == 'x' || ch == 'X') {
|
||||
this._state = State.ZX;
|
||||
} else if (Utils.IsOctDigit(ch)) {
|
||||
this._val *= 8;
|
||||
this._val += ch - '0';
|
||||
this._state = State.O;
|
||||
} else if (ch == 'u' || ch == 'U') {
|
||||
this._suffix = TokenInt.IntSuffix.U;
|
||||
this._state = State.U;
|
||||
} else if (ch == 'l' || ch == 'L') {
|
||||
this._suffix = TokenInt.IntSuffix.L;
|
||||
this._state = State.L;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.D:
|
||||
if (Char.IsDigit(ch)) {
|
||||
this._val *= 10;
|
||||
this._val += ch - '0';
|
||||
this._state = State.D;
|
||||
} else if (ch == 'u' || ch == 'U') {
|
||||
this._suffix = TokenInt.IntSuffix.U;
|
||||
this._state = State.U;
|
||||
} else if (ch == 'l' || ch == 'L') {
|
||||
this._suffix = TokenInt.IntSuffix.L;
|
||||
this._state = State.L;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.ZX:
|
||||
if (Utils.IsHexDigit(ch)) {
|
||||
this._val *= 0x10;
|
||||
this._val += Utils.GetHexDigit(ch);
|
||||
this._state = State.H;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.O:
|
||||
if (Utils.IsOctDigit(ch)) {
|
||||
this._val *= 8;
|
||||
this._val += ch - '0';
|
||||
this._state = State.O;
|
||||
} else if (ch == 'u' || ch == 'U') {
|
||||
this._suffix = TokenInt.IntSuffix.U;
|
||||
this._state = State.U;
|
||||
} else if (ch == 'l' || ch == 'L') {
|
||||
this._suffix = TokenInt.IntSuffix.L;
|
||||
this._state = State.L;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.L:
|
||||
if (ch == 'u' || ch == 'U')
|
||||
{
|
||||
this._suffix = TokenInt.IntSuffix.UL;
|
||||
this._state = State.UL;
|
||||
} else if (ch == 'l' || ch == 'L')
|
||||
{
|
||||
this._suffix = TokenInt.IntSuffix.LL;
|
||||
this._state = State.LL;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.H:
|
||||
if (Utils.IsHexDigit(ch)) {
|
||||
this._val *= 0x10;
|
||||
this._val += Utils.GetHexDigit(ch);
|
||||
this._state = State.H;
|
||||
} else if (ch == 'u' || ch == 'U') {
|
||||
this._suffix = TokenInt.IntSuffix.U;
|
||||
this._state = State.U;
|
||||
} else if (ch == 'l' || ch == 'L') {
|
||||
this._suffix = TokenInt.IntSuffix.L;
|
||||
this._state = State.L;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.U:
|
||||
if (ch == 'l' || ch == 'L') {
|
||||
this._suffix = TokenInt.IntSuffix.UL;
|
||||
this._state = State.UL;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.LL:
|
||||
if (ch == 'u' || ch == 'U')
|
||||
{
|
||||
this._suffix = TokenInt.IntSuffix.ULL;
|
||||
this._state = State.ULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.UL:
|
||||
if (ch == 'l' || ch == 'L')
|
||||
{
|
||||
this._suffix = TokenInt.IntSuffix.ULL;
|
||||
this._state = State.ULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.ULL:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReadEOF() {
|
||||
switch (this._state) {
|
||||
case State.D:
|
||||
case State.Z:
|
||||
case State.O:
|
||||
case State.L:
|
||||
case State.H:
|
||||
case State.U:
|
||||
case State.UL:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
98
LibIFPSCC/Scanner/Keyword.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
public enum KeywordVal {
|
||||
AUTO,
|
||||
DOUBLE,
|
||||
INT,
|
||||
STRUCT,
|
||||
BREAK,
|
||||
ELSE,
|
||||
LONG,
|
||||
SWITCH,
|
||||
CASE,
|
||||
ENUM,
|
||||
REGISTER,
|
||||
TYPEDEF,
|
||||
CHAR,
|
||||
EXTERN,
|
||||
RETURN,
|
||||
UNION,
|
||||
CONST,
|
||||
FLOAT,
|
||||
SHORT,
|
||||
UNSIGNED,
|
||||
CONTINUE,
|
||||
FOR,
|
||||
SIGNED,
|
||||
VOID,
|
||||
DEFAULT,
|
||||
GOTO,
|
||||
SIZEOF,
|
||||
VOLATILE,
|
||||
DO,
|
||||
IF,
|
||||
STATIC,
|
||||
WHILE,
|
||||
|
||||
ATTRIBUTE,
|
||||
STRING,
|
||||
INT64,
|
||||
INTERFACE,
|
||||
VARIANT
|
||||
}
|
||||
|
||||
public class TokenKeyword : Token {
|
||||
public TokenKeyword(KeywordVal val) {
|
||||
this.Val = val;
|
||||
}
|
||||
|
||||
public override TokenKind Kind { get; } = TokenKind.KEYWORD;
|
||||
public KeywordVal Val { get; }
|
||||
public static Dictionary<String, KeywordVal> Keywords { get; } = new Dictionary<String, KeywordVal>(StringComparer.InvariantCultureIgnoreCase) {
|
||||
{ "AUTO", KeywordVal.AUTO },
|
||||
{ "DOUBLE", KeywordVal.DOUBLE },
|
||||
{ "INT", KeywordVal.INT },
|
||||
{ "STRUCT", KeywordVal.STRUCT },
|
||||
{ "BREAK", KeywordVal.BREAK },
|
||||
{ "ELSE", KeywordVal.ELSE },
|
||||
{ "LONG", KeywordVal.LONG },
|
||||
{ "SWITCH", KeywordVal.SWITCH },
|
||||
{ "CASE", KeywordVal.CASE },
|
||||
{ "ENUM", KeywordVal.ENUM },
|
||||
{ "REGISTER", KeywordVal.REGISTER },
|
||||
{ "TYPEDEF", KeywordVal.TYPEDEF },
|
||||
{ "CHAR", KeywordVal.CHAR },
|
||||
{ "EXTERN", KeywordVal.EXTERN },
|
||||
{ "RETURN", KeywordVal.RETURN },
|
||||
{ "UNION", KeywordVal.UNION },
|
||||
{ "CONST", KeywordVal.CONST },
|
||||
{ "FLOAT", KeywordVal.FLOAT },
|
||||
{ "SHORT", KeywordVal.SHORT },
|
||||
{ "UNSIGNED", KeywordVal.UNSIGNED },
|
||||
{ "CONTINUE", KeywordVal.CONTINUE },
|
||||
{ "FOR", KeywordVal.FOR },
|
||||
{ "SIGNED", KeywordVal.SIGNED },
|
||||
{ "VOID", KeywordVal.VOID },
|
||||
{ "DEFAULT", KeywordVal.DEFAULT },
|
||||
{ "GOTO", KeywordVal.GOTO },
|
||||
{ "SIZEOF", KeywordVal.SIZEOF },
|
||||
{ "VOLATILE", KeywordVal.VOLATILE },
|
||||
{ "DO", KeywordVal.DO },
|
||||
{ "IF", KeywordVal.IF },
|
||||
{ "STATIC", KeywordVal.STATIC },
|
||||
{ "WHILE", KeywordVal.WHILE },
|
||||
{ "__ATTRIBUTE", KeywordVal.ATTRIBUTE },
|
||||
{ "__STRING", KeywordVal.STRING },
|
||||
{ "__INT64", KeywordVal.INT64 },
|
||||
{ "__INTERFACE", KeywordVal.INTERFACE },
|
||||
{ "__VARIANT", KeywordVal.VARIANT }
|
||||
};
|
||||
|
||||
public override String ToString() {
|
||||
return this.Kind + $" [{Line}:{Column}]: " + this.Val;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
415
LibIFPSCC/Scanner/Operator.cs
Normal file
@ -0,0 +1,415 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
/// <summary>
|
||||
/// Note that '...' is recognized as three '.'s
|
||||
/// </summary>
|
||||
public enum OperatorVal {
|
||||
LBRACKET,
|
||||
RBRACKET,
|
||||
LPAREN,
|
||||
RPAREN,
|
||||
PERIOD,
|
||||
COMMA,
|
||||
QUESTION,
|
||||
COLON,
|
||||
TILDE,
|
||||
SUB,
|
||||
RARROW,
|
||||
DEC,
|
||||
SUBASSIGN,
|
||||
ADD,
|
||||
INC,
|
||||
ADDASSIGN,
|
||||
BITAND,
|
||||
AND,
|
||||
ANDASSIGN,
|
||||
MULT,
|
||||
MULTASSIGN,
|
||||
LT,
|
||||
LEQ,
|
||||
LSHIFT,
|
||||
LSHIFTASSIGN,
|
||||
GT,
|
||||
GEQ,
|
||||
RSHIFT,
|
||||
RSHIFTASSIGN,
|
||||
ASSIGN,
|
||||
EQ,
|
||||
BITOR,
|
||||
OR,
|
||||
ORASSIGN,
|
||||
NOT,
|
||||
NEQ,
|
||||
DIV,
|
||||
DIVASSIGN,
|
||||
MOD,
|
||||
MODASSIGN,
|
||||
XOR,
|
||||
XORASSIGN,
|
||||
SEMICOLON,
|
||||
LCURL,
|
||||
RCURL
|
||||
}
|
||||
|
||||
public sealed class TokenOperator : Token {
|
||||
public TokenOperator(OperatorVal val) {
|
||||
this.Val = val;
|
||||
}
|
||||
|
||||
public override TokenKind Kind { get; } = TokenKind.OPERATOR;
|
||||
public OperatorVal Val { get; }
|
||||
|
||||
public static Dictionary<String, OperatorVal> Operators { get; } = new Dictionary<String, OperatorVal> {
|
||||
{ "[", OperatorVal.LBRACKET },
|
||||
{ "]", OperatorVal.RBRACKET },
|
||||
{ "(", OperatorVal.LPAREN },
|
||||
{ ")", OperatorVal.RPAREN },
|
||||
{ ".", OperatorVal.PERIOD },
|
||||
{ ",", OperatorVal.COMMA },
|
||||
{ "?", OperatorVal.QUESTION },
|
||||
{ ":", OperatorVal.COLON },
|
||||
{ "~", OperatorVal.TILDE },
|
||||
{ "-", OperatorVal.SUB },
|
||||
{ "->", OperatorVal.RARROW },
|
||||
{ "--", OperatorVal.DEC },
|
||||
{ "-=", OperatorVal.SUBASSIGN },
|
||||
{ "+", OperatorVal.ADD },
|
||||
{ "++", OperatorVal.INC },
|
||||
{ "+=", OperatorVal.ADDASSIGN },
|
||||
{ "&", OperatorVal.BITAND },
|
||||
{ "&&", OperatorVal.AND },
|
||||
{ "&=", OperatorVal.ANDASSIGN },
|
||||
{ "*", OperatorVal.MULT },
|
||||
{ "*=", OperatorVal.MULTASSIGN },
|
||||
{ "<", OperatorVal.LT },
|
||||
{ "<=", OperatorVal.LEQ },
|
||||
{ "<<", OperatorVal.LSHIFT },
|
||||
{ "<<=", OperatorVal.LSHIFTASSIGN },
|
||||
{ ">", OperatorVal.GT },
|
||||
{ ">=", OperatorVal.GEQ },
|
||||
{ ">>", OperatorVal.RSHIFT },
|
||||
{ ">>=", OperatorVal.RSHIFTASSIGN },
|
||||
{ "=", OperatorVal.ASSIGN },
|
||||
{ "==", OperatorVal.EQ },
|
||||
{ "|", OperatorVal.BITOR },
|
||||
{ "||", OperatorVal.OR },
|
||||
{ "|=", OperatorVal.ORASSIGN },
|
||||
{ "!", OperatorVal.NOT },
|
||||
{ "!=", OperatorVal.NEQ },
|
||||
{ "/", OperatorVal.DIV },
|
||||
{ "/=", OperatorVal.DIVASSIGN },
|
||||
{ "%", OperatorVal.MOD },
|
||||
{ "%=", OperatorVal.MODASSIGN },
|
||||
{ "^", OperatorVal.XOR },
|
||||
{ "^=", OperatorVal.XORASSIGN },
|
||||
{ ";", OperatorVal.SEMICOLON },
|
||||
{ "{", OperatorVal.LCURL },
|
||||
{ "}", OperatorVal.RCURL }
|
||||
};
|
||||
|
||||
public override String ToString() {
|
||||
return this.Kind + " [" + this.Val + $"] [{Line}:{Column}]: " + Operators.First(pair => pair.Value == this.Val).Key;
|
||||
}
|
||||
}
|
||||
|
||||
public class FSAOperator : FSA {
|
||||
private enum State {
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
FINISH,
|
||||
SUB,
|
||||
ADD,
|
||||
AMP,
|
||||
MULT,
|
||||
LT,
|
||||
LTLT,
|
||||
GT,
|
||||
GTGT,
|
||||
EQ,
|
||||
OR,
|
||||
NOT,
|
||||
DIV,
|
||||
MOD,
|
||||
XOR
|
||||
};
|
||||
|
||||
public static ImmutableHashSet<Char> OperatorChars { get; } = ImmutableHashSet.Create(
|
||||
'[',
|
||||
']',
|
||||
'(',
|
||||
')',
|
||||
'.',
|
||||
',',
|
||||
'?',
|
||||
':',
|
||||
'-',
|
||||
'>',
|
||||
'+',
|
||||
'&',
|
||||
'*',
|
||||
'~',
|
||||
'!',
|
||||
'/',
|
||||
'%',
|
||||
'<',
|
||||
'=',
|
||||
'^',
|
||||
'|',
|
||||
';',
|
||||
'{',
|
||||
'}'
|
||||
);
|
||||
|
||||
private State _state;
|
||||
private String _scanned;
|
||||
|
||||
public FSAOperator() {
|
||||
this._state = State.START;
|
||||
this._scanned = "";
|
||||
}
|
||||
|
||||
public override sealed void Reset() {
|
||||
this._state = State.START;
|
||||
this._scanned = "";
|
||||
}
|
||||
|
||||
public override sealed FSAStatus GetStatus() {
|
||||
switch (this._state) {
|
||||
case State.START:
|
||||
return FSAStatus.NONE;
|
||||
case State.END:
|
||||
return FSAStatus.END;
|
||||
case State.ERROR:
|
||||
return FSAStatus.ERROR;
|
||||
default:
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
public override sealed Token RetrieveToken() {
|
||||
return new TokenOperator(TokenOperator.Operators[this._scanned.Substring(0, this._scanned.Length - 1)]);
|
||||
}
|
||||
|
||||
public override sealed void ReadChar(Char ch) {
|
||||
this._scanned = this._scanned + ch;
|
||||
switch (this._state) {
|
||||
case State.END:
|
||||
case State.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
case State.START:
|
||||
if (OperatorChars.Contains(ch)) {
|
||||
switch (ch) {
|
||||
case '-':
|
||||
this._state = State.SUB;
|
||||
break;
|
||||
case '+':
|
||||
this._state = State.ADD;
|
||||
break;
|
||||
case '&':
|
||||
this._state = State.AMP;
|
||||
break;
|
||||
case '*':
|
||||
this._state = State.MULT;
|
||||
break;
|
||||
case '<':
|
||||
this._state = State.LT;
|
||||
break;
|
||||
case '>':
|
||||
this._state = State.GT;
|
||||
break;
|
||||
case '=':
|
||||
this._state = State.EQ;
|
||||
break;
|
||||
case '|':
|
||||
this._state = State.OR;
|
||||
break;
|
||||
case '!':
|
||||
this._state = State.NOT;
|
||||
break;
|
||||
case '/':
|
||||
this._state = State.DIV;
|
||||
break;
|
||||
case '%':
|
||||
this._state = State.MOD;
|
||||
break;
|
||||
case '^':
|
||||
this._state = State.XOR;
|
||||
break;
|
||||
default:
|
||||
this._state = State.FINISH;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.FINISH:
|
||||
this._state = State.END;
|
||||
break;
|
||||
case State.SUB:
|
||||
switch (ch) {
|
||||
case '>':
|
||||
case '-':
|
||||
case '=':
|
||||
this._state = State.FINISH;
|
||||
break;
|
||||
default:
|
||||
this._state = State.END;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.ADD:
|
||||
switch (ch) {
|
||||
case '+':
|
||||
case '=':
|
||||
this._state = State.FINISH;
|
||||
break;
|
||||
default:
|
||||
this._state = State.END;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.AMP:
|
||||
switch (ch) {
|
||||
case '&':
|
||||
case '=':
|
||||
this._state = State.FINISH;
|
||||
break;
|
||||
default:
|
||||
this._state = State.END;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.MULT:
|
||||
if (ch == '=') {
|
||||
this._state = State.FINISH;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.LT:
|
||||
switch (ch) {
|
||||
case '=':
|
||||
this._state = State.FINISH;
|
||||
break;
|
||||
case '<':
|
||||
this._state = State.LTLT;
|
||||
break;
|
||||
default:
|
||||
this._state = State.END;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.GT:
|
||||
switch (ch) {
|
||||
case '=':
|
||||
this._state = State.FINISH;
|
||||
break;
|
||||
case '>':
|
||||
this._state = State.GTGT;
|
||||
break;
|
||||
default:
|
||||
this._state = State.END;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.EQ:
|
||||
if (ch == '=') {
|
||||
this._state = State.FINISH;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.OR:
|
||||
switch (ch) {
|
||||
case '|':
|
||||
case '=':
|
||||
this._state = State.FINISH;
|
||||
break;
|
||||
default:
|
||||
this._state = State.END;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.NOT:
|
||||
if (ch == '=') {
|
||||
this._state = State.FINISH;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.DIV:
|
||||
if (ch == '=') {
|
||||
this._state = State.FINISH;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.MOD:
|
||||
if (ch == '=') {
|
||||
this._state = State.FINISH;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.XOR:
|
||||
if (ch == '=') {
|
||||
this._state = State.FINISH;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.LTLT:
|
||||
if (ch == '=') {
|
||||
this._state = State.FINISH;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
case State.GTGT:
|
||||
if (ch == '=') {
|
||||
this._state = State.FINISH;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override sealed void ReadEOF() {
|
||||
this._scanned = this._scanned + '0';
|
||||
switch (this._state) {
|
||||
case State.FINISH:
|
||||
case State.SUB:
|
||||
case State.ADD:
|
||||
case State.AMP:
|
||||
case State.MULT:
|
||||
case State.LT:
|
||||
case State.LTLT:
|
||||
case State.GT:
|
||||
case State.GTGT:
|
||||
case State.EQ:
|
||||
case State.OR:
|
||||
case State.NOT:
|
||||
case State.DIV:
|
||||
case State.MOD:
|
||||
case State.XOR:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
100
LibIFPSCC/Scanner/Scanner.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
public sealed class Scanner {
|
||||
public Scanner(String source) {
|
||||
this.Source = source;
|
||||
this.FSAs = ImmutableList.Create<FSA>(
|
||||
new FSAComment(),
|
||||
new FSAFloat(),
|
||||
new FSAInt(),
|
||||
new FSAOperator(),
|
||||
new FSAIdentifier(),
|
||||
new FSASpace(),
|
||||
new FSANewLine(),
|
||||
new FSACharConst(),
|
||||
new FSAString()
|
||||
);
|
||||
this.Tokens = Lex();
|
||||
}
|
||||
|
||||
public static Scanner FromFile(String fileName) {
|
||||
if (File.Exists(fileName)) {
|
||||
String source = File.ReadAllText(fileName);
|
||||
return FromSource(source);
|
||||
}
|
||||
throw new FileNotFoundException("Source file does not exist.", fileName);
|
||||
}
|
||||
|
||||
public static Scanner FromSource(String source) {
|
||||
return new Scanner(source);
|
||||
}
|
||||
|
||||
private IEnumerable<Token> Lex() {
|
||||
var tokens = new List<Token>();
|
||||
int line = 1, column = 1, lastColumn = column;
|
||||
for (Int32 i = 0; i < this.Source.Length; ++i) {
|
||||
if (i > 0 && this.Source[i - 1] == '\n')
|
||||
{
|
||||
line++;
|
||||
lastColumn = 1;
|
||||
column = 1;
|
||||
}
|
||||
else column++;
|
||||
this.FSAs.ForEach(fsa => fsa.ReadChar(this.Source[i]));
|
||||
|
||||
// if no running
|
||||
if (this.FSAs.FindIndex(fsa => fsa.GetStatus() == FSAStatus.RUNNING) == -1) {
|
||||
Int32 idx = this.FSAs.FindIndex(fsa => fsa.GetStatus() == FSAStatus.END);
|
||||
if (idx != -1) {
|
||||
Token token = this.FSAs[idx].RetrieveToken();
|
||||
if (token.Kind != TokenKind.NONE) {
|
||||
token.Line = line;
|
||||
token.Column = lastColumn;
|
||||
lastColumn = column;
|
||||
tokens.Add(token);
|
||||
}
|
||||
i--; column--;
|
||||
if (this.Source[i] == '\n') line--;
|
||||
this.FSAs.ForEach(fsa => fsa.Reset());
|
||||
} else {
|
||||
Console.WriteLine("error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.FSAs.ForEach(fsa => fsa.ReadEOF());
|
||||
// find END
|
||||
Int32 idx2 = this.FSAs.FindIndex(fsa => fsa.GetStatus() == FSAStatus.END);
|
||||
if (idx2 != -1) {
|
||||
Token token = this.FSAs[idx2].RetrieveToken();
|
||||
if (token.Kind != TokenKind.NONE) {
|
||||
token.Line = line;
|
||||
token.Column = column + 1;
|
||||
tokens.Add(token);
|
||||
}
|
||||
} else {
|
||||
Console.WriteLine("error");
|
||||
}
|
||||
|
||||
tokens.Add(new EmptyToken());
|
||||
return tokens;
|
||||
}
|
||||
|
||||
public override String ToString() {
|
||||
String str = "";
|
||||
foreach (Token token in this.Tokens) {
|
||||
str += $"{token}\n";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public String Source { get; }
|
||||
private ImmutableList<FSA> FSAs { get; }
|
||||
public IEnumerable<Token> Tokens { get; }
|
||||
|
||||
}
|
||||
}
|
163
LibIFPSCC/Scanner/String.cs
Normal file
@ -0,0 +1,163 @@
|
||||
using System;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
/// <summary>
|
||||
/// String literal
|
||||
/// </summary>
|
||||
public sealed class TokenString : Token {
|
||||
public TokenString(String val, String raw) {
|
||||
if (val == null) {
|
||||
throw new ArgumentNullException(nameof(val));
|
||||
}
|
||||
this.Val = val;
|
||||
this.Raw = raw;
|
||||
}
|
||||
|
||||
public override TokenKind Kind { get; } = TokenKind.STRING;
|
||||
public String Raw { get; }
|
||||
public String Val { get; }
|
||||
|
||||
public override String ToString() =>
|
||||
$"{this.Kind} [{Line}:{Column}]: \"{this.Raw}\"\n\"{this.Val}\"";
|
||||
}
|
||||
|
||||
|
||||
public sealed class TokenUnicodeString : Token
|
||||
{
|
||||
public TokenUnicodeString(String val, String raw)
|
||||
{
|
||||
if (val == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(val));
|
||||
}
|
||||
this.Val = val;
|
||||
this.Raw = raw;
|
||||
}
|
||||
|
||||
public override TokenKind Kind { get; } = TokenKind.STRING;
|
||||
public String Raw { get; }
|
||||
public String Val { get; }
|
||||
|
||||
public override String ToString() =>
|
||||
$"{this.Kind} [{Line}:{Column}]: L\"{this.Raw}\"\n\"{this.Val}\"";
|
||||
}
|
||||
|
||||
public sealed class FSAString : FSA {
|
||||
private enum State {
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
L,
|
||||
Q,
|
||||
QQ
|
||||
};
|
||||
|
||||
private State _state;
|
||||
private readonly FSAChar _fsachar;
|
||||
private String _val;
|
||||
private String _raw;
|
||||
private bool unicode = false;
|
||||
|
||||
public FSAString() {
|
||||
this._state = State.START;
|
||||
this._fsachar = new FSAChar('\"');
|
||||
this._raw = "";
|
||||
this._val = "";
|
||||
}
|
||||
|
||||
public override void Reset() {
|
||||
this._state = State.START;
|
||||
this._fsachar.Reset();
|
||||
this._raw = "";
|
||||
this._val = "";
|
||||
unicode = false;
|
||||
}
|
||||
|
||||
public override FSAStatus GetStatus() {
|
||||
if (this._state == State.START) {
|
||||
return FSAStatus.NONE;
|
||||
}
|
||||
if (this._state == State.END) {
|
||||
return FSAStatus.END;
|
||||
}
|
||||
if (this._state == State.ERROR) {
|
||||
return FSAStatus.ERROR;
|
||||
}
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
|
||||
public override Token RetrieveToken() {
|
||||
if (unicode) return new TokenUnicodeString(this._val, this._raw);
|
||||
return new TokenString(this._val, this._raw);
|
||||
}
|
||||
|
||||
public override void ReadChar(Char ch) {
|
||||
switch (this._state) {
|
||||
case State.END:
|
||||
case State.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
case State.START:
|
||||
switch (ch) {
|
||||
case 'L':
|
||||
unicode = true;
|
||||
this._state = State.L;
|
||||
break;
|
||||
case '\"':
|
||||
this._state = State.Q;
|
||||
this._fsachar.Reset();
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.L:
|
||||
if (ch == '\"') {
|
||||
this._state = State.Q;
|
||||
this._fsachar.Reset();
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.Q:
|
||||
if (this._fsachar.GetStatus() == FSAStatus.NONE && ch == '\"') {
|
||||
this._state = State.QQ;
|
||||
this._fsachar.Reset();
|
||||
} else {
|
||||
this._fsachar.ReadChar(ch);
|
||||
switch (this._fsachar.GetStatus()) {
|
||||
case FSAStatus.END:
|
||||
this._state = State.Q;
|
||||
this._val = this._val + this._fsachar.RetrieveChar();
|
||||
this._raw = this._raw + this._fsachar.RetrieveRaw();
|
||||
this._fsachar.Reset();
|
||||
ReadChar(ch);
|
||||
break;
|
||||
case FSAStatus.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.QQ:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReadEOF() {
|
||||
if (this._state == State.QQ) {
|
||||
this._state = State.END;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
286
LibIFPSCC/Scanner/Tokens.cs
Normal file
@ -0,0 +1,286 @@
|
||||
using System;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
public enum TokenKind {
|
||||
NONE,
|
||||
FLOAT,
|
||||
INT,
|
||||
CHAR,
|
||||
STRING,
|
||||
IDENTIFIER,
|
||||
KEYWORD,
|
||||
OPERATOR
|
||||
}
|
||||
|
||||
public abstract class Token {
|
||||
public override String ToString() {
|
||||
return this.Kind.ToString();
|
||||
}
|
||||
public abstract TokenKind Kind { get; }
|
||||
|
||||
public int Line { get; internal set; }
|
||||
public int Column { get; internal set; }
|
||||
}
|
||||
|
||||
public sealed class EmptyToken : Token {
|
||||
public override TokenKind Kind { get; } = TokenKind.NONE;
|
||||
}
|
||||
|
||||
public sealed class FSAComment : FSA
|
||||
{
|
||||
private enum State
|
||||
{
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
FIRST,
|
||||
COMMENT,
|
||||
END_FIRST,
|
||||
END_SECOND,
|
||||
}
|
||||
|
||||
private State _state;
|
||||
private bool CStyle = false;
|
||||
|
||||
public FSAComment()
|
||||
{
|
||||
this._state = State.START;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
this._state = State.START;
|
||||
CStyle = false;
|
||||
}
|
||||
|
||||
public override FSAStatus GetStatus()
|
||||
{
|
||||
if (this._state == State.START)
|
||||
{
|
||||
return FSAStatus.NONE;
|
||||
}
|
||||
if (this._state == State.END)
|
||||
{
|
||||
return FSAStatus.END;
|
||||
}
|
||||
if (this._state == State.ERROR)
|
||||
{
|
||||
return FSAStatus.ERROR;
|
||||
}
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
|
||||
// RetrieveToken : () -> Token
|
||||
// ===========================
|
||||
// Note that this function never gets used, because FSAChar is just an inner FSA for other FSAs.
|
||||
//
|
||||
public override Token RetrieveToken()
|
||||
{
|
||||
return new EmptyToken();
|
||||
}
|
||||
|
||||
// ReadChar : Char -> ()
|
||||
// =====================
|
||||
// Implementation of the FSA
|
||||
//
|
||||
public override void ReadChar(Char ch)
|
||||
{
|
||||
switch (this._state)
|
||||
{
|
||||
case State.END:
|
||||
case State.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
case State.START:
|
||||
if (ch == '/')
|
||||
{
|
||||
this._state = State.FIRST;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.FIRST:
|
||||
CStyle = ch == '*';
|
||||
if (CStyle || ch == '/')
|
||||
{
|
||||
_state = State.COMMENT;
|
||||
}
|
||||
else _state = State.ERROR;
|
||||
break;
|
||||
case State.COMMENT:
|
||||
if (CStyle && ch == '*')
|
||||
{
|
||||
_state = State.END_FIRST;
|
||||
}
|
||||
else if (!CStyle && ch == '\n') _state = State.END_SECOND;
|
||||
break;
|
||||
case State.END_FIRST:
|
||||
if (ch == '/') _state = State.END_SECOND;
|
||||
else if (ch != '*') _state = State.COMMENT;
|
||||
break;
|
||||
case State.END_SECOND:
|
||||
_state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ReadEOF : () -> ()
|
||||
// ==================
|
||||
//
|
||||
public override void ReadEOF()
|
||||
{
|
||||
switch (this._state)
|
||||
{
|
||||
case State.COMMENT:
|
||||
case State.END_FIRST:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public sealed class FSASpace : FSA {
|
||||
private enum State {
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
SPACE
|
||||
};
|
||||
|
||||
private State _state;
|
||||
|
||||
public FSASpace() {
|
||||
this._state = State.START;
|
||||
}
|
||||
|
||||
public override void Reset() {
|
||||
this._state = State.START;
|
||||
}
|
||||
|
||||
public override FSAStatus GetStatus() {
|
||||
if (this._state == State.START) {
|
||||
return FSAStatus.NONE;
|
||||
}
|
||||
if (this._state == State.END) {
|
||||
return FSAStatus.END;
|
||||
}
|
||||
if (this._state == State.ERROR) {
|
||||
return FSAStatus.ERROR;
|
||||
}
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
|
||||
public override Token RetrieveToken() {
|
||||
return new EmptyToken();
|
||||
}
|
||||
|
||||
public override void ReadChar(Char ch) {
|
||||
switch (this._state) {
|
||||
case State.END:
|
||||
case State.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
case State.START:
|
||||
if (Utils.IsSpace(ch)) {
|
||||
this._state = State.SPACE;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.SPACE:
|
||||
if (Utils.IsSpace(ch)) {
|
||||
this._state = State.SPACE;
|
||||
} else {
|
||||
this._state = State.END;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReadEOF() {
|
||||
switch (this._state) {
|
||||
case State.SPACE:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FSANewLine : FSA {
|
||||
private enum State {
|
||||
START,
|
||||
END,
|
||||
ERROR,
|
||||
NEWLINE
|
||||
};
|
||||
|
||||
private State _state;
|
||||
|
||||
public FSANewLine() {
|
||||
this._state = State.START;
|
||||
}
|
||||
|
||||
public override void Reset() {
|
||||
this._state = State.START;
|
||||
}
|
||||
|
||||
public override FSAStatus GetStatus() {
|
||||
if (this._state == State.START) {
|
||||
return FSAStatus.NONE;
|
||||
}
|
||||
if (this._state == State.END) {
|
||||
return FSAStatus.END;
|
||||
}
|
||||
if (this._state == State.ERROR) {
|
||||
return FSAStatus.ERROR;
|
||||
}
|
||||
return FSAStatus.RUNNING;
|
||||
}
|
||||
|
||||
public override Token RetrieveToken() {
|
||||
return new EmptyToken();
|
||||
}
|
||||
|
||||
public override void ReadChar(Char ch) {
|
||||
switch (this._state) {
|
||||
case State.END:
|
||||
case State.ERROR:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
case State.START:
|
||||
if (ch == '\n') {
|
||||
this._state = State.NEWLINE;
|
||||
} else {
|
||||
this._state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case State.NEWLINE:
|
||||
this._state = State.END;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReadEOF() {
|
||||
switch (this._state) {
|
||||
case State.NEWLINE:
|
||||
this._state = State.END;
|
||||
break;
|
||||
default:
|
||||
this._state = State.ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
65
LibIFPSCC/Scanner/Utils.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
|
||||
namespace LexicalAnalysis {
|
||||
public static class Utils {
|
||||
|
||||
// IsEscapeChar : Char -> Boolean
|
||||
// ==============================
|
||||
//
|
||||
public static Boolean IsEscapeChar(Char ch) {
|
||||
switch (ch) {
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'f':
|
||||
case 'n':
|
||||
case 'r':
|
||||
case 't':
|
||||
case 'v':
|
||||
case '\'':
|
||||
case '\"':
|
||||
case '\\':
|
||||
case '?':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// IsHexDigit : Char -> Boolean
|
||||
// ============================
|
||||
//
|
||||
public static Boolean IsHexDigit(Char ch) {
|
||||
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
|
||||
}
|
||||
|
||||
// IsOctDigit : Char -> Boolean
|
||||
// ============================
|
||||
//
|
||||
public static Boolean IsOctDigit(Char ch) {
|
||||
return ch >= '0' && ch <= '7';
|
||||
}
|
||||
|
||||
// GetHexDigit : Char -> Int64
|
||||
// ===========================
|
||||
//
|
||||
public static Int64 GetHexDigit(Char ch) {
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
return ch - '0';
|
||||
}
|
||||
if (ch >= 'a' && ch <= 'f') {
|
||||
return ch - 'a' + 0xA;
|
||||
}
|
||||
if (ch >= 'A' && ch <= 'F') {
|
||||
return ch - 'A' + 0xA;
|
||||
}
|
||||
throw new Exception("GetHexDigit: Character is not a hex digit. You should first call IsHexDigit(ch) for a check.");
|
||||
}
|
||||
|
||||
// IsSpace : Char -> Boolean
|
||||
// =========================
|
||||
//
|
||||
public static Boolean IsSpace(Char ch) {
|
||||
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\f' || ch == '\v');
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@ -32,6 +32,7 @@
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
|
14
ifpscc/App.config
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
96
ifpscc/Program.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Driver;
|
||||
|
||||
namespace ifpscc
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static readonly char[] OPTION_SEP = { '=' };
|
||||
|
||||
private static void Usage()
|
||||
{
|
||||
Console.WriteLine("Usage: {0} [-A|--disassemble] [-O=out.bin|--output=out.bin] files...", Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().Location));
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("C compiler targeting RemObjects PascalScript.");
|
||||
Console.WriteLine("if -A or -disassemble is passed, writes the disassembly of the compiler output to stdout");
|
||||
Console.WriteLine("if -O or -output is not passed, the compiled script will be written to [first C file passed].bin");
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var files = new List<string>();
|
||||
var options = new Dictionary<string, string>();
|
||||
|
||||
// Parse command line for options and files.
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i][0] == '-')
|
||||
{
|
||||
var option = args[i].Split(OPTION_SEP, 2);
|
||||
if (option.Length == 1) options.Add(option[0], string.Empty);
|
||||
else options.Add(option[0], option[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
files.Add(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.ContainsKey("-?") || options.ContainsKey("--help"))
|
||||
{
|
||||
Usage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (files.Count == 0)
|
||||
{
|
||||
Console.WriteLine("At least one C file must be passed in");
|
||||
Usage();
|
||||
return;
|
||||
}
|
||||
|
||||
var code = string.Empty;
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
string fileText = string.Empty;
|
||||
try
|
||||
{
|
||||
fileText = File.ReadAllText(file) + "\r\n";
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
Usage();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: preprocessor?
|
||||
code += fileText;
|
||||
}
|
||||
|
||||
#if DEBUG // TODO: remove this later
|
||||
while (!System.Diagnostics.Debugger.IsAttached) System.Threading.Thread.Sleep(100);
|
||||
#endif
|
||||
var script = Compiler.FromSource(code).Script;
|
||||
|
||||
if (options.ContainsKey("--disassemble") || options.ContainsKey("-A"))
|
||||
{
|
||||
Console.WriteLine(script.Disassemble());
|
||||
return;
|
||||
}
|
||||
|
||||
var output = string.Empty;
|
||||
|
||||
if (options.ContainsKey("-o")) output = options["-o"];
|
||||
if (output == string.Empty && options.ContainsKey("--output")) output = options["--output"];
|
||||
if (output == string.Empty) output = Path.ChangeExtension(files[0], "bin");
|
||||
|
||||
script.Save(File.OpenWrite(output));
|
||||
}
|
||||
}
|
||||
}
|
36
ifpscc/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ifpscc")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ifpscc")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2022")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("8b953068-8b67-4d9a-802c-7657a7cafc78")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
81
ifpscc/ifpscc.csproj
Normal file
@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{8B953068-8B67-4D9A-802C-7657A7CAFC78}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>ifpscc</RootNamespace>
|
||||
<AssemblyName>ifpscc</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Collections.Immutable, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.7.0.0\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IFPSLib\IFPSLib.csproj">
|
||||
<Project>{E83BF0B3-4427-41E8-9A99-9DF8951DE711}</Project>
|
||||
<Name>IFPSLib</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\LibIFPSCC\LibIFPSCC.csproj">
|
||||
<Project>{B56F2451-B1FF-4103-A2BC-0C61463D6971}</Project>
|
||||
<Name>LibIFPSCC</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
8
ifpscc/packages.config
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="System.Buffers" version="4.5.1" targetFramework="net472" />
|
||||
<package id="System.Collections.Immutable" version="7.0.0" targetFramework="net472" />
|
||||
<package id="System.Memory" version="4.5.5" targetFramework="net472" />
|
||||
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net472" />
|
||||
</packages>
|
@ -32,6 +32,7 @@
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
|
76
readme.md
@ -14,6 +14,7 @@ The API is modeled on dnlib's.
|
||||
A known bug is that the `Extended` primitive in IFPSLib will always refer internally to an 80-bit floating point value; any compiled IFPS script intended for an architecture that is not x86 (32-bit) will use 64-bit floating point values.
|
||||
|
||||
## IFPSAsmLib
|
||||
|
||||
Library implementing an assembler for IFPS scripts.
|
||||
|
||||
Depends on IFPSLib.
|
||||
@ -32,6 +33,18 @@ Wrapper around IFPSAsmLib.
|
||||
|
||||
Usage: `ifpsasm file.txt` will assemble `file.txt` into `file.bin`
|
||||
|
||||
## LibIFPSCC
|
||||
|
||||
Implements an ANSI C compiler targeting PascalScript bytecode. Fork of [phisiart's C compiler](https://github.com/phisiart/C-Compiler).
|
||||
|
||||
Further details below.
|
||||
|
||||
## ifpscc
|
||||
|
||||
Wrapper around LibIFPSCC.
|
||||
|
||||
Usage: `ifpscc [-A|--disassemble] [-O=out.bin|--output=out.bin] files...` - where `--disassemble` writes the disassembly of the compiler output to stdout, and if `--output` is not passed, compiled script will be written to `[first C file passed].bin`.
|
||||
|
||||
## uninno
|
||||
|
||||
The Inno Setup Uninstaller Configuration Creator.
|
||||
@ -106,3 +119,66 @@ The `examples` directory contains a few example scripts, intended to be used wit
|
||||
It is possible to assemble or save a script which would be considered invalid by the runtime.
|
||||
|
||||
Notably, even if it is not used by your script, a compiled script without the `primitive(Pointer)` type included is invalid; the runtime expects it to be present and calling any function in a script without such a type present leads to a null pointer dereference.
|
||||
|
||||
Opcodes involving COM variants and COM interfaces can specifically compare the type name against `"IDISPATCH"`.
|
||||
|
||||
Passing arrays to imported functions may require the type name of the array to start with `!OPENARRAY`.
|
||||
|
||||
## LibIFPSCC details
|
||||
|
||||
This compiler specifically targets PascalScript for x86 Windows (for Inno Setup). It has been tested for some time, but please report any bugs you find.
|
||||
|
||||
A preprocessor is not implemented (might happen in the future, not sure).
|
||||
|
||||
Static functions or typedefs will not be exported unless needed.
|
||||
|
||||
No inbuilt runtime types or functions are imported by default, you will need to specify prototypes yourself. For a "generic" function like GetArrayLength/SetArrayLength, do not provide an argument list in the prototype.
|
||||
|
||||
Unbounded arrays are allowed, as the runtime supports them.
|
||||
|
||||
Initialiser lists are supported in function calls, where the type of the initialised object is the type of that function parameter.
|
||||
|
||||
Additional types:
|
||||
- `__int64` becomes `primitive(S64)`
|
||||
- `__String` becomes `primitive(String)`
|
||||
- `unsigned __String` becomes `primitive(UnicodeString)`
|
||||
- `__variant` becomes `primitive(Variant)`
|
||||
- `__interface("guid")` specifies a COM interface
|
||||
- `void*` (and arrays of pointer, pointer to pointer, ...) becomes `primitive(U32)` internally.
|
||||
|
||||
Because this is intended for use with `uninno` / etc, `class()` types are not (yet) supported.
|
||||
|
||||
Casting to and from pointers are fully supported, thanks to some tricks.
|
||||
- `__fastcall` calling convention is Delphi style (on x86, first argument in `eax`). This means that calling an exported function from a DLL that performs no operation allows simple casting from reference to integer.
|
||||
- `ntdll!RtlDebugPrintTimes` is used for this. On free builds of NT this is a no-op (and checked builds aren't a thing anymore)
|
||||
- For casting to a reference type, a `memcpy` (actually `ntdll!RtlMoveMemory`) is done to a null pointer.
|
||||
- The runtime doesn't support pointer to pointer, we work around this by using `Pointer[1]`. This also works around null derefs in the runtime when doing almost anything to a null pointer.
|
||||
- PascalScript objects have a pointer to the type information located before the object value, which pointers are always obtained to.
|
||||
- Pointers additionally have an additional pointer afterwards, to the type being referenced: `struct Pointer { TypeHeader hdr; void* pValue; void* pType; }`
|
||||
- Therefore, casting to a reference type is effectively done by: `void CreateValidPointer(u32 ptr, u32 pObjForType, ArrayOfPointer* outPtr) { memcpy(&outPtr[0]->pValue, &ptr, sizeof(ptr)); pObjForType -= sizeof(ptr); memcpy(&outPtr[0]->pType, &pObjForType, sizeof(ptr)); }`
|
||||
- pObjForType is obtained by creating a new instance of the wanted type on the stack.
|
||||
|
||||
Unions are supported by emitting a byte array of sizeof(union) and casting to the correct pointer type when needed.
|
||||
|
||||
Types and functions support additional attributes. For example:
|
||||
|
||||
- `typedef __variant __attribute(__open) OpenArrayOfVariant[];` - unbounded array of COM variants, which will be marshaled to a C array when passed to an imported function.
|
||||
- `typedef void * PVOID ; typedef PVOID __attribute(__open) OpenArrayOfConst[];` - unbounded array of any object, which will be marshaled correctly when passed to an imported function (only used for Format() function).
|
||||
- `typedef __interface("bca8b44d-aad6-3a86-8ab7-03349f4f2da2") __attribute(__dispatch) IClrType;` - COM interface; expected to be an IDispatch when in a variant.
|
||||
- `static IntPtr __attribute(__stdcall) __attribute(__dll("kernel32.dll", "LoadLibraryW")) LoadLibraryW(PWSTR filename);` - stdcall calling convention function, imported from kernel32.dll!LoadLibraryW
|
||||
- `static void __attribute(__internal) OleCheck(HRESULT hr);` - function implemented internally by the PascalScript runtime
|
||||
- `static HRESULT __attribute(__com(7)) __attribute(__stdcall) IClrObject_ToString(IClrMethodInfo this, IntPtr* ret);` - stdcall calling convention function, the 7th entry in the vtable of a COM object
|
||||
|
||||
Initialised global variables are supported by emitting the initialisation instructions in a separate function (with an additional initialisation global boolean) and having all non-static functions call it first.
|
||||
The script's entry point is also set to the initialiser function.
|
||||
|
||||
An `__attribute(__open)` array of any pointer will be emitted as array of const (that is, open array of pointer).
|
||||
"Implicit cast to pointer" for array-of-const element assignment will emit `cpval` instruction (that is, copy constructor), like the official PascalScript compiler.
|
||||
|
||||
Because the PascalScript runtime is an interpreter, the compiler has a few optimisations for size (that is, number of instructions):
|
||||
|
||||
- Removing un-needed nops where possible
|
||||
- Removing pops directly before ret (return instruction fixes up the stack itself)
|
||||
- Immediate operands are only spilled to stack when needed.
|
||||
- For example, emitting `add Var1, UnicodeString("ABCD")` instead of (what the official PascalScript compiler does) `pushtype UnicodeString ; assign Var2, Var1 ; add Var2, UnicodeString("ABCD") ; assign Var1, Var2 ; pop`, saving 4 instructions.
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
|