pax_pascal.pas

来自「Delphi脚本控件」· PAS 代码 · 共 2,877 行 · 第 1/5 页

PAS
2,877
字号
////////////////////////////////////////////////////////////////////////////
// PAXScript Interpreter
// Author: Alexander Baranovsky (ab@cable.netlux.org)
// ========================================================================
// Copyright (c) Alexander Baranovsky, 2003-2005. All rights reserved.
// Code Version: 3.0
// ========================================================================
// Unit: PAX_PASCAL.pas
// ========================================================================
////////////////////////////////////////////////////////////////////////////

{$I PaxScript.def}
unit PAX_PASCAL;

interface

uses
  SysUtils, Classes,
  BASE_CONSTS,
  BASE_SYNC,
  BASE_SYS,
  BASE_SCANNER, BASE_PARSER, BASE_SCRIPTER, BASE_CLASS, BASE_EXTERN;

const
  SP_INTERVAL = -1001;
  SP_ADDRESS = -1002;
  errForward = 'Unsatisfied forward declaration';
type
  TPAXPascalScanner = class(TPAXScanner)
  public
    procedure ReadToken; override;
  end;

  TPAXPascalParser = class(TPAXParser)
  private
    ForwardIds, ForwardPos, EnumIds,

    OperCompare, OperAdditive, OperMult, ConstIds

    : TPAXIds;

    IsInterfaceSection: Boolean;

    function IsUnitId: boolean;
    function IsTypeID: boolean;
  public
    function IsConstant: boolean; override;
    constructor Create; override;
    destructor Destroy; override;
    procedure Reset; override;
    procedure Call_SCANNER; override;
    function IsBaseType(const S: String): Boolean; override;
    function Parse_OverloadableOperator: Integer; override;

/// expressions

    function Parse_EvalExpression: Integer; override;
    function Parse_ArgumentExpression: Integer; override;
    function Parse_ConstExpr: Integer;
    function Parse_Expression: Integer;
    function Parse_SimpleExpression: Integer;
    function Parse_Term: Integer;
    function Parse_Factor: Integer;
    function Parse_Designator(ResID: Integer = 0): Integer;
    function Parse_QualID: Integer;
    function Parse_UnitID: Integer;
    function Parse_TypeID: Integer;
    function Parse_SetConstructor: Integer;
    function Parse_SetElement: Integer;

// Statements
    procedure Parse_Statement;
    procedure Parse_StmtList; override;
    procedure Parse_SimpleStatement;

    procedure Parse_CompoundStmt;
    procedure Parse_IfStmt;
    procedure Parse_CaseStmt;
    procedure Parse_RepeatStmt;
    procedure Parse_WhileStmt;
    procedure Parse_ForStmt;
    procedure Parse_BreakStmt;
    procedure Parse_ContinueStmt;
    procedure Parse_ExitStmt;
    procedure Parse_LabelStmt;
    procedure Parse_HaltStmt;
    procedure Parse_WithStmt;
    procedure Parse_TryStmt;
    procedure Parse_RaiseStmt;
    procedure Parse_ProgramStmt;
    procedure Parse_UnitStmt;

    function Parse_FunctionStmt(ts: TPAXTypeSub; ml: TPAXModifierList): Integer;
    procedure Parse_VarStmt(IsField: Boolean; ml: TPAXModifierList; IsConst: Boolean);
    function Parse_VariableDeclaration(IsField: Boolean; ml: TPAXModifierList;
                                       Ids: TPAXIds; IsConst: Boolean): Integer;
    function Parse_ClassStmt(ClassML: TPAXModifierList; ck: TPAXClassKind; _ClassID: Integer = -1): Integer;
    function Parse_EnumStmt(_ClassID: Integer = -1): Integer;
    procedure Parse_NamespaceStmt(ml: TPAXModifierList);
    procedure Parse_TypeStmt;
    procedure Parse_ArrayStmt(ClassID: Integer);
    procedure Parse_DynamicArrayType(ClassID: Integer);

    procedure Parse_Program; override;
  end;


implementation

procedure TPAXPascalScanner.ReadToken;

procedure ScanChrs;
var
  S: String;
  I: Integer;
begin
  S := '';
  repeat
    GetNextChar;
    ScanHexDigits;
    I := StrToInt(Token.Text);
    S := S + Chr(I);

    if LA(1) = '#' then
      GetNextChar
    else
      Break;
  until false;

  Token.Text := S;
  Token.TokenClass := tcStringConst;

  if LA(1) = '''' then
  begin
    GetNextChar;
    ScanString('''');
    Token.Text := S + Token.Text;
  end;
end;

var
  S: String;
begin
  inherited;

  repeat

    if BuffToken.Text <> '' then
    begin
      Token := BuffToken;
      BuffToken.Text := '';
      Exit;
    end;

    GetNextChar;

    Token.TokenClass := tcNone;
    Token.ID := 0;

    case c of
      #8, #9, #10, #13, #32: ScanWhiteSpace;
      #255: ScanEOF;
      '0'..'9':
        ScanDigits;
      '$':
        ScanHexDigits;
      'A'..'Z', 'a'..'z', '_':
      begin
        ScanIdentifier;
        if StrEql(Token.Text, 'mod') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_MOD;
        end
        else if StrEql(Token.Text, 'div') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_INT_DIV;
        end
        else if StrEql(Token.Text, 'and') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_AND;
        end
        else if StrEql(Token.Text, 'or') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_OR;
        end
        else if StrEql(Token.Text, 'xor') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_XOR;
        end
        else if StrEql(Token.Text, 'shl') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_LEFT_SHIFT;
        end
        else if StrEql(Token.Text, 'shr') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_RIGHT_SHIFT;
        end
        else if StrEql(Token.Text, 'in') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_IN_SET;
        end
        else if StrEql(Token.Text, 'as') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_TYPE_CAST;
        end
        else if StrEql(Token.Text, 'is') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_IS;
        end
        else if StrEql(Token.Text, 'not') then
        begin
          Token.TokenClass := tcSpecial;
          Token.ID := OP_NOT;
        end;
      end;
      '+': ScanPlus;
      '-': ScanMinus;
      '*': ScanMult;
      '/':
      begin
        if LA(1) = '/' then
        begin
          repeat
            GetNextChar;
          until LA(1) in [#13, #10];
          Continue;
        end;
        ScanDiv;
      end;
      '{':
      begin
        if (LA(1) = '$') and (not LookForward) then
          ScanCondDir('{', ['$'])
        else
        begin
          repeat
            GetNextChar;

            if c in [#10,#13] then
            begin
              IncLineNumber;
              PosNumber := -1;

              if c = #13 then
                GetNextChar;
            end;

          until LA(1) in ['}', #255];
          GetNextChar;
        end;
        Continue;
      end;
      '=': ScanEQ;
      '>':
      begin
        ScanGT;
        if LA(1) = '=' then
        begin
          GetNextChar;
          Token.Text := '>=';
          Token.ID := OP_GE;
        end;
      end;
      '?', '%':
      case ScannerState of
        ScanText:
          raise TPAXScriptFailure.Create(errIllegalCharacter);
        ScanProg:
        if LA(1) = '>' then
        begin
          ScannerState := scanText;
          GetNextChar;
          ScanHtmlString('');
        end
      end;
      '<':
      case ScannerState of
        scanText:
        begin
          if LA(1) = '?' then
          begin
            GetNextChar;
            GetNextChar;
            ScanIdentifier;

            if not StrEql('pax', Trim(Token.Text)) then
              raise TPAXScriptFailure.Create(errIllegalCharacter);

            ScannerState := scanProg;
            Continue;
          end
          else if LA(1) = '%' then
          begin
            GetNextChar;
            ScannerState := scanProg;

            if LA(1) = '=' then
            begin
              GetNextChar;
              BuffToken.Text := 'print';
            end;

            Continue;
          end
          else if LA(1) in ['a'..'z','A'..'Z', '!'] then
            ScanHtmlString(c)
          else
            raise TPAXScriptFailure.Create(errIllegalCharacter);
        end;
        scanProg:
        begin
          ScanLT;
          if LA(1) = '=' then
          begin
            GetNextChar;
            Token.Text := '<=';
            Token.ID := OP_LE;
          end
          else if LA(1) = '>' then
          begin
            GetNextChar;
            Token.Text := '<>';
            Token.ID := OP_NE;
          end;
        end;
      end;
      ':':
      begin
        ScanColon;
        if LA(1) = '=' then
        begin
          GetNextChar;
          Token.Text := ':=';
          Token.ID := OP_ASSIGN;
        end;
      end;
      '@':
      begin
        Token.Text := '@';
        Token.ID := SP_ADDRESS;
        Token.TokenClass := tcSpecial;
      end;
      '^':
      begin
        Token.Text := '^';
        Token.ID := OP_GET_TERMINAL;
        Token.TokenClass := tcSpecial;
      end;
      ';': ScanSemiColon;
      '(': ScanLeftRoundBracket;
      ')': ScanRightRoundBracket;
      '[': ScanLeftBracket;
      ']': ScanRightBracket;
      ',': ScanComma;
      '.':
      begin
        ScanPoint;
        if LA(1) = '.' then
        begin
          GetNextChar;
          Token.Text := '..';
          Token.ID := SP_INTERVAL;
        end;
      end;
      '''':
      begin
        ScanString('''');
        if LA(1) = '#' then
        begin
          S := Token.Text;
          GetNextChar;
          ScanChrs;
          Token.Text := S + Token.Text;
        end;
      end;
      '"': ScanString('"');
      '#':
      begin
        if LA(1) = '!' then
        begin
          repeat
            GetNextChar;
          until LA(1) in [#13, #10];
          Continue;
        end;
        ScanChrs;
      end;
    else
      raise TPAXScriptFailure.Create(errIllegalCharacter);
    end;

    if not (Token.TokenClass in [tcNone]) then
      Break;

  until false;
end;

constructor TPAXPascalParser.Create;
begin
  ConstIds := TPaxIds.Create(false);

  inherited;

  Scanner := TPAXPascalScanner.Create(Self);

  Upcase := true;
  IsArrayInitialization := true;

  IsInterfaceSection := false;
  IsImplementationSection := false;

  with Keywords do
  begin
    Add('AND');
    Add('ARRAY');
    Add('AS');
    Add('BEGIN');
    Add('BREAK');
    Add('CASE');
    Add('CLASS');
    Add('CONST');
    Add('CONSTRUCTOR');
    Add('CONTINUE');
    Add('DESTRUCTOR');
    Add('DIV');
    Add('DO');
    Add('ELSE');
    Add('END');
    Add('ENUM');
    Add('EXCEPT');
    Add('EXIT');
    Add('EXTERNAL');
    Add('HALT');
    Add('FINALLY');
    Add('FOR');
    Add('FORWARD');
    Add('FUNCTION');
    Add('GOTO');
    Add('IF');
    Add('IN');
    Add('INHERITED');
    Add('LABEL');
    Add('INHERITED');
    Add('NAMESPACE');
    Add('NEW');
    Add('NOT');
    Add('MOD');
    Add('ON');
    Add('OF');
    Add('OPERATOR');
    Add('OR');
    Add('OUT');
    Add('OVERRIDE');
    Add('PROCEDURE');
    Add('PROGRAM');
    Add('PRIVATE');
    Add('PRINT');
    Add('PRINTLN');
    Add('PUBLIC');
    Add('PROPERTY');
    Add('RAISE');
    Add('RECORD');
    Add('REDUCED');
    Add('STATIC');
//    Add('DELETE');
    Add('REPEAT');
    Add('SHL');
    Add('SHR');
    Add('THEN');
    Add('TO');
    Add('TRY');
    Add('TYPE');
    Add('VAR');
    Add('UNTIL');
    Add('USES');
    Add('VIRTUAL');
    Add('WHILE');
    Add('WITH');
    Add('XOR');

    Add('IS');

    Add('UNIT');
    Add('INTERFACE');
    Add('IMPLEMENTATION');
    Add('INITIALIZATION');
    Add('FINALIZATION');
    Add('OVERLOAD');
  end;

  ForwardIds := TPaxIds.Create(false);
  ForwardPos := TPaxIds.Create(false);
  EnumIds := TPaxIds.Create(false);

  OperCompare := TPaxIds.Create(false);
  with OperCompare do
  begin
    Add(OP_EQ);
    Add(OP_NE);
    Add(OP_GT);
    Add(OP_LT);
    Add(OP_GE);
    Add(OP_LE);
    Add(OP_IS);
    Add(OP_IN_SET);
    Add(OP_TYPE_CAST);
  end;

  OperAdditive := TPaxIds.Create(false);
  with OperAdditive do
  begin
    Add(OP_PLUS);
    Add(OP_MINUS);
    Add(OP_OR);
    Add(OP_XOR);
  end;

  OperMult := TPaxIds.Create(false);
  with OperMult do
  begin
    Add(OP_MULT);
    Add(OP_DIV);
    Add(OP_INT_DIV);
    Add(OP_MOD);
    Add(OP_AND);
    Add(OP_RIGHT_SHIFT);
    Add(OP_LEFT_SHIFT)
  end;

end;

procedure TPAXPascalParser.Call_SCANNER;
var
  S: String;
  TempID: Integer;
begin
  inherited;

  if CurrToken.TokenClass in [tcId, tcKeyword] then
  begin
    if IsCurrText('TDateTime') or IsCurrText('Real') then
    begin
      CurrToken.ID := typeDOUBLE;
      CurrToken.TokenClass := tcId;
    end
    else if IsCurrText('OleVariant') then
    begin
      CurrToken.ID := typeVARIANT;
      CurrToken.TokenClass := tcId;
    end
    else if IsCurrText('static') then
    begin
      CurrToken.Text := 'class';
    end
    else if IsCurrText('nil') then
      CurrToken.ID := UndefinedID
    else
    begin
      S := FindTypeAlias(CurrToken.Text, UpCase);
      if S <> '' then
      begin
        CurrToken.Text := S;
        TempID := SymbolTable.LookUpID(S, 0, UpCase);
        if TempID > 0 then
          CurrToken.ID := TempID
        else

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?